Linux音訊驅動
概述
ALSA(Advanced Linux Sound Architecture)是linux上主流的音訊結構,在沒有出現ALSA架構之前,一直使用的是OSS(Open Sound System)音訊架構。關於OSS的退出以及ALSA的出現,可以看 Linux音訊驅動-OSS和ALSA聲音系統簡介及其比較。
關於OSS和ALSA音訊架構之間的區別圖如下:
主要的區別就是在OSS架構下,App訪問底層是直接通過Sound裝置節點訪問的。而在ALSA音訊架構下,App是通過ALSA提供的alsa-lib庫訪問底層硬體的操作,不再訪問Sound裝置節點了。這樣做的好處可以簡化App實現的難度。
同樣ALSA為了相容OSS,ALSA提供了核心模組來模擬OSS聲音驅動,所以在OSS架構下編寫的App無需修改就可以在ALSA下執行。另外libaoos庫也可以模擬OSS,無需OSS相關的核心模組。
音訊子系統檔案目錄結構
音訊系統的檔案位於kernel/sound下:
root@test:~/k3.18/kernel/sound$ ls
ac97_bus.c atmel firewire Kconfig mips pci sh sound_firmware.c synth
aoa core i2c last.c oss pcmcia soc sparc usb
arm drivers isa Makefile parisc ppc sound_core.c spi
主要的目錄的介紹資訊如下:
音視訊聊天技術分享1、前言
無論對於音訊編碼還是視訊編碼而言,對於編解碼來說,都有不同的應用場景。
比較大的應用場景有兩個範圍:
第一種:面向檔案直播的編解碼器;
第二種:面向網路通訊的編解碼器。
在不同的應用場景下面,編解碼器的選擇完全不一樣。舉個例子,面向直播的時候,延時比較長、丟一些包、網路頻寬跳動等,是不需要考慮。可以在離線的時候,充分的利用CPU的能力去編,可以固定I幀的間隔、固定I幀的大小,甚至多個B幀都可以。位元速率也可以是固定的,這對於錄檔案和錄視訊沒有問題。
但是如果面向實時網路通訊這樣恐怕不太好,因為網路是瞬變,而且網路狀態一般來說都是會伴隨著丟包這樣的狀態,這個時候就不適合使用,比如說使用固定的I幀間隔,如果I幀丟了就要使用I幀重傳。還有就是也不能使用固定的位元速率,因為位元速率也是瞬變的。
所以在面向於網路通訊的編解碼器中,編解碼器選擇和直播的編解碼器的選擇差別比較大。比如有人提出在網路通訊中硬體壓縮是否可以,其實由於不同的廠商對硬體壓縮的支援是不一樣的,硬體壓縮的迭代不一樣,編碼支援也不太一樣。有的硬體版本也不能產生可變I幀大小,或者位元速率是固定的,如果位元速率不固定就會自動重啟硬體的編解碼器。我說的都是一些個例,並不代表所有編解碼器都是這樣。
我想強調的是,編解碼器面向直播和網路通訊是不一樣的,我今天想說的是面向不可靠傳輸網路的抗丟包編解碼器。
2、抗丟包的重要性
首先我們來思考一下抗丟包的重要性。
在2017年,有幾類應用是比較火的。第一類在大學校園最火的遊戲應該是王者榮耀和狼人殺,王者榮耀10人組隊實時廝殺、還有語音,狼人殺提供實時視訊。第二類就是互動直播,主播端把通訊直播流發到觀眾端,同時也可以把觀眾端拉上麥,實現主播和觀眾的互動。
我記得滬江的技術負責人吳海濱曾經提出,“在當前互動網路教育中最難解決的問題還是實時性,就是老師跟學生怎麼能夠更好的互動”。互動直播,在當前網路下給技術提了更高的要求,要求能夠在低延時下提供高品質的通訊品質。包交換的網路中,要想實現低延時和提高包品質,如果承載資訊的包沒有按時到達,接收品質不會好。
3、行動網路時代的丟包現狀
既然丟包是低延時和高品質的一個攔路華,我們來看一下當前的網路狀態是不是有那麼多丟包。
我們提到丟包的時候首先要想到一點,丟包的定義是什麼?
其實對於通訊來說丟包並不意味著真正的包丟了,我個人理解“只要包沒有按時到達都叫丟包”。比如第一個包沒有來,第二個包已經到了,此時第二個包發出去了,那麼第一個包再來對我沒有任何的幫助,實時通訊不可能重來。
對於通訊系統而言,下圖是一個基本的通訊系統,一個APP通過4G和WIFI,再通過公有云實現通訊。大家可能會講了,已經4G、5G,是不是頻寬足夠大,不需要考慮丟包的問題了呢?
不完全是那樣。舉個例子,我們看一下上圖這個包的到達通路,手機和Pad通訊,假如說Pad通過4G到達公有云,再通過WIFI發回給手機端。在這個通路中有三段網路,也就是有三段可能會產生丟包的地方。第一段是公有云之間,因為公有云之間也會有很多路由的轉換。第二是4G或者WIFI到APP端,第三段就是,還有就是APP,device本身。
先來講第一段,在公有云上我們都會建一些伺服器。即使是在同一個運營商下,早上八點、中午12點到晚上8點,網路狀況都不一樣,通常來說晚上8點網路高峰期,這個時候網路傳輸非常上不穩定,在伺服器和伺服器之間經常容易產生丟包。第二是不同的運營商之間,比如電信和聯通之間,當聯通向電信傳輸資料時,由於兩個運營商出口頻寬結算問題,不在高峰期都可能不太穩定。還有一個問題就是,小運營商,比如教育網,機房的狀態不是一直穩定的,可能會產生丟包。
第二段是4G或WIFI到終端,這一塊也並不是非常可靠。如果兩臺裝置連著是不同的基站互相通訊,基站之間可能產生一個轉化和丟包的問題。雖然連線了骨幹網,但是由於骨幹網不同的運營商差異,會產生丟包。在同一個運營商之間,不同的地區,網路狀態也是不一樣的。舉個例子,在今天的會場,雖然我覺得這附近4G是蠻不錯的,但是在2000人大型的會場都在使用聯通4G的時候,實際上共享網路的狀態是比較差的。這就是所謂的共享網路帶來的問題。
當我們在不同的國家連線網路的時候,比如印度、美國,不同的國家網路狀態也不同,所以我們在做網路策略的時候實際上都不能一概而論。
除此之外還有WIFI。如果大家做過實時通訊的話,都會有一個感覺,有些時候4G比WIFI更穩定。因為WIFI實際上是非常不穩定的系統,如果大家在公司連WIFI的話,公司可能會存在數十臺路由器,路由器之間有頻率干擾。另外,即時是上千塊錢的路由器,可支援連線人數最多也就是三四十人。超過這個限制,即使是連上了也會主動丟包。所以,並不是4G和WIFI頻寬足夠高就沒有丟包的問題。
再提出一個非常現實的現狀,運營商都在推行VoLTE,還有一個新詞叫VoWiFi。這是數字電話在產生的一個變革,把電路交換過渡到分組交換,在分組交換下所有的通訊都是通過包來傳遞,而不是固定的鏈路。之前的通話,我一旦撥通這個電話,鏈路就保持下來,不管有沒有用、用多少,鏈路都是存在,所以效果都比較好。但是在分組交換網路下,資料都是以包的形式傳遞,不說話的時候不發包,或者發包比較少,位元速率也會比較低。但是運營商提出VoLTE和VoWiFi已經很久了,實際上我們只有少量情況會使用VoLTE,當網路不好時,都會回落到電路交換進行通話。運營商自己的4G網路品質就不好,更不用說我們在上面做端到端視訊的通訊。
4、丟包問題這麼常見,我們有哪些辦法?
既然丟包問題這麼常見,我們有哪些辦法?
通常有這四種方案:FEC、PLC、ARC、ARQ,還有一個是編碼器。
ARC和ARQ分別是自動位元速率控制和自動請求重傳,都是針對網路狀態進行調整的策略,但是它們倆應用的場景不一樣。對於音訊來說,無論是語音還是音樂,位元速率通常需求比較低,尤其是語音,此時ARC的應用場景並不是特別大。比如,要傳一個16kbps的語音,在當今的網路狀態下問題不大。要傳一個128kbps的音樂就稍微有一點問題。要傳一個400-800kbps的視訊問題就會比較多了。所以ARC是一種針對當前網路狀態進行估計,並且回傳回來主動進行位元速率調整的策略。
ARQ——自動請求重傳,當網路延時比較低的時候,我們可以通過重傳的方法來實現抗丟包。這種方案有兩種策略:第一種,發出一個包,只要在規定的時間沒有響應,就再發一個包;第二種,發出一個包會等它的請求,如果它的請求到了就給它一個重傳包。但是這種技術的使用前提是端到端的網路延時比較短,如果延時比較長,比如延時200-400毫秒,用重傳請求的方法,網路傳輸的延時會更長。
PLC是一個完全後端的抗丟包方法,有最簡單的插值法、過取樣法、還有WebRTC比較流行的拉伸和縮短法。WebRTC的這種方法效果不是很好,因為拉長或縮短會改變聲音的頻率,會產生奇怪的聲音,或者改變語速。插值法採用的策略是,第一個包到了,第二個包丟了,第三個包到了,可以通過差值來實現。一般的PLC,可以對抗5%的丟包,再高了就效果不好。
我想重點講的是FEC,這是一個很大的話題。FEC可以分為兩大類:基於信源和基於通道。信源FEC是,包可以多發幾遍,對於音訊來說一秒可以發50個包,信源FEC就發兩倍100個包,同樣大小多發一遍,來實現抗丟包。基於通道的抗丟包是,比如當前的丟包率25%,我們可以加50%的抗丟包。那麼原始有4個包,經過處理生成6個包,這6個包到達任意的4個包,都可以實現準確解碼。
信源FEC中,如果採用多發包的方式,會產生新的問題,比如要傳輸的是16kpbs的語音,丟包時,是發32kpbs的語音,兩個16kpbs的都發過去。還是把它拆成兩個8kpbs再發?各有優劣。如果使用兩個8kpbs,下降了音質來換取抗丟包性。如果選擇32kpbs,保持音質,之前16kpbs下網路丟包假如是10%,頻寬變成32kpbs後,丟包情況也會不同。
所以,Opus和Silk的編碼器提出一種新方法,採用了下降位元速率的做法,類似於兩個8kbps。在16kbps的音訊流中,有4kbps的小包來對前一幀補償。一旦大的包丟了,就使用小包來進行恢復,但是帶來的問題是音訊品質下降了。FEC是一種很好的抗丟包方法,但是它的問題是有可能會浪費頻寬。使用FEC之後,確實能提高包的到達率,能在有限的延時下把通訊的品質提高。
如上圖所示,我們來看一下FEC的流程。先發出了三個包,從device1發了3個包到device2,packet2丟了,那麼此時的丟包率是33%。device2會發一個lossinfo給device1,告訴它丟包率是33%。然後,device1接著發新的包,此時會發雙倍,兩個packet4,兩個packet5。packet4發生丟包,就會被另一個packet4補償回來了。
這是典型的FEC的流程,但有幾個問題:
loss info本身也可能會丟丟失;
loss info沒丟失,但從device 2 到device 1會有延時;
丟包估計可能不準。丟包率33%,如果擴大視窗可能是25%;
雖然是33%的抗丟包,發冗餘包時只能多發一倍,沒有辦法準確的發33%;
只丟了packet4,但packet5也發了兩遍,這浪費了頻寬,沒有意義的。
如果是多方通訊, device2丟包率33%,但是device3和device4不丟包怎麼辦?如果所有device都多發了一倍的包,而且位元速率不上升,確實device2的抗丟包性好了,但是犧牲了device3和device4的品質。如果把位元速率擴大一倍,所有device的品質都好了,但是浪費了一倍的頻寬。
5、聲網的解決方法分享
基於上一節提到的這種現狀和當前的很多學術研究,我們提出了一種新的思路:結合信源和通道編碼的特點,利用充分包交換網路的特性,基於此,研發出了聲網新的編解碼器——Agora SOLO™。從通訊原理來說,信源編碼是儘可能去追求高壓縮比,去冗餘。而通道編碼是追求強糾錯,靠加冗餘來實現糾錯。Agora SOLO™就是把加冗餘和減冗餘結合起來,不重要的地方減冗餘,重要的地方加冗餘。
我們以上圖為例,來看一下Agora SOLO™的抗丟包特點。對所有的接收端,我們預設都發了這些包。但是,我們會把包分成兩塊,一個是packet 1,一個是packet 1’。如果只收到其中一個包,那麼就實現一個有限失真的恢復,品質相對稍差。如果packet 2和packet 2’都收到了,那麼就兩個包合起來,實現一個高品質的解碼。也就是說,Agora SOLO™預設就不需要等待對當前網路丟包狀態的統計,只需要直接把抗丟包做到編解碼內部。
這樣做的好處是:
首先實現了更低的延時,因為它不需要估計通道的狀態,直接把包發出去就好;
第二是更高品質,收到一個包時品質達到的普通編解碼器水平,收到兩個包達到高品質編解碼水平;
第三,這是面向多人環境的。不同人下行網路不一樣,丟包不一樣;
第四,策略更簡化,使用Agora SOLO™幾乎可以不需要再做策略調整。
上圖我用ITU NTT的中文測試序列跑的測試結果。我稍微介紹一下,ITU的NTT是標準的編解碼器測試序列,裡面有26國語言,我這裡面只拿出了中文部分。除了荷蘭語和俄羅斯語以外,中文是比較難編。因為中文除了一般語言外還有四聲。看圖第一列,是隻收到8kbps的packet1, PESQ的平均分是3.52分。如果只收到packet2,它分數是3.51分。如果packet1和packet2都收到,16kbps時,分數是3.95分。以上是窄帶的分數。寬頻下的分數,是3.58分,滿分是4.5分。這個測試結果可以清晰的看到,只收到1個包時是有限失真的。當兩個包都收到時,品質會明顯提升。
接下來,我們來與其它編碼器進行比較。上圖一箇中文的女聲序列在不同的編解碼器的比較。第一列是不同的丟包率,後面各列是不同編解碼器在不同丟包率下的分數。可以看到在丟包率25%時,Agora SOLO™整整比其它編解碼器高出1分。
最後,再來回顧一下Agora SOLO™抗丟包的特點:
我們可以不再關心網路丟包狀態,預設發兩個包;
如果只收到一個就是有限失真,收到兩個就是高品質的恢復。
6、本文小結
本文分享了實時音視訊聊天在不可靠網路下面臨的諸多資料傳輸層挑戰,並給出了一些參考解決方法,重點分享了聲網的解決思路,僅供參考。
關注+私信;資料;兩個字可以免費領取還有一些關於c++ Linux後臺伺服器開發的資料分享:Linux,Nginx,MySQL,Redis,P2P,K8S,Docker,TCP/IP,協程,DPDK,webrtc,音視訊等等資料、附上一份音視訊學習課程大綱給大家
。