之前几文在分析OMX_Codec的数据流时主要以对输入缓存区的说明为主,这里单独是为了加深对SurfaceFlinger的理解,特地将用于渲染的输出缓存区申请等拿出来做一次分析,看其是如何完成创建,并通知底层的解码器组件进行解码输出的。该部分的核心内容需要有较强的surfaceflinger显示模块相关的知识点。
1.来看解码输出缓存在surfaceflinger下的缓存申请:
status_t OMXCodec::allocateBuffersOnPort(OMX_U32 portIndex) { if (mNativeWindow != NULL && portIndex == kPortIndexOutput) { return allocateOutputBuffersFromNativeWindow();//使用surface渲染,为输出分配图形缓存GraphicBuffer }
可以看到当申请的是输入缓存区时并且本地 mNativeWindow存在,这说明了输出的buffer是需要去直接完成render的,所以输出缓存区要从本地窗口buffer进行申请:
2.allocateOutputBuffersFromNativeWindow的实现
SurfaceFlinger架构下的Buffer申请机制可以看博文Android4.2.2 SurfaceFlinger之图形缓存区申请与分配dequeueBuffe, 有了这些基础的知识后,对这个函数的实现会变的更加的轻松,分以下几个步骤来完成:
step1:获取待播放的视频格式,通过getParameter来获取底层的解码器对显示的参数,完成对显示buffer的设置:
status_t err = mOMX->getParameter( mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); if (err != OK) { CODEC_LOGE("getParameter failed: %d", err); return err; } if(def.format.video.eColorFormat == OMX_COLOR_FormatYUV420Planar) { err = native_window_set_buffers_geometry( mNativeWindow.get(), def.format.video.nFrameWidth, def.format.video.nFrameHeight, HAL_PIXEL_FORMAT_YV12); } else { err = native_window_set_buffers_geometry( mNativeWindow.get(), def.format.video.nFrameWidth, def.format.video.nFrameHeight, def.format.video.eColorFormat); }
step2:设置显示需要用到的buffer格式,比如解码器使用的个数为2个循环,故需要设置为2个。
err = native_window_set_buffer_count( mNativeWindow.get(), def.nBufferCountActual);
step3: graphicBuffer的申请
这里是真正的实现当前需要的buffer的图像缓存区的内存申请,可以看到native_window_dequeue_buffer_and_wait函数,本地窗口进行dequeue_buffer操作,如果熟悉SurfaceFlinger架构的话,就知道其实质是通过匿名的Binder架构进行BufferQueue的dequeue_buffer的操作,请求SurfaceFlinger通过Gralloc模块分配帧缓存或者pmem匿名共享内存缓存区等。
申请到的buf(应用侧的缓存区ANativeWindowBuffer)将新建一个GraphicBuffer对象,而这个buf所具备的特点是完成了对分配到的缓存区进行了在客户端亦称为应用端进行了mmap的操作,使得客户端对这个图形缓存的操作,实现了对服务端的操作。两者的物理空间实现了一致。相关的内容深入可看博文:Android4.2.2 SurfaceFlinger之图形缓存区申请与分配dequeueBuffer。上述的过程其实也就是OpenGL ES中的eglsweapbuffer的内部机制。
在完成了Buf的申请后,需要控制底层的解码器将输出端口调整为当前的buf端口中故有了mOMX->useGraphicBuffer的操作,并将最终的buffer_id切换保存到mPortBuffers[kPortIndexOutput]中去。
// Dequeue buffers and send them to OMX for (OMX_U32 i = 0; i < def.nBufferCountActual; i++) { ANativeWindowBuffer* buf; err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf);//请求SF完成buffer的申请 if (err != 0) { ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err); break; } sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(buf, false));//新建了一个应用端的图形缓存区对象保存ANativeWindowBuffer BufferInfo info; info.mData = NULL; info.mSize = def.nBufferSize; info.mStatus = OWNED_BY_US; info.mMem = NULL; info.mMediaBuffer = new MediaBuffer(graphicBuffer); info.mMediaBuffer->setObserver(this); mPortBuffers[kPortIndexOutput].push(info);//加入到输入口 IOMX::buffer_id bufferId; err = mOMX->useGraphicBuffer(mNode, kPortIndexOutput, graphicBuffer, &bufferId);//当前申请的图像buffer通知底层解码器作为输出buffer if (err != 0) { CODEC_LOGE("registering GraphicBuffer with OMX IL component " "failed: %d", err); break; } mPortBuffers[kPortIndexOutput].editItemAt(i).mBuffer = bufferId; CODEC_LOGV("registered graphic buffer with ID %p (pointer = %p)", bufferId, graphicBuffer.get()); }
step4 基于surfaceflinger的渲染
AwesomeNativeWindowRenderer在前面的博文中也有提到过,和step3中的图形缓存申请结合在一起的话,就更能说明问题:
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);//直接使用queuebuffer进行渲染显示 if (err != 0) { ALOGE("queueBuffer failed with error %s (%d)", strerror(-err), -err); return;
这里使用queueBuffer完成当前图像缓存的投递,buffer->graphicBuffer().get()来获取这个本地Buffer。最终显示的核心机制将由surfacefliner去完成。
如果有时间,将集中分析底层的A31下编解码器的实现框架,主要是基于全志A31下的cedarx架构下的编解码机制。