2012-03-07 14 views
24

Actualmente estoy tratando de leer un pequeño archivo de vídeo enviadas desde un servidorLa lectura de un archivo que se encuentra en la memoria con libavformat

Para poder leer un archivo usando libavformat, que se supone que 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 llamar al av_open_input_file con el nombre del archivo temporal, que no es una solución muy clara.

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á usando.

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

+0

¡Escapa de tu '\' s! ;-) – Konrad

Respuesta

30

Es curioso cómo siempre encuentro la solución por mí mismo justo después de publicar la pregunta en este sitio, a pesar de que he estado trabajando en este problema durante horas.

De hecho, debe inicializar avFormatContext->pb antes de llamar al 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 se desea cargar desde un istream (no probado, sólo para que alguien que tiene el mismo problema puede tener la 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); 
+0

esto es genial de saber. ¿Alguien ha probado la solución sin usar la biblioteca estándar? – tom

+0

para gcc, 'me._stream.read' y' me._stream.gcount' deberían ser me.read y me.gcount, consulte http://www.cplusplus.com/reference/iostream/istream/ – tmatth

+0

Esta solución funciona muy bien hasta que tengo que buscar. ¿Alguna idea de cómo lograr que esta solución funcione con la búsqueda? Actualmente estoy usando avformat_seek_file con el contexto de formato en la secuencia de video y de audio por separado. Cuando se usa buscar en un archivo de transmisión (url), funciona muy bien. Cuando estoy en un mp4 local con este método, obtengo '[mov, mp4, m4a, 3gp, 3g2, mj2 @ 00ee8360] stream 0, offset 0xfd97fc: archivo parcial'. – leetNightshade

7

excelente respuesta de Tomaka17 me dio un buen comienzo hacia la solución de un problema análogo utilizando Qt QIODevice en lugar de std :: istream. He encontrado que necesitaba para combinar los 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 encargo función de lectura se parece a esto:

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 para crear una costumbre Seek función:

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 me ataron juntos de esta manera:

... 
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 Todavía no he resuelto los problemas de administración de memoria.

+0

'FF_INPUT_BUFFER_PADDING_SIZE' se cambió a' AV_INPUT_BUFFER_PADDING_SIZE' en las últimas versiones de ffmpeg –

9

Esta es una gran información y me ayudó bastante, pero hay un par de cuestiones que la gente debería conocer. libavformat puede interferir con tu memoria intermedia que le diste a avio_alloc_context. Esto conduce a errores realmente dobles molestos o posiblemente a pérdidas de memoria. Cuando comencé a buscar el problema, encontré https://lists.ffmpeg.org/pipermail/libav-user/2012-December/003257.html que lo clavó perfectamente.

Mi solución para realizar la limpieza de este trabajo es sólo seguir adelante y llamar

av_free(avioContext->buffer) 

, y luego poner su propio puntero de memoria intermedia (que se asigna para su llamada avio_alloc_context) a NULL si le interesa.

+0

Gracias. Hacer esto justo antes de av_free (avioContext) resolvió mi problema de pérdida de memoria. – Michel

Cuestiones relacionadas