在使用FFMPEG的類庫進行編程的過程當中,能夠直接輸出解複用以後的的視頻數據碼流。只須要在每次調用av_read_frame()以後將獲得的視頻的AVPacket存爲本地文件便可。編程
經試驗,在分離MPEG2碼流的時候,直接存儲AVPacket便可。函數
在分離H.264碼流的時候,直接存儲AVPacket後的文件多是不能播放的。ui
若是視音頻複用格式是TS(MPEG2 Transport Stream),直接存儲後的文件是能夠播放的。編碼
複用格式是FLV,MP4則不行。spa
通過長時間資料搜索發現,FLV,MP4這些屬於「特殊容器」,須要通過如下處理才能獲得可播放的H.264碼流:指針
1.第一次存儲AVPacket以前須要在前面加上H.264的SPS和PPS。這些信息存儲在AVCodecContext的extradata裏面。code
而且須要使用FFMPEG中的名爲"h264_mp4toannexb"的bitstream filter 進行處理。視頻
而後將處理後的extradata存入文件blog
具體代碼以下:(源碼見最後)內存
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);
2.經過查看FFMPEG源代碼咱們發現,AVPacket中的數據起始處沒有分隔符(0x00000001), 也不是0x6五、0x6七、0x6八、0x41等字節,因此能夠AVPacket確定這不是標準的nalu。其實,AVPacket前4個字表示的是nalu的長度,從第5個字節開始纔是nalu的數據。因此直接將AVPacket前4個字節替換爲0x00000001便可獲得標準的nalu數據。
具體代碼以下:
char nal_start[]={0,0,0,1}; fwrite(nal_start,4,1,fp); fwrite(pkt->data+4,pkt->size-4,1,fp); fclose(fp);
通過以上兩步處理以後,咱們就獲得了能夠正常播放的H.264碼流。
3.ffmpeg中提供了一個流過濾器"h264_mp4toannexb"完成這項工做(從extradata中解析出sps及pps),關鍵代碼以下:
1 //h264_mp4toannexb_bsf.c 2 static int h264_mp4toannexb_filter(AVBitStreamFilterContext *bsfc, 3 AVCodecContext *avctx, const char *args, 4 uint8_t **poutbuf, int *poutbuf_size, 5 const uint8_t *buf, int buf_size, 6 int keyframe) { 7 H264BSFContext *ctx = bsfc->priv_data; 8 uint8_t unit_type; 9 int32_t nal_size; 10 uint32_t cumul_size = 0; 11 const uint8_t *buf_end = buf + buf_size; 12 13 14 /* nothing to filter */ 15 if (!avctx->extradata || avctx->extradata_size < 6) { 16 *poutbuf = (uint8_t*) buf; 17 *poutbuf_size = buf_size; 18 return 0; 19 } 20 21 // 22 //從extradata中分析出SPS、PPS 23 // 24 /* retrieve sps and pps NAL units from extradata */ 25 if (!ctx->extradata_parsed) { 26 uint16_t unit_size; 27 uint64_t total_size = 0; 28 uint8_t *out = NULL, unit_nb, sps_done = 0, sps_seen = 0, pps_seen = 0; 29 const uint8_t *extradata = avctx->extradata+4; //跳過前4個字節 30 static const uint8_t nalu_header[4] = {0, 0, 0, 1}; 31 32 33 /* retrieve length coded size */ 34 ctx->length_size = (*extradata++ & 0x3) + 1; //用於指示表示編碼數據長度所需字節數 35 if (ctx->length_size == 3) 36 return AVERROR(EINVAL); 37 38 39 /* retrieve sps and pps unit(s) */ 40 unit_nb = *extradata++ & 0x1f; /* number of sps unit(s) */ 41 if (!unit_nb) { 42 goto pps; 43 } else { 44 sps_seen = 1; 45 } 46 47 48 while (unit_nb--) { 49 void *tmp; 50 51 52 unit_size = AV_RB16(extradata); 53 total_size += unit_size+4; 54 if (total_size > INT_MAX - FF_INPUT_BUFFER_PADDING_SIZE || 55 extradata+2+unit_size > avctx->extradata+avctx->extradata_size) { 56 av_free(out); 57 return AVERROR(EINVAL); 58 } 59 tmp = av_realloc(out, total_size + FF_INPUT_BUFFER_PADDING_SIZE); 60 if (!tmp) { 61 av_free(out); 62 return AVERROR(ENOMEM); 63 } 64 out = tmp; 65 memcpy(out+total_size-unit_size-4, nalu_header, 4); 66 memcpy(out+total_size-unit_size, extradata+2, unit_size); 67 extradata += 2+unit_size; 68 pps: 69 if (!unit_nb && !sps_done++) { 70 unit_nb = *extradata++; /* number of pps unit(s) */ 71 if (unit_nb) 72 pps_seen = 1; 73 } 74 } 75 76 77 if(out) 78 memset(out + total_size, 0, FF_INPUT_BUFFER_PADDING_SIZE); 79 80 81 if (!sps_seen) 82 av_log(avctx, AV_LOG_WARNING, "Warning: SPS NALU missing or invalid. The resulting stream may not play.\n"); 83 if (!pps_seen) 84 av_log(avctx, AV_LOG_WARNING, "Warning: PPS NALU missing or invalid. The resulting stream may not play.\n"); 85 86 87 av_free(avctx->extradata); 88 avctx->extradata = out; 89 avctx->extradata_size = total_size; 90 ctx->first_idr = 1; 91 ctx->extradata_parsed = 1; 92 } 93 94 95 *poutbuf_size = 0; 96 *poutbuf = NULL; 97 do { 98 if (buf + ctx->length_size > buf_end) 99 goto fail; //buf爲NULL時,如下代碼將再也不執行 100 101 102 // 103 //用於保存數據長度的字節數,是在分析原extradata計算出來的 104 // 105 if (ctx->length_size == 1) { 106 nal_size = buf[0]; 107 } else if (ctx->length_size == 2) { 108 nal_size = AV_RB16(buf); 109 } else 110 nal_size = AV_RB32(buf); 111 112 113 buf += ctx->length_size; 114 unit_type = *buf & 0x1f; 115 116 117 if (buf + nal_size > buf_end || nal_size < 0) 118 goto fail; 119 120 121 /* prepend only to the first type 5 NAL unit of an IDR picture */ 122 if (ctx->first_idr && unit_type == 5) { 123 // 124 //copy IDR 幀時,須要將sps及pps一同拷貝 125 // 126 if (alloc_and_copy(poutbuf, poutbuf_size, 127 avctx->extradata, avctx->extradata_size, 128 buf, nal_size) < 0) 129 goto fail; 130 ctx->first_idr = 0; 131 } else { 132 // 133 //非IDR幀,沒有sps及pps 134 if (alloc_and_copy(poutbuf, poutbuf_size, 135 NULL, 0, 136 buf, nal_size) < 0) 137 goto fail; 138 if (!ctx->first_idr && unit_type == 1) 139 ctx->first_idr = 1; 140 } 141 142 143 buf += nal_size; 144 cumul_size += nal_size + ctx->length_size; 145 } while (cumul_size < buf_size); 146 147 148 return 1; 149 150 151 fail: 152 av_freep(poutbuf); 153 *poutbuf_size = 0; 154 return AVERROR(EINVAL); 155 }
通常狀況下,extradata中包含一個sps、一個pps 的nalu, 從上面的代碼中容易看出extradata的數據格式。分析後的sps及pps依然儲存在extradata域中,並添加了起始符。從代碼中還能夠看出,上面的函數會將sps、pps及packet中的數據,都copy到poutbuf指示的內存中,若是不須要copy到指定內存,直接給buf參數傳入空值便可。