Чтение файла, расположенного в памяти, с помощью libavformat - PullRequest
26 голосов
/ 07 марта 2012

В настоящее время я пытаюсь прочитать небольшие видеофайлы, отправленные с сервера

Чтобы прочитать файл с помощью 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?

Ответы [ 3 ]

35 голосов
/ 07 марта 2012

Забавно, что я всегда нахожу решение сам, сразу после публикации вопроса на этом сайте, хотя я уже несколько часов работаю над этой проблемой.

На самом деле вам нужно инициализировать 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);
11 голосов
/ 22 марта 2013

Это отличная информация, которая очень мне помогла, но есть пара вопросов, о которых нужно знать.libavformat может и будет связываться с вашим буфером, который вы передали avio_alloc_context.Это приводит к действительно раздражающим двойным свободным ошибкам или, возможно, утечкам памяти.Когда я начал искать проблему, я обнаружил, что https://lists.ffmpeg.org/pipermail/libav-user/2012-December/003257.html отлично ее прибил.

Мой обходной путь при очистке от этой работы - просто пойти дальше и позвонить

    av_free(avioContext->buffer)
* 1007.* и затем установите свой собственный указатель буфера (который вы выделили для вашего вызова avio_alloc_context) в NULL, если вам это нужно.
7 голосов
/ 25 января 2013

Отличный ответ 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;
}

... но мне также нужно было создать собственную функцию поиска:

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

Примечание. Проблемы с управлением памятью еще не решены.

...