畢業至今,之前一直從事Android開發的工作,今年5月份開始接觸音視訊開發相關工作,于是打算寫一個音視訊相關專欄,讓移動端的同學,能通過這個專欄快速掌握音視訊相關知識,首先帶來第一篇,主要講講移動端的音視訊技術涉及哪些?
音視訊開發基礎知識(1)——圖像基本概念
給Android工程師的音視訊教程之一文弄懂MediaCodec
1. 整體流程
以手機直播為例,其整體流程如下:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAjM2EzLcd3LcJzLcJzdllmVldWYtl2Pn5GcuEGN2IWY4MWM5UGOiRTNhJGMjVGM3QmZyI2YlRmN3MTNvwFN3ADN2kDNtUGall3LcVmdhNXLwRHdo9CXt92YucWbpRWdvx2Yx5yazF2Lc9CX6MHc0RHaiojIsJye.png)
2. 資料采集
2.1. 音頻采集
音頻采集涉及到以下幾點:
檢測麥克風是否可以使用; 需要檢測手機對某個音頻采樣率的支援; 在一些情況下需要對音頻進行回聲消除處理; 音頻采集時設定正确的緩沖區大小。
在 Android 系統中,一般使用 AudioRecord 或者 MediaRecord 來采集音頻。AudioRecord 是一個比較偏底層的 API,它可以擷取到一幀幀 PCM 資料,之後可以對這些資料進行處理。而 MediaRecorder 是基于 AudioRecorder 的 API (最終還是會建立AudioRecord 用來與 AudioFlinger 進行互動) ,它可以直接将采集到的音頻資料轉化為執行的編碼格式,并儲存。
2.2 視訊采集
視訊采集涉及到以下幾點:
檢測攝像頭是否可以使用; 攝像頭采集到的圖像是橫向的,需要對采集到的圖像進行一定的旋轉後再進行顯示; 攝像頭采集時有一系列的圖像大小可以選擇,當采集的圖像大小和手機螢幕大小比例不一緻時,需要進行特殊處理; Android 手機攝像頭有一系列的狀态,需要在正确的狀态下才能對攝像頭進行相應的操作。 Android 手機攝像頭的很多參數存在相容性問題,需要較好地處理這些相容性的問題。
在 Android 系統下有三套 API 可以進行視訊采集,它們是 Camera 和 Camera2還有CameraX 。Camera是以前老的 API ,從 Android 5.0(21) 之後就已經放棄了。和音頻一樣,也有高層和低層的 API,高層就是 Camera 和 MediaRecorder,可以快速實作編碼,低層就是直接使用 Camera,然後将采集的資料進行濾鏡、降噪等前處理,處理完成後由 MediaCodec 進行硬體編碼,最後采用 MediaMuxer 生成最終的視訊檔案。
3. 資料處理
3.1 音頻處理
可以對音頻的原始流做處理,如降噪、回音、以及各種 filter 效果。
3.2 視訊處理
現在抖音、美圖秀秀等,在拍攝,視訊處理方面,都提供了很多視訊濾鏡,而且還有各種貼紙、場景、人臉識别、特效、添加水印等。
其實對視訊進行美顔和添加特效都是通過 OpenGL 進行處理的。Android 中有 GLSurfaceView,這個類似于 SurfaceView,不過可以利用 Renderer 對其進行渲染。通過 OpenGL 可以生成紋理,通過紋理的 Id 可以生成 SurfaceTexture,而 SurfaceTexture 可以交給 Camera,最後通過紋理就将攝像頭預覽畫面和 OpenGL 建立了聯系,進而可以通過 OpenGL 進行一系列的操作。
美顔的整個過程無非是根據 Camera 預覽的紋理通過 OpenGL 中 FBO 技術生成一個新的紋理,然後在 Renderer 中的onDrawFrame() 使用新的紋理進行繪制。添加水印也就是先将一張圖檔轉換為紋理,然後利用 OpenGL 進行繪制。添加動态挂件特效則比較複雜,先要根據目前的預覽圖檔進行算法分析識别人臉部相應部位,然後在各個相應部位上繪制相應的圖像,整個過程的實作有一定的難度,人臉識别技術目前有 OpenCV、Dlib、MTCNN 等。
4. 資料編碼
4.1 音頻編碼
Android 中利用 AudioRecord 可以錄制聲音,錄制出來的聲音是 PCM 聲音,使用三個參數來表示聲音,它們是:聲道數、采樣位數和采樣頻率。如果音頻全部用 PCM 的格式進行傳輸,則占用帶寬比較大,是以在傳輸之前需要對音頻進行編碼。
現在已經有一些廣泛使用的聲音格式,如:WAV、MIDI、MP3、WMA、AAC、Ogg 等等。相比于 PCM 格式而言,這些格式對聲音資料進行了壓縮處理,可以降低傳輸帶寬。對音頻進行編碼也可以分為軟編和硬編兩種。軟編則下載下傳相應的編碼庫,寫好相應的 JNI,然後傳入資料進行編碼。硬編則是使用 Android 自身提供的 MediaCodec。
寫死和軟編碼的差別是:軟編碼可以在運作時确定、修改;而寫死是不能夠改變的。
4.2 視訊編碼
在 Android 平台上實作視訊的編碼有兩種實作方式:一種是軟編,一種是硬編。軟編的話,往往是依托于 cpu,利用 cpu 的計算能力去進行編碼。比如我們可以下載下傳 x264 編碼庫,寫好相關的 JNI 接口,然後傳入相應的圖像資料。經過 x264 庫的處理以後就将原始的圖像轉換成為 h264 格式的視訊。
硬編則是采用 Android 自身提供的 MediaCodec,使用 MediaCodec 需要傳入相應的資料,這些資料可以是 YUV 的圖像資訊,也可以是一個 Surface,一般推薦使用 Surface,這樣的話效率更高。Surface 直接使用本地視訊資料緩存,而沒有映射或複制它們到 ByteBuffers;是以,這種方式會更加高效。在使用 Surface 的時候,通常不能直接通路原始視訊資料,但是可以使用ImageReader 類來通路不可靠的解碼後 (或原始) 的視訊幀。這可能仍然比使用 ByteBuffers 更加高效,因為一些本地緩存可以被映射到 direct ByteBuffers。當使用 ByteBuffer 模式,可以利用 Image 類和 getInput/OutputImage(int) 方法來通路到原始視訊資料幀。
5. 音視訊混合
以合成 MP4 視訊為例:
整體來看,合成的 MP4 檔案,視訊部分為 H.264 編碼格式的資料,音頻部分為 AAC 編碼格式的資料。
通過 MediaMuxer 提供的接口-writeSampleData(),将 H.264 和 AAC 資料分别同時寫入到 MP4 檔案。
6. 資料傳輸
目前比較主流的視訊推流協定有 RTMP 協定、RTSP 協定。
7. 需要用到的技術
涉及到如下技術,我将從圖像、音頻、視訊的順序來羅列:
Camera、Camera2、CameraX。
SurfaceView、TextureView、SurfaceTexture、GLSurfaceView。
OpenGL ES。
OpenCV、DLIB。
YUV、PCM、H.264、H.265、ACC。
AudioRecord、AudioTrack。
MediaRecorder。
MediaCodec。
MediaExtractor、MediaMuxer。
ffmpeg、ijkplayer。
RTMP、RTSP。