K歌、短視頻技術最佳實踐——「唱吧」音視頻技術探索

內容原創,轉載請註明出處算法

1、引言

  移動App發展到今時今日,幾乎全部應用程序裏都有音視頻相關功能,總結起來大概有音視頻錄製,音視頻播放,音視頻特效處理,音視頻傳輸這幾方面內容。
  比較熱門的短視頻應用抖音與快手,K歌應用唱吧與全民K歌,以及網易雲新出的音街,還有聽歌的網易雲音樂和qq音樂等。雖然每種應用的運營模式不同,可是從技術實現上大同小異。短視頻類軟件的視頻處理更豐富,有很是炫酷的濾鏡,貼紙,K歌類軟件對音樂的細節處理的較多,諸如音量,音效,音調。
  筆者是唱吧較早的用戶,發展至今,唱吧涵蓋的音視頻技術相對比較全面,原唱吧音視頻技術負責人展曉凱老師在《音視頻進階指南》一書中對移動端音視頻最佳實踐給出了具體實現,因此就以唱吧爲參考,探索移動端的音視頻相關技術。(代碼實現上基於安卓,不過對於音視頻這種偏底層的操做,各平臺實現思路大體同樣。另外,筆者不曾在唱吧工做過,如有侵權或其餘行爲,請留言溝通。若想與筆者共同探討移動音視頻技術,歡迎留言或微信smzh_james探討)。

json


涉及技術目錄一覽
1、音頻錄製(錄音,音頻解碼,音頻重採樣,音頻混合)
2、音頻播放與效果處理 (音量,音調,音效)
3、視頻錄製 (Camera相關,視頻編碼)
4、視頻特效處理(美顏,濾鏡,貼紙)
5、視頻播放(視頻解碼,視頻渲染, 音視頻同步)
6、音視頻混合
(內容連接會陸續更新)






2、效果對比與技術實現淺析

參考唱吧App的界面,作了一款簡易的K歌軟件,黑白風格,可是涵蓋了大多數功能,如下稱爲SuperKtv。效果對好比下圖。微信

(一)K歌列表頁

  圖1是SuperKtv,圖2是唱吧。

網絡

  SuperKtv實現比較簡單,直接獲取手機上現有音頻文件以列表展現,爲後續功能提供一個入口。實現上沒啥難度,作開發的都懂,就再也不贅述。異步

(二)音頻錄製頁

  前兩幅圖是SuperKtv的音頻錄製頁,第三幅圖是唱吧音頻錄製頁。

