programing

libavcodec을 이용한 mPEGts 컨테이너의 원시 H264 프레임

mailnote 2023. 9. 23. 23:00
반응형

libavcodec을 이용한 mPEGts 컨테이너의 원시 H264 프레임

다음 문제와 관련하여 도움을 주시면 정말 감사하겠습니다.

저는 H264 압축 비디오 프레임을 만드는 카메라가 있는 장치를 가지고 있는데, 이 프레임들은 제 어플리케이션으로 전송되고 있습니다.이 프레임들은 컨테이너에 있는 것이 아니라 원시 데이터일 뿐입니다.

나중에 사용할 수 있는 fmpeg와 libav 기능을 사용해서 동영상 파일을 만들고 싶습니다.

프레임을 해독한 다음 인코딩하면 모든 것이 정상적으로 작동하여 유효한 비디오 파일을 얻을 수 있습니다.(디코드/인코드 단계는 일반적인 libav 명령어이며, 여기서는 화려한 것이 아닙니다. 전능한 인터넷에서 가져온 것입니다. 그것들은 바위처럼 견고합니다.)...하지만 디코딩과 인코딩으로 많은 시간을 낭비하기 때문에 이 단계를 생략하고 프레임을 출력 스트림에 직접 넣고 싶습니다.자, 문제가 발생합니다.

인코딩을 만들기 위해 생각해낸 코드는 다음과 같습니다.

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

변수가 다음과 같은 경우:

frameData디코딩된 프레임 데이터이며, 카메라로부터 이전 단계에서 디코딩된 데이터입니다.videoOutBuf는 데이터를 보관하기 위한 일반 uint8_t 버퍼입니다.

프레임을 디코딩하지 않고 다음과 같은 데이터를 전달하기 위해 애플리케이션을 수정했습니다.

    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는 H264 frame이고,currentFrameSize는 원시 H264 프레임의 크기입니다. 즉, 각 프레임에 대해 가젯에서 얻을 수 있는 바이트 수.

그리고 갑자기 애플리케이션이 제대로 작동하지 않아 제작된 동영상을 재생할 수 없습니다.패킷에 대한 PTS를 정확하게 설정하지 않았기 때문에 이는 명백합니다.제가 한 일은 다음과 같습니다. (절망합니다, 이 방법을 보면 알 수 있습니다 :)

    packet.pts = timestamps[timestamp_counter ++];

어디에timestamps는 실제로 위의 워킹 코드에 의해 생산되고 파일에 작성된 PTS 목록입니다(네, 제대로 읽으셨습니다, 10분 세션 동안 PTS를 모두 기록했고, 사용하고 싶었습니다).

응용프로그램이 여전히 작동하지 않습니다.

자, 저는 어떻게 해야 할지 전혀 모릅니다. 그래서 여기 질문이 있습니다.

libav 함수를 이용하여 "mpegts" 비디오 스트림을 만들고, 이미 인코딩된 비디오 프레임을 스트림에 삽입하고 그것으로 비디오 파일을 만들고 싶습니다.제가 그걸 어떻게 합니까?

고마워요, f.

아래와 같이 설정하시면 동영상 재생이 가능할 것 같습니다.

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

당신은 정말로 패킷을 설정해야 합니다.h264 패킷 헤더에 따라 플래그를 지정합니다.스트림에서 직접 추출하기 위해 이 동료 스택 오버플로어의 제안을 시도해 볼 수 있습니다.

오디오도 추가하는 경우 pts/dts가 더 중요합니다.이 자습서를 공부할 것을 제안합니다.

편집

저는 테스트 앱에서 저에게 효과가 있는 것을 추출할 시간을 가졌습니다.어떤 이유에서인지 0의 dts/pts 값은 제게 적합하지만 0 또는 AV_NOPTS_VALUE 이외의 값은 그렇지 않습니다.다른 버전의 fmpeg가 있는지 궁금합니다.git://git.videolan.org/ffmpeg.git 에서 최신 정보를 받았습니다.

ffest.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();
}

콘솔에서 fffmpeg를 호출하는 프로세스를 만들 수 있습니다.

000001.jpg, 000002.jpg, 000003.jpg, ...과 같은 파일을 처리하기 위한 명령줄의 예

fmpeg -ic:\frames\%06d. jpg -r 16 -vcodec mPEG4 -an -y c:\video\some_video.avi

fmpeg 문서의 다른 예

언급URL : https://stackoverflow.com/questions/5964142/raw-h264-frames-in-mpegts-container-using-libavcodec

반응형