Android开发

【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 4】【02】

本文主要是介绍【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 4】【02】,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

承接上一章节分析:【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 4】【01】
本系列文章分析的安卓源码版本:【Android 10.0 版本】

【此章节小节编号就接着上一章节排列】
4、rememberCodecSpecificData(format)实现分析:
读取缓存编解码器特殊数据,CSD其实对应Codec Specific Data的缩写。
对于H.264来说,"csd-0"和"csd-1"分别对应sps和pps;对于AAC来说,"csd-0"对应ADTS。

// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp]
void NuPlayer::Decoder::rememberCodecSpecificData(const sp<AMessage> &format) {
    if (format == NULL) {
        return;
    }
    mCSDsForCurrentFormat.clear();
    for (int32_t i = 0; ; ++i) {
        AString tag = "csd-";
        tag.append(i);
        sp<ABuffer> buffer;
        if (!format->findBuffer(tag.c_str(), &buffer)) {
        	// 查找失败则退出
            break;
        }
        // 缓存该buffer信息
        mCSDsForCurrentFormat.push(buffer);
    }
}

5、mCodec->getOutputFormat(&mOutputFormat)实现分析:
获取输出buffer格式信息

// [frameworks/av/media/libstagefright/MediaCodec.cpp]
status_t MediaCodec::getOutputFormat(sp<AMessage> *format) const {
	// 发送【kWhatGetOutputFormat】事件消息给自身处理并等待应答
    sp<AMessage> msg = new AMessage(kWhatGetOutputFormat, this);

    sp<AMessage> response;
    status_t err;
    if ((err = PostAndAwaitResponse(msg, &response)) != OK) {
        return err;
    }

    // 成功则获取该返回的格式信息
    CHECK(response->findMessage("format", format));

    return OK;
}

【kWhatGetOutputFormat】事件消息处理:
其实从下面处理可以看到,输入输出都是同一个流程处理的,只是类型不同而已

// [frameworks/av/media/libstagefright/MediaCodec.cpp]
void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
    switch (msg->what()) {
        case kWhatGetInputFormat:
        case kWhatGetOutputFormat:
        {
            // 根据格式类型获取对应的格式信息
            sp<AMessage> format =
                (msg->what() == kWhatGetOutputFormat ? mOutputFormat : mInputFormat);

            // 获取应答消息对象
            sp<AReplyToken> replyID;
            CHECK(msg->senderAwaitsResponse(&replyID));

            // 判断当前状态是否有效,否则应答无效操作错误码
            if ((mState != CONFIGURED && mState != STARTING &&
                 mState != STARTED && mState != FLUSHING &&
                 mState != FLUSHED)
                    || format == NULL) {
                PostReplyWithError(replyID, INVALID_OPERATION);
                break;
            } else if (mFlags & kFlagStickyError) {
                PostReplyWithError(replyID, getStickyError());
                break;
            }

            sp<AMessage> response = new AMessage;
            // 应答该对应格式信息对象给调用端
            response->setMessage("format", format);
            response->postReply(replyID);
            break;
        }
    }
}        

6、mCodec->getInputFormat(&mInputFormat)实现分析:
获取输入buffer格式信息,其实和上面第5小节类似处理,因此不再分析

// [frameworks/av/media/libstagefright/MediaCodec.cpp]
status_t MediaCodec::getInputFormat(sp<AMessage> *format) const {
    sp<AMessage> msg = new AMessage(kWhatGetInputFormat, this);

    sp<AMessage> response;
    status_t err;
    if ((err = PostAndAwaitResponse(msg, &response)) != OK) {
        return err;
    }

    CHECK(response->findMessage("format", format));

    return OK;
}

7、mCodec->setCallback(reply)实现分析:
设置一个接收应答数据的应答事件【kWhatCodecNotify】消息对象作为一个callback回调监听

