Tramas H264 raw en contenedor mpegts usando libavcodec


Realmente agradecería un poco de ayuda con el siguiente tema:

Tengo un gadget con una cámara, produciendo fotogramas de video comprimidos H264, estos fotogramas se envían a mi aplicación. Estos fotogramas no están en un contenedor, solo datos sin procesar.

Quiero usar las funciones ffmpeg y libav para crear un archivo de video, que se puede usar más tarde.

Si decodifico los fotogramas, luego los codifico, todo funciona bien, obtengo un archivo de video válido. (los pasos de decodificación/codificación son los habituales comandos libav, nada de fantasía aquí, los tomé de la Internet todopoderoso, son sólidos como una roca)... Sin embargo, pierdo mucho tiempo decodificando y codificando, así que me gustaría omitir este paso y poner directamente los fotogramas en el flujo de salida. Ahora, los problemas vienen.

Aquí está el código que se me ocurrió para producir la codificación:

AVFrame* picture;

avpicture_fill((AVPicture*) picture, (uint8_t*)frameData, 
                 codecContext->pix_fmt, codecContext->width,
                 codecContext->height);
int outSize = avcodec_encode_video(codecContext, videoOutBuf, 
                 sizeof(videoOutBuf), picture);
if (outSize > 0) 
{
    AVPacket packet;
    av_init_packet(&packet);
    packet.pts = av_rescale_q(codecContext->coded_frame->pts,
                  codecContext->time_base, videoStream->time_base);
    if (codecContext->coded_frame->key_frame) 
    {
        packet.flags |= PKT_FLAG_KEY;
    }
    packet.stream_index = videoStream->index;
    packet.data =  videoOutBuf;
    packet.size =  outSize;

    av_interleaved_write_frame(context, &packet);
    put_flush_packet(context->pb);
}

Donde las variables son como:

frameData es los datos de fotograma decodificados, que provenían de la cámara, se decodificó en un paso anterior y videoOutBuf es un búfer uint8_t simple para contener los datos

He modificado la aplicación para no decodificar los frames, sino simplemente pasar a través de los datos como:

    AVPacket packet;
    av_init_packet(&packet);

    packet.stream_index = videoStream->index;
    packet.data = (uint8_t*)frameData;
    packet.size = currentFrameSize;

    av_interleaved_write_frame(context, &packet);
    put_flush_packet(context->pb);

Donde

frameData es el marco crudo H264 y currentFrameSize es el tamaño del marco H264 crudo, es decir. el número de bytes que obtengo del gadget para cada fotograma.

Y de repente la aplicación ya no funciona correctamente, el video producido no se puede reproducir. Esto es obvio, ya que no estaba estableciendo un correcto PTS para el paquete. Lo que hice fue lo siguiente (estoy desesperado, se puede ver desde este enfoque :))

    packet.pts = timestamps[timestamp_counter ++];

Donde timestamps es en realidad una lista de PTS producidos por el código de trabajo anterior, y escrito en un archivo (sí, lo leyó correctamente, registré todos los PTS para una sesión de 10 minutos, y quería usarlos).

La aplicación todavía no funciona.

Ahora, aquí estoy sin ninguna pista de qué hacer, así que aquí está la pregunta:

Me gustaría crear un "mpegts" transmisión de vídeo con las funciones libav, inserte en la transmisión fotogramas de vídeo ya codificados y cree un archivo de vídeo con él. ¿Cómo lo hago?

Gracias, f.

Author: fritzone, 2011-05-11

2 answers

Creo que si configura lo siguiente, verá la reproducción de video.

packet.flags |= AV_PKT_FLAG_KEY;
packet.pts = packet.dts = 0;

Realmente debería configurar paquete.banderas según las cabeceras de paquetes h264. Puedes probar la sugerencia de este compañero de stack overflowian para extraer directamente de la secuencia.

Si también está agregando audio, entonces pts/dts va a ser más importante. Te sugiero que estudies este tutorial

EDITAR

Encontré tiempo para extraer lo que está funcionando para mí de mi aplicación de prueba. Por alguna razón, los valores dts / pts de cero funcionan para mí, pero los valores que no sean 0 o AV_NOPTS_VALUE no lo hacen. Me pregunto si tenemos diferentes versiones de ffmpeg. Tengo lo último de git: / / git. videolan.org/ffmpeg. git .

Fftest.cpp

#include <string>

#ifndef INT64_C
#define INT64_C(c) (c ## LL)
#define UINT64_C(c) (c ## ULL)
#endif

//#define _M
#define _M printf( "%s(%d) : MARKER\n", __FILE__, __LINE__ )

extern "C"
{
    #include "libavcodec/avcodec.h"
    #include "libavformat/avformat.h"
};


AVFormatContext *fc = 0;
int vi = -1, waitkey = 1;

// < 0 = error
// 0 = I-Frame
// 1 = P-Frame
// 2 = B-Frame
// 3 = S-Frame
int getVopType( const void *p, int len )
{   
    if ( !p || 6 >= len )
        return -1;

    unsigned char *b = (unsigned char*)p;

    // Verify NAL marker
    if ( b[ 0 ] || b[ 1 ] || 0x01 != b[ 2 ] )
    {   b++;
        if ( b[ 0 ] || b[ 1 ] || 0x01 != b[ 2 ] )
            return -1;
    } // end if

    b += 3;

    // Verify VOP id
    if ( 0xb6 == *b )
    {   b++;
        return ( *b & 0xc0 ) >> 6;
    } // end if

    switch( *b )
    {   case 0x65 : return 0;
        case 0x61 : return 1;
        case 0x01 : return 2;
    } // end switch

    return -1;
}

void write_frame( const void* p, int len )
{
    if ( 0 > vi )
        return;

    AVStream *pst = fc->streams[ vi ];

    // Init packet
    AVPacket pkt;
    av_init_packet( &pkt );
    pkt.flags |= ( 0 >= getVopType( p, len ) ) ? AV_PKT_FLAG_KEY : 0;   
    pkt.stream_index = pst->index;
    pkt.data = (uint8_t*)p;
    pkt.size = len;

    // Wait for key frame
    if ( waitkey )
        if ( 0 == ( pkt.flags & AV_PKT_FLAG_KEY ) )
            return;
        else
            waitkey = 0;

    pkt.dts = AV_NOPTS_VALUE;
    pkt.pts = AV_NOPTS_VALUE;

//  av_write_frame( fc, &pkt );
    av_interleaved_write_frame( fc, &pkt );
}

void destroy()
{
    waitkey = 1;
    vi = -1;

    if ( !fc )
        return;

_M; av_write_trailer( fc );

    if ( fc->oformat && !( fc->oformat->flags & AVFMT_NOFILE ) && fc->pb )
        avio_close( fc->pb ); 

    // Free the stream
_M; av_free( fc );

    fc = 0;
_M; 
}

int get_nal_type( void *p, int len )
{
    if ( !p || 5 >= len )
        return -1;

    unsigned char *b = (unsigned char*)p;

    // Verify NAL marker
    if ( b[ 0 ] || b[ 1 ] || 0x01 != b[ 2 ] )
    {   b++;
        if ( b[ 0 ] || b[ 1 ] || 0x01 != b[ 2 ] )
            return -1;
    } // end if

    b += 3;

    return *b;
}

int create( void *p, int len )
{
    if ( 0x67 != get_nal_type( p, len ) )
        return -1;

    destroy();

    const char *file = "test.avi";
    CodecID codec_id = CODEC_ID_H264;
//  CodecID codec_id = CODEC_ID_MPEG4;
    int br = 1000000;
    int w = 480;
    int h = 354;
    int fps = 15;

    // Create container
_M; AVOutputFormat *of = av_guess_format( 0, file, 0 );
    fc = avformat_alloc_context();
    fc->oformat = of;
    strcpy( fc->filename, file );

    // Add video stream
_M; AVStream *pst = av_new_stream( fc, 0 );
    vi = pst->index;

    AVCodecContext *pcc = pst->codec;
_M; avcodec_get_context_defaults2( pcc, AVMEDIA_TYPE_VIDEO );
    pcc->codec_type = AVMEDIA_TYPE_VIDEO;

    pcc->codec_id = codec_id;
    pcc->bit_rate = br;
    pcc->width = w;
    pcc->height = h;
    pcc->time_base.num = 1;
    pcc->time_base.den = fps;

    // Init container
_M; av_set_parameters( fc, 0 );

    if ( !( fc->oformat->flags & AVFMT_NOFILE ) )
        avio_open( &fc->pb, fc->filename, URL_WRONLY );

_M; av_write_header( fc );

_M; return 1;
}

int main( int argc, char** argv )
{
    int f = 0, sz = 0;
    char fname[ 256 ] = { 0 };
    char buf[ 128 * 1024 ];

    av_log_set_level( AV_LOG_ERROR );
    av_register_all();

    do
    {
        // Raw frames in v0.raw, v1.raw, v2.raw, ...
//      sprintf( fname, "rawvideo/v%lu.raw", f++ );
        sprintf( fname, "frames/frame%lu.bin", f++ );
        printf( "%s\n", fname );

        FILE *fd = fopen( fname, "rb" );
        if ( !fd )
            sz = 0;
        else
        {
            sz = fread( buf, 1, sizeof( buf ) - FF_INPUT_BUFFER_PADDING_SIZE, fd );
            if ( 0 < sz )
            {
                memset( &buf[ sz ], 0, FF_INPUT_BUFFER_PADDING_SIZE );          

                if ( !fc )
                    create( buf, sz );

                if ( fc )
                    write_frame( buf, sz );

            } // end if

            fclose( fd );

        } // end else

    } while ( 0 < sz );

    destroy();
}
 27
Author: bob2,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:32:14

Puede crear un proceso para llamar a ffmpeg desde la consola.

Ejemplo de línea de comandos para procesar archivos como 000001.jpg, 000002.jpg, 000003.jpg, ...

Ffmpeg-i c:\frames\%06d.jpg -r 16-vcodec mpeg4-an-y c:\video\some_video.avi

Otros ejemplos de ffmpeg docs

 -7
Author: user347594,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-22 16:42:44