MediaCodec(native)状态机分析

article/2025/10/6 17:53:47

一、引入:

MediaCodec这条通路的调用逻辑是MediaCode->ACodec->OMX,因为OMX有自己的状态机,所以MediaCodec和ACodec也分别基于OMX组件的调用维护了其状态机,这篇博客就先分析MediaCodec的状态机是如何运转的。

二、MediaCodec与ACodec的联动:

1.MediaCodec的继承关系:

struct MediaCodec : public AHandler {...
}

MediaCodec实际上是一个AHandler ,用于维护native层的消息机制。

2.ACodec的继承关系:

struct ACodec : public AHierarchicalStateMachine, public CodecBase {...
}

ACodec继承自CodecBase ,而CodecBase 中含有一个内联函数:

inline void setCallback(std::unique_ptr<CodecCallback> &&callback) {mCallback = std::move(callback);
}

这表明,在实例化ACodec的时候,是可以设置一个回调函数下来的

3.MediaCodec与ACodec的关联:
看一下MediaCodec创建ACodec的场景:

status_t MediaCodec::init(const AString &name) {.../* 1.实例化Acodec */mCodec = GetCodecBase(name);if (mCodec == NULL) {return NAME_NOT_FOUND;}.../* 2.设置ACodec的回调 */mCodec->setCallback(std::unique_ptr<CodecBase::CodecCallback>(new CodecCallback(new AMessage(kWhatCodecNotify, this))));/* 3.创建buffer channel,为fillbuffer和emptybuffer做准备 */mBufferChannel = mCodec->getBufferChannel();mBufferChannel->setCallback(std::unique_ptr<CodecBase::BufferCallback>(new BufferCallback(new AMessage(kWhatCodecNotify, this))));...
}

在MediaCodec的init函数中,首先会去创建ACodec,然后创建CodecCallback对象,这个对象专门用于处理ACodec中回调回来的各种消息,然后根据不同的消息去设置MediaCodec的状态机。

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

三、MediaCodec的各状态机:

MediaCodec有如下状态机:

    enum State {UNINITIALIZED,INITIALIZING,INITIALIZED,CONFIGURING,CONFIGURED,STARTING,STARTED,FLUSHING,FLUSHED,STOPPING,RELEASING,};

1.UNINITIALIZED:
在构造MediaCodec对象的时候,会设置当前状态为UNINITIALIZED:

MediaCodec::MediaCodec(const sp<ALooper> &looper, pid_t pid, uid_t uid): mState(UNINITIALIZED),mReleasedByResourceManager(false),mLooper(looper),mCodec(NULL),...{...
}

当然,如果在其他的状态机中(INITIALIZING/STOPPING/FLUSHING),ACodec回调了kWhatError消息时,也会将MediaCodec设置为UNINITIALIZED。

2.INITIALIZING:

MediaCodec的init函数中,会发送kWhatInit消息,而消息的处理阶段,将会设置此状态机:

        case kWhatInit:{sp<AReplyToken> replyID;CHECK(msg->senderAwaitsResponse(&replyID));if (mState != UNINITIALIZED) {PostReplyWithError(replyID, INVALID_OPERATION);break;}mReplyID = replyID;/* 设置状态机 */setState(INITIALIZING);sp<RefBase> codecInfo;CHECK(msg->findObject("codecInfo", &codecInfo));AString name;CHECK(msg->findString("name", &name));sp<AMessage> format = new AMessage;format->setObject("codecInfo", codecInfo);format->setString("componentName", name);/* 跟进到ACodec去初始化omx组件 */mCodec->initiateAllocateComponent(format);break;}

3.INITIALIZED:
在上面kWhatInit的消息处理中,可以看到会跟进到ACodec去初始化omx组件,当底层组件初始化完成后,会回调给MediaCodec并设置当前状态:

void CodecCallback::onComponentAllocated(const char *componentName) {sp<AMessage> notify(mNotify->dup());notify->setInt32("what", kWhatComponentAllocated);notify->setString("componentName", componentName);notify->post();
}

看一下kWhatComponentAllocated消息的处理:

case kWhatComponentAllocated:
{CHECK_EQ(mState, INITIALIZING);setState(INITIALIZED);mFlags |= kFlagIsComponentAllocated;CHECK(msg->findString("componentName", &mComponentName));if (mComponentName.c_str()) {mAnalyticsItem->setCString(kCodecCodec, mComponentName.c_str());}...
}

当然,在其他一些场景下也会设置为此状态,这里不去赘述。

4.CONFIGURING:
MediaCodec的configure函数会发送kWhatConfigure消息,在此消息的处理阶段,会去设置当前状态:

