文章插圖

文章插圖
一,MediaCodec工作原理
MediaCodec類Android提供的用于訪問低層多媒體編/解碼器接口,它是Android低層多媒體架構的一部分,通常與MediaExtractor、MediaMuxer、AudioTrack結合使用,能夠編解碼諸如H.264、H.265、AAC、3gp等常見的音視頻格式 。
廣義而言,MediaCodec的工作原理就是處理輸入數據以產生輸出數據 。具體來說,MediaCodec在編解碼的過程中使用了一組輸入/輸出緩存區來同步或異步處理數據:首先,客戶端向獲取到的編解碼器輸入緩存區寫入要編解碼的數據并將其提交給編解碼器,待編解碼器處理完畢后將其轉存到編碼器的輸出緩存區,同時收回客戶端對輸入緩存區的所有權;然后,客戶端從獲取到編解碼輸出緩存區讀取編碼好的數據進行處理,待處理完畢后編解碼器收回客戶端對輸出緩存區的所有權 。不斷重復整個過程,直至編碼器停止工作或者異常退出 。
mediacodec的作用是處理輸入的數據生成輸出數據 。首先生成一個輸入數據緩沖區,將數據填入緩沖區提供給codec,codec會采用異步的方式處理這些輸入的數據,然后將填滿輸出緩沖區提供給消費者,消費者消費完后將緩沖區返還給codec 。
【音頻解碼器起什么作用 什么叫音頻解碼器】二,MediaCodec編碼過程
在整個編解碼過程中,MediaCodec的使用會經歷配置、啟動、數據處理、停止、釋放幾個過程,相應的狀態可歸納為停止(Stopped),執行(Executing)以及釋放(Released)三個狀態,而Stopped狀態又可細分為未初始化(Uninitialized)、配置(Configured)、異常( Error),Executing狀態也可細分為讀寫數據(Flushed)、運行(Running)和流結束(End-of-Stream) 。
MediaCodec整個狀態結構圖如下:
三,MediaCodec API 說明
MediaCodec可以處理具體的視頻流,主要有這幾個方法:
getInputBuffers:獲取需要編碼數據的輸入流隊列,返回的是一個ByteBuffer數組queueInputBuffer:輸入流入隊列dequeueInputBuffer:從輸入流隊列中取數據進行編碼操作getOutputBuffers:獲取編解碼之后的數據輸出流隊列,返回的是一個ByteBuffer數組dequeueOutputBuffer:從輸出隊列中取出編碼操作之后的數據releaseOutputBuffer:處理完成,釋放ByteBuffer數據四,MediaCodec基本使用
所有的同步模式的 MediaCodec API都遵循一個模式:
創建并配置一個 MediaCodec 對象循環直到完成:如果輸入緩沖區就緒,讀取一個輸入塊,并復制到輸入緩沖區中如果輸出緩沖區就緒,復制輸出緩沖區的數據釋放 MediaCodec 對象
(1) 創建編/解碼器
MediaCodec主要提供了createEncoderByType(String type)、createDecoderByType(String type)兩個方法來創建編解碼器,它們均需要傳入一個MIME類型多媒體格式 。常見的MIME類型多媒體格式如下:● “video/x-vnd.on2.vp8” – VP8 video (i.e. video in .webm)● “video/x-vnd.on2.vp9” – VP9 video (i.e. video in .webm)● “video/avc” – H.264/AVC video● “video/mp4v-es” – MPEG4 video● “video/3gpp” – H.263 video● “audio/3gpp” – AMR narrowband audio● “audio/amr-wb” – AMR wideband audio● “audio/mpeg” – MPEG1/2 audio layer III● “audio/mp4a-latm” – AAC audio (note, this is raw AAC packets, not packaged in LATM!)● “audio/vorbis” – vorbis audio● “audio/g711-alaw” – G.711 alaw audio● “audio/g711-mlaw” – G.711 ulaw audio當然,MediaCodec還提供了一個createByCodecName (String name)方法,支持使用組件的具體名稱來創建編解碼器 。但是該方法使用起來有些麻煩,且官方是建議最好是配合MediaCodecList使用,因為MediaCodecList記錄了所有可用的編解碼器 。當然,我們也可以使用該類對傳入的minmeType參數進行判斷,以匹配出MediaCodec對該mineType類型的編解碼器是否支持 。
以指定MIME類型為“video/avc”為例,代碼如下:
private static MediaCodecInfo selectCodec(String mimeType) {// 獲取所有支持編解碼器數量int numCodecs = MediaCodecList.getCodecCount();for (int i = 0; i < numCodecs; i++) {// 編解碼器相關性信息存儲在MediaCodecInfo中MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);// 判斷是否為編碼器if (!codecInfo.isEncoder()) {continue;}// 獲取編碼器支持的MIME類型,并進行匹配String[] types = codecInfo.getSupportedTypes();for (int j = 0; j < types.length; j++) {if (types[j].equalsIgnoreCase(mimeType)) {return codecInfo;}}}return null; }(2) 配置、啟動編/解碼器編解碼器配置使用的是MediaCodec的configure方法,該方法首先對MediaFormat存儲的數據map進行提取,然后調用本地方法native-configure實現對編解碼器的配置工作 。在配置時,configure方法需要傳入format、surface、crypto、flags參數,其中format為MediaFormat的實例,它使用”key-value”鍵值對的形式存儲多媒體數據格式信息;surface用于指明解碼器的數據源來自于該surface;crypto用于指定一個MediaCrypto對象,以便對媒體數據進行安全解密;flags指明配置的是編碼器(CONFIGURE_FLAG_ENCODE) 。
MediaFormat mFormat = MediaFormat.createVideoFormat("video/avc", 640 ,480);// 創建MediaFormatmFormat.setInteger(MediaFormat.KEY_BIT_RATE,600);// 指定比特率mFormat.setInteger(MediaFormat.KEY_FRAME_RATE,30);// 指定幀率mFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,mColorFormat);// 指定編碼器顏色格式mFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL,10); // 指定關鍵幀時間間隔mVideoEncodec.configure(mFormat,null,null,MediaCodec.CONFIGURE_FLAG_ENCODE); 以上代碼是在編碼H.264時的配置方法,createVideoFormat(“video/avc”, 640 ,480)為”video/avc”類型(即H.264)編碼器的MediaFormat對象,需要指定視頻數據的寬高,如果編解碼音頻數據,則調用MediaFormat的createAudioFormat(String mime, int sampleRate,int channelCount)的方法 。除了一些諸如視頻幀率、音頻采樣率等配置參數,這里需要著重講解一下MediaFormat.KEY_COLOR_FORMAT配置屬性,該屬性用于指明video編碼器的顏色格式,具體選擇哪種顏色格式與輸入的視頻數據源顏色格式有關 。比如,我們都知道Camera預覽采集的圖像流通常為NV21或YV12,那么編碼器需要指定相應的顏色格式,否則編碼得到的數據可能會出現花屏、疊影、顏色失真等現象 。MediaCodecInfo.CodecCapabilities.存儲了編碼器所有支持的顏色格式,常見顏色格式映射如下:原始數據 編碼器 NV12(YUV420sp) ———> COLOR_FormatYUV420PackedSemiPlanar NV21 ———-> COLOR_FormatYUV420SemiPlanar YV12(I420) ———-> COLOR_FormatYUV420Planar 當編解碼器配置完畢后,就可以調用MediaCodec的start()方法,該方法會調用低層native_start()方法來啟動編碼器,并調用低層方法ByteBuffer[] getBuffers(input)來開辟一系列輸入、輸出緩存區 。start()方法源碼如下:
public final void start() {native_start();synchronized(mBufferLock) {cacheBuffers(true /* input */);cacheBuffers(false /* input */);} }(3) 數據處理MediaCodec支持兩種模式編解碼器,即同步synchronous、異步asynchronous,所謂同步模式是指編解碼器數據的輸入和輸出是同步的,編解碼器只有處理輸出完畢才會再次接收輸入數據;而異步編解碼器數據的輸入和輸出是異步的,編解碼器不會等待輸出數據處理完畢才再次接收輸入數據 。這里,我們主要介紹下同步編解碼,因為這種方式我們用得比較多 。我們知道當編解碼器被啟動后,每個編解碼器都會擁有一組輸入和輸出緩存區,但是這些緩存區暫時無法被使用,只有通過MediaCodec的dequeueInputBuffer/dequeueOutputBuffer方法獲取輸入輸出緩存區授權,通過返回的ID來操作這些緩存區 。下面我們通過一段官方提供的代碼,進行擴展分析:
MediaCodec codec = MediaCodec.createByCodecName(name); codec.configure(format, …); MediaFormat outputFormat = codec.getOutputFormat(); // option B codec.start(); for (;;) {int inputBufferId = codec.dequeueInputBuffer(timeoutUs);if (inputBufferId >= 0) {ByteBuffer inputBuffer = codec.getInputBuffer(…);// fill inputBuffer with valid data…codec.queueInputBuffer(inputBufferId, …);}int outputBufferId = codec.dequeueOutputBuffer(…);if (outputBufferId >= 0) {ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A// bufferFormat is identical to outputFormat// outputBuffer is ready to be processed or rendered.…codec.releaseOutputBuffer(outputBufferId, …);} else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {// Subsequent data will conform to new format.// Can ignore if using getOutputFormat(outputBufferId)outputFormat = codec.getOutputFormat(); // option B} } codec.stop(); codec.release();從上面代碼可知,當編解碼器start后,會進入一個for(;;)循環,該循環是一個死循環,以實現不斷地去從編解碼器的輸入緩存池中獲取包含數據的一個緩存區,然后再從輸出緩存池中獲取編解碼好的輸出數據 。- 最能引起男人的保護欲望 讓男生有保護欲的女孩
- gta5開飛機鍵盤操作沒有小鍵盤 gta5開飛機鍵盤操作起落架
- 華為手機音頻設置在哪里 vivo手機單聲道音頻設置在哪里
- 女演員高葉個人簡歷 高葉的老公是誰
- 千金瓜 佛手不能和什么一起吃
- 高原公路 219國道有多可怕
- 蝦和茶水能一起吃嗎
- 輕易激起男生保護欲 保護欲是怎么產生的
- 成化十四年汪直喜歡誰,汪直結局和誰在一起了
- excel顏色填充快捷鍵是什么 excel顏色填充快捷鍵F4怎么用不起了