佈局

  • 歌詞
      對於K歌軟件來講歌詞是很重要的一部,像唱吧這種專業K歌軟件,曲庫很龐大,爲了與演唱同步,歌詞通常都帶有時間戳,以便演唱過程當中歌詞動態滾動,還有打分功能。從技術方面分析,歌詞和打分功能每首歌應該都不相同,這方面本質不涉及音視頻技術,筆者也沒有太多精力爲歌曲自定義歌詞和打分功能。
      爲了讓SuperKtv與唱吧有較高的鍥合度,筆者仍是想辦法作了這方面的工做。要快速解決SuperKtv的歌詞問題,只能從網絡入手,利用 歌詞網https://www.90lrc.cn 強大的搜索功能,使用其搜索接口先搜索歌名,返回帶搜索結果列表的Html,而後解析Html獲取合適的列表項,通常取第一項最爲合適,並取出其中的歌詞連接,再經過歌詞連接獲取帶歌詞的Html,過濾後便可獲取獲取歌詞文本,顯示效果如上圖1。打分功能的一中實現方法是在演唱過程當中匹配動態識別的音高與預約義文件中的內容。

    性能

  • 錄音
       K歌軟件的核心功能之一,對於錄音的低延時性能要求較高,這一點上IOS比Android作的要好的多,並且功能很完善,用AudioUnit能夠解決大部分問題;Android8.0如下推薦使用OpenSL ES,是OpengSL在嵌入式設備上的精簡實現;Android8.0及以上推薦使用AAudio,一個輕量級錄音接口,爲低延時音頻處理而生,據官網文檔描述低延時性能較好,並且給出了Pixel幾款機型的測試數據。可是由於安卓的機型是在太多,硬件實現上付出的努力不盡相同,因此這二者在兼容性上均不如AudioRecorder好。Google Android團隊對OpenSL ES和AAudio進行了封裝,命名爲Oboe,看起來大有追趕AudioUnit的理想,提供了統一的調用接口,使用極爲方便,在GitHub上直接能搜到Oboe https://GitHub/Oboe,提供的Demo參考價值很大,但根據GitHub上的反饋來看,使用起來問題很多,主要在雜音和延遲上。期待後續的更新能完全解決這個安卓從問世以來就一直被詬病的問題。
       低延遲錄音的另外一個解決方案是計算錄音延遲,設想若是能獲取到錄音延遲,那麼在編輯頁混合的時候能夠人爲的將錄到的聲音提早一段時間,獲得的結果幾乎接近於沒有延遲。但要是能這麼容易解決的話就不是Android了,錄音的延遲計算並不容易,粗略的計算 錄音延遲 = 硬件延遲 + 緩衝延遲,若是在錄音回調中有較多計算的話還須要加上計算延遲。截止目前只有AAudio中提供了計算延遲的方法,尷尬的是經實測延遲爲0。即使是8.0以上的設備已經佔據主流市場,但對於商用軟件來講支持到8.0確定是遠遠不夠的,硬件的延遲或許只有廠商可以拿到比較準確的數據,因此和廠商合做也是其中一種解決辦法。對於華爲手機來講,華爲的Sdk裏面提供了相應的Api,使用AudioKit能直接獲取錄音延遲,並且具有耳返功能和七個音效,這應該是迄今聽到的最好的消息了。

    學習

  • 音頻解碼和重採樣
       在K歌軟件錄音的時候通常會播放伴奏或者原唱爲演唱者提供參考,底層的Api通常都不提供解碼功能,因此在將數據「填充」到設備緩衝區以前要將伴奏或原唱解碼成原始數據,就是大名鼎鼎的Pcm。歌曲文件通常是Mp3或者AAC格式,Mp3的編解碼推薦使用Lame,AAC的編解碼推薦使用Fdk-aac,這二者在速度上迄今爲止作到了最好,對於有實時性要求的最合適不過了,能夠直接使用或者將這兩個庫編譯到FFmpeg裏面,調用FFmpeg的Api操做,再退一步,直接使用FFmpeg自帶的解碼算法也能完成相應功能,固然性能上是有所區別。在解碼上廣泛的作法是編譯帶有Lame或Fdk-aac的FFmpeg,調用FFmpeg的Api去作編解碼,由於其Api的統一性上頗有優點。
       在Android平臺下還可使用MediaCodec進行解碼,系統自帶Api,使用方便,文檔較多,並且使用硬件解碼,速度上也比軟件解碼快不少,遺憾的是直到Android6.0之後才提供了MediaCodec的C++接口,考慮到和IOS的統一性,推薦使用前者。
       重採樣關鍵點是音樂文件的採樣率、聲道數和採樣深度(也稱量化精度)。某一個音樂文件的採樣率、聲道數、量化精度能夠認爲是不肯定的,但播放平臺硬件支持的採樣率、聲道、採樣深度是有限的、肯定的,因此在「填充」數據給音響設備的緩衝區以前的另外一項工做是重採樣,直接使用FFmpeg就行了,FFmpeg提供的重採樣功能比較強大,包括採樣率、聲道數和採樣深度均可以改變。參考文檔或在網上搜索均可以 。須要注意的是網上的FFmpeg重採樣教程大多忽略了一個問題,從單聲道重採樣成雙聲道的時候,爲了保證聽覺上音量不變,實際的分貝(響度)會有輕微的變化,這點還須要特別注意。


    測試

  • 音頻內容保存
       考慮到K歌軟件錄到的音頻內容在後面的要進行編輯,在磁盤空間不受限制的狀況下建議直接保存原始數據,方便後續操做,一首歌的空間通常在100Mb之內。若是考慮磁盤空間的話,能夠將錄到的數據編碼保存,編輯的時候再進行解碼。關於音頻編碼部分在保存的時候再詳細分析,要注意的是在保存數據的時候要頻繁讀寫文件,考慮到性能問題,能夠選用異步操做來完成,異步操做的線程同步問題可使用Boost庫中提供的無鎖隊列來完成。
    編碼

