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

視頻中磨皮、美顏功能已成爲剛需,那麼如何在Android短視頻中實現720P磨皮美顏錄製?本篇文章中, 網易雲信資深開發工程師將向你們介紹具體的操做方法。

相關閱讀推薦

短視頻技術詳解:Android端的短視頻開發技術算法

如何快速實現移動端短視頻功能?緩存

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

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

視頻的編碼錄製主要是軟編和硬編兩種方案:

軟編即採用CPU對相機採集的原始數據進行編碼後再和音頻一塊兒合併成一個MP4等格式的文件。性能

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

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

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


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

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

缺點是某些奇葩機型須要處理兼容性問題,同時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磨皮美顏錄製的分享。

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

相關文章
相關標籤/搜索