編輯:關於Android編程
之前幾文在分析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; } spgraphicBuffer(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架構下的編解碼機制。
本文翻譯了這篇文章:Using the Android action bar (ActionBar) - Tutorial 1、ActionBar的簡介 ActionB
前言本文的中文注釋代碼demo更新在我的github上。SDWebImage是一個十分有名的Objective-C第三方開源框架,作用是: Asynchronous im
前言在Android開發中ListView是最為常用的控件之一,基本每個應用都會涉及到它,要使用ListView列表展示,就不可避免地涉及到另外一個東西—&m
激動人心的時刻到來了:你花了幾天和幾周時間(甚至是幾個月)制作了一個精彩的 App,准備發布到全世界。剩下來的事情就是將 App 提交到蘋果商店了,但是 —&