How to successfully parse the output of FFMpeg in NodeJS

Danny SMc picture Danny SMc · May 19, 2017 · Viewed 8k times · Source

So I have seen a lot of topics on FFMPeg and it's a great tool I learnt about today, but I have spent the day perfecting the command and now am a little stuck with the NodeJS part.

In essence the command does the following: take input from a Mac OSX webcam, and then stream it to a web-socket. Now I looked at a lot of the NodeJS libraries but I couldn't find one that did what I need; or did not understand how to. Here is an example of the command that I am using:

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

This does everything I need for the streaming side of things, but I wish to call it via NodeJS, and then be able to monitor the log, and parse the data that comes back for example:

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

and use it to get a JSON array back for me to output to a webpage.

Now all I am doing is working on ways of actually parsing the data, and I have looked at lots of other answers for things like this, but I can't seem to split/replace/regex it. I can't get anything but a long string from it.

Here is the code I am using (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);
});

I have tried splitting with new lines, carridge return, spaces, tabs, but I just can't seem to get a basic array of bits, that I can work with.

Another thing to note, is you will notice the log comes back via stderr, I have seen this online and apparently it does it for a lot of people? So I am not sure what the deal is with that? but the code is is the sdterr callback.

Any help is very appreciated as I am truly confused on what I am doing wrong.

Thanks.

Answer

Danny SMc picture Danny SMc · May 22, 2017

An update on this, I worked with one of the guys off the IRC channel: #ffmpeg on FreeNode. The answer was to send the output via pipe to stdout.

For example I appended the following to the FFMpeg command:

-progress pipe:1

The progress flag is used to give an output every second with information about the stream, so this is pretty much everything you get from the stderr stream every second, but piped to the stdout stream in a format that I can parse. Below is taken from the documentation.

-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".

Here is an example of the code I used to parse the stream information:

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

});

Thanks to all that commented!