32

我目前正在尝试读取从服务器发送的小视频文件

为了使用 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_input_file 的替代方法?

4

3 回答 3

43

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

实际上,您必须avFormatContext->pb在调用 之前进行初始化av_open_input,并将假文件名传递给它。这不是写在文档中,而是直接在库的源代码中的注释中。

示例代码,如果您想从 istream 加载(未经测试,以便有相同问题的人可以理解)

static int readFunction(void* opaque, uint8_t* buf, int buf_size) {
    auto& me = *reinterpret_cast<std::istream*>(opaque);
    me.read(reinterpret_cast<char*>(buf), buf_size);
    return me.gcount();
}

std::ifstream stream("file.avi", std::ios::binary);

const std::shared_ptr<unsigned char> buffer(reinterpret_cast<unsigned char*>(av_malloc(8192)), &av_free);
const std::shared_ptr<AVIOContext> avioContext(avio_alloc_context(buffer.get(), 8192, 0, reinterpret_cast<void*>(static_cast<std::istream*>(&stream)), &readFunction, nullptr, nullptr), &av_free);

const auto avFormat = std::shared_ptr<AVFormatContext>(avformat_alloc_context(), &avformat_free_context);
auto avFormatPtr = avFormat.get();
avFormat->pb = avioContext.get();
avformat_open_input(&avFormatPtr, "dummyFilename", nullptr, nullptr);
于 2012-03-07T16:19:44.387 回答
14

这是很好的信息,对我有很大帮助,但是人们应该注意几个问题。libavformat 可以并且将会弄乱您提供给 avio_alloc_context 的缓冲区。这会导致非常烦人的双重释放错误或可能的内存泄漏。当我开始搜索问题时,我发现https://lists.ffmpeg.org/pipermail/libav-user/2012-December/003257.html完美地解决了问题。

从这项工作中清理时,我的解决方法是继续打电话

    av_free(avioContext->buffer)

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

于 2013-03-22T15:32:55.377 回答
9

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

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

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

...但我还需要创建一个自定义的 Seek 函数:

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);
...

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

于 2013-01-25T18:31:16.920 回答