1. 程式人生 > >Android5.0錄屏方案

Android5.0錄屏方案

導語 本文主要是圍繞android直播助手的功能做了一些研究,因為之前對Android多媒體相關的內容知之甚少,只有概念,於是查閱了相關資料並做以總結。

由於我對音視訊相關知識零基礎所以補充了一些相關知識

視音訊編解碼技術零基礎學習方法

視音訊資料處理入門:RGB、YUV畫素資料處理

採集音訊原始資料---->壓縮編碼----> 封裝

採集視訊原始資料---->壓縮編碼----> 封裝

音視訊編碼

壓縮編碼就是對資料進行壓縮以節省空間便於儲存和傳輸。

視訊壓縮編碼就是將視訊幀的畫素資料RGB或YUV等壓縮成視訊碼流,編碼一般對YUV格式進行,視訊編碼方案H.264,MPEG2,MPEG4等。

音訊壓縮編碼就是將取樣的音訊取樣資料PCM等壓縮成音訊碼流。音訊編碼方案:AAC,WMA,MP3等。

編碼是音視訊技術中最重要的技術之一,也是難點,所幸的是,Android提供了MediaCodec用來方便開發者進行視音訊的編解碼,並且對於某種編碼器還可以指定幀格式,儘管如此,我們也不能指定任意格式,因為這需要硬體的支援,可以通過API查詢支援的幀格式。

通過createEncoderByType方法只需要傳入mime即可建立對應的解碼器,mime可以時下面的形式。


MediaCodec類的使用邏輯大致如下圖所示,簡單來說,一個編碼器用於處理輸入資料並對其進行編碼處理後輸出,它內部有一系列的輸入和輸出緩衝區,使用時,從編碼器申請空的輸入緩衝區,然後填充資料後傳送給編碼器處理,編碼完成後編碼器會將編碼後的資料放入輸出緩衝區,只需要從輸出緩衝區取出編碼後的資料後用於處理,最後將空間返還給編碼器。


使用MediaCodec可以有三種方式

1. 使用Buffer陣列的同步方式(API20以後deprecated)

2. 使用Buffer同步方式

3.非同步方式

前兩種方式基本類似,只是使用Buffer的同步方式效能更好。以第一種方式為例:


當要中指編碼時只需要在最後一個有效資料Buffer中或者額外發送一個空的Buffer指定其Flag標誌位為BUFFER_FLAG_END_OF_STREAM,然後呼叫queueInputBuffer傳送給編碼器即可。

除了直接使用ByteBuffer作為MediaCodec的輸入輸出還可以通過Surface作為資料的載體,具體的有兩種:使用Input Surface和使用Output Surface。

以使用Input Surface為例,使用createInputSurface()方法建立一個Input Surface。

Requests a Surface to use as the input to an encoder, in place of input buffers.表明使用這個Surface來代替Buffer作為編碼器的輸入。

編碼器將會自動的從Input Surface讀取幀資料送往編碼器。此時訪問不到輸入Buffer緩衝區,使用getInputBuffers等方法丟擲異常。編碼流結束時呼叫signalEndOfInputStream函式,該函式呼叫後,Surface就會停止向編碼器提供資料流。

顯然,這種方式在不需要獲取音視訊流的原始資料時是非常方便的。

音視訊混合

封裝一般指的是將進行過壓縮編碼的音訊流和視訊流進行合併,封裝格式種類很多,例如MP4,MKV等等,它的作用就是將壓縮編碼的視訊和音訊按照一定的格式封裝在一起。例如,將H.264編碼的視訊碼流和AAC編碼的音訊碼流合併成MP4格式的資料,

Android也提供了MediaMuxer支援將編碼後的音視訊碼流合併成MP4格式的檔案。


使用MediaMuxer的關鍵程式碼如下


Android5.0錄製視訊的方案

通過MediaProjectionManager進行錄製螢幕。關鍵程式碼如下


在onActivityResult中判斷是否獲得錄屏許可權。然後執行下面操作。


createVirtualDisplay:Creates a VirtualDisplay to capture the contents of the screen

這個引數表示了錄製的手機螢幕的內容要顯示到哪個SurfaceView上,實際上表示了螢幕幀資料的走向,這個引數非常關鍵。

相關的幾個類

ImageReader :The ImageReader class allows direct application access to image data rendered into a Surface

ImageReader類允許應用直接訪問渲染到Surface上的image資料。使用MediaProjectionManager錄製的螢幕內容可以直接渲染到一個Surface上,這個引數在createVirtualDisplay時傳入,但是我們無法訪問到渲染的內容。所以ImageReader類主要是用於使用Surface時訪問不到原始視訊資料流的情形。要想訪問到每一幀的內容可以使用ImageReader類。

該類有一個函式getSurface獲取一個Surface,通過這個函式獲取一個Surface用來為ImageReader產生Images即視訊流幀資料。


將這個Surface指定到錄製螢幕時的Surface引數,就可以通過ImageReader讀取到Surface上面渲染的每一幀的資料,通過acquireLatestImage()等方法可以獲取一個Image物件。

Image:A single complete image buffer to use with a media source such as a MediaCodec or a CameraDevice.

也就是說Image類表示一個圖片緩衝區,用來和MediaCodec一起使用。通過Image物件可以得到這一幀畫面的畫素資料,應該是RGB資料,轉化為YUV格式的資料house,然後通過MediaCodec對其進行編碼。

本地錄屏方案

有了上面的準備知識,再來看直播助手的兩個主要功能本地錄屏和推流功能的實現邏輯容易多了。

本地錄屏的邏輯:本地錄屏不需要操作視訊原始資料,因此使用Input Surface作為編碼器的輸入。

視訊:MediaProjection通過createVirtualDisplay建立的VirtualDisplay傳入的Surface是通過MediaCodec的createInputSurface方法返回的,表明編碼器的輸入其實來自於錄製到的螢幕資料,於是只需要在MediaCodec的輸出緩衝區中拿到編碼後的ByteBuffer即可。

音訊:錄製程式獲得音訊原始資料PCM,傳給MediaCodec編碼,然後從MediaCodec的輸出緩衝區拿到編碼後的ByteBuffer即可。

最終通過合併模組將音視訊混合。

推流的邏輯:推流SDK提供了一個編碼器TVLiveSdk_LiveEncoder,它接受YUV420的視訊資料格式和PCM編碼的原始音訊流。因此要獲得視訊的原始幀資料才行,可以通過ImageReader實現該功能。

視訊:MediaProjection通過createVirtualDisplay建立的VirtualDisplay傳入的Surface是通過ImageReader的getSurface方法返回的,表明錄製的螢幕幀資料傳遞到ImageReader,於是通過ImageReader的相關API可以讀取到錄製的螢幕每一幀的資料,但這個資料時RGB格式的,轉化為YUV格式後傳到推流SDK即可。

音訊:由於推流SDK需要的就是原始PCM編碼的音訊資料,因此錄製到音訊資料後直接呼叫推流SDK即可。

簡單說就是重定向了螢幕錄製的資料的方向,這個Surface提供的是什麼,錄製的視訊資料就傳到哪裡。Surface提供的是本地某個SurfaceView控制元件,那麼就會將螢幕內容顯示到這個控制元件上,提供MediaCodec就是作為編碼器的輸入源最終獲得編碼後的資料,提供ImageReader就會作為ImageReader的資料來源,最終獲得了視訊的原始資料流。

Android直播助手中錄屏和推流具體框架設計

...

檢視原文:http://qhyuang1992.com/index.php/2016/08/08/android5_0_lu_ping_fang_an/