2017-03-03 331 views
1

美好的一天。FFMpeg:無需更改即可將h264流寫入mp4容器

爲簡潔起見,代碼省略了錯誤處理和內存管理。

我想要捕獲h264視頻流並將其打包到mp4容器中而不進行任何更改。由於我不控制流的來源,我不能對流結構做出假設。這樣我就必須探測輸入流。

AVProbeData probeData; 
    probeData.buf_size = s->BodySize(); 
    probeData.buf = s->GetBody(); 
    probeData.filename = ""; 

    AVInputFormat* inFormat = av_probe_input_format(&probeData, 1); 

此代碼正確定義h264流。

接着,我創建輸入格式方面,

unsigned char* avio_input_buffer = reinterpret_cast<unsigned char*> (av_malloc(AVIO_BUFFER_SIZE)); 

    AVIOContext* avio_input_ctx = avio_alloc_context(avio_input_buffer, AVIO_BUFFER_SIZE, 
     0, this, &read_packet, NULL, NULL); 

    AVFormatContext* ifmt_ctx = avformat_alloc_context(); 
    ifmt_ctx->pb = avio_input_ctx; 

    int ret = avformat_open_input(&ifmt_ctx, NULL, inFormat, NULL); 

設置的圖像大小,

ifmt_ctx->streams[0]->codec->width = ifmt_ctx->streams[0]->codec->coded_width = width; 
    ifmt_ctx->streams[0]->codec->height = ifmt_ctx->streams[0]->codec->coded_height = height; 

創建輸出格式方面,

unsigned char* avio_output_buffer = reinterpret_cast<unsigned char*>(av_malloc(AVIO_BUFFER_SIZE)); 

    AVIOContext* avio_output_ctx = avio_alloc_context(avio_output_buffer, AVIO_BUFFER_SIZE, 
     1, this, NULL, &write_packet, NULL); 

    AVFormatContext* ofmt_ctx = nullptr; 
    avformat_alloc_output_context2(&ofmt_ctx, NULL, "mp4", NULL); 
    ofmt_ctx->pb = avio_output_ctx; 

    AVDictionary* dict = nullptr; 
    av_dict_set(&dict, "movflags", "faststart", 0); 
    av_dict_set(&dict, "movflags", "frag_keyframe+empty_moov", 0); 

    AVStream* outVideoStream = avformat_new_stream(ofmt_ctx, nullptr); 

    avcodec_copy_context(outVideoStream->codec, ifmt_ctx->streams[0]->codec); 

    ret = avformat_write_header(ofmt_ctx, &dict); 

初始化完成。此外還有一個從h264流向mp4容器轉移的數據包。我不計算pts和dts,因爲源包中有AV_NOPTS_VALUE。

AVPacket pkt; 
    while (...) 
    { 
     ret = av_read_frame(ifmt_ctx, &pkt); 
     ret = av_interleaved_write_frame(ofmt_ctx, &pkt); 
     av_free_packet(&pkt); 
    } 

此外我寫預告片和自由分配的內存。就這些。代碼作品,我得到可播放的MP4文件。

現在的問題是:結果文件的流特性不完全符合源流的特性。特別是,fps和比特率比它應該高。

作爲樣品,下面是用於源數據流

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'd:/movies/source.mp4':0/0 
Metadata: 
    major_brand  : isom 
    minor_version : 1 
    compatible_brands: isom 
    creation_time : 2014-04-14T13:03:54.000000Z 
Duration: 00:00:58.08, start: 0.000000, bitrate: 12130 kb/s 
Stream #0:0(und): Video: h264 (Constrained Baseline) (avc1/0x31637661),yuv420p, 1920x1080, 12129 kb/s, 25 fps, 25 tbr, 25 tbn, 50 tbc (default) 
Metadata: 
    handler_name : VideoHandler 
Switch subtitle stream from #-1 to #-1 vq= 1428KB sq= 0B f=0/0 
Seek to 49% (0:00:28) of total duration (0:00:58)  B f=0/0 
    30.32 M-V: -0.030 fd= 87 aq= 0KB vq= 1360KB sq= 0B f=0/0 

和用於所得流輸出ffplay.exe(包含源流的一部分)

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'd:/movies/target.mp4':f=0/0 
Metadata: 
    major_brand  : isom 
    minor_version : 512 
    compatible_brands: isomiso2avc1iso6mp41 
    encoder   : Lavf57.56.101 
Duration: 00:00:11.64, start: 0.000000, bitrate: 18686 kb/s 
Stream #0:0(und): Video: h264 (Constrained Baseline) (avc1/0x31637661), yuv420p, 1920x1080, 18683 kb/s, 38.57 fps, 40 tbr, 90k tbn, 50 tbc (default) 
Metadata: 
    handler_name : VideoHandler 
Switch subtitle stream from #-1 to #-1 vq= 2309KB sq= 0B f=0/0 
    5.70 M-V: 0.040 fd= 127 aq= 0KB vq= 2562KB sq= 0B f=0/0 

因此,有一個問題,什麼我錯過當複製流?我會很感激任何幫助。

問候

回答

1

I dont calculate pts and dts這是你的問題。幀速率和比特率都是以時間爲分母的比率。但是,不要寫pts/dts,你最終的視頻比你想要的要短。 h.264不會爲每個幀添加時間戳。這是容器的工作。您必須根據已知幀速率或其他值組合時間戳。

+0

感謝您的回答,szatmary。但我是ffmpeg的新手,我不完全明白我應該做什麼。我只有有用的字段是AVStream結構中的輸入和輸出流的time_base字段。用哪種方法計算pts \ dts? – Bumblebee

+0

時基是一個比例,比如1/30,這意味着時間戳以1/30秒的速度前進。所以如果你每秒鐘有30幀,那麼每一幀都會增加1。如果時基爲1/3000,則它將增加100.查看編解碼器上下文時基,並將幀時間戳寫入該基地。 – szatmary

+0

問題是,通常我不知道輸入流的fps。字段pts和dts包含AV_NOPTS_VALUE,所以我不能調用av_rescale_來獲取輸出流的正確pts \ dts。 – Bumblebee