MediaCodec_Analyze-1-create

article/2025/10/6 19:11:26

MediaCodec Analyse – create

Refrence: https://source.android.google.cn/devices/media

Android 媒体架构

一 APK调用的核心API

Android APK使用 MediaCodec API 播放音视频的简易流程:

MediaCodec codec = MediaCodec.createDecoderByType("video/avc");
MediaFormat format = MediaFormat.createVideoFormat("video/avc", 320, 480);
codec.configure(format, surface, null, 0);
codec.start();

MediaCodec codec = MediaCodec.createDecoderByType("video/avc");

创建java-framework层的 MediaCodec 对象实例,并调用jni层的 native_setup(...) 函数。

frameworks\base\media\java\android\media\MediaCodec.java

    public static MediaCodec createDecoderByType(@NonNull String type) throws IOException {return new MediaCodec(type, true /* nameIsType */, false /* encoder */);}private MediaCodec(@NonNull String name, boolean nameIsType, boolean encoder) {......// looper、handler处理native_setup(name, nameIsType, encoder);}private native final void native_setup(@NonNull String name, boolean nameIsType, boolean encoder);

android_media_MediaCodec_native_setup(...)中new了一个JMediaCodec实例,然后检查返回结果。

frameworks\base\media\jni\android_media_MediaCodec.cpp

static void android_media_MediaCodec_native_setup(JNIEnv *env, jobject thiz,jstring name, jboolean nameIsType, jboolean encoder) {...... //变量判断等sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder);...... //mInitStatus,即create MediaCodec结果的判断setMediaCodec(env,thiz, codec); //强引用等操作,作用是?
}

通过MediaCodec::CreateByType(...)MediaCodec::CreateByComponentName(...)创建native层的MediaCodec实例。

frameworks\base\media\jni\android_media_MediaCodec.cpp

JMediaCodec::JMediaCodec(JNIEnv *env, jobject thiz,const char *name, bool nameIsType, bool encoder): mClass(NULL),mObject(NULL) {...... //mLooper等相关变量的处理if (nameIsType) {mCodec = MediaCodec::CreateByType(mLooper, name, encoder, &mInitStatus);if (mCodec == nullptr || mCodec->getName(&mNameAtCreation) != OK) {mNameAtCreation = "(null)";}} else {mCodec = MediaCodec::CreateByComponentName(mLooper, name, &mInitStatus);mNameAtCreation = name;}CHECK((mCodec != NULL) != (mInitStatus != OK));
}

mCodec = MediaCodec::CreateByType(mLooper, name, encoder, &mInitStatus);为例,在native层,通过MediaCodecList::findMatchingCodecs(...)查找出所有合适的编解码器,然后循环创建所有的编解码器实例,并通过codec->init(componentName)初始化相关编解码器实例,直到有一个编解码器的init返回OK,否则返回NULL。

frameworks\av\media\libstagefright\MediaCodec.cpp

sp<MediaCodec> MediaCodec::CreateByType(const sp<ALooper> &looper, const AString &mime, bool encoder, status_t *err, pid_t pid, uid_t uid) {Vector<AString> matchingCodecs;MediaCodecList::findMatchingCodecs(mime.c_str(),encoder,0,&matchingCodecs);if (err != NULL) {*err = NAME_NOT_FOUND;}for (size_t i = 0; i < matchingCodecs.size(); ++i) {sp<MediaCodec> codec = new MediaCodec(looper, pid, uid);AString componentName = matchingCodecs[i];status_t ret = codec->init(componentName);if (err != NULL) {*err = ret;}if (ret == OK) { //有一个成功就会退出循环,此时*err==OK,即上文的mInitStatus==OKreturn codec;}ALOGD("Allocating component '%s' failed (%d), try next one.", componentName.c_str(), ret);}return NULL;
}

实例化native层的MediaCodec时,会设置mState为UNINITIALIZED,要重点提一下。

frameworks\av\media\libstagefright\MediaCodec.cpp

MediaCodec::MediaCodec(const sp<ALooper> &looper, pid_t pid, uid_t uid): mState(UNINITIALIZED), //保存状态到mState中mReleasedByResourceManager(false),mLooper(looper),mCodec(NULL),mReplyID(0),mFlags(0),mStickyError(OK),mSoftRenderer(NULL),mIsVideo(false),mVideoWidth(0),mVideoHeight(0),mRotationDegrees(0),mDequeueInputTimeoutGeneration(0),mDequeueInputReplyID(0),mDequeueOutputTimeoutGeneration(0),mDequeueOutputReplyID(0),mTunneledInputWidth(0),mTunneledInputHeight(0),mTunneled(false),mHaveInputSurface(false),mHavePendingInputBuffers(false),mCpuBoostRequested(false),mLatencyUnknown(0),mNumLowLatencyEnables(0),mNumLowLatencyDisables(0),mIsLowLatencyModeOn(false),mIndexOfFirstFrameWhenLowLatencyOn(-1),mInputBufferCounter(0) {if (uid == kNoUid) {mUid = AIBinder_getCallingUid();} else {mUid = uid;}mResourceManagerProxy = new ResourceManagerServiceProxy(pid, mUid, ::ndk::SharedRefBase::make<ResourceManagerClient>(this));initMediametrics(); //初始化Media相关指标变量
}

MediaCodec::init(const AString &name)中,通过mCodec = GetCodecBase(localname, owner);创建具体的Codec,并保存到mCodec变量中。这里要注意一点,后文还有mCodec变量,遇到时要分清楚是属于哪个类的!

frameworks\av\media\libstagefright\MediaCodec.cpp

status_t MediaCodec::init(const AString &name) {mResourceManagerProxy->init();...... //相关变量的处理mCodec = GetCodecBase(localname, owner);if (mCodec == NULL) {return NAME_NOT_FOUND;}...... // looper和callback的处理sp<AMessage> msg = new AMessage(kWhatInit, this);...... //msg等变量的处理for (int i = 0; i <= kMaxRetry; ++i) {if (i > 0) {// Don't try to reclaim resource for the first time.if (!mResourceManagerProxy->reclaimResource(resources)) {break;}}sp<AMessage> response;err = PostAndAwaitResponse(msg, &response);if (!isResourceError(err)) {break;}}return err;
}

MediaCodec::GetCodecBase(...)中判断前缀决定使用哪种Codec,目前(22年5月)网络视频一般用ACodec,所以本文以此为例分析。

frameworks\av\media\libstagefright\MediaCodec.cpp

sp<CodecBase> MediaCodec::GetCodecBase(const AString &name, const char *owner) {if (owner) {if (strcmp(owner, "default") == 0) {return new ACodec;} else if (strncmp(owner, "codec2", 6) == 0) {return CreateCCodec();}}if (name.startsWithIgnoreCase("c2.")) {return CreateCCodec();} else if (name.startsWithIgnoreCase("omx.")) {// at this time only ACodec specifies a mime type.return new ACodec;} else if (name.startsWithIgnoreCase("android.filter.")) {return new MediaFilter;} else {return NULL;}
}

frameworks\av\media\libstagefright\ACodec.cpp

ACodec::ACodec(): mSampleRate(0),mNodeGeneration(0),mUsingNativeWindow(false),mNativeWindowUsageBits(0),mLastNativeWindowDataSpace(HAL_DATASPACE_UNKNOWN),mIsVideo(false),mIsImage(false),mIsEncoder(false),mFatalError(false),mShutdownInProgress(false),mExplicitShutdown(false),mIsLegacyVP9Decoder(false),mEncoderDelay(0),mEncoderPadding(0),mRotationDegrees(0),mChannelMaskPresent(false),mChannelMask(0),mDequeueCounter(0),mMetadataBuffersToSubmit(0),mNumUndequeuedBuffers(0),mRepeatFrameDelayUs(-1LL),mMaxPtsGapUs(0LL),mMaxFps(-1),mFps(-1.0),mCaptureFps(-1.0),mCreateInputBuffersSuspended(false),mTunneled(false),mDescribeColorAspectsIndex((OMX_INDEXTYPE)0),mDescribeHDRStaticInfoIndex((OMX_INDEXTYPE)0),mDescribeHDR10PlusInfoIndex((OMX_INDEXTYPE)0),mStateGeneration(0),mVendorExtensionsStatus(kExtensionsUnchecked) {memset(&mLastHDRStaticInfo, 0, sizeof(mLastHDRStaticInfo));mUninitializedState = new UninitializedState(this); //这里设置了ACodec的mCodec变量mLoadedState = new LoadedState(this);mLoadedToIdleState = new LoadedToIdleState(this);mIdleToExecutingState = new IdleToExecutingState(this);mExecutingState = new ExecutingState(this);mOutputPortSettingsChangedState = new OutputPortSettingsChangedState(this);mExecutingToIdleState = new ExecutingToIdleState(this);mIdleToLoadedState = new IdleToLoadedState(this);mFlushingState = new FlushingState(this);mPortEOS[kPortIndexInput] = mPortEOS[kPortIndexOutput] = false;mInputEOSResult = OK;mPortMode[kPortIndexInput] = IOMX::kPortModePresetByteBuffer;mPortMode[kPortIndexOutput] = IOMX::kPortModePresetByteBuffer;memset(&mLastNativeWindowCrop, 0, sizeof(mLastNativeWindowCrop));changeState(mUninitializedState);
}ACodec::UninitializedState::UninitializedState(ACodec *codec): BaseState(codec) {
}ACodec::BaseState::BaseState(ACodec *codec, const sp<AState> &parentState): AState(parentState),mCodec(codec) {
}

先说明一下MediaCodec::init(...)函数中的mCodec = GetCodecBase(localname, owner);,此条语句执行完,mCodec保存的就是ACodec实例了。继续分析MediaCodec::init(...)中的sp<AMessage> msg = new AMessage(kWhatInit, this);,通过AHandler-ALooper-AMessage架构发送了一条消息kWhatInit消息。在MediaCodec::onMessageReceived(...)case kWhatInit中设置了回调、状态、编解码器相关信息等,然后调用ACodec的initiateAllocateComponent(...)

frameworks\av\media\libstagefright\MediaCodec.cpp

void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {switch (msg->what()) {......case kWhatInit:{...... //设置回调、状态、编解码器相关信息mCodec->initiateAllocateComponent(format); //此处的mCodec为ACodec实例break;}......}
}

ACodec::initiateAllocateComponent(...)中通过发送kWhatAllocateComponent消息,调用ACodec::UninitializedState::onAllocateComponent(...),在其中通过OMXClient实例化omx对象,并调用其allocateNode函数分配相关节点。然后保存好omx对象和相关omx节点,并调用mCallback->onComponentAllocated(...)回调函数,最后更改状态。

frameworks\av\media\libstagefright\ACodec.cpp

void ACodec::initiateAllocateComponent(const sp<AMessage> &msg) {msg->setWhat(kWhatAllocateComponent);msg->setTarget(this);msg->post();
}bool ACodec::UninitializedState::onMessageReceived(const sp<AMessage> &msg) {bool handled = false;switch (msg->what()) {......case ACodec::kWhatAllocateComponent:{onAllocateComponent(msg);handled = true;break;}......}return handled;
}bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) {ALOGV("onAllocateComponent");...... //codecInfo、componentName等相关变量的处理sp<CodecObserver> observer = new CodecObserver(notify);sp<IOMX> omx;sp<IOMXNode> omxNode;status_t err = NAME_NOT_FOUND;OMXClient client;if (client.connect(owner.c_str()) != OK) {mCodec->signalError(OMX_ErrorUndefined, NO_INIT);return false;}omx = client.interface();pid_t tid = gettid();int prevPriority = androidGetThreadPriority(tid);androidSetThreadPriority(tid, ANDROID_PRIORITY_FOREGROUND);err = omx->allocateNode(componentName.c_str(), observer, &omxNode);androidSetThreadPriority(tid, prevPriority);if (err != OK) {ALOGE("Unable to instantiate codec '%s' with err %#x.", componentName.c_str(), err);mCodec->signalError((OMX_ERRORTYPE)err, makeNoSideEffectStatus(err));return false;}mDeathNotifier = new DeathNotifier(new AMessage(kWhatOMXDied, mCodec));auto tOmxNode = omxNode->getHalInterface<IOmxNode>();if (tOmxNode && !tOmxNode->linkToDeath(mDeathNotifier, 0)) {mDeathNotifier.clear();}++mCodec->mNodeGeneration;mCodec->mComponentName = componentName; //此处的mCodec是ACodec实例mCodec->mRenderTracker.setComponentName(componentName);mCodec->mFlags = 0;if (componentName.endsWith(".secure")) {mCodec->mFlags |= kFlagIsSecure;mCodec->mFlags |= kFlagIsGrallocUsageProtected;mCodec->mFlags |= kFlagPushBlankBuffersToNativeWindowOnShutdown;}mCodec->mOMX = omx;mCodec->mOMXNode = omxNode;mCodec->mCallback->onComponentAllocated(mCodec->mComponentName.c_str());mCodec->changeState(mCodec->mLoadedState);return true;
}

这里稍微分析下mCodec->mCallback->onComponentAllocated(mCodec->mComponentName.c_str());中的mCallback。在ACodec中找不到mCallback变量,那它在哪里?怎样回调的呢?

正向不好找,反向找。查找onComponentAllocated可以找到MediaCodec.cpp:517:void CodecCallback::onComponentAllocated(const char *componentName) {...},继续查找CodecCallback可以找到MediaCodec.cpp:446:class CodecCallback : public CodecBase::CodecCallback {...},简单推测ACodec中的mCallback也是在MediaCodec中初始化的,再加上继续往下找没有明显关联。

所以返回查找mCallback,可以找到MediaCodec.cpp:2898: mCallback = callback;CodecBase.h:243: std::unique_ptr<CodecCallback> mCallback;CodecBase.h:267: mCallback = std::move(callback);,通过下文的分析可知,ACodec中的mCallback变量是CodecBase中的,即,CodecBase.h:243: std::unique_ptr<CodecCallback> mCallback;。而CodecBase.h:267: mCallback = std::move(callback);CodecBase.h:211: inline void setCallback(std::unique_ptr<CodecCallback> &&callback) {...}函数中的语句。继续查找setCallback可以找到MediaCodec.cpp:1210: mCodec->setCallback((此处的mCodec指的是ACodec实例),查看code可以知道此处new了一个CodecCallback实例: mCodec->setCallback(std::unique_ptr<CodecBase::CodecCallback>(new CodecCallback(new AMessage(kWhatCodecNotify, this))));,是status_t MediaCodec::init(const AString &name) {...}函数中的语句。至此,和上面联系起来了。

另外,搜索h文件查找mCallback可以找到CodecBase.h:243: std::unique_ptr<CodecCallback> mCallback;,再找到ACodecCodecBase的关系,应该就结束了。再ACodec.cpp文件中可以看到类定义ACodec.h:60:struct ACodec : public AHierarchicalStateMachine, public CodecBase {...}。所以可知,ACodec继承自CodecBase,所以也继承了CodecBase中的std::unique_ptr<CodecCallback> mCallback;变量。

因此,对ACodec中的mCodec->mCallback->onComponentAllocated(mCodec->mComponentName.c_str());分析为:在CodecBase.h中定义了std::unique_ptr<CodecCallback> mCallback;变量,ACodec继承过来了。等APK调用到native层的MediaCodec::init(...)函数时,会将通过mCodec->setCallback(...)也就是CodecBase中的inline void setCallback(...)函数将其赋值。之后就可以调用MediaCodec.cpp:517:void CodecCallback::onComponentAllocated(const char *componentName) {...}函数了。


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

相关文章

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、自我介绍 您好…

软件测试面试怎样介绍自己的项目?会问到什么程度?

最近收到很多粉丝的私信说找不到工作&#xff0c;简历投了百十来份&#xff0c;邀约都没几个&#xff0c;更别说offer了&#xff0c;是不是软件测试要黄了&#xff1f; 说句实话&#xff0c;现在大环境确实不好&#xff0c;互联网大厂裁员这是摆在明面上的原因。时代的一粒沙&…

软件测试面试怎样介绍自己的测试项目?会问到什么程度?

想知道面试时该怎样介绍测试项目&#xff1f;会问到什么程度&#xff1f;那就需要换位思考&#xff0c;思考HR在这个环节想知道什么。 HR在该环节普遍想获得的情报主要是下面这2个方面&#xff1a; 1&#xff09;应聘者的具体经验和技术能力&#xff0c; 2&#xff09;应聘者的…

【软件测试】面试中介绍项目你该这么说!

黑马程序员视频库 播妞微信号&#xff1a;heiniu526 传智播客旗下互联网资讯、学习资源免费分享平台 测试人员在找工作的过程中&#xff0c;通常有一个问题是很难绕开的。就是要如何向别人介绍自己之前做过的项目。下面我们就这个问题简单的做一些分析。 大体上可以分为如下几个…

软件测试面试自我介绍/项目介绍居然还有模板?我要是早点发现就好了

目录 1、自我介绍 2、项目介绍 2.1、最全电商项目介绍 2.2、电商项目介绍 2.3、在线教育项目介绍 2.4、互联网金融项目介绍 总结 1、自我介绍 以XXX简历来举例&#xff08;参照下面的案例&#xff0c;编写你的自我介绍&#xff0c;框架就是&#xff1a;我是谁&#xff0…

软件测试面试,如何自我介绍?如何介绍项目?如何介绍个人技术?(提供面试话术)

前准备不足而导致面试失败那可就亏大了&#xff01;为了提高面试成功率&#xff0c;帮助大家尽快拿到高薪offer&#xff0c;我们盘点了面试环节必问的三类问题&#xff0c;希望对即将参加软件测试面试的小伙伴们有所帮助。 01 如何自我介绍 面试过程中一定要放慢语速&#xf…

软件测试面试,如何自我介绍?

01 如何自我介绍 面试过程中一定要放慢语速&#xff0c;做到条理清晰。特别是做自我介绍时&#xff0c;可以适当多介绍自己会什么&#xff0c;有哪些重要经验。 例如&#xff1a; 面试官&#xff0c;上午/下午好。 我是XXX&#xff0c;今天来面试贵公司的软件测试工程师岗位&a…

软件测试工程师面试如何做好自我介绍?

听了很多提问者和我的学生&#xff0c;在做自我自我介绍的时候&#xff0c;一般存在的问题&#xff1a; 1、表述不太流畅。多练习表述&#xff0c;自己录音&#xff0c;听回放&#xff0c;有问题改正。 2、表述太溜&#xff0c;语速太快。不自信表现&#xff0c;隐含紧张的情绪…

软件测试三分钟自我介绍

目录 一、个人的基本信息&#xff0c;扬长避短 二、突出自己的工作经验 三、突出自己的技能 四、个人兴趣爱好与结尾 自我介绍在面试中常常作为第一个问题而出现&#xff0c;好的自我介绍可以带来良好的第一印象&#xff0c;如何讲好自我介绍呢。我建议从三部分下手 一、个…

软件测试面试指导之自我介绍 (干货)

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

软件测试面试必考题:自我介绍

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

软件测试工程师自我介绍(范本)

一、先介绍自己的基本信息&#xff0c;要注意扬长避短 1、年纪太大与太小&#xff0c;就不要主动去说明。 比如你的年纪只有20岁 例子&#xff1a;我叫***&#xff0c;从事软件测试工作有几年了。 2、不是计算机相关专业毕业的也不要过多的去提 比如你的专业是机械专业 例…