// [frameworks/av/media/libstagefright/MediaCodec.cpp]
status_t MediaCodec::setCallback(const sp<AMessage> &callback) {
	// 发送【kWhatSetCallback】事件消息给自身处理并等待应答
    sp<AMessage> msg = new AMessage(kWhatSetCallback, this);
    // 设置回调参数
    msg->setMessage("callback", callback);

    // 应答消息获取,未使用
    sp<AMessage> response;
    return PostAndAwaitResponse(msg, &response);
}

【kWhatSetCallback】事件消息处理:

// [frameworks/av/media/libstagefright/MediaCodec.cpp]
void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
    switch (msg->what()) {
        case kWhatSetCallback:
        {
            // 获取应答消息对象
            sp<AReplyToken> replyID;
            CHECK(msg->senderAwaitsResponse(&replyID));

            // 检查状态是否有效,无效返回无效操作错误码
            if (mState == UNINITIALIZED
                    || mState == INITIALIZING
                    || isExecuting()) {
                // callback can't be set after codec is executing,
                // or before it's initialized (as the callback
                // will be cleared when it goes to INITIALIZED)
                PostReplyWithError(replyID, INVALID_OPERATION);
                break;
            }

            // 获取callback消息事件通知对象
            sp<AMessage> callback;
            CHECK(msg->findMessage("callback", &callback));

            // 缓存该对象
            mCallback = callback;

            if (mCallback != NULL) {
            	// 不为空时将打印,MediaCodec将允许在异步操作模式
                ALOGI("MediaCodec will operate in async mode");
                mFlags |= kFlagIsAsync;
            } else {
            	// 为空则去掉该模式标志位
                mFlags &= ~kFlagIsAsync;
            }

            // 应答消息对象
            sp<AMessage> response = new AMessage;
            response->postReply(replyID);
            break;
        }
    }
}

8、mCodec->start()实现分析:
启动底层编解码器组件工作,真正的开启编解码器工作的处理流程

// [frameworks/av/media/libstagefright/MediaCodec.cpp]
status_t MediaCodec::start() {
	// 创建一个【kWhatStart】事件消息给MediaCodec自身处理
    sp<AMessage> msg = new AMessage(kWhatStart, this);

    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.
    resources.push_back(MediaResource(MediaResource::kGraphicMemory, 1));
    // 该循环结构处理逻辑,未分析的处理请见前面已有流程的分析
    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;
            }
            // reset当前编解码器【其实reset中还会重新执行init流程】,因为此前start失败
            // 备注:关于reset流程会在后续有时间单独章节中分析
            // Recover codec from previous error before retry start.
            err = reset();
            if (err != OK) {
                ALOGE("retrying start: failed to reset codec");
                break;
            }
            // 注意此次请求的是配置处理流程并等待应答,若第二次也失败则直接退出,
            // 若成功将会再次执行下面的start启动工作流程
            sp<AMessage> response;
            err = PostAndAwaitResponse(mConfigureMsg, &response);
            if (err != OK) {
                ALOGE("retrying start: failed to configure codec");
                break;
            }
        }
		
		// 发送【kWhatStart】事件消息,并等待应答
        sp<AMessage> response;
        err = PostAndAwaitResponse(msg, &response);
        if (!isResourceError(err)) {
            break;
        }
    }
    return err;
}

【kWhatStart】事件消息处理:

// [frameworks/av/media/libstagefright/MediaCodec.cpp]
void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
    switch (msg->what()) {
        case kWhatStart:
        {
            // 获取应答消息对象
            sp<AReplyToken> replyID;
            CHECK(msg->senderAwaitsResponse(&replyID));

            // 状态检查处理
            if (mState == FLUSHED) {
            	// FLUSHED状态时设置已开始工作状态,目前暂不分析这种流程,先分析下面正常状态下的工作流程
                setState(STARTED);
                if (mHavePendingInputBuffers) {
                    onInputBufferAvailable();
                    mHavePendingInputBuffers = false;
                }
                mCodec->signalResume();
                PostReplyWithError(replyID, OK);
                break;
            } else if (mState != CONFIGURED) {
                PostReplyWithError(replyID, INVALID_OPERATION);
                break;
            }

            // 缓存应答消息对象
            mReplyID = replyID;
            // 设置正在开始工作状态处理流程中
            setState(STARTING);

            // 执行ACodec开启工作流程
            // 见下面分析
            mCodec->initiateStart();
            break;
        }
    }
}

