2013年3月7日 星期四

FFMPEG -- 直接撥放記憶體內的資料


直接播放記憶體內的 video 是一個很方便的技巧,例如:我們可以從網路上持續的接收串流資料,放置在記憶體內,再要求ffmpeg播放記憶體內的資料。
參考 http://stackoverflow.com/questions/9604633/reading-a-file-located-in-memory-with-libavformat
其實作方式是註冊自行撰寫的 readFunction, 用來讀取放在記憶體中的 video data。

下列程式碼是在 iFrameExtractor project 上所作的修改,
我將一個小尺寸的影像檔直接放到記憶體中,然後要求 ffmpeg 直接讀取此段記憶體進行影像播放,
預期此作法應該可以減少 I/O 的時間,實際測試結果後也的確是如此。



typedef struct tFileX { int64_t FileSize; int64_t FilePosition; unsigned char *pBuffer; }tFileX; int readFunction(void* opaque, uint8_t* buf, int buf_size) { tFileX *vpFileX = (tFileX *) opaque; if((vpFileX->FileSize - vpFileX->FilePosition) > buf_size) { memcpy(buf, vpFileX->pBuffer + vpFileX->FilePosition, buf_size); vpFileX->FilePosition += buf_size; return buf_size; } else { memcpy(buf, vpFileX->pBuffer + vpFileX->FilePosition, (vpFileX->FileSize - vpFileX->FilePosition) ); vpFileX->FilePosition = vpFileX->FileSize; return (vpFileX->FileSize - vpFileX->FilePosition); } } int64_t seekFunction(void* opaque, int64_t offset, int whence) { tFileX *vpFileX = (tFileX *) opaque; if (whence == AVSEEK_SIZE) return vpFileX->FileSize; else if (whence == SEEK_SET) vpFileX->FilePosition = offset; else if (whence == SEEK_CUR) vpFileX->FilePosition += offset; else if (whence == SEEK_END) vpFileX->FilePosition = vpFileX->FileSize - offset; return vpFileX->FilePosition; } -(id)initWithVideoMemory:(NSString *)moviePath { AVCodec *pCodec; int ioBufferSize=0; tFileX *vpFileX=malloc(sizeof(tFileX)); memset(vpFileX, 0, sizeof(tFileX)); // Register all formats and codecs avcodec_register_all(); av_register_all(); // Get File size and read to the buffer if (moviePath) { NSData *pData = [NSData dataWithContentsOfFile:moviePath]; if (pData) { vpFileX->FilePosition =0 ; vpFileX->FileSize = [pData length]; vpFileX->pBuffer = (unsigned char*) av_malloc(vpFileX->FileSize); memcpy(vpFileX->pBuffer , [pData bytes], vpFileX->FileSize); NSLog(@"filesize =%d", vpFileX->FileSize); } } ioBufferSize = 10240;//32768; unsigned char * ioBuffer = (unsigned char *)av_malloc(ioBufferSize + FF_INPUT_BUFFER_PADDING_SIZE); // can get av_free()ed by libav AVIOContext * avioContext = avio_alloc_context(ioBuffer, ioBufferSize, 0, (void*)vpFileX, &readFunction, NULL, &seekFunction); pFormatCtx = avformat_alloc_context(); pFormatCtx->pb = avioContext; // Open video file if(avformat_open_input(&pFormatCtx, "dummyFileName", NULL, NULL) != 0) { av_log(NULL, AV_LOG_ERROR, "Couldn't open file\n"); } //... 以下省略, 請直接參考 https://github.com/lajos/iFrameExtractor }