通过CustomIO实现ffmpeg内存输入

来源:互联网 发布:淘宝返利网站有哪些 编辑:程序博客网 时间:2024/05/22 04:24

Introduction 

In this short Article I will explain how to use a custom IO-Context with FFmpeg. Although I used an IStream Object the code can be used for other Streams like std::istream.  

FFmpeg can only read from files or named pipes easily, but if you want to read directly from memory, from sockets or IStreams you have to provide a custom IO-Context. I could not find any resource in the internet which offeres a complete and working solution with the current version of FFmpeg explaining how to deal correctly with an IO-Context.  After some hours of experimentation I finally managed to get this working without getting access-violations in FFmpeg functions.

Creating the IO-Context     

FFmpeg uses a custom IO-Context, when you allocate the AVFormatContext-structure yourself and provide your own version of AVIOContext but there are several other things to consider. At first we will create the AVIOContext and the AVFormatContext structures. The size of the internal buffer is up to you, I decided to provide 32kb for internal buffering. The two functions ReadFunc and SeekFunc are shown later.

typedef struct CustomStream {

    uint64_t pos_cur;

    uint64_t length;

    uint8_t *buf;

} CustomStream;

// CustomStream-Interface that was already created elsewhere:CustomStream* pInStream; // Create internal Buffer for FFmpeg:const int iBufSize = 32 * 1024;BYTE* pBuffer = new BYTE[iBufSize]; // Allocate the AVIOContext:// The fourth parameter (pStream) is a user parameter which will be passed to our callback functionsAVIOContext* pIOCtx = avio_alloc_context(pBuffer, iBufSize,  // internal Buffer and its size                                         0,                  // bWriteable (1=true,0=false)                                          pInStream,          // user data ; will be passed to our callback functions                                         ReadFunc,                                          0,                  // Write callback function (not used in this example)                                          SeekFunc);  // Allocate the AVFormatContext:AVFormatContext* pCtx = avformat_alloc_context(); // Set the IOContext:pCtx->pb = pIOCtx;

Note: As you can see, the custom IO-Context can also be used for writing, but this is not explained here.

Now you have to tell FFmpeg, which input format it has to use. For a custom IO-Context this is necessary! FFmpeg will otherwise read about 5Mb data from the stream by default to determine the input format. By doing this, FFmpeg will crash because of a buffer overrun. I have not tested whether it will work, if the internal buffer is large enough to hold the 5Mb data because it was easier for me to determine the input format on my own.  

// Determining the input format:ULONG ulReadBytes = 0;if(FAILED(pInStream->Read(pBuffer, iBufSize, &ulReadBytes)))    // Error Handling...// Don't forget to reset the data pointer back to the beginning!if(FAILED(pInStream->Seek(0, SEEK_SET)))    // Error Handling...// Now we set the ProbeData-structure for av_probe_input_format:AVProbeData probeData;probeData.buf = pBuffer;probeData.buf_size = ulReadBytes;probeData.filename = ""; // Determine the input-format:pCtx->iformat = av_probe_input_format(&probeData, 1);

The last thing to do is to set the flags of the AVFormatContext. This is not directly mentioned in the documentation and although FFmpeg realizes that you have set your own AVIOContext you have to set the AVFMT_FLAG_CUSTOM_IO-flag on your own. 

pCtx->flags = AVFMT_FLAG_CUSTOM_IO;

Now we use the avformat_open_input function to tell FFmpeg that it can start to read from the stream.

if(avformat_open_input(&pCtx, "", 0, 0)) != 0)    // Error Handling

The second parameter of avformat_open_input is the filename. This is not used, because we want to use the custom IO-Context. Older versions of FFmpeg will crash if you pass 0 as filename instead of "". This issue was fixed in newer versions of FFmpeg.  

The callback functions ReadFunc and SeekFunc are easily implemented:  

int ReadFunc(void* ptr, uint8_t* buf, int buf_size){    

    CustomStream* pStream = (CustomStream *)ptr;

    if (pStream->pos_cur >= pStream->length) {

        return 0;

    }

    int readBytes = buf_size;

    if (pStream->pos_cur + buf_size > pStream->length) {

        readBytes = pStream->length - pStream->pos_cur;

    }

    memcpy(buf, pStream->buf + pStream->pos_cur, readBytes);

    pStream->pos_cur += readBytes;

    return readBytes;

}// whence: SEEK_SET, SEEK_CUR, SEEK_END (like fseek) and AVSEEK_SIZEint64_t SeekFunc(void* ptr, int64_t pos, int whence){ // Quelle Abfragen: CustomStream* pStream = reinterpret_cast<IStream*>(ptr); // Seek: LARGE_INTEGER in = { pos }; ULARGE_INTEGER out = { 0 }; if(FAILED(pStream->Seek(in, whence, &out))) return -1; // Return the new position: return out.QuadPart;}

 The whence-parameter has one  more option than fseek: AVSEEK_SIZE. When this option is passed to the seek function it should return the file size (if possible).  If its not possible, the function may return and do nothing -1. In my implementation pStream->Seek(...) will fail with AVSEEK_SIZE and SeekFunc will return -1.

Freeing resources

One last comment on which functions are to use to release all the allocated resources:

avformat_close_input(pCtx);  // AVFormatContext is released by avformat_close_inputav_free(pIOCtx);             // AVIOContext is released by av_freedelete[] pBuffer; 
0 0
原创粉丝点击