(三)音頻編輯頁


  前兩幅圖是SuperKtv,後兩幅圖是唱吧。在寫這邊文章的時候唱吧的編輯頁進行的改版,頁面佈局發生了巨大變化,風格上跟音街比較像。但在筆者模仿唱吧界面的時候,老用戶應該比較清楚,長相跟SuperKtv很是接近,此處比較許遺憾,沒能以最高類似度復原唱吧App。

  • 音頻播放
       K歌軟件的在編輯頁須要音頻播放功能,對低延時性能要求也比較高,目的是將前面錄好的內容和伴奏同時播放,聽起來像是在聽歌星的專輯同樣。在Android平臺上使用OpenSL ES和AAudio是較好的選擇。爲達到較好的同步效果,選用低延時Api是必要的,此外,還須要精確控制兩個軌道播放的時間同樣。比較簡陋的作法是打開兩個播放器,一個播放人聲,另外一個播放伴奏,以達到同步播放的效果,更具使用價值的是選擇一個播放器,將人聲和伴奏的Pcm數據混合以後播放,後者的同步性要好於前者,效率是也更高。在SuperKtv中使用的是第二種方案,效果還不錯。在播放以前要關注Pcm數據的聲道、採樣率、採樣深度,必要時進行重採樣。

  • 音量控制
       在音頻處理中,對編碼好的音頻處理很難,通常是對原始數據進行處理。在後續的音量、音效、音調處理都是對Pcm處理。
       衆所周知聲音是波,音量能夠理解爲波的振幅,也叫響度,改變音量即改變波的振幅,從數學的角度要增大一個正弦波的振幅只要乘以增益就能夠,一樣,音量也是對Pcm數據乘以增益就能夠,固然能夠直接使用FFmpeg來解決。要注意的是增大振幅以後不能超過每一幀數據的最大值和最小值,以16位整形精度爲例,若是最終的數值大於32767或小於-32768,那麼音質就會受損,聽起來像雜音同樣。

  • 音調控制
       音調主要由聲音的頻率決定,同時也與聲音強度有關。對必定強度的純音,音調隨頻率的升降而升降;對必定頻率的純音、低頻純音的音調隨聲強增長而降低,高頻純音的音調卻隨強度增長而上升。音調調節推薦使用SoundTouch https://sourceforge.net/projects/soundtouch/),用法比較簡單,並且還具備變速等功能。
       音調主要是由聲波的頻率決定的,聯想到數學或物理學中波相關的知識點,能夠本身手動實現,簡單理解爲將音頻數據重採樣成另外一個頻率,但數據內容沒有減小。由傅里葉變換的定義能夠知道,任何週期性的波均可以分解成若干個正弦波的疊加。在這裏廣泛的作法是將Pcm數據經傅里葉變換獲得頻域的結果,改變基波的頻率,由於頻率的變化可能致使數據減小或增多,須要再進行插值計算,最後作反向傅里葉變換將數據變換到時域,就能夠獲得改變頻率的Pcm數據,也就是改變了音調。

  • 音效控制
       由於聲音是波,因此音效也是根據波的一些特性進行處理的,好比回聲效果就是根據波的反射性實現的。展曉凱老師在《音視頻進階指南》一書中給出了用Sox實現音頻效果的具體實現,FFmpeg也能夠對音頻進行效果處理,SuperKtv中用二者均實現了音頻效果處理。Sox在音頻效果處理上功能稍微更全面一些,推薦使用Sox http://sox.sourceforge.net/,被譽爲音頻處理方面的瑞士軍刀,遺憾的是這個庫在早些時候已經中止更新了,其功能逐漸被桌面版客戶端AudioCity取代。
       音頻效果處理分爲混響,均衡,壓縮。混響能夠理解爲在一個房子裏面唱歌有回聲,影響的因素通常有房間的大小、牆壁的反射率(裝修材料等),演唱者所處的位置等,因此經過改變房間大小等參數,就能夠模仿專業演播大廳的效果,像維也納演播大廳的混響參數都是已知的,參考這些參數就能模擬出在維也納演播大廳開演唱會的效果。均衡器能夠分別調節各類頻率成分信號放大量,通常調音臺上的均衡器僅能對高頻、中頻、低頻三段頻率電信號分別進行調節,均衡器還具有高通濾波和低通濾波的功能,好比唱吧中的留聲機效果就是使用了均衡器,一方面過濾掉某些頻率的諧波,另外一方變改變某些頻率信號份量的增益就能夠獲得留聲機的效果。壓縮器在百度百科上的定義以下:「壓縮器是一種隨着輸入信號電平增大而自己增益減小的放大器,實質上改變的就是輸入與輸出信號的比例。壓縮器是兩種最多見的用於處理音頻信號動態範圍的設備之一」,這個定義應該很明瞭了,在Sox的實現上主要以調節參數爲主。經過這三種效果的疊加,就能夠調節出Ktv、錄音機、流行,說唱等不一樣風格的效果。上圖中的自定義音效是對混響、均衡、壓縮三種效果中一些重要參數提供了可調節入口,其餘音效能夠理解爲一些固定參數根據經驗的組合。以上三種算法原理能夠參考Sox源碼中reverb.c,bequed.c,compand.c這三個文件。

