某些封裝格式(例如MP4/FLV/MKV等)的H.264碼流的SPS和PPS信息存儲在AVCodeccontext結構體的extradata中。分離某些封裝格式(例如MP4/FLV/MKV等)中的H.264的時候,需要首先寫入SPS和PPS,否則會導致分離出來的數據沒有SPS、PPS而無法播。需要使用ffmpeg中名稱為“h264 mp4toannexb”的bitstream filter處理
關鍵概念解析與操作指南
?一、SPS/PPS 的作用與存儲位置?
- ?SPS (Sequence Parameter Set)?
定義視頻序列的全局參數,包括分辨率、幀率、編碼級別等。 - ?PPS (Picture Parameter Set)?
定義圖像解碼參數,如熵編碼模式、分片參數等。 - ?存儲位置?
- 在 MP4/FLV/MKV 等封裝格式中,SPS/PPS 通常存儲在 ?
AVCodecContext->extradata
? 中,而非嵌入到每個關鍵幀。 - 裸 H.264 流(如 Annex B 格式)要求在每個關鍵幀(IDR幀)前攜帶 SPS/PPS。
- 在 MP4/FLV/MKV 等封裝格式中,SPS/PPS 通常存儲在 ?
?二、問題根源分析?
當直接從封裝格式(如 MP4)中提取 H.264 裸流時:
- ?直接提取的缺陷?:
輸出的 H.264 數據缺少 SPS/PPS 頭部信息,導致播放器無法初始化解碼器。 - ?典型報錯?:
[h264 @ 0x7f8a5c006800] no frame! Invalid NAL unit size
?三、解決方案:使用 Bitstream Filter?
通過 FFmpeg 的 ?h264_mp4toannexb
? 比特流過濾器,實現以下功能:
- ?從?
extradata
?提取 SPS/PPS?:
自動讀取?AVCodecContext->extradata
?中的參數集。 - ?轉換為 Annex B 格式?:
- 在碼流起始位置插入 SPS/PPS。
- 在每個關鍵幀前插入起始碼?
0x00000001
?或?0x000001
。
?四、操作命令示例?
提取 MP4 中的 H.264 裸流并修復 SPS/PPS:
ffmpeg -i input.mp4 -c:v copy -bsf:v h264_mp4toannexb output.h264
- ?參數說明?:
-c:v copy
:直接復制視頻流,不重新編碼。-bsf:v h264_mp4toannexb
:對視頻流應用比特流過濾器。
?五、不同封裝格式的適配?
?封裝格式? | ?比特流過濾器? | ?作用? |
---|---|---|
MP4 | h264_mp4toannexb | 插入 SPS/PPS,添加起始碼 |
FLV | h264_mp4toannexb | 同上(FLV 的 H.264 存儲方式類似 MP4) |
MKV | h264_mp4toannexb | 同上 |
TS | 無需過濾器 | TS 流已符合 Annex B 格式 |
?六、驗證輸出文件?
- ?使用?
ffprobe
?檢查?:
確認輸出中包含?ffprobe -v error -show_streams output.h264
codec_tag_string=avc1
?和?extradata
。 - ?播放測試?:
ffplay output.h264
?七、底層原理詳解?
- ?
extradata
?結構?- MP4 中?
extradata
?的格式為 ?AVCDecoderConfigurationRecord?,包含 SPS/PPS 的二進制數據。 h264_mp4toannexb
?過濾器解析此結構,將其轉換為 Annex B 的頭部。
- MP4 中?
- ?Annex B 格式要求?
- 每個 NAL 單元以起始碼?
0x00000001
?或?0x000001
?開頭。 - SPS/PPS 必須出現在碼流開頭或關鍵幀前。
- 每個 NAL 單元以起始碼?
?八、擴展場景:處理多路流?
若需合并多個 MP4 文件的 H.264 流,需對每個輸入單獨應用過濾器:
ffmpeg -i input1.mp4 -i input2.mp4 -filter_complex \ "[0:v]bsf=h264_mp4toannexb[0v];[1:v]bsf=h264_mp4toannexb[1v]; \ [0v][1v]concat=n=2:v=1" -c:v libx264 output_combined.mp4
通過上述方法,可確保提取的 H.264 裸流符合播放器要求,避免因缺失 SPS/PPS 導致的解碼失敗問題。
ffmpeg -i 1.mp4 -c:v copy output_without_sps_pps.h264轉換完成后通過ffplay播放的h264為什么播放速度很快
問題原因解析
使用?ffmpeg -i 1.mp4 -c:v copy output.h264
?轉換的 H.264 裸流播放速度異常加快,主要與 ?時間戳信息丟失? 和 ?封裝格式差異? 相關,具體原因如下:
1. ?時間基(Timebase)未正確傳遞?
- ?MP4 容器特性?:MP4 文件的時間戳基值(
time_base
)通常為?1/1000
?或?1/90000
,而 H.264 裸流無容器層,默認時間基可能被錯誤解析為?1/25
?或?1/30
[?2][?7]。 - ?播放器誤判?:
ffplay
?根據 H.264 裸流的 NAL 單元間隔推算幀率,若時間戳缺失或錯誤,會強制按默認高幀率(如 25fps)播放[?1][?7]。
2. ?關鍵幀間隔(GOP)與 B/P 幀依賴斷裂?
- ?
-c:v copy
?的局限性?:直接復制流時,B/P 幀的解碼依賴關系可能因裸流缺少容器層的元數據而斷裂,導致解碼器跳過參考幀計算,加速播放[?2][?7]。
3. ?SPS/PPS 信息未嵌入?
- ?缺失參數集?:MP4 中的 SPS/PPS 存儲在?
extradata
?中,直接復制時未通過?h264_mp4toannexb
?過濾器插入碼流,播放器可能無法正確初始化解碼器時間軸[?1][?2]。
解決方案
1. ?強制指定輸出幀率?
通過?-r
?參數顯式定義輸出幀率,覆蓋默認值:
bashCopy Code
ffmpeg -i 1.mp4 -c:v copy -r 30 output.h264
- ?作用?:強制 H.264 裸流按 30fps 播放,避免時間基誤判[?2][?7]。
2. ?添加比特流過濾器處理時間戳?
使用?setpts
?濾鏡修正時間戳:
bashCopy Code
ffmpeg -i 1.mp4 -c:v copy -bsf:v h264_mp4toannexb -vf "setpts=PTS-STARTPTS" output.h264
- ?關鍵參數?:
-bsf:v h264_mp4toannexb
:插入 SPS/PPS 并修正 NAL 單元格式[?1][?2]。setpts=PTS-STARTPTS
:重置時間戳為從零開始,消除容器時間基差異[?7]。
3. ?驗證與調試?
- ?檢查時間戳信息?:
bashCopy Code
確認輸出幀的?ffprobe -show_frames output.h264 | grep "pkt_pts_time"
pkt_pts_time
?均勻遞增(如 0.033s 間隔對應 30fps)[?7]。 - ?對比播放效果?:
bashCopy Code
ffplay -vf "setpts=PTS-STARTPTS" output.h264 # 強制按原始速度播放
擴展場景:直播推流中的類似問題
若需將 USB 相機的 RGBA 數據編碼為 H.264 并推流至 RTMP,需在編碼器初始化時手動插入 SPS/PPS,并通過?avcodec_parameters_from_context
?傳遞至輸出流上下文,否則 RTMP 播放會出現加速或花屏[?1][?2]。
總結
H.264 裸流播放速度異常的本質是 ?時間戳與幀率元數據丟失?,需通過過濾器修正時間基或顯式指定幀率參數[?1][?2][?7]。