網易MC朱金波:Android短視頻中如何實現720P磨皮美顏錄製
在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可以獲取到Camer採集到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了。
最後大體的流程圖就是:
網易雲短視頻SDK按照上述方案實現後的效果就是:720P、30幀,磨皮、美顏濾鏡全開的情況下,市面上的絕大部分手機都可以達到這個效果,連一些Android 4.3系統的老爺機也可以,而且CPU佔用才4~5%,錄製一小時也感覺不到手機發燙。
以上由網易企業信息化服務提供商,湖南領先網路科技整理髮布。
網易企業服務(qiye163.co)是網易憑藉其20年品牌優勢與經驗在企業郵箱的基礎上,為進一步布局企業市場而打造的企業級產品矩陣,致力於提供一站式企業信息化解決方案。湖南領先網路科技是網易企業產品授權經銷商,專業為企業提供網易企業郵箱、網易辦公套件等一站式企業信息化專業解決方案。辦理網易企業郵箱及旗下企業產品相關業務,就找湖南領先網路科技。
※iOS 11北京時間明日凌晨更新:哪些設備能升級
※英特爾Stacy Smith:摩爾定律不會失效 我們領先友商3年
※網易MC:Windows中各類畫面源的截取和合成方法總結
※想成為下一個BAT?請先做好這一件事情
※網易雲攜手 Intel 打造遊戲雲服務新生態
TAG:網易企服 |