分離某些封裝格式中的H.264
分離某些封裝格式(例如MP4/FLV/MKV等)中的H.264的時候,需要首先寫入SPS和PPS,否則會導致分離出來的數據沒有SPS、PPS而無法播放。H.264碼流的SPS和PPS信息存儲在AVCodecContext結構體的extradata中。需要使用ffmpeg中名稱為“h264_mp4toannexb”的bitstream filter處理。有兩種處理方式:
(1)使用bitstream filter處理每個AVPacket(簡單)
把每個AVPacket中的數據(data字段)經過bitstream filter“過濾”一遍。關鍵函數是av_bitstream_filter_filter()。示例代碼如下。
[cpp] view plain copy 
AVBitStreamFilterContext* h264bsfc = av_bitstream_filter_init("h264_mp4toannexb"); while(av_read_frame(ifmt_ctx, &pkt)>=0){ if(pkt.stream_index==videoindex){ av_bitstream_filter_filter(h264bsfc, ifmt_ctx->streams[videoindex]->codec, NULL, &pkt.data, &pkt.size, pkt.data, pkt.size, 0); fwrite(pkt.data,1,pkt.size,fp_video); //... } av_free_packet(&pkt); } av_bitstream_filter_close(h264bsfc); 上述代碼中,把av_bitstream_filter_filter()的輸入數據和輸出數據(分別對應第4,5,6,7個參數)都設置成AVPacket的data字段就可以了。
需要注意的是bitstream%20filter需要初始化和銷毀,分別通過函數av_bitstream_filter_init()和av_bitstream_filter_close()。
經過上述代碼處理之后,AVPacket中的數據有如下變化:
*每個AVPacket的data添加了H.264的NALU的起始碼{0,0,0,1}
*每個IDR幀數據前面添加了SPS和PPS
(2)手工添加SPS,PPS(稍微復雜)
將AVCodecContext的extradata數據經過bitstream%20filter處理之后得到SPS、PPS,拷貝至每個IDR幀之前。下面代碼示例了寫入SPS、PPS的過程。
[cpp] view%20plain copy ![在CODE上查看代碼片]()
FILE *fp=fopen("test.264","ab"); AVCodecContext *pCodecCtx=... unsigned char *dummy=NULL; int dummy_len; AVBitStreamFilterContext* bsfc = av_bitstream_filter_init("h264_mp4toannexb"); av_bitstream_filter_filter(bsfc, pCodecCtx, NULL, &dummy, &dummy_len, NULL, 0, 0); fwrite(pCodecCtx->extradata,pCodecCtx-->extradata_size,1,fp); av_bitstream_filter_close(bsfc); free(dummy); 然后修改AVPacket的data。把前4個字節改為起始碼。示例代碼如下所示。[cpp] view%20plain copy ![在CODE上查看代碼片]()
char nal_start[]={0,0,0,1}; memcpy(packet->data,nal_start,4);
經過上述兩步也可以得到可以播放的H.264碼流,相對于第一種方法來說復雜一些。