在Windows上使用FFMPEG从图像流式传输视频

时间:2013-05-16 06:32:40

标签: windows ffmpeg

我编写了一个模拟相机并将输出转换为视频流的程序。该程序必须能够在Windows上运行。 系统中有两个组件:

  1. 相机模拟器。一个模拟相机的C ++程序。它使用windows copy命令每0.1秒将预先生成的帧(即PNG文件)复制到目标路径./target/target_image.png
  2. 视频流。使用FFmpeg,它可以从复制的图像中创建视频流。使用以下命令运行FFmpeg: ffmpeg -loop 1 -i ./target/target_image.png -r 10 -vcodec mpeg4 -f mpegts udp://127.0.0.1:1234
  3. 当一起运行整个过程时,它会正常工作几秒钟,直到ffmpeg停止。这是在调试模式下运行时的日志:

    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
    

    在我看来,读/写同一文件之间存在某种冲突。有趣的是,在Linux上(将copy替换为cp时),该程序运行正常。

    有人可以提出解决此问题的方法吗?只要逻辑工作流程保持不变,替代解决方案也是可接受的。

2 个答案:

答案 0 :(得分:4)

在Nick van Tilborg的评论之后,我最终使用了FFmpeg的image2pipe。此功能允许将图像数据简化为FFmpeg,而不是同时访问同一文件的两个进程。

下面是我写的C ++代码。它可能没有完全优化,但它完成了工作。它是在Windows 7上使用Visual Studio 2012编译和测试的。

#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;
}

答案 1 :(得分:2)

使用当前命令行,FFmpeg以尽可能高的速度运行。这意味着FFmpeg不是以10 fps的速度读取帧,而是尽可能快地读取帧。它也不以10 fps传输传输流。因为您的程序是以10 fps的速度固定写入帧,所以这有时候您可能无法读取文件,因为文件是在这些特定时间写入的。

要解决此问题,请尝试使用FFmpeg命令行中的-re标志强制FFmpeg以本机帧速率读取输入。

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