我的目标是在 Node 中渲染画布,并将该画布流式传输到 RTMP 服务器(最终是 Twitch,但在本地 RTMP 服务器上进行测试)。流式传输到 RTMP 的标准方式似乎是
ffmpeg
,所以我正在使用它,从节点内作为子进程产生。我已经尝试了一堆不同的技术组合和ffmpeg
参数来获得一致的帧率和“实时”速度的流,但无法弄清楚。这是我到目前为止走过的路
import { createCanvas } from 'canvas';
const canvas = createCanvas(1920, 1080);
const ctx = canvas.getContext('2d');
const fps = 30;
const ffmpeg = spawn('ffmpeg', [
'-re',
'-framerate', String(.fps),
'-r', String(fps),
'-i', '-',
'-vcodec', 'libx264',
'-r', String(fps),
'-s', '1920x1080',
'-g:v', String(2*fps),
'-c:a', 'aac',
'-f', 'flv', 'rtmp://127.0.0.1/live'
]);
ffmpeg.stdout.pipe(process.stdout)
ffmpeg.stderr.pipe(process.stderr)
const send = () => {
ctx.fillStyle = 'red'
ctx.fillRect(0, 0, 1920, 1080);
ctx.font = '100px Arial';
ctx.fillStyle = 'black'
ctx.fillText(new Date().toLocaleString(), 500, 500);
ffmpeg.stdin.write(canvas.toBuffer())
setImmediate(() => send())
}
send()
Frame= 906 fps=3.9 q=29.0 size= 311kB time=00:00:27.83 bitrate= 91.5kbits/s speed=0.119x
我在这里有一种预感,至少造成这种奇怪行为的部分原因是,在我将输入发送到
ffmpeg
的同一循环中渲染画布太慢,无法达到 30 FPS。
每秒仅渲染画布 FPS 次
继续尽快向
ffmpeg
发送输入
import { createCanvas } from 'canvas';
const canvas = createCanvas(1920, 1080);
const ctx = canvas.getContext('2d');
let buffer = canvas.toBuffer();
const fps = 30;
const ffmpeg = spawn('ffmpeg', [
...same as before
]);
const render = () => {
ctx.fillStyle = 'red'
ctx.fillRect(0, 0, 1920, 1080);
ctx.font = '100px Arial';
ctx.fillStyle = 'black'
ctx.fillText(new Date().toLocaleString(), 500, 500);
buffer = canvas.toBuffer();
setTimeout(() => render(), 1/fps)
}
render();
const send = () => {
ffmpeg.stdin.write(buffer)
setImmediate(() => send())
}
send()
ffmpeg
几乎立即开始流式传输frame=15421 fps= 30 q=29.0 size= 4502kB time=00:08:31.66 bitrate= 72.1kbits/s speed=0.994x
我对流卡在 1 个时间戳上的预感是,虽然 ffmpeg 以每秒 30 帧的速度发送帧out,但我发送帧的速度比这快得多。所以在流式传输的第一秒
send
运行 N 次,其中 N 可能远高于 30,使用当前时间戳发送 ffmpeg
N 帧和以前一样,但不是尽快发送 ffmpeg 帧,而是每秒只发送 FPS 帧。
import { createCanvas } from 'canvas';
const canvas = createCanvas(1920, 1080);
const ctx = canvas.getContext('2d');
let buffer = canvas.toBuffer();
const fps = 30;
const ffmpeg = spawn('ffmpeg', [
...same as before
]);
const render = () => {
...same as before
}
render();
const send = () => {
ffmpeg.stdin.write(buffer)
setTimeout(() => send(), 1/fps)
}
send()
ffmpeg
需要几秒钟才能开始流式传输frame= 1329 fps= 16 q=29.0 size= 463kB time=00:00:41.93 bitrate= 90.5kbits/s speed= 0.5x
我会停在那里,但是 tl;dr 我已经尝试了一大堆不同的
-re, -framerate, -fps_mode, -r
ffmpeg args 组合,以及代码中的一些其他技术,比如继续使用 setImmediate
发送帧,但使用日期与实际以 FPS 速率发送帧的比较。我确定我可能缺少一些基本的视频流知识,所以我只是在寻找任何关于如何让我的画布以“实时”速度流式传输的指导,或者我在这里可能缺少的任何内容.