天天看點

【基于libRTMP的流媒體直播之 AAC、H264 推送】

這段時間在搗騰基于 RTMP 協定的流媒體直播架構,其間參考了衆多部落客的文章,剩下一些細節問題自行琢磨也算摸索出個門道,現将自己認為比較惱人的 AAC 音頻幀的推送和解析、H264 碼流的推送和解析以及網上沒說清楚的地方分享給各位。

        言歸正傳,我們首先來看看 AAC 以及 H264 的推送。

        不論向 RTMP 伺服器推送音頻還是視訊,都需要按照 FLV 的格式進行封包。是以,在我們向伺服器推送第一個 AAC 或 H264 資料包之前,需要首先推送一個音頻 Tag [AAC Sequence Header] 以下簡稱“音頻同步包”,或者視訊 Tag [AVC Sequence Header] 以下簡稱“視訊同步包”。

AAC 音頻幀的推送                                         

【基于libRTMP的流媒體直播之 AAC、H264 推送】
【基于libRTMP的流媒體直播之 AAC、H264 推送】
【基于libRTMP的流媒體直播之 AAC、H264 推送】

        我們可以将其簡化并得到 AAC 音頻同步包的格式如下:

【基于libRTMP的流媒體直播之 AAC、H264 推送】

        音頻同步包大小固定為 4 個位元組。前兩個位元組被稱為 [AACDecoderSpecificInfo],用于描述這個音頻包應當如何被解析。後兩個位元組稱為 [AudioSpecificConfig],更加詳細的指定了音頻格式。

        [AACDecoderSpecificInfo] 倆位元組可以直接使用 FAAC 庫的 faacEncGetDecoderSpecificInfo 函數來擷取,也可以根據自己的音頻源進行計算。一般情況下,雙聲道,44kHz 采樣率的 AAC 音頻,其值為 0xAF00,示例代碼:

【基于libRTMP的流媒體直播之 AAC、H264 推送】

        音頻同步包的後兩個位元組 [AudioSpecificConfig] 的結構,援引其他部落客圖如下:

【基于libRTMP的流媒體直播之 AAC、H264 推送】

        我們隻需參照上述結構計算出對應的值即可。至此,4 個位元組的音頻同步包組裝完畢,便可推送至 RTMP 伺服器,示例代碼如下:

【基于libRTMP的流媒體直播之 AAC、H264 推送】

        網上有部落客說音頻采樣率小于等于 44100 時 SamplingFrequencyIndex 應當選擇 3(48kHz),Bill 測試發現采樣率等于 44100 時設定标記為 3 或 4 均能正常推送并在用戶端播放,不過我們還是應當按照标準規定的行事,故此處的 SamplingFrequencyIndex 選 4。

        完成音頻同步包的推送後,我們便可向伺服器推送普通的 AAC 資料包,推送資料包時,[AACDecoderSpecificInfo] 則變為 0xAF01,向伺服器說明這個包是普通 AAC 資料包。後面的資料為 AAC 原始資料去掉前 7 個位元組(若存在 CRC 校驗,則去掉前 9 個位元組),我們同樣以一張簡化的表格加以闡釋:

【基于libRTMP的流媒體直播之 AAC、H264 推送】

        推送普通 AAC 資料包的示例代碼:

【基于libRTMP的流媒體直播之 AAC、H264 推送】

        至此,我們便完成了 AAC 音頻的推送流程。此時可嘗試使用 VLC 或其他支援 RTMP 協定的播放器連接配接到伺服器測試正在直播的 AAC 音頻流。     

H264 碼流的推送                                           

【基于libRTMP的流媒體直播之 AAC、H264 推送】
【基于libRTMP的流媒體直播之 AAC、H264 推送】

        由于視訊同步包前半部分比較簡單易懂,仔細閱讀上述标準便可明白如何操作,故 Bill 不另作圖闡釋。由上圖可知,我們的視訊同步包 FrameType == 1,CodecID == 7,VideoData == AVCVIDEOPACKET,繼續展開 AVCVIDEOPACKET,我們可以得到 AVCPacketType == 0x00,CompositionTime == 0x000000,Data == AVCDecoderConfigurationRecord。

        是以構造視訊同步包的關鍵點便是構造 AVCDecoderConfigurationRecord。同樣,我們援引其他部落客的圖檔來闡釋這個結構的細節:

【基于libRTMP的流媒體直播之 AAC、H264 推送】

        當我們得到本次 H264 碼流的 Sps 以及 Pps 的相關資訊後,我們便可以完成視訊同步包的組裝,示例代碼如下:

【基于libRTMP的流媒體直播之 AAC、H264 推送】
【基于libRTMP的流媒體直播之 AAC、H264 推送】

        至此,視訊同步包便構造完畢并推送給 RTMP 伺服器。接下來隻需要将普通 H264 碼流稍加封裝便可實作 H264 直播,下面我們來看一下普通視訊包的組裝過程。

        構造視訊資料包的示例代碼如下:

【基于libRTMP的流媒體直播之 AAC、H264 推送】
【基于libRTMP的流媒體直播之 AAC、H264 推送】

        至此 H264 碼流的整個推送流程便已完成,我們可以使用 VLC 或其他支援 RTMP 協定的播放器進行測試。

關于 AAC 音頻幀及 H264 碼流的時間戳         

        通過前文的步驟我們已經能夠将 AAC 音頻幀以及 H264 碼流正常推送到 RTMP 直播伺服器,并能夠使用相關播放器進行播放。但播放的效果如何還取決于時間戳的設定。

        在網絡良好的情況下,自己最開始使用的音頻流時間戳為 AAC 編碼器剛輸出一幀的時間,視訊流時間戳為 H264 編碼器剛編碼出來一幀的時間,VLC 播放端就頻繁報異常,要麼是重新緩沖,要麼直接沒聲音或花屏。在排除了推送步驟實作有誤的問題後,Bill 發現問題出在時間戳上。

        之後有網友說直播流的時間戳不論音頻還是視訊,在整體時間線上應當呈現遞增趨勢。由于 Bill 最開始的時間戳計算方法是按照音視訊分開計算,而音頻時戳和視訊時戳并不是在一條時間線上,這就有可能出現音頻時戳在某一個時間點比對應的視訊時戳小, 在某一個時間點又跳變到比對應的視訊時戳大,導緻播放端無法對齊。

        目前采用的時間戳為底層發送 RTMP 包的時間,不區分音頻流還是視訊流,統一使用即将發送 RTMP 包的系統時間作為該包的時間戳。目前區域網路測試播放效果良好,音視訊同步且流暢。

參考博文

繼續閱讀