Cadres bruts H264 dans un contenant mpegts en utilisant libavcodec

je voudrais vraiment l'apprécier un peu d'aide avec le problème suivant:

j'ai un gadget avec une caméra, produisant des images vidéo compressées H264, ces images sont envoyées à mon application. Ces cadres 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 cadres, puis les encoder, tout fonctionne bien, j'obtiens un valide fichier vidéo. (les étapes de décodage/encodage sont les commandes libav habituelles, rien de fantaisiste ici, je les ai prises à partir de l'Internet tout-puissant, ils sont rock solid)... Cependant, je perds beaucoup de temps à décoder et encoder, donc je voudrais sauter cette étape et mettre directement les images dans le flux de sortie. Maintenant, les problèmes arrivent.

voici le code que j'ai inventé 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 la donnée de cadre décodée, qui vient de la caméra, il a été décodé dans une étape précédente et videoOutBuf est un tampon uint8_t simple pour tenir les données

j'ai modifié l'application afin de ne pas décoder les trames, mais tout 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 de octets-je obtenir à partir du gadget pour chaque trame.

et soudain l'application ne fonctionne plus correctement, la vidéo produite est injouable. C'est évident, puisque 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 de cette approche :) )

    packet.pts = timestamps[timestamp_counter ++];

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

L'application ne fonctionne toujours pas.

maintenant, me voici sans savoir quoi faire, alors voici la question:

j'aimerais 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 dois-je faire?

Merci, F.

34
demandé sur fritzone 2011-05-11 16:22:56

2 réponses

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 devriez vraiment mettre paquet.drapeaux selon les en-têtes de paquets h264. Vous pourriez essayer ce gars pile suggestion de débordement pour extraire directement du flux.

si vous ajoutez aussi de l'audio, alors pts/dts va être plus important. Je vous suggère d'étudier ce tutoriel

MODIFIER

j'ai trouvé le temps d'extraire ce qui fonctionne pour moi à partir 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 fonctionnent pas. Je me demande si nous avons différentes versions de ffmpeg. J'ai la dernière 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
répondu bob2 2017-05-23 12:32:14

vous pouvez créer un processus pour appeler ffmpeg depuis la console.

exemple de ligne de commande pour le traitement de fichiers comme 000001.jpg, 000002.jpg, 000003.jpg,...

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

autres exemples tirés de ffmpeg docs

-7
répondu user347594 2011-05-22 16:42:44