How to successfully parse FFMpeg output in NodeJS

So, I've seen a lot of topics on FFMPeg and it's a great tool that I learned today, but I spent a day perfecting the team and am now a little stuck with the NodeJS part.

Basically, the command does the following: takes data from a Mac OSX webcam and then sends it to a web socket. Now I have looked through many NodeJS libraries but could not find one that did what I needed; or didn't understand how to do it. Below is an example of the command 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 to stream things, but I want to call it via NodeJS and then follow the log and parse the data that is returned, e.g .:

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 to output to a web page.

Now everything I do is working on ways to actually parse the data, and I've looked at many other answers for things like this, but I can't seem to split / replace / reapply. I can't get anything but a long string.

Here is the code I'm 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've tried splitting with newlines, curry back, spaces, tabs, but I just can't get the underlying array of bits I can work with.

One more note, will you notice the log is being returned via stderr, I've seen this on the internet and apparently it does it for a lot of people? So I'm not sure what the deal is? but the code is sdterr callback.

Any help is greatly appreciated as I am really confused as to what I am doing wrong.

Thank.

+3


source to share


2 answers


Update on this, I worked with one of the guys on the IRC channel: #ffmpeg on FreeNode. The answer was to send the output through a pipe to standard output.

For example, I added the following FFMpeg command:

-progress pipe:1

      

The progress flag is used to print information about the stream every time, so that's pretty much all you get from the stderr stream every second, but gets passed to the stdout stream in a format that I can parse. Below is taken from the documentation.



-progress url (global) Submit program compatibility information for the url. Progress information is recorded approximately every second and at the end of the encoding process. It is made of key = value strings. the key consists of alphanumeric characters only. The last key to sequence the progress information is always "progress".

Here is some sample code that I used to parse 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 everyone for commenting!

+1


source


In the spirit of not reinventing the wheel, you can try using fluent-ffmpeg . It dispatches a progress event with a number of useful fields

"progress": information about the progress of transcoding

A progress event is emitted every time ffmpeg reports progress Information. It is emitted with an object argument with the following Keys:

  • frames: total number of frames processed
  • currentFps: the frame rate at which FFmpeg is currently processing
  • currentKbps: The bandwidth that FFmpeg is currently handling
  • targetSize: the current size of the target file in kilobytes
  • timemark: timestamp of the current frame in seconds
  • percent: an estimate of the percentage of progress

If you are wondering how they do it, you can read the source code starting at here

Ffmpeg uses stderr to output log information as stdout is used to pipe output to other processes. The stuff in stderr is actually just debug information, not the actual output of the process.


BONUS ROUND

I've seen some hacky video players that use web ports to stream video, but this approach has a number of problems with it. I'm not going to overcome them, but I will explain why I think you should use hls.js .



The support is very good; basically works everywhere except old IE. It uses MSE to update the standard video element, so you don't have to struggle with creating a custom player.

Here are the docs for the hls format flag

Here is some code I am using to stream from IPTV window to webpage.

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

      

It generates a .m3u8 manifest file along with segmented mpeg-ts video files. All you have to do is load the m3u8 file into the hls.js player and you have a live stream!

If you are going to recode a stream, you will probably see some low fps and glitchiness. I got lucky as the original stream is already encoded as mpeg-ts.

+1


source







All Articles