(四)視頻錄製頁

  前三幅圖的SuperKtv的視頻錄製界面,後三幅圖是唱吧的視頻錄製界面。
  從功能上對比,SuperKtv美顏面板少了銳化,瘦臉,大眼功能,另外道具功能也沒有實現,稍後具體分析。
   音頻部分跟前面同樣,重點分析視頻的錄製。視頻的錄製須要保存帶美顏效果的視頻,固然也能夠保存原始視頻畫面在編輯頁進行處理,唱吧中採用了前者,因此SuperKtv中也採用了第一種方案。

  • 相機操做及美顏、濾鏡
       這一部份內容須要有必定的OpenGL ES基礎,能夠理解爲對每一幀畫面中的每個像素進行處理,而後渲染到屏幕上。詳細原理能夠參考博主OpenGL ES系列文章
    Android OpenGL ES從入門到進階(一)—— 五分鐘開發一款美顏相機
    Android OpenGL ES從入門到進階(六)—— OpenGL ES人像美白與磨皮初探
    Android OpenGL ES從入門到進階(八)—— 萬能的Lookup濾鏡
    上述內容都屬於OpenGL ES基礎部分,本文再也不描述,有疑問的能夠留言交流。




  • 貼紙(道具)
       在美顏相機類產品中,貼紙功能應該算一個重頭戲,唱吧中稱爲道具,目前抖音中的貼紙類功能算比較強大的。這類功能的實現依賴於人臉面部特徵點識別,並且對實時性要求比較高,國內商用人臉識別Sdk裏面作得比較好的商湯科技和曠視科技,中小型公司的美顏類產品都從這兩家購買Sdk,抖音和FaceU激萌據筆者所知用的是字節跳動自研的人臉識別Sdk。因爲對實時性要求較高,通常都是採用深度學習算法,訓練好模型以後根據模型識別。若是對實時性要求不高的話可使用Dlib http://dlib.net/,基於OpenCV的使用比較簡單,這個也是基於深度學習的,官方文檔中給的模型大概有100Mb,畢竟是開源免費的,性能上較商用Sdk有不小差距。經實測,Dlib識別一幅1080 x 720的圖片大概須要400 - 500ms,在某些性能低的手機上甚至須要1000ms,而商用Sdk能夠在20ms內完成,這也是爲何有開源免費的,商用的還能賣那麼貴的緣由,同時啓示咱們要尊重知識產權。因爲這些限制,SuperKtv中暫時沒有動態貼紙功能。

   關於貼紙的使用,能夠參考另外一篇博客 Android OpenGL ES從入門到進階(七)—— OpenGL ES 2D貼紙與Blend混合 這是一篇關於靜態貼紙的文章,動態貼紙稍微麻煩一些,須要根據面部特徵點時時改變貼紙的位置和角度,有的還須要作平移和縮放,貼紙的變化規則須要提早定義好,以商湯Sdk爲例,貼紙的運動規則定義在一個json文件中,使用貼紙先解析json內容,而後隨時間作週期運動,這就是動態貼紙的大體原理。

  • 視頻編碼
       不一樣於音頻,原始視頻佔用磁盤空間很是大,若是錄到的視頻保存成原始數據的話那不可想象,因此在錄製視頻以後要編碼以後進行保存,視頻編碼格式不少,移動端流行H264和H265,SuperKtv中採用的事H264編碼,因此文中也以H264爲例分析。
       若是保存了帶美顏效果的視頻後不須要對畫面進行二次編輯(好比唱吧),在Android平臺首選MediaCodec配合Surface,一方面使用硬件編碼速度要優於軟件編碼,另外一方面系統自帶Api使用上會更方便,Mediacodec能夠將Surface上的數據直接編碼,只須要將編碼後的數據寫文件便可,這很是契合SuperKtv的需求。但硬件編碼的兼容性不是很好,尤爲是安卓的機型成千上萬種,總有幾款手機使用起來有這樣那樣的問題,考慮到這樣的狀況有時候也會採用軟件編碼,軟件編碼H264推薦使用X264,算法性能上要優於FFmpeg自帶的H264編碼算法,可是廣泛的用法是將X264編譯到FFmpeg裏面,用統一的接口調用,這種方法稍微麻煩一些,先要將相機的回調數據作美顏、濾鏡等處理,而後再根據需求看是否須要進行數據格式轉換,由於Android的Camera API有兩套,Camera1推薦使用NV21,Camera2推薦使用Yuv420p,用FFmpeg編碼的時候,因爲顏色空間的特殊性,爲方便使用,建議將數據統一轉換爲Yuv420p格式再進行編碼,轉換算法網上有,這裏推薦使用LibYuv,功能很強大,這一部份內容要求要對圖像的幾種顏色空間有所瞭解,特別是RGBA系列和YUV系列。
       若是在編輯頁面要對原始視頻進行二次編輯,那麼只能保存原始畫面,這時候可使用MediaCodec,也可使用FFmpeg,在這種需求下兩者的卻別不是很大了,硬件編碼速度快,但兼容性稍差,軟件編碼兼容性好,但速度稍慢,可適當取捨而定。固然FFmpeg也是支持硬件編碼的,只是編譯的時候須要作一些額外的處理,但若是是硬件編碼,爲什麼不直接使用系統API呢。


