FFmpeg - 来自 Node 的 RTMP 流,流比实时更快

问题描述 投票:0回答:0

我的目标是在 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()
观察
  • 流实际开始需要大约 35 秒(我认为是因为 ffmpeg 需要一些时间来分析输入?)
  • 帧速率远低于我设置的值,“速度”也非常低,尽管我不是 100% 确定这意味着什么。示例日志
    Frame=  906 fps=3.9 q=29.0 size= 311kB time=00:00:27.83 bitrate= 91.5kbits/s speed=0.119x
  • 流行为
    • 在 VLC 中打开后大约需要一分钟加载
    • 流上的计时器比实际时间慢 1 分钟左右开始,停留在一秒上 30 多秒,然后快速增加几秒,然后再次卡住

我在这里有一种预感,至少造成这种奇怪行为的部分原因是,在我将输入发送到

ffmpeg
的同一循环中渲染画布太慢,无法达到 30 FPS。

在与 ffmpeg 输入间隔不同的间隔中渲染画布

每秒仅渲染画布 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
    几乎立即开始流式传输
  • fps 从 16 左右开始,需要几秒钟才能达到 28,然后大约需要 30 秒才能达到 30fps。速度更接近 1 倍,但并非完全如此。示例日志
    frame=15421 fps= 30 q=29.0 size=    4502kB time=00:08:31.66 bitrate=  72.1kbits/s speed=0.994x       
  • 流行为
    • 在 VLC 中打开后加载大约需要 5 秒
    • 计时器停留在同一秒数分钟

我对流卡在 1 个时间戳上的预感是,虽然 ffmpeg 以每秒 30 帧的速度发送帧out,但我发送帧的速度比这快得多。所以在流式传输的第一秒

  1. Canvas 使用时间戳 T 渲染 30 次
  2. send
    运行 N 次,其中 N 可能远高于 30,使用当前时间戳发送
    ffmpeg
    N 帧
  3. ffmpeg 现在有 N 帧,上面有时间戳 T,但每秒只能发送 30 个,所以屏幕上的时间戳需要 1 秒以上才能改变

每 1/FPS 秒仅向 ffmpeg 发送一帧

和以前一样,但不是尽快发送 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
    需要几秒钟才能开始流式传输
  • fps 开始时很高,大约 28,然后在接下来的一分钟左右下降到 16。速度也随之下降。示例日志
    frame= 1329 fps= 16 q=29.0 size=     463kB time=00:00:41.93 bitrate=  90.5kbits/s speed= 0.5x
  • 流行为
    • 在 VLC 中打开后加载大约需要 10 秒
    • 计时器增加的速度大约是预期的两倍,然后暂停一秒,然后再次以相同的速度增加

我会停在那里,但是 tl;dr 我已经尝试了一大堆不同的

-re, -framerate, -fps_mode, -r
ffmpeg args 组合,以及代码中的一些其他技术,比如继续使用
setImmediate
发送帧,但使用日期与实际以 FPS 速率发送帧的比较。我确定我可能缺少一些基本的视频流知识,所以我只是在寻找任何关于如何让我的画布以“实时”速度流式传输的指导,或者我在这里可能缺少的任何内容.

javascript node.js video ffmpeg streaming
© www.soinside.com 2019 - 2024. All rights reserved.