如何在JavaScript中使用ffmpeg将H.264帧解码为RGB帧

5
我正在尝试将ffmpeg编译为JavaScript,以便使用node解码H.264视频流。这些流是打包到RTP NALU中的H.264帧,因此任何解决方案都必须能够接受H.264帧而不是整个文件名。这些帧不能在像MP4或AVI这样的容器中,因为解复用器需要在解复用之前需要每个帧的时间戳,但我正在处理实时流,没有容器。

通过RTP流式传输H.264

以下是我使用的基本代码来监听udp套接字。在“message”回调中,数据包是一个RTP数据报。数据报的数据部分是一个H.264帧(P帧和I帧)。

var PORT = 33333;
var HOST = '127.0.0.1';

var dgram = require('dgram');
var server = dgram.createSocket('udp4');

server.on('listening', function () {
    var address = server.address();
    console.log('UDP Server listening on ' + address.address + ":" + address.port);
});

server.on('message', function (message, remote) {
    console.log(remote.address + ':' + remote.port +' - ' + message);
    frame = parse_rtp(message);

    rgb_frame = some_library.decode_h264(frame); // This is what I need.

});

server.bind(PORT, HOST);  

我找到了Broadway.js库,但是无法让它工作并且它不处理我需要的P-frames。我也找到了ffmpeg.js,但无法使用它并且它需要整个文件而不是流。同样,fluent-ffmpeg似乎不支持文件流;所有的示例都展示了将文件名传递给构造函数。因此,我决定编写自己的API。

我的当前解决方案尝试

我已经能够将ffmpeg编译成一个大的js文件,但我不能像那样使用它。我想围绕ffmpeg编写一个API,然后将这些函数暴露给JS。所以我觉得我需要做以下事情:

  1. 将ffmpeg组件(avcodec、avutil等)编译为llvm位代码。
  2. 编写一个C包装器来公开解码功能并使用EMSCRIPTEN_KEEPALIVE。
  3. 使用emcc编译包装器并将其链接到步骤1中创建的位代码。
我发现了WASM+ffmpeg,但是这篇文章是中文的,有些步骤不太清楚。特别是其中有一步:
emcc web.c process.c ../lib/libavformat.bc ../lib/libavcodec.bc ../lib/libswscale.bc ../lib/libswresample.bc ../lib/libavutil.bc \

:( 我卡住了

我不理解所有的ffmpeg组件是如何编译成单独的*.bc文件的。我按照那篇文章中的emmake命令进行操作,但最终只得到一个大的.bc文件。

2个问题

1. 有人知道使用emscripten编译ffmpeg的步骤,以便我可以将一些API暴露给JavaScript吗?
2. 是否有更好的方法(带有良好的文档/示例)来使用node解码h264视频流?


尝试查看流畅的API:https://www.npmjs.com/package/fluent-ffmpeg - Robert Rowntree
1
文档中说:“您可以将输入文件名或可读取流,配置对象或两者一起传递给构造函数”。什么是“文件流”? “文件流”指的是在程序中处理文件时使用的一种数据流。它允许程序以连续的方式读取或写入文件的内容,而不需要一次性将整个文件读入内存。 - Alex Taylor
1
https://github.com/fluent-ffmpeg/node-fluent-ffmpeg/blob/master/examples/input-stream.js 看起来对我来说是输入流。 - Robert Rowntree
1
你可以打开一个子进程来运行ffmpeg,然后将你的RTP消息输入到该子进程的stdin中,并从子进程的stdout中读取你的rgb_frames。 - GroovyDotCom
1
请更详细地描述一下您的“H.264帧打包成RTP NALU” - 如果没有这样的描述,那么就无法确定如何处理。例如,您可以展示一个[mcve]来生成它们。例如,接收端如何找到元数据?时间戳?如何将相互依赖的帧连接在一起? - ivan_pozdeev
显示剩余5条评论
2个回答

1

针对问题1: 只需遵循官方文档即可。

Consider the case where you normally build with the following commands:

./configure
make

To build with Emscripten, you would instead use the following commands:

# Run emconfigure with the normal configure command as an argument.
./emconfigure ./configure

# Run emmake with the normal make to generate linked LLVM bitcode.
./emmake make

# Compile the linked bitcode generated by make (project.bc) to JavaScript.
# 'project.bc' should be replaced with the make output for your project (e.g. 'yourproject.so')
#  [-Ox] represents build optimisations (discussed in the next section).
./emcc [-Ox] project.bc -o project.js
对于问题2:在node环境中可以调用c/c++库。您可以编写一些c/c++粘合代码或使用代理节点模块,例如node-ffi
使用node-ffi调用现有库可能更容易。 希望能有所帮助 :)

0
最简单的方法,特别是如果您需要在Web浏览器中运行它,就是利用媒体源扩展。我只用了3天就做到了。此外,只要浏览器内置支持,它就会自动使用GPU硬件(Cuda、Intel Qsv等)加速。如果它在真实世界的应用程序中运行,这一点非常重要。昨天我进行了测试,仅使用我的旧i7机器CPU的5%来解码4K(比1080p多4倍)IP摄像头H.264 nal原始实时流。我不确定它是否在类似于nodejs的服务器端js下运行,但我希望结果是相似的。如果您需要更多信息,请与我联系。关于H.265 / HEVC,您需要使用emscripten部分地使用ffmpeg或x265或OpenH265类似的工具,并且尽可能减小bc大小(取决于配置小于1,2,3 M)。祝好运...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接