Android_ICS_OMX_In_Stagefright------>2开始解码(软解)

来源:互联网 发布:刺客信条4黑旗优化补丁 编辑:程序博客网 时间:2024/05/16 05:13

当应用层调用mediaplayer.start()的时候,在framework层对应的是在awesomeplayerpost一个mVideoEventTimedEventQueue中等待被调度。

        当其被调度到的时候,会激活回调函数onVideoEvent        在这个回调函数中,会做音视频的同步处理。代码很长捡关键的贴。        void AwesomePlayer::onVideoEvent() {        for (;;) {...            status_t err = mVideoSource->read(&mVideoBuffer, &options); ...            initRenderer_l();...             mVideoRenderer->render(mVideoBuffer);     }在这个回调函数中可以看到这样一句话,status_t err = mVideoSource->read(&mVideoBuffer, &options);其中,mVideoBuffer 是一个MediaBuffer类型的成员变量。还记得mVideoSource是什么类型吗?这里的mVideoSource,就是前面返回的OMXCodec,那么实际调用是的:        status_t OMXCodec::read(MediaBuffer **buffer, const ReadOptions *options),这个read函数会填充MediaBuffer *mVideoBuffer这个成员变量,然后交给Renderer来渲染输出。看看read函数的实现:                 status_t OMXCodec::read(MediaBuffer **buffer, const ReadOptions *options) {        …...        drainInputBuffers();        if (mState == EXECUTING) {            fillOutputBuffers();        }    }        …...    while (mState != ERROR && !mNoMoreOutputData && mFilledBuffers.empty()) {        if ((err = waitForBufferFilled_l()) != OK) {            return err;        }    }    if (mState == ERROR) {        return UNKNOWN_ERROR;    }    if(seeking) {        CHECK_EQ((int)mState, (int)FLUSHING);        setState(EXECUTING);    }    if (mFilledBuffers.empty()) {        return mSignalledEOS ? mFinalStatus : ERROR_END_OF_STREAM;    }    if (mOutputPortSettingsHaveChanged) {        mOutputPortSettingsHaveChanged = false;        return INFO_FORMAT_CHANGED;    }    size_t index = *mFilledBuffers.begin();    mFilledBuffers.erase(mFilledBuffers.begin());    BufferInfo *info = &mPortBuffers[kPortIndexOutput].editItemAt(index);    CHECK_EQ((int)info->mStatus, (int)OWNED_BY_US);    info->mStatus = OWNED_BY_CLIENT;    info->mMediaBuffer->add_ref();    *buffer = info->mMediaBuffer;    return OK;}在这个read函数中最重要的两个方法是drainInputBuffers()fillOutputBuffers(),可以看到这两个函数是先后执行的。先来看看drainInputBuffers()方法中,做了哪些操作。        void OMXCodec::drainInputBuffers() {        …...        Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexInput];        for (size_t i = 0; i < buffers->size(); ++i) {            BufferInfo *info = &buffers->editItemAt(i);         …...                       if (!drainInputBuffer(info)) {                break;            }         …...}这个 drainInputBuffers函数从输入端口的buffers中取出之前分配好的BufferInfo,然后交给drainInputBuffer(info)函数来处理,函数实现如下:bool OMXCodec::drainInputBuffer(BufferInfo *info) {            //comment by Alan, this buffer has only codec config informations        status_t err = mOMX->emptyBuffer(                mNode, info->mBuffer, 0, size,                OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_CODECCONFIG,                0);           for (;;) {        …....         MediaBuffer *srcBuffer;        …...        err = mSource->read(&srcBuffer);        …...        memcpy((uint8_t *)info->mData + offset,                        (const uint8_t *)srcBuffer->data()                            + srcBuffer->range_offset(),                        srcBuffer->range_length());        ….....        err = mOMX->emptyBuffer(            mNode, info->mBuffer, 0, offset,            flags, timestampUs);        …....}这个函数比较复杂,主要的关键代码如上,在这个函数中,首先会把一些和编解码组件相关的specific data送给omx框架来调用一次emptybuffer,然后会进入一个for(;;)循环,在这个for循环内,会通过mSource->read(&srcBuffer)srcBuffer中填充一些原始的流数据(从XXXExtractor解析出来未解码的数据),这里mSource就是前面XXXExtractor在文件解析的过程中new XXXSource这里假定是MPEG4Source。函数的大致实现如下:       status_t MPEG4Source::read(MediaBuffer **out, const ReadOptions *options) {        …...        err = mGroup->acquire_buffer(&mBuffer);                if (usesDRM) {            num_bytes_read =                mDataSource->readAt(offset, (uint8_t*)mBuffer->data(), size);        } else {            num_bytes_read = mDataSource->readAt(offset, mSrcBuffer, size);        }        …...        *out = mBuffer;        mBuffer = NULL;        return OK;}这个函数的实现很复杂,在read的时候不但考虑了seek的问题,而且还涉及到了不少多媒体容器格式方面的一些问题。但是关于数据流向的函数就是两个,首先acquire_buffer,这个acquire_buffer和前面的add_buffer对应,在MPEG4Source::start中会被调用到。获得一个mBuffer后就可以通过mDataSource->readAt来往这个buffer上填充数据了。这里的mDataSource实际上可以对应到一个FileSource或者来之网络的CacheSourcemBuffer数据填充完后赋值给调用函数的 srcBuffer,这样就相当于给 drainInputBuffer(BufferInfo *info)中的info填充了数据,然后回到drainInputBuffer函数,接着emptyBuffer。实际上这里的BufferInfo *info的数据填充过程还有一些细节需要弄清楚,BufferInfo的结构体各个字段的意义还有待进一步弄明白。        到了mOMX->emptyBuffer()基本就进入OMX框架了,无非是OMX那一套消息机制,之前已经说过,这里就不在赘述了。如果还有不清楚的话,其在openMax文档的时候我画的一张时序图。还有一点需要明确,在emptyBuffer的时候会将App Data  copyToOMX,将来之Appcation的数据copyOMX框架中,而在OMXNodeInstance::onMessage  FILL_BUFFER_DONE的时候,会将数据从OMX框架中copyAppcation空间,即copyFromOMXAppcation<--  data --->OMX       来看看其中一个函数的实现,       void CopyToOMX(const OMX_BUFFERHEADERTYPE *header) {        if (!mIsBackup) {            return;        }        memcpy(header->pBuffer + header->nOffset,               (const OMX_U8 *)mMem->pointer() + header->nOffset,               header->nFilledLen);    }注意以下,mMem这个成员变量具体指什么,我们看看emptyBuffer这个函数的实现:       status_t OMXNodeInstance::emptyBuffer(        OMX::buffer_id buffer,        OMX_U32 rangeOffset, OMX_U32 rangeLength,        OMX_U32 flags, OMX_TICKS timestamp) {    Mutex::Autolock autoLock(mLock);    OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer;    header->nFilledLen = rangeLength;    header->nOffset = rangeOffset;    header->nFlags = flags;    header->nTimeStamp = timestamp;    BufferMeta *buffer_meta =        static_cast<BufferMeta *>(header->pAppPrivate);    buffer_meta->CopyToOMX(header);    OMX_ERRORTYPE err = OMX_EmptyThisBuffer(mHandle, header);    return StatusFromOMXError(err);}去看一下BufferMeta的构造函数就可以发现,上面mMem实际上指的是head->pAppPrivate那么具体的解码过程什么时候开始呢?        OMX框架中所有的软解部分,最终都会通过OMX的消息机制走到SimpleSoftOMXComponent::onMessageReceived,函数的实现如下: void SimpleSoftOMXComponent::onMessageReceived(const sp<AMessage> &msg) {    Mutex::Autolock autoLock(mLock);    switch (msg->what()) {        case kWhatSendCommand:        {            int32_t cmd, param;            CHECK(msg->findInt32("cmd", &cmd));            CHECK(msg->findInt32("param", &param));            onSendCommand((OMX_COMMANDTYPE)cmd, (OMX_U32)param);            break;        }        case kWhatEmptyThisBuffer:        case kWhatFillThisBuffer:        {            OMX_BUFFERHEADERTYPE *header;            CHECK(msg->findPointer("header", (void **)&header));            CHECK(mState == OMX_StateExecuting && mTargetState == mState);            bool found = false;            for (size_t i = 0; i < mPorts.size(); ++i) {                PortInfo *port = &mPorts.editItemAt(i);                for (size_t j = 0; j < port->mBuffers.size(); ++j) {                    BufferInfo *buffer = &port->mBuffers.editItemAt(j);                    if (buffer->mHeader == header) {                        CHECK(!buffer->mOwnedByUs);                        buffer->mOwnedByUs = true;                        CHECK((msg->what() == kWhatEmptyThisBuffer                                    && port->mDef.eDir == OMX_DirInput)                                || (port->mDef.eDir == OMX_DirOutput));                        port->mQueue.push_back(buffer);                        onQueueFilled(i);                        found = true;                        break;                    }                }            }            CHECK(found);            break;        }        default:            TRESPASS();            break;    }}显然这是一个消息的处理函数,对于 kWhatEmptyThisBufferkWhatFillThisBuffer这类消息都会进入到 onQueueFilled(i)函数,这个 onQueueFilled函数相当于是OMX软件编解码组件的一个入口,函数显示如下:void SoftMPEG4::onQueueFilled(OMX_U32 portIndex) {            …...            Bool success = PVInitVideoDecoder(                    mHandle, vol_data, &vol_size, 1, mWidth, mHeight, mode);           …...            MP4DecodingMode actualMode = PVGetDecBitstreamMode(mHandle);                       PVSetPostProcType((VideoDecControls *) mHandle, 0);                notifyEmptyBufferDone(inHeader);            …...            PVSetReferenceYUV(mHandle, outHeader->pBuffer);        if (PVDecodeVideoFrame(mHandle, &bitstream, &timestamp, &tmp,            &useExtTimestamp,            outHeader->pBuffer) != PV_TRUE) {            notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);            mSignalledError = true;            return;        }        …...        notifyFillBufferDone(outHeader);}上面的函数只保留了几个关键的函数调用,可以看到在这个onQueueFilled函数中会通过一些类似与 PVDecodeVideoFrame进入到OMX 框架中软件编解码的部分。可以看到通过 PVDecodeVideoFrame之后的yuv数据会保存在outHeader->pBuffer字段中。然后通过notifyEmptyBufferDone(inHeader); 或者notifyFillBufferDone(outHeader);来向组件OMX外部通知已经从inPutPort消费了一个buffer或者已经向outPutPort填充了一个buffer        简单说来,当notifyEmptyBufferDone的时候,OMX会记录已经被消费的buffer的索引,然后继续在该索引对应的bufferdrainInputBuffer,当notifyFillBufferDone时候最终会通过OMX的消息机制走到OMXCodec::on_message中进入case omx_message::FILL_BUFFER_DONE:主要工作是,记录这个已经填充的buffer的索引号,然后将这个索引号保存在                mFilledBuffers.push_back(i);                mBufferFilled.signal();看到mBufferFilled这个Conditionsignal了,那么谁wait在这个Condition上呢?看上OMXCodec::read函数中我红色标注的部分。原来在OMXCodec::read函数的后半段中会一直while (mState != ERROR && !mNoMoreOutputData && mFilledBuffers.empty()) {        if ((err = waitForBufferFilled_l()) != OK) {            return err;        }    }       那么FILL_BUFFER_DONE之后,在OMXCodec::read函数中被返回给AwesomePlayermVideoBuffer就是在OMX框架中outPutPort上的buffer通过上面的分析可以看到,虽然在OMX框架中 Input/OutPutPort上的buffer的生产和消费是异步,但是还是通过了一个Condition mBufferFilled来做同步。这个生产者消费者的问题,来用一个信号量做同步,还是比较好的。
原创粉丝点击