经过一段时间的研究,对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->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的东西。
让人看的头疼。需要后面继续深入研究
其中难免有错误。请大家指出