H264 推流到RTMP服务器
這段時間在搗騰基于?RTMP?協議的流媒體直播框架,其間參考了眾多博主的文章,剩下一些細節問題自行琢磨也算摸索出個門道,現將自己認為比較惱人的?AAC?音頻幀的推送和解析、H264?碼流的推送和解析以及網上沒說清楚的地方分享給各位。
????????RTMP?協議棧的實現,Bill?直接使用的?libRTMP,關于?libRTMP?的編譯、基本使用方法,以及簡單的流媒體直播框架,請參見博文[C++實現RTMP協議發送H.264編碼及AAC編碼的音視頻],言簡意賅,故不再贅述。
????????言歸正傳,我們首先來看看?AAC?以及?H264?的推送。
? ? ? ? 不論向?RTMP?服務器推送音頻還是視頻,都需要按照?FLV?的格式進行封包。因此,在我們向服務器推送第一個?AAC?或?H264?數據包之前,需要首先推送一個音頻?Tag?[AAC Sequence Header]?以下簡稱“音頻同步包”,或者視頻?Tag?[AVC Sequence Header]?以下簡稱“視頻同步包”。
?
AAC 音頻幀的推送 ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
????????我們首先來看看音頻?Tag,根據?FLV?標準?Audio Tags?一節的描述:
?
? ? ? ? 我們可以將其簡化并得到?AAC?音頻同步包的格式如下:
?
?
?
?
?
?
?
?
? ? ? ? 音頻同步包大小固定為?4?個字節。前兩個字節被稱為?[AACDecoderSpecificInfo],用于描述這個音頻包應當如何被解析。后兩個字節稱為?[AudioSpecificConfig],更加詳細的指定了音頻格式。
? ? ? ??[AACDecoderSpecificInfo]?倆字節可以直接使用?FAAC?庫的?faacEncGetDecoderSpecificInfo?函數來獲取,也可以根據自己的音頻源進行計算。一般情況下,雙聲道,44kHz?采樣率的?AAC?音頻,其值為?0xAF00,示例代碼:
?
?
?
????????根據?FLV?標準?不難得知,[AACDecoderSpecificInfo]?第?1?個字節高?4?位?|1010|?代表音頻數據編碼類型為?AAC,接下來?2?位?|11|?表示采樣率為?44kHz,接下來?1?位?|1|?表示采樣點位數?16bit,最低?1?位?|1|?表示雙聲道。其第二個字節表示數據包類型,0?則為?AAC?音頻同步包,1?則為普通?AAC?數據包。
? ? ? ? 音頻同步包的后兩個字節?[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 碼流的推送? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
????????前面提到過,向?RTMP?服務器發送?H264?碼流,需要按照?FLV?格式進行封包,并且首先需要發送視頻同步包?[AVC Sequence Header]。我們依舊先閱讀?FLV?標準?Video Tags?一節:
????????由于視頻同步包前半部分比較簡單易懂,仔細閱讀上述標準便可明白如何操作,故?Bill?不另作圖闡釋。由上圖可知,我們的視頻同步包?FrameType == 1,CodecID == 7,VideoData == AVCVIDEOPACKET,繼續展開?AVCVIDEOPACKET,我們可以得到?AVCPacketType == 0x00,CompositionTime == 0x000000,Data == AVCDecoderConfigurationRecord。
????????因此構造視頻同步包的關鍵點便是構造?AVCDecoderConfigurationRecord。同樣,我們援引其他博主的圖片來闡釋這個結構的細節:
????????其中需要額外計算的是?H264?碼流的?Sps?以及?Pps,這兩個關鍵數據可以在開始編碼?H264?的時候提取出來并加以保存,在需要時直接使用即可。具體做法請讀者自行?Google?或參見?參考博文[2],在此不再贅述。
????????當我們得到本次?H264?碼流的?Sps?以及?Pps?的相關信息后,我們便可以完成視頻同步包的組裝,示例代碼如下:
?
?
?
?
?
? ? ? ? 至此,視頻同步包便構造完畢并推送給?RTMP?服務器。接下來只需要將普通?H264?碼流稍加封裝便可實現?H264?直播,下面我們來看一下普通視頻包的組裝過程。
????????回顧?FLV?標準?的?Video Tags?一節,我們可以得到?H264?普通數據包的封包信息,FrameType ==?(H264 I?幀?? 1 : 2),CodecID == 7,VideoData == AVCVIDEOPACKET,繼續展開,我們可以得到 ?AVCPacketType == 0x01,CompositionTime?此處仍然設置為?0x000000,具體原因?TODO(billhoo),Data == H264 NALU Size + NALU Raw Data。
????????構造視頻數據包的示例代碼如下:
?
????????至此?H264?碼流的整個推送流程便已完成,我們可以使用?VLC?或其他支持?RTMP?協議的播放器進行測試。
?
關于 AAC 音頻幀及 H264 碼流的時間戳? ? ? ? ?
????????通過前文的步驟我們已經能夠將?AAC?音頻幀以及?H264?碼流正常推送到?RTMP?直播服務器,并能夠使用相關播放器進行播放。但播放的效果如何還取決于時間戳的設定。
????????在網絡良好的情況下,自己最開始使用的音頻流時間戳為?AAC?編碼器剛輸出一幀的時間,視頻流時間戳為?H264?編碼器剛編碼出來一幀的時間,VLC?播放端就頻繁報異常,要么是重新緩沖,要么直接沒聲音或花屏。在排除了推送步驟實現有誤的問題后,Bill?發現問題出在時間戳上。
????????之后有網友說直播流的時間戳不論音頻還是視頻,在整體時間線上應當呈現遞增趨勢。由于?Bill?最開始的時間戳計算方法是按照音視頻分開計算,而音頻時戳和視頻時戳并不是在一條時間線上,這就有可能出現音頻時戳在某一個時間點比對應的視頻時戳小, 在某一個時間點又跳變到比對應的視頻時戳大,導致播放端無法對齊。
????????目前采用的時間戳為底層發送?RTMP?包的時間,不區分音頻流還是視頻流,統一使用即將發送?RTMP?包的系統時間作為該包的時間戳。目前局域網測試播放效果良好,音視頻同步且流暢。
總結
以上是生活随笔為你收集整理的H264 推流到RTMP服务器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 比较两个人的年龄大小
- 下一篇: [Python] Matchering2