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

时间: 2024-08-03 19:18:10

MediaPlayer本地播放流程解析(二)的相关文章

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

应用场景: MediaPlayer mediaPlayer = new MediaPlayer(); mediaPlayer.setOnCompletionListener(new OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { mediaPlayer.release(); mediaPlayer = null; } }); mediaPlayer.setDataSource("abc.m

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

这一篇文章接着之前的prepare,讲play的流程 前面的流程省略,直接从AwesomePlayer的Play()开始讲. status_t AwesomePlayer::play() { -- return play_l(); } status_t AwesomePlayer::play_l() { -- if (mAudioSource != NULL) { if (mAudioPlayer == NULL) { createAudioPlayer_l(); } if (mVideoSou

Android Mediaplayer本地音乐播放器(绑定服务)

本文章介绍MediaPlayer本地音乐播放器,而当应用程序不再位于前台且没有正在使用它的活动时,为了确保音频继续播放,我们需要建立一个服务Service. Activity与绑定服务Service之间的交互是本文章的重点(这里需要说明一点的是,Activity不能直接访问服务对象中的方法,所以才有了我们一下的介绍,这也是为服务的安全等方面的考虑). 直接上代码: 布局文件:activity_main: <LinearLayout xmlns:android="http://schemas

蓝牙OBEX剖析(二)-- 流程解析

OBEX流程解析(封包格式见上篇文章) 1.连接 2.Get 3.put 4.disconnect

【Android 多媒体开发】 MediaPlayer 状态机 接口 方法 解析

作者 : 韩曙亮 转载请著名出处 :  http://blog.csdn.net/shulianghan/article/details/38487967 一. MediaPlayer 状态机 介绍 Android MediaPlayer 状态即图例 : 1. Idle (闲置) 状态 和 End (结束) 状态 MediaPlayer 对象声明周期 : 从 Idle 到 End 状态就是 MediaPlayer 整个生命周期; -- 生命周期開始 : 进入 Idle (闲置) 状态; -- 生

ijkplayer阅读学习笔记之从代码上看播放流程

看了很久的ijkplayer的视频播放,其实还是没有怎么看懂,只是个人浅浅的笔记 关键部分就是联网获取数据那部分,还没有搞定其实 从用户点击一个已有地址的网络视频开始,从源码分析播放流程. 1.        // init player  加载native底层库 IjkMediaPlayer.loadLibrariesOnce(null); IjkMediaPlayer.native_profileBegin("libijkplayer.so"); 第一句话是加载三个重要的so文件

mapreduce(2)--combiner使用和mr流程解析

一.准备工作 1.需求 在wordcount程序中使用自定义combiner 解析mapreduce的流程 2.环境配置 (1)hadoop为本地模式 (2)pom文件代码如下 <dependencies> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-client</artifactId> <version>2.7.3</v

SSL/TLS算法流程解析

SSL/TLS 早已不是陌生的词汇,然而其原理及细则却不是太容易记住.本文将试图通过一些简单图示呈现其流程原理,希望读者有所收获. 一.相关版本 Version Source Description   Browser Support SSL v2.0 Vendor Standard (from Netscape Corp.) [SSL2] First SSL protocol for which implementations exist - NS Navigator 1.x/2.x - MS

Spring 源码解析之HandlerAdapter源码解析(二)

Spring 源码解析之HandlerAdapter源码解析(二) 前言 看这篇之前需要有Spring 源码解析之HandlerMapping源码解析(一)这篇的基础,这篇主要是把请求流程中的调用controller流程单独拿出来了 解决上篇文章遗留的问题 getHandler(processedRequest) 这个方法是如何查找到对应处理的HandlerExecutionChain和HandlerMapping的,比如说静态资源的处理和请求的处理肯定是不同的HandlerMapping ge