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

6
所以我看到了很多关于FFMPeg的话题,这是我今天学到的一个很好的工具,但是我花了一整天完善命令,现在在NodeJS部分卡住了一点。
实质上,该命令执行以下操作:从Mac OSX摄像头输入,并将其流式传输到WebSocket。尽管我查看了很多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数组,并将其输出到网页上。

目前,我正在研究解析数据的方法,我已经查看了很多其他答案,但似乎无法使用split/replace/regex等方法进行分割。我只能得到一个长字符串。

以下是我正在使用的代码(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作为流消耗,看看是否有帮助。 - Robert Rowntree
谢谢,这是一个可读流,只是不知道为什么它使用stderr输出,但实际返回值无法解析。 - Dahknee
2个回答

10

关于这个问题的更新,我和FreeNode上IRC频道#ffmpeg的一个人合作解决了。答案是通过管道将输出发送到标准输出。

例如,我将以下内容附加到FFMpeg命令中:

-progress pipe:1

进度标识用于每秒输出流信息,因此这基本上是您从stderr流中每秒获得的所有内容,但以我可以解析的格式发送到stdout流中。以下内容摘自文档。

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

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

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

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

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

});

感谢所有留言者!

1
还可以使用 ffmpeg -hide_banner -loglevel error 命令来隐藏冗长的输出。 - Tran Chien

2
为了不重复造轮子,您可能想尝试使用fluent-ffmpeg。它会发送一个progress event,其中包含许多有用的字段。
引用块: 'progress': 转码进度信息 每次FFmpeg报告进度信息时都会发出进度事件。它带有一个对象参数,其中包含以下键: - frames:已处理的总帧数 - currentFps:FFmpeg当前处理的帧速率 - currentKbps:FFmpeg当前处理的吞吐量 - targetSize:目标文件的当前大小(以千字节为单位) - timemark:当前帧的时间戳(以秒为单位) - percent:进度百分比的估计值
如果你对他们是如何实现这一点感到好奇,你可以阅读源代码,从这里开始这里
Ffmpeg使用stderr输出日志信息,因为stdout用于将输出传输到其他进程。stderr中的内容实际上只是调试信息,而不是进程的实际输出。
奖励回合
我见过一些使用Websockets来流式传输视频的技巧性视频播放器,但这种方法有很多问题。我不打算详细讨论这些问题,但我会解释为什么我认为你应该使用hls.js
支持相当好;基本上在除老IE之外的所有地方都有效。它使用MSE来升级标准视频元素,因此您无需与构建自定义播放器争斗。
这里是hls格式标志的文档。
这是我用来从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播放器中,就可以进行实时流!如果您要重新编码流,则可能会看到一些低帧率和故障。我很幸运,因为我的源流已经编码为mpeg-ts。

这里有一个将流媒体转换为HLS格式的示例:https://github.com/fluent-ffmpeg/node-fluent-ffmpeg/blob/master/examples/livertmp2hls.js - posit labs
在写 .save() 的时候,你觉得我可以在里面加上 websocket 的 url 吗?而且,不太确定 hls 是什么?.addOption 等是用来添加更多命令的吗? - Dahknee
啊,好的,谢谢!你所说的奖励内容是什么意思?WebSocket方式似乎相当稳定,我无法想到更好的方法来完成这个任务。毕竟我们需要向大量用户广播信息。我们在公司内部进行了测试,能够让50人同时使用,延迟不到一秒钟,而且这还是在外部服务器上进行的。唯一的问题是Safari iOS中的音频... :/ - Dahknee
我还是新手,但如果我要使用视频流播放器进行生产,我可能会将mpeg-ts和m3u8文件上传到云存储并从那里提供服务。我想静态文件服务器的性能比Websockets要好得多。你的Websocket方法可能还可以,但你如何处理回放?Canvas和WebAudio?当然,本地播放器更有效率。 - posit labs
问题在于我们需要实时流传输音频,这不仅仅是一个网页播放器,而是一个实时的网络摄像头和音频流。当然,对于静态视频的服务,我完全同意,但对于实时流传输则不适用。我正在使用jsmpeg和canvas进行实时流传输。 - Dahknee
显示剩余5条评论

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