Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Streaming video from an image using FFMPEG on Windows

Tags:

windows

ffmpeg

I wrote a program that simulates a camera and converts the output into a video stream. The program is required to be able to run on Windows. There are two components in the system:

  1. Camera Simulator. A C++ program that simulates the camera. It copies a pre-generated frame (i.e. PNG file) every 0.1 seconds, using the windows copy command, to a destination path ./target/target_image.png
  2. Video Stream. Using FFmpeg, it creates a video stream out of the copied images. FFmpeg is ran with the following command: ffmpeg -loop 1 -i ./target/target_image.png -r 10 -vcodec mpeg4 -f mpegts udp://127.0.0.1:1234

When running the whole thing together, it works fine for a few seconds until the ffmpeg halts. Here is a log while running in debug mode:

ffmpeg version N-52458-gaa96439 Copyright (c) 2000-2013 the FFmpeg developers
  built on Apr 24 2013 22:19:32 with gcc 4.8.0 (GCC)
  configuration: --enable-gpl --enable-version3 --disable-w32threads --enable-avisynth --enable-bzlib --enable-fontconfig --enable-frei0r --enable-gnutls --enable-iconv --enable-libass --enable-libbluray --enable-libcaca --enable-libfreetype --enable-libgsm --enable-libilbc --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-librtmp --enable-libschroedinger --enable-libsoxr --enable-libspeex --enable-libtheora --enable-libtwolame --enable-libvo-aacenc --enable-libvo-amrwbenc --enable-libvorbis --enable-libvpx --enable-libx264 --enable-libxavs --enable-libxvid --enable-zlib
  libavutil      52. 27.101 / 52. 27.101
  libavcodec     55.  6.100 / 55.  6.100
  libavformat    55.  3.100 / 55.  3.100
  libavdevice    55.  0.100 / 55.  0.100
  libavfilter     3. 60.101 /  3. 60.101
  libswscale      2.  2.100 /  2.  2.100
  libswresample   0. 17.102 /  0. 17.102
  libpostproc    52.  3.100 / 52.  3.100
Splitting the commandline.
Reading option '-loop' ... matched as AVOption 'loop' with argument '1'.
Reading option '-i' ... matched as input file with argument './target/target_image.png'.
Reading option '-r' ... matched as option 'r' (set frame rate (Hz value, fraction or abbreviation)) with argument '10'.
Reading option '-vcodec' ... matched as option 'vcodec' (force video codec ('copy' to copy stream)) with argument 'mpeg4'.
Reading option '-f' ... matched as option 'f' (force format) with argument 'mpegts'.
Reading option 'udp://127.0.0.1:1234' ... matched as output file.
Reading option '-loglevel' ... matched as option 'loglevel' (set logging level) with argument 'debug'.
Finished splitting the commandline.
Parsing a group of options: global .
Applying option loglevel (set logging level) with argument debug.
Successfully parsed a group of options.
Parsing a group of options: input file ./target/target_image.png.
Successfully parsed a group of options.
Opening an input file: ./target/target_image.png.
[AVIOContext @ 02678840] Statistics: 234307 bytes read, 0 seeks
[AVIOContext @ 02678840] Statistics: 221345 bytes read, 0 seeks
    Last message repeated 1 times
[AVIOContext @ 02678840] Statistics: 226329 bytes read, 0 seeks
    Last message repeated 2 times
[AVIOContext @ 02678840] Statistics: 228676 bytes read, 0 seeks
    Last message repeated 2 times
[AVIOContext @ 02678840] Statistics: 230685 bytes read, 0 seeks
    Last message repeated 2 times
[AVIOContext @ 02678840] Statistics: 232697 bytes read, 0 seeks
    Last message repeated 5 times
[AVIOContext @ 02678840] Statistics: 234900 bytes read, 0 seeks
    Last message repeated 2 times
[AVIOContext @ 02678840] Statistics: 236847 bytes read, 0 seeks
[image2 @ 02677ac0] Probe buffer size limit of 5000000 bytes reached
Input #0, image2, from './target/target_image.png':
  Duration: 00:00:00.04, start: 0.000000, bitrate: N/A
    Stream #0:0, 22, 1/25: Video: png, rgb24, 1274x772 [SAR 1:1 DAR 637:386], 1/25, 25 fps, 25 tbr, 25 tbn, 25 tbc
Successfully opened the file.
Parsing a group of options: output file udp://127.0.0.1:1234.
Applying option r (set frame rate (Hz value, fraction or abbreviation)) with argument 10.
Applying option vcodec (force video codec ('copy' to copy stream)) with argument mpeg4.
Applying option f (force format) with argument mpegts.
Successfully parsed a group of options.
Opening an output file: udp://127.0.0.1:1234.
Successfully opened the file.
[graph 0 input from stream 0:0 @ 02769280] Setting 'video_size' to value '1274x772'
[graph 0 input from stream 0:0 @ 02769280] Setting 'pix_fmt' to value '2'
[graph 0 input from stream 0:0 @ 02769280] Setting 'time_base' to value '1/25'
[graph 0 input from stream 0:0 @ 02769280] Setting 'pixel_aspect' to value '1/1'
[graph 0 input from stream 0:0 @ 02769280] Setting 'sws_param' to value 'flags=2'
[graph 0 input from stream 0:0 @ 02769280] Setting 'frame_rate' to value '25/1'
[graph 0 input from stream 0:0 @ 02769280] w:1274 h:772 pixfmt:rgb24 tb:1/25 fr:25/1 sar:1/1 sws_param:flags=2
[format @ 02768ba0] compat: called with args=[yuv420p]
[format @ 02768ba0] Setting 'pix_fmts' to value 'yuv420p'
[auto-inserted scaler 0 @ 02768740] Setting 'w' to value '0'
[auto-inserted scaler 0 @ 02768740] Setting 'h' to value '0'
[auto-inserted scaler 0 @ 02768740] Setting 'flags' to value '0x4'
[auto-inserted scaler 0 @ 02768740] w:0 h:0 flags:'0x4' interl:0
[format @ 02768ba0] auto-inserting filter 'auto-inserted scaler 0' between the filter 'Parsed_null_0' and the filter 'format'
[AVFilterGraph @ 026772c0] query_formats: 4 queried, 3 merged, 1 already done, 0 delayed
[auto-inserted scaler 0 @ 02768740] w:1274 h:772 fmt:rgb24 sar:1/1 -> w:1274 h:772 fmt:yuv420p sar:1/1 flags:0x4
[mpeg4 @ 02785020] detected 4 logical cores
[mpeg4 @ 02785020] intra_quant_bias = 0 inter_quant_bias = -64
[mpegts @ 0277da40] muxrate VBR, pcr every 1 pkts, sdt every 200, pat/pmt every 40 pkts
Output #0, mpegts, to 'udp://127.0.0.1:1234':
  Metadata:
    encoder         : Lavf55.3.100
    Stream #0:0, 0, 1/90000: Video: mpeg4, yuv420p, 1274x772 [SAR 1:1 DAR 637:386], 1/10, q=2-31, 200 kb/s, 90k tbn, 10 tbc
Stream mapping:
  Stream #0:0 -> #0:0 (png -> mpeg4)
Press [q] to stop, [?] for help
*** drop!
    Last message repeated 10 times
frame=   11 fps=0.0 q=4.0 size=     118kB time=00:00:01.10 bitrate= 875.1kbits/s dup=0 drop=11    
Statistics: 242771 bytes read, 0 seeks
[AVIOContext @ 02674a60] Statistics: 246525 bytes read, 0 seeks
*** drop!
[AVIOContext @ 02674a60] Statistics: 230678 bytes read, 0 seeks
[AVIOContext @ 02674a60] Statistics: 244023 bytes read, 0 seeks
*** drop!
[AVIOContext @ 02674a60] Statistics: 246389 bytes read, 0 seeks

