经过一段时间的研究,对android视频解码,直到显示端的整体流程,有了浅薄的理解

这里总结一下,解码出来的视频帧,是怎么一步步走到显示的。

平台: Exynos 4412  android 4.4

1,  Exynos 4412 视频编解码依赖于硬件MFC。  解码出来的数据,不会进行原始数据的搬运。实际传递的是这些解码完数据的物理地址。

2,显示端硬件获得这些数据的物理地址,用这些物理地址取数据进行显示

3,这里不研究解码驱动和显示驱动。内核态的东西留到以后再研究。这里只研究了android中的流程

4,整体上,可以理解为

MFC->   V4L2  ->  OMX ->     awesomeplayer ->  sufrfaceflinger  ->    bufferqueue->   sufrfaceflinger   ->     HWcomposer ->  GPU -> DISPLAY


1,初始化的时候,
-----> allocateOutputBuffersFromNativeWindow
-----> status_t OMXNodeInstance::useGraphicBuffer
-----> virtual status_t useGraphicBuffer
-----> status_t OMXNodeInstance::useGraphicBuffer2_l
-----> OMX_UseBuffer
-----> SEC_OMX_UseBuffer   //temp_bufferHeader->pBuffer        = pBuffer; 
//这里就是把graphicBuffer->handle复制给了bufferHeader->pBuffer,也就是data_ptr,也就是存贮一些地址值的内存,也就是graphicBuffer->handle中最终存储了解码完毕数据存放地址信息,最后传给显示
说到底,就是MediaBuffer中指向了有效的data_ptr, MediaBuffer指向了GraphicBuffer,graphicBuffer->handle实际上就是data_ptr
所以一开始传的是MediaBuffer,最后又传的GraphicBuffer, 但是都能使用到data_ptr


2,解码过程运转起来之后
-----> SEC_OutputBufferGetQueue里面,把pSECComponent->processData[OUTPUT_PORT_INDEX].dataBuffer = dataBuffer->bufferHeader->pBuffer;
-----> SEC_MFC_H264_Decode_Nonblock里面
-----> SsbSipMfcDecGetOutBuf 获取到解码完的数据物理地址填充到dataBuffer,也就是填充到了dataBuffer->bufferHeader->pBuffer,然后一步步往上送
-----> sec_mfc_bufferProcess
-----> SEC_Postprocess_OutputData
-----> SEC_OutputBufferReturn
-----> FillBufferDone
-----> OMX_ERRORTYPE OMXNodeInstance::OnFillBufferDone
-----> OMX_ERRORTYPE OMX::OnFillBufferDone(   node_id node, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer)
 {
    ALOGV("OnFillBufferDone buffer=%p", pBuffer);
    omx_message msg;
    msg.type = omx_message::FILL_BUFFER_DONE;
    msg.node = node;
    msg.u.extended_buffer_data.buffer = pBuffer;
    msg.u.extended_buffer_data.range_offset = pBuffer->nOffset;
    msg.u.extended_buffer_data.range_length = pBuffer->nFilledLen;
    msg.u.extended_buffer_data.flags = pBuffer->nFlags;
    msg.u.extended_buffer_data.timestamp = pBuffer->nTimeStamp;
    msg.u.extended_buffer_data.platform_private = pBuffer->pPlatformPrivate;
    msg.u.extended_buffer_data.data_ptr = pBuffer->pBuffer;      ///pBuffer->pBuffer实际上就是dataBuffer->bufferHeader->pBuffer;  也就是  pSECComponent->processData[OUTPUT_PORT_INDEX].dataBuffer,  也就是SEC_MFC_H264_Decode_Nonblock里面的pOutputData->dataBuffer;
//    void *pOutputBuf = (void *)pOutputData->dataBuffer; 这里的pOutputBuf的内容就传给了data_ptr
//    SEC_OSAL_Memcpy(pYUVBuf[0], &(outputInfo.YPhyAddr), sizeof(outputInfo.YPhyAddr));
    //    SEC_OSAL_Memcpy((unsigned char *)pYUVBuf[0] + (sizeof(void *) * 1), &(outputInfo.CPhyAddr), sizeof(outputInfo.CPhyAddr));
    //    SEC_OSAL_Memcpy((unsigned char *)pYUVBuf[0] + (sizeof(void *) * 2), &(outputInfo.YVirAddr), sizeof(outputInfo.YVirAddr));
    //    SEC_OSAL_Memcpy((unsigned char *)pYUVBuf[0] + (sizeof(void *) * 3), &(outputInfo.CVirAddr), sizeof(outputInfo.CVirAddr));
//    data_ptr 里面存的就是几个虚拟地址
    findDispatcher(node)->post(msg);
    return OMX_ErrorNone;
}
-----> void OMX::CallbackDispatcher::post(const omx_message &msg) {
   Mutex::Autolock autoLock(mLock);
    mQueue.push_back(msg);
    mQueueChanged.signal();
}