mCodec->initiateStart()实现分析:
执行ACodec开启工作流程

// [frameworks/av/media/libstagefright/ACodec.cpp]
void ACodec::initiateStart() {
    (new AMessage(kWhatStart, this))->post();
}

ACodec接收kWhatStart事件消息处理:

// [frameworks/av/media/libstagefright/ACodec.cpp]
bool ACodec::LoadedState::onMessageReceived(const sp<AMessage> &msg) {
    bool handled = false;

    switch (msg->what()) {
        case ACodec::kWhatStart:
        {
            onStart();
            handled = true;
            break;
        }
    }
}

onStart()实现分析:

// [frameworks/av/media/libstagefright/ACodec.cpp]
void ACodec::LoadedState::onStart() {
    ALOGV("onStart");

    // 发送【OMX_CommandStateSet】状态事件的【OMX_StateIdle】IDLE状态命令给底层组件
    // 发送命令给OMXNodeInstance,将通过OMXNodeInstance代理执行底层组件
    // 见8.1小节分析
    // 备注:OMXNode该方法命令功能向底层发送action和参数
    // 因此根据8.1分析此处直接举例分析SoftAVCDec和SoftAAC2这两个解码器组件的实现结果:
    // 该方法在底层组件是共同的父类实现的,因此它们两处理流程一致,底层主要处理状态扭转(在组件自身ALooper中)并在可能的事件执行完毕时
    // 进行通知OMXNodeInstance此前设置的callback监听实现类的回调。此方法为状态扭转,因此底层组件扭转成功
    // 将会发送一个状态切换完成回调给OMXNode。执行为:notify(OMX_EventCmdComplete, OMX_CommandStateSet, mState, NULL);
    // 最终将会执行OMX框架层的OMX_CALLBACKTYPE结构对象的该指针方法OMX_ERRORTYPE (*EventHandler),
    // 而该结构对象根据此前流程的分析可知,它是在初始化底层组件时传入的是OMX_CALLBACKTYPE OMXNodeInstance::kCallbacks结构对象定义。
    // 也就是最终会执行该结构实现的对应方法,如下见8.2小节分析
    status_t err = mCodec->mOMXNode->sendCommand(OMX_CommandStateSet, OMX_StateIdle);
    if (err != OK) {
    	// 失败将会回调通知MediaCodec的Callback接受处理该错误,见此前已有分析
        mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
    } else {
    	// 成功,则扭转状态机状态为IDLE状态机实现者
    	// 见8.3小节分析
        mCodec->changeState(mCodec->mLoadedToIdleState);
    }
}

8.1、mCodec->mOMXNode->sendCommand(OMX_CommandStateSet, OMX_StateIdle)实现分析:
发送命令给OMXNodeInstance,将通过OMXNodeInstance代理执行底层组件,OMXNode该方法命令功能向底层发送action和参数

// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp]
status_t OMXNodeInstance::sendCommand(
        OMX_COMMANDTYPE cmd, OMX_S32 param) {
    // 解码器时该对象OMX数据源为空,如下处理若此前不为空,将会在后续开启播放时最终还是设置为null
    // 备注:该对象其实表示的是此前分析过的setInputSurface设置进入的,其实该输入surface的作用为:
    // 配置编解码器(例如编码器)以使用持久性输入Surface(提供视频原始数据源)代替输入缓冲区buffers
    // 向底层组件提供buffer数据,通常是编码器使用。
    // getBufferSource() 该方法实现,见8.1.1小节
    // 关于IOMXBufferSource不会详细讨论
    const sp<IOMXBufferSource> bufferSource(getBufferSource());
    if (bufferSource != NULL && cmd == OMX_CommandStateSet) {
        if (param == OMX_StateIdle) {
            // Initiating transition from Executing -> Idle
            // ACodec is waiting for all buffers to be returned, do NOT
            // submit any more buffers to the codec.
            // 请求为闲置状态时,将清除IOMXBufferSource。
            // 根据英文注释可知,此时ACodec正在等待所有buffer返回结束,不再提供任何buffer数据给底层编解码器。
            bufferSource->onOmxIdle();
        } else if (param == OMX_StateLoaded) {
            // Initiating transition from Idle/Executing -> Loaded
            // Buffers are about to be freed.
            // 已加载完成状态时将会释放该buffer对象,设置为null
            bufferSource->onOmxLoaded();
            setBufferSource(NULL);
        }

        // fall through
    }

    Mutex::Autolock autoLock(mLock);
    if (mHandle == NULL) {
        return DEAD_OBJECT;
    }

    if (cmd == OMX_CommandStateSet) {
    	// 状态事件时,设置该值为true
        // There are no configurations past first StateSet command.
        mSailed = true;
    }

    // 该处理不关注
    // bump internal-state debug level for 2 input and output frames past a command
    {
        Mutex::Autolock _l(mDebugLock);
        bumpDebugLevel_l(2 /* numInputBuffers */, 2 /* numOutputBuffers */);
    }

   	// 转换事件参数为字符串描述值,根据不同事件来转换,状态事件时将转换为对应字符串描述,见此前已有分析 
   	// portString(param) 端口字符串转换,见8.1.2小节实现
    const char *paramString =
        cmd == OMX_CommandStateSet ? asString((OMX_STATETYPE)param) : portString(param);
    CLOG_STATE(sendCommand, "%s(%d), %s(%d)", asString(cmd), cmd, paramString, param);
    // 此OMX_SendCommand方法为宏定义,最终将会执行底层具体组件的对应方法实现
    OMX_ERRORTYPE err = OMX_SendCommand(mHandle, cmd, param, NULL);
    CLOG_IF_ERROR(sendCommand, err, "%s(%d), %s(%d)", asString(cmd), cmd, paramString, param);
    // 注意该方法返回状态err只是代表底层组件实现是否被成功调用了,而不是执行功能的结果
    return StatusFromOMXError(err);
}

OMX_SendCommand宏定义:
最终将会执行底层具体组件的对应方法实现,并传递参数

#define OMX_SendCommand(                                    \
         hComponent,                                        \
         Cmd,                                               \
         nParam,                                            \
         pCmdData)                                          \
     ((OMX_COMPONENTTYPE*)(hComponent))->SendCommand(       \
         hComponent,                                        \
         Cmd,                                               \
         nParam,                                            \
         pCmdData)                          /* Macro End */

8.1.1、getBufferSource() 该方法实现:
只是返回全局变量,该变量在解码器时通常为null。也就是说该OMX数据源来自于输入的Surface,而surface提供的数据为视频原始数据源,而非压缩的编码数据源,因此在我们播放压缩编码数据源时该值通常为null。

// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp]
sp<IOMXBufferSource> OMXNodeInstance::getBufferSource() {
    Mutex::Autolock autoLock(mOMXBufferSourceLock);
    return mOMXBufferSource;
}

// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp]
// 附上set方法,而该方法将在setInputSurface中执行
void OMXNodeInstance::setBufferSource(const sp<IOMXBufferSource>& bufferSource) {
    Mutex::Autolock autoLock(mOMXBufferSourceLock);
    CLOG_INTERNAL(setBufferSource, "%p", bufferSource.get());
    mOMXBufferSource = bufferSource;
}

8.1.2、portString(param) 端口字符串转换实现:
就是将对应请求端口索引转换为直观的描述

// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp]
static inline const char *portString(OMX_U32 portIndex) {
    switch (portIndex) {
        case kPortIndexInput:  return "Input";
        case kPortIndexOutput: return "Output";
        // 注意该值的最终计算为无符号类型最大值,~符号运算符其实就是每个bit位取反的意思
        case ~0U:              return "All";
        default:               return "port";
    }
}

8.2、OMX_CALLBACKTYPE OMXNodeInstance::kCallbacks结构对象定义,如下
该对象在底层组件初始化流程可查看早前流程分析。
备注:此方法结构非常重要,它是一个OMX回调监听消息事件和(音视频)数据传递的监听回调处理,也就是说它的处理流程将会是编解码器工作状态时底层组件和上层进行消息命令通知和数据传递的通道。

// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp]
// static
OMX_CALLBACKTYPE OMXNodeInstance::kCallbacks = {
    &OnEvent, &OnEmptyBufferDone, &OnFillBufferDone
};

将会执行第一个方法来接收该命令完成事件:

// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp]
// static
OMX_ERRORTYPE OMXNodeInstance::OnEvent(
        OMX_IN OMX_HANDLETYPE /* hComponent */,
        OMX_IN OMX_PTR pAppData,
        OMX_IN OMX_EVENTTYPE eEvent,
        OMX_IN OMX_U32 nData1,
        OMX_IN OMX_U32 nData2,
        OMX_IN OMX_PTR pEventData) {
    if (pAppData == NULL) {
        ALOGE("b/25884056");
        return OMX_ErrorBadParameter;
    }
    // 强转为OMX节点组件实例
    OMXNodeInstance *instance = static_cast<OMXNodeInstance *>(pAppData);
    // 检查当前对象是否正在死亡即是否被结束过程中
    if (instance->mDying) {
        return OMX_ErrorNone;
    }

    // 执行命令事件处理
    // 见下面分析
    instance->onEvent(eEvent, nData1, nData2);

    // 注译:输出渲染事件在到达观察者之前不会作为常规事件进行处理
    // output rendered events are not processed as regular events until they hit the observer
    if (eEvent == OMX_EventOutputRendered) {
    	// 输出渲染事件完成时
        if (pEventData == NULL) {
            return OMX_ErrorBadParameter;
        }

        // process data from array
        // 返回视频渲染输出帧的时间戳以及隧道组件EOS的结构对象指针,其实它指向的是数组
        // 该结构见下面定义
        OMX_VIDEO_RENDEREVENTTYPE *renderData = (OMX_VIDEO_RENDEREVENTTYPE *)pEventData;
        // 循环创建OMX消息对象来发送该消息给消息分发者mDispatcher,该对象早前初始化分析流程中涉及到的,
        // 它将会添加来自底层组件的事件消息到消息队列中,然后不断取出该消息队列中消息进行执行并通知给上层
        for (size_t i = 0; i < nData1; ++i) {
        	// OMX消息结构见下面定义
            omx_message msg;
            msg.type = omx_message::FRAME_RENDERED;
            // 该参数用于EMPTY_BUFFER_DONE 和 FILL_BUFFER_DONE 这两个事件时,并且client端必须关闭它
            msg.fenceFd = -1;
            // 已渲染视频帧媒体时间戳
            msg.u.render_data.timestamp = renderData[i].nMediaTimeUs;
            // 已渲染视频帧时的系统时间戳【已开机时间戳】
            msg.u.render_data.nanoTime = renderData[i].nSystemTimeNs;
            // 判断是否为INT64最大值,是的话则为真实时间渲染
            bool realTime = msg.u.render_data.timestamp == INT64_MAX;
            // 发送该消息到消息分发者进行分析
           	// 见下面分析
            instance->mDispatcher->post(msg, realTime);
        }
        return OMX_ErrorNone;
    }

    // 命令事件类型
    omx_message msg;
    msg.type = omx_message::EVENT;
    msg.fenceFd = -1;
    msg.u.event_data.event = eEvent;
    msg.u.event_data.data1 = nData1;
    msg.u.event_data.data2 = nData2;

    // 发送该消息到消息分发者进行分析
    // 见下面分析
    instance->mDispatcher->post(msg, true /* realTime */);

    return OMX_ErrorNone;
}

instance->onEvent(eEvent, nData1, nData2)实现分析:
执行命令事件处理

// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp]

// OMXNodeInstance::OnEvent calls OMX::OnEvent, which then calls here.
// Don't try to acquire mLock here -- in rare circumstances this will hang.
void OMXNodeInstance::onEvent(
        OMX_EVENTTYPE event, OMX_U32 arg1, OMX_U32 arg2) {
    const char *arg1String = "??";
    const char *arg2String = "??";
    ADebug::Level level = ADebug::kDebugInternalState;

    switch (event) {
        case OMX_EventCmdComplete:
        	// 命令完成事件处理
            arg1String = asString((OMX_COMMANDTYPE)arg1);
            switch (arg1) {
                case OMX_CommandStateSet:
                	// 状态命令设置事件
                    arg2String = asString((OMX_STATETYPE)arg2);
                    level = ADebug::kDebugState;
                    break;
                case OMX_CommandFlush:
                case OMX_CommandPortEnable:
                {
                    // bump internal-state debug level for 2 input and output frames
                    Mutex::Autolock _l(mDebugLock);
                    bumpDebugLevel_l(2 /* numInputBuffers */, 2 /* numOutputBuffers */);
                    FALLTHROUGH_INTENDED;
                }
                default:
                    arg2String = portString(arg2);
            }
            break;
           
        case OMX_EventError:
        	// 命令失败事件 
            arg1String = asString((OMX_ERRORTYPE)arg1);
            level = ADebug::kDebugLifeCycle;
            break;
        case OMX_EventPortSettingsChanged:
        	// buffer端口设置改变事件
            arg2String = asString((OMX_INDEXEXTTYPE)arg2);
            FALLTHROUGH_INTENDED;
        default:
            arg1String = portString(arg1);
    }

    CLOGI_(level, onEvent, "%s(%x), %s(%x), %s(%x)",
            asString(event), event, arg1String, arg1, arg2String, arg2);
    // 由前面分析可知,该对象在解码器时为空,不分析  
    const sp<IOMXBufferSource> bufferSource(getBufferSource());

    if (bufferSource != NULL
            && event == OMX_EventCmdComplete
            && arg1 == OMX_CommandStateSet
            && arg2 == OMX_StateExecuting) {
        bufferSource->onOmxExecuting();
    }

    // allow configuration if we return to the loaded state
    if (event == OMX_EventCmdComplete
            && arg1 == OMX_CommandStateSet
            && arg2 == OMX_StateLoaded) {
        // 由前面可知,此值前面被设置为true了,但此时还不能设置为false,因为此时arg2为 OMX_StateIdle,而不是OMX_StateLoaded
        mSailed = false;
    }
}

omx_message即OMX消息结构见下面定义

// [frameworks/av/media/libmedia/include/media/IOMX.h]
struct omx_message {
	// 发送消息的类型有如下几个
    enum {
        // 不同的命令事件类型
        EVENT,
        // 清空buffer完成事件
        EMPTY_BUFFER_DONE,
        // 填充buffer完成事件
        FILL_BUFFER_DONE,
        // 帧渲染完成事件类型
        FRAME_RENDERED,
    } type;

    int fenceFd; // used for EMPTY_BUFFER_DONE and FILL_BUFFER_DONE; client must close this

    // 共享体结构,可以看到针对上面的不同的消息类型,其携带的参数将会存储在不同的结构中
    union {
        // if type == EVENT
        struct {
            OMX_EVENTTYPE event;
            OMX_U32 data1;
            OMX_U32 data2;
            OMX_U32 data3;
            OMX_U32 data4;
        } event_data;

        // if type == EMPTY_BUFFER_DONE
        struct {
            IOMX::buffer_id buffer;
        } buffer_data;

        // if type == FILL_BUFFER_DONE
        struct {
            IOMX::buffer_id buffer;
            OMX_U32 range_offset;
            OMX_U32 range_length;
            OMX_U32 flags;
            OMX_TICKS timestamp;
        } extended_buffer_data;

        // if type == FRAME_RENDERED
        struct {
            OMX_TICKS timestamp;
            OMX_S64 nanoTime;
        } render_data;
    } u;
};

OMX_VIDEO_RENDEREVENTTYPE定义:
根据英文注释可基本知晓该结构用处,此处不展开翻译了,可自行理解

// [framework/native/headers/media_plugin/media/openmax/OMX_VideoExt.h]

/** Structure to return timestamps of rendered output frames as well as EOS
 *  for tunneled components.
 */
typedef struct OMX_VIDEO_RENDEREVENTTYPE {
    OMX_S64 nMediaTimeUs;  // timestamp of rendered video frame
    OMX_S64 nSystemTimeNs; // system monotonic time at the time frame was rendered
                           // Use INT64_MAX for nMediaTimeUs to signal that the EOS
                           // has been reached. In this case, nSystemTimeNs MUST be
                           // the system time when the last frame was rendered.
                           // This MUST be done in addition to returning (and
                           // following) the render information for the last frame.
} OMX_VIDEO_RENDEREVENTTYPE;

instance->mDispatcher->post(msg, true /* realTime */)实现分析:
发送该消息到消息分发者进行分析

// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp]
void OMXNodeInstance::CallbackDispatcher::post(const omx_message &msg, bool realTime) {
	// 加锁添加
    Mutex::Autolock autoLock(mLock);

    // 添加消息事件在OMX消息队列中
    mQueue.push_back(msg);
    // kMaxQueueSize = 12
    if (realTime || mQueue.size() >= kMaxQueueSize) {
    	// realTime 为true或者当前消息队列大小已大于默认最大值(12)时均需要唤醒可能wait的消息取出端
    	// 消息取出端实现将下面的分析
    	// 备注:根据早前初始化时流程分析可知消息取出端实现为OMXNodeInstance::CallbackDispatcher::loop(),
    	// 而loop()该方法早前已分析过,将会执行分析其分发消息实现dispatch(messages)
        mQueueChanged.signal();
    }
}

消息取出端实现dispatch(messages)实现分析:
分发消息队列

// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp]
void OMXNodeInstance::CallbackDispatcher::dispatch(std::list<omx_message> &messages) {
	// mOwner对象就是缓存的当前OMXNodeInstance对象
    if (mOwner == NULL) {
        ALOGV("Would have dispatched a message to a node that's already gone.");
        return;
    }
    mOwner->onMessages(messages);
}

mOwner->onMessages(messages)实现分析:

// [frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp]
void OMXNodeInstance::onMessages(std::list<omx_message> &messages) {
	// 注意:OMX消息实际分发发送时机是通过上面的 realTime 为true或消息大于12个时,才会触发分发,
	// 或者加入消息时正在分发消息也会触发分发,也就是说当前实现的消费者-生产者模式和通常情况实现的不同,
	// 它的消息一次性读取可能是一个或多个消息
	// 循环处理每一个消息,其实际下面处理结果将是,若OMXNode自身的handleMessage能够处理,
	// 那么将不会给上层处理【mObserver->onMessages】,若不能处理则剩下不能处理的消息将会通过该回调监听mObserver对象给到上层去处理
    for (std::list<omx_message>::iterator it = messages.begin(); it != messages.end(); ) {
    	// 处理消息并判断处理结果
    	// 见8.2.1小节分析
        if (handleMessage(*it)) {
        	// OMXNode节点实例自身能够处理则从消息列表中移除它
            messages.erase(it++);
        } else {
        	// 未拦截处理,则继续下一个检查
            ++it;
        }
    }

    if (!messages.empty()) {
    	// 若最终剩下的OMX消息列表不为空,则将会执行此前创建OMXNodeInstance时传入的回调监听实现类对象
    	// 备注:其实在OMXNode类中该对象mObserver实际上只是个Bp代理对象
    	// 见8.2.2小节分析
        mObserver->onMessages(messages);
    }
}

8.2.1、handleMessage(*it)处理消息实现分析:
OMXNodeInstance的该方法返回true才表明自身处理该事件类型
由于本章节接下来内容篇幅过长,因此必须放入另一章节分析,请查看:
TODO 【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 4】【03】

这篇关于【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 4】【02】的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!