Android短視頻中如何實現720P磨皮美顏錄製?

視頻中磨皮、美顏功能已成爲剛需,那麼如何在Android短視頻中實現720P磨皮美顏錄製?本篇文章中,網易雲信資深開發工程師將向大家介紹具體的操作方法。
相關閱讀推薦
《短視頻技術詳解:Android端的短視頻開發技術》

《如何快速實現移動端短視頻功能?》

在Android上要實現一個錄製功能,需要有幾個方面的知識儲備:自定義相機的開發、視頻數據格式的瞭解、編碼相關知識以及視頻合成技術,同時如果需要美顏、磨皮等濾鏡操作還需要一定的openGL的知識。如果有需要深入音視頻方面開發的同學建議先了解下上述的基本知識點。

既然要實現720P、30幀,同時又要對視頻數據進行濾鏡處理的錄製,那我們首先就要確定一個正確的實現方案。如果方案選錯了,那即使實現了錄製功能,但性能達不到30幀或是CPU消耗太大手機發燙那就不好了。

視頻的編碼錄製主要是軟編和硬編兩種方案:
軟編即採用CPU對相機採集的原始數據進行編碼後再和音頻一起合併成一個MP4等格式的文件。

優點是技術相對成熟,網上開源的編碼以及合成庫很多,實現相對較快,同時兼容性比較好。

缺點是CPU暫用率高,性能差的手機無法達到720P的30幀,同時引用了大量的第三方庫,導致包很大。

軟編的具體實現方案如下圖所示,流程相對清晰簡單:

硬編即採用手機提供的硬編接口,利用硬件芯片直接進行編碼合成。

優點是速度快、效率高、CPU佔用極少,即使長時間高清錄製也不會發燙,同時由於使用系統API,庫相對較小。

缺點是某些奇葩機型需要處理兼容性問題,同時Android上的硬編跟Surface以及openGL關係比較密切,網上相關知識較少,需要自己摸索踩坑。

硬編的主要流程如下圖所示,可以看到所有的數據,從採集、編碼、顯示以及合成都在GPU裏面進行流轉。

結合上面分析的兩種方案我們可以看到,在Android這類移動平臺上,使用第二種硬編的方式是比較合適的。由於短視頻的本地錄製不像直播等場景對帶寬的要求比較大,需要動態調節編碼器碼率幀率的情況,本地錄製可以將編碼器的碼率設置的比較高,也不需要動態改變分辨率。因此採用硬件編碼的方式既可以省CPU的性能又可以實現720P的高效編碼。

確定了方案之後,我們就着重講一下硬編方案的各個步驟的實現方式。

自定義相機的開發
我們知道根據Android的系統Camera API,可以通過setPreviewDisplay接口給Camera設置一個SurfaceView的SurfaceHolder就可以讓Camera採集的數據顯示到SurfaceView上了。這個接口的好處是系統幫我們處理了相機採集的各種角度同時進行了繪製,如果只是簡單的錄製可以這麼使用,但我們需要對相機採集的數據進行濾鏡處理,那這個接口就不合適了。

因此我們需要用到另外一個接口 setPreviewTexture:

通過給Camera設置一個SurfaceTexture,可以將Camera採集的數據先映射到這個SurfaceTexture上,然後我們根據創建這個SurfaceTexture的TextureID來獲取GPU上的Camera數據

濾鏡以及本地繪製
我們通過SurfaceTexture綁定的TextureID可以獲取到Camera採集到GPU上的視頻數據。然後可以將TextureID送給一些第三方濾鏡庫進行美顏濾鏡或是自己編寫Shader進行磨皮和美白。自己編寫Shader需要opengl以及圖像算法方面的知識,通常需要專門的開發人員,這裏就不做詳細的展開了(當然最簡單的就是接入網易雲短視頻SDK了,裏面實現了磨皮、美顏和多款濾鏡)。

本地繪製主要靠openGL進行繪製,我們需要先在Camera的採集回調線程上創建一個EGLContext以及EGLDisplay和EGLSurface, 其中EGLContext是openGL在該線程上的上下文,EGLDisplay是一塊GPU中的虛擬顯示區,主要用於緩存GPU上的視頻數據,EGLSurface爲具體顯示的View到openGL上的映射,是真正繪製到View上的工具。當接受到Camera採集回調的一幀數據後,我們先通過SurfaceTexture.updateTexImage()方法,將Camera採集的數據映射到SurfaceTexture。然後根據glsl語言將TextureID對應的數據繪製到EGLDisplay上,這裏需要注意的是,Camera採集是有角度的,橫豎屏下角度不同,可以通過SurfaceTexture的getTransformMatrix方法獲取角度矩陣,然後把矩陣傳給EGLDisplay進行旋轉。EGLDisplay旋轉繪製完成後通過eglSwapBuffers方法就可以將EGLDisplay上的數據拷貝到EGLSurface上進行顯示了。Android 系統中的GLSurfaceView最後就是通過eglSwapBuffers將數據顯示到我們看到的屏幕上的。

硬件編碼
Android上的硬件編碼主要靠MediaCodeC API實現的,下面是MediaCodeC比較經典的一張數據處理圖。

從圖中我們看到,MediaCodeC主要處理流程就是:

創建並配置一個 MediaCodec 對象
循環直到完成:

    如果輸入緩衝區就緒,讀取一個輸入塊,並複製到輸入緩衝區中
    如果輸出緩衝區就緒,複製輸出緩衝區的數據

釋放 MediaCodec 對象

從Android的官方文檔我們看到,MediaCodeC支持ByteBuffers和Surface兩種輸入方式,文檔也指明瞭Surface方式可以提高編碼效率,而且我們上面的Camera數據也是採集到的SurfaceTexture,因此我們這裏使用Surface方式作爲輸入源。

我們在上面顯示部分提到EGLSurface是作爲真正輸出顯示的模塊,MediaCodec也是。我們先通過MediaCodec創建一個Surface,然後將這個Surface綁定到一個EGLSurface,當Camera採集的數據回調時,我們只要重複一次繪製模塊的操作,將Camera採集到SurfaceTexture上的數據swapBuffers到EGLSurface 上就可以了。然後循環MediaCodec輸出緩衝區,MediaCodec就會將編碼後的數據返回給我們了。這樣做的好處就是將顯示和編碼完全分離了,即使我們沒有UI View的情況下也可以進行編碼,比如在不同Activity之間切換也不會影響我們的正常編碼。

視頻合成
Android上視頻合成主要通過MediaMuxer API實現。MediaMuxer類相對比較簡單,特別是配合MediaCodec使用。 我們只需要通過 addTrack 來添加視頻和音頻通道接口。AddTrack 接口需要傳入一個MediaFormat對象,MediaFormat即媒體格式類,用於描述媒體的格式參數,如視頻幀率、音頻採樣率等。還好我們使用了MediaCodeC,MediaCodeC會返回MediaFormat給我們,如果是使用軟編然後用MediaMuxer進行合併的話,這裏有一個比較大的坑,如果手動創建MediaFormat對象的話,一定要記得設置"csd-0"和"csd-1"這兩個參數:其中"csd-0"和"csd-1"對應的是視頻的sps和pps,對於AAC音頻的話,對應的是ADTS。不設置的話會崩潰的。 設置完這些之後,只要編碼器來一幀數據,我們送到MediaMuxer中可以出來MP4了。

最後大體的流程圖就是:

以上就是在Android短視頻中實現720P磨皮美顏錄製的分享。

另外,想要獲取更多產品乾貨、技術乾貨,記得關注網易雲信博客。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章