?做了快一年的視頻會(huì)議部分桶癣,現(xiàn)在有時(shí)間可以把對音視頻的認(rèn)知總結(jié)一下翠储,再次聲明所有文章只代表本人對音視頻的理解筐乳,如果觀念不同或者理解錯(cuò)誤歡迎批評指正隘马。本人開發(fā)了基于android系統(tǒng)的會(huì)議客戶端以及會(huì)議系統(tǒng)的服務(wù)器端野宜,以及ios端和MacOSX的音視頻會(huì)議部分扫步。整個(gè)系統(tǒng)并不是基于向webRTC或者FFMPEG等主要框架做集成優(yōu)化,只是某些工具類使用了webRTC或者FFMPEG的一些方法匈子。
一河胎、音視頻會(huì)議整體架構(gòu)
各系統(tǒng)客戶端的硬編解碼(壓縮和解壓縮h264/h265 aac) +TCP自定義協(xié)議傳輸+netty轉(zhuǎn)發(fā)服務(wù)器+各系統(tǒng)客戶端播放器(播放緩沖區(qū)保質(zhì)和延遲的取舍)
二、主要模塊理解部分
1.各系統(tǒng)客戶端的硬編解碼
軟硬解碼:通俗的說一個(gè)GPU一個(gè)CPU虎敦,理論最好使用GPU游岳,本就是做圖形圖像的事,但是硬編解碼我遇到了在一些芯片上顏色不正其徙,例如我之前的mstar公版芯片顏色的默認(rèn)值和PC電腦上看到的效果顏色會(huì)更亮胚迫,黑色會(huì)更深。如果你有Android系統(tǒng)源碼唾那,可以去修改數(shù)據(jù)庫的數(shù)值即可访锻,開發(fā)修改難度不大,但是要求專門的光學(xué)知識的人,一般小規(guī)模直接找精品對比期犬。說這么多其實(shí)想說用硬編解碼河哑,對于app的來說是存在一套代碼在同樣android系統(tǒng)不同廠家出現(xiàn)差異化色彩的。解決這個(gè)問題可以換成軟解嗎+OpenGL渲染(android和ios的這部分api基本上相同)龟虎。所以說軟的方案跨平臺好璃谨,一套FFMPEG可以跑到任何平臺,但是我想說如果快速開發(fā)多平臺使用用現(xiàn)成的框架體系比較適合鲤妥,但是最好的性能最好是基于不同平臺去做改變佳吞。我們曾經(jīng)就在一款芯片上使用ffplay去播放,發(fā)現(xiàn)CPU很高出現(xiàn)卡頓不流暢棉安,基于FFMPEG做了很多優(yōu)化容达,我們定位每一幀數(shù)據(jù)的首發(fā)時(shí)間和播放渲染時(shí)間,基于FFMPEG的修改很頭痛垂券,研究了RTCP花盐、RTP封包的影響等等,修改協(xié)議修改發(fā)包頻率修改接收和播放緩沖區(qū)菇爪,后來我換了思路就是基于各個(gè)平臺做自己的編解碼+傳輸+渲染算芯。因此android端編解碼就用MediaCodec,ios和macOSX用VideoToolBox凳宙。
2.各系統(tǒng)客戶端數(shù)據(jù)傳輸
開始我們使用RTSP(RTCP和RTP)熙揍,我對其理解RTCP就是兩邊環(huán)境實(shí)時(shí)狀態(tài)的相互通知,RTP發(fā)送和接收想要的數(shù)據(jù)包氏涩。目前我看到的一些demo用的RTSP都比較基礎(chǔ)(可能是我看項(xiàng)目看得少)届囚。后來我們自定義協(xié)議,本質(zhì)是是一個(gè)用來互通狀態(tài)是尖,一個(gè)傳數(shù)據(jù)意系,說白了是你需要什么就告知對方你的需要,當(dāng)異常告知你異常了需要怎么恢復(fù)等饺汹。遇到的問題本質(zhì)是基于TCP和UDP封裝(TCP和UDP的優(yōu)缺點(diǎn)我就不說了蛔添,一搜一大把),首先TCP/UDP在各平臺上會(huì)出現(xiàn)什么問題兜辞,1)TCP傳輸在各平臺會(huì)出現(xiàn)粘包現(xiàn)象迎瞧,例如發(fā)送A之后發(fā)送B,接收的時(shí)候AB在一起逸吵,解決這個(gè)問題就是包頭加包體凶硅。2)UDP發(fā)送不一定肯定能接收到,協(xié)議本身決定扫皱,在有的平臺上會(huì)出現(xiàn)我發(fā)一個(gè)很大的包發(fā)不出去足绅,例如你這張照片有5M压语,沒發(fā)送成功,其本質(zhì)一次性發(fā)送的數(shù)據(jù)量受MTU控制(各平臺設(shè)置的不同)编检,我之前改過MacOsx的MTU,可以增大一次性發(fā)包數(shù)據(jù)扰才,但這種做法很傻允懂,其實(shí)對于UDP就是分包和組包,要發(fā)5M的圖片將其分成多個(gè)數(shù)據(jù)發(fā)送接收到后再組合衩匣。如果多久沒收到完整的包蕾总,要進(jìn)行拋包處理,就是將不完整的拋掉琅捏,也可以TCP告知對方你異常重傳生百。
其實(shí)自定義傳輸都是服務(wù)與怎樣讓平臺的解碼器正常解碼播放,例如你的解碼器告知你少sps和pps錯(cuò)誤柄延,你第一時(shí)間補(bǔ)充其數(shù)據(jù)蚀浆,該重新初始化的重新初始化。其實(shí)這塊的問題比較好模擬搜吧,比如你用android手機(jī)自己解碼h264時(shí)市俊,自己故意少發(fā)送sps和pps 、I幀B幀P幀還有整個(gè)GOP丟失現(xiàn)象滤奈,挨個(gè)模擬試試缺少數(shù)據(jù)會(huì)出現(xiàn)什么現(xiàn)象摆昧,也就知道怎么去異常恢復(fù)了蜒程。保證正常的傳輸和播放后绅你,可以在協(xié)議本身加一些安全措施,頭部加密昭躺,關(guān)鍵音視頻信息替換等忌锯。具體的不方便細(xì)說畢竟我做的是安全會(huì)議。
傳輸協(xié)議沒使用hls蘋果出的领炫,其實(shí)前幾年我用ios的試過只不過以前的Android將視頻分片成.m3u8工具不多汉规,主要是個(gè)人時(shí)間問題,畢竟當(dāng)時(shí)只是愛好驹吮,用ios的實(shí)現(xiàn)過针史,這個(gè)本質(zhì)是將視頻分成小段,也就是說你先錄制小段碟狞,默認(rèn)的之前是10s這個(gè)可以修改啄枕,換句話說你最小延遲10s,其實(shí)比較適合實(shí)時(shí)性沒那高要求的直播族沃,畢竟可以通俗理解你播放了一個(gè)又一個(gè)的10s小視頻频祝。之后我按著這個(gè)思路用json+數(shù)據(jù)替換ts+.m3u8在一款投影上去試了試泌参,現(xiàn)象沒啥印象了。這個(gè)只要你的播放器能銜接上就OK常空。數(shù)據(jù)封裝別用mp4沽一,mp4合成要知道你整個(gè)視頻合成時(shí)長。
3.后臺服務(wù)端轉(zhuǎn)發(fā)
相對我接觸java后臺時(shí)間不長漓糙,主要是netty的合包和轉(zhuǎn)發(fā)铣缠,這部分沒啥特別的,主要就是netty的使用和轉(zhuǎn)發(fā)昆禽,整個(gè)服務(wù)器集群是自己的主從模式蝗蛙,主服務(wù)器分配給從服務(wù)器做具體的音視頻時(shí)間,主服務(wù)器獲得從服務(wù)器資源情況醉鳖,分配會(huì)議音視頻資源給從服務(wù)器捡硅。邏輯業(yè)務(wù)和普通的沒啥區(qū)別。在轉(zhuǎn)發(fā)音視頻數(shù)據(jù)時(shí)將一些異常重發(fā)信息做提取盗棵,在上傳的正常數(shù)據(jù)做一些關(guān)鍵提取保存到緩存中壮韭,會(huì)議結(jié)束時(shí)放掉,例如有時(shí)客戶端需要sps和pps重新初始化等纹因。
4.各系統(tǒng)客戶端收到數(shù)據(jù)顯示
客戶端收到數(shù)據(jù)最理想狀態(tài)是接收到直接就去顯示播放泰涂,但是會(huì)遇到一些問題:
????1)如果播放視頻畫面網(wǎng)絡(luò)良好的情況下可以順暢播放,如果網(wǎng)絡(luò)波動(dòng)時(shí)就會(huì)卡頓辐怕。一般會(huì)加一個(gè)播放緩沖區(qū)逼蒙,其實(shí)就是加延遲換流暢度,你的最小緩沖區(qū)就類似于上面的HLS的設(shè)置的數(shù)據(jù)大小一個(gè)概念寄疏。假如你的采集端是fps=25是牢,理想狀態(tài)下你的接收播放也是fps =25。緩沖區(qū)單位時(shí)間內(nèi)播放對應(yīng)的幀數(shù)陕截。在android端注意緩沖區(qū)的大小驳棱。最好是動(dòng)態(tài)的修改,我的思路是根據(jù)收包后發(fā)送響應(yīng)時(shí)間給服務(wù)端农曲,然后計(jì)算出當(dāng)前的網(wǎng)絡(luò)環(huán)境社搅,動(dòng)態(tài)修改播放緩沖區(qū),將ping和播放緩沖區(qū)結(jié)合起來用的思路乳规,這個(gè)動(dòng)態(tài)修改還可以是更改碼率降低清晰度等操作根據(jù)業(yè)務(wù)需求去特定修改形葬。
????2)音頻在網(wǎng)絡(luò)波動(dòng)的時(shí)候的處理,如果說音頻平均傳輸需要12kb/s的帶寬暮的,你當(dāng)前是8kb/s甚至更低笙以,那可定是不能正常播放的,這種場景并不多冻辩,場景比較多的是某一時(shí)刻網(wǎng)速不好猖腕,之后又恢復(fù)正常拆祈,這種的會(huì)出現(xiàn)如果你一直順序直播放,那么就會(huì)延遲越來越大倘感,此時(shí)需要拋包放坏,也就是緩沖區(qū)有兩個(gè)限制項(xiàng),一個(gè)是最小播放只有滿足最小播放才能播放老玛,一個(gè)是超出最大數(shù)據(jù)量拋包淤年。
? ? 3)音視頻同步問題,其實(shí)解決辦法很多逻炊,例如傳輸之前合規(guī),也可以在傳輸前打上標(biāo)記id犁享,接收到時(shí)可以選擇合規(guī)也可以視頻播放視頻的音頻播放音頻的余素,通過標(biāo)記id統(tǒng)一處理。我的音視頻鏈路是分開傳輸?shù)拇独ィ部梢灾苯右粭l鏈路傳輸音視頻桨吊,那么可以忽略音視頻同步問題。
三 之后編寫部分
之后會(huì)重點(diǎn)寫寫各端上音視頻部分凤巨,會(huì)寫一下例子放到github上视乐。也會(huì)重點(diǎn)說說遇到的問題情況。例如有的芯片版上同屏IOS數(shù)據(jù)橫豎屏有問題敢茁,以及遇到捕獲IOS數(shù)據(jù)的extension的坑等佑淀。