feat: 1080p60 NVENC selfbot broadcast (8 Mbps default)

Bump the default broadcast to 1080p 60fps at 8 Mbps and route both encode
stages through the GPU (RTX 5050, h264_nvenc) so 60fps stays smooth without
loading the 4-core host.

- selfbot.ts: capture ffmpeg uses h264_nvenc when streamHw is on (falls back
  to software x264 otherwise), and prepareStream now passes Encoders.nvenc()
  so the library's transcode runs on the GPU too. Guard loadLib for Encoders.
- config.ts: VNC_FRAMERATE default 30 -> 60, VNC_BITRATE_KBPS 4000 -> 8000.
- .env.example: document the new 1080p60/8 Mbps defaults and STREAM_HW.

Verified locally: h264_nvenc x11grab holds a steady 60fps with headroom,
Encoders.nvenc() returns valid h264_nvenc settings, and tsc --noEmit passes.
Live Discord voice-channel verification pending a host reboot.
This commit is contained in:
javis-bot
2026-06-10 11:17:44 +09:00
parent 5137fdeaf7
commit ad0caa8142
3 changed files with 23 additions and 8 deletions

View File

@@ -35,8 +35,8 @@ export const config = {
// x11grab source for the VNC display (TigerVNC runs the desktop on :1)
vncDisplay: opt("VNC_DISPLAY", ":1"),
vncResolution: opt("VNC_RESOLUTION", "1920x1080"),
vncFramerate: parseInt(opt("VNC_FRAMERATE", "30"), 10),
vncBitrateKbps: parseInt(opt("VNC_BITRATE_KBPS", "4000"), 10),
vncFramerate: parseInt(opt("VNC_FRAMERATE", "60"), 10),
vncBitrateKbps: parseInt(opt("VNC_BITRATE_KBPS", "8000"), 10),
// selfbot backend (ToS-risk; use a throwaway account token, never your main)
selfbotToken: opt("DISCORD_SELFBOT_TOKEN"),

View File

@@ -46,9 +46,9 @@ export class SelfbotStreamer implements ScreenStreamer {
`원본 오류: ${(e as Error).message}`,
);
}
if (!vs.Streamer || !vs.prepareStream || !vs.playStream) {
if (!vs.Streamer || !vs.prepareStream || !vs.playStream || !vs.Encoders) {
throw new Error(
"@dank074/discord-video-stream v6 API(Streamer/prepareStream/playStream)를 찾지 못했습니다. " +
"@dank074/discord-video-stream v6 API(Streamer/prepareStream/playStream/Encoders)를 찾지 못했습니다. " +
"package.json 버전을 ^6.0.0으로 맞추세요.",
);
}
@@ -64,7 +64,7 @@ export class SelfbotStreamer implements ScreenStreamer {
return "셀프봇 송출은 음성 채널 안에서 호출해야 합니다.";
}
const { selfbot, vs } = await this.loadLib();
const { Streamer, prepareStream, playStream } = vs;
const { Streamer, prepareStream, playStream, Encoders } = vs;
this.streamer = new Streamer(new selfbot.Client());
await this.streamer.client.login(this.config.selfbotToken);
@@ -77,13 +77,22 @@ export class SelfbotStreamer implements ScreenStreamer {
// x11grab), then pipe that stream into the library. Relying on the lib's
// bundled libav for the x11grab input device is not portable; piping the
// system ffmpeg is. (Verified live against a real voice channel.)
//
// With streamHw on (default) the capture is encoded by the GPU
// (h264_nvenc, RTX 5050) so 1080p60 stays smooth without loading the CPU;
// the library then transcodes with NVENC too (see encoder below). Without
// hardware support we fall back to software x264.
const hw = this.config.streamHw;
const captureCodecArgs = hw
? ["-c:v", "h264_nvenc", "-preset", "p4", "-tune", "ll"]
: ["-c:v", "libx264", "-preset", "ultrafast", "-tune", "zerolatency"];
const capture = spawn("ffmpeg", [
"-loglevel", "error",
"-f", "x11grab",
"-framerate", String(this.config.vncFramerate),
"-video_size", this.config.vncResolution,
"-i", this.config.vncDisplay,
"-c:v", "libx264", "-preset", "ultrafast", "-tune", "zerolatency",
...captureCodecArgs,
"-pix_fmt", "yuv420p", "-g", String(this.config.vncFramerate),
"-f", "mpegts", "pipe:1",
]);
@@ -101,6 +110,7 @@ export class SelfbotStreamer implements ScreenStreamer {
videoCodec: "H264",
bitrateVideo: this.config.vncBitrateKbps,
bitrateVideoMax: Math.round(this.config.vncBitrateKbps * 1.5),
encoder: hw ? Encoders.nvenc() : Encoders.software(),
},
this.controller.signal,
);