web-dev-qa-db-fra.com

Trames H264 brutes dans le conteneur mpegts à l'aide de libavcodec

J'apprécierais vraiment de l'aide avec le problème suivant:

J'ai un gadget avec un appareil photo, produisant des images vidéo compressées H264, ces images sont envoyées à mon application. Ces trames ne sont pas dans un conteneur, juste des données brutes.

Je veux utiliser les fonctions ffmpeg et libav pour créer un fichier vidéo, qui peut être utilisé plus tard.

Si je décode les images, puis les encode, tout fonctionne bien, j'obtiens un fichier vidéo valide. (les étapes de décodage/encodage sont les commandes libav habituelles, rien de spécial ici, je les ai prises sur Internet tout-puissant, elles sont solides) ... Cependant, je perds beaucoup de temps en décodant et en encodant, donc je voudrais ignorez cette étape et placez directement les images dans le flux de sortie. Maintenant, les problèmes viennent.

Voici le code que j'ai trouvé pour produire l'encodage:

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

Où les variables sont comme:

frameData est les données de trame décodées, qui provenaient de la caméra, elles ont été décodées dans une étape précédente et videoOutBuf est un simple tampon uint8_t pour contenir les données

J'ai modifié l'application afin de ne pas décoder les frames, mais simplement passer à travers les données comme:

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

frameData est le cadre brut H264 et currentFrameSize est la taille du cadre brut H264, c.-à-d. le nombre d'octets que j'obtiens du gadget pour chaque trame.

Et soudain, l'application ne fonctionne plus correctement, la vidéo produite est illisible. C'est évident, car je ne définissais pas un PTS correct pour le paquet. Ce que j'ai fait était le suivant (je suis désespéré, vous pouvez le voir dans cette approche :))

    packet.pts = timestamps[timestamp_counter ++];

timestamps est en fait une liste de PTS produits par le code de travail ci-dessus et écrits dans un fichier (oui, vous l'avez lu correctement, j'ai enregistré tous les PTS pendant une session de 10 minutes et je voulais les utiliser) .

L'application ne fonctionne toujours pas.

Maintenant, je suis ici sans savoir quoi faire, alors voici la question:

Je voudrais créer un flux vidéo "mpegts" en utilisant les fonctions libav, insérer dans le flux des images vidéo déjà encodées et créer un fichier vidéo avec. Comment fait-on ça?

Merci F.

36
Ferenc Deak

Je crois que si vous définissez ce qui suit, vous verrez la lecture vidéo.

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

Vous devez vraiment définir packet.flags selon les en-têtes de paquets h264. Vous pourriez essayer cette autre pile de débordement suggestion pour extraire directement du flux.

Si vous ajoutez également de l'audio, les pts/dts seront plus importants. Je vous suggère d'étudier ce tutoriel

[~ # ~] modifier [~ # ~]

J'ai trouvé le temps d'extraire ce qui fonctionne pour moi de mon application de test. Pour une raison quelconque, les valeurs dts/pts de zéro fonctionnent pour moi, mais les valeurs autres que 0 ou AV_NOPTS_VALUE ne le font pas. Je me demande si nous avons différentes versions de ffmpeg. J'ai la dernière version 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();
}
28
bob2