Pregunta Marcos H264 sin procesar en contenedores mpegts usando libavcodec


Realmente agradecería algo de ayuda con el siguiente problema:

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

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

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

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 el dato de fotograma decodificado, que vino de la cámara, fue decodificado en un paso anterior y videoOutBuf es un buffer simple uint8_t para contener los datos

He modificado la aplicación para no decodificar los marcos, simplemente pasar 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);

dónde

frameData es el marco crudo H264 y currentFrameSize es el tamaño del marco crudo H264, es decir. la cantidad 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 configurando un PTS correcto para el paquete. Lo que hice fue lo siguiente (estoy desesperado, puedes verlo desde este enfoque :))

    packet.pts = timestamps[timestamp_counter ++];

dónde timestamps es en realidad una lista de PTS producida por el código de trabajo anterior, y escrita en un archivo (sí, la 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 idea de qué hacer, así que aquí está la pregunta:

Me gustaría crear un flujo de video "mpegts" usando las funciones de libav, insertar en el flujo marcos de video ya codificados y crear un archivo de video con él. ¿Cómo lo hago?

Gracias, F.


32
2018-05-11 12:22


origen


Respuestas:


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

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

Realmente debe establecer packet.flags de acuerdo con los encabezados de paquetes h264. Puede intentar este tipo apila desbordamiento sugerencia 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 funciona para mí desde mi aplicación de prueba. Por alguna razón, los valores dts / pts de cero me funcionan, pero los valores distintos de 0 o AV_NOPTS_VALUE no. 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();
}

26
2018-05-16 19:36



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

Ejemplo de línea de comando 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 documentos de ffmpeg


-7
2018-05-22 16:42