*** drop!
[AVIOContext @ 02674a60] Statistics: 224478 bytes read, 0 seeks
[AVIOContext @ 02674a60] Statistics: 228013 bytes read, 0 seeks
*** drop!
[image2 @ 02677ac0] Could not open file : ./target/target_image.png
./target/target_image.png: Input/output error
[output stream 0:0 @ 02768c20] EOF on sink link output stream 0:0:default.
No more output streams to write to, finishing.
frame=  164 fps= 17 q=31.0 Lsize=     959kB time=00:00:16.40 bitrate= 478.9kbits/s dup=0 drop=240    

video:869kB audio:0kB subtitle:0 global headers:0kB muxing overhead 10.285235%
404 frames successfully decoded, 0 decoding errors
[AVIOContext @ 026779c0] Statistics: 0 seeks, 746 writeouts

It seems to me there's some kind of collision between the reading and writing to/from the same file. What's also interesting is that on Linux (while replacing the copy with cp) the program works just fine.

Can someone suggest a way to solve this issue? Alternatives solutions are also acceptable as long as the logical workflow remains the same.

like image 820
Daniel Zohar Avatar asked May 16 '13 06:05

Daniel Zohar


People also ask

Can FFmpeg work with images?

FFmpeg looks for image files with file names with 'img-' resulting in a two-digit number. In the command section, you will need to define a search pattern for the ffmpeg output file so that you can find the image sequence.


2 Answers

Following Nick van Tilborg's comment, I ended up using FFmpeg's image2pipe. This feature allows streamlining the image data into FFmpeg, instead of two processes accessing the same file simultaneously.

Below is the C++ code I wrote. It might not be fully optimized, but it does the job. It was compiled and tested with Visual Studio 2012 on Windows 7.

#include "stdafx.h"
#include "windows.h"
#include "iostream"

#include "stdio.h"

using namespace std;

#pragma warning (disable : 4996)
int _tmain(int argc, _TCHAR* argv[])
{
    int count;
    int times = 2200;
    FILE *pPipe;
    FILE * pFile;
    long lSize;
    char * buffer;
    size_t result;

    // open a pipe to FFmpeg
    if( (pPipe = _popen( "ffmpeg -re -f image2pipe -vcodec mjpeg -i - -vcodec h264 -r 10 -f mpegts udp://127.0.0.1:1234", "wb")) == NULL ) {exit( 1 );}

    for ( count = 1; count <= times; count++) {
        char filename[40];
        sprintf(&filename[0], ".\\images\\image-%07d.jpg", count);

        pFile = fopen ( filename , "rb" );
        if (pFile==NULL) {fputs ("File error",stderr); exit (2);}

        // obtain file size:
        fseek (pFile , 0 , SEEK_END);
        lSize = ftell (pFile);
        rewind (pFile);

        // allocate memory to contain the whole file:
        buffer = (char*) malloc (sizeof(char)*lSize);
        if (buffer == NULL) {fputs ("Memory error",stderr); exit (3);}

        // copy the file into the buffer:
        result = fread (buffer, 1, lSize, pFile);
        if (result != lSize) {fputs ("Reading error",stderr); exit (4);}

        // write to pipe
        fwrite(buffer, 1, lSize, pPipe); 
        fflush(pPipe);

        // clean
        fclose (pFile);
        free (buffer);

        //
        Sleep(100);
    }

    //
    return 0;
}
like image 95
Daniel Zohar Avatar answered Oct 18 '22 13:10

Daniel Zohar


Using your current command line, FFmpeg is running at the highest possible speed. This means FFmpeg is reading the frames not at 10 fps, but as fast as possible. It is also not transmitting the transport stream at 10 fps. Because your program is fixed writing frames at 10 fps, this is the reason you probably can't read your file sometimes, since the file is writed at these particular times.

To solve this, try using the -re flag in your FFmpeg command line to force FFmpeg to read the input at the native framerate.

ffmpeg -re -loop 1 -i ./target/target_image.png -r 10 -vcodec mpeg4 -f mpegts udp://127.0.0.1:1234

like image 29
Nick van Tilborg Avatar answered Oct 18 '22 14:10

Nick van Tilborg