版權聲明:本文為CSDN部落客(天才2012)原創文章,未經部落客允許不得轉載。
之前幾文在分析OMX_Codec的資料流時主要以對輸入緩存區的說明為主,這裡單獨是為了加深對SurfaceFlinger的了解,特地将用于渲染的輸出緩存區申請等拿出來做一次分析,看其是如何完成建立,并通知底層的解碼器元件進行解碼輸出的。該部分的核心内容需要有較強的surfaceflinger顯示子產品相關的知識點。
1.來看解碼輸出緩存在surfaceflinger下的緩存申請:
[cpp] view plain copy
- 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的設定:
[cpp] view plain copy
- 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個。
[cpp] view plain copy
- 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]中去。
[cpp] view plain copy
- // 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中的圖形緩存申請結合在一起的話,就更能說明問題:
[cpp] view plain copy
- 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架構下的編解碼機制。