case kWhatConfigure:
{sp<AReplyToken> replyID;CHECK(msg->senderAwaitsResponse(&replyID));if (mState != INITIALIZED) {PostReplyWithError(replyID, INVALID_OPERATION);break;}sp<RefBase> obj;CHECK(msg->findObject("surface", &obj));sp<AMessage> format;CHECK(msg->findMessage("format", &format));int32_t push;if (msg->findInt32("push-blank-buffers-on-shutdown", &push) && push != 0) {mFlags |= kFlagPushBlankBuffersOnShutdown;}if (obj != NULL) {format->setObject("native-window", obj);status_t err = handleSetSurface(static_cast<Surface *>(obj.get()));if (err != OK) {PostReplyWithError(replyID, err);break;}} else {handleSetSurface(NULL);}mReplyID = replyID;/* 状态机设置 */setState(CONFIGURING);
...
}

5. CONFIGURED:
当底层OMX IL层完成上面的CONFIGURING指令后,会先回调给ACodec:

bool ACodec::LoadedState::onConfigureComponent(const sp<AMessage> &msg) {ALOGV("onConfigureComponent");CHECK(mCodec->mOMXNode != NULL);status_t err = OK;AString mime;if (!msg->findString("mime", &mime)) {err = BAD_VALUE;} else {/* 根据msg配置codec */err = mCodec->configureCodec(mime.c_str(), msg);}if (err != OK) {ALOGE("[%s] configureCodec returning error %d",mCodec->mComponentName.c_str(), err);mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));return false;}/* 配置完成,通知至MediaCodec */mCodec->mCallback->onComponentConfigured(mCodec->mInputFormat, mCodec->mOutputFormat);return true;
}

MediaCodec会在onComponentConfigured函数中发送msg,之后设置此状态机:

