feat(selfbot): broadcast desktop audio + smart subtitles in the browse scenario
Two broadcast-experience improvements: - Audio: the Go-Live stream was video-only. Capture the desktop sound (the default PipeWire/Pulse sink monitor, @DEFAULT_MONITOR@) as a second ffmpeg input and mux AAC into the mpegts; the library re-encodes it to Opus for Discord. Controlled by STREAM_AUDIO / STREAM_AUDIO_SOURCE (default on). ffmpeg inherits XDG_RUNTIME_DIR to reach the pulse socket. Verified: the streamer now reports "Found audio stream" and the monitor carries Chrome audio (~-11 dB). - Subtitles: in the browse scenario, default captions OFF, but auto-enable a Korean track when the video offers one (getOption captions tracklist -> setOption / unloadModule). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -42,6 +42,11 @@ export const config = {
|
||||
selfbotToken: opt("DISCORD_SELFBOT_TOKEN"),
|
||||
// Use NVENC hardware encode + hw-accelerated decode for the stream (RTX 5050).
|
||||
streamHw: opt("STREAM_HW", "1") !== "0",
|
||||
// Capture desktop audio into the broadcast so the stream has sound. Pulls the
|
||||
// PipeWire/Pulse monitor of the default sink (what the desktop plays). Set
|
||||
// STREAM_AUDIO=0 to mute; STREAM_AUDIO_SOURCE overrides the capture source.
|
||||
streamAudio: opt("STREAM_AUDIO", "1") !== "0",
|
||||
streamAudioSource: opt("STREAM_AUDIO_SOURCE", "@DEFAULT_MONITOR@"),
|
||||
|
||||
// novnc backend
|
||||
novncUrl: opt("NOVNC_URL", ""),
|
||||
|
||||
@@ -143,17 +143,31 @@ export class SelfbotStreamer implements ScreenStreamer {
|
||||
const captureCodecArgs = hw
|
||||
? ["-c:v", "h264_nvenc", "-preset", "p4", "-tune", "ll", "-forced-idr", "1"]
|
||||
: ["-c:v", "libx264", "-preset", "ultrafast", "-tune", "zerolatency"];
|
||||
// Optionally pull desktop audio (the default sink's PipeWire/Pulse monitor)
|
||||
// so the broadcast has sound. We add it as a second input and mux AAC into
|
||||
// the mpegts; the library re-encodes it to Opus for Discord. ffmpeg needs
|
||||
// XDG_RUNTIME_DIR (inherited) to reach the pulse socket. -map is required
|
||||
// once there are two inputs.
|
||||
const audioOn = this.config.streamAudio;
|
||||
const audioInput = audioOn ? ["-f", "pulse", "-i", this.config.streamAudioSource] : [];
|
||||
const audioMap = audioOn ? ["-map", "0:v:0", "-map", "1:a:0"] : [];
|
||||
const audioCodec = audioOn ? ["-c:a", "aac", "-b:a", "160k", "-ar", "48000", "-ac", "2"] : [];
|
||||
capture = this.capture = spawn("ffmpeg", [
|
||||
"-loglevel", "error",
|
||||
"-thread_queue_size", "1024",
|
||||
"-f", "x11grab",
|
||||
"-framerate", String(this.config.vncFramerate),
|
||||
"-video_size", this.config.vncResolution,
|
||||
"-i", this.config.vncDisplay,
|
||||
...(audioOn ? ["-thread_queue_size", "1024"] : []),
|
||||
...audioInput,
|
||||
...audioMap,
|
||||
...captureCodecArgs,
|
||||
"-b:v", `${kbps}k`, "-maxrate", `${maxKbps}k`, "-bufsize", `${kbps}k`,
|
||||
"-bf", "0",
|
||||
"-pix_fmt", "yuv420p",
|
||||
"-g", String(this.config.vncFramerate),
|
||||
...audioCodec,
|
||||
"-f", "mpegts", "pipe:1",
|
||||
]);
|
||||
capture.stderr?.on("data", (d) => {
|
||||
|
||||
Reference in New Issue
Block a user