Files
Obsidian-Main/21.04. Android/MediaCodec.md
Awin Huang afeb207be2 vault backup: 2025-08-20 14:35:48
Affected files:
Too many files to list
2025-08-20 14:35:48 +08:00

109 lines
6.8 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 一般流程
1. 使用者從MediaCodec請求一個空的輸入bufferByteBuffer填充滿數據後將它傳遞給MediaCodec處理。
2. MediaCodec處理完這些數據並將處理結果輸出至一個空的輸出bufferByteBuffer中。
3. 使用者從MediaCodec獲取輸出buffer的數據消耗掉裡面的數據使用完輸出buffer的數據之後將其釋放回編解碼器。
流程如下圖所示:
![[android_mediacodec_flow.png]]
## 生命週期
MediaCodec的生命週期有三種狀態Stopped、Executing、Released。
- Stopped包含三種子狀態Uninitialized、Configured、Error。
- Executing包含三種子狀態Flushed、Running、End-of-Stream。
![[android_mediacodec_life_cycle.png]]
**Stopped** 的三種子狀態:
1. Uninitialized當創建了一個MediaCodec對象此時處於Uninitialized狀態。可以在任何狀態調用reset()方法使MediaCodec返回到Uninitialized狀態。
2. Configured使用configure(…)方法對MediaCodec進行配置轉為Configured狀態。
3. ErrorMediaCodec遇到錯誤時進入Error狀態。錯誤可能是在隊列操作時返回的錯誤或者異常導致的。
**Executing** 的三種子狀態:
1. Flushed在調用start()方法後MediaCodec立即進入Flushed子狀態此時MediaCodec會擁有所有的緩存。可以在Executing狀態的任何時候通過調用flush()方法返回到Flushed子狀態。
2. Running一旦第一個輸入緩存input buffer被移出隊列MediaCodec就轉入Running子狀態這種狀態佔據了MediaCodec的大部分生命週期。通過調用stop()方法轉移到Uninitialized狀態。
3. End-of-Stream將一個帶有end-of-stream標記的輸入buffer入隊列時MediaCodec將轉入End-of-Stream子狀態。在這種狀態下MediaCodec不再接收之後的輸入buffer但它仍然產生輸出buffer直到end-of-stream標記輸出。
**Released**
1. 當使用完MediaCodec後必須調用release()方法釋放其資源。調用release()方法進入最終的Released狀態。
## API
### createEncoderByType
- [createEncoderByType](https://developer.android.com/reference/android/media/MediaCodec#createEncoderByType(java.lang.String))
### createDecoderByType
- [createDecoderByType](https://developer.android.com/reference/android/media/MediaCodec#createDecoderByType(java.lang.String))
### configure
- [configure]([MediaCodec  |  Android Developers](https://developer.android.com/reference/android/media/MediaCodec#configure(android.media.MediaFormat,%20android.view.Surface,%20android.media.MediaCrypto,%20int)))
### start
- [start](https://developer.android.com/reference/android/media/MediaCodec#start())
### dequeueInputBuffer
- [dequeueInputBuffer](https://developer.android.com/reference/android/media/MediaCodec#dequeueInputBuffer(long))
### queueInputBuffer
- [queueInputBuffer](https://developer.android.com/reference/android/media/MediaCodec#queueInputBuffer(int,%20int,%20int,%20long,%20int))
### getInputBuffer
- [getInputBuffer](https://developer.android.com/reference/android/media/MediaCodec#getInputBuffer(int))
### dequeueOutputBuffer
- [dequeueOutputBuffer](https://developer.android.com/reference/android/media/MediaCodec#dequeueOutputBuffer(android.media.MediaCodec.BufferInfo,%20long))
### getOutputBuffer
- [getOutputBuffer](https://developer.android.com/reference/android/media/MediaCodec#getOutputBuffer(int))
### releaseOutputBuffer
- [releaseOutputBuffer](https://developer.android.com/reference/android/media/MediaCodec#releaseOutputBuffer(int,%20boolean))
### stop
- [stop](https://developer.android.com/reference/android/media/MediaCodec#stop())
### release
- [release](https://developer.android.com/reference/android/media/MediaCodec#release())
## 使用
1. 根據需求使用 [[#createEncoderByType]]或是 [[#createDecoderByType]] 建立codec。以下以 encode 為例。
2. 呼叫 [[#configure]],傳入相應的 MediaFormat。
3. 呼叫 [[#start]],開始 encode。
4. 建立一個迴圈,不斷的傳入要 encode 的 buffer也不斷的拿出已經 encode 的 buffer。
5. 在迴圈內,要傳入的 buffer 處理方法:
1. 呼叫 [[#dequeueInputBuffer]],試探是否有能用的 buffer如果有回傳值將大於等於0>= 0。這裡假設回傳值的變數叫做 index。
2. 如果 index 合法,用 [[#queueInputBuffer]],像是 `inputBuffer = queueInputBuffer(index)` 來取得可用的 buffer。這裡假設 buffer 的變數叫做 inputBuffer。
3. 將要 encode 的資料 copy 到 inputBuffer。
4. 若要停止 encode送出 `codec.queueInputBuffer(inputBufIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM)`
6. 在迴圈內要取得已經encode buffer的方法
1. 呼叫 [[#dequeueOutputBuffer]],試探是否有 encoded buffer如果有回傳值將大於等於0>= 0。這裡假設回傳值的變數叫做 index。
2. 如果 index合法用 [[#getOutputBuffer]],像是`outputBuffer = getOutputBuffer(index)` 來取得可用的 buffer。這裡假設 buffer 的變數叫做 outputBuffer。
3. outputBuffer 就是已經 encode 好的,就看你怎麼處理。
4. 重要!呼叫 [[#releaseOutputBuffer]] 來回收剛剛那一塊 buffer。
Psuedo code 如下:
```kotlin
while (true)
// send buffer to encode
index = dequeueInputBuffer()
if (index >= 0)
if (!end)
inputBuffer = queueInputBuffer(index)
copy(inputBuffer, srcBuffer)
else
queueInputBuffer(inputBufIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM)
// Get encoded buffer
index = dequeueOutputBuffer()
if (index >= 0)
outputBuffer = getOutputBuffer(index)
releaseOutputBuffer(index)
```
## 參考資料
- [Android音视频之使用MediaCodec编解码AAC - 简书](https://www.jianshu.com/p/14daab91b951)
- [MultiMediaLearning/app/src/main/java/com/richie/multimedialearning/mediacodec at master · isuperqiang/MultiMediaLearning](https://github.com/isuperqiang/MultiMediaLearning/tree/master/app/src/main/java/com/richie/multimedialearning/mediacodec)
- [初识MediaCodec - 知乎](https://zhuanlan.zhihu.com/p/45224834)
- [MediaCodec的使用介绍 - 简书](https://www.jianshu.com/p/f5a1c9318524)
- [Android原生编解码接口MediaCodec详解 - 掘金](https://juejin.cn/post/7086297619764346887)
- [AndroidMediaCodecDemo/AudioDecoder.kt at main · king-ma1993/AndroidMediaCodecDemo](https://github.com/king-ma1993/AndroidMediaCodecDemo/blob/main/app/src/main/java/com/myl/mediacodedemo/decode/audio/AudioDecoder.kt)
- [Android使用系统API进行音视频编码_key_max_input_size_blueberry_mu的博客-CSDN博客](https://blog.csdn.net/a992036795/article/details/54286654)
- [MediaCodec 完成PCM编码成AAC - 知乎](https://zhuanlan.zhihu.com/p/564759685)
- [MediaCodec 同步方式完成AAC硬解成PCM - 知乎](https://zhuanlan.zhihu.com/p/564734700)