1. 程式人生 > >Android音視訊編解碼庫MediaCodec

Android音視訊編解碼庫MediaCodec

MediaCodec類可用於訪問低階媒體編解碼器,即編碼器/解碼器元件。 它是Android低階多媒體支援基礎架構的一部分(通常與MediaExtractor,MediaSync,MediaMuxer,MediaCrypto,MediaDrm,Image,Surface和AudioTrack一起使用)。


廣義而言,編解碼器處理輸入資料以生成輸出資料。 它非同步處理資料並使用一組輸入和輸出緩衝區。 在簡單的層面上,您請求(或接收)一個空輸入緩衝區,填充資料並將其傳送到編解碼器進行處理。 編解碼器使用資料並將其轉換為其空的輸出緩衝區之一。 最後,您請求(或接收)一個填充的輸出緩衝區,消耗其內容並將其釋放回編解碼器。

一、資料型別

編解碼器對三種資料進行操作:壓縮資料,原始音訊資料和原始視訊資料。 所有三種資料都可以使用ByteBuffers進行處理,但是您應該使用Surface來處理原始視訊資料,以提高編解碼器的效能。 Surface使用本地視訊緩衝區而不對映或將它們複製到ByteBuffers; 因此,它更有效率。 使用Surface時通常無法訪問原始視訊資料,但可以使用ImageReader類訪問不安全的解碼(原始)視訊幀。 這可能比使用ByteBuffers更高效,因為某些本地緩衝區可能對映到直接ByteBuffers。 使用ByteBuffer模式時,可以使用Image類和getInput / OutputImage(int)來訪問原始視訊幀。

一)、壓縮資料

輸入緩衝器(用於解碼器)和輸出緩衝器(用於編碼器)根據格式的型別包含壓縮資料。 對於視訊型別,這通常是單個壓縮視訊幀。 對於音訊資料,這通常是單個訪問單元(編碼音訊片段通常包含由格式型別規定的幾毫秒的音訊),但是這個要求稍微寬鬆,因為緩衝器可能包含多個編碼的音訊訪問單元。 無論哪種情況,緩衝區都不會以任意位元組邊界開始或結束,而是在幀/訪問單元邊界上,除非它們被標記為BUFFER_FLAG_PARTIAL_FRAME。

二)、原始音訊資料

原始音訊緩衝區包含整個PCM音訊資料幀,這是通道順序中每個通道的一個樣本。 每個取樣都是以本地位元組順序的16位有符號整數。

short[] getSamplesForChannel(MediaCodec codec, int bufferId, int channelIx) {
   ByteBuffer outputBuffer = codec.getOutputBuffer(bufferId);
   MediaFormat format = codec.getOutputFormat(bufferId);
   ShortBuffer samples = outputBuffer.order(ByteOrder.nativeOrder()).asShortBuffer();
   int numChannels = formet.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
   if (channelIx < 0 || channelIx >= numChannels) {
     return null;
   }
   short[] res = new short[samples.remaining() / numChannels];
   for (int i = 0; i < res.length; ++i) {
     res[i] = samples.get(i * numChannels + channelIx);
   }
   return res;
 }

三)、原始視訊資料

在ByteBuffer模式下,視訊緩衝區根據其顏色格式進行佈局。 您可以從getCodecInfo()。getCapabilitiesForType(...).colorFormats獲取支援的顏色格式。 視訊編解碼器可支援三種顏色格式:

1、本地原始視訊格式:這是由COLOR_FormatSurface標記的,它可以與輸入或輸出表面一起使用。

2、靈活的YUV緩衝區(例如COLOR_FormatYUV420Flexible):通過使用getInput / OutputImage(int),可以將這些緩衝區與輸入/輸出Surface以及ByteBuffer模式結合使用。

3、其他特定格式:這些通常僅在ByteBuffer模式下受支援。 一些顏色格式是供應商特定的。 其他在MediaCodecInfo.CodecCapabilities中定義。 對於等效於靈活格式的顏色格式,仍然可以使用getInput / OutputImage(int)。

自LOLLIPOP_MR1開始,所有視訊編解碼器都支援靈活的YUV 4:2:0緩衝區。

四)、在舊裝置上訪問原始視訊ByteBuffers

在LOLLIPOP和Image支援之前,您需要使用KEY_STRIDE和KEY_SLICE_HEIGHT輸出格式值來了解原始輸出緩衝區的佈局。

請注意,在某些裝置上,切片高度被宣傳為0.這可能意味著切片高度與幀高度相同,或者切片高度是與某個值對齊的幀高度(通常是2)。 不幸的是,在這種情況下沒有標準和簡單的方法來說明實際的切片高度。 此外,平面格式的U平面的垂直跨度也沒有規定或限定,儘管通常它是切片高度的一半。

KEY_WIDTH和KEY_HEIGHT鍵指定視訊幀的大小; 然而,對於大多數情況,視訊(圖片)只佔據視訊幀的一部分。 這由“裁剪矩形”表示。

您需要使用以下鍵從輸出格式獲取原始輸出影象的裁剪矩形。 如果這些鍵不存在,則該視訊佔據整個視訊幀。在應用任何旋轉之前,在輸出幀的上下文中理解裁剪矩形。

Format KeyTypeDescription
"crop-left"IntegerThe left-coordinate (x) of the crop rectangle
"crop-top"IntegerThe top-coordinate (y) of the crop rectangle
"crop-right"IntegerThe right-coordinate (x) MINUS 1 of the crop rectangle
"crop-bottom"IntegerThe bottom-coordinate (y) MINUS 1 of the crop rectangle
The right and bottom coordinates can be understood as the coordinates of the right-most valid column/bottom-most valid row of the cropped output image.

視訊幀的大小(旋轉之前)可以這樣計算

MediaFormat format = decoder.getOutputFormat(…);
 int width = format.getInteger(MediaFormat.KEY_WIDTH);
 if (format.containsKey("crop-left") && format.containsKey("crop-right")) {
     width = format.getInteger("crop-right") + 1 - format.getInteger("crop-left");
 }
 int height = format.getInteger(MediaFormat.KEY_HEIGHT);
 if (format.containsKey("crop-top") && format.containsKey("crop-bottom")) {
     height = format.getInteger("crop-bottom") + 1 - format.getInteger("crop-top");
 }

另請注意,BufferInfo.offset的含義在各個裝置上並不一致。 在某些裝置上,偏移量指向裁剪矩形的左上角畫素,而在大多數裝置上,它指向整個幀的左上角畫素。

二、狀態

在其生命週期中,編解碼器概念上存在三種狀態之一:停止,執行或釋放。 停止的集體狀態實際上是三種狀態的集合:未初始化,已配置和錯誤,而執行狀態在概念上通過三個子狀態:重新整理,執行和結束流。


當您使用其中一種工廠方法建立編解碼器時,編解碼器處於未初始化狀態。 首先,您需要通過配置(...)進行配置,該配置會將其置於Configured狀態,然後呼叫start()將其移至Executing狀態。 在這種狀態下,您可以通過上述的緩衝區佇列處理來處理資料。

執行狀態有三個子狀態:重新整理,執行和結束流。 在開始()之後立即編解碼器處於Flushed子狀態,它儲存所有緩衝區。 只要第一個輸入緩衝區出列,編解碼器就會轉移到執行子狀態,在該狀態下它大部分時間都在使用。 當您使用流結束標記對輸入緩衝區進行排隊時,編解碼器將轉換為流結束子狀態。 在這種狀態下,編解碼器不再接受進一步的輸入緩衝器,但仍然產生輸出緩衝器,直到輸出端達到輸出端。 您可以在執行狀態下使用flush()隨時移回Flushed子狀態。

呼叫stop()將編解碼器返回到未初始化狀態,然後再次配置它。 當您使用編解碼器時,您必須通過呼叫release()來釋放它。

在極少數情況下,編解碼器可能會遇到錯誤並進入錯誤狀態。 這是使用來自排隊操作的無效返回值或有時通過異常傳遞的。 呼叫reset()使編解碼器再次可用。 您可以從任何狀態呼叫它以將編解碼器移回未初始化狀態。 否則,呼叫release()移到終端Released狀態。

三、建立

使用MediaCodecList為特定的MediaFormat建立MediaCodec。 在解碼檔案或流時,您可以從MediaExtractor.getTrackFormat獲取所需的格式。 使用MediaFormat.setFeatureEnabled注入要新增的任何特定功能,然後呼叫MediaCodecList.findDecoderForFormat以獲取可處理該特定媒體格式的編解碼器的名稱。 最後,使用createByCodecName(String)建立編解碼器。

注意:在LOLLIPOP上,MediaCodecList.findDecoder / EncoderForFormat的格式不得包含幀速率。 使用format.setString(MediaFormat.KEY_FRAME_RATE,null)清除格式中的任何現有幀速率設定。

您還可以使用createDecoder / EncoderByType(String)為特定的MIME型別建立首選編解碼器。 但是,這不能用於注入功能,並且可能會建立無法處理特定所需媒體格式的編解碼器。

1、建立安全解碼器

在KITKAT_WATCH和更早版本上,安全編解碼器可能未列在MediaCodecList中,但可能仍在系統中可用。 存在的安全編碼解碼器只能通過名稱例項化,方法是將“.secure”附加到常規編解碼器的名稱(所有安全編解碼器的名稱必須以“.secure”結尾。)createByCodecName(String)將丟擲IOException錯誤,如果編解碼器不在系統中。

從LOLLIPOP開始,您應該使用媒體格式的FEATURE_SecurePlayback功能來建立安全的解碼器。

四、初始化

建立編解碼器後,如果要非同步處理資料,可以使用setCallback設定回撥。 然後,使用特定的媒體格式配置編解碼器。 這是您可以為視訊製作者指定輸出Surface - 生成原始視訊資料的編解碼器(例如視訊解碼器)。 您也可以設定安全編解碼器的解密引數(請參閱MediaCrypto)。 最後,由於一些編解碼器可以在多種模式下執行,因此您必須指定是否要將其用作解碼器或編碼器。

由於LOLLIPOP,您可以在配置狀態下查詢生成的輸入和輸出格式。 您可以使用它來驗證最終的配置,例如 顏色格式,然後開始編解碼器。

如果您想要使用視訊使用者本地處理原始輸入視訊緩衝區 - 處理原始視訊輸入的編解碼器(例如視訊編碼器) - 使用配置後的createInputSurface()為輸入資料建立目標Surface。 或者,通過呼叫setInputSurface(Surface)將編解碼器設定為使用先前建立的持久輸入曲面。

1、編解碼器專用資料

某些格式,特別是AAC音訊和MPEG4,H.264和H.265視訊格式要求實際資料字首包含設定資料或編解碼器特定資料的多個緩衝區。 處理這種壓縮格式時,必須在開始()之後和任何幀資料之前將此資料提交給編解碼器。 在呼叫queueInputBuffer時,必須使用標誌BUFFER_FLAG_CODEC_CONFIG標記這些資料。

特定於編解碼器的資料也可以包含在通過金鑰“csd-0”,“csd-1”等在ByteBuffer條目中進行配置的格式中。這些金鑰始終包含在從MediaExtractor獲取的音軌MediaFormat中。 在開始()時,格式中的編解碼器專用資料會自動提交給編解碼器; 你絕不能明確地提交這些資料。 如果格式不包含編解碼器特定資料,則可以根據格式要求,選擇使用指定數量的緩衝區以正確的順序提交它。 在H.264 AVC的情況下,您還可以連線所有編解碼器專用資料並將其作為單個編解碼器配置緩衝區提交。

Android使用以下編解碼器特定的資料緩衝區。 這些也需要設定為正確的MediaMuxer音軌配置的音軌格式。 用(*)標記的每個引數集和特定於編解碼器的資料部分必須以“\ x00 \ x00 \ x00 \ x01”開始程式碼開頭。

FormatCSD buffer #0CSD buffer #1CSD buffer #2
AACDecoder-specific information from ESDS*Not UsedNot Used
VORBISIdentification headerSetup headerNot Used
OPUSIdentification headerPre-skip in nanosecs
(unsigned 64-bit native-order integer.)
This overrides the pre-skip value in the identification header.
Seek Pre-roll in nanosecs
(unsigned 64-bit native-orderinteger.)
FLACmandatory metadata block (called the STREAMINFO block),
optionally followed by any number of other metadata blocks
Not UsedNot Used
MPEG-4Decoder-specific information from ESDS*Not UsedNot Used
H.264 AVCSPS (Sequence Parameter Sets*)PPS (Picture Parameter Sets*)Not Used
H.265 HEVCVPS (Video Parameter Sets*) +
SPS (Sequence Parameter Sets*) +
PPS (Picture Parameter Sets*)
Not UsedNot Used
VP9Not UsedNot Used

注意:如果編解碼器在啟動之後立即重新整理或在輸出緩衝區或輸出格式更改返回之前不久,請注意,因為在清空過程中編解碼器的特定資料可能會丟失。 在這樣的重新整理之後,您必須使用標有BUFFER_FLAG_CODEC_CONFIG的緩衝區重新提交資料,以確保正確的編解碼器操作。

編碼器(或生成壓縮資料的編解碼器)將在使用codec-config標誌標記的輸出緩衝區中的任何有效輸出緩衝區之前建立並返回編解碼器特定資料。 包含編解碼器專用資料的緩衝器沒有有意義的時間戳。

五、資料處理

每個編解碼器都維護一組由API呼叫中的緩衝區ID引用的輸入和輸出緩衝區。 在成功呼叫start()之後,客戶端“擁有”既沒有輸入也沒有輸出緩衝區。 在同步模式下,呼叫dequeueInput / OutputBuffer(...)以從編解碼器獲取(獲取所有權)輸入或輸出緩衝區。 在非同步模式下,您將通過MediaCodec.Callback.onInput / OutputBufferAvailable(...)回撥自動接收可用緩衝區。

在獲得輸入緩衝區時,填充資料並使用queueInputBuffer - 或queueSecureInputBuffer(如果使用解密)將其提交給編解碼器。 不要使用相同的時間戳提交多個輸入緩衝區(除非它是特定編解碼器的資料標記)。

編解碼器將通過非同步模式下的onOutputBufferAvailable回撥或者響應同步模式下的dequeuOutputBuffer呼叫返回一個只讀輸出緩衝區。 輸出緩衝區處理完畢後,呼叫其中一個releaseOutputBuffer方法將緩衝區返回給編解碼器。

雖然您不需要立即重新提交/釋放緩衝區到編解碼器,但握住輸入和/或輸出緩衝區可能會使編解碼器停止工作,並且此行為取決於裝置。 特別是,編解碼器有可能在產生輸出緩衝區之前暫緩,直到所有未完成的緩衝區被釋放/重新提交。 因此,儘可能少地嘗試保留可用的緩衝區。

根據API版本,您可以通過三種方式處理資料:

Processing ModeAPI version <= 20
Jelly Bean/KitKat
API version >= 21
Lollipop and later
Synchronous API using buffer arraysSupportedDeprecated
Synchronous API using buffersNot AvailableSupported
Asynchronous API using buffersNot AvailableSupported

一)使用緩衝區的非同步處理

由於LOLLIPOP,首選方法是在呼叫configure之前通過設定回撥來非同步處理資料。 非同步模式會稍微改變狀態轉換,因為您必須在flush()之後呼叫start()將編解碼器轉換為Running子狀態並開始接收輸入緩衝區。 同樣,在初始呼叫開始時,編解碼器將直接移至Running子狀態,並通過回撥開始傳遞可用的輸入緩衝區。

MediaCodec通常以非同步模式使用:

MediaCodec codec = MediaCodec.createByCodecName(name);
 MediaFormat mOutputFormat; // member variable
 codec.setCallback(new MediaCodec.Callback() {
   @Override
   void onInputBufferAvailable(MediaCodec mc, int inputBufferId) {
     ByteBuffer inputBuffer = codec.getInputBuffer(inputBufferId);
     // fill inputBuffer with valid data
     …
     codec.queueInputBuffer(inputBufferId, …);
   }

   @Override
   void onOutputBufferAvailable(MediaCodec mc, int outputBufferId, …) {
     ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
     MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A
     // bufferFormat is equivalent to mOutputFormat
     // outputBuffer is ready to be processed or rendered.
     …
     codec.releaseOutputBuffer(outputBufferId, …);
   }

   @Override
   void onOutputFormatChanged(MediaCodec mc, MediaFormat format) {
     // Subsequent data will conform to new format.
     // Can ignore if using getOutputFormat(outputBufferId)
     mOutputFormat = format; // option B
   }

   @Override
   void onError(…) {
     …
   }
 });
 codec.configure(format, …);
 mOutputFormat = codec.getOutputFormat(); // option B
 codec.start();
 // wait for processing to complete
 codec.stop();
 codec.release();

二)使用緩衝區的同步處理

由於LOLLIPOP,即使在同步模式下使用編解碼器時,也應該使用getInput / OutputBuffer(int)和/或getInput / OutputImage(int)檢索輸入和輸出緩衝區。 這允許框架進行某些優化,例如 處理動態內容時。 如果您呼叫getInput / OutputBuffers(),則此優化將被禁用。

注意:不要混合同時使用緩衝區和緩衝區陣列的方法。 具體而言,只需在start()之後或者在使用INFO_OUTPUT_FORMAT_CHANGED值出隊輸出緩衝區ID之後直接呼叫getInput / OutputBuffers。

MediaCodec通常在同步模式下使用:

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();

三)、使用緩衝區陣列的同步處理(不建議使用)

在版本KITKAT_WATCH和之前,輸入和輸出緩衝區集由ByteBuffer []陣列表示。 在成功呼叫start()之後,使用getInput / OutputBuffers()檢索緩衝區陣列。 如以下示例所示,使用緩衝區ID-s作為這些陣列中的索引(非負數時)。 請注意,儘管陣列大小提供了上限,但陣列大小與系統使用的輸入和輸出緩衝區數量之間沒有固有的相關性。

 MediaCodec codec = MediaCodec.createByCodecName(name);
 codec.configure(format, …);
 codec.start();
 ByteBuffer[] inputBuffers = codec.getInputBuffers();
 ByteBuffer[] outputBuffers = codec.getOutputBuffers();
 for (;;) {
   int inputBufferId = codec.dequeueInputBuffer(…);
   if (inputBufferId >= 0) {
     // fill inputBuffers[inputBufferId] with valid data
     …
     codec.queueInputBuffer(inputBufferId, …);
   }
   int outputBufferId = codec.dequeueOutputBuffer(…);
   if (outputBufferId >= 0) {
     // outputBuffers[outputBufferId] is ready to be processed or rendered.
     …
     codec.releaseOutputBuffer(outputBufferId, …);
   } else if (outputBufferId == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
     outputBuffers = codec.getOutputBuffers();
   } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
     // Subsequent data will conform to new format.
     MediaFormat format = codec.getOutputFormat();
   }
 }
 codec.stop();
 codec.release();

四)、流結束處理

當您到達輸入資料的末尾時,您必須通過指定queueInputBuffer呼叫中的BUFFER_FLAG_END_OF_STREAM標誌將其傳送給編解碼器。 您可以在最後一個有效的輸入緩衝區上執行此操作,或者通過提交一個額外的空輸入緩衝區並設定流結束標誌。 如果使用空緩衝區,時間戳將被忽略。

編解碼器將繼續返回輸出緩衝區,直到它通過指定dequeueOutputBuffer中設定的MediaCodec.BufferInfo中的相同流結束標誌或通過onOutputBufferAvailable返回來指示輸出流的結束為止。 這可以在最後一個有效的輸出緩衝區上設定,或者在最後一個有效的輸出緩衝區之後的空緩衝區上設定。 這個空緩衝區的時間戳應該被忽略。

除非編解碼器已被重新整理,停止並重新啟動,否則在傳送輸入流結束訊號之後,請勿提交其他輸入緩衝區。

五)、使用輸出Surface

使用輸出Surface時,資料處理與ByteBuffer模式幾乎相同; 但是,輸出緩衝區將不可訪問,並表示為空值。 例如。 getOutputBuffer / Image(int)將返回null,getOutputBuffers()將返回僅包含null-s的陣列。

使用輸出曲面時,可以選擇是否渲染曲面上的每個輸出緩衝區。 你有三個選擇:

1、不要渲染緩衝區:呼叫releaseOutputBuffer(bufferId,false)。

2、使用預設時間戳呈現緩衝區:呼叫releaseOutputBuffer(bufferId,true)。

3、使用特定的時間戳呈現緩衝區:呼叫releaseOutputBuffer(bufferId,timestamp)。

直到M版本,預設時間戳是緩衝區的顯示時間戳(轉換為納秒)。

此外,由於M,您可以使用setOutputSurface動態更改輸出Surface。

六)、渲染到Sueface時的轉換

如果編解碼器配置為Surface模式,任何裁剪矩形,旋轉和視訊縮放模式將自動應用,但有一個例外:

在M版本之前,軟體解碼器在渲染到Surface上時可能未應用旋轉。 不幸的是,沒有標準和簡單的方法來識別軟體解碼器,或者如果他們應用輪換而不是通過嘗試它。

也有一些警告。

請注意,將輸出顯示在Surface上時,不考慮畫素寬高比。 這意味著如果您使用VIDEO_SCALING_MODE_SCALE_TO_FIT模式,則必須定位輸出Surface,以使其具有適當的最終顯示寬高比。 相反,對於具有方形畫素(畫素寬高比或1:1)的內容,您只能使用VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING模式。

另請注意,從N版本開始,對於旋轉90度或270度的視訊,VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING模式可能無法正常工作。

設定視訊縮放模式時,請注意每次輸出緩衝區更改後必須重置。 由於不推薦使用INFO_OUTPUT_BUFFERS_CHANGED事件,因此每次輸出格式更改後都可以這樣做。

七)、使用輸出Surfzce

使用輸入表面時,沒有可訪問的輸入緩衝區,因為緩衝區會自動從輸入表面傳遞到編解碼器。 呼叫dequeueInputBuffer將丟擲一個IllegalStateException,並且getInputBuffers()返回一個不可寫入的虛構ByteBuffer []陣列。

呼叫signalEndOfInputStream()來發訊號結束。 此通話後,輸入表面將立即停止向編解碼器提交資料。

六、尋求和自適應回放支援

視訊解碼器(以及一般使用壓縮視訊資料的編解碼器)在搜尋和格式更改方面表現不同,無論它們是否支援並配置為自適應播放。 您可以檢查解碼器是否支援通過CodecCapabilities.isFeatureSupported(String)進行自適應播放。 只有將編解碼器配置為解碼到Surface上時,才會啟用對視訊解碼器的自適應播放支援。

一)、流邊界和關鍵幀

在start()或flush()之後的輸入資料從合適的流邊界開始非常重要:第一個幀必須是關鍵幀。 關鍵幀可以完全自行解碼(對於大多數編解碼器來說,這意味著I幀),並且在關鍵幀之後沒有要顯示的幀指的是關鍵幀之前的幀。

下表總結了各種視訊格式的合適關鍵幀。

FormatSuitable key frame
VP9/VP8a suitable intraframe where no subsequent frames refer to frames prior to this frame.
(There is no specific name for such key frame.)
H.265 HEVCIDR or CRA
H.264 AVCIDR
MPEG-4
H.263
MPEG-2
a suitable I-frame where no subsequent frames refer to frames prior to this frame.
(There is no specific name for such key frame.)

二)、對於不支援自適應回放的解碼器(包括不解碼到Surface上時)

為了開始解碼與先前提交的資料不相鄰的資料(即,在尋找之後),你必須清空解碼器。 由於所有輸出緩衝區在重新整理時立即被撤銷,因此您可能需要首先發出訊號,然後在呼叫flush之前等待流結束。 沖洗後的輸入資料始於合適的流邊界/關鍵幀,這一點很重要。

注意:重新整理後提交的資料格式不能改變; flush()不支援格式不連續性; 為此,完全stop() - configure(...) - start()週期是必要的。

另請注意:如果您在start()後過早重新整理編解碼器 - 通常在收到第一個輸出緩衝區或輸出格式更改之前 - 您需要重新提交編解碼器特定資料到編解碼器。 有關更多資訊,請參閱編解碼器特定的資料部分。

三)、適用於支援和配置自適應回放的解碼器

為了開始解碼與先前提交的資料不相鄰的資料(即,在尋找之後),不需要重新整理解碼器; 然而,不連續性之後的輸入資料必須從合適的流邊界/關鍵幀開始。

對於某些視訊格式(即H.264,H.265,VP8和VP9),也可以在中途更改圖片大小或配置。 為此,您必須將全部新的編解碼器專用配置資料與關鍵幀一起打包到單個緩衝區(包括任何啟動程式碼)中,並將其作為常規輸入緩衝區提交。

剛剛發生圖片大小更改後,以及在返回任何具有新大小的幀之前,您將收到來自dequeueOutputBuffer的INFO_OUTPUT_FORMAT_CHANGED返回值或onOutputFormatChanged回撥。

注意:就像編解碼器專用資料的情況一樣,在更改圖片大小後不久呼叫flush()時要小心。 如果您還沒有收到圖片尺寸更改的確認,則需要重新請求新圖片尺寸。

七、錯誤處理

工廠方法createByCodecName和createDecoder / EncoderByType在發生失敗時丟擲IOException,您必須捕獲或宣告傳遞失敗。 當方法從不允許的編解碼器狀態呼叫時,MediaCodec方法丟擲IllegalStateException; 這通常是由於不正確的應用程式API使用。 涉及安全緩衝區的方法可能會丟擲MediaCodec.CryptoException,該錯誤資訊可從getErrorCode()獲得更多錯誤資訊。

內部編解碼器錯誤導致MediaCodec.CodecException,這可能是由於媒體內容損壞,硬體故障,資源耗盡等等,即使應用程式正確使用該API也是如此。 接收到CodecException時推薦的操作可以通過呼叫isRecoverable()和isTransient()來確定:

1、可恢復錯誤:如果isRecoverable()返回true,則呼叫stop(),configure(...)和start()以恢復。

2、瞬間錯誤:如果isTransient()返回true,則資源暫時不可用,並且該方法可能會在稍後重試。

3、致命錯誤:如果isRecoverable()和isTransient()都返回false,那麼CodecException是致命的,編解碼器必須重置或釋放。

isRecoverable()和isTransient()都不會同時返回true。

八、有效的API呼叫和API歷史

本部分總結了每種狀態下有效的API呼叫和MediaCodec類的API歷史記錄。 有關API版本號,請參閱Build.VERSION_CODES。

SymbolMeaning
支援
語義改變
實驗支援
[ ]廢棄
受限於Surface輸入模式
受限於Surface輸出模式
受限於ByteBuffer輸出模式
受限於同步模式
受限於非同步模式
( )可以被呼叫,但不應該
UninitializedConfiguredFlushedRunningEnd of StreamErrorReleasedSDK Version
StateMethod1617181920212223

九、概要



公共方法

int

返回要用有效資料填充的輸入緩衝區的索引,如果當前沒有可用的緩衝區,則返回-1。

voidflush()

沖洗元件的輸入和輸出埠。

返回已清除的可寫ByteBuffer物件,用於包含輸入資料的出列輸入緩衝區索引。

此方法在API級別21中已棄用。每次輸入緩衝區出列時,請使用新的getInputBuffer(int)方法。 注意:自API 21起,自動清除出隊輸入緩衝區。 如果使用輸入表面,請勿使用此方法。

在configure(MediaFormat,Surface,MediaCrypto,int)成功返回以獲取編解碼器接受的輸入格式後呼叫此函式。

Image

返回出隊輸入緩衝區索引的可寫Image物件,以包含原始輸入視訊幀。

此方法在API級別21中已棄用。每次輸出緩衝區出列時,請使用新的getOutputBuffer(int)方法。 如果編解碼器配置為非同步模式,則不支援此方法。 注意:從API 21開始,出隊的輸出緩衝區的位置和限制將被設定為有效的資料範圍。 如果使用輸出表面,請勿使用此方法。

通過返回INFO_OUTPUT_FORMAT_CHANGED,dequeueOutputBuffer發出格式更改後呼叫此函式。

Image

返回包含原始視訊幀的出列輸出緩衝區索引的只讀Image物件。

voidqueueInputBuffer(int index, int offset, int size, long presentationTimeUs, int flags)

在指定索引處填充輸入緩衝區的範圍後,將其提交給元件。

void

與queueInputBuffer類似,但提交可能加密的緩衝區。

void

釋放編解碼器例項使用的資源。

void

如果完成了緩衝區,則使用此呼叫將緩衝區返回給編解碼器或將其呈現在輸出表面上。

voidreleaseOutputBuffer(int index, long renderTimestampNs)

如果已完成緩衝區,則使用此呼叫更新其表面時間戳並將其返回給編解碼器以在輸出表面上呈現它。

voidreset()

將編解碼器返回到其初始(未初始化)狀態。

void

如果在之前的配置呼叫中指定了曲面(MediaFormat,Surface,MediaCrypto,int),則指定要使用的縮放模式。

voidstart()

成功配置元件後,呼叫start。

voidstop()

完成解碼/編碼會話,請注意編解碼器例項保持活動狀態並準備好再次開始()。


保護方法

void

當垃圾收集確定沒有更多對該物件的引用時,由物件上的垃圾回收器呼叫。



相關推薦

Android視訊解碼MediaCodec

MediaCodec類可用於訪問低階媒體編解碼器,即編碼器/解碼器元件。 它是Android低階多媒體支援基礎架構的一部分(通常與MediaExtractor,MediaSync,MediaMuxer,MediaCrypto,MediaDrm,Image,Surface和Au

關注IOS、Android網路、視訊解碼、特效、Neon演算法優化,DSP等嵌入式驅動開發演算法移植

(1)音訊格式:典型WAV 封裝格式是每個音訊檔案必不可少的組成部分之一,它給我們提供了以下參考資訊。音訊檔案型別、編解碼方法、單雙聲道、取樣深度、取樣率、量化位數、音訊檔案大小、長度。下面首先來分析一下經典的wav音訊的封裝格式。個人精力有限不可能把每種音視訊格式都一一解

視訊解碼流程

編碼流程:RGB->YUV->H.264 只有YUVcain才能編碼成H264,為啥需要編碼, 因為一張沒有壓縮過的圖片,資料量太大了,比如一張640x480的圖片,每秒30幀 每秒的資料量是  640x480x3x30=3.2MB,一張VGA圖片,每秒資料3.2M

視訊解碼流媒體處理總結

本篇把音視訊及流媒體基礎相關部落格總結一下:       RTSP協議相關部落格 1.RTSP基礎之RTSP/RTP OVER TCP 2.RTSP基礎之RTSP/RTP OVER UDP 3.RTSP基礎之RTSP/RTP推流協議流程

邱長勇的專欄 [計算機視覺 計算機圖形學 三維重建 影象理解 語音識別 視訊解碼 機器學習]

HTML 5的Audio/Video元素是基於Flash外掛的音視訊替代方案。 HTML5 視訊和音訊的 DOM 參考手冊 HTML5 DOM 為 <audio> 和 <video> 元素提供了方法、屬性和事件。 這些方法、屬性和事件允許您使

各種視訊解碼學習詳解之 解碼學習筆記(一):基本概念

最近在研究音視訊編解碼這一塊兒,看到@bitbit大神寫的【各種音視訊編解碼學習詳解】這篇文章,非常感謝,佩服的五體投地。奈何大神這邊文章太長,在這裡我把它分解很多小的篇幅,方便閱讀。大神部落格傳送門:https://www.cnblogs.com/skyofbitbit/p/3651270.htm

各種視訊解碼學習詳解之 解碼學習筆記(三):Mpeg系列——Mpeg 1和Mpeg 2

    最近在研究音視訊編解碼這一塊兒,看到@bitbit大神寫的【各種音視訊編解碼學習詳解】這篇文章,非常感謝,佩服的五體投地。奈何大神這邊文章太長,在這裡我把它分解很多小的篇幅,方便閱讀。大神部落格傳送門:https://www.cnblogs.com/skyofbitbit

各種視訊解碼學習詳解之 解碼學習筆記(二):codec型別

    最近在研究音視訊編解碼這一塊兒,看到@bitbit大神寫的【各種音視訊編解碼學習詳解】這篇文章,非常感謝,佩服的五體投地。奈何大神這邊文章太長,在這裡我把它分解很多小的篇幅,方便閱讀。大神部落格傳送門:https://www.cnblogs.com/skyofbitbit

各種視訊解碼學習詳解之 解碼學習筆記(四):Mpeg系列——Mpeg 4

   最近在研究音視訊編解碼這一塊兒,看到@bitbit大神寫的【各種音視訊編解碼學習詳解】這篇文章,非常感謝,佩服的五體投地。奈何大神這邊文章太長,在這裡我把它分解成很多小的篇幅,方便閱讀。大神部落格傳送門:https://www.cnblogs.com/skyofbitbit

各種視訊解碼學習詳解之 解碼學習筆記(七):微軟Windows Media系列

    最近在研究音視訊編解碼這一塊兒,看到@bitbit大神寫的【各種音視訊編解碼學習詳解】這篇文章,非常感謝,佩服的五體投地。奈何大神這邊文章太長,在這裡我把它分解成很多小的篇幅,方便閱讀。大神部落格傳送門:https://www.cnblogs.com/skyofbitbi

各種視訊解碼學習詳解之 解碼學習筆記(五):Mpeg系列——AAC音訊

     最近在研究音視訊編解碼這一塊兒,看到@bitbit大神寫的【各種音視訊編解碼學習詳解】這篇文章,非常感謝,佩服的五體投地。奈何大神這邊文章太長,在這裡我把它分解成很多小的篇幅,方便閱讀。大神部落格傳送門:https://www.cnblogs.com/skyo

各種視訊解碼學習詳解之 解碼學習筆記(十二):其他解碼(M-JPEG,Motion JPEG 2000,DivX)

    最近在研究音視訊編解碼這一塊兒,看到@bitbit大神寫的【各種音視訊編解碼學習詳解】這篇文章,非常感謝,佩服的五體投地。奈何大神這邊文章太長,在這裡我把它分解成很多小的篇幅,方便閱讀。大神部落格傳送門:https://www.cnblogs.com/skyofbitbi

各種視訊解碼學習詳解之 解碼學習筆記(十一):Flash Video系列

 最近在研究音視訊編解碼這一塊兒,看到@bitbit大神寫的【各種音視訊編解碼學習詳解】這篇文章,非常感謝,佩服的五體投地。奈何大神這邊文章太長,在這裡我把它分解成很多小的篇幅,方便閱讀。大神部落格傳送門:https://www.cnblogs.com/skyofbitbit/p/3651

各種視訊解碼學習詳解之 解碼學習筆記(十):Ogg系列

 最近在研究音視訊編解碼這一塊兒,看到@bitbit大神寫的【各種音視訊編解碼學習詳解】這篇文章,非常感謝,佩服的五體投地。奈何大神這邊文章太長,在這裡我把它分解成很多小的篇幅,方便閱讀。大神部落格傳送門:https://www.cnblogs.com/skyofbitbit/p/3651

各種視訊解碼學習詳解之 解碼學習筆記(九):QuickTime系列

 最近在研究音視訊編解碼這一塊兒,看到@bitbit大神寫的【各種音視訊編解碼學習詳解】這篇文章,非常感謝,佩服的五體投地。奈何大神這邊文章太長,在這裡我把它分解成很多小的篇幅,方便閱讀。大神部落格傳送門:https://www.cnblogs.com/skyofbitbit/p/3651

各種視訊解碼學習詳解之 解碼學習筆記(八):Real系列

     最近在研究音視訊編解碼這一塊兒,看到@bitbit大神寫的【各種音視訊編解碼學習詳解】這篇文章,非常感謝,佩服的五體投地。奈何大神這邊文章太長,在這裡我把它分解成很多小的篇幅,方便閱讀。大神部落格傳送門:https://www.cnblogs.com/skyo

各種視訊解碼學習詳解之 解碼學習筆記(六):H.26x系列

    最近在研究音視訊編解碼這一塊兒,看到@bitbit大神寫的【各種音視訊編解碼學習詳解】這篇文章,非常感謝,佩服的五體投地。奈何大神這邊文章太長,在這裡我把它分解成很多小的篇幅,方便閱讀。大神部落格傳送門:https://www.cnblogs.com/skyofbitbi

webrtc-android平臺視訊解碼分析

WebRTC是一個實時的視訊通訊功能,Android平臺上的Chrome也提供了支援,在Chrome 29之後WebRTC功能趨於穩定,所以在之後的版本中預設被開啟。也就是說不需要在”chrome://flags”中手動去開啟該功能。 本節主要介紹一下Android平臺

Android 視訊開發(六): MediaCodec API 詳解

在學習了Android 音視訊的基本的相關知識,並整理了相關的API之後,我們應該對基本的音視訊有一定的輪廓了。下面開始接觸一個Android音視訊中相當重要的一個API: MediaCodec。 一、MediaCodec API介紹 MediaCodec可以處理具體的視

【SoaringLee_fighting的技術專欄】專注於視訊解碼和AI技術領域,開源分享,不忘初心,追求卓越!

【關於博主】   目前就職於國內知名安防企業,從事於編解碼演算法開發和優化工作,對音視訊編解碼、機器視覺和深度學習有很大興趣,期待與有識之士共同交流學習! 座標: 浙江杭州 Email: [email protected] 【推薦部落格】  編解碼: h