void CodecCallback::onComponentConfigured(const sp<AMessage> &inputFormat, const sp<AMessage> &outputFormat) {sp<AMessage> notify(mNotify->dup());notify->setInt32("what", kWhatComponentConfigured);notify->setMessage("input-format", inputFormat);notify->setMessage("output-format", outputFormat);notify->post();
}
 case kWhatComponentConfigured:{if (mState == UNINITIALIZED || mState == INITIALIZED) {// In case a kWhatError message came in and replied with error,// we log a warning and ignore.ALOGW("configure interrupted by error, current state %d", mState);break;}/* 检查状态 */CHECK_EQ(mState, CONFIGURING);.../* 设置状态 */setState(CONFIGURED);....
}

6.STARTING:

状态机的发起由start()函数开始执行:

status_t MediaCodec::start() {sp<AMessage> msg = new AMessage(kWhatStart, this);.../* 发送kWhatStart消息 */err = PostAndAwaitResponse(msg, &response);...
}

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

对应的消息处理:

case kWhatStart:
{sp<AReplyToken> replyID;CHECK(msg->senderAwaitsResponse(&replyID));if (mState == FLUSHED) {setState(STARTED);if (mHavePendingInputBuffers) {onInputBufferAvailable();mHavePendingInputBuffers = false;}mCodec->signalResume();PostReplyWithError(replyID, OK);break;} else if (mState != CONFIGURED) {PostReplyWithError(replyID, INVALID_OPERATION);break;}mReplyID = replyID;/* 设置STARTING状态 */setState(STARTING);/* 调用到底层进行codec初始化 */mCodec->initiateStart();break;
}

7.STARTED:
此状态的发起在ACodec的状态机中完成,ACodec在LoadedToIdleState状态时,会去申请buffer同时回调至MediaCodec:

status_t ACodec::LoadedToIdleState::allocateBuffers() {/* 申请输入buffer */status_t err = mCodec->allocateBuffersOnPort(kPortIndexInput);if (err != OK) {return err;}/* 申请输出buffer */err = mCodec->allocateBuffersOnPort(kPortIndexOutput);if (err != OK) {return err;}/* 通知至MediaCodec */mCodec->mCallback->onStartCompleted();return OK;
}

看一下MediaCodec对onStartCompleted函数的处理:

void CodecCallback::onStartCompleted() {sp<AMessage> notify(mNotify->dup());notify->setInt32("what", kWhatStartCompleted);notify->post();
}
case kWhatStartCompleted:
{CHECK_EQ(mState, STARTING);if (mIsVideo) {addResource(MediaResource::kGraphicMemory,MediaResource::kUnspecifiedSubType,getGraphicBufferSize());}/* 设置状态 */setState(STARTED);(new AMessage)->postReply(mReplyID);break;
}

8.FLUSHING:
此状态为MediaCodec主动发起:

status_t MediaCodec::flush() {sp<AMessage> msg = new AMessage(kWhatFlush, this);sp<AMessage> response;return PostAndAwaitResponse(msg, &response);
}
case kWhatFlush:
{sp<AReplyToken> replyID;CHECK(msg->senderAwaitsResponse(&replyID));if (!isExecuting()) {PostReplyWithError(replyID, INVALID_OPERATION);break;} else if (mFlags & kFlagStickyError) {PostReplyWithError(replyID, getStickyError());break;}mReplyID = replyID;// TODO: skip flushing if already FLUSHEDsetState(FLUSHING);mCodec->signalFlush();returnBuffersToCodec();break;
}

9.FLUSHED:

因为flush的使用很灵活,所以会有多个地方会回调至MediaCodec:

void CodecCallback::onFlushCompleted() {sp<AMessage> notify(mNotify->dup());notify->setInt32("what", kWhatFlushCompleted);notify->post();
}
case kWhatFlushCompleted:
{if (mState != FLUSHING) {ALOGW("received FlushCompleted message in state %d",mState);break;}if (mFlags & kFlagIsAsync) {setState(FLUSHED);} else {setState(STARTED);mCodec->signalResume();}(new AMessage)->postReply(mReplyID);break;
}

10.stop/release:
这两个状态都是由MediaCodec主动发起的:

status_t MediaCodec::stop() {sp<AMessage> msg = new AMessage(kWhatStop, this);sp<AMessage> response;return PostAndAwaitResponse(msg, &response);
}
status_t MediaCodec::release() {sp<AMessage> msg = new AMessage(kWhatRelease, this);sp<AMessage> response;return PostAndAwaitResponse(msg, &response);
}

对应的消息处理会设置其状态:

case kWhatStop:
case kWhatRelease:
{.../* 设置状态 */setState(msg->what() == kWhatStop ? STOPPING : RELEASING);/* 通知底层进行相应操作 */mCodec->initiateShutdown(msg->what() == kWhatStop /* keepComponentAllocated */);
}

四、状态机总览图: 

如果你对音视频开发感兴趣,觉得文章对您有帮助,别忘了点赞、收藏哦!或者对本文的一些阐述有自己的看法,有任何问题,欢迎在下方评论区讨论!

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓


http://chatgpt.dhexx.cn/article/KwFnXX1V.shtml

相关文章

视频-MediaCodec

1.解析视频可以使用android 提供的api MediaPlayer,实现简单的播放暂停&#xff0c;提取视频中的一帧或者编辑视频&#xff0c;需要另一个api MediaCodec&#xff08;硬解&#xff0c;控制DSP芯片&#xff09;或者 ffmpeg(软解&#xff0c;耗时)&#xff0c;选择的优先级一定要…

MediaCodec解析MP4视频

MediaCodec讲解 MediaCodec是Android提供的用于对音视频进行编解码的类&#xff0c;它通过访问底层的codec来实现编解码的功能。是Android media基础框架的一部分&#xff0c;通常和 MediaExtractor, MediaSync, MediaMuxer, MediaCrypto, MediaDrm, Image, Surface和AudioTra…

Android视频编解码之MediaCodec简单入门

本篇只是简单入门&#xff0c;后面会继续写文章详细讲解&#xff1a; 由于MediaCodec涉及内容众多&#xff0c;原本想一篇文章把所有内容概括&#xff0c;但是后来发现不太可能&#xff0c;限于自己能力&#xff0c;想要考虑全面太难&#xff0c;我也是刚开始学习需要借助网上…

Android:MediaCodec基本原理

最近需要使用MediaCodec做一些工作&#xff0c;因此对MediaCodec做了些研究和代码编写&#xff0c;在此先对MediaCodec的一些基础原理、工作流程、常用API等做个初步总结&#xff0c;方便后续开发过程中查阅。 1.MediaCodec简介 1.1 MediaCodec来历 Android从最初的API 1.0版…

MediaCodec原理及使用

使用MediaCodec目的 MediaCodec是Android底层多媒体框架的一部分&#xff0c;通常与MediaExtractor、MediaMuxer、AudioTrack结合使用&#xff0c;可以编码H264、H265、AAC、3gp等常见的音视频格式 MediaCodec工作原理是处理输入数据以产生输出数据 MediaCodec工作流程 Medi…

Android MediaCodec

Android中可以使用MediaCodec来访问底层的媒体编解码器&#xff0c;可以对媒体进行编/解码。 MediaCodec可以处理的数据有以下三种类型&#xff1a;压缩数据、原始音频数据、原始视频数据。这三种类型的数据均可以利用ByteBuffers进行处理&#xff0c;但是对于原始视频数据应提…

android P MediaCodec编解码流程分析

1.MediaCodec初始化流程分析 通过上面流程分析可知&#xff0c;MediaCodecList初始化是通过调用它的getLocalInstance函数&#xff0c;然后在里面new的MediaCodecList对象。在MediaCodec的CreateByType函数中new了MediaCodec对象。 allocateNode调用流程是在ACodec.cpp中的onA…

MediaCodec_Analyze-3-start

MediaCodec Analyse – start Refrence: https://source.android.google.cn/devices/media 一 APK调用的核心API Android APK使用 MediaCodec API 播放音视频的简易流程&#xff1a; MediaCodec codec MediaCodec.createDecoderByType("video/avc"); MediaFormat …

mediacodec api

mediacodec api 官网&#xff1a; https://developer.android.com/reference/android/media/MediaCodec 中文&#xff1a; https://www.apiref.com/android-zh/android/media/MediaCodec.html 学习mediacodec api的使用说明&#xff0c;在android源码查看&#xff1a; andro…

MediaCodec硬解流程

一 MediaCodec概述 MediaCodec 是Android 4.1(api 16)版本引入的低层编解码接口&#xff0c;同时支持音视频的编码和解码。通常与MediaExtractor、MediaMuxer、AudioTrack结合使用&#xff0c;能够编解码诸如H.264、H.265、AAC、3gp等常见的音视频格式。MediaCodec在编解码的过…

MediaCodec_Analyze-1-create

MediaCodec Analyse – create Refrence: https://source.android.google.cn/devices/media 一 APK调用的核心API Android APK使用 MediaCodec API 播放音视频的简易流程&#xff1a; MediaCodec codec MediaCodec.createDecoderByType("video/avc"); MediaFormat…

mediacodec

MedaiCodec简介 MediaCodec是Android中提供的音视频编/解码工具。它主要是完成上层接口的封装&#xff0c;供给开发者使用&#xff0c;编解码功能实际是在native底层服务中完成的 MediaCodec工作流程 包括两个缓冲区队列 一个输入缓冲区队列&#xff0c;包含一组输入缓冲区(格…

Android MediaCodec 完全解析

MediaCodec是什么&#xff1f; MediaCodec类为开发者提供了能访问到Android底层媒体Codec&#xff08;Encoder/Decoder&#xff09;的能力&#xff0c;它是Android底层多媒体基础架构的一部分&#xff08;通常和MediaExtractor、MediaSync、MediaMuxer、MediaCrypto、MediaDrm…

Android MediaCodec解析

Android MediaCodec解析 1 引言 MediaCodec是Android平台提供的一个底层的音视频编解码框架&#xff0c;它是安卓底层多媒体基础框架的重要组成部分。它经常和 MediaExtractor, MediaSync, MediaMuxer, MediaCrypto, MediaDrm, Image, Surface, AudioTrack一起使用。解码的作…

Android原生编解码接口MediaCodec详解

作者&#xff1a;躬行之 了解了音视频的相关知识&#xff0c;可以先阅读同系列文章&#xff1a; 音视频开发基础知识音频帧、视频帧及其同步Camera2、MediaCodec录制mp4 MediaCodec 是 Android 中的编解码器组件&#xff0c;用来访问底层提供的编解码器&#xff0c;通常与 Me…

MediaCodec视频解码流程详解及参考demo

一、MediaCodec简介 MediaCodec是Android自带的底层多媒体支持架构的一部分&#xff08;通常与 MediaExtractor&#xff0c;MediaSync&#xff0c;MediaMuxer&#xff0c;MediaCrypto&#xff0c;MediaDrm&#xff0c;Image&#xff0c;Surface 和 AudioTrack 一起使用&#xf…

Android MediaCodec简单总结

#.MedaiCodec简介 MediaCodec是Android中提供的音视频编码、解码工具。它主要是完成上层接口的封装&#xff0c;提供给开发者使用&#xff0c;编解码功能实际是在native底层服务中完成的。 #.MediaCodec工作的宏观流程&#xff1a; ##.包换两个缓冲区队列 一个输入缓冲区队列&a…

软件测试面试指导之自我介绍

面试自我介绍虽然人人都准备&#xff0c;但是做到让人印象深刻可不容易啊。 本篇就具体来聊聊人人都要经历的面试&#xff0c;怎么做自我介绍&#xff0c;才能让你在面试官的眼睛里像金子一样闪闪发光&#xff1f; 面试是什么&#xff1f; 它是个机会&#xff0c;让面试官更…

软件测试面试要注意的细节以及处理(自我介绍篇)

面试问题第一问&#xff0c;95%都会是&#xff1a; 请简单的做个自我介绍吧~ 分以下几点说明。 一、个人的基本信息&#xff0c;扬长避短 1、年纪太大与太小&#xff0c;都不需要主动去说明。 比如我年纪只有21岁 例子&#xff1a;面试官您好&#xff0c;我叫***&#xff…

【软件测试】企业测试面试题9道,从自我介绍到项目考察+回答......

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、自我介绍 您好…