android 原生 MediaPlayer 和 MediaCodec 的區別和聯系(三)


目錄:
    (4)Android 官方網站 對 MediaCodec的介紹
  
   注:編解碼器特定數據(Code-specific Data,簡寫為csd) 部分結合網上資料加入了補充和個人理解。請悉知。
 
正文:
        MediaCodec類可用於訪問底層媒體編解碼器,例如:編碼器/解碼器組件。它是Android底層多媒體支持基礎架構的一部分(通常與 MediaExtractorMediaSyncMediaMuxerMediaCryptoMediaDrmImageSurface, 和  AudioTrack 一起使用。)
         
        從廣義上講,編解碼器處理輸入數據生成輸出數據。它異步處理數據並使用一組輸入和輸出緩沖區。簡單來講,你請求(或接受)一個空的輸入緩沖區,填滿數據將它發送給該編解碼處理器處理。 編解碼器使用數據並將其轉換為其空輸出緩沖區之一。 最后,您請求(或接收)填充的輸出緩沖區,使用其內容並將其釋放回編解碼器。
 
         數據類型
        
        編解碼器對三種數據進行操作:壓縮數據、原始音頻數據、原始視頻數據。可以使用  ByteBuffers 處理所有這三種數據,但是你應該對原始視頻數據使用一個  Surface 來提高編解碼性能。Surface 使用本地視頻緩沖而不映射或復制他們到 ByteBuffers;因此,效率更高。使用Surface時通常無法訪問原始視頻數據,但您可以使用  ImageReader 類訪問不安全的解碼(原始)視頻幀。這可能仍然比使用 ByteBuffer 高效,因為一些本地緩沖區可能被映射到  direct ByteBuffers。使用 ByteBuffer 模式時,可以使用  Image 和  getInput/ OutputImage(int) 來訪問原始視頻幀。
 
         壓縮緩沖區        
 
        輸入緩沖區(用於解碼器)和輸出緩沖區(用於編碼器)包含根據樣式類型(according to the  format's type)的壓縮數據。對於視頻類型,這通常是單個壓縮視頻幀。對於音頻數據,這通常是單個訪問單元(一個音頻編碼通常包含由格式類型指示的幾毫秒的音頻),但是該要求稍微放寬,一個緩沖區也可能包含多個編碼的音頻訪問單元。在任何一種情況下,緩沖區不會在任意字節邊界上開始或結束,而是在幀/訪問單元邊界上開始或結束,除非它們使用   BUFFER_FLAG_PARTIAL_FRAME標記。
 
         原始音頻緩沖區
 
        原始音頻緩沖區包含整個PCM音頻數據幀,這是通道順序中每個通道的一個樣本。 每個樣本都是本機字節順序的16位有符號整數。
    
 
         原始視頻緩沖區
 
        在ByteBuffer模式下,視頻緩沖區根據它們的顏色格式來布局。你可以從  getCodecInfo(). getCapabilitiesForType(…). colorFormats 獲取到支持的顏色格式數組。視頻編解碼器可以支持三種顏色格式:
      從  Build.VERSION_CODES.LOLLIPOP_MR1 起所有的視頻編解碼器均支持靈活的 YUV 4:2:0 緩沖器。
 
       在舊設備上訪問原始視頻緩沖區
      
      在 Build.VERSION_CODES.LOLLIPOP 和  Image 支持之前,你需要使用  MediaFormat.KEY_STRIDE 和  MediaFormat.KEY_SLICE_HEIGHT 輸出格式值去了解原始輸出緩存的布局。
      ★  請注意,在某些設備上,切片高度公布為0。這可能意味着切片高度與框架高度相同,或者切片高度是與某個值對齊的框架高度(通常是2)。 不幸的是,在這種情況下,沒有標准和簡單的方法來告訴實際的切片高度。 此外,平面格式的U平面的垂直步幅也未指定或定義,但通常它是切片高度的一半。
 
         MediaFormat.KEY_WIDTH 和  MediaFormat.KEY_HEIGHT 鍵指定視頻幀的大小;然而,對於大多數內容,視頻(圖片)僅占據視頻幀的一部分。這由‘裁剪矩形’表示。
 
        你需要使用如下鍵從 輸出格式 ( output format)獲取原始輸出圖像裁剪矩形。如果這些鍵不存在,視頻占據整個視頻幀。在應用任何旋轉( rotation)之前結合輸出幀的上下文理解裁剪矩形。
       
        注:1).  MINUS 翻譯為“減”
              2). 右側和底部坐標可以理解為裁剪輸出圖像的最右側有效列/最底部有效行的坐標。
 
        視頻幀(旋轉前)的大小可以這樣計算:  
 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的含義在各設備之間並不一致。 在某些設備上,偏移指向裁剪矩形的左上角像素,而在大多數設備上,它指向整個幀的左上角像素。
        
         狀態
 
        在其生命周期中,編解碼器在概念上存在以下三種狀態之一:停止、執行或釋放。停止集合狀態實際上是三個狀態的集合:未初始化、已配置以及錯誤,而執行狀態通過三個子狀態進行:刷新、運行和流結束。
          
        當你使用其中一種工廠方法創建編解碼器時,該編解碼器處於未初始化狀態。首先,你需要通過   configure(…) 進行配置,將其置於已配置狀態,然后調用  start() 將其移至執行狀態。在此狀態下,您可以通過上述緩沖區隊列操作處理數據。
 
        執行狀態有三個子狀態:Flushed,Running和 End-of-Stream。在  start() 之后,編解碼器立即處於 Flushed子狀態,該狀態持有所有緩沖區。一旦第一個輸入緩沖區出列,編解碼器就會移動到(變為是否更合適?)Running子狀態,在該狀態下會花費生命周期中絕大部分的時間。使用  end-of-stream marker 對輸入緩沖區進行排隊時,編解碼器轉換為 End-Of-Stream子狀態。在此狀態下,編解碼器不再接受其他輸入緩沖區,但仍會生成輸出緩沖區直到 end-of-steam 到達輸出端。在執行狀態時使用  flush() ,您可以隨時返回到 Flushed 子狀態。
 
        調用  stop() 將編解碼器返回到 未初始化狀態,然后可以在此配置它。當你使用編碼器后,必須通過  release() 來釋放它。
        
        在極少數情況下,編解碼器可能會遇到錯誤並移動到 錯誤 狀態。這是使用來自排隊操作的無效返回值來傳達的,或者有時是通過異常。調用  reset() 以使得編解碼器再次可用。您可以從任何狀態調用它以將編解碼器移回到 未初始化狀態。否則,調用  release() 移動到終端 釋放狀態。
 
         創建
        
        使用  MediaCodecList 為特定的  MediaFormat 創建一個 MediaCodec。解碼文件或流時,您可以從  MediaExtractor.getTrackFormat 獲取所需的格式。使用  MediaFormat.setFeatureEnabled 注入您想添加的任何特定功能,然后調用  MediaCodecList.findDecoderForFormat 以獲取可以處理該特定媒體格式的編解碼器的名稱。最后,使用  createByCodecName(String) 創建 編解碼器(the codec)。
        ★ 注:在 Build.VERSION_CODES.LOLLIPOP上,MediaCodecList.findDecoder/EncoderForFormat 的格式不得包含 frame rate 。使用 format.setString(MediaFormat.KEY_FRAME_RATE, null) 清除格式中的任何現有幀率設置。
 
        您還可以使用  createDecoder/ EncoderByType(String) 為特定 MIME 類型創建首選編解碼器/但是,這不能用於注入功能,並且可能會創建無法處理特定所需媒體格式的編解碼器。
 
         創建安全解碼器   
 
        在 Build.VERSION_CODES.KITKAT_WATCH 以及更早版本中,安全編解碼器可能未在  MediaCodecList 中列出,但可能仍在系統可用。存在的安全編解碼器只能通過名稱實例化,方法是通過給常規編解碼器的名稱追加“.secure”后綴(所有安全編解碼器的名稱必須以“.secure”結尾。)。如果該編解碼器不存在於系統中, createByCodecName(String)  將拋出 IOException。
 
        從  Build.VERSION_CODES.LOLLIPOP 開始,你應該使用媒體格式中的  MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback 功能來創建安全的編解碼器。
 
         初始化
 
        創建編解碼器后,如果您想異步處理數據,你可以使用  setCallback 設置一個回調。然后,使用特定媒體格式配置(   configure  )該編解碼器。這時您可以為視頻生產者(生成原始視頻數據的編解碼器(例如:視頻解碼器))指定輸出  Surface。這時您也可以為安全編解碼器(請參閱 MediaCrypto)設置解密參數。最后,由於某些編解碼器可以在多種模式下運行,因此必須指定是否要將其用作解碼器或編碼器。
 
        從 Build.VERSION_CODES.LOLLIPOP 后,您可以在 已配置 狀態下查詢生成的輸入和輸出格式。你可以使用它來驗證生成的配置,例如:顏色格式,在啟動編解碼器。
 
        如果要使用視頻消費者本地化地處理原始輸入視頻緩沖區——處理原始視頻輸入的編解碼器,例如視頻編碼器——在配置后使用 createInputSurface() 為輸入數據創建一個目標Surface。或者,通過調用  setInputSurface(Surface) 設置編解碼器以使用先前創建的持久輸入surface( persistent input surface)。
 
         編解碼器特定數據(Code-specific Data,簡寫為csd)
 
        某些格式,尤其 AAC音頻 和 MPEG4, H.264以及H.265視頻格式 要求 實際數據 以包含設置數據或編解碼器特定數據的多個緩沖區 來做前綴(為輔助理解,附上相關示例:請參閱圖一、二、三及圖四)。(未完待續……,
        
                    (圖一)
    
                    (圖二)
    
                    (圖三)
    
                      ( 圖四
 
繼續接以上未完成翻譯)處理此類壓縮數據時,必須在  start() 之后 和 任何幀數據 之前將此數據提交給編解碼器。在調用 queueInputBuffer源碼)時*最后一位參數*必須賦值為標志  BUFFER_FLAG_CODEC_CONFIG 以標記此類數據。
        
        特定編解碼器數據也可以包含在通過關鍵字“csd-0”,“csd-1”,等的ByteBuffer條目中配置( configure)的格式中。這些鍵總是包含在從  MediaExtractor 獲得的軌道  MediaFormat 中。格式中的編解碼器特定數據在  start() 時自動提交給編解碼器;你不能顯示地提交這類數據。如果格式不包含特定編解碼器數據,則可以根據根據格式要求選擇使用指定數量的緩沖區以正確的順序提交。對於 H.264 AVC,您還可以連接所有特定編解碼器數據並將其作為單個編解碼器配置緩沖區提交。
 
        Android使用以下特定編解碼器數據緩沖區。這些也需要以軌道格式設置,以便正確配置  MediaMuxer 軌道。每個參數集和標有(*)的編解碼器特定數據部分必須以起始代碼“\x00\x00\x00\x01”開頭。
      
        ★  注意:如果在返回任何輸出緩沖區或輸出格式更改之前立即或在啟動后不久刷新編解碼器,則必須小心,因為在刷新期間編解碼器特定數據可能會丟失。在進行此類刷新后,必須使用標記為 BUFFER_FLAG_CODEC_CONFIG  的緩沖區重新提交數據,以確保正確的編解碼器操作。
 
        編碼器(或生成壓縮數據的編解碼器)將在標記有 codec-config flag標志的輸出緩沖區中的任何有效輸出緩沖區前創建並返回編解碼器特定數據。包含編解碼器特定數據的緩沖區沒有有意義的時間戳。
 
         數據處理
 
        每一個編解碼器都維護一組輸入和輸出緩沖區,這些緩沖區在API調用中由緩沖區ID引用。在成功調用 start()之后,客戶端“owns”既不擁有輸入也不擁有輸出緩沖區。在同步模式下,調用  dequeueInput/ OutputBuffer(…) 獲取(獲得所有權)輸入或輸出緩沖區。在異步模式下,您將通過  MediaCodec.Callback.onInput/ OutputBufferAvailable(…) 回調自動接收可用緩沖區。
 
        獲取輸入緩沖區后,使用  queueInputBuffer – 或  queueSecureInputBuffer(如果使用解密)將其填入數據並將其提交給編解碼器。不要提交具有相同時間戳的多個輸入緩沖區(除非它是標記為此類編解碼器特定數據)。
 
        反過來,編解碼器將通過異步模式下的 onOutputBufferAvailable回調返回只讀輸出緩沖區,或者響應同步模式下的 dequeuOutputBuffer 調用。處理完輸出緩沖區后,調用 releaseOutputBuffer方法之一將緩沖區返回給編解碼器。
 
        雖然您不需要立即向編解碼器重新提交/釋放緩沖區,但持有輸入和/或輸出緩沖區可能會使編解碼器停止運行,並且此行為取決於設備。具體地說,編解碼器可能會在生成輸出緩沖區前暫停,直到所有未完成的緩沖區都被釋放/重新提交。因此,盡可能少地保持可用的緩沖區。
 
        根據API版本,您可以通過三種方式處理數據:
    
 
        使用 Buffers 異步處理
 
        從 Build.VERSION_CODES.LOLLIPOP開始,首選方法是通過在調用  configure 之前設置回調來異步處理數據。異步模式稍微地改變了狀態轉換,因為必須在  flush() 之后調用 start() 以將編解碼器轉換為 Running 子狀態並開始接收輸入緩沖區。類似地,在初始調用啟動后,編解碼器將直接移動到 Runnning 子狀態並開始通過回調傳遞可用的輸入緩沖區。
    
        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();

 

         使用  Buffers 同步處理
 
        從  Build.VERSION_CODES.LOLLIPOP 開始,即使在同步模式下使用編解碼器,也應使用  getInput/ OutputBuffer(int) 和/或  getInput/ OutputImage(int) 檢索輸入和輸出緩沖區。這允許框架的某些優化,例如:處理動態內容時。如果調用  getInput/ OutputBuffers(),則禁用此優化。
 
        ★  不要同時混合使用緩沖區和緩沖區數組的方法。具體來說,只能在 start() 之后或在講輸出緩沖區ID以INFO_OUTPUT_FORMAT_CHANGED 值出列后直接調用 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();

 

         使用 Buffer Arrays 同步處理(舍棄)
 
        版本 Build.VERSION_CODES.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();

 

        流末端處理(End-of-stream Handling)

 
        當您到達輸入數據的末尾時,必須通過在對  queueInputBuffer 的調用中指定  BUFFER_FLAG_END_OF_STREAM 標志來將其發送到編解碼器。 您可以在最后一個有效的輸入緩沖區上執行此操作,或者通過提交一個設置有 end-of-stream 標志的額外空輸入緩沖區來執行此操作。 如果使用空緩沖區,則將忽略時間戳。
 
        編解碼器將繼續返回輸出緩沖區,直到它最終通過在  dequeueOutputBuffer 中設置的  MediaCodec.BufferInfo 中指定相同的 end-of-stream 標志或通過  onOutputBufferAvailable 返回來通知輸出流的結尾。 這可以在最后一個有效輸出緩沖區上設置,也可以在最后一個有效輸出緩沖區后的空緩沖區上設置。 應忽略此類空緩沖區的時間戳。
 
        除非編解碼器已刷新,或已停止並重新啟動,否則在發出輸入流結束信號后,請勿提交額外輸入緩沖區。
 
         使用一個輸出 Surface
 
        使用輸出 Surface時,數據處理幾乎與 ByteBuffer 模式相同; 但是,輸出緩沖區將不可訪問,並表示為空值。 例如。  getOutputBuffer/ Image(int) 將返回 null, getOutputBuffers() 將返回僅包含nulll-s的數組。
 
        使用輸出Surface時,可以選擇是否在曲面上渲染每個輸出緩沖區。 你有三個選擇:
 
        從 Build.VERSION_CODES.M開始,默認時間戳是緩沖區的顯示時間戳- presentation timestamp (轉換為納秒)。 之前沒有定義。
 
        此外,自 Build.VERSION_CODES.M起,您可以使用 setOutputSurface動態更改輸出Surface。
 
        渲染到曲面上的變換
 
        如果編解碼器配置為“曲面”模式,任何裁剪矩形,旋轉( rotation)和視頻縮放模式( video scaling mode)會自動應用,但有一個例外:
        ★ 在 Build.VERSION_CODES.M版本之前,軟件解碼器在渲染到Surface上時可能沒有應用旋轉。遺憾的是,沒有標准和簡單的方法來識別軟件解碼器,或者如果他們應用旋轉而不是通過嘗試它。
 
        還有一些警告。
 
          請注意,在Surface上顯示輸出時不考慮像素長寬比。這意味着如果您使用的是VIDEO_SCALING_MODE_SCALE_TO_FIT模式,則必須定位輸出Surface,使其具有正確的最終顯示寬高比。相反,您只能對具有方形像素(像素長寬比或1:1)的內容使用VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING模式。
 
        ★ 另請注意,從Build.VERSION_CODES.N 版本開始,對於旋轉90度或270度的視頻,VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING模式可能無法正常工作。
 
        ★ 設置視頻縮放模式時,請注意每次輸出緩沖區更改后必須復位。由於不推薦使用INFO_OUTPUT_BUFFERS_CHANGED事件,因此可以在每次輸出格式更改后執行此操作。
 
         使用一個輸入 Surface
 
        使用輸入Surface時,沒有可訪問的輸入緩沖區,因為緩沖區會自動從輸入 surface 傳遞到編解碼器。 調用  dequeueInputBuffer 將拋出IllegalStateException,並且 getInputBuffers()返回一個絕不能寫入的偽造的ByteBuffer[]數組。
 
        調用  signalEndOfInputStream() 來發信號給末端流(end-of-stream)。 在此調用之后,輸入surface 將立即停止向編解碼器提交數據。
 
         尋址(seek) & 自適應回放支持
 
        視頻解碼器(以及消費壓縮視頻數據的一般編解碼器)在尋址和格式改變方面表現不同,無論它們是否支持並配置為自適應回放。 您可以通過 CodecCapabilities.isFeatureSupported(String)檢查解碼器是否支持自適應播放( adaptive playback)。 只有將編解碼器配置為在 Surface上解碼時,才會激活對視頻解碼器的自適應回放支持。
 
         流邊界和關鍵幀
 
        重要的是 start()flush()之后的輸入數據在合適的流邊界處開始:第一幀必須是關鍵幀。 關鍵幀可以完全自己解碼(對於大多數編解碼器,這意味着I-幀(I-frame)),並且在關鍵幀之后沒有要顯示的幀指的是關鍵幀之前的幀。
 
        下表總結了適用於各種視頻格式的關鍵幀。
    
 
        對於不支持自適應播放的解碼器(包括不解碼到Surface時)
        為了開始解碼與先前提交的數據不相鄰的數據(即在尋址(seek)之后),你必須刷新解碼器。由於所有輸出緩沖區在刷新時立即被撤銷,因此您可能需要首先發出信號,然后在調用flush之前等待末端流(end-of-steam)。重要的是,刷新之后的輸入數據在合適的流邊界/關鍵幀處開始。
 
       ★  注意:刷新后提交的數據格式不得更改;flush() 不支持格式不連續;為此,一個完整的stop() - configure(…) - start() 循環是必要的。
 
       ★  另請注意:如果在start() 后過早刷新編解碼器 - 通常,在收到第一個輸出緩沖區或輸出格式更改之前 - 您需要將編解碼器特定數據(codec-specific-data)重新提交給編解碼器。有關詳細信息,請參閱編解碼器特定數據部分(codec-specific-data section)。
 
        適用於支持和配置自適應播放的解碼器
        為了開始解碼與先前提交的數據不相鄰的數據(即,在尋址(seek)之后),不必刷新解碼器;但是,不連續后的輸入數據必須從合適的流邊界/關鍵幀開始。
 
        對於某些視頻格式 - 即H.264,H.265,VP8和VP9 - 也可以在中途改變圖像大小或配置mid-stream。 為此,必須將整個新的編解碼器特定配置數據與關鍵幀一起打包到單個緩沖區(包括任何起始代碼)中,並將其作為常規輸入緩沖區提交。
 
        在發生圖片大小更改之后以及返回任何具有新大小的幀之前,您將從 dequeueOutputBufferonOutputFormatChanged回調中收到 INFO_OUTPUT_FORMAT_CHANGED返回值。
 
        ★  注意:就像編解碼器特定數據的情況一樣,在更改圖片大小后立即調用flush()時要小心。 如果您尚未收到圖片尺寸更改的確認,則需要重復請求新圖片尺寸。
 
         異常處理
 
        工廠方法  createByCodecName 和  createDecoder/ EncoderByType 在失敗時拋出IOException,您必須捕獲或聲明要傳遞。當從不允許它的編解碼器狀態調用方法時,MediaCodec 方法拋出 IllegalStateException;這通常是由於申請API用法不正確。涉及安全緩沖區的方法可能會拋出 MediaCodec.CryptoException,它可以從 MediaCodec.CryptoException.getErrorCode()獲得更多錯誤信息。
 
        內部編解碼器錯誤導致 MediaCodec.CodecException,這可能是由於媒體內容損壞,硬件故障,資源耗盡等等,即使應用程序正確使用API也是如此。接收到一個CodecException時建議的操作是可以通過調用 MediaCodec.CodecException.isRecoverable()MediaCodec.CodecException.isTransient()來確定:
  • 可恢復的錯誤:如果isRecoverable()返回 true,則調用stop()configure(…)start()進行恢復。
  • 瞬態錯誤:如果iisTransient()返回 true,則資源暫時不可用,並且可以在以后重試該方法。
  • 致命錯誤:如果isRecoverable()和isTransient()都返回 false,則CodecException是致命的,並且必須重置(reset )或釋放編解碼器(released)。
       isRecoverable()和isTransient()都不會同時返回 true。
 
 
  寫在文末:本文在對官方文檔進行翻譯時,一些地方結合網上資料加入了補充資料或自己的理解,譬如: 編解碼器特定數據(Code-specific Data,簡寫為csd) 部分。如有不當或影響閱讀,請留言告知。先行謝過。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM