feat(streamer): ffmpeg rtmp encoder spawn
This commit is contained in:
parent
baef915561
commit
7cdc8f0d44
1 changed files with 58 additions and 0 deletions
58
streamer/src/ffmpeg.ts
Normal file
58
streamer/src/ffmpeg.ts
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { ChildProcessWithoutNullStreams, spawn } from "node:child_process";
|
||||||
|
import { EventEmitter } from "node:events";
|
||||||
|
import { Writable } from "node:stream";
|
||||||
|
|
||||||
|
export interface FfmpegOpts {
|
||||||
|
rtmpUrl: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
framerate: number;
|
||||||
|
videoBitrate: string;
|
||||||
|
audioBitrate: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Ffmpeg extends EventEmitter {
|
||||||
|
private proc: ChildProcessWithoutNullStreams | null = null;
|
||||||
|
|
||||||
|
constructor(private opts: FfmpegOpts) { super(); }
|
||||||
|
|
||||||
|
start(): { videoIn: Writable; audioIn: Writable } {
|
||||||
|
const args = [
|
||||||
|
"-loglevel", "warning",
|
||||||
|
// video in (mjpeg pipe on fd 3)
|
||||||
|
"-f", "image2pipe", "-c:v", "mjpeg",
|
||||||
|
"-r", String(this.opts.framerate), "-i", "pipe:3",
|
||||||
|
// audio in (s16le pcm pipe on fd 4)
|
||||||
|
"-f", "s16le", "-ar", "48000", "-ac", "2", "-i", "pipe:4",
|
||||||
|
// video encode
|
||||||
|
"-c:v", "libx264", "-preset", "veryfast",
|
||||||
|
"-pix_fmt", "yuv420p",
|
||||||
|
"-b:v", this.opts.videoBitrate,
|
||||||
|
"-maxrate", this.opts.videoBitrate, "-bufsize", "9000k",
|
||||||
|
"-g", String(this.opts.framerate * 2), "-keyint_min", String(this.opts.framerate * 2),
|
||||||
|
"-r", String(this.opts.framerate),
|
||||||
|
// audio encode
|
||||||
|
"-c:a", "aac", "-b:a", this.opts.audioBitrate, "-ar", "48000",
|
||||||
|
// output
|
||||||
|
"-f", "flv", this.opts.rtmpUrl,
|
||||||
|
];
|
||||||
|
|
||||||
|
this.proc = spawn("ffmpeg", args, {
|
||||||
|
stdio: ["ignore", "pipe", "pipe", "pipe", "pipe"],
|
||||||
|
}) as unknown as ChildProcessWithoutNullStreams;
|
||||||
|
|
||||||
|
const stdio = (this.proc as unknown as { stdio: Writable[] }).stdio;
|
||||||
|
const videoIn = stdio[3]!;
|
||||||
|
const audioIn = stdio[4]!;
|
||||||
|
|
||||||
|
this.proc.stderr.on("data", (d) => this.emit("log", d.toString()));
|
||||||
|
this.proc.on("exit", (code) => this.emit("exit", code));
|
||||||
|
|
||||||
|
return { videoIn, audioIn };
|
||||||
|
}
|
||||||
|
|
||||||
|
stop(): void {
|
||||||
|
this.proc?.kill("SIGTERM");
|
||||||
|
this.proc = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue