Android多媒体框架--18:OpenMAX介绍

2023-05-02  本文已影响0人  DarcyZhou

"本文转载自:[yanbixing123]的Android MultiMedia框架完全解析 - OpenMAX介绍"

1.概述

  OpenMAX IL API通过C语言致力于打造可移植媒体组件的阵列平台。这些组件可以是来源(source)、汇出(sink)、编解码器(codec)、过滤器(filter)、分离器(splitter)、混频器(mixers),或任何其他操作。

  OpenMAX IL API允许用户加载,控制,连接和卸载各个组件。Android主要的多媒体引擎StageFright是通过IBinder使用OpenMax,用于编解码(Codec)处理。按照OpenMAX的抽象,Android本身是不关心被构造的Codec到底是硬件解码还是软件解码的。

  OpenMAX的官网如下:OpenMAX Overview - The Khronos Group Inc

2.OMX介绍

  OpenMAX IL主要内容如下:

01.png

  OpenMAX IL的客户端,通过调用四个OpenMAX IL组件,实现了一个功能。四个组件分别是Source组件、Host组件、Accelerator组件和Sink组件。Source组件只有一个输出端口;而Host组件有一个输入端口和一个输出端口;Accelerator组件具有一个输入端口,调用了硬件的编解码器,加速主要体现在这个环节上。Accelerator组件和Sink组件通过私有通讯方式在内部进行连接,没有经过明确的组件端口。

  OpenMAX IL在使用的时候,其数据流也有不同的处理方式:既可以经由客户端,也可以不经由客户端。图中Source组件到Host组件的数据流就是经过客户端的;而Host组件到Accelerator组件的数据流就没有经过客户端,使用了隧道化的方式;Accelerator组件和Sink组件甚至可以使用私有的通讯方式。

  OpenMAX Core是辅助各个组件运行的部分,它通常需要完成各个组件的初始化等工作,在真正运行过程中,重点是各个OpenMAX IL的组件,OpenMAX Core不是重点,也不是标准。

  OpenMAX IL的组件是OpenMax IL实现的核心内容,一个组件以输入、输出端口为接口,端口可以被连接到另一个组件上。外部对组件可以发送命令,还进行设置/获取参数、配置等内容。组件的端口可以包含缓冲区(Buffer)的队列。

  组件的处理的核心内容是:通过输入端口消耗Buffer,通过输出端口填充Buffer,由此多组件相联接可以构成流式的处理。OpenMAX IL中一个组件的结构如下图所示:

02.png

OpenMAX的这些组件,可以完全构成一个播放器,但是在Android中使用最多的,还是只用OMX的Accelerator组件来做编解码,因为如果纯靠CPU来做软件的编解码的话,会消耗大量的资源,甚至对于现在的高清4K视频,有的CPU甚至都解不动,所以,一般会将这个任务交给VPU(Video Process Unit)来处理,即所谓的硬解。

3.Android中的OMX

  Android中的NuPlayer就是用openmax来做(Codec)编解码,其实在OpenMAX接口设计中,它不光能用来当编解码,它的组件也可以组成一个完整的播放器,包括source、demux、decode、output。但是Android只用它来做code,主要有如下原因:

(1)在整个播放器中,解码器不得不说是最重要的一部分,而且也是最耗资源的一块。如果全靠软解,直接通过cpu来运算,特别是高清视频。别的事你就可以啥都不干了,所以解码器是最需要硬件提供加速的部分。现在的高清解码芯片都是主芯片+DSP结构,解码的工作都是通过DSP来做,不会在过多的占用主芯片。所有将芯片中DSP硬件编解码的能力通过openmax标准接口呈现出来,提供上层播放器来用,这块是openmax最重要的意义。

(2)source主要是和协议打交道,demux分解容器部分,大多数的容器格式的分解是不需要通过硬件来支持。只是ts流这种格式最可能用到硬件的支持。因为ts格式比较特殊,单包的大小太小了,只有188字节。所以也是为什么现在常见的解码芯片都会提供硬件ts demux 的支持。

(3)音视频输出部分video/audio output这块和操作系统关系十分紧密。可以看看著名开源播放器vlc。vlc 在mac、linux、Windows都有,功能上差别也不大。所以说他是跨平台的,他跨平台跨在哪?主要的工作量还是在音视频解码完之后的输出模块。因为各个系统的图像渲染和音频输出实现方法不同,所以vlc需要针对每个平台实现不同的output。这部分内容放在openmax来显然不合适。

  Android中使用的主要是OpenMax的编解码功能。虽然OpenMax也可以生成输入、输出、文件解析-构建等组件,但是在各个系统(不仅是Android)中使用的最多的还是编解码组件。媒体的输入、输出环节和系统的关系很大,引入OpenMax标准比较麻烦;文件解析-构建环节一般不需要使用硬件加速。编解码组件也是最能体现硬件加速的环节,因此最常使用。

(不知道这里还对不对,有没有使用到OMXCodec,还是说直接使用ACodec了?下面这个图中的OMXCodec也值得商榷)

(1)android系统中只用openmax来做codec,所以android向上抽象了一层OMXCodec,提供给上层播放器用,也就是上面所讲述的Accelerator组件。播放器中音视频解码器mVideosource、mAudiosource都是OMXCodec的实例。

(2)OMXCodec通过IOMX 依赖binder机制 获得 OMX服务,OMX服务才是openmax在android中实现。

(3)OMX把软编解码和硬件编解码统一看作插件的形式管理起来。

03.png

(4)OMX具体实现

(5)ACodec同OMXNodeInstance的消息传递

(6)ACodec与OMX组件的关系

(7)连接OMX服务器

  在前面文档《Android多媒体框架--15:MediaCodec解析》中,MediaCodec创建成功后会执行init()函数,主要是改变内部状态为INITIALIZING,同时调用了mCodec(此时为ACodec)的initiateAllocateComponent()方法:

mCodec->initiateAllocateComponent(format);
----------------------------
void ACodec::initiateAllocateComponent(const sp &msg) {
    msg->setWhat(kWhatAllocateComponent);
    msg->setTarget(this);
    msg->post();
}

按照消息队列机制找到回调:

ACodec::UninitializedState::onMessageReceived(const sp &msg)
 case ACodec::kWhatAllocateComponent:
        {
            onAllocateComponent(msg);
            handled = true;
            break;
        }

MediaCodec::UninitializedState::onAllocateComponent()

bool ACodec::UninitializedState::onAllocateComponent(const sp &msg) {
    ALOGV("onAllocateComponent");

    CHECK(mCodec->mNode == 0);

    OMXClient client;
    // 客户端链接OMX服务
    if (client.connect() != OK) {
        mCodec->signalError(OMX_ErrorUndefined, NO_INIT);
        return false;
    }

    sp omx = client.interface();

    sp notify = new AMessage(kWhatOMXDied, mCodec);

    Vector matchingCodecs;

    AString mime;

    AString componentName;
    uint32_t quirks = 0;
    int32_t encoder = false;
    if (msg->findString("componentName", &componentName)) {
        sp list = MediaCodecList::getInstance();
        if (list != NULL && list->findCodecByName(componentName.c_str()) >= 0) {
            matchingCodecs.add(componentName);
        }
    } else {
        CHECK(msg->findString("mime", &mime));

        if (!msg->findInt32("encoder", &encoder)) {
            encoder = false;
        }

        // 找出符合要求的解码器
        MediaCodecList::findMatchingCodecs(
                mime.c_str(),
                encoder, // createEncoder
                0,       // flags
                &matchingCodecs);
    }

    sp observer = new CodecObserver;
    IOMX::node_id node = 0;

    status_t err = NAME_NOT_FOUND;
    for (size_t matchIndex = 0; matchIndex < matchingCodecs.size();
            ++matchIndex) {
        componentName = matchingCodecs[matchIndex];
        quirks = MediaCodecList::getQuirksFor(componentName.c_str());

        pid_t tid = gettid();
        int prevPriority = androidGetThreadPriority(tid);
        androidSetThreadPriority(tid, ANDROID_PRIORITY_FOREGROUND);
        // 创建真正的解码器实例
        err = omx->allocateNode(componentName.c_str(), observer, &mCodec->mNodeBinder, &node);
        androidSetThreadPriority(tid, prevPriority);

        if (err == OK) {
            break;
        } else {
            ALOGW("Allocating component '%s' failed, try next one.", componentName.c_str());
        }

        node = 0;
    }

    if (node == 0) {
        if (!mime.empty()) {
            ALOGE("Unable to instantiate a %scoder for type '%s' with err %#x.",
                    encoder ? "en" : "de", mime.c_str(), err);
        } else {
            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(notify);
    if (mCodec->mNodeBinder == NULL ||
            mCodec->mNodeBinder->linkToDeath(mDeathNotifier) != OK) {
        // This was a local binder, if it dies so do we, we won't care
        // about any notifications in the afterlife.
        mDeathNotifier.clear();
    }

    notify = new AMessage(kWhatOMXMessageList, mCodec);
    observer->setNotificationMessage(notify);

    mCodec->mComponentName = componentName;
    mCodec->mRenderTracker.setComponentName(componentName);
    mCodec->mFlags = 0;

    if (componentName.endsWith(".secure")) {
        mCodec->mFlags |= kFlagIsSecure;
        mCodec->mFlags |= kFlagIsGrallocUsageProtected;
        mCodec->mFlags |= kFlagPushBlankBuffersToNativeWindowOnShutdown;
    }

    mCodec->mQuirks = quirks;
    mCodec->mOMX = omx;
    mCodec->mNode = node;

    {
        sp notify = mCodec->mNotify->dup();
        notify->setInt32("what", CodecBase::kWhatComponentAllocated);
        notify->setString("componentName", mCodec->mComponentName.c_str());
        notify->post();
    }

    // 这里设置的是ACodec的状态
    mCodec->changeState(mCodec->mLoadedState);

    return true;
}

这里首先是client.connect() 去连接服务器,看下这块的实现:

status_t OMXClient::connect() {
    sp<IServiceManager> sm = defaultServiceManager();
    sp<IBinder> playerbinder = sm->getService(String16("media.player"));
    sp<IMediaPlayerService> mediaservice = interface_cast<IMediaPlayerService>(playerbinder);

    if (mediaservice.get() == NULL) {
        ALOGE("Cannot obtain IMediaPlayerService");
        return NO_INIT;
    }

    sp<IOMX> mediaServerOMX = mediaservice->getOMX();
    if (mediaServerOMX.get() == NULL) {
        ALOGE("Cannot obtain mediaserver IOMX");
        return NO_INIT;
    }

    // If we don't want to use the codec process, and the media server OMX
    // is local, use it directly instead of going through MuxOMX
    if (!sCodecProcessEnabled &&
            mediaServerOMX->livesLocally(0 /* node */, getpid())) {
        mOMX = mediaServerOMX;
        return OK;
    }

    sp<IBinder> codecbinder = sm->getService(String16("media.codec"));
    sp<IMediaCodecService> codecservice = interface_cast<IMediaCodecService>(codecbinder);

    if (codecservice.get() == NULL) {
        ALOGE("Cannot obtain IMediaCodecService");
        return NO_INIT;
    }

    sp<IOMX> mediaCodecOMX = codecservice->getOMX();
    if (mediaCodecOMX.get() == NULL) {
        ALOGE("Cannot obtain mediacodec IOMX");
        return NO_INIT;
    }

    mOMX = new MuxOMX(mediaServerOMX, mediaCodecOMX);

    return OK;
}

它通过获取mediaplayer和mediacodec这两个service来分别获取OMX服务,而在MediaPlayerService.cpp中,它new出来一个OMX:

sp<IOMX> MediaPlayerService::getOMX() {
    Mutex::Autolock autoLock(mLock);

    if (mOMX.get() == NULL) {
        mOMX = new OMX;
    }

    return mOMX;
}

同样在MediaCodecService.cpp中,它也是new出来一个OMX:

sp<IOMX> MediaCodecService::getOMX() {

    Mutex::Autolock autoLock(mLock);

    if (mOMX.get() == NULL) {
        mOMX = new OMX;
    }

    return mOMX;
}

在OMX的构造函数中,创建了一个OMXMaster:

Omx::Omx() :
    mMaster(new OMXMaster()),
    mParser() {
}

那么OpenMAX就从OMXMaster开始讲起。

上一篇下一篇

猜你喜欢

热点阅读