概述
隨著整個互聯(lián)網(wǎng)的崛起鄙信,數(shù)據(jù)傳遞的形式也在不斷升級變化蹈丸,總的流行趨勢如下:
純文本的短信陵吸,QQ -> 空間驾锰,微博,朋友圈的圖片文字結(jié)合 -> 微信語音 -> 各大直播軟件 -> 抖音短視頻
復(fù)制代碼
音視頻的發(fā)展正在向各個行業(yè)不斷擴展走越,從教育的遠程授課椭豫,交通的人臉識別,醫(yī)療的遠程就醫(yī)等等,音視頻方向已經(jīng)占據(jù)一個相當(dāng)重要的位置赏酥,而音視頻真正入門的文章又少之甚少喳整,一個剛畢業(yè)小白可能很難切入理解,因為音視頻中涉及大量理論知識,而代碼的書寫需要結(jié)合這些理論,所以搞懂音視頻,編解碼等理論知識至關(guān)重要.本人也是從實習(xí)開始接觸音視頻項目,看過很多人的文章,在這里總結(jié)一個通俗易懂的文章裸扶,讓更多準(zhǔn)備學(xué)習(xí)音視頻的同學(xué)更快入門框都。
劃重點
本文中理論知識來自于各種音視頻文章的歸納音視頻編碼基本原理匯總,其中也會有一些我自己總結(jié)增加的部分.若有錯誤可評論,檢查后會更正.
為了防止大家理解過于空洞,作者花了三個月時間將最常用,最重要的一些功能的理論知識及實戰(zhàn)Demo親自寫出來,配合文章閱讀效果更佳.每一部分的文章可以在下面每章章節(jié)開始的深入學(xué)習(xí)中點擊鏈接查看, 鏈接中文章均有Github地址,每個Demo都親測可以通過,可以下載Demo運行.
如果喜歡,請幫忙點贊并支持轉(zhuǎn)載,轉(zhuǎn)載請附原文鏈接.
原理
-
采集
無論是iOS平臺,還是安卓平臺,我們都是需要借助官方的API實現(xiàn)一系列相關(guān)功能.首先我們要明白我們想要什么,最開始我們需要一部手機,智能手機中攝像頭是不可缺少的一部分,所以我們通過一些系統(tǒng)API獲取就要可以獲取物理攝像頭將采集到的視頻數(shù)據(jù)與麥克風(fēng)采集到的音頻數(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)一,所以需要有一個標(biāo)準(zhǔn)作為傳輸?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端,即手機充當(dāng)采集的功能烂翰,將手機攝像頭采集到視頻和麥克風(fēng)采集到的音頻合成編碼后傳給對應(yīng)平臺的播放端叮阅。
拉流: 將播放端傳來的視頻數(shù)據(jù)在手機上播放,推流的逆過程,即將windows, linux, web端傳來的視頻數(shù)據(jù)進行解碼后傳給對應(yīng)音視頻硬件,最終將視頻渲染在手機界面上播放.
推流如下:
<figcaption></figcaption>
拉流如下:
<figcaption></figcaption>
具體剖析
推流,拉流實際為互逆過程,這里按照從采集開始介紹.
1. 采集
采集是推流的第一個環(huán)節(jié)檩咱,是原始的音視頻數(shù)據(jù)的來源.采集的原始數(shù)據(jù)類型為音頻數(shù)據(jù)PCM,視頻數(shù)據(jù)YUV,RGB...胯舷。
1.1. 音頻采集
-
深入研究
-
采集來源
- 內(nèi)置麥克風(fēng)
- 外置具有麥克風(fēng)功能的設(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 (即紅黃藍三種顏色混合組成各種顏色).
- 分辨率: 當(dāng)前設(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 MB
audio = (48000 * 16 * 2) / 8 = 192000B = 0.192 MB
復(fù)制代碼
由此我們可得,如果直接將原始采集的數(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ù)我們自己的流程.當(dāng)然很多開源庫仍需要根據(jù)項目需求略微更改并封裝.
3.編碼
3.1. 為什么要編碼
在第1.
步采集最后已經(jīng)講到,原始的視頻每秒鐘就產(chǎn)生200多MB,如果直接拿原始數(shù)據(jù)傳輸,網(wǎng)絡(luò)帶寬即內(nèi)存消耗是巨大的,所以視頻在傳輸中是必須經(jīng)過編碼的.
類似的例子就像我們平常搬家,如果直接搬家,東西很零散,需要跑很多趟拿,如果將衣服,物品打包,我們僅僅需要幾個行李箱就可以一次搞定.等我們到達新家,再將東西取出來,重新布置,編解碼的原理就是如此.
3.2. 有損壓縮 VS 無損壓縮
-
有損壓縮
視頻利用人眼的視覺特性, 以一定的客觀失真換取數(shù)據(jù)的壓縮,比如人眼對亮度識別的閾值,視覺閾值,對亮度和色度的敏感度不同,以至于可以在編碼時引入適量誤差,不會被察覺出來.
音頻利用了人類對圖像或聲波中的某些頻率成分不敏感的特性眉反,允許壓縮過程中損失一定的信息昙啄;去除聲音中冗余成分的方法實現(xiàn)。冗余成分指的是音頻中不能被人耳朵察覺的信號寸五,它們對聲音的音色梳凛,音調(diào)等信息沒有任何幫助。重構(gòu)后的數(shù)據(jù)與原來的數(shù)據(jù)有所不同梳杏,但不影響人對原始資料表達的信息造成誤解韧拒。
有損壓縮適用于重構(gòu)信號不一定非要和原始信號完全相同的場合淹接。
-
無損壓縮
- 視頻的空間冗余,時間冗余,結(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ù)
將當(dāng)前的輸入圖像分割成若干彼此不相重疊的小圖像子塊霞篡,例如一幀圖像為1280720,首先將其以網(wǎng)格狀形式分成4045個尺寸為16*16彼此沒有重疊的圖像塊世蔗,然后在前一圖像或者后一圖像某個搜索窗口的范圍內(nèi)為每一個圖像塊尋找一個與之最為相似的圖像塊,這個搜尋的過程叫做運動估計寇损。
b. 運動補償
通過計算最相似的圖像塊與該圖像塊之間的位置信息凸郑,可以得到一個運動矢量。這樣在編碼的過程中就可以將當(dāng)前圖像中的塊與參考圖像運動矢量所指向的最相似的圖像塊相減矛市,得到一個殘差圖像塊芙沥,由于每個殘差圖像塊中的每個像素值都很小,所以在壓縮編碼中可以獲得更高的壓縮比浊吏。
-
-
壓縮數(shù)據(jù)類型
正因為運動估計與運動補償而昨,所以編碼器將輸入的每一幀圖像根據(jù)參考圖像分成了三種類型:I幀,P幀找田,B幀歌憨。
I幀: 只使用本幀內(nèi)的數(shù)據(jù)進行編碼,在編碼過程中不需要進行運動估計和運動補償墩衙。
P幀: 在編碼過程中使用前面的I幀或P幀作為參考圖像的運動補償务嫡,實際是對當(dāng)前圖像與參考圖像的差值進行編碼。
B幀: 在編碼過程中使用前面的I幀或P幀和后面的I幀或P幀進行預(yù)測漆改。由此可見心铃,每個P幀利用一幀圖像為參考圖像。而B幀需要兩幀圖像作為參考挫剑。
實際應(yīng)用中使用混合編碼(變換編碼+運動估計去扣,運動補償+熵編碼)
<figcaption></figcaption>
-
編碼器
經(jīng)過數(shù)十年的發(fā)展,編碼器的功能已經(jīng)十分強大樊破,種類繁多愉棱,下面介紹最主流的一些編碼器。
- H.264
與舊標(biāo)準(zhǔn)相比哲戚,它能夠在更低帶寬下提供優(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)是一種視頻壓縮標(biāo)準(zhǔn)祈纯,被視為是 ITU-T H.264/MPEG-4 AVC 標(biāo)準(zhǔn)的繼任者令宿。HEVC 被認(rèn)為不僅提升視頻質(zhì)量,同時也能達到 H.264/MPEG-4 AVC 兩倍之壓縮率(等同于同樣畫面質(zhì)量下比特率減少了 50%).
- VP8
VP8 是一個開放的視頻壓縮格式腕窥,最早由 On2 Technologies 開發(fā)粒没,隨后由 Google 發(fā)布。
- VP9
VP9 的開發(fā)從 2011 年第三季開始簇爆,目標(biāo)是在同畫質(zhì)下癞松,比 VP8 編碼減少 50%的文件大小爽撒,另一個目標(biāo)則是要在編碼效率上超越 HEVC 編碼。
3.4. 音頻編碼
-
原理
數(shù)字音頻壓縮編碼在保證信號在聽覺方面不產(chǎn)生失真的前提下响蓉,對音頻數(shù)據(jù)信號進行盡可能的壓縮硕勿。數(shù)字音頻壓縮編碼采取去除聲音中冗余成分的方法實現(xiàn)。冗余成分指的是音頻中不能被人耳朵察覺的信號枫甲,它們對聲音的音色源武,音調(diào)等信息沒有任何幫助。
冗余信號包含人耳聽覺范圍外的音頻信號以及被掩蔽掉的音頻信號燈想幻。例如粱栖,人耳能察覺的聲音頻率為20Hz~20kHz,出此之外的其他頻率人耳無法察覺,都為冗余信號脏毯。此外闹究,根據(jù)人耳聽覺的生理和心理學(xué)現(xiàn)象。當(dāng)一個強音信號與一個弱音信號同時存在時食店,弱音信號將被強音信號所掩蔽而聽不見渣淤,這樣弱音信號就可以視為冗余信號不用傳送。這就是人耳聽覺的掩蔽效應(yīng)吉嫩。
-
壓縮編碼方法
- 頻譜掩蔽
一個頻率的聲音能量小于某個閾值之后价认,人耳就會聽不到,這個閾值稱為最小可聞閾率挣。當(dāng)有另外能量較大的聲音出現(xiàn)的時候刻伊,該聲音頻率附近的閾值會提高很多露戒,即所謂的掩蔽效應(yīng)
人耳對2KHz~5KHz的聲音最敏感椒功,而對頻率太低或太高的聲音信號都很遲鈍,當(dāng)有一個頻率為0.2KHz智什、強度為60dB的聲音出現(xiàn)時动漾,其附近的閾值提高了很多。
- 時域掩蔽
當(dāng)強音信號和弱音信號同時出現(xiàn)時荠锭,還存在時域掩蔽效應(yīng)旱眯,前掩蔽,同時掩蔽证九,后掩蔽删豺。前掩蔽是指人耳在聽到強信號之前的短暫時間內(nèi),已經(jīng)存在的弱信號會被掩蔽而聽不到愧怜。
- 前掩蔽是指人耳在聽到強信號之前的短暫時間內(nèi)呀页,已經(jīng)存在的弱信號會被掩蔽而聽不到 - 同時掩蔽是指當(dāng)強信號與弱信號同時存在時,弱信號會被強信號所掩蔽而聽不到拥坛。 - 后掩蔽是指當(dāng)強信號消失后蓬蝶,需經(jīng)過較長的一段時間才能重新聽見弱信號尘分,稱為后掩蔽。這些被掩蔽的弱信號即可視為冗余信號丸氛。 復(fù)制代碼
<figcaption></figcaption>
4. 封裝編碼數(shù)據(jù)
4.1 定義
封裝就是把編碼器生成的音頻,視頻同步以生成我們?nèi)庋劭梢?耳朵可聽并且看到的與聽到的是同步的視頻文件.即封裝后生成一個容器,來存放音頻和視頻流以及一些其他信息(比如字幕, metadata等).
4.2 格式
- AVI(.AVI):
- 優(yōu)點是圖像質(zhì)量好培愁。由于無損AVI可以保存 alpha 通道,經(jīng)常被我們使用
- 缺點太多缓窜,體積過于龐大定续,而且更加糟糕的是壓縮標(biāo)準(zhǔn)不統(tǒng)一,
- MOV(.MOV): 美國Apple公司開發(fā)的一種視頻格式禾锤,默認(rèn)的播放器是蘋果的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)蛉威。
10.file_header<figcaption></figcaption>
每個Tag前面還包含了Previous Tag Size字段日丹,表示前面一個Tag的大小。Tag的類型可以是視頻蚯嫌、音頻和Script哲虾,每個Tag只能包含以上三種類型的數(shù)據(jù)中的一種。圖2展示了FLV文件的詳細結(jié)構(gòu)择示。
9.flv_structure<figcaption></figcaption>
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)。當(dāng)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編碼的命令赃份,負(fù)責(zé)用戶與服務(wù)器之間的交互寂拆,比如播放,暫停等等
- 消息首部(Message Header)有四部分組成:標(biāo)志消息類型的Message Type ID抓韩,標(biāo)志消息長度的Payload Length纠永,標(biāo)識時間戳的Timestamp,標(biāo)識消息所屬媒體流的Stream ID
<figcaption></figcaption>
2.消息塊
在網(wǎng)絡(luò)上傳輸數(shù)據(jù)時谒拴,消息需要被拆分成較小的數(shù)據(jù)塊尝江,才適合在相應(yīng)的網(wǎng)絡(luò)環(huán)境上傳輸。RTMP協(xié)議中規(guī)定英上,消息在網(wǎng)絡(luò)上傳輸時被拆分成消息塊(Chunk)炭序。
消息塊首部(Chunk Header)有三部分組成:
- 用于標(biāo)識本塊的Chunk Basic Header
- 用于標(biāo)識本塊負(fù)載所屬消息的Chunk Message Header
- 以及當(dāng)時間戳溢出時才出現(xiàn)的Extended Timestamp
<figcaption></figcaption>
3.消息分塊
在消息被分割成幾個消息塊的過程中啤覆,消息負(fù)載部分(Message Body)被分割成大小固定的數(shù)據(jù)塊(默認(rèn)是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ù)。
<figcaption></figcaption>
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)系如圖所示:
<figcaption></figcaption>
5. 連接流程
播放一個RTMP協(xié)議的流媒體需要經(jīng)過以下幾個步驟:
- 握手
- 建立連接
- 建立流
- 播放
RTMP連接都是以握手作為開始的送火。建立連接階段用于建立客戶端與服務(wù)器之間的“網(wǎng)絡(luò)連接”拳话;建立流階段用于建立客戶端與服務(wù)器之間的“網(wǎng)絡(luò)流”;播放階段用于傳輸視音頻數(shù)據(jù)种吸。
<figcaption></figcaption>
<figcaption></figcaption>
<figcaption></figcaption>
<figcaption></figcaption>
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)能夠播放的格式.
<figcaption></figcaption>
7. 音視頻同步并播放
當(dāng)我們拿到解碼后的音視頻幀時,首先要考慮的問題就是如何同步音視頻,在網(wǎng)絡(luò)正常的情況下是不需要做音視頻同步操作,因為我們parse到的音視頻數(shù)據(jù)里本身帶著它們在采集時的時間戳,只要我們在合理時間內(nèi)拿到音視頻幀,將它們分別送給屏幕與揚聲器即可實現(xiàn)同步播放.但是考慮到網(wǎng)絡(luò)波動,所以可能丟失一些幀或延遲后才能獲取,當(dāng)這種情況出現(xiàn)時就會造成聲音視頻不同步,因此需要對音視頻做同步處理.
我們可以這樣理解: 有一把尺子 一只螞蟻(視頻)跟著一個標(biāo)桿(音頻)走弃衍, 標(biāo)桿是勻速的 螞蟻或快或慢,慢了你就抽它 讓它跑起來坚俗,快了就拽它镜盯。這樣音視頻就能同步了。 這里最大的問題就是音頻是勻速的猖败,視頻是非線性的速缆。
分別獲得音視頻的PTS后,我們有三個選擇:視頻同步音頻(計算音視頻PTS之差恩闻,來判定視頻是否有延遲)艺糜、音頻同步視頻(根據(jù)音視頻PTS差值調(diào)整音頻取的樣值,即改變音頻緩沖區(qū)的大小)和音頻視頻同步外部時鐘(同前一個)破停,因為調(diào)整音頻范圍過大翅楼,會造成令用戶不適的尖銳聲,所以通常我們選擇第一種真慢。
我們的策略是通過比較前一個 PTS 和當(dāng)前的 PTS 來預(yù)測下一幀的 PTS犁嗅。與此同時,我們需要同步視頻到音頻晤碘。我們將創(chuàng)建一個 audio clock 作為內(nèi)部變量來跟蹤音頻現(xiàn)在播放的時間點褂微,video thread 將用這個值來計算和判斷視頻是播快了還是播慢了。
現(xiàn)在假設(shè)我們有一個 get_audio_clock 函數(shù)來返回我們 audio clock园爷,那當(dāng)我們拿到這個值宠蚂,我們怎么去處理音視頻不同步的情況呢?如果只是簡單的嘗試跳到正確的 packet 來解決并不是一個很好的方案童社。我們要做的是調(diào)整下一次刷新的時機:如果視頻播慢了我們就加快刷新求厕,如果視頻播快了我們就減慢刷新。既然我們調(diào)整好了刷新時間扰楼,接下來用 frame_timer 跟設(shè)備的時鐘做一下比較呀癣。frame_timer 會一直累加在播放過程中我們計算的延時。換而言之弦赖,這個 frame_timer 就是播放下一幀的應(yīng)該對上的時間點项栏。我們簡單的在 frame_timer 上累加新計算的 delay,然后和系統(tǒng)時間比較蹬竖,并用得到的值來作為時間間隔去刷新沼沈。
參考文章
作者:小東邪
原文鏈接:https://juejin.im/post/5d29d884f265da1b971aa220
如有侵權(quán)請留言我,我會盡快刪除币厕。