這段時間在搗騰基于 RTMP 協定的流媒體直播架構,其間參考了衆多部落客的文章,剩下一些細節問題自行琢磨也算摸索出個門道,現将自己認為比較惱人的 AAC 音頻幀的推送和解析、H264 碼流的推送和解析以及網上沒說清楚的地方分享給各位。
言歸正傳,我們首先來看看 AAC 以及 H264 的推送。
不論向 RTMP 伺服器推送音頻還是視訊,都需要按照 FLV 的格式進行封包。是以,在我們向伺服器推送第一個 AAC 或 H264 資料包之前,需要首先推送一個音頻 Tag [AAC Sequence Header] 以下簡稱“音頻同步包”,或者視訊 Tag [AVC Sequence Header] 以下簡稱“視訊同步包”。
AAC 音頻幀的推送

我們可以将其簡化并得到 AAC 音頻同步包的格式如下:
音頻同步包大小固定為 4 個位元組。前兩個位元組被稱為 [AACDecoderSpecificInfo],用于描述這個音頻包應當如何被解析。後兩個位元組稱為 [AudioSpecificConfig],更加詳細的指定了音頻格式。
[AACDecoderSpecificInfo] 倆位元組可以直接使用 FAAC 庫的 faacEncGetDecoderSpecificInfo 函數來擷取,也可以根據自己的音頻源進行計算。一般情況下,雙聲道,44kHz 采樣率的 AAC 音頻,其值為 0xAF00,示例代碼:
音頻同步包的後兩個位元組 [AudioSpecificConfig] 的結構,援引其他部落客圖如下:
我們隻需參照上述結構計算出對應的值即可。至此,4 個位元組的音頻同步包組裝完畢,便可推送至 RTMP 伺服器,示例代碼如下:
網上有部落客說音頻采樣率小于等于 44100 時 SamplingFrequencyIndex 應當選擇 3(48kHz),Bill 測試發現采樣率等于 44100 時設定标記為 3 或 4 均能正常推送并在用戶端播放,不過我們還是應當按照标準規定的行事,故此處的 SamplingFrequencyIndex 選 4。
完成音頻同步包的推送後,我們便可向伺服器推送普通的 AAC 資料包,推送資料包時,[AACDecoderSpecificInfo] 則變為 0xAF01,向伺服器說明這個包是普通 AAC 資料包。後面的資料為 AAC 原始資料去掉前 7 個位元組(若存在 CRC 校驗,則去掉前 9 個位元組),我們同樣以一張簡化的表格加以闡釋:
推送普通 AAC 資料包的示例代碼:
至此,我們便完成了 AAC 音頻的推送流程。此時可嘗試使用 VLC 或其他支援 RTMP 協定的播放器連接配接到伺服器測試正在直播的 AAC 音頻流。
H264 碼流的推送
由于視訊同步包前半部分比較簡單易懂,仔細閱讀上述标準便可明白如何操作,故 Bill 不另作圖闡釋。由上圖可知,我們的視訊同步包 FrameType == 1,CodecID == 7,VideoData == AVCVIDEOPACKET,繼續展開 AVCVIDEOPACKET,我們可以得到 AVCPacketType == 0x00,CompositionTime == 0x000000,Data == AVCDecoderConfigurationRecord。
是以構造視訊同步包的關鍵點便是構造 AVCDecoderConfigurationRecord。同樣,我們援引其他部落客的圖檔來闡釋這個結構的細節:
當我們得到本次 H264 碼流的 Sps 以及 Pps 的相關資訊後,我們便可以完成視訊同步包的組裝,示例代碼如下:
至此,視訊同步包便構造完畢并推送給 RTMP 伺服器。接下來隻需要将普通 H264 碼流稍加封裝便可實作 H264 直播,下面我們來看一下普通視訊包的組裝過程。
構造視訊資料包的示例代碼如下:
至此 H264 碼流的整個推送流程便已完成,我們可以使用 VLC 或其他支援 RTMP 協定的播放器進行測試。
關于 AAC 音頻幀及 H264 碼流的時間戳
通過前文的步驟我們已經能夠将 AAC 音頻幀以及 H264 碼流正常推送到 RTMP 直播伺服器,并能夠使用相關播放器進行播放。但播放的效果如何還取決于時間戳的設定。
在網絡良好的情況下,自己最開始使用的音頻流時間戳為 AAC 編碼器剛輸出一幀的時間,視訊流時間戳為 H264 編碼器剛編碼出來一幀的時間,VLC 播放端就頻繁報異常,要麼是重新緩沖,要麼直接沒聲音或花屏。在排除了推送步驟實作有誤的問題後,Bill 發現問題出在時間戳上。
之後有網友說直播流的時間戳不論音頻還是視訊,在整體時間線上應當呈現遞增趨勢。由于 Bill 最開始的時間戳計算方法是按照音視訊分開計算,而音頻時戳和視訊時戳并不是在一條時間線上,這就有可能出現音頻時戳在某一個時間點比對應的視訊時戳小, 在某一個時間點又跳變到比對應的視訊時戳大,導緻播放端無法對齊。
目前采用的時間戳為底層發送 RTMP 包的時間,不區分音頻流還是視訊流,統一使用即将發送 RTMP 包的系統時間作為該包的時間戳。目前區域網路測試播放效果良好,音視訊同步且流暢。
參考博文