3,stagefright的某个循环:
  void OMXCodec::on_message(const omx_message &msg) {
    switch (msg.type) {
        case omx_message::FILL_BUFFER_DONE:
        {
                mFilledBuffers.push_back(i);
                mBufferFilled.signal();
                if (mIsEncoder) {
                    sched_yield();
                }
        }
}
FillBufferDone时候最终会通过OMX的消息机制走到OMXCodec::on_message中进入case omx_message::FILL_BUFFER_DONE:主要工作是,记录这个已经填充的buffer的索引号,然后将这个索引号保存在
                mFilledBuffers.push_back(i);
                mBufferFilled.signal();
看到mBufferFilled这个Condition被signal了,那么谁wait在这个Condition上呢?看上OMXCodec::read函数中我红色标注的部分。原来在OMXCodec::read函数的后半段中会一直
while (mState != ERROR && !mNoMoreOutputData && mFilledBuffers.empty()) {
        if ((err = waitForBufferFilled_l()) != OK) {
            return err;
        }
    }
       那么FILL_BUFFER_DONE之后,在OMXCodec::read函数中被返回给AwesomePlayer的mVideoBuffer就是在OMX框架中outPutPort上的buffer。
通过上面的分析可以看到,虽然在OMX框架中 Input/OutPutPort上的buffer的生产和消费是异步,但是还是通过了一个Condition mBufferFilled来做同步。这个生产者消费者的问题,来用一个信号量做同步,还是比较好的。
         
4,stagefright的某个循环:   
void AwesomePlayer::onVideoEvent()   
-----> status_t err = mVideoSource->read(&mVideoBuffer, &options);
-----> mVideoRenderer->render(mVideoBuffer);
-----> virtual void render(MediaBuffer *buffer) {
        ATRACE_CALL();
        int64_t timeUs;
        CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));
        native_window_set_buffers_timestamp(mNativeWindow.get(), timeUs * 1000);
        status_t err = mNativeWindow->queueBuffer(
                mNativeWindow.get(), buffer->graphicBuffer().get(), -1);  //在此之前,传递的都是MediaBuffer,在allocateOutputBuffersFromNativeWindow初始化的时候,MediaBuffer中的成员已经指向。graphicBuffer。从这一步开始,一步步传递的是graphicBuffer。但是前面讲了,graphicBuffer->handle实际上就是要传递的buffer。里面包含了解码物理地址
        if (err != 0) {
            ALOGE("queueBuffer failed with error %s (%d)", strerror(-err),
                    -err);
            return;
        }
        sp metaData = buffer->meta_data();
        metaData->setInt32(kKeyRendered, 1);
    }

×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××以上为stagefright的内容,以下为surfaceflinger的内容×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

-----> mNativeWindow->queueBuffer
-----> ANativeWindow::queueBuffer      = hook_queueBuffer;
 -----> Surface::hook_queueBuffer()
-----> int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) //查出32个slot buffer中的哪一个,把index传下去
 -----> virtual status_t queueBuffer
-----> status_t BnGraphicBufferProducer::onTransact
       case QUEUE_BUFFER: {
            CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
            int buf = data.readInt32();
            QueueBufferInput input(data);
            QueueBufferOutput* const output =
                    reinterpret_cast(
                            reply->writeInplace(sizeof(QueueBufferOutput)));
            status_t result = queueBuffer(buf, input, output);
            reply->writeInt32(result);
            return NO_ERROR;
        } break;
   ----->  status_t BufferQueue::queueBuffer   //item.mBuf = buf; mQueue.push_back(item); listener->onFrameAvailable();
  ----->  void FramebufferSurface::onFrameAvailable()  里面nextBuffer获得GraphicBuffer
  ----->  mHwc.fbPost
  ----->  int HWComposer::fbPost
  ----->  status_t HWComposer::setFramebufferTarget   //disp.fbTargetHandle = buf->handle;  disp.framebufferTarget->handle = disp.fbTargetHandle;  前面已经讲了,graphicBuffer->handle 存的就是有效数据。 也就是最终disp.framebufferTarget->handle指向有效数据
  在初始化的时候,HWComposer::createWorkList里面  disp.framebufferTarget = &disp.list->hwLayers[numLayers - 1];让framebufferTarget指向了disp.list->hwLayers[numLayers - 1]。 所以这里最终disp.list->hwLayers[numLayers - 1]->handle指向有效数据
    
 5, Vsync事件触发
  -----> dispatchRefresh
  -----> SurfaceFlinger::onMessageReceived
  ----> handleMessageRefresh
-----> doComposition
----->  doDisplayComposition
----->  clip.isEmpty
----->  onDraw
----->  drawWithOpenGL 0,0,1280,720,1280,720,
-----> GPU

总结:
1,graphicBuffer->handle 存放有效数据。具体在哪里初始化申请的,待研究
2,OMX,stagefright,surfaceflinger,HWcomposer说到底就是在传递这个graphicBuffer->handle
3,传到HWcomposer后,并不是马上刷新屏幕。而是先用setFramebufferTarget存好。等Vsync时机到了,刷新
  

以上只是对流程进行了简单梳理

其中涉及到很多surfaceflinger,bufferqueue,binder的东西。

让人看的头疼。需要后面继续深入研究

其中难免有错误。请大家指出

更多相关文章

  1. 集成Android免费语音合成功能(在线、离线、离在线融合),有这一篇文
  2. Android如果对APK进行加密,提高反编译难度(思路)
  3. Android使用WCF的服务程序之入门
  4. Android(安卓)数据库文件 db 的备份和重载
  5. Android全局主题样式控制,语言设置、主题颜色、字体大小、字体样
  6. Android照相功能驱动层中HAL的实现(基于OK6410开发板+OV9650摄像
  7. Android(安卓)必须知道2018年流行的框架库及开发语言
  8. 处理Android应用在后台被杀死
  9. Android(安卓)API Guides---Near Field Communication

随机推荐

  1. Notification通知栏图标5.0以后效果不同
  2. Android(安卓)解决程序启动时的黑屏问题
  3. Android多线程及异步处理问题
  4. 网络请求框架----理解 HTTPs
  5. android http 如何使用Put方式进行网络请
  6. Android项目管理之配置管理
  7. Android(安卓)ANR 问题第二弹------Input
  8. android自定义view 右侧字母导航
  9. 【Android即时通讯】Android(安卓)高仿微
  10. android 根据文件Uri获取图片url