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

How to successfully parse the output of FFMpeg in NodeJS

所以我看到了很多关于 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 数组,供我输出到网页。

现在我所做的就是研究实际解析数据的方法,并且我已经查看了很多其他类似问题的答案,但我似乎无法 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);
});

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

另一件需要注意的事情,你会注意到日志通过 stderr 返回吗,我在网上看到过这个,显然很多人都这样做了?所以我不确定这有什么关系?但代码是 sdterr 回调。

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

谢谢。

本着不重新发明轮子的精神,您可能想尝试使用 fluent-ffmpeg. It dispatches a progress event 和一些有用的字段

'progress': transcoding progress information

The progress event is emitted every time ffmpeg reports progress information. It is emitted with an object argument with the following keys:

  • frames: total processed frame count
  • currentFps: framerate at which FFmpeg is currently processing
  • currentKbps: throughput at which FFmpeg is currently processing
  • targetSize: current size of the target file in kilobytes
  • timemark: the timestamp of the current frame in seconds
  • percent: an estimation of the progress percentage

如果你想知道他们是怎么做到的,你可以阅读来源,starting from here

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


奖励回合

我见过一些使用 websockets 流式传输视频的 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。

关于这方面的更新,我在 IRC 频道上与其中一个人一起工作:FreeNode 上的#ffmpeg。答案是通过管道将输出发送到标准输出。

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

-progress pipe:1

进度标志用于每秒输出一个关于流的信息,所以这几乎是你每秒从 stderr 流获得的所有内容,但以我可以解析的格式传输到 stdout 流.以下摘自文档。

-progress url (global) Send program-friendly progress information to url. Progress information is written approximately every second and at the end of the encoding process. It is made of "key=value" lines. key consists of only alphanumeric characters. The last key of a sequence of progress information is always "progress".

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

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);

});

感谢所有评论!