码迷,mamicode.com
首页 > 其他好文 > 详细

MediaPlayer本地播放流程解析(二)

时间:2014-08-20 21:13:12      阅读:488      评论:0      收藏:0      [点我收藏+]

标签:android   media   多媒体框架   prepare   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的流程就基本分析完成了。

MediaPlayer本地播放流程解析(二),布布扣,bubuko.com

MediaPlayer本地播放流程解析(二)

标签:android   media   多媒体框架   prepare   mediaplayer   

原文地址:http://blog.csdn.net/super_dc/article/details/38710957

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!