(五)音視頻編輯頁

  前兩幅圖是SuperKtv的實現,後兩幅圖是唱吧的實現。
  一樣的,唱吧在音視頻編輯頁面也進行了較大改版。唱吧在演唱功能下沒有實現對視頻的編輯,因此也跟隨了唱吧的風格,主要是對音頻的編輯,功能實現上跟前面提到的音頻編輯同樣,比較重要的一點是視頻播放,涉及到視頻解碼和音視頻同步。若是說要實現視頻的編輯功能,有兩種思路,一中是用OpenGL ES處理後後臺保存,另外一種是使用FFmpeg或OpenCV對數據處理後保存,推薦使用OpenGL ES進行視頻編輯。

  • 視頻解碼
       Android平臺推薦使用MediaCodec解碼,能夠直接解碼到Surface上進行顯示,這省去了不少額外的操做(數據格式轉換和渲染)。
    固然也可使用X264或FFmpeg進行解碼,將解碼以後的數據拷貝到ANative_Window提供的緩衝區用以顯示,這比起FFmpeg編碼已經方便不少了,若須要使用OpenGL ES對視頻進行編輯,須要將數據轉換成紋理,並上傳到OpenGL ES提供的緩衝區用來顯示。唱吧導入外部視頻能夠編輯,能夠用上述思路都可實現。

  • 音視頻同步
       音視頻同步是視頻播放很重要的一個環節,直接影響到前面全部工做的結果。音視頻同步通常使用時間戳同步,這要求在編碼的時候加上正確的時間戳。Camera的回調裏面有當前幀的時間戳,以微秒爲單位,跟MediaCodec所需單位一致,編碼時使用較方便。FFmpeg的時間戳跟MediaCodec不一樣,使用的是根據時間基計算出來的,編碼時只須要加上當前幀的索引就能夠。同步的緣由是由於音頻在單位時間內的播放數據量是固定的,只需給播放Api回調填充數據就能夠,但視頻沒有相應的機制,在畫面上顯示的內容以及何時顯示都是由外界控制,假如用解碼速度控制的話,若是解碼速度太快,在很短的時間內就能播放完一個較長視頻,若是解碼速度太慢,就會致使畫面卡頓。同步的過程大體能夠描述以下:若是當前幀播放快了,那下一陣繼續播放當前幀,至關於等待,若是當前幀播放太慢,那就丟棄,播放下一幀或下下一幀,這樣就能控制視頻播放的速度了。同步的方法通常有音頻向視頻同步,視頻向音頻同步,兩者向基準時間同步,普片選用視頻向音頻同步,由於音頻在單位時間內播放的數據固定的。

