如何在 NodeJS 中成功解析 FFMpeg 的输出

Posted

技术标签:

【中文标题】如何在 NodeJS 中成功解析 FFMpeg 的输出【英文标题】:How to successfully parse the output of FFMpeg in NodeJS 【发布时间】:2017-10-19 21:52:33 【问题描述】:

所以我看到了很多关于 FFMPeg 的主题,它是我今天学到的一个很棒的工具,但是我花了一天时间完善命令,现在有点卡在 NodeJS 部分。

本质上,该命令执行以下操作:从 Mac OSX 网络摄像头获取输入,然后将其流式传输到网络套接字。现在我查看了很多 NodeJS 库,但找不到一个满足我需要的库;或者不明白怎么做。这是我正在使用的命令示例:

ffmpeg -f avfoundation -framerate 30 -video_size 640x480 -pix_fmt uyvy422 -i "0:1" -f mpegts -codec:v mpeg1video -s 640x480 -b:v 1000k -bf 0 http://localhost:8081/stream

这可以满足我在流式传输方面所需的一切,但我希望通过 NodeJS 调用它,然后能够监控日志并解析返回的数据,例如:

frame= 4852 fps= 30 q=6.8 size=   30506kB time=00:02:41.74 bitrate=1545.1kbits/s speed=   1x    \r

并使用它来获取一个 JSON 数组,以便我输出到网页。

现在我所做的只是研究实际解析数据的方法,并且我已经查看了很多其他类似的答案,但我似乎无法拆分/替换/正则表达式。除了一长串,我什么也得不到。

这是我正在使用的代码(NodeJS):

var ffmpeg = require('child_process').spawn('/usr/local/Cellar/ffmpeg/3.3.1/bin/ffmpeg', ['-f', 'avfoundation', '-framerate', '30', '-video_size', '640x480', '-pix_fmt', 'uyvy422', '-i', '0:1', '-f', 'mpegts', '-codec:v', 'mpeg1video', '-s', '640x480', '-b:v', '1000k', '-bf', '0', 'http://localhost:8081/test']);

ffmpeg.on('error', function (err) 
    console.log(err);
);

ffmpeg.on('close', function (code) 
    console.log('ffmpeg exited with code ' + code);
);

ffmpeg.stderr.on('data', function (data) 
    // console.log('stderr: ' + data);
    var tData = data.toString('utf8');
    // var a = tData.split('[\\s\\xA0]+');
    var a = tData.split('\n');
    console.log(a);
);

ffmpeg.stdout.on('data', function (data) 
    var frame = new Buffer(data).toString('base64');
    // console.log(frame);
);

我尝试使用新行、回车符、空格、制表符进行拆分,但我似乎无法获得可以使用的基本位数组。

另外需要注意的是,您是否会注意到日志是通过 stderr 返回的,我在网上看到过,显然很多人都这样做了?所以我不确定这是怎么回事?但代码是 sdterr 回调。

非常感谢任何帮助,因为我真的对自己做错了什么感到困惑。

谢谢。

【问题讨论】:

我认为回调中的 stderr 类型是“readable.stream”......你不应该将它用作字符串..尝试查看流接口,然后将 CB.stderr 作为流使用看看是否有帮助 谢谢,是可读流,只是不知道为什么要用stderr输出,但是实际返回是解析不了的。 【参考方案1】:

对此的更新,我与 IRC 频道外的一个人一起工作:FreeNode 上的#ffmpeg。答案是通过管道将输出发送到标准输出。

例如,我在 FFMpeg 命令中附加了以下内容:

-progress pipe:1

进度标志用于每秒输出有关流的信息,因此这几乎是您每秒从 stderr 流中获得的所有内容,但以我可以解析的格式通过管道传输到 stdout 流。以下摘自文档。

-progress url (global) 向 url 发送程序友好的进度信息。进度信息大约每秒写入一次,并且在编码过程结束时写入。它由“key=value”行组成。 key 仅包含字母数字字符。进度信息序列的最后一个键始终是“进度”。

下面是我用来解析流信息的代码示例:

ffmpeg.stdout.on('data', function (data) 

    var tLines = data.toString().split('\n');
    var progress = ;
    for (var i = 0; i < tLines.length; i++) 
        var key = tLines[i].split('=');
        if (typeof key[0] != 'undefined' && typeof key[1] != 'undefined') 
            progress[key[0]] = key[1];
        
    

    // The 'progress' variable contains a key value array of the data
    console.log(progress);

);

感谢所有评论!

【讨论】:

【参考方案2】:

本着不重新发明***的精神,您可能想尝试使用fluent-ffmpeg。它发送带有许多有用字段的progress event

'progress':转码进度信息

每次 ffmpeg 报告进度时都会发出进度事件 信息。它与带有以下内容的对象参数一起发出 键:

帧:处理的总帧数 currentFps:FFmpeg 当前处理的帧率 currentKbps:FFmpeg 当前处理的吞吐量 targetSize:目标文件的当前大小,以千字节为单位 timemark:当前帧的时间戳,以秒为单位 百分比:估计进度百分比

如果您对他们如何做到这一点感到好奇,您可以阅读源代码,starting from here

Ffmpeg 使用 stderr 输出日志信息,因为 stdout 用于将输出通过管道传输到其他进程。 stderr 中的东西实际上只是调试信息,而不是进程的实际输出。


奖金回合

我见过一些使用 websocket 流式传输视频的 hacky 视频播放器,但这种方法存在许多问题。我不打算讨论这些,但我会解释为什么我认为你应该使用hls.js。

支持还不错;除了旧的 IE 之外,基本上可以在任何地方使用。它使用MSE 升级标准视频元素,因此您不必为构建自定义播放器而苦恼。

这是hls format flag的文档

这是我用来从 IPTV 盒流式传输到网页的一些代码。

this.ffmpeg = new FFFmpeg()
this.ffmpeg.input(request(this.http_stream))
    .videoCodec('copy')
    .audioCodec('copy')
    .outputOptions([
        '-f hls',
        '-hls_list_size 6',
        '-hls_flags delete_segments'
    ])
    .output( path.join(this.out_dir, 'video.m3u8') )
    .run()

它会生成一个 .m3u8 清单文件以及分段的 mpeg-ts 视频文件。之后您需要做的就是将 m3u8 文件加载到 hls.js 播放器中,您就有了直播!

如果您要重新编码流,您可能会看到一些低 fps 和故障。我很幸运,因为我的源流已经编码为 mpeg-ts。

【讨论】:

谢谢你,你知道如何映射我正在使用的当前命令来使用它吗?我查看了这个库,但不确定如何真正让它运行命令以获取输入,然后将它们流式传输到 websocket,因为我找不到示例并且示例似乎只是视频...... ?如果是这样,我一定会使用它! 这里有一个流到 hls 格式的例子:github.com/fluent-ffmpeg/node-fluent-ffmpeg/blob/master/… 它说.save(),你认为我可以在那里添加websocket的url吗?嗯,不知道 hls 是什么? .addOption 等,是否只是为命令添加更多内容? 是的,我认为 addOption 只会在适当的位置添加一个标志。我不知道。无论如何,我用更多信息更新了答案 啊,好的,谢谢!你的奖金是什么意思? websocket 方式似乎相当稳定,但我看不到更好的方法吗?无论如何都能够向大量用户广播?我们在工作中对其进行了内部测试,并且能够以不到一秒的延迟让 50 人使用它,而且那是在外部服务器上?我们唯一遇到的问题是 Safari ios 中的音频...:/

以上是关于如何在 NodeJS 中成功解析 FFMpeg 的输出的主要内容,如果未能解决你的问题,请参考以下文章

fs.readFile 无法在 nodejs 中使用 ffmpeg 读取缩略图生成的文件

电子 - 如何在电子应用程序中使用 ffmpeg

ffmpeg nodejs中使用

ffmpeg 可以从文件中的特定位置解析吗?

使用JAVA调用ffmpeg组件进行视频转换时不成功

从 nodejs 向 FFmpeg 发送 2 个流