概述
隨著整個互聯(lián)網(wǎng)的崛起,數(shù)據(jù)傳遞的形式也在不斷升級變化陋率,總的流行趨勢如下:
<pre style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important; font-size: inherit; color: inherit; line-height: inherit;">
純文本的短信教翩,QQ -> 空間,微博,朋友圈的圖片文字結(jié)合 -> 微信語音 -> 各大直播軟件 -> 抖音短視頻
</pre>
音視頻的發(fā)展正在向各個行業(yè)不斷擴展斥废,從教育的遠程授課椒楣,交通的人臉識別,醫(yī)療的遠程就醫(yī)等等营袜,音視頻方向已經(jīng)占據(jù)一個相當重要的位置撒顿,而音視頻真正入門的文章又少之甚少,一個剛畢業(yè)小白可能很難切入理解,因為音視頻中涉及大量理論知識,而代碼的書寫需要結(jié)合這些理論,所以搞懂音視頻,編解碼等理論知識至關(guān)重要.本人也是從實習開始接觸音視頻項目,看過很多人的文章荚板,在這里總結(jié)一個通俗易懂的文章凤壁,讓更多準備學(xué)習音視頻的同學(xué)更快入門.
劃重點
本文中理論知識來自于各種音視頻文章的歸納音視頻編碼基本原理匯總,其中也會有一些我自己總結(jié)增加的部分.若有錯誤可評論,檢查后會更正。
為了防止大家理解過于空洞,作者花了三個月時間將最常用,最重要的一些功能的理論知識及實戰(zhàn)Demo親自寫出來,配合文章閱讀效果更佳.每一部分的文章可以在下面每章章節(jié)開始的深入學(xué)習中點擊鏈接查看, 鏈接中文章均有Github地址,每個Demo都親測可以通過,可以下載Demo運行.
如果喜歡,請幫忙點贊并支持轉(zhuǎn)載,轉(zhuǎn)載請附原文鏈接.
原理
采集
無論是iOS平臺,還是安卓平臺,我們都是需要借助官方的API實現(xiàn)一系列相關(guān)功能.首先我們要明白我們想要什么,最開始我們需要一部手機,智能手機中攝像頭是不可缺少的一部分,所以我們通過一些系統(tǒng)API獲取就要可以獲取物理攝像頭將采集到的視頻數(shù)據(jù)與麥克風采集到的音頻數(shù)據(jù).-
處理
音頻和視頻原始數(shù)據(jù)本質(zhì)都是一大段數(shù)據(jù),系統(tǒng)將其包裝進自定義的結(jié)構(gòu)體中,通常都以回調(diào)函數(shù)形式提供給我們,拿到音視頻數(shù)據(jù)后,可以根據(jù)各自項目需求做一系列特殊處理,如: 視頻的旋轉(zhuǎn),縮放,濾鏡,美顏,裁剪等等功能, 音頻的單聲道降噪,消除回聲,靜音等等功能.
-
編碼
原始數(shù)據(jù)做完自定義處理后就可以進行傳輸,像直播這樣的功能就是把采集好的視頻數(shù)據(jù)發(fā)送給服務(wù)器,以在網(wǎng)頁端供所有粉絲觀看,而傳輸由于本身就是基于網(wǎng)絡(luò)環(huán)境,龐大的原始數(shù)據(jù)就必須壓縮后才能帶走,可以理解為我們搬家要將物品都打包到行李箱這樣理解.
-
傳輸
編碼后的音視頻數(shù)據(jù)通常以RTMP協(xié)議進行傳輸,這是一種專門用于傳輸音視頻的協(xié)議,因為各種各樣的視頻數(shù)據(jù)格式無法統(tǒng)一,所以需要有一個標準作為傳輸?shù)囊?guī)則.協(xié)議就起到這樣的作用.
-
解碼
服務(wù)端接收到我們送過去的編碼數(shù)據(jù)后,需要對其解碼成原始數(shù)據(jù),因為編碼的數(shù)據(jù)直接送給物理硬件的設(shè)備是不能直接播放的,只有解碼為原始數(shù)據(jù)才能使用.
-
音視頻同步
解碼后的每幀音視頻中都含有最開始錄制時候設(shè)置的時間戳,我們需要根據(jù)時間戳將它們正確的播放出來,但是在網(wǎng)絡(luò)傳輸中可能會丟失一些數(shù)據(jù),或者是延時獲取,這時我們就需要一定的策略去實現(xiàn)音視頻的同步,大體分為幾種策略:緩存一定視頻數(shù)據(jù),視頻追音頻等等.
推流跪另,拉流流程
推流: 將手機采集到的視頻數(shù)據(jù)傳給后臺播放端進行展示拧抖,播放端可以是windows, linux, web端,即手機充當采集的功能免绿,將手機攝像頭采集到視頻和麥克風采集到的音頻合成編碼后傳給對應(yīng)平臺的播放端唧席。
拉流: 將播放端傳來的視頻數(shù)據(jù)在手機上播放,推流的逆過程,即將windows, linux, web端傳來的視頻數(shù)據(jù)進行解碼后傳給對應(yīng)音視頻硬件嘲驾,最終將視頻渲染在手機界面上播放.
推流如下:
拉流如下:
具體剖析
推流淌哟,拉流實際為互逆過程,這里按照從采集開始介紹.
1. 采集
采集是推流的第一個環(huán)節(jié)辽故,是原始的音視頻數(shù)據(jù)的來源.采集的原始數(shù)據(jù)類型為音頻數(shù)據(jù)PCM徒仓,視頻數(shù)據(jù)YUV,RGB...。
1.1. 音頻采集
深入研究
采集來源
內(nèi)置麥克風
外置具有麥克風功能的設(shè)備(相機誊垢,話筒...)
系統(tǒng)自帶相冊
音頻主要參數(shù)
采樣率(samplerate): 模擬信號數(shù)字化的過程,每秒鐘采集的數(shù)據(jù)量,采樣頻率越高,數(shù)據(jù)量越大,音質(zhì)越好掉弛。
聲道數(shù)(channels): 即單聲道或雙聲道 (iPhone無法直接采集雙聲道,但可以模擬,即復(fù)制一份采集到的單聲道數(shù)據(jù).安卓部分機型可以)
位寬: 每個采樣點的大小,位數(shù)越多,表示越精細,音質(zhì)越好,一般是8bit或16bit.
數(shù)據(jù)格式: iOS端設(shè)備采集的原始數(shù)據(jù)為線性PCM類型音頻數(shù)據(jù)
其他: 還可以設(shè)置采樣值的精度,每個數(shù)據(jù)包有幾幀數(shù)據(jù),每幀數(shù)據(jù)占多少字節(jié)等等.
-
音頻幀
音頻與視頻不同,視頻每一幀就是一張圖片,音頻是流式,本身沒有明確的幀的概念,實際中為了方便,取2.5ms~60ms為單位的數(shù)據(jù)為一幀音頻.
-
計算
數(shù)據(jù)量(字節(jié) / 秒)=(采樣頻率(Hz)* 采樣位數(shù)(bit)* 聲道數(shù))/ 8
單聲道的聲道數(shù)為1喂走,立體聲的聲道數(shù)為2. 字節(jié)B殃饿,1MB=1024KB = 1024*1024B
1.2. 視頻采集
深入研究
采集來源
攝像頭
屏幕錄制
外置帶攝像頭采集功能的設(shè)備(相機,DJI無人機芋肠,OSMO...)
系統(tǒng)自帶相冊
注意: 像一些外置攝像頭,如像利用攝像機的攝像頭采集,然后用手機將數(shù)據(jù)處理編碼并發(fā)出,也是可以的,但是數(shù)據(jù)的流向需要我們解析,即從攝像頭的HDMI線轉(zhuǎn)成網(wǎng)線口,網(wǎng)線口再轉(zhuǎn)USB,USB轉(zhuǎn)蘋果Lighting接口,利用FFmpeg可以獲取其中的數(shù)據(jù).
視頻主要參數(shù)
圖像格式: YUV, RGB (即紅黃藍三種顏色混合組成各種顏色).
分辨率: 當前設(shè)備屏幕支持的最大分辨率
幀率: 一秒鐘采集的幀數(shù)
其他: 白平衡,對焦,曝光,閃光燈等等
計算 (RGB)
1幀數(shù)據(jù)量 = 分辨率(width * height) * 每個像素的所占字節(jié)數(shù)(一般是3個字節(jié))
注意上面計算的方法并不是唯一的,因為視頻的數(shù)據(jù)格式有很多種,如YUV420計算方式為
分辨率(width * height) * 3/2
1.3. 綜上所述
我們假設(shè)要上傳的視頻是1080P 30fps(分辨率:1920*1080), 聲音是48kHz,那么每秒鐘數(shù)據(jù)量如下:
video = 1920 * 1080 * 30 * 3 = 186624000B = 186.624 MBaudio = (48000 * 16 * 2) / 8 = 192000B = 0.192 MB
由此我們可得乎芳,如果直接將原始采集的數(shù)據(jù)進行傳輸,那么一部電影就需要1000多G的視頻,如果是這樣將多么恐怖帖池,所以就涉及到我們后面的編碼環(huán)節(jié)玩荠。
2. 處理
深入研究 (待添加)
從上一步中伴逸,我們可以得到采集到的音頻原始數(shù)據(jù)和視頻原始數(shù)據(jù),在移動端,一般是通過各自手機平臺官方API中拿到, 前文鏈接中皆有實現(xiàn)的方法.
之后,我們可以對原始數(shù)據(jù)加以處理,對原始操作處理只能在編碼之前,因為編碼后的數(shù)據(jù)只能用于傳輸. 比如可 以對圖像處理
美顏
水印
濾鏡
裁剪
旋轉(zhuǎn)
...
對音頻處理
混音
消除回聲
降噪
...
目前流行的有很多大型框架專門用來處理視頻,音頻,如OpenGL, OpenAL, GPUImage...以上的各種處理網(wǎng)上均有開源的庫可以實現(xiàn),基本原理就是,我們拿到原始的音視頻幀數(shù)據(jù),將其送給開源庫,處理完后再拿到處理好的音視頻繼續(xù)我們自己的流程.當然很多開源庫仍需要根據(jù)項目需求略微更改并封裝.
3.編碼
3.1. 為什么要編碼
在第 1.
步采集最后已經(jīng)講到,原始的視頻每秒鐘就產(chǎn)生200多MB,如果直接拿原始數(shù)據(jù)傳輸,網(wǎng)絡(luò)帶寬即內(nèi)存消耗是巨大的,所以視頻在傳輸中是必須經(jīng)過編碼的.
類似的例子就像我們平常搬家,如果直接搬家,東西很零散,需要跑很多趟拿,如果將衣服,物品打包,我們僅僅需要幾個行李箱就可以一次搞定.等我們到達新家,再將東西取出來,重新布置,編解碼的原理就是如此.
3.2. 有損壓縮 VS 無損壓縮
-
有損壓縮
有損壓縮適用于重構(gòu)信號不一定非要和原始信號完全相同的場合箕昭。
視頻利用人眼的視覺特性, 以一定的客觀失真換取數(shù)據(jù)的壓縮,比如人眼對亮度識別的閾值,視覺閾值,對亮度和色度的敏感度不同,以至于可以在編碼時引入適量誤差,不會被察覺出來.
音頻利用了人類對圖像或聲波中的某些頻率成分不敏感的特性居灯,允許壓縮過程中損失一定的信息克胳;去除聲音中冗余成分的方法實現(xiàn)类缤。冗余成分指的是音頻中不能被人耳朵察覺的信號不撑,它們對聲音的音色,音調(diào)等信息沒有任何幫助晤斩。重構(gòu)后的數(shù)據(jù)與原來的數(shù)據(jù)有所不同焕檬,但不影響人對原始資料表達的信息造成誤解。
無損壓縮
視頻的空間冗余,時間冗余,結(jié)構(gòu)冗余,熵冗余等,即圖像的各個像素間存在很強的關(guān)聯(lián)性,消除這些冗余不會導(dǎo)致信息丟失
音頻壓縮格式則是利用數(shù)據(jù)的統(tǒng)計冗余進行壓縮澳泵,可完全恢復(fù)原始數(shù)據(jù)而不引起任何失真实愚,但壓縮率是受到數(shù)據(jù)統(tǒng)計冗余度的理論限制,一般為2:1到5:1兔辅。
正因為有著上面的壓縮方法,視頻數(shù)據(jù)量可以極大的壓縮,有利于傳輸和存儲.
3.3. 視頻編碼
原理:編碼是如何做到將很大的數(shù)據(jù)量變小的呢? 主要原理如下
空間冗余: 圖像相鄰像素之間有很強的相關(guān)性
時間冗余: 視頻序列的相鄰圖像之間內(nèi)容相似
編碼冗余: 不同像素值出現(xiàn)的概率不同
視覺冗余: 人的視覺系統(tǒng)對某些細節(jié)不敏感
知識冗余: 規(guī)律性的結(jié)構(gòu)可由先前知識和背景知識得到
壓縮編碼的方法
-
變換編碼 (了解即可腊敲,具體請谷歌)
將空間域描述的圖像信號變換成頻率域,然后對變換后的系數(shù)進行編碼處理维苔。一般來說碰辅,圖像在空間上具有較強的相關(guān)性,變換頻率域可以實現(xiàn)去除相關(guān)與能量集中介时。常用的正交變換有離散傅里葉變換没宾,離散余弦變換等等。
-
熵編碼 (了解即可沸柔,具體請谷歌)
熵編碼是因編碼后的平均碼長接近信源熵值而得名循衰。熵編碼多用可變字長編碼(VLC,Variable Length Coding)實現(xiàn)褐澎。其基本原理是對信源中出現(xiàn)概率大的符號賦予短碼羹蚣,對于出現(xiàn)概率小的符號賦予長碼,從而在統(tǒng)計上獲得較短的平均碼長乱凿。可變字長編碼通常有霍夫曼編碼咽弦、算術(shù)編碼徒蟆、游程編碼等。
-
運動估計和運動補償 (重要)
運動估計和運動補償是消除圖像序列時間方向相關(guān)性的有效手段型型。上面介紹的變換編碼段审,熵編碼都是在以一幀圖像的基礎(chǔ)上進行的,通過這些方法可以消除圖像內(nèi)部各像素在空間上的相關(guān)性闹蒜。實際上圖像信號除了空間上的相關(guān)性外寺枉,還有時間上的相關(guān)性。例如對于像新聞聯(lián)播這種背景靜止绷落,畫面主體運動較小的數(shù)字視頻姥闪,每一幅畫面之間的區(qū)別很小,畫面之間的相關(guān)性很大砌烁。對于這種情況我們沒有必要對每一幀圖像單獨進行編碼筐喳,而是可以只對相鄰視頻幀中變化的部分進行編碼催式,從而進一步減小數(shù)據(jù)量,這方面的工作是由運動估計和運動補償來實現(xiàn)的避归。
a. 運動估計技術(shù)
將當前的輸入圖像分割成若干彼此不相重疊的小圖像子塊荣月,例如一幀圖像為1280720,首先將其以網(wǎng)格狀形式分成4045個尺寸為16*16彼此沒有重疊的圖像塊,然后在前一圖像或者后一圖像某個搜索窗口的范圍內(nèi)為每一個圖像塊尋找一個與之最為相似的圖像塊梳毙,這個搜尋的過程叫做運動估計哺窄。
b. 運動補償
通過計算最相似的圖像塊與該圖像塊之間的位置信息,可以得到一個運動矢量账锹。這樣在編碼的過程中就可以將當前圖像中的塊與參考圖像運動矢量所指向的最相似的圖像塊相減萌业,得到一個殘差圖像塊,由于每個殘差圖像塊中的每個像素值都很小牌废,所以在壓縮編碼中可以獲得更高的壓縮比咽白。
-
壓縮數(shù)據(jù)類型
正因為運動估計與運動補償,所以編碼器將輸入的每一幀圖像根據(jù)參考圖像分成了三種類型:I幀鸟缕,P幀晶框,B幀。
I幀: 只使用本幀內(nèi)的數(shù)據(jù)進行編碼懂从,在編碼過程中不需要進行運動估計和運動補償授段。
P幀: 在編碼過程中使用前面的I幀或P幀作為參考圖像的運動補償,實際是對當前圖像與參考圖像的差值進行編碼番甩。
B幀: 在編碼過程中使用前面的I幀或P幀和后面的I幀或P幀進行預(yù)測侵贵。由此可見,每個P幀利用一幀圖像為參考圖像缘薛。而B幀需要兩幀圖像作為參考窍育。
實際應(yīng)用中使用混合編碼(變換編碼+運動估計,運動補償+熵編碼)
編碼器
經(jīng)過數(shù)十年的發(fā)展宴胧,編碼器的功能已經(jīng)十分強大漱抓,種類繁多,下面介紹最主流的一些編碼器恕齐。
- H.264
與舊標準相比乞娄,它能夠在更低帶寬下提供優(yōu)質(zhì)視頻(換言之,只有 MPEG-2显歧,H.263 或 MPEG-4 第 2 部分的一半帶寬或更少)仪或,也不增加太多設(shè)計復(fù)雜度使得無法實現(xiàn)或?qū)崿F(xiàn)成本過高。
- H.265/HEVC
高效率視頻編碼(High Efficiency Video Coding士骤,簡稱HEVC)是一種視頻壓縮標準范删,被視為是 ITU-T H.264/MPEG-4 AVC 標準的繼任者。HEVC 被認為不僅提升視頻質(zhì)量拷肌,同時也能達到 H.264/MPEG-4 AVC 兩倍之壓縮率(等同于同樣畫面質(zhì)量下比特率減少了 50%).
- VP8
VP8 是一個開放的視頻壓縮格式瓶逃,最早由 On2 Technologies 開發(fā)束铭,隨后由 Google 發(fā)布。
- VP9
VP9 的開發(fā)從 2011 年第三季開始厢绝,目標是在同畫質(zhì)下契沫,比 VP8 編碼減少 50%的文件大小,另一個目標則是要在編碼效率上超越 HEVC 編碼昔汉。
3.4. 音頻編碼
-
原理
數(shù)字音頻壓縮編碼在保證信號在聽覺方面不產(chǎn)生失真的前提下懈万,對音頻數(shù)據(jù)信號進行盡可能的壓縮。數(shù)字音頻壓縮編碼采取去除聲音中冗余成分的方法實現(xiàn)靶病。冗余成分指的是音頻中不能被人耳朵察覺的信號会通,它們對聲音的音色,音調(diào)等信息沒有任何幫助娄周。
冗余信號包含人耳聽覺范圍外的音頻信號以及被掩蔽掉的音頻信號燈涕侈。例如,人耳能察覺的聲音頻率為20Hz~20kHz,出此之外的其他頻率人耳無法察覺煤辨,都為冗余信號裳涛。此外,根據(jù)人耳聽覺的生理和心理學(xué)現(xiàn)象众辨。當一個強音信號與一個弱音信號同時存在時端三,弱音信號將被強音信號所掩蔽而聽不見,這樣弱音信號就可以視為冗余信號不用傳送鹃彻。這就是人耳聽覺的掩蔽效應(yīng)郊闯。
壓縮編碼方法
頻譜掩蔽
一個頻率的聲音能量小于某個閾值之后,人耳就會聽不到蛛株,這個閾值稱為最小可聞閾团赁。當有另外能量較大的聲音出現(xiàn)的時候,該聲音頻率附近的閾值會提高很多谨履,即所謂的掩蔽效應(yīng)
人耳對2KHz~5KHz的聲音最敏感欢摄,而對頻率太低或太高的聲音信號都很遲鈍,當有一個頻率為0.2KHz屉符、強度為60dB的聲音出現(xiàn)時,其附近的閾值提高了很多锹引。
- 時域掩蔽
當強音信號和弱音信號同時出現(xiàn)時矗钟,還存在時域掩蔽效應(yīng),前掩蔽嫌变,同時掩蔽吨艇,后掩蔽。前掩蔽是指人耳在聽到強信號之前的短暫時間內(nèi)腾啥,已經(jīng)存在的弱信號會被掩蔽而聽不到东涡。
- 前掩蔽是指人耳在聽到強信號之前的短暫時間內(nèi)冯吓,已經(jīng)存在的弱信號會被掩蔽而聽不到 - 同時掩蔽是指當強信號與弱信號同時存在時,弱信號會被強信號所掩蔽而聽不到疮跑。 - 后掩蔽是指當強信號消失后组贺,需經(jīng)過較長的一段時間才能重新聽見弱信號,稱為后掩蔽祖娘。這些被掩蔽的弱信號即可視為冗余信號失尖。
4. 封裝編碼數(shù)據(jù)
4.1 定義
封裝就是把編碼器生成的音頻,視頻同步以生成我們?nèi)庋劭梢?耳朵可聽并且看到的與聽到的是同步的視頻文件.即封裝后生成一個容器,來存放音頻和視頻流以及一些其他信息(比如字幕, metadata等).
4.2 格式
AVI(.AVI):
優(yōu)點是圖像質(zhì)量好。由于無損AVI可以保存 alpha 通道渐苏,經(jīng)常被我們使用
缺點太多掀潮,體積過于龐大,而且更加糟糕的是壓縮標準不統(tǒng)一琼富,
MOV(.MOV): 美國Apple公司開發(fā)的一種視頻格式仪吧,默認的播放器是蘋果的QuickTime。
MPEG(.MPG,.MPEG,MPE,.DAT,.VOB,.ASF,.3GP,.MP4):
WMV(.WMV,.ASF)
Real Video(.RM,.RMBV): 根據(jù)不同的網(wǎng)絡(luò)傳輸速率制定出不同的壓縮比率鞠眉,從而實現(xiàn)在低速率的網(wǎng)絡(luò)上進行影像數(shù)據(jù)實時傳送和播放
Flash Video(.FLV):由 Adobe Flash 延伸出來的的一種流行網(wǎng)絡(luò)視頻封裝格式薯鼠。隨著視頻網(wǎng)站的豐富,這個格式已經(jīng)非常普及凡蚜。
4.3 將編碼數(shù)據(jù)合成流
在移動端我們需要借助FFmpeg框架,正如上面介紹的,FFmpeg不僅可以做編解碼,還可以合成視頻流,像常用的.flv流,.asf流.
最后, 合成好的數(shù)據(jù)即可用于寫文件或者在網(wǎng)絡(luò)上傳播
補充: FFmpeg (必學(xué)框架)
FFmpeg 是一個開源框架人断,可以運行音頻和視頻多種格式的錄影、轉(zhuǎn)換朝蜘、流功能恶迈,包含了 libavcodec: 這是一個用于多個項目中音頻和視頻的解碼器庫,以及 libavformat 一個音頻與視頻格式轉(zhuǎn)換庫谱醇。
目前支持 Linux ,Mac OS,Windows 三個主流的平臺暇仲,也可以自己編譯到 Android 或者 iOS 平臺。如果是 Mac OS 副渴,可以通過 brew 安裝 brew install ffmpeg --with-libvpx --with-libvorbis --with-ffplay
4.4. FLV流簡介
-
Overview
FLV封裝格式分析器奈附。FLV全稱是Flash Video,是互聯(lián)網(wǎng)上使用極為廣泛的視頻封裝格式煮剧。像Youtube斥滤,優(yōu)酷這類視頻網(wǎng)站,都使用FLV封裝視頻
FLV(Flash Video)是Adobe公司設(shè)計開發(fā)的一種流行的流媒體格式勉盅,由于其視頻文件體積輕巧佑颇、封裝簡單等特點,使其很適合在互聯(lián)網(wǎng)上進行應(yīng)用草娜。此外挑胸,F(xiàn)LV可以使用Flash Player進行播放,而Flash Player插件已經(jīng)安裝在全世界絕大部分瀏覽器上宰闰,這使得通過網(wǎng)頁播放FLV視頻十分容易茬贵。目前主流的視頻網(wǎng)站如優(yōu)酷網(wǎng)簿透,土豆網(wǎng),樂視網(wǎng)等網(wǎng)站無一例外地使用了FLV格式解藻。FLV封裝格式的文件后綴通常為“.flv”老充。
-
結(jié)構(gòu)
FLV包括文件頭(File Header)和文件體(File Body)兩部分,其中文件體由一系列的Tag組成舆逃。因此一個FLV文件是如圖1結(jié)構(gòu)蚂维。
每個Tag前面還包含了Previous Tag Size字段,表示前面一個Tag的大小路狮。Tag的類型可以是視頻虫啥、音頻和Script,每個Tag只能包含以上三種類型的數(shù)據(jù)中的一種奄妨。圖2展示了FLV文件的詳細結(jié)構(gòu)涂籽。
5. 將數(shù)據(jù)通過RTMP協(xié)議傳輸
優(yōu)點
CDN 支持良好,主流的 CDN 廠商都支持
協(xié)議簡單砸抛,在各平臺上實現(xiàn)容易
缺點
基于 TCP 评雌,傳輸成本高,在弱網(wǎng)環(huán)境丟包率高的情況下問題顯著
不支持瀏覽器推送
Adobe 私有協(xié)議直焙,Adobe 已經(jīng)不再更新
我們推送出去的流媒體需要傳輸?shù)接^眾景东,整個鏈路就是傳輸網(wǎng)絡(luò).
5.1. Overview
RTMP協(xié)議是一個互聯(lián)網(wǎng)TCP/IP五層體系結(jié)構(gòu)中應(yīng)用層的協(xié)議。RTMP協(xié)議中基本的數(shù)據(jù)單元稱為消息(Message)奔誓。當RTMP協(xié)議在互聯(lián)網(wǎng)中傳輸數(shù)據(jù)的時候斤吐,消息會被拆分成更小的單元,稱為消息塊(Chunk)厨喂。
5.2. 消息
消息是RTMP協(xié)議中基本的數(shù)據(jù)單元和措。不同種類的消息包含不同的Message Type ID,代表不同的功能蜕煌。RTMP協(xié)議中一共規(guī)定了十多種消息類型派阱,分別發(fā)揮著不同的作用。
1-7的消息用于協(xié)議控制斜纪,這些消息一般是RTMP協(xié)議自身管理要使用的消息贫母,用戶一般情況下無需操作其中的數(shù)據(jù)
Message Type ID為8,9的消息分別用于傳輸音頻和視頻數(shù)據(jù)
Message Type ID為15-20的消息用于發(fā)送AMF編碼的命令盒刚,負責用戶與服務(wù)器之間的交互腺劣,比如播放,暫停等等
消息首部(Message Header)有四部分組成:標志消息類型的Message Type ID伪冰,標志消息長度的Payload Length誓酒,標識時間戳的Timestamp樟蠕,標識消息所屬媒體流的Stream ID
2.消息塊
在網(wǎng)絡(luò)上傳輸數(shù)據(jù)時贮聂,消息需要被拆分成較小的數(shù)據(jù)塊靠柑,才適合在相應(yīng)的網(wǎng)絡(luò)環(huán)境上傳輸。RTMP協(xié)議中規(guī)定吓懈,消息在網(wǎng)絡(luò)上傳輸時被拆分成消息塊(Chunk)歼冰。
消息塊首部(Chunk Header)有三部分組成:
用于標識本塊的Chunk Basic Header
用于標識本塊負載所屬消息的Chunk Message Header
以及當時間戳溢出時才出現(xiàn)的Extended Timestamp
3.消息分塊
在消息被分割成幾個消息塊的過程中,消息負載部分(Message Body)被分割成大小固定的數(shù)據(jù)塊(默認是128字節(jié)耻警,最后一個數(shù)據(jù)塊可以小于該固定長度)隔嫡,并在其首部加上消息塊首部(Chunk Header),就組成了相應(yīng)的消息塊甘穿。消息分塊過程如圖5所示腮恩,一個大小為307字節(jié)的消息被分割成128字節(jié)的消息塊(除了最后一個)。
RTMP傳輸媒體數(shù)據(jù)的過程中温兼,發(fā)送端首先把媒體數(shù)據(jù)封裝成消息秸滴,然后把消息分割成消息塊,最后將分割后的消息塊通過TCP協(xié)議發(fā)送出去募判。接收端在通過TCP協(xié)議收到數(shù)據(jù)后荡含,首先把消息塊重新組合成消息,然后通過對消息進行解封裝處理就可以恢復(fù)出媒體數(shù)據(jù)届垫。
4.RTMP中的邏輯結(jié)構(gòu)
RTMP協(xié)議規(guī)定释液,播放一個流媒體有兩個前提步驟
第一步,建立一個網(wǎng)絡(luò)連接(NetConnection)
第二步装处,建立一個網(wǎng)絡(luò)流(NetStream)误债。
其中,網(wǎng)絡(luò)連接代表服務(wù)器端應(yīng)用程序和客戶端之間基礎(chǔ)的連通關(guān)系符衔。網(wǎng)絡(luò)流代表了發(fā)送多媒體數(shù)據(jù)的通道找前。服務(wù)器和客戶端之間只能建立一個網(wǎng)絡(luò)連接,但是基于該連接可以創(chuàng)建很多網(wǎng)絡(luò)流判族。他們的關(guān)系如圖所示:
5. 連接流程
播放一個RTMP協(xié)議的流媒體需要經(jīng)過以下幾個步驟:
握手
建立連接
建立流
播放
RTMP連接都是以握手作為開始的躺盛。建立連接階段用于建立客戶端與服務(wù)器之間的“網(wǎng)絡(luò)連接”;建立流階段用于建立客戶端與服務(wù)器之間的“網(wǎng)絡(luò)流”形帮;播放階段用于傳輸視音頻數(shù)據(jù)槽惫。
6. 解析并解碼視頻流
深入研究
到這里為止,完整的推流過程已經(jīng)介紹完成,下面的過程即為逆向過程-拉流.
因為接收端拿到編碼的視頻流最終還是想將視頻渲染到屏幕上, 將音頻通過揚聲器等輸出設(shè)備播出,所以接著上面的步驟,接收端可以通過RTMP協(xié)議拿到視頻流數(shù)據(jù),然后需要利用FFmpeg parse數(shù)據(jù),因為我們需要將數(shù)據(jù)中的音頻跟視頻分開,分離出音視頻數(shù)據(jù)后需要分別對它們做解碼操作.解碼的視頻即為YUV/RGB等格式,解碼后的音頻即為線性PCM數(shù)據(jù).
需要注意的是,我們解碼出來的數(shù)據(jù)并不能夠直接使用,因為,手機端如果想要播放解碼出來的數(shù)據(jù)是需要將其放入特定的數(shù)據(jù)結(jié)構(gòu)中,在iOS中,視頻數(shù)據(jù)需要放入CMSampleBufferRef中,而該數(shù)據(jù)結(jié)構(gòu)又由CMTime,CMVideoFormatDes,CMBlockBuffer組成,所以我們需要提供它所需要的信息才能組成系統(tǒng)能夠播放的格式.
7. 音視頻同步并播放
當我們拿到解碼后的音視頻幀時,首先要考慮的問題就是如何同步音視頻,在網(wǎng)絡(luò)正常的情況下是不需要做音視頻同步操作,因為我們parse到的音視頻數(shù)據(jù)里本身帶著它們在采集時的時間戳,只要我們在合理時間內(nèi)拿到音視頻幀,將它們分別送給屏幕與揚聲器即可實現(xiàn)同步播放.但是考慮到網(wǎng)絡(luò)波動,所以可能丟失一些幀或延遲后才能獲取,當這種情況出現(xiàn)時就會造成聲音視頻不同步,因此需要對音視頻做同步處理.
我們可以這樣理解: 有一把尺子 一只螞蟻(視頻)跟著一個標桿(音頻)走, 標桿是勻速的 螞蟻或快或慢辩撑,慢了你就抽它 讓它跑起來界斜,快了就拽它。這樣音視頻就能同步了合冀。這里最大的問題就是音頻是勻速的各薇,視頻是非線性的。
分別獲得音視頻的PTS后,我們有三個選擇:視頻同步音頻(計算音視頻PTS之差峭判,來判定視頻是否有延遲)开缎、音頻同步視頻(根據(jù)音視頻PTS差值調(diào)整音頻取的樣值,即改變音頻緩沖區(qū)的大辛煮Α)和音頻視頻同步外部時鐘(同前一個)奕删,因為調(diào)整音頻范圍過大,會造成令用戶不適的尖銳聲疗认,所以通常我們選擇第一種完残。
我們的策略是通過比較前一個 PTS 和當前的 PTS 來預(yù)測下一幀的 PTS。與此同時横漏,我們需要同步視頻到音頻谨设。我們將創(chuàng)建一個 audio clock 作為內(nèi)部變量來跟蹤音頻現(xiàn)在播放的時間點,video thread 將用這個值來計算和判斷視頻是播快了還是播慢了缎浇。
現(xiàn)在假設(shè)我們有一個 get_audio_clock 函數(shù)來返回我們 audio clock铝宵,那當我們拿到這個值,我們怎么去處理音視頻不同步的情況呢华畏?如果只是簡單的嘗試跳到正確的 packet 來解決并不是一個很好的方案鹏秋。我們要做的是調(diào)整下一次刷新的時機:如果視頻播慢了我們就加快刷新,如果視頻播快了我們就減慢刷新亡笑。既然我們調(diào)整好了刷新時間侣夷,接下來用 frame_timer 跟設(shè)備的時鐘做一下比較。frame_timer 會一直累加在播放過程中我們計算的延時仑乌。換而言之百拓,這個 frame_timer 就是播放下一幀的應(yīng)該對上的時間點。我們簡單的在 frame_timer 上累加新計算的 delay晰甚,然后和系統(tǒng)時間比較衙传,并用得到的值來作為時間間隔去刷新。