【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 4】【01】
2021/5/14 20:55:23
本文主要是介绍【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 4】【01】,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
承接上一章节分析:【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 3】
本系列文章分析的安卓源码版本:【Android 10.0 版本】
【此章节小节编号将重新排序】
(*decoder)->configure(format)实现分析:
解码器配置工作。该方法也是在父类中声明和实现的,如下
注意:此方法执行完毕后,第10.1小节NuPlayer的执行将会和此处后续流程是异步同时执行的!因为它们已经属于两个不同的消息循环线程中执行了。
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp] void NuPlayer::DecoderBase::configure(const sp<AMessage> &format) { // 也就是给自己的消息循环线程发送【kWhatConfigure】事件消息接收处理 sp<AMessage> msg = new AMessage(kWhatConfigure, this); msg->setMessage("format", format); msg->post(); }
【kWhatConfigure】事件消息接收处理:
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp] void NuPlayer::DecoderBase::onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { case kWhatConfigure: { sp<AMessage> format; CHECK(msg->findMessage("format", &format)); // 见下面的分析 onConfigure(format); break; } } }
onConfigure(format)实现分析:
解码器配置工作,创建MediaCodec处理流程。
关于下面分析的【MediaCodec::CreateByType】和【MediaCodec::CreateByComponentName】两种方式创建MediaCodec的处理流程请查看如下系列章节分析:【该流程处理是非常复杂的,需耐心掌握】
Android MediaPlayer整体架构源码分析 -【MediaCodec编解码器插件模块化注册和创建处理流程】
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp] void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) { CHECK(mCodec == NULL); mFormatChangePending = false; mTimeChangePending = false; // 缓冲Buffer操作代数值加1 ++mBufferGeneration; // 获取当前音频或视频track的编码格式 AString mime; CHECK(format->findString("mime", &mime)); // 是否为音频 mIsAudio = !strncasecmp("audio/", mime.c_str(), 6); // 是否为AVC即h264编码的视频格式 // MEDIA_MIMETYPE_VIDEO_AVC:"video/avc" mIsVideoAVC = !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime.c_str()); mComponentName = mime; // mime格式字符串后面添加 " decoder" 字符串 mComponentName.append(" decoder"); ALOGV("[%s] onConfigure (surface=%p)", mComponentName.c_str(), mSurface.get()); // 此处我们默认为空实现,关于AVUtils的实现原理此前章节中已有分析过,不再阐述,就是厂商可自定义编解码器so库实现 mCodec = AVUtils::get()->createCustomComponentByName(mCodecLooper, mime.c_str(), false /* encoder */, format); if (mCodec == NULL) { // 创建MediaCodec,见上面推荐章节分析 mCodec = MediaCodec::CreateByType( mCodecLooper, mime.c_str(), false /* encoder */, NULL /* err */, mPid, mUid); } // 此次处理安全编解码器流程,【CreateByComponentName】见相关章节分析 int32_t secure = 0; if (format->findInt32("secure", &secure) && secure != 0) { if (mCodec != NULL) { mCodec->getName(&mComponentName); mComponentName.append(".secure"); mCodec->release(); ALOGI("[%s] creating", mComponentName.c_str()); mCodec = MediaCodec::CreateByComponentName( mCodecLooper, mComponentName.c_str(), NULL /* err */, mPid, mUid); } } if (mCodec == NULL) { ALOGE("Failed to create %s%s decoder", (secure ? "secure " : ""), mime.c_str()); // 编解码器创建失败,发送未知错误码 // 见第1小节分析 handleError(UNKNOWN_ERROR); return; } mIsSecure = secure; // 获取(插件编解码器)组件名 // 见第2小节分析 mCodec->getName(&mComponentName); status_t err; if (mSurface != NULL) { // disconnect from surface as MediaCodec will reconnect // 如英文注释:断开native window,后续MediaCodec将会重连接 // 该方法分析见前面第四章节中的分析 err = nativeWindowDisconnect(mSurface.get(), "onConfigure"); // We treat this as a warning, as this is a preparatory step. // Codec will try to connect to the surface, which is where // any error signaling will occur. ALOGW_IF(err != OK, "failed to disconnect from surface: %d", err); } // 通常非加密源, pCrypto 为null // Modular DRM void *pCrypto; if (!format->findPointer("crypto", &pCrypto)) { pCrypto = NULL; } sp<ICrypto> crypto = (ICrypto*)pCrypto; // non-encrypted source won't have a crypto mIsEncrypted = (crypto != NULL); // configure is called once; still using OR in case the behavior changes. mIsEncryptedObservedEarlier = mIsEncryptedObservedEarlier || mIsEncrypted; ALOGV("onConfigure mCrypto: %p (%d) mIsSecure: %d", crypto.get(), (crypto != NULL ? crypto->getStrongCount() : 0), mIsSecure); // 配置工作 // 见第3小节分析 err = mCodec->configure( format, mSurface, crypto, 0 /* flags */); if (err != OK) { // 失败将会执行底层Codec的释放清除工作,并上报上层APP该错误事件。 // 备注:目前暂不分析错误处理流程的实现。TODO ALOGE("Failed to configure [%s] decoder (err=%d)", mComponentName.c_str(), err); mCodec->release(); mCodec.clear(); handleError(err); return; } // 记录编解码器特殊数据,CSD其实对应Codec Specific Data的缩写。 // 见第4小节分析 rememberCodecSpecificData(format); // 获取底层(输入输出buffer)格式信息,并且必须返回状态都为OK // 获取输出buffer格式信息,见第5小节分析 // 获取输入buffer格式信息,见第6小节分析 // the following should work in configured state CHECK_EQ((status_t)OK, mCodec->getOutputFormat(&mOutputFormat)); CHECK_EQ((status_t)OK, mCodec->getInputFormat(&mInputFormat)); // 加锁代码块,缓存值在媒体统计数据项中 { Mutex::Autolock autolock(mStatsLock); mStats->setString("mime", mime.c_str()); mStats->setString("component-name", mComponentName.c_str()); } if (!mIsAudio) { // 若非音频编解码器 int32_t width, height; // 获取图像或视频编解码器输出视频宽高 if (mOutputFormat->findInt32("width", &width) && mOutputFormat->findInt32("height", &height)) { // 都获取成功时,加锁,缓存这两个值 Mutex::Autolock autolock(mStatsLock); mStats->setInt32("width", width); mStats->setInt32("height", height); } } sp<AMessage> reply = new AMessage(kWhatCodecNotify, this); // 设置一个接收应答数据的应答事件【kWhatCodecNotify】消息对象作为一个callback回调监听 // 见第7小节分析 mCodec->setCallback(reply); // 启动底层编解码器组件工作 // 见第8小节分析 err = mCodec->start(); if (err != OK) { // 失败将会执行底层Codec的释放清除工作,并上报上层APP该错误事件。 // 备注:目前暂不分析错误处理流程的实现。TODO ALOGE("Failed to start [%s] decoder (err=%d)", mComponentName.c_str(), err); mCodec->release(); mCodec.clear(); handleError(err); return; } // 释放并重置媒体buffer缓冲区 // 见第9小节分析 releaseAndResetMediaBuffers(); // 状态修改 mPaused = false; mResumePending = false; }
1、handleError(UNKNOWN_ERROR)实现分析:
编解码器创建失败,发送未知错误码
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp] void NuPlayer::Decoder::handleError(int32_t err) { // We cannot immediately release the codec due to buffers still outstanding // in the renderer. We signal to the player the error so it can shutdown/release the // decoder after flushing and increment the generation to discard unnecessary messages. // 再次增加buffer代数值 ++mBufferGeneration; // mNotify消息即为前面分析的【kWhatVideoNotify】事件通知消息,用于视频解码器通知NuPlayer解码器状态等 sp<AMessage> notify = mNotify->dup(); // 添加子事件类型和错误码 notify->setInt32("what", kWhatError); notify->setInt32("err", err); notify->post(); }
【kWhatVideoNotify】事件通知消息处理:
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp] void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { case kWhatVideoNotify: // TODO 注意:目前开始播放流程我们只需要关注正常处理播放流程分析, // 因此关于正常播放前的异常处理只会简单提醒下,因此暂不先分析。 // 另外还有一点原因就是异常时的处理比较复杂,判断非常多的条件来做不同处理。 // 只需要知道每种错误都有对应处理并且通常会通知上层APP该错误事件。 } }
2、mCodec->getName(&mComponentName)实现分析:
获取(插件编解码器)组件名,根据下面实现可知,也就是MediaCodec自身接收【kWhatGetName】事件消息,并且是同步线程执行,成功则取出该组件名。
关于ALooper实现原理请查看另外的分析章节。
// [frameworks/av/media/libstagefright/MediaCodec.cpp] status_t MediaCodec::getName(AString *name) const { sp<AMessage> msg = new AMessage(kWhatGetName, this); sp<AMessage> response; status_t err; if ((err = PostAndAwaitResponse(msg, &response)) != OK) { return err; } CHECK(response->findString("name", name)); return OK; }
MediaCodec自身接收【kWhatGetName】事件消息处理:
如下处理很简单,就是将 MediaCodec前面获取到的底层组件名【mComponentName】返回给NuPlayerDecoder缓存。
// [frameworks/av/media/libstagefright/MediaCodec.cpp] void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { case kWhatGetName: { sp<AReplyToken> replyID; CHECK(msg->senderAwaitsResponse(&replyID)); if (mComponentName.empty()) { PostReplyWithError(replyID, INVALID_OPERATION); break; } sp<AMessage> response = new AMessage; response->setString("name", mComponentName.c_str()); response->postReply(replyID); break; } } }
3、mCodec->configure(format, mSurface, crypto, 0 /* flags */)实现分析:
根据输入格式信息和Surface完成MediaCodec配置工作
// [frameworks/av/media/libstagefright/MediaCodec.cpp] status_t MediaCodec::configure( const sp<AMessage> &format, const sp<Surface> &nativeWindow, const sp<ICrypto> &crypto, uint32_t flags) { return configure(format, nativeWindow, crypto, NULL, flags); } // [frameworks/av/media/libstagefright/MediaCodec.cpp] status_t MediaCodec::configure( const sp<AMessage> &format, const sp<Surface> &surface, const sp<ICrypto> &crypto, const sp<IDescrambler> &descrambler, uint32_t flags) { // 【kWhatConfigure】事件消息 sp<AMessage> msg = new AMessage(kWhatConfigure, this); if (mAnalyticsItem != NULL) { int32_t profile = 0; // 统计缓存当编解码器标准档次信息 if (format->findInt32("profile", &profile)) { mAnalyticsItem->setInt32(kCodecProfile, profile); } int32_t level = 0; // 统计缓存当编解码器标准级别信息 if (format->findInt32("level", &level)) { mAnalyticsItem->setInt32(kCodecLevel, level); } // 解码器时该值为0 mAnalyticsItem->setInt32(kCodecEncoder, (flags & CONFIGURE_FLAG_ENCODE) ? 1 : 0); } if (mIsVideo) { // 视频编解码器时 // 获取输出格式信息宽高 format->findInt32("width", &mVideoWidth); format->findInt32("height", &mVideoHeight); // 获取输入格式中视频旋转角度值,若获取失败则默认为 0 if (!format->findInt32("rotation-degrees", &mRotationDegrees)) { mRotationDegrees = 0; } // 缓存这些值并再次获取最大最小输入宽高值到媒体统计信息数据项中 if (mAnalyticsItem != NULL) { mAnalyticsItem->setInt32(kCodecWidth, mVideoWidth); mAnalyticsItem->setInt32(kCodecHeight, mVideoHeight); mAnalyticsItem->setInt32(kCodecRotation, mRotationDegrees); int32_t maxWidth = 0; if (format->findInt32("max-width", &maxWidth)) { mAnalyticsItem->setInt32(kCodecMaxWidth, maxWidth); } int32_t maxHeight = 0; if (format->findInt32("max-height", &maxHeight)) { mAnalyticsItem->setInt32(kCodecMaxHeight, maxHeight); } } // 检查(输入格式宽高值【初始化时为0】)整型溢出 // Prevent possible integer overflow in downstream code. if (mVideoWidth < 0 || mVideoHeight < 0 || (uint64_t)mVideoWidth * mVideoHeight > (uint64_t)INT32_MAX / 4) { ALOGE("Invalid size(s), width=%d, height=%d", mVideoWidth, mVideoHeight); return BAD_VALUE; } } // 设置消息参数值 msg->setMessage("format", format); msg->setInt32("flags", flags); msg->setObject("surface", surface); if (flags & CONFIGURE_FLAG_ENCODE) { msg->setInt32("encoder", 1); } // 均为null,不关注加密源分析 if (crypto != NULL || descrambler != NULL) { if (crypto != NULL) { msg->setPointer("crypto", crypto.get()); } else { msg->setPointer("descrambler", descrambler.get()); } if (mAnalyticsItem != NULL) { mAnalyticsItem->setInt32(kCodecCrypto, 1); } } else if (mFlags & kFlagIsSecure) { ALOGW("Crypto or descrambler should be given for secure codec"); } // 全局变量缓存该事件信息配置对象,用于reset流程时清除 // save msg for reset mConfigureMsg = msg; status_t err; // 见前面章节相关流程已有分析,处理媒体资源类型信息 Vector<MediaResource> resources; MediaResource::Type type = (mFlags & kFlagIsSecure) ? MediaResource::kSecureCodec : MediaResource::kNonSecureCodec; MediaResource::SubType subtype = mIsVideo ? MediaResource::kVideoCodec : MediaResource::kAudioCodec; resources.push_back(MediaResource(type, subtype, 1)); // Don't know the buffer size at this point, but it's fine to use 1 because // the reclaimResource call doesn't consider the requester's buffer size for now. // 第3个参数如英文注释表示 buffer 大小,但是该值目前未使用 resources.push_back(MediaResource(MediaResource::kGraphicMemory, 1)); // 循环尝试处理流程,该处理流程见前面类似(init流程中)处理,因此不再描述 for (int i = 0; i <= kMaxRetry; ++i) { if (i > 0) { // Don't try to reclaim resource for the first time. if (!mResourceManagerService->reclaimResource(resources)) { break; } } sp<AMessage> response; // 同步发送该配置【kWhatConfigure】事件消息并等待应答 // 见下面的分析 err = PostAndAwaitResponse(msg, &response); if (err != OK && err != INVALID_OPERATION) { // 失败时并且非无效操作失败,则必须reset操作。无效操作错误码表示配置失败原因是状态错误,因此此时不能reset // 备注:目前暂不分析错误处理流程的实现。TODO // MediaCodec now set state to UNINITIALIZED upon any fatal error. // To maintain backward-compatibility, do a reset() to put codec // back into INITIALIZED state. // But don't reset if the err is INVALID_OPERATION, which means // the configure failure is due to wrong state. ALOGE("configure failed with err 0x%08x, resetting...", err); reset(); } if (!isResourceError(err)) { break; } } return err; }
配置【kWhatConfigure】事件消息接收处理:
// [frameworks/av/media/libstagefright/MediaCodec.cpp] void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { 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)); // 获取输入配置项中是否允许在shutdown时推送空数据buffer int32_t push; if (msg->findInt32("push-blank-buffers-on-shutdown", &push) && push != 0) { mFlags |= kFlagPushBlankBuffersOnShutdown; } if (obj != NULL) { // Surface不为空时 // 判断是否设置了是否允许丢帧渲染参数标记值 if (!format->findInt32("allow-frame-drop", &mAllowFrameDroppingBySurface)) { // 获取失败则默认允许丢帧处理 // allow frame dropping by surface by default mAllowFrameDroppingBySurface = true; } // 设置surface参数给输入格式format信息 format->setObject("native-window", obj); // 设置surface处理流程,该方法此前流程中已分析过,请参考此前分析 status_t err = handleSetSurface(static_cast<Surface *>(obj.get())); if (err != OK) { // 失败应答 PostReplyWithError(replyID, err); break; } } else { // 无surface时 设置为false,并设置surface为null,主要就是断开此前已有surface的连接 // we are not using surface so this variable is not used, but initialize sensibly anyway mAllowFrameDroppingBySurface = false; handleSetSurface(NULL); } // 全局变量缓存应答消息对象,用于后续处理完成后进行应答 mReplyID = replyID; // 设置当前MediaCodec状态为正在配置中,见前面相关分析 setState(CONFIGURING); // 只关注非加密源,因此该参数为null void *crypto; if (!msg->findPointer("crypto", &crypto)) { crypto = NULL; } ALOGV("kWhatConfigure: Old mCrypto: %p (%d)", mCrypto.get(), (mCrypto != NULL ? mCrypto->getStrongCount() : 0)); mCrypto = static_cast<ICrypto *>(crypto); mBufferChannel->setCrypto(mCrypto); ALOGV("kWhatConfigure: New mCrypto: %p (%d)", mCrypto.get(), (mCrypto != NULL ? mCrypto->getStrongCount() : 0)); // 也为null的情况不分析 void *descrambler; if (!msg->findPointer("descrambler", &descrambler)) { descrambler = NULL; } mDescrambler = static_cast<IDescrambler *>(descrambler); mBufferChannel->setDescrambler(mDescrambler); // 获取flag(传入参数为0) uint32_t flags; CHECK(msg->findInt32("flags", (int32_t *)&flags)); // 此处判断是否为编码器处理 if (flags & CONFIGURE_FLAG_ENCODE) { format->setInt32("encoder", true); mFlags |= kFlagIsEncoder; } // 提取CSD编解码器特定数据 // 见3.1小节分析 extractCSD(format); // 初始化配置组件 // 见3.2小节分析 mCodec->initiateConfigureComponent(format); break; } } }
3.1、extractCSD(format)实现分析:
提取CSD编解码器特定数据
// [frameworks/av/media/libstagefright/MediaCodec.cpp] void MediaCodec::extractCSD(const sp<AMessage> &format) { // 关于ABuffer的详细实现分析,见前面prepare分析章节 // List<sp<ABuffer> > mCSD; mCSD.clear(); size_t i = 0; // 循环缓存csd类型数据 for (;;) { sp<ABuffer> csd; // AStringPrintf是格式化字符串功能 if (!format->findBuffer(AStringPrintf("csd-%u", i).c_str(), &csd)) { // 失败则退出 break; } if (csd->size() == 0) { // 获取到该值可惜其可读数据长度为空,但仍然放入列表中缓存 ALOGW("csd-%zu size is 0", i); } mCSD.push_back(csd); ++i; } ALOGV("Found %zu pieces of codec specific data.", mCSD.size()); }
3.2、mCodec->initiateConfigureComponent(format)实现分析:
初始化配置组件。
// [frameworks/av/media/libstagefright/ACodec.cpp] void ACodec::initiateConfigureComponent(const sp<AMessage> &msg) { // 发送给自己该事件消息处理 msg->setWhat(kWhatConfigureComponent); msg->setTarget(this); msg->post(); }
kWhatConfigureComponent 事件消息处理:
// [frameworks/av/media/libstagefright/ACodec.cpp] bool ACodec::LoadedState::onMessageReceived(const sp<AMessage> &msg) { bool handled = false; switch (msg->what()) { case ACodec::kWhatConfigureComponent: { onConfigureComponent(msg); handled = true; break; } } }
onConfigureComponent(msg)实现分析:
// [frameworks/av/media/libstagefright/ACodec.cpp] bool ACodec::LoadedState::onConfigureComponent( const sp<AMessage> &msg) { ALOGV("onConfigureComponent"); CHECK(mCodec->mOMXNode != NULL); status_t err = OK; AString mime; // 获取输入格式mime信息 if (!msg->findString("mime", &mime)) { err = BAD_VALUE; } else { // 成功时 // 执行ACodec的配置编解码器方法 // 见3.2.1小节分析 err = mCodec->configureCodec(mime.c_str(), msg); } if (err != OK) { // 失败时通知外层(MediaCodec)该错误事件,见前面相关章节的该方法分析 ALOGE("[%s] configureCodec returning error %d", mCodec->mComponentName.c_str(), err); mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err)); return false; } // 回调通知MediaCodec底层编解码器配置工作完成事件 // 见3.2.2小节分析 mCodec->mCallback->onComponentConfigured(mCodec->mInputFormat, mCodec->mOutputFormat); // 返回true return true; }
3.2.1、mCodec->configureCodec(mime.c_str(), msg)实现分析:
ACodec配置编解码器
由于该部分分析较长,因此单独拎出后续章节分析,请查看:
【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 5】
TODO
3.2.2、mCodec->mCallback->onComponentConfigured(mCodec->mInputFormat, mCodec->mOutputFormat)实现分析:
回调通知MediaCodec底层编解码器配置工作完成事件,此处的输入输出格式信息就是 3.2.1 小节流程中获得到的最终编解码器输入输出(buffer)格式配置信息,该mCallback由此前的流程分析可知,是MediaCodec中实现的,因此直接定位到该实现方法,如下
// [frameworks/av/media/libstagefright/MediaCodec.cpp] void CodecCallback::onComponentConfigured( const sp<AMessage> &inputFormat, const sp<AMessage> &outputFormat) { // 完全复制mNotify消息即为前面分析的【kWhatCodecNotify】事件通知消息,MediaCodec自身接收处理 sp<AMessage> notify(mNotify->dup()); // 添加子事件类型和输入输出buffer格式参数信息 notify->setInt32("what", kWhatComponentConfigured); notify->setMessage("input-format", inputFormat); notify->setMessage("output-format", outputFormat); // 立即发送该事件 notify->post(); }
MediaCodec接收处理【kWhatCodecNotify】事件:
void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { case kWhatCodecNotify: { // 获取该事件下的子事件类型 int32_t what; CHECK(msg->findInt32("what", &what)); switch (what) { case kWhatComponentConfigured: { // 根据注释可知,若此时状态为这三种状态时,表明此前流程异步配置编解码器时MediaCodec中的状态 // 被(用户)改变了,此时不应该再次处理消息应答流程 if (mState == RELEASING || mState == UNINITIALIZED || mState == INITIALIZED) { // In case a kWhatError or kWhatRelease message came in and replied, // we log a warning and ignore. ALOGW("configure interrupted by error or release, current state %d", mState); break; } // 异步完成后此时状态必须为配置中才正确 CHECK_EQ(mState, CONFIGURING); // reset input surface flag mHaveInputSurface = false; // 获取编解码器输出输入格式信息 CHECK(msg->findMessage("input-format", &mInputFormat)); CHECK(msg->findMessage("output-format", &mOutputFormat)); // 根据此前流程分析,mAllowFrameDroppingBySurface 默认为true即允许surface丢帧处理 // limit to confirming the opt-in behavior to minimize any behavioral change if (mSurface != nullptr && !mAllowFrameDroppingBySurface) { // signal frame dropping mode in the input format as this may also be // meaningful and confusing for an encoder in a transcoder scenario mInputFormat->setInt32("allow-frame-drop", mAllowFrameDroppingBySurface); } ALOGV("[%s] configured as input format: %s, output format: %s", mComponentName.c_str(), mInputFormat->debugString(4).c_str(), mOutputFormat->debugString(4).c_str()); int32_t usingSwRenderer; // 根据此前流程分析,可知在举例SoftAVCDec解码器组件时该参数为1 即 使用软渲染器 if (mOutputFormat->findInt32("using-sw-renderer", &usingSwRenderer) && usingSwRenderer) { // 添加该标志位 mFlags |= kFlagUsesSoftwareRenderer; } // 设置已完成配置状态,见前面已有分析 setState(CONFIGURED); // 应答前面该流程开始时全局缓存的应答信息对象 // 备注:此处应答的是一个空消息对象,也就是不返回任何可用消息 (new AMessage)->postReply(mReplyID); // augment our media metrics info, now that we know more things if (mAnalyticsItem != NULL) { // 媒体统计记录信息项启用时获取配置信息对象中的mime类型数据并设置给该统计对象对应字段中 // 备注:注意该信息项可关闭,通过在【MediaAnalyticsItem.cpp】中设置【EnabledProperty_default】为-1,默认为1开启记录 sp<AMessage> format; if (mConfigureMsg != NULL && mConfigureMsg->findMessage("format", &format)) { // format includes: mime AString mime; if (format->findString("mime", &mime)) { mAnalyticsItem->setCString(kCodecMime, mime.c_str()); } } } break; } } } } }
4、rememberCodecSpecificData(format)实现分析:
读取缓存编解码器特殊数据,CSD其实对应Codec Specific Data的缩写。
对于H.264来说,"csd-0"和"csd-1"分别对应sps和pps;对于AAC来说,"csd-0"对应ADTS。
由于本章节接下来内容篇幅过长,因此必须放入另一章节分析,请查看:
TODO 【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 4】【01】
这篇关于【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 4】【01】的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-01-18android.permission.read_media_video
- 2024-01-18android_getaddrinfo failed eai_nodata
- 2024-01-18androidmo
- 2024-01-15Android下三种离屏渲染技术
- 2024-01-09Android 蓝牙使用
- 2024-01-06Android对接华为AI - 文本识别
- 2023-11-15代码安全之代码混淆及加固(Android)
- 2023-11-10简述Android语音播报TTS
- 2023-11-06Android WiFi工具类
- 2023-07-22Android开发未来的出路