c++ c file libavcodec libavformat

c++ - Leyendo un archivo ubicado en memoria con formato libav.



file libavcodec (3)

Actualmente estoy tratando de leer pequeños archivos de video enviados desde un servidor

Para leer un archivo usando libavformat, debes llamar

av_open_input_file(&avFormatContext, "C://path//to//video.avi", 0, 0, 0);

El problema es que, en este caso, el archivo no está en el disco, sino en la memoria.

Lo que estoy haciendo por el momento es descargar el archivo, escribirlo en el disco con un nombre temporal y luego llamar a av_open_input_file con el nombre del archivo temporal, que no es una solución muy limpia.

De hecho, lo que quiero es una función como av_open_custom(&avFormatContext, &myReadFunction, &mySeekFunction); Pero no encontré ninguno en la documentación. Supongo que es técnicamente posible, ya que el nombre del archivo no es algo que ayude a la biblioteca a determinar qué formato está utilizando.

Entonces, ¿hay una función como esta, o una alternativa a av_open_input_file?


Es curioso que siempre encuentre la solución solo después de publicar la pregunta en este sitio, a pesar de que he estado trabajando en este problema durante horas.

De hecho, tiene que inicializar avFormatContext->pb antes de llamar a av_open_input , y pasarle un nombre de archivo falso. Esto no está escrito en la documentación sino en un comentario directamente en el código fuente de la biblioteca.

Código de ejemplo si desea cargar desde un istream (sin probar, solo para que alguien que tenga el mismo problema pueda tener una 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);


Esta es una gran información y me ayudó bastante, pero hay un par de problemas que la gente debería conocer. libavformat puede y va a meterse con el búfer que le diste a avio_alloc_context. Esto conduce a errores de doble libertad realmente molestos o posiblemente a fugas de memoria. Cuando comencé a buscar el problema, encontré https://lists.ffmpeg.org/pipermail/libav-user/2012-December/003257.html que lo resolvió perfectamente.

Mi solución al limpiar este trabajo es seguir adelante y llamar

av_free(avioContext->buffer)

y luego establecer su propio puntero del búfer (que asignó para su llamada avio_alloc_context) a NULL si le importa.


La excelente respuesta de Tomaka17 me dio un buen comienzo para resolver un problema análogo utilizando Qt QIODevice en lugar de std :: istream. Descubrí que necesitaba combinar aspectos de la solución de Tomaka17, con aspectos de la experiencia relacionada en http://cdry.wordpress.com/2009/09/09/using-custom-io-callbacks-with-ffmpeg/

Mi función de lectura personalizada se ve así:

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

... pero también necesitaba crear una función de Búsqueda personalizada:

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

... y lo amarré así:

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

Nota aún no he resuelto los problemas de gestión de memoria.