Bohl's Blog

my digital life

Driving ffmpeg II

OK, the idea of using ffmpeg as a stand-alone executable and feeding images into via a named pipe seemed simple and straight forward. However, as usual, things are not that simple... I will try to walk through some of the obstacles that I have come across (but this for sure uncomplete).

My first idea was that I should be able to pipe anything that ffmpeg could deal with. However, it turned out, that the only format which worked (for me) is yuv4mpegpipe. All other attempts did not work, ffmpeg complained in various ways.

So, the command I am using to launch ffmpeg is:

ffmpeg -f yuv4mpegpipe -i {0} output.mov

For {0} the name of the pipe has to be filled in, something like \\.\pipe\mypipe.

Now, we need to find out what the format yuv4mpegpipe looks like. It starts with a header

YUV4MPEG2 W720 H576 F25:1 Ip A0:0 C420mpeg2 XYSCSS=420MPEG2\n

Here we have to give the width and height, the framerate (as a fraction), the aspect ratio (or 0:0 for unspecified). This line has to transmitted once. Then what follows is a magic string

FRAME\n

And now the image data follows (for one frame), in binary format: it is a 4:2:0 format, first the Y component, then followed by V and then U - usually called YV12 (see e.g. here). Creating this format is simple enough, especially if you have IPP at your hand - ippiBGRToYCbCr420 or one of its cousins will do the job. Now the following frame is again started with the magic "FRAME\n".

One caveat I ran across: make sure that you add the first two lines (i.e. the line starting with YUV4MPEG2 and the magic "FRAME\n") in one write call! Otherwise ffmpeg is complaining and it does not work.

So, basically all what has to be done is

serverStream = new NamedPipeServerStream(
                                 this.PipeName,
                                 PipeDirection.Out,
                                 1,
                                 PipeTransmissionMode.Byte, 
                                 PipeOptions.Asynchronous,
                                 65535,                      
                                 65535);

// now launch ffmpeg

serverStream.WaitForConnection();

// now pump data into the pipe with serverStream.Write

So far, so good - and this solves my task at hand. However - since I got this far, I also wanted to pipe audio into ffmpeg. To make a long story short - I did not get this to work properly so far. The trouble starts with the following: if we simply add a second input like so

ffmpeg -f yuv4mpegpipe  -i {0} -f s16le -ar 48000 -ac 2 -i {1} output.mov

then ffmpeg does not open the second file (or pipe) immediately, so we run into trouble with WaitForConnection. The file is opened after we have added some frames, so we can work around this. However - I did not manage to make it work reliably. 

Kommentar schreiben

Loading