Pertanyaan Bingkai H264 mentah dalam wadah mpegts menggunakan libavcodec


Saya akan sangat menghargai bantuan dengan masalah berikut:

Saya memiliki gadget dengan kamera, menghasilkan H264 dikompresi frame video, frame ini sedang dikirim ke aplikasi saya. Frame-frame ini tidak dalam wadah, hanya data mentah.

Saya ingin menggunakan fungsi ffmpeg dan libav untuk membuat file video, yang dapat digunakan nanti.

Jika saya memecahkan kodenya, lalu menyandikannya, semuanya berfungsi dengan baik, saya mendapatkan file video yang valid. (langkah-langkah decode / encode adalah perintah libav biasa, tidak ada yang mewah di sini, saya mengambilnya dari internet maha kuasa, mereka sangat kuat) ... Namun, saya menghabiskan banyak waktu dengan mendekode dan enkode, jadi saya ingin lewati langkah ini dan letakkan frame secara langsung di arus keluaran. Sekarang, masalahnya datang.

Berikut adalah kode yang saya dapatkan untuk menghasilkan pengkodean:

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

Di mana variabel seperti:

frameData adalah data frame yang didekode, yang berasal dari kamera, itu diterjemahkan dalam langkah sebelumnya dan videoOutBuf adalah buffer uint8_t polos untuk menyimpan data

Saya telah memodifikasi aplikasi agar tidak men-decode frame, tetapi hanya melewati data seperti:

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

dimana

frameData adalah frame H264 mentah dan currentFrameSize adalah ukuran frame H264 mentah, yaitu. jumlah byte yang saya dapatkan dari gadget untuk setiap frame.

Dan tiba-tiba aplikasi tidak berfungsi dengan benar lagi, video yang dihasilkan tidak dapat diputar. Ini jelas, karena saya tidak mengatur PTS yang benar untuk paket. Apa yang saya lakukan adalah sebagai berikut (saya putus asa, Anda dapat melihatnya dari pendekatan ini :))

    packet.pts = timestamps[timestamp_counter ++];

dimana timestamps sebenarnya adalah daftar PTS yang dihasilkan oleh kode kerja di atas, dan ditulis ke file (ya, Anda membacanya dengan benar, saya mencatat semua PTS untuk sesi 10 menit, dan ingin menggunakannya).

Aplikasi masih tidak berfungsi.

Sekarang, di sini saya tidak tahu apa yang harus dilakukan, jadi inilah pertanyaannya:

Saya ingin membuat aliran video "mpegts" menggunakan fungsi libav, menyisipkan dalam streaming frame video yang sudah dikodekan dan membuat file video dengannya. Bagaimana saya melakukannya?

Terima kasih, f.


32
2018-05-11 12:22


asal


Jawaban:


Saya percaya jika Anda mengatur yang berikut ini, Anda akan melihat pemutaran video.

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

Anda harus benar-benar mengatur packet.flags sesuai dengan header paket h264. Anda mungkin mencoba sesama stack overflowian ini saran untuk mengekstraksi langsung dari aliran.

Jika Anda juga menambahkan audio, maka pts / dts akan menjadi lebih penting. Saya sarankan kamu belajar tutorial ini

EDIT

Saya menemukan waktu untuk mengekstrak apa yang bekerja untuk saya dari aplikasi pengujian saya. Untuk beberapa alasan, nilai dts / pts nol berfungsi untuk saya, tetapi nilai selain 0 atau AV_NOPTS_VALUE tidak. Saya ingin tahu apakah kami memiliki versi ffmpeg yang berbeda. Saya memiliki yang terbaru dari 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



Anda dapat membuat proses untuk memanggil ffmpeg dari konsol.

Contoh baris perintah untuk memproses file seperti 000001.jpg, 000002.jpg, 000003.jpg, ...

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

Contoh lain dari dokumen ffmpeg


-7
2018-05-22 16:42