(六)保存和做品列表頁

  第一幅圖是保存的界面,第二幅圖是保存後的做品列表頁面。界面簡單,沒有與唱吧進行對比。

  保存的目的是爲了讓保存的結果和編輯頁面調節後的效果同樣,至關於重複一遍編輯頁面的操做,只不過要變成後臺操做,前臺惟一須要顯示的是保存進度,後臺處理過的數據直接進行編碼便可。

  • 音頻編碼
       前面提到,爲方便編輯,錄到聲音後保存成了原始數據,但音頻的結果必然是成品(編碼後的)的,因此在保存以前先要編碼。移動端音頻編碼通常使用Mp3合適或者AAC格式。Mp3編碼推薦使用Lame,AAC編碼推薦使用Fdk-aac,也可使用MediaCodec 或者FFmpeg自帶編碼算法。對於SuperKtv的保存需求來講,編碼速度影響不是很大,因此選哪種均可以,在瞭解MP3格式和AAC格式的區別以後,任選一種使用哪一種方法均可以達到很好的效果。有一個不一樣點是MediaCodec編碼的AAC是不帶ADTS的裸流,若是此處是最終音頻文件,須要手動添加ADTS,而使用Fdk-aac編碼以後會自動加上ADTS。

  • 音視頻合併
       截止這一步驟,前面生成的音視頻文件都是單獨的,對於音頻錄製來講到這一步就已經完成了,對於視頻來講還須要將音頻和視頻合併成一個文件,音視頻合併能夠理解爲將兩個文件合併成一個文件,經過某種規則將兩個類型的數據區分,使用的時候可以單獨拿到音頻和視頻數據,有點相似於編輯頁面音視頻的同步的感受。使用Android提供API MediaMuxer或者使用FFmpeg都可以。使用MediaMuxer合併音視頻的時候,若音頻是AAC格式,須要提供AAC裸流,使用FFmpeg的話就不用,甚至能夠直接用命令來完成,合併完以後就是廣泛意義上的視頻,是能夠直接發佈的做品。

(七)本地做品播放頁

  第一幅推是音頻播放界面,第二幅圖是視頻播放界面。界面簡單,沒有與唱吧進行對比。

   前面作了好多工做,最後的結果只是一個文件,一種是音頻文件,相似於.mp3,經常使用的還有.m4a等;另外一種是視頻文件,相似於.mp4,經常使用格式還有.flv等。對於這樣的文件每一個人應該都很熟悉,手機自帶功能就可以播放,因此在SuperKtv中欣賞或者分享我的做品的時候不必本身再寫一套播放器,使用現成的就能夠,甚至直接調用系統API MediaPlayer就能夠。關於播放器的使用,安卓平臺下推薦使用EXOPlayer,這是谷歌基於MediaCodec的一款開源播放器,支持音、視頻及其經常使用格式,使用硬件解碼,還帶有一些基礎控件,定製性較高。SuperKtv就是基於ExoPlayer作的本地做品播放功能,效果展現如上圖,使用體驗也很不錯。

3、總結

   本文是以「唱吧App」爲參考,基於Android平臺,總結了移動端音視頻技術的實現策略,適合有必定音視頻基礎的開發人員,能夠做爲技術方案參考,因爲時間精力有限,實現細節隨後以文章形式發佈,如有疑問歡迎你們留言交流。

轉載請註明出處。

友情連接:
一、FFmpeg http://ffmpeg.org/ (音視頻必備神器)
二、Sox http://sox.sourceforge.net/ (音頻界瑞士軍刀)
三、SoundTouch https://sourceforge.net/projects/soundtouch/ (音調、變速)
四、Dlib http://dlib.net/ (人臉特徵點識別開源庫)
五、Oboe https://GitHub/Oboe (Android平臺低延時音頻接口)

相關文章
相關標籤/搜索