Reading a file located in memory with libavformat

若如初见. 提交于 2019-11-28 16:24:16

It's funny how I always find the solution by myself right after I post the question on this site, even though I've been working on this problem for hours.

In fact you have to initialize avFormatContext->pb before calling av_open_input, and pass to it a fake filename. This is not written in the documentation but in a commentary directly in the library's source code.

Example code if you want to load from an istream (untested, just so somebody which has the same problem can get the idea)

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

This is great information and helped me out quite a bit, but there are a couple of issues people should be aware of. libavformat can and will mess with your buffer that you gave to avio_alloc_context. This leads to really annoying double-free errors or possibly memory leaks. When I started searching for the problem, I found https://lists.ffmpeg.org/pipermail/libav-user/2012-December/003257.html which nailed it perfectly.

My workaround when cleaning up from this work is to just go ahead and call

    av_free(avioContext->buffer)

and then setting your own buffer pointer (that you allocated for your avio_alloc_context call) to NULL if you care.

Tomaka17's excellent answer gave me a good start toward solving an analogous problem using Qt QIODevice rather than std::istream. I found I needed to blend aspects of Tomaka17's solution, with aspects of the related experience at http://cdry.wordpress.com/2009/09/09/using-custom-io-callbacks-with-ffmpeg/

My custom Read function looks like this:

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

...but I also needed to create a custom Seek function:

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();
}

...and I tied it together like this:

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

Note I have not yet worked out the memory management issues.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!