美好的一天。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
因此,有一個問題,什麼我錯過當複製流?我會很感激任何幫助。
問候
感謝您的回答,szatmary。但我是ffmpeg的新手,我不完全明白我應該做什麼。我只有有用的字段是AVStream結構中的輸入和輸出流的time_base字段。用哪種方法計算pts \ dts? – Bumblebee
時基是一個比例,比如1/30,這意味着時間戳以1/30秒的速度前進。所以如果你每秒鐘有30幀,那麼每一幀都會增加1。如果時基爲1/3000,則它將增加100.查看編解碼器上下文時基,並將幀時間戳寫入該基地。 – szatmary
問題是,通常我不知道輸入流的fps。字段pts和dts包含AV_NOPTS_VALUE,所以我不能調用av_rescale_來獲取輸出流的正確pts \ dts。 – Bumblebee