首页 > 代码库 > MediaPlayer本地播放流程解析(二)
MediaPlayer本地播放流程解析(二)
上一篇MediaPlayer本地播放流程解析(一)讲了MediaPlayer的setDataSource流程,本篇将接着讲MediaPlayer的prepare流程。
Prepare前面的流程一直到AwesomePlayer,和setDataSource都基本上一样,这里直接略掉。下面将从AwesomePlayer开始。
status_t AwesomePlayer::prepare() { ATRACE_CALL(); Mutex::Autolock autoLock(mLock); return prepare_l(); } status_t AwesomePlayer::prepare_l() { if (mFlags & PREPARED) { return OK; } if (mFlags & PREPARING) { return UNKNOWN_ERROR; } mIsAsyncPrepare = false; status_t err = prepareAsync_l(); if (err != OK) { return err; } while (mFlags & PREPARING) { mPreparedCondition.wait(mLock); } return mPrepareResult; } status_t AwesomePlayer::prepareAsync_l() { if (mFlags & PREPARING) { return UNKNOWN_ERROR; // async prepare already pending } if (!mQueueStarted) { mQueue.start(); mQueueStarted = true; } modifyFlags(PREPARING, SET); mAsyncPrepareEvent = new AwesomeEvent( this, &AwesomePlayer::onPrepareAsyncEvent); mQueue.postEvent(mAsyncPrepareEvent); return OK; }这里涉及到一个时间事件队列模型,下一篇来详细分析之,这里我们只要知道mQueue.postEvent(mAsyncPrepareEvent)后,onPrepareAsyncEvent方法将会被执行。接着看onPrepareAsyncEvent方法的实现。
void AwesomePlayer::onPrepareAsyncEvent() { Mutex::Autolock autoLock(mLock); beginPrepareAsync_l(); } void AwesomePlayer::beginPrepareAsync_l() { if (mFlags & PREPARE_CANCELLED) { ALOGI("prepare was cancelled before doing anything"); abortPrepare(UNKNOWN_ERROR); return; } if (mUri.size() > 0) { status_t err = finishSetDataSource_l(); if (err != OK) { abortPrepare(err); return; } } if (mVideoTrack != NULL && mVideoSource == NULL) { status_t err = initVideoDecoder(); if (err != OK) { abortPrepare(err); return; } } if (mAudioTrack != NULL && mAudioSource == NULL) { status_t err = initAudioDecoder(); if (err != OK) { abortPrepare(err); return; } } modifyFlags(PREPARING_CONNECTED, SET); if (isStreamingHTTP()) { postBufferingEvent_l(); } else { finishAsyncPrepare_l(); } }initVideoDecoder和initAudioDecoder实现差不多,这里只讲initVideoDecoder的实现。
status_t AwesomePlayer::initVideoDecoder(uint32_t flags) { …… mVideoSource = OMXCodec::Create( mClient.interface(), mVideoTrack->getFormat(), false, // createEncoder mVideoTrack, NULL, flags, USE_SURFACE_ALLOC ? mNativeWindow : NULL); if (mVideoSource != NULL) { int64_t durationUs; if (mVideoTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) { Mutex::Autolock autoLock(mMiscStateLock); if (mDurationUs < 0 || durationUs > mDurationUs) { mDurationUs = durationUs; } } status_t err = mVideoSource->start(); if (err != OK) { ALOGE("failed to start video source"); mVideoSource.clear(); return err; } } …… return mVideoSource != NULL ? OK : UNKNOWN_ERROR; }
AwesomePlayer通过OMXCodec与openMax交互。硬件厂家提供.so库,实现openMax的接口,即可被AwesomePlayer调用,实现硬解码,加速性能。
OMXCodec::Create 的第一个参数mClient.interface()返回什么呢?
1、 mClient在AwesomePlayer构造时初始化:mClient.connect()。
2、 mClient.interface()在OMXClient.h中定义,返回mOMX,一个BpOMX类型的对象。
初始化connect看下面的代码:status_t OMXClient::connect() { sp<IServiceManager> sm = defaultServiceManager(); sp<IBinder> binder = sm->getService(String16("media.player")); sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder); CHECK(service.get() != NULL); mOMX = service->getOMX(); CHECK(mOMX.get() != NULL); if (!mOMX->livesLocally(NULL /* node */, getpid())) { ALOGI("Using client-side OMX mux."); mOMX = new MuxOMX(mOMX); } return OK; } virtual sp<IOMX> getOMX() { Parcel data, reply; data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); remote()->transact(GET_OMX, data, &reply); return interface_cast<IOMX>(reply.readStrongBinder()); } sp<IOMX> MediaPlayerService::getOMX() { Mutex::Autolock autoLock(mLock); if (mOMX.get() == NULL) { mOMX = new OMX; } return mOMX; }继续看OMXCodec::Create的实现,在Create中将打开AwesomePlayer和openMax交互的通路。
sp<MediaSource> OMXCodec::Create( const sp<IOMX> &omx, const sp<MetaData> &meta, bool createEncoder, const sp<MediaSource> &source, const char *matchComponentName, uint32_t flags, const sp<ANativeWindow> &nativeWindow) { …… Vector<CodecNameAndQuirks> matchingCodecs; findMatchingCodecs( mime, createEncoder, matchComponentName, flags, &matchingCodecs); sp<OMXCodecObserver> observer = new OMXCodecObserver; IOMX::node_id node = 0; for (size_t i = 0; i < matchingCodecs.size(); ++i) { const char *componentNameBase = matchingCodecs[i].mName.string(); uint32_t quirks = matchingCodecs[i].mQuirks; const char *componentName = componentNameBase; …… status_t err = omx->allocateNode(componentName, observer, &node); if (err == OK) { sp<OMXCodec> codec = new OMXCodec( omx, node, quirks, flags, createEncoder, mime, componentName, source, nativeWindow); observer->setCodec(codec); err = codec->configureCodec(meta); if (err == OK) { if (!strcmp("OMX.Nvidia.mpeg2v.decode", componentName)) { codec->mFlags |= kOnlySubmitOneInputBufferAtOneTime; } return codec; } } } return NULL; }
findMatchingCodecs找到系统提供的所有能对当前mimeType解码的codec,并按照软解码优先的顺序排序。Codec通过"/etc/media_codecs.xml"由芯片厂商提供,在full build后自动生成,findMatchingCodecs执行完后,会得到一些codecs的名称等信息,保存在matchingCodecs里面。
allocateNode会根据componentName创建解码库实例,并将observer、node_id等和它建立联系,方便AwesomePlayer根据node_id找到解码器,解码器通过observer的回调函数通知AwesomePlayer。status_t OMX::allocateNode( const char *name, const sp<IOMXObserver> &observer, node_id *node) { Mutex::Autolock autoLock(mLock); *node = 0; // 创建instance OMXNodeInstance *instance = new OMXNodeInstance(this, observer); // 根据name创建解码库实例,并通过参数handle返回component,component结构保存了一些函数指针,实现外部程序对解码库的调用。mMaster在OMX的构造函数中初始化 // ,并加载了外部芯片厂商提供的解码库和google提供的软解码库。 OMX_COMPONENTTYPE *handle; OMX_ERRORTYPE err = mMaster->makeComponentInstance( name, &OMXNodeInstance::kCallbacks, instance, &handle); …… // 后面的这些就都是建立通路和联系了 *node = makeNodeID(instance); mDispatchers.add(*node, new CallbackDispatcher(instance)); instance->setHandle(*node, handle); mLiveNodes.add(observer->asBinder(), instance); observer->asBinder()->linkToDeath(this); return OK; }在OMX的构造函数中初始化OMXMaster
OMX::OMX() : mMaster(new OMXMaster), mNodeCounter(0) { }在OMXMaster的构造函数中加载解码库
OMXMaster::OMXMaster() : mVendorLibHandle(NULL) { // 芯片厂商提供的解码库 addVendorPlugin(); // 软解码库 addPlugin(new SoftOMXPlugin); }下面以softPlugin为例来看makeComponentInstance
OMX_ERRORTYPE OMXMaster::makeComponentInstance( const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component) { Mutex::Autolock autoLock(mLock); *component = NULL; ssize_t index = mPluginByComponentName.indexOfKey(String8(name)); if (index < 0) { return OMX_ErrorInvalidComponentName; } // 最终会调用解码库实现的makeComponentInstance OMXPluginBase *plugin = mPluginByComponentName.valueAt(index); OMX_ERRORTYPE err = plugin->makeComponentInstance(name, callbacks, appData, component); if (err != OMX_ErrorNone) { return err; } mPluginByInstance.add(*component, plugin); return err; } OMX_ERRORTYPE SoftOMXPlugin::makeComponentInstance( const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component) { ALOGV("makeComponentInstance '%s'", name); for (size_t i = 0; i < kNumComponents; ++i) { // 根据name找到具体的解码库 if (strcmp(name, kComponents[i].mName)) { continue; } AString libName = "libstagefright_soft_"; libName.append(kComponents[i].mLibNameSuffix); libName.append(".so"); void *libHandle = dlopen(libName.c_str(), RTLD_NOW); if (libHandle == NULL) { ALOGE("unable to dlopen %s", libName.c_str()); return OMX_ErrorComponentNotFound; } typedef SoftOMXComponent *(*CreateSoftOMXComponentFunc)( const char *, const OMX_CALLBACKTYPE *, OMX_PTR, OMX_COMPONENTTYPE **); // 找到库文件中创建解码库的方法 CreateSoftOMXComponentFunc createSoftOMXComponent = (CreateSoftOMXComponentFunc)dlsym( libHandle, "_Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPE" "PvPP17OMX_COMPONENTTYPE"); if (createSoftOMXComponent == NULL) { dlclose(libHandle); libHandle = NULL; return OMX_ErrorComponentNotFound; } // 执行该方法,并创建解码库 sp<SoftOMXComponent> codec = (*createSoftOMXComponent)(name, callbacks, appData, component); if (codec == NULL) { dlclose(libHandle); libHandle = NULL; return OMX_ErrorInsufficientResources; } OMX_ERRORTYPE err = codec->initCheck(); if (err != OMX_ErrorNone) { dlclose(libHandle); libHandle = NULL; return err; } codec->incStrong(this); codec->setLibHandle(libHandle); return OMX_ErrorNone; } return OMX_ErrorInvalidComponentName; }//MP3类型的解码器创建方法
android::SoftOMXComponent *createSoftOMXComponent( const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component) { return new android::SoftMP3(name, callbacks, appData, component); }
create的最后一步configureCodec(meta),主要是设置下输出的宽高和initNativeWindow。
最后来讲status_t err = mVideoSource->start();status_t OMXCodec::start(MetaData *meta) { …… // Decoder case if ((err = mSource->start(params.get())) != OK) { CODEC_LOGE("source failed to start: %d", err); return err; } return init(); }mSource为mVideoTrack,这里不多讲,直接看init()
status_t OMXCodec::init() { // mLock is held. CHECK_EQ((int)mState, (int)LOADED); status_t err; if (!(mQuirks & kRequiresLoadedToIdleAfterAllocation)) { err = mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle); CHECK_EQ(err, (status_t)OK); setState(LOADED_TO_IDLE); } err = allocateBuffers(); if (err != (status_t)OK) { return err; } if (mQuirks & kRequiresLoadedToIdleAfterAllocation) { err = mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle); CHECK_EQ(err, (status_t)OK); setState(LOADED_TO_IDLE); } while (mState != EXECUTING && mState != ERROR) { mAsyncCompletion.wait(mLock); } return mState == ERROR ? UNKNOWN_ERROR : OK; }主要就是allocateBuffers,然后设置LOADED_TO_IDLE状态。
status_t OMXCodec::allocateBuffers() { status_t err = allocateBuffersOnPort(kPortIndexInput); if (err != OK) { return err; } return allocateBuffersOnPort(kPortIndexOutput); }到此,prepare的流程就基本分析完成了。
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。