開發一個語音通訊解決方案是一個軟件項目。既然是軟件項目,就要有相應的計劃:有多少功能,安排多少軟件工程師去作,這些工程師在這一領域的經驗如何,是否須要培訓,要多長時間作完,中間有幾個主要的milestone等。咱們曾經四我的花了近一年時間開發了一個語音解決方案,成功經過驗收,各項關鍵指標(語音質量、單向時延)均達到運營商要求。當時是在芯片公司,在公司本身的芯片上作語音解決方案,增長芯片的賣點,加強芯片競爭力。咱們作語音數據面實現,同時提供API。用戶在上層控制協議(例如SIP)中調用API,從而實現語音通訊功能。下面聊聊咱們當時是怎麼一步步作出來的。算法
1,討論需求和軟件架構網絡
第一步是討論需求,看要實現哪些功能,並對功能排一個優先級。討論下來前處理(回聲消除、噪聲抑制、增益控制)、tone generation、重採樣、codec(g.7十一、g.72二、g.729)、RTP、RTCP 、jitter buffer等是第一優先級功能,VAD、CNG、SID、PLC等是第二優先級功能。同時制定了幾個關鍵的milestone點:討論需求和軟件架構、成功打通第一個basic call、完成第一優先級功能產品基本能用、完成第二優先級功能攻克關鍵指標、全面測試經過驗收。需求討論後就要看用一個什麼樣的軟件架構去實現這個解決方案。因爲在Linux上開發,Linux上關於聲音的架構是ALSA,討論下來咱們基於ALSA來作,這樣能夠縮短開發週期,在driver上僅僅是調試,在user space裏用好ALSA-Lib等。同時肯定了須要三個thread。Capture thread每隔10ms運行一次,靠ALSA獲取採集到的語音PCM數據,而後作前處理編碼打包等發到網絡上。Play thread也是每隔10ms運行一次,從jitter buffer中獲取10ms的碼流,而後解碼成PCM等再經由ALSA送給audio device播放出來。Packet receive thread用來從網絡上收包,收到後解析並放進jitter buffer中。因爲jitter buffer會被兩個thread訪問,須要加保護。這樣語音數據面的軟件架構圖以下:架構
這些都肯定後咱們就開始向第一個Milestone進軍了。框架
2,成功打通第一個basic callless
這一階段的目標就是成功打通第一個basic call。在每一個階段的開始咱們都討論一下你們的分工。共四我的,一個同窗負責搭軟件框架以及作API給上層協議調用。一個同窗對ALSA比較熟悉,由他負責調ALSA,分兩個階段:第一階段是把從ALSA獲得的MIC的PCM數據再經由ALSA送給speaker播放出來,即在PCM側造成Loopback。若是從MIC講的話到speaker正確播放出來,ALSA的調試就完成了。第二階段是加上最簡單的codec G.711,造成codec loopback,即從ALSA獲得的MIC的PCM數據用G.711編碼獲得碼流,而後用G.711解碼獲得PCM數據再經由ALSA送給speaker播放出來。我主要負責網絡側,包括RTP的實現以及UDP收發包的實現等。也是經過loopback的方式調試,即將特殊字符(如全’a’)做爲RTP的payload打包(codec用G.711),而後經過UDP socket發給目標地址,在目標地址板子上用UDP socket收包,收到後解析RTP獲得payload,這個payload與發送端的payload 相比較,若是徹底相同則說明RTP的實現UDP收發包的實現是正確的。因爲jitter buffer相對比較複雜,這一階段暫時不用jitter buffer,用固定的緩衝buffer來替代,可是jitter buffer的設計開始作起來。一個同窗負責上層協議,也就是SIP,用一個開源的實現(上層協議不是咱們的重點,主要是配合底層實現的),把基本功能調試好就能夠了。socket
通過你們的努力,這一階段的目標如期實現。當成功打通第一個basic call的時刻,你們別提多高興啦。畢竟這幾乎是從零開始作方案,這種經歷仍是可貴的。咱們還特意出去吃了一頓大餐,慶祝basic call成功打通。oop
3,完成第一優先級功能,產品基本能用測試
這一階段的目標就是完成第一優先級功能,產品基本能用。依舊先分好工。先前作框架和API的作重採樣、tone generation和codec(G.722/G.729)。先前調試ALSA的負責前處理(回聲消除、噪聲抑制、增益控制)。他們主要是先驗證算法再調試算法使其在咱們的方案裏能很好的運行,先保證能用,後面再優化。我仍是負責網絡側,完成jitter buffer和RTCP。作SIP的同窗在這一階段使功能更完善。這一階段相對於上一階段難度加大了,咱們每一個人在作的過程當中都或多或少的遇到了一些困難。以我本身爲例,在作jitter buffer的過程當中就遇到了很多困難,主要是在一些細節上設計時想的不全,致使在良好網絡下音質有時也很差。這些問題後來都一一被解決,同時也更新了設計文檔。在你們的努力下,這一階段也如期完工。老闆來驗收後對目標完成表示滿意,又帶着你們出去吃了一頓大餐。優化
4,完成第二優先級功能,攻克關鍵指標編碼
這一階段的目標就是完成第二優先級功能,攻克關鍵指標。關鍵指標有各類場景下的打電話時CPU load、各類網絡環境下的語音質量(MOS值)、單向時延(one way delay)、連續打多少通電話不出錯(bulk call)、單通電話最大能持續多長時間(long call)等。先前作重採樣等的同窗作第二優先級功能(VAD、CNG、SID、PLC等)。先前作前處理的作CPU load優化,使打電話時所有模塊所佔的CPU load控制在一個合理的範圍內,固然是越小越好,作的很好就是咱們方案的一個賣點。我主要負責語音質量、bulk call、long call等。作SIP的同窗依舊完善SIP的功能。這一階段使愈來愈難愈來愈有挑戰了。優化算法load的同窗須要測出各個算法的load,而後一一優化,儘量降到最小。語音質量把我折騰的夠嗆。單向時延也是,儀器測出來的是一個總的值,我要找到各個模塊引入的delay是多少,而後看怎麼減少。bulk call和long call都是作起來很是頭疼傷腦細胞的,通常都是晚上或者週末測,次日早上或者下週一早上看結果,若是有問題就分析看怎麼解決。
在這一階段很多問題都是你們一塊兒討論看怎麼解決的,畢竟這些問題都是很難的。 咱們沒被困難打趴,仍是在規定時間內搞定了。咱們離最終目標愈來愈近了!
5,全面測試,經過驗收
在前面幾個階段中,把一個功能作完後會對這個功能相關的進行測試,確保功能無缺,可是因爲趕進度,並無作全面性的測試。如今各項功能所有完備,關鍵指標也已所有達標,是時候全面測試確保產品質量了。首先是由咱們開發人員本身測,咱們以爲質量能夠了再交由測試人員測。咱們本身測時先由一我的寫測試用例,而後你們一塊兒review討論,確保儘可能全的覆蓋,還有就是異常case要儘可能多的考慮。測試用例review後你們分工測,發現了些問題,因爲代碼除了算法全是咱們本身寫的,對代碼機制很是熟悉,發現的問題很快就解決了(算法都是很是成熟的,通常不會出問題)。交給測試人員後共測試了三輪,也發現了一些問題(測試人員的測試用例會更多一些,他們的測試也會更專業一些,必定要由他們把關產品質量),也都很快解決了。最終順利的經過了驗收。老闆和咱們都特別高興,雖然咱們的方案是免費提供給客戶,可是這加強了咱們芯片的競爭力,能夠提升芯片的出貨量。對咱們我的而言也是一次很可貴的一個方案從無到成功完成的經歷,畢竟工做中這種經歷太少了。
最後咱們也進行了總結,有哪些lesson learnt,哪些作的不錯,哪些還能夠作的更好。