MediaCodec 類可以用來訪問底層媒體編解碼器,即編碼器/解碼器的組件。它是 Android 底層多媒體支持架構(gòu)的一部分。

一個編解碼器處理輸入數(shù)據(jù)以生成輸出數(shù)據(jù)。它異步地處理數(shù)據(jù),并使用一組輸入和輸出緩沖器。
調(diào)用的時候需要先初始化 MediaCodec 作為視頻的編碼器,然后只需要不停傳入原始的 YUV 數(shù)據(jù)進(jìn)入編碼器就可以直接輸出編碼好的 h264 流。解碼器則對應(yīng)相反。
數(shù)據(jù)類型(Data Types)
編解碼器對 3 種數(shù)據(jù)進(jìn)行操作:壓縮后的數(shù)據(jù),原始音頻數(shù)據(jù)和原始視頻數(shù)據(jù)??梢允褂?ByteBuffers 處理所有三種數(shù)據(jù),但對原始視頻數(shù)據(jù),可以使用 Surface 提高編解碼的性能。Surface 使用本地視頻緩沖區(qū)而不是映射或復(fù)制到 ByteBuffers,因此,效率更高。(通常在使用 Surface 時無法訪問原始視頻數(shù)據(jù),但可以使用 ImageReader 類訪問不安全的解碼(原始)視頻幀)。
壓縮緩沖區(qū)(Compressed Buffers)
輸入緩沖器和輸出緩沖器根據(jù)格式的類型來存放已壓縮的數(shù)據(jù)。對于視頻類型,這是一個單一的壓縮后的視頻幀。對于音頻數(shù)據(jù),通常是單個訪問單元(一個編碼后的音頻片段通常包含幾毫秒音頻)。
FFmpeg 中的 MediaCodec
FFmpeg 中僅支持 Android MediaCodec 解碼,本文主要介紹 FFmpeg 中關(guān)于 MediaCodec 解碼流程。后面會介紹如何在 FFmpeg 中添加 MediaCodec 編碼。
解碼流程:
-
mediacodec_deocde_init(初始化 MediaCodec 解碼器)

stativ av_cold int mediacodec_decode_init(AVCodecContext *avctx)
{
// MediaCodec H.264 解碼相關(guān)屬性
MediaCodecH264DecContext *s = avctx->priv_data;
// 調(diào)用 jni 創(chuàng)建 MediaFormat
FFAMediaFormat *format = ff_AMediaFormat_new();
// 根據(jù) codec_id 獲取 mimeType
const char *codec_mime = "video/avc";
// 獲取 AVCodecContext 的 Extreadata 中的 sps & pps,存入H.264文件的頭部
ret = h264_set_extradata(avctx, format);
// 設(shè)置 MediaFormat 屬性
ff_AMediaFormat_setxxx(format, "xxx", xxx);
ret = ff_mediacodec_dec_init(avctx, s->ctx)
}
-
mediacodec_decode_frame(解碼方法)FFmpeg 中 MediaCodec 解碼.png
static int mediacodec_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPacket *avpkt)
{
// mediacodec 私有參數(shù)
MediaCodecH264DecContext *s = avctx->priv_data;
// 解碼得到的數(shù)據(jù)
AVFrame *frame = data;
int ret;
// 將要解碼的 AVPacket 寫入 fifo
av_fifo_generic_write(s->fifo, &input_pkt, sizeof(input_pkt), NULL);
while (!*got_frame) {
/* prepare the input data */
if (s->buffered_pkt.size <= 0) {
// 從 fifo 讀到 buffered_pkt
av_fifo_generic_read(s->fifo, &s->buffered_pkt, sizeof(s->buffered_pkt), NULL);
}
// 解碼
ret = mediacodec_process_data(avctx, frame, got_frame, &s->buffered_pkt);
}
}
-
ff_mediacodec_dec_decode(解碼)ff_mediacodec_dec_decode.png
int ff_mediacodec_dec_decode(AVCodecContext *avctx, MediaCodecDecContext *s,
AVFrame *frame, int *got_frame,
AVPacket *pkt)
{
while(...) {
// 查找是否有可用的 輸入緩沖區(qū)
index = ff_AMediaCodec_dequeueInputBuffer(codec, input_dequeue_timeout_us);
// 獲取輸入緩沖區(qū)
data = ff_AMediaCodec_getInputBuffer(codec, index, &size);
if (need_draining) {
// 發(fā)送 結(jié)束信號
status = ff_AMediaCodec_queueInputBuffer(codec, index, 0, 0, pts, flags);
} else {
// 傳送 要解碼的數(shù)據(jù)
status = ff_AMediaCodec_queueInputBuffer(codec, index, 0, size, pts, 0);
}
}
// 獲取輸出緩沖區(qū)
index = ff_AMediaCodec_dequeueOutputBuffer(codec, &info, output_dequeue_timeout_us);
if (index >= 0) {
if (info.size) {
if (s->surface) {
// 直接顯示
ret = mediacodec_wrap_hw_buffer(avctx, s, index, &info, frame);
} else {
// 獲取解碼后的數(shù)據(jù)
data = ff_AMediaCodec_getOutputBuffer(codec, index, &size);
// 轉(zhuǎn)換格式
ret = mediacodec_wrap_sw_buffer(avctx, s, data, size, index, &info, frame);
}
} else {
// 沒有輸出數(shù)據(jù),釋放緩沖區(qū)
status = ff_AMediaCodec_releaseOutputBuffer(codec, index, 0);
}else if (ff_AMediaCodec_infoOutputFormatChanged(codec, index)) {
// 輸出格式已更改
// 刪除當(dāng)前輸出格式
status = ff_AMediaFormat_delete(s->format);
// 使用新的輸出格式
s->format = ff_AMediaCodec_getOutputFormat(codec);
} else if (ff_AMediaCodec_infoOutputBuffersChanged(codec, index)) {
// 輸出緩沖區(qū)已更改
ff_AMediaCodec_cleanOutputBuffers(codec);
} else if (ff_AMediaCodec_infoTryAgainLater(codec, index)) {
// 超時
} else {
// 解碼錯誤
av_log(avctx, AV_LOG_ERROR, "Failed to dequeue output buffer (status=%zd)\n", index);
return AVERROR_EXTERNAL;
}
}
}

