2012-03-07 4 views
24

В настоящее время я пытаюсь читать небольшие видео-файлы, отправленные с сервераЧтение файл, расположенный в памяти с 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?

+0

Побег ваших '! ;-) – Konrad

ответ

30

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

Фактически вы должны инициализировать 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); 
+0

это здорово знать. Кто-нибудь пробовал решение без использования библиотеки std? – tom

+0

для gcc, 'me._stream.read' и' me._stream.gcount' должен быть просто me.read и me.gcount, см. Http://www.cplusplus.com/reference/iostream/istream/ – tmatth

+0

Это решение работает отлично, пока мне не нужно искать. Любая идея, как заставить это решение работать с поиском? В настоящее время я использую avformat_seek_file с контекстом формата в потоке видео и аудиопотоке отдельно. При использовании поиска в потоковом файле (url) он отлично работает. Когда на локальном mp4 с этим методом, я получаю поток '[mov, mp4, m4a, 3gp, 3g2, mj2 @ 00ee8360] 0, смещение 0xfd97fc: partial file'. – leetNightshade

7

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

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

+0

'FF_INPUT_BUFFER_PADDING_SIZE' был изменен на' AV_INPUT_BUFFER_PADDING_SIZE' в последних версиях ffmpeg –

9

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

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

av_free(avioContext->buffer) 

, а затем установив свой собственный указатель на буфер (который вы выделили для avio_alloc_context вызова) в NULL, если вы заботитесь.

+0

Спасибо. Выполняя это прямо перед av_free (avioContext), я решил проблему с утечкой памяти! – Michel

Смежные вопросы