Чтение файла, находящегося в памяти с помощью libavformat
В настоящее время я пытаюсь читать небольшие видеофайлы, отправленные с сервера
Чтобы прочитать файл с использованием 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?
Ответы
Ответ 1
Забавно, как я всегда нахожу решение самостоятельно сразу после публикации вопроса на этом сайте, хотя я уже несколько часов работаю над этой проблемой.
На самом деле вам нужно инициализировать 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);
Ответ 2
Это отличная информация и немного помогли мне, но есть пара проблем, о которых люди должны знать. libavformat может и будет работать с вашим буфером, который вы передали avio_alloc_context. Это приводит к действительно раздражающим двукратным ошибкам или, возможно, утечкам памяти. Когда я начал искать проблему, я нашел https://lists.ffmpeg.org/pipermail/libav-user/2012-December/003257.html, который отлично прибил ее.
Мое обходное решение при очистке от этой работы - просто пойти вперед и позвонить
av_free(avioContext->buffer)
а затем, если вам интересно, установите свой собственный буферный указатель (который вы выделили для вашего вызова avio_alloc_context) в NULL.
Ответ 3
Отличный ответ 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);
...
Примечание. Я еще не разработал проблемы с управлением памятью.