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

2024-05-17

我的目标是在 Node 中渲染画布,并将该画布流式传输到 RTMP 服务器(最终是 Twitch,但现在我正在在本地 RTMP 服务器上测试)。流式传输到 RTMP 的标准方式似乎是ffmpeg,所以我使用它,从 NodeJS 中作为子进程生成。

我尝试了很多不同的技术组合ffmpegparams 以获得一致的帧速率和“实时”速度的流,但无法弄清楚。这是我迄今为止走过的路

渲染画布并以连续间隔发送输入

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
  • Stream behavior
    • 在 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 fps,需要几秒钟才能达到 28 fps,然后大约 30 秒才能达到 30 fps。速度更接近 1 倍,但并非完全如此。示例日志frame=15421 fps= 30 q=29.0 size= 4502kB time=00:08:31.66 bitrate= 72.1kbits/s speed=0.994x
  • Stream behavior
    • 在 VLC 中打开后,加载大约需要 5 秒
    • 计时器在同一秒停留多分钟

我对流被卡在 1 个时间戳的预感是,当 ffmpeg 发送帧时out以每秒 30 帧的速度,我发送帧的速度比这快得多。所以在流媒体的前一秒

  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
  • Stream behavior
    • 在 VLC 中打开后,加载大约需要 10 秒
    • 计时器增加的速度大约是预期的两倍,然后挂在一秒上一段时间,然后再次开始以相同的速率增加

我就到此为止,但是 tl;dr 我已经尝试了一大堆不同的组合-re, -framerate, -fps_mode, -rffmpeg args,以及代码中的一些其他技术,例如继续使用setImmediate发送帧,但使用日期比较以 FPS 速率实际发送帧。我确信我可能缺少一些基本的视频流知识,所以我只是在寻找有关如何让我的画布以“实时”速度流式传输的任何指导,或者我在这里可能缺少的任何内容。


None

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FFmpeg - 来自 NodeJS 的 RTMP 流,流比实时更快 的相关文章

  • Jquery 悬停卡

    我在用着http designwithpc com Plugins Hovercard http designwithpc com Plugins Hovercard 但我不知道如何在悬停卡上声明 var 每个工作描述都有自己的 ID 当悬
  • 在特定页面上执行 javascript 的正确“Rails”方式

    我试图在特定页面上运行 javascript 而我唯一的解决方案似乎是反模式 我有controller js内部生成的assets javascripts 我在用着gem jquery turbolinks 我的代码类似于以下内容 docu
  • Firebase,只得到新的孩子[重复]

    这个问题在这里已经有答案了 var firebase new Firebase firebaseRef on child added function snapshot 这将接收所有元素 有没有办法在创建新的 Firebase 引用时不接收
  • Vue 3 Composition API 提供/注入在单文件组件中不起作用

    我正在使用 Composition API 在 VueJS 3 中创建一个库 我实现了提供 注入 如中所述docs https v3 vuejs org guide composition api provide inject html i
  • 在版本 4.4.6 中禁用 ckeditor 上下文菜单

    我在 Rails4 项目中使用 ckeditor 我尝试了 ckeditor gem 和 ckeditor rails gem 来提供 ckeditor 库 这里有多个帖子 人们希望删除 ckeditor 上下文菜单 以便可以显示本机浏览器
  • 如何使用 Playwright 使用选择器查找框架 (iframe)

    我有一个小问题 无法找到使用 Microsoft Playwright 框架的答案 根据您可以使用以下代码获取 iframe const frame page frame frame login 但是如何使用选择器来查找 iframe 并与
  • 本地推送通知到在应用程序内运行 JS 代码的 Win8 Live Tile

    我正在尝试将更新发送到我的应用程序的磁贴 当应用程序运行时 这可以正常工作 例如 当用户单击按钮时 我可以轻松地将磁贴更新通知发送到磁贴 我无法解决的是当应用程序无法运行时如何更新磁贴 我找到的唯一选择是使用以下命令从远程 Web 服务器拉
  • javascript 选择自定义光标 (svg)

    我正在动态地将光标更改为悬停时的本地 svg element on mouseover function this css cursor url svgs pointer svg 9 30 auto 工作正常 但我想选择该 svg 来操纵其
  • IE 中的 XPath 查询使用从零开始的索引,但 W3C 规范是从一开始的。我应该如何处理差异?

    问题 我正在转换目前仅适用于 Internet Explorer 的相对较大的 Javascript 代码 以便使其也适用于其他浏览器 由于代码广泛使用 XPath 我们做了一些兼容性功能以使事情变得更容易 function selectN
  • mongodb 聚合 - 累积字段的不同组值

    如果我有Player表格文件 name String score Int 我有Group文档 其中组代表玩家列表 groupName String players ObjectID 玩家可以属于多个组 我想做一个聚合Player文档 按以下
  • 如何始终将焦点保持在文本框中

    我创建了一个包含两个 div 的 HTML 页面 左侧的 div 页面的 90 是 ajax 结果的目标 右侧的 div 页面的 10 包含一个文本框 该页面的想法是在文本框中输入零件编号 通过条形码扫描仪 并显示与该零件编号匹配的绘图 显
  • 如何计算特定字符在字符串中出现的次数

    我正在尝试创建一个函数来查看数组中的任何字符是否在字符串中 如果是 有多少个 我尝试计算每一种模式 但是太多了 我尝试使用 Python 中的 in 运算符的替代方案 但效果不佳 function calc fit element var
  • LeafleteachLayer函数不会迭代所有Layer

    使用 GeoJSON 数据数组创建一些标记 getJSON GetLocationsServlet function data L geoJSON data onEachFeature onEachFeature addTo mymap G
  • 使用 Javascript 设置 cookie [重复]

    这个问题在这里已经有答案了 我正在尝试构建我的第一个移动应用程序 它需要连接到我的 mysql 数据库并使用 json 返回数据 这很好 目前我有一个登录系统 一旦确定用户名和密码存在 它就会返回一条成功消息 对于下一步 我想在我的页面上使
  • 如何使用 JavaScript 或 jQuery 克隆 HTML 元素的样式对象?

    我正在尝试克隆元素的样式对象 这应该允许我在更改后重置所述元素的样式 例如 el style left 50px curr style left 50px Modify the elements style The cloned style
  • 如何从浏览器向服务器发送“页面将关闭”消息?

    我想向每个 html 文档添加一个脚本 JavaScript 该脚本向服务器发送两条消息 页面确实打开了 页面将关闭 此消息包含页面打开的时间 打开消息应在文档加载时 或加载完成时 发送 这是简单的部分 The close message
  • 使用javascript动态更新css内容

    需要将 css 更新为动态值 我不确定最好的方法是什么 div style zoom 1 div 缩放级别将根据窗口大小调整触发 应用程序将相应缩放 我将此应用程序加载到 cordova 中并让它在 iPAD 中运行 然后我意识到需要使用
  • 使用 next.js 进行服务器端渲染与传统 SSR

    我非常习惯 SSR 意味着页面得到完全刷新并从服务器接收完整 HTML 的方法 其中根据后端堆栈使用 razor pub other 进行渲染 因此 每次用户单击导航链接时 它只会向服务器发送请求 整个页面将刷新 接收新的 HTML 这就是
  • 滚动顶部不符合预期

    Note 由于上次忘记奖励而重新开放赏金 A Woff 大师已经给出答案 我想在用户展开某一行时到达该行 这样当最后一个可见行展开时 用户不必向下滚动即可查看内容 I used example tbody on click td green
  • 测量窗口偏移

    有没有一种方法可以测量 jQuery 中窗口的偏移量 以便我可以比较 固定 元素和相对定位元素的位置 我需要能够知道窗口滚动了多远 以便我可以使用该图来计算固定元素的高度 相对于视口顶部 和相对对象的高度 相对于顶部 之间的差异文件的内容

随机推荐