編輯:關於Android編程
導語 本文主要是圍繞android直播助手的功能做了一些研究,因為之前對Android多媒體相關的內容知之甚少,只有概念,於是查閱了相關資料並做以總結。
由於我對音視頻相關知識零基礎所以補充了一些相關知識
采集音頻原始數據---->壓縮編碼---->封裝
采集視頻原始數據---->壓縮編碼---->封裝
壓縮編碼就是對數據進行壓縮以節省空間便於存儲和傳輸。
視頻壓縮編碼就是將視頻幀的像素數據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作為數據的載體,具體的有兩種:使用InputSurface和使用OutputSurface。
以使用InputSurface為例,使用createInputSurface()方法創建一個InputSurface。
RequestsaSurfacetouseastheinputtoanencoder,inplaceofinputbuffers.表明使用這個Surface來代替Buffer作為編碼器的輸入。
編碼器將會自動的從InputSurface讀取幀數據送往編碼器。此時訪問不到輸入Buffer緩沖區,使用getInputBuffers等方法拋出異常。編碼流結束時調用signalEndOfInputStream函數,該函數調用後,Surface就會停止向編碼器提供數據流。
顯然,這種方式在不需要獲取音視頻流的原始數據時是非常方便的。
封裝一般指的是將進行過壓縮編碼的音頻流和視頻流進行合並,封裝格式種類很多,例如MP4,MKV等等,它的作用就是將壓縮編碼的視頻和音頻按照一定的格式封裝在一起。例如,將H.264編碼的視頻碼流和AAC編碼的音頻碼流合並成MP4格式的數據,
Android也提供了MediaMuxer支持將編碼後的音視頻碼流合並成MP4格式的文件。
使用MediaMuxer的關鍵代碼如下
通過MediaProjectionManager進行錄制屏幕。關鍵代碼如下
在onActivityResult中判斷是否獲得錄屏權限。然後執行下面操作。
createVirtualDisplay:CreatesaVirtualDisplaytocapturethecontentsofthescreen
這個參數表示了錄制的手機屏幕的內容要顯示到哪個SurfaceView上,實際上表示了屏幕幀數據的走向,這個參數非常關鍵。
ImageReader:TheImageReaderclassallowsdirectapplicationaccesstoimagedatarenderedintoaSurface
ImageReader類允許應用直接訪問渲染到Surface上的image數據。使用MediaProjectionManager錄制的屏幕內容可以直接渲染到一個Surface上,這個參數在createVirtualDisplay時傳入,但是我們無法訪問到渲染的內容。所以ImageReader類主要是用於使用Surface時訪問不到原始視頻數據流的情形。要想訪問到每一幀的內容可以使用ImageReader類。
該類有一個函數getSurface獲取一個Surface,通過這個函數獲取一個Surface用來為ImageReader產生Images即視頻流幀數據。
將這個Surface指定到錄制屏幕時的Surface參數,就可以通過ImageReader讀取到Surface上面渲染的每一幀的數據,通過acquireLatestImage()等方法可以獲取一個Image對象。
Image:AsinglecompleteimagebuffertousewithamediasourcesuchasaMediaCodecoraCameraDevice.
也就是說Image類表示一個圖片緩沖區,用來和MediaCodec一起使用。通過Image對象可以得到這一幀畫面的像素數據,應該是RGB數據,轉化為YUV格式的數據house,然後通過MediaCodec對其進行編碼。
有了上面的准備知識,再來看直播助手的兩個主要功能本地錄屏和推流功能的實現邏輯容易多了。
本地錄屏的邏輯:本地錄屏不需要操作視頻原始數據,因此使用InputSurface作為編碼器的輸入。
視頻: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的過程中,合理使用動畫能夠提高用戶體驗,帶給用戶耳目一新的感覺。因此我們應該掌握android的動畫使用。我在開發的過程中,很少自己寫動畫,在gi
iPhone用戶從來不用在意剩余內存的多少,也無需考慮太多的系統安全性問題,因為封閉的iOS從“根兒”上就比開放的Android靠譜
示意代碼: /** * 調用系統的分享功能 * Created by admin on 15-4-13. */public class ShareActivit
相信大家都見到了微信圖標顏色漸變的過程,是不是感覺很牛逼?不得不說微信團隊確實是很厲害的團隊,不管是從設計還是開發人員。今天我帶大家來看看,微信 tab 欄圖標和字體顏色