1. 程式人生 > >mux復用 demux解復用

mux復用 demux解復用

字節 參數 只需要 表示 get 播放 手工 context 想要

保存音頻包: 直接輸出解復用之後的的音頻數據碼流。只需要在每次調用av_read_frame()之後將得到的音頻的AVPacket存為本地文件即可。
但在分離AAC碼流的時候,直接存儲AVPacket後的文件可能是不能播放的。 分離H.264碼流的時候,直接存儲AVPacket後的文件可能是不能播放的。
復用格式是FLV,MP4等,直接存儲後的文件是不能播放的。
因為FLV,MP4這些屬於“特殊容器”。經過仔細對比後發現,調用av_read_frame()後得到的AVPacket裏面的內容是AAC純數據,就是那種不包含ADTS文件頭的AAC數據。(存儲AAC數據的AVPacket的data字段中的數據是不包含7字節ADTS文件頭的數據)因此如果想要得到可以播放的AAC文件,需要在每個AVPacket前面加上7字節ADTS文件頭。
demux分離時,h264需要h264_mp4toannexb,音頻aac好像沒有用到 mux時,h264也需要h264_mp4toannexb,aac用到aac_adtstoasc mux時對於某些封裝格式(例如MP4/FLV/MKV等)中的AAC,需要用到名稱為“aac_adtstoasc”的bitstream filter。 ============================================================ 保存視頻包:
分離某些封裝格式(例如MP4/FLV/MKV等)中的H.264的時候,需要首先寫入SPS和PPS

例如flv格式時,從av_read_frame中讀出的包也是不包含video tag header的裸視頻數據。
FLV,MP4這些“特殊容器”,需要經過以下2步處理才能得到可播放的H.264碼流: 1.第一次存儲AVPacket之前需要在前面加上H.264的SPS和PPS。這些信息存儲在AVCodecContext的extradata裏面。 並且需要使用FFMPEG中的名為"h264_mp4toannexb"的bitstream filter 進行處理。 然後將處理後的extradata存入文件 具體代碼如下: 代碼片段A
  1. unsigned char
    *dummy=NULL; //輸入的指針
  2. int dummy_len;
  3. AVBitStreamFilterContext* bsfc = av_bitstream_filter_init("h264_mp4toannexb");
  4. av_bitstream_filter_filter(bsfc, pCodecCtx, NULL, &dummy, &dummy_len, NULL, 0, 0);
  5. fwrite(pCodecCtx->extradata,pCodecCtx-->extradata_size,1,fp);
  6. av_bitstream_filter_close(bsfc);
  7. free(dummy);
2.通過查看FFMPEG源代碼發現,AVPacket中的數據起始處沒有分隔符(0x00000001), 也不是0x65、0x67、0x68、0x41等字節,所以可以肯定AVPacket不是標準的nalu。其實,AVPacket前4個字表示的是nalu的長度,從第5個字節開始才是nalu的數據。所以直接將AVPacket前4個字節替換為0x00000001即可得到標準的nalu數據。 具體代碼如下: 代碼片段B
  1. char nal_start[]={0,0,0,1};
  2. fwrite(nal_start,4,1,fp);
  3. fwrite(pkt->data+4,pkt->size-4,1,fp);
  4. fclose(fp);

  1. char nal_start[]={0,0,0,1};
  2. memcpy(packet->data,nal_start,4);

經過以上兩步處理之後,我們就得到了可以正常播放的H.264碼流 ====================================================

其它處理方式: (1)使用bitstream filter處理每個AVPacket(簡單) 把每個AVPacket中的數據(data字段)經過bitstream filter“過濾”一遍。關鍵函數是av_bitstream_filter_filter()。示例代碼如下。
  1. AVBitStreamFilterContext* h264bsfc = av_bitstream_filter_init("h264_mp4toannexb");
  2. while(av_read_frame(ifmt_ctx, &pkt)>=0){
  3. if(pkt.stream_index==videoindex){
  4. av_bitstream_filter_filter(h264bsfc, ifmt_ctx->streams[videoindex]->codec, NULL, &pkt.data, &pkt.size, pkt.data, pkt.size, 0);
  5. fwrite(pkt.data,1,pkt.size,fp_video);
  6. //...
  7. }
  8. av_free_packet(&pkt);
  9. }
  10. av_bitstream_filter_close(h264bsfc);
代碼中,把av_bitstream_filter_filter()的輸入數據和輸出數據(分別對應第4,5,6,7個參數)都設置成AVPacket的data字段就可以了。
經過上述代碼處理之後,AVPacket中的數據有如下變化: *每個AVPacket的data添加了H.264的NALU的起始碼{0,0,0,1} *每個IDR幀數據前面添加了SPS和PPS (2)手工添加SPS,PPS(稍微復雜) 將AVCodecContext的extradata數據經過bitstream filter處理之後得到SPS、PPS,拷貝至每個IDR幀之前。“代碼片段A”示例了寫入SPS、PPS的過程。


然後修改AVPacket的data。把前4個字節改為起始碼。參見:代碼片段B



參考: 視音頻分離器(demuxer):ts->h264+aac 利用av_interleaved_write_frame
視音頻復用器(muxer):h264+aac->mkv,用到av_interleaved_write_frame 分離多媒體文件中的音頻碼流
分離多媒體文件中的H.264碼流
視音頻分離器簡化版(demuxer-simple):flv->h264+aac

mux復用 demux解復用