使用libavformat读取位于内存中的文件

我目前正在尝试阅读从服务器发送的小型video文件

为了使用libavformat读取文件,您应该调用

av_open_input_file(&avFormatContext, "C:\\path\\to\\video.avi", 0, 0, 0); 

问题是在这种情况下,文件不在磁盘上,而是在内存中。

我现在正在做的是下载文件,使用临时名称将其写入磁盘,然后使用临时文件名调用av_open_input_file ,这不是一个非常干净的解决方案。

事实上,我想要的是像av_open_custom(&avFormatContext, &myReadFunction, &mySeekFunction);这样的函数av_open_custom(&avFormatContext, &myReadFunction, &mySeekFunction); 但我没有在文档中找到任何内容。 我想这在技术上是可行的,因为文件的名称不能帮助库确定它使用的格式。

那么有这样的函数,还是av_open_input_file的替代品?

有趣的是,在我在这个网站上发布问题后,我总是自己找到解决方案,即使我已经在这个问题上工作了好几个小时。

实际上你必须在调用av_open_input之前初始化avFormatContext->pb ,并向它传递一个伪文件名。 这不是在文档中编写的,而是直接在库的源代码中的注释中编写的。

示例代码,如果你想从istream加载(未经测试,只是因为有同样问题的人可以得到这个想法)

 static int readFunction(void* opaque, uint8_t* buf, int buf_size) { auto& me = *reinterpret_cast(opaque); me.read(reinterpret_cast(buf), buf_size); return me.gcount(); } std::ifstream stream("file.avi", std::ios::binary); const std::shared_ptr buffer(reinterpret_cast(av_malloc(8192)), &av_free); const std::shared_ptr avioContext(avio_alloc_context(buffer.get(), 8192, 0, reinterpret_cast(static_cast(&stream)), &readFunction, nullptr, nullptr), &av_free); const auto avFormat = std::shared_ptr(avformat_alloc_context(), &avformat_free_context); auto avFormatPtr = avFormat.get(); avFormat->pb = avioContext.get(); avformat_open_input(&avFormatPtr, "dummyFilename", nullptr, nullptr); 

这是一个很好的信息并且帮助了我很多,但是人们应该注意一些问题。 libavformat可以并且会混淆你给avio_alloc_context的缓冲区。 这导致非常烦人的双重错误或可能的内存泄漏。 当我开始搜索问题时,我找到了https://lists.ffmpeg.org/pipermail/libav-user/2012-December/003257.html ,它完美地将其钉住了。

清理这项工作时我的工作方法就是继续打电话

  av_free(avioContext->buffer) 

然后将您自己的缓冲区指针(您为avio_alloc_context调用分配的)设置为NULL,如果您愿意的话。

Tomaka17的优秀答案为我使用Qt QIODevice而不是std :: istream解决类似问题提供了良好的开端。 我发现我需要将Tomaka17解决方案的各个方面与http://cdry.wordpress.com/2009/09/09/using-custom-io-callbacks-with-ffmpeg/相关经验相结合。

我的自定义读取function如下所示:

 int readFunction(void* opaque, uint8_t* buf, int buf_size) { QIODevice* stream = (QIODevice*)opaque; int numBytes = stream->read((char*)buf, buf_size); return numBytes; } 

…但我还需要创建一个自定义Seekfunction:

 int64_t seekFunction(void* opaque, int64_t offset, int whence) { if (whence == AVSEEK_SIZE) return -1; // I don't know "size of my handle in bytes" QIODevice* stream = (QIODevice*)opaque; if (stream->isSequential()) return -1; // cannot seek a sequential stream if (! stream->seek(offset) ) return -1; return stream->pos(); } 

……我把它绑在一起像这样:

 ... const int ioBufferSize = 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*)(&fileStream), &readFunction, NULL, &seekFunction); AVFormatContext * container = avformat_alloc_context(); container->pb = avioContext; avformat_open_input(&container, "dummyFileName", NULL, NULL); ... 

注意我还没有解决内存管理问题。