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