網站首頁 編程語言 正文
前言
獲取到音視頻軌道(編解碼格式),知道設備支持哪些編解碼器,下一步就是創建編解碼器去實現數據流的編解碼過程了。在Android
開發中提供了實現音視頻編解碼工具MediaCodec
,針對對應音視頻解碼類型通過該類創建對應解碼器就能實現對數據進行解碼操作。
MediaCodec
MediaCodec
所支持的數據類型:壓縮的音視頻數據、原始音頻數據和原始視頻數據。 首先show代碼,緊接著之前MediaExtactor
提取資源,MediaCodecList
遍歷支持格式,確認設置支持該資源格式后通過MediaCodec
創建解碼器(這里是做視頻解碼播放)。
// 加載資源 extractor.setDataSource(path); // 獲取視頻軌道 int trackIndex = getTrackIndex(extractor,"video/"); // 獲取視頻軌道參數 MediaFormatInfo mediaFormatInfo = MediaFormatInfo.buildUpVideoMediaFormatInfo(extractor.getTrackFormat(trackIndex)); // 選取上述的視頻軌道 extractor.selectTrack(trackIndex); MediaCodecInfo mediaCodecInfo = CodecInfoInstance.getInstance().selectDeCodec(mediaFormatInfo.getMime()); // 判斷設備是否支持該視頻解碼,創建視頻編碼器 if(mediaCodecInfo != null){ mediaCodec = MediaCodec.createDecoderByType(mediaFormatInfo.getMime()); mediaCodec.configure(mediaFormatInfo.getMediaFormat(),surface,null,0); }
編解碼流程
-
MediaCodec
的功能其實很簡單通過一個數據緩沖去,將數據填充到輸入緩沖區給到Codec
-
Codec
通過異步方式處理輸入緩沖區數據將處理好數據填充到輸出緩沖區 - 客戶端從輸出緩沖區獲取到處理好的數據去消費,最后把緩沖區返還給
Codec
部分代碼實現
- dequeueInputBuffer:從輸入流隊列取數據進行編碼操作
- getInputBuffers: 獲取需要編碼數據的輸入隊列 返回ByteBuffer數組
- queueInoutBuffer: 輸入流入隊列
- dequeueOutputBuffer: 從輸入隊列中取出編碼操作結果數據
- getOutPutBuffer: 獲取編解碼之后數據輸出隊列 返回一個ByterBuffer數組
- releaseOutPutBuffer: 處理完成釋放ByterBuffer數組
while (!isEnd && !isInterrupted()){ if (!mIsEOS) { mIsEOS = dequeueInputBuffers(); } isEnd = dequeueOutputBuffers(); } //輸入緩沖區 private boolean dequeueInputBuffers() { boolean isMediaEOS = false; // 等待編碼器輸入緩沖區數據出隊 int inputBufferId = mediaCodec.dequeueInputBuffer(TIMEOUT_US); if (inputBufferId >= 0) { // 獲取緩存區數據 ByteBuffer inputBuffer = mediaCodec.getInputBuffer(inputBufferId); // 讀取數據 返回值sampleSize大于0表示還有數據,否則表示結束 int sampleSize = extractor.readSampleData(inputBuffer, 0); if (sampleSize < 0) { // 數據末尾 必須再次調用queueInputBuffer使用BUFFER_FLAG_END_OF_STREAM標識符輸入到編碼器 mediaCodec.queueInputBuffer(inputBufferId, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); isMediaEOS = true; MediaLogUtils.printI(TAG + "end of stream"); } else { // 輸入緩沖區數據入隊 mediaCodec.queueInputBuffer(inputBufferId, 0, sampleSize, extractor.getSampleTime(), 0); extractor.advance(); } } return isMediaEOS; } //輸出緩沖區 private synchronized boolean dequeueOutputBuffers() { MediaCodec.BufferInfo outBufferInfo = new MediaCodec.BufferInfo(); //輸出緩沖區 int outputBufferId = mediaCodec.dequeueOutputBuffer(outBufferInfo, TIMEOUT_US); switch (outputBufferId) { case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: MediaLogUtils.printI(TAG+"INFO_OUTPUT_FORMAT_CHANGED"); break; case MediaCodec.INFO_TRY_AGAIN_LATER: MediaLogUtils.printI(TAG+ "INFO_TRY_AGAIN_LATER"); break; case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: MediaLogUtils.printI(TAG+ "INFO_OUTPUT_BUFFERS_CHANGED"); break; default: // 延遲解碼 decodeDelay(outBufferInfo, mStartMs); // 釋放輸出緩沖區數據 render為true渲染到surface上 mediaCodec.releaseOutputBuffer(outputBufferId, true); break; } // 結尾 if ((outBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { MediaLogUtils.printI(TAG+"buffer stream end"); return true; } return false; }
生命周期
MediaCodec
生命周期分為三種狀態:Stopped
、Executing
、Released
- Stopped具有三種子狀態:Uninitialized、Configured、Error
- Executing具有三種子狀態:Flushed、Running、End-of-Stream
Stopped
Uninitialized: Uninitialized
是MediaCodec
創建后初始狀態。可通過reset()
復位到Uninitialized
Configured: MediaCodec
通過configure
方法設置配置(編解碼器類型等)進入到Configured
狀態。
Error: MediaCodec
發生異常情況下會進入Error
狀態。
Executing
Flush:MediaCodec
調用start
方法后進入Flushed
,MediaCodec
就具備所有緩存能力。若可以在Executing
,調用flush()
回到Flushed
狀態。
Running: 當第一次InputBuffer
輸入緩存被移除隊列,MediaCodec
就會進入到Running
狀態。
End-of-Stream:將end-of-stream
標記輸入InputBuffer
隊列,MediaCodec
就會進入到 End-of-Stream
狀態,MediaCodec
就不再接收InputBuffer
,但不影響輸出隊列OutBuffer
產出直到end-of-stream
標記輸出為止(輸入和輸出中間是有一定處理時間)。
接口簡介
createDecoderByType/createEncoderByType,創建解碼器/編碼器對象
createByCodecName,根據編解碼器名稱創建
configure,配置編解碼器配置
start,配置完成后需要執行start完成配置
dequeueInputBuffer, 輸入隊列取數據編碼操作
queueInputBuffer,輸入入隊列
dequeueOutputBuffer,從輸出隊列取出編碼操作后的數據
releaseOutputBuffer,釋放輸出隊列釋放ByteBuffer數據
getInputBuffers,獲取需要編碼數據輸入流隊列,返回ByteBuffer數組
getOutputBuffers,獲取編解碼后數據輸出流隊列,返回ByteBuffer數組
flush,清空輸入和輸出端口
stop,終止decode/encode會話
release,釋放編解碼器資源
參考文章
原文鏈接:https://juejin.cn/post/7091656564322500615
相關推薦
- 2022-11-23 Python面向對象的內置方法梳理講解_python
- 2023-02-03 C++中為何推薦要把基類析構函數設置成虛函數_C 語言
- 2022-07-17 絕對、相對導入以及包和常見內置模塊
- 2022-11-03 Python?pandas中apply函數簡介以及用法詳解_python
- 2022-10-11 Spring Boot 使用@Scheduled注解實現定時任務
- 2022-07-12 Linux虛擬機設置雙網卡
- 2022-08-23 Python?Pandas數據處理高頻操作詳解_python
- 2023-01-28 Python進程間通訊與進程池超詳細講解_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支