延續音頻碼流分離的主題,本文聚焦於 H.264 視頻碼流的提取。同樣基於 av_read_frame() 獲取 AVPacket,但 H.264 在 FLV、MP4 等容器中的封裝特性導致直接存儲無法播放,需經過兩步關鍵處理:補全 SPS/PPS 並轉換為 Annex B 格式(起始碼 0x00000001)。
AVPacket 可播放,因 TS 封裝已包含起始碼或 PES 頭。H.264 解碼器初始化需要 SPS 與 PPS,它們存儲在 AVCodecContext->extradata 中。但在 FLV/MP4 中,extradata 格式為 AVCC(長度前綴),需轉換為 Annex B 格式(起始碼分隔)才能被標準解碼器識別。
FFmpeg 提供了 h264_mp4toannexb Bitstream Filter(BSF)專門完成此轉換。核心代碼邏輯如下:
// 初始化 BSF 上下文
AVBitStreamFilterContext* bsfc = av_bitstream_filter_init("h264_mp4toannexb");
// 執行轉換:輸入 extradata,輸出帶起始碼的 SPS/PPS 數據
unsigned char *dummy = NULL;
int dummy_len = 0;
av_bitstream_filter_filter(bsfc,
fmt_ctx->streams[video_stream_index]->codec,
NULL,
&dummy, &dummy_len,
NULL, 0, 0);
// 寫入文件(已包含起始碼的 SPS/PPS)
fwrite(fmt_ctx->streams[video_stream_index]->codec->extradata,
fmt_ctx->streams[video_stream_index]->codec->extradata_size, 1, fp);
av_bitstream_filter_close(bsfc);
free(dummy);
分析 FLV/MP4 中的 AVPacket->data 發現:前 4 字節為 NALU 長度(大端序),第 5 字節起才是真正的 NALU 數據(不含起始碼)。標準 H.264 碼流要求每個 NALU 以 0x00 0x00 0x00 0x01 起始。
處理邏輯極簡單:
char nal_start[] = {0, 0, 0, 1};
fwrite(nal_start, 4, 1, fp); // 寫入起始碼
fwrite(pkt->data + 4, pkt->size - 4, 1, fp); // 跳過長度字段,寫入 NALU 本體
h264_mp4toannexb BSFextradata(含 SPS/PPS + 起始碼)av_read_frame() 獲取視頻幀pkt->data+4(去除長度前綴)av_bitstream_filter_filter,新版(4.0+)建議改用 av_bsf_send_packet/av_bsf_receive_packet API。AVPacket 的引用計數(av_packet_ref/av_packet_unref),避免內存洩漏。本文轉載自 “leixiaohua1020視音頻技術” 博客,原文地址:https://leixiaohua1020.blog.51cto.com/3974648/1303852