直接播放記憶體內的 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
}