編輯:關於android開發
*由於工作需要,需要利用MediaCodec實現Playback及Transcode等功能,故在學習過程中翻譯了Google官方的MediaCodec API文檔,由於作者水平限制,文中難免有錯誤和不恰當之處,望批評指正。
*轉載請注明出處:http://www.cnblogs.com/roger-yu/
public final class MediaCodec extends Object
Java.lang.Object
→ android.media.MediaCodec
MediaCodec類可用於訪問Android底層的媒體編解碼器,也就是,編碼器/解碼器組件。它是Android底層多媒體支持基本架構的一部分(通常與MediaExtractor, MediaSync, MediaMuxer, MediaCrypto, MediaDrm, Image, Surface, 以及AudioTrack一起使用)。
從廣義上講,一個編解碼器通過處理輸入數據來產生輸出數據。MediaCode采用異步方式處理數據,並且使用了一組輸入輸出緩存(buffer)。在簡單的層面,你請求或接收到一個空的輸入緩存(buffer),向其中填充滿數據並將它傳遞給編解碼器處理。編解碼器處理完這些數據並將處理結果輸出至一個空的輸出緩存(buffer)中。最終,你請求或接收到一個填充了數據的輸出緩存(buffer),使用完其中的數據,並將其釋放回編解碼器。
編解碼器處理三種類型的數據:壓縮數據、原始音頻數據、原始視頻數據。三種類型的數據均可以利用ByteBuffers進行處理,但是對於原始視頻數據應提供一個Surface以提高編解碼器的性能。Surface直接使用本地視頻數據緩存,而沒有映射或復制它們到ByteBuffers;因此,這種方式會更加高效。在使用Surface的時候,通常不能直接訪問原始視頻數據,但是可以使用ImageReader類來訪問不可靠的解碼後(或原始)的視頻幀。這可能仍然比使用ByteBuffers更加高效,因為一些本地緩存可以被映射到 direct ByteBuffers。當使用ByteBuffer模式,你可以利用Image類和getInput/OutputImage(int)方法來訪問到原始視頻數據幀。
輸入緩存-buffers(用於解碼器)和輸出緩存-buffers(用於編碼器)中包含由媒體格式類型決定的壓縮數據。對於視頻類型是單個壓縮的視頻幀。對於音頻數據通常是單個可訪問單元(一個編碼的音頻片段,通常包含幾毫秒的遵循特定格式類型的音頻數據),但這種要求也不是十分嚴格,一個緩存-buffer可能包含多個可訪問的音頻單元。在這兩種情況下,緩存不會在任意的字節邊界上開始或結束,而是在幀或可訪問單元的邊界上開始或結束。
原始的音頻數據緩存(buffers)包含整個PCM(脈沖編碼調制)音頻數據幀,這是每一個通道按照通道順序的一個樣本。每一個樣本是一個按照本機字節順序的16位帶符號整數(16-bit signed integer in native byte order)。
1 short[] getSamplesForChannel(MediaCodec codec, int bufferId, int channelIx) { 2 ByteBuffer outputBuffer = codec.getOutputBuffer(bufferId); 3 MediaFormat format = codec.getOutputFormat(bufferId); 4 ShortBuffer samples = outputBuffer.order(ByteOrder.nativeOrder()).asShortBuffer(); 5 int numChannels = formet.getInteger(MediaFormat.KEY_CHANNEL_COUNT); 6 if (channelIx < 0 || channelIx >= numChannels) { 7 return null; 8 } 9 short[] res = new short[samples.remaining() / numChannels]; 10 for (int i = 0; i < res.length; ++i) { 11 res[i] = samples.get(i * numChannels + channelIx); 12 } 13 return res; 14 }
在ByteBuffer模式下,視頻緩存-buffers根據它們的顏色格式(color format)進行展現。你可以通過調用getCodecInfo().getCapabilitiesForType(…).colorFormats方法獲得其支持的顏色格式數組。視頻編解碼器可以支持三種類型的顏色格式:
從Android 5.1(LOLLIPOP_MR1)開始,所有的視頻編解碼器都支持靈活的YUV4:2:0緩存(flexible YUV 4:2:0 buffers)。
在編解碼器的生命周期內有三種理論狀態:停止態-Stopped、執行態-Executing、釋放態-Released,停止狀態(Stopped)包括了三種子狀態:未初始化(Uninitialized)、配置(Configured)、錯誤(Error)。執行狀態(Executing)在概念上會經歷三種子狀態:刷新(Flushed)、運行(Running)、流結束(End-of-Stream)。
使用MediaCodecList創建一個指定MediaFormat的MediaCodec實例。在解碼文件或流時,你可以通過調用MediaExtractor.getTrackFormat方法獲得所期望的格式。並調用MediaFormat.setFeatureEnabled方法注入任意你想要添加的特定特性,然後調用MediaCodecList.findDecoderForFormat方法獲得可以處理指定的媒體格式的編解碼器的名字。最後,通過調用createByCodecName(String)方法創建一個編解碼器。
注意:在Android 5.0 (LOLLIPOP)上,傳遞給MediaCodecList.findDecoder/EncoderForFormat的格式不能包含幀率-frame rate。通過調用format.setString(MediaFormat.KEY_FRAME_RATE, null)方法清除任何存在於當前格式中的幀率。
你也可以利用createDecoder/EncoderByType(String)方法創建一個指定MIME類型的首選編解碼器。然而,這種方式不能夠給編解碼器加入指定特性,而且創建的編解碼器有可能不能處理具體期望的媒體格式。
創建安全的解碼器(Creating secure decoders)
在Android 4.4(KITKAT_WATCH)及之前版本,安全的編解碼器沒有被列在MediaCodecList中,但是仍然可以在系統中使用。存在的安全編解碼器只能夠通過名字進行實例化,其名字是在常規編解碼器的名字後附加.secure標識(所有安全編解碼器的名字都必須以.secure結尾),調用createByCodecName(String)方法創建安全編解碼器時,如果系統中不存在指定名字的編解碼器就會拋出IOException異常。
從Android 5.0(LOLLIPOP)及之後版本,你可以在媒體格式中使用FEATURE_SecurePlayback屬性來創建一個安全編解碼器。
在創建了編解碼器後,如果你想異步地處理數據,可以通過調用setCallback方法設置一個回調方法。然後,使用指定的媒體格式配置編解碼器。這段時間你可以為視頻原始數據產生者(例如視頻解碼器)指定輸出Surface。此時你也可以為secure 編解碼器設置解碼參數(詳見MediaCrypto) 。最後,因為有些編解碼器可以操作於多種模式,你必須指定是想讓他作為一個解碼器或編碼器運行。
從API LOLLIPOP起,你可以在Configured 狀態查詢輸入和輸出格式的結果。在開始編解碼前你可以通過這個結果來驗證配置的結果,例如,顏色格式。
如果你想通過視頻處理者處理原始輸入視頻buffers,一個處理原始視頻輸入的編解碼器,例如視頻編碼器,在配置完成後通過調用createInputSurface()方法為你的輸入數據創建一個目標Surface。通過先前創建的persistent input surface調用setInputSurface(Surface)配置這個編解碼器。
Codec-specific數據
有些格式,特別是ACC音頻和MPEG4、H.264和H.265視頻格式要求實際數據以若干個包含配置數據或編解碼器指定數據的緩存為前綴。當處理這種壓縮格式時,這些數據必須在調用start()方法後及任何幀數據之前提交給編解碼器。這些數據必須在調用queueInputBuffer方法時使用BUFFER_FLAG_CODEC_CONFIG進行標記。
Codec-specific數據也可以被包含在傳遞給configure方法的格式format中,在ByteBuffer條目中以"csd-0", "csd-1"等key標記。這些keys通常包含在通過MediaExtractor獲得的軌道MediaFormat中。一旦調用start()方法格式中的Codec-specific數據自動提交給編解碼器;你不能顯示的提交這些數據。如果這個格式不包含編解碼器指定的數據,你可以根據格式要求,按照正確的順序使用指定數目的緩存來提交數據。還有,你也可以連接所有的codec-specific數據並作為一個單獨的codec-config buffer提交。
Android 使用下列的codec-specific數據buffers。對於適當的MediaMuxer軌道配置,這些也要在軌道格式中進行設置。每一個參數集以及被標記為(*)的codec-specific-data段必須以"\x00\x00\x00\x01"字符開頭。
注意:當編解碼器被立即刷新或start之後不久刷新,並且在任何輸出buffer或輸出格式變化被返回前需要特別地小心,因為編解碼器的codec specific data可能會在flush過程中丟失。為保證編解碼器的正常運行,你必須在刷新後使用標記為BUFFER_FLAG_CODEC_CONFIGbuffers的buffers再次提交這些數據。
編碼器(或者產生壓縮數據的編解碼器)會產生和返回編解碼器指定的數據,它會在以codec-config flag標記的輸出緩存中的有效輸出緩存之前。包含codec-specific-data的Buffers沒有有意義的時間戳。
每一個編解碼器包含一組輸入和輸出緩存,這些緩存在API調用中通過buffer-ID進行引用。當成功調用start()方法後客戶端將不會“擁有”輸入或輸出buffers。在同步模式下,通過調用dequeueInput/OutputBuffer(…) 方法從編解碼器獲得(取得所有權)一個輸入或輸出buffer。在異步模式下,你可以通過MediaCodec.Callback.onInput/OutputBufferAvailable(…)的回調方法自動地獲得可用的buffers。
在獲得一個輸入buffe後,填充數據,利用queueInputBuffer方法將其提交給編解碼器,若使用解密模式,則利用queueSecureInputBuffer方法提交。不要提交多個具有相同時間戳的輸入bufers(除非它是也被同樣標記的codec-specific data)。
在異步模式下通過onOutputBufferAvailable方法的回調或者在同步模式下響應dequeuOutputBuffer的調用,編解碼器返回一個只讀的output buffer。在這個output buffer被處理後,調用一個releaseOutputBuffer方法將這個buffer返回給編解碼器。
當你不需要立即向編解碼器重新提交或釋放buffers時,保持對輸入或輸出buffers的所有權可使編解碼器停止工作,當然這些行為依賴於設備情況。特別地,編解碼器可能延遲產生輸出buffers直到輸出的buffers被釋放或重新提交。因此,盡可能少地保存可用的buffers。
根據API版本情況,你有三種處理相關數據的方式:
從Android 5.0(LOLLIPOP)開始,首選的方法是調用configure之前通過設置回調異步地處理數據。異步模式稍微改變了狀態轉換方式,因為你必須在調用flush()方法後再調用start()方法才能使編解碼器的狀態轉換為Running子狀態並開始接收輸入buffers。同樣,初始調用start方法將編解碼器的狀態直接變化為Running 子狀態並通過回調方法開始傳遞可用的輸入buufers。
異步模式下,編解碼器典型的使用方法如下:
1 MediaCodec codec = MediaCodec.createByCodecName(name); 2 MediaFormat mOutputFormat; // member variable 3 codec.setCallback(new MediaCodec.Callback() { 4 @Override 5 void onInputBufferAvailable(MediaCodec mc, int inputBufferId) { 6 ByteBuffer inputBuffer = codec.getInputBuffer(inputBufferId); 7 // fill inputBuffer with valid data 8 … 9 codec.queueInputBuffer(inputBufferId, …); 10 } 11 12 @Override 13 void onOutputBufferAvailable(MediaCodec mc, int outputBufferId, …) { 14 ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId); 15 MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A 16 // bufferFormat is equivalent to mOutputFormat 17 // outputBuffer is ready to be processed or rendered. 18 … 19 codec.releaseOutputBuffer(outputBufferId, …); 20 } 21 22 @Override 23 void onOutputFormatChanged(MediaCodec mc, MediaFormat format) { 24 // Subsequent data will conform to new format. 25 // Can ignore if using getOutputFormat(outputBufferId) 26 mOutputFormat = format; // option B 27 } 28 29 @Override 30 void onError(…) { 31 … 32 } 33 }); 34 codec.configure(format, …); 35 mOutputFormat = codec.getOutputFormat(); // option B 36 codec.start(); 37 // wait for processing to complete 38 codec.stop(); 39 codec.release();
從Android5.0(LOLLIPOP)開始,即使在同步模式下使用編解碼器你應該通過getInput/OutputBuffer(int) 和/或 getInput/OutputImage(int) 方法檢索輸入和輸出buffers。這允許通過框架進行某些優化,例如,在處理動態內容過程中。如果你調用getInput/OutputBuffers()方法這種優化是不可用的。
注意,不要同時混淆使用緩存和緩存數組的方法。特別地,僅僅在調用start()方法後或取出一個值為INFO_OUTPUT_FORMAT_CHANGED的輸出buffer ID後你才可以直接調用getInput/OutputBuffers方法。
同步模式下MediaCodec的典型應用如下:
1 MediaCodec codec = MediaCodec.createByCodecName(name); 2 codec.configure(format, …); 3 MediaFormat outputFormat = codec.getOutputFormat(); // option B 4 codec.start(); 5 for (;;) { 6 int inputBufferId = codec.dequeueInputBuffer(timeoutUs); 7 if (inputBufferId >= 0) { 8 ByteBuffer inputBuffer = codec.getInputBuffer(…); 9 // fill inputBuffer with valid data 10 … 11 codec.queueInputBuffer(inputBufferId, …); 12 } 13 int outputBufferId = codec.dequeueOutputBuffer(…); 14 if (outputBufferId >= 0) { 15 ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId); 16 MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A 17 // bufferFormat is identical to outputFormat 18 // outputBuffer is ready to be processed or rendered. 19 … 20 codec.releaseOutputBuffer(outputBufferId, …); 21 } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 22 // Subsequent data will conform to new format. 23 // Can ignore if using getOutputFormat(outputBufferId) 24 outputFormat = codec.getOutputFormat(); // option B 25 } 26 } 27 codec.stop(); 28 codec.release();
在Android 4.4(KITKAT_WATCH)及之前版本,一組輸入或輸出buffers使用ByteBuffer[]數組表示。在成功調用了start()方法後,通過調用getInput/OutputBuffers()方法檢索buffer數組。在這些數組中使用buffer的ID-s(非負數)作為索引,如下面的演示示例中,注意數組大小和系統使用的輸入和輸出buffers的數量之間並沒有固定的關系,盡管這個數組提供了上限邊界。
1 MediaCodec codec = MediaCodec.createByCodecName(name); 2 codec.configure(format, …); 3 codec.start(); 4 ByteBuffer[] inputBuffers = codec.getInputBuffers(); 5 ByteBuffer[] outputBuffers = codec.getOutputBuffers(); 6 for (;;) { 7 int inputBufferId = codec.dequeueInputBuffer(…); 8 if (inputBufferId >= 0) { 9 // fill inputBuffers[inputBufferId] with valid data 10 … 11 codec.queueInputBuffer(inputBufferId, …); 12 } 13 int outputBufferId = codec.dequeueOutputBuffer(…); 14 if (outputBufferId >= 0) { 15 // outputBuffers[outputBufferId] is ready to be processed or rendered. 16 … 17 codec.releaseOutputBuffer(outputBufferId, …); 18 } else if (outputBufferId == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 19 outputBuffers = codec.getOutputBuffers(); 20 } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 21 // Subsequent data will conform to new format. 22 MediaFormat format = codec.getOutputFormat(); 23 } 24 } 25 codec.stop(); 26 codec.release();
當到達輸入數據結尾時,你必須在調用queueInputBuffer方法中通過指定BUFFER_FLAG_END_OF_STREAM標記來通知編解碼器。你可以在最後一個有效的輸入buffer上做這些操作,或者提交一個額外的以end-of-stream標記的空的輸入buffer。如果使用一個空的buffer,它的時間戳將被忽略。
編解碼器將會繼續返回輸出buffers,直到它發出輸出流結束的信號,這是通過指定dequeueOutputBuffer方法中MediaCodec.BufferInfo的end-of-stream標記來實現的,或者是通過回調方法onOutputBufferAvailable來返回end-of-stream標記。可以在最後一個有效的輸出buffer中設置或者在最後一個有效的輸出buffer後添加一個空的buffer來設置,這種空的buffer的時間戳應該被忽略。
當通知輸入流結束後不要再提交額外的輸入buffers,除非編解碼器被刷新或停止或重啟。
使用一個輸出Surface進行數據處理的方法與ByteBuffer模式幾乎是相同的,然而,輸出buffers不再可訪問,而且被表示為null值。E.g.方法getOutputBuffer/Image(int)將返回null,方法getOutputBuffers()將返回僅包含null值的數組。
當使用一個輸出Surface時,你能夠選擇是否渲染surface上的每一個輸出buffer,你有三種選擇:
從Android6.0(M)開始,默認的時間戳是buffer的presentation timestamp(轉換為納秒)。在此前的版本中這是沒有被定義的。
而且,從Android6.0(M)開始,你可以通過使用setOutputSurface方法動態地改變輸出Surface。
當使用輸入Surface時,將沒有可訪問的輸入buffers,因為這些buffers將會從輸入surface自動地向編解碼器傳輸。調用dequeueInputBuffer時將拋出一個IllegalStateException異常,調用getInputBuffers()將要返回一個不能寫入的偽ByteBuffer[]數組。
調用signalEndOfInputStream()方法發送end-of-stream信號。調用這個方法後,輸入surface將會立即停止向編解碼器提交數據。
視頻解碼器(通常指處理壓縮視頻數據的編解碼器)關於搜索-seek和格式轉換(不管它們是否支持)表現不同,且被配置為adaptive playback。你可以通過調用CodecCapabilities.isFeatureSupported(String)方法來檢查解碼器是否支持adaptive playback 。支持Adaptive playback的解碼器只有在編解碼器被配置在Surface上解碼時才被激活。
流域界與關鍵幀(Stream Boundary and Key Frames)
在調用start()或flush()方法後,輸入數據在合適的流邊界開始是非常重要的:其第一幀必須是關鍵幀。一個關鍵幀能夠獨立地完全解碼(對於大多數編解碼器它意味著幀內編碼),關鍵幀之後顯示的幀不會引用關鍵幀之前的幀。
下面的表格針對不同的視頻格式總結了合適的關鍵幀。
對於不支持adaptive playback的解碼器(包括解碼到Surface上解碼器)
為了開始解碼與先前提交的數據(也就是seek後)不相鄰的數據你必須刷新解碼器。由於所有輸出buffers會在flush的一刻立即撤銷,你可能希望在調用flush方法前等待這些buffers首先被標記為end-of-stream。在調用flush方法後輸入數據在一個合適的流邊界或關鍵幀開始是非常重要的。
注意:flush後提交的數據的格式不能改變;flush()方法不支持格式的不連續性;為此,一個完整的stop()-configure(...)-start()的過程是必要的。
同時注意:如果你調用start()方法後過快地刷新編解碼器,通常,在收到第一個輸出buffer或輸出format變化前,你需要向這個編解碼器再次提交codec-specific-data。具體查看codec-specific-data部分以獲得更多信息。
對於支持及被配置為adaptive playback的幾碼器
為了開始解碼與先前提交的數據(也就是seek後)不相鄰的數據,你沒有必要刷新解碼器;然而,在間斷後傳入的數據必須開始於一個合適的流邊界或關鍵幀。
針對一些視頻格式-也就是H.264、H.265、VP8和VP9,也可以修改圖片大小或者配置mid-stream。為了做到這些你必須將整個新codec-specific配置數據與關鍵幀一起打包到一個單獨的buffer中(包括所有的開始數據),並將它作為一個常規的輸入數據提交。
在picture-size被改變後以及任意具有新大小的幀返回之前,你可以從dequeueOutputBuffer方法或onOutputFormatChanged回調中得到 INFO_OUTPUT_FORMAT_CHANGED的返回值。
注意:就像使用codec-specific data時的情況,在你修改圖片大小後立即調用fush()方法時需要非常小心。如果你沒有接收到圖片大小改變的確認信息,你需要重試修改圖片大小的請求。
工廠方法createByCodecName以及createDecoder/EncoderByType會在創建codec失敗時拋出一個IOException,你必須捕獲異常或聲明向上傳遞異常。在編解碼器不允許使用該方法的狀態下調用時,MediaCodec方法將會拋出IllegalStateException異常;這種情況一般是由於API接口的不正確調用引起的。涉及secure buffers的方法可能會拋出一個MediaCodec.CryptoException異常,可以調用getErrorCode()方法獲得更多的異常信息。
內部的編解碼器錯誤將導致MediaCodec.CodecException,這可能是由於media內容錯誤、硬件錯誤、資源枯竭等原因所致,即使你已經正確的使用了API。當接收到一個CodecException時,可以調用isRecoverable()和isTransient()兩個方法來決定建議的行為。
isRecoverable()和isTransient()方法不可能同時都返回true。
下面的表格總結了MediaCodec中合法的API以及API歷史版本。更多的API版本號詳見Build.VERSION_CODES。
MediaCodec.BufferInfo
每一個緩存區的元數據都包含有一個偏移量offset和大小size用於指示相關編解碼器(輸出)緩存中有效數據的范圍。
classMediaCodec.Callback
MediaCodec回調接口。
classMediaCodec.CodecException
當發生內部的編解碼器錯誤是拋出。
classMediaCodec.CryptoException
在入隊列一個安全的輸入緩存過程中發生加密錯誤時拋出。
MediaCodec.CryptoInfo
描述(至少部分地)加密的輸入樣本的結構的元數據。
interfaceMediaCodec.OnFrameRenderedListener
當一個輸出幀在輸出surface上呈現時,監聽器被調用。
BUFFER_FLAG_CODEC_CONFIG
這表示帶有此標記的緩存包含編解碼器初始化或編解碼器特定的數據而不是多媒體數據media data。
常量值:2(0x00000002)
intBUFFER_FLAG_END_OF_STREAM
它表示流結束,該標志之後不會再有可用的buffer,除非接下來對Codec執行flush()方法。
常量值:4(0x00000004)
intBUFFER_FLAG_KEY_FRAME
這表示帶有此標記的(編碼的)緩存包含關鍵幀數據。
常量值:1(0x00000001)
intBUFFER_FLAG_SYNC_FRAME
這個常量在API level 21中棄用,使用BUFFER_FLAG_KEY_FRAME代替。
這表示帶有此標記的(編碼的)緩存包含關鍵幀數據。
常量值:1(0x00000001)
intCONFIGURE_FLAG_ENCODE
如果編解碼器被用作編碼器,傳遞這個標志。
常量值:1(0x00000001)
intCRYPTO_MODE_AES_CBC
常量值:2(0x00000002)
intCRYPTO_MODE_AES_CTR
常量值:1(0x00000001)
intCRYPTO_MODE_UNENCRYPTED
常量值:0(0x00000000)
int INFO_OUTPUT_BUFFERS_CHANGED int INFO_OUTPUT_FORMAT_CHANGED int INFO_TRY_AGAIN_LATER String PARAMETER_KEY_REQUEST_SYNC_FRAMEString
PARAMETER_KEY_SUSPEND String PARAMETER_KEY_VIDEO_BITRATE int VIDEO_SCALING_MODE_SCALE_TO_FIT int VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
*
configure
void configure (MediaFormat format, Surface surface, MediaCrypto crypto, int flags)
配置一個組件。
參數說明 format MediaFormat : 輸入數據的格式(解碼器)或期望的輸出數據的格式(編碼器)。format為null時等價於empty format. surface Surface : 指定一個surface用於顯示解碼器的輸出。如果codec沒有產生raw video output(不是一個視頻解碼器),或者把codec配置為ByteBuffer輸出模式時,surface值為null。 crypto MediaCryto : 指定一個cryto對象實現媒體數據的安全解密,cryto為null時是non-secure codecs flags int : 指定為CONFIGURE_FLAG_ENCODE時將該組件配置為編碼器
拋出異常 IllegalArgumentException surface已經釋放(或非法),或format不可接受(e.g. 丟失了強制秘鑰),或flags設置不合適(e.g. encoder時忽略了CONFIGURE_FLAG_ENCODE) IllegalStateException 不在未初始化狀態 MediaCodec.CryptoException DRM錯誤
MediaCodec.CodecException
Codec錯誤
*
Start
void start()
成功地配置組件後,調用start方法。
如果codec在異步模式下被配置且處於flushed狀態,為處理要求的的輸入buffer,也需調用start方法。
拋出異常 IllegalStateException 如果codec不處於configured狀態或異步模式下的codec執行flush()方法後,調用start()方法拋出該異常 MediaCodec.CodecException codec錯誤。注意對於start時的一些codec error可能是由於後續方法調用引起的。
dequeueInputBuffer
int dequeueInputBuffer(long timeoutUs)
返回一個填充了有效數據的input buffer的索引,如果沒有可用的buffer則返回-1.當timeoutUs==0時,該方法立即返回;當timeoutUs<0時,無限期地等待一個可用的input buffer;當timeoutUs>0時,至多等待timeoutUs微妙。
參數說明 timeoutUs long : 微妙單位,負值代表無限期。 返回值 int 拋出異常 IllegalStateException 如果codec不在Executing狀態,或者codec處於異步模式。 MediaCodec.CodecException codec錯誤
*
queueInputBuffer(沒完全理解)
void queueInputbuffer(int index, int offset, int size, long presentationtimeUs, int flags)
給指定索引的input buffer填充數據後,將其提交給codec組件。一旦一個input buffer在codec中排隊,它就不再可用直到通過getInputBuffer(int)重新獲取,getInputBuffer(int)是對dequeueInputbuffer(long)的返回值或onInputBufferAvailable(MediaCodec, int)回調的響應。
許多解碼器要求實際壓縮數據流以“codec specific data”為先導,也就是用於初始化codec的設置數據,例如AVC視頻情況時的PPS/SPS,或vorbis音頻情況時的code tables。MediaExtractor類提供codec specific data作為返回的track format的一部分,在命名為csd-0,csd-1的條目中。
通過指定BUFFER_FLAG_CODEC_CONFIG,這些buffers可以在start()或flush()後直接提交。然而,如果你使用包含這些keys的MediaFormat配置codec,他們將在start後自動地提交。因此,不鼓勵使用BUFFER_FLAG_CODEC_CONFIG,僅推薦高級用戶使用。
為了指示輸入數據的最後一塊(或除非decoder flush否則不再有輸入數據)需指定DUFFER_FLAG_END_OF_STREAM。
注意:android6.0(M)之前,presentationTimeUs不會傳遞到Surface output buffer 的幀的時間戳,這會導致幀的時間戳沒有定義。使用releaseOutputBuffer(int, long)方法確保一個指定的時間戳被設置。同樣地,盡管frame timestamp可以被destination surface用於同步渲染,必須注意presentationTimeUs正常化,以便不被誤認為是一個系統時間。
參數說明 index int : 調用dequeueInputBuffer(long)方法返回的持有者所有的input buffer的索引。 offset int : input buffer中數據起始位置的字節偏移量。 size int : 有效輸入數據的字節數。 presentationTimeUs long : 這個buffer提交呈現的時間戳(微妙),它通常是指這個buffer應該提交或渲染的media time。當使用output surface時,將作為timestamp傳遞給frame(轉換為納秒後)。 flags int : BUFFER_FLAG_CODEC_CONFIG和BUFFER_FLAG_END_OF_STREAM的位掩碼。當沒有禁止時,大多數codecs不會在input buffer中使用BUFFER_FLAG_KEY_FRAME。 拋出異常 IllegalStateException 如果沒有在Executing狀態 MediaCodec.CodecException codec錯誤 MediaCodec.CryptoException 如果cryto對象已經在configure(MediaFormat, Surface, MediaCryto, int)中指定。
release
void release()
釋放codec實例使用的資源。釋放任何開放的組件實例時調用該方法,而不是依賴於垃圾回收機制在以後某個時間完成資源的釋放。
reset
void reset()
使codec返回到初始(未初始化)狀態。如果發生了不可恢復的錯誤,調用該方法使codec復位到創建時的初始狀態。
拋出異常 MediaCodec.CodecException 如果codec發生了不可恢復的錯誤且codec不能reset. IllegalStateException 如果codec處於released狀態。
stop
void stop()
完成解碼/編碼任務後,需注意的是codec任然處於活躍狀態且准備重新start。為了確保其他client可以調用release()方法,且不僅僅只依賴於garbage collection為你完成這些工作。
拋出異常 IllegalStateException 如果codec處於Released狀態。
flush
void flush()
沖洗組件的輸入和輸出端口。
返回時,所有之前通過調用dequeueInputBuffer方法和dequeueOutputBuffer方法或通過onInputBufferAvailable和onOutputBufferAvailable回調獲得的索引會失效。所有的buffers都屬於codec。
如果codec被配置為異步模式,flush後調用start()重新開始codec操作。編解碼器不會請求輸入緩沖區,直到這已經發生了。
如果codec配置為同步模式,配置時使用了一個input buffer時codec會自動重新開始,否則,當調用dequeueInputBuffer時重新開始。
拋出異常 IllegalStateException 如果codec沒有處於Executing狀態 MediaCodec.CodecException codec錯誤
*
AndroidStudio支持新的NDK的操作使用,androidstudiondk在2015的Google I / O大會,5月底,谷歌宣布了一項新的支持由Androi
由於Android模擬器是Android開發的必備工具,所以在我們開始Andr
android:Spinner(下拉框)控件的使用,androidspinner 1.效果圖 2.創建頁面文件(main.xml)&n
點擊文本改變改行背景色,彈出對話框,改行背景 我想單純靠一個文本實現微信回復評論的效果,在一列回復文本中點擊某一行,然後該行的背景色改變並且彈出對話框,如下圖,