基本概念
透過下列的 3 個 FFMPEG API,將資料封裝成 mp4 格式。
- av_write_header
- av_interleaved_write_frame
- av_write_trailer
想直接看程式的人,可以參考這個範例,想直接在 iphone下測試,可以下載我寫的一個簡單範例。
實作過程中的故障排除
第一次測試 (ffmpeg)
測試方式:撥放本地端檔案,並且將檔案儲存在本地。
測試結果:VLC可以辨識檔案,可以知道影像檔長度,但撥放時並沒有影像。
問題分析:
1. 使用 mediainfo 檢視檔案封裝,比較其不同處。
原始檔案為 ftypmp42, 儲存後的檔案為ftypisom。此部分沒有關係。2. 使用 VLC 進行播放,強制印出除錯訊息,指令如下
"C:\Program Files (x86)\VideoLAN\VLC\vlc" -vvv test.mp4
接著可以透過下列操作來觀看錯誤訊息
[工具] -> [訊息] 或是
[檢視] -> [增加介面] -> [除錯紀錄]
分析log,由下圖可知,VLC已經正確解開 mp4 內的各個box
參考下圖,推測無法正確撥放的原因應與除錯訊息中的 "insane cropping not completely supported" 有關。
![]()
初步推測是 mp4 header 的資料不足,導致無法正確解碼,比對原始檔案,發現新產生的檔案中沒有正確的 avcC box (放置 SPS, PPS),使用 ultraedit 手動加入 avcC box的內容之後,檔案便可以使用 VLC與 QuickTime 播放。
剩下的問題就是如何透過程式碼,讓avcC box 正確的加入到 mp4 檔案內。
參考 ffmpeg mp4toannexb 原始碼,可以得知 ffmpeg 透過 extradata 欄位取得 SPS 與 PPS,因此只要正確的設定此欄位, avcC box 就可以正確的產生。
第二次測試 (live555+ffmpeg)
這次測試使用 live555 所擷取的封包當成輸入資料,透過 ffmpeg 的3個API,將資料封裝成 mp4 檔案。
使用 live555 作法也是類似的,不同點在於需要自行取得 SPS/PPS,並且放入到 extradata內。
An example of CodecCtx->extradata
注意1:每次儲存時,應該從iFrame開始存,畫面初期才會正常,否則播放初期可能會呈現馬賽克,
- 若使用 ffmpeg 讀取網路封包,則需要判斷 packet.flags 中是否存在 AV_PKT_FLAG_KEY
- 若使用 live555 讀取網路封包,則需要判斷 nal_unit_type&0x1f=0x06
注意2:關於pts,dts的設定
當呼叫avformat_write_header()時要記住此時的pts與dts(將其稱之為pts_init, dts_init),而當呼叫 av_interleaved_write_frame(fc, &pkt)時,記得要將此時的 pkt.pts, pkt.dts 的值減去pts_init, dts_init,這樣才是正確的設定值。