1. 程式人生 > >Android graphics 學習-生產者、消費者、BufferQueue介紹

Android graphics 學習-生產者、消費者、BufferQueue介紹

備註:博文根據Android5.1程式碼分析,為個人學習筆記。如有問題,歡迎指正。
  博文是後續理解Camera preview,recording buffer流轉的基礎。要不然後續Camera buffer部分不好分析。這是這幾天看的筆記,也算是對BufferQueue有了新的認識,仍有許多地方需要探索。首先從整體上初步認識一下graphics元件(圖片來源於Android官方文件)。


  上圖中大家見的最多的就是mediaPlayer和CameraPreview.buffer資料流,首先經過surface->GLConsumer->SurfaceFlinger->hardwareComposer,這樣一條線路,buffer資料就渲染到螢幕上了。這裡為什麼只提到Camera Preview 那麼Camera Recording,Caputre模式下buffer又如何流轉的呢。這裡先提出疑問?後續博文在一一解開。這裡大體上有個整體認識舉行。

一、BufferQueue介紹

  BufferQueue是buffer流轉重要中轉站,理解它的工作機制是非常重要的,它的狀態改變經歷過下面幾個過程。只需要先了解一下buffer狀態和owner轉變過程。後面會結合例子來分析。

  • 1.首先生產者dequeue過來一塊Buffer,此時該buffer的狀態為DEQUEUED,所有者為PRODUCER,生產者可以填充資料了。在沒有dequeue操作時,buffer的狀態為free,所有者為BUFFERQUEUE.
  • 2.生產者填充完資料後,進行queue操作,此時buffer的狀態由DEQUEUED->QUEUED的轉變,buffer所有者也變成了BufferQueue了。
    • 注意
      :這個時候producer代理物件進行queue操作後,producer本地物件會回撥BufferQueue的onFrameAvailable函式,通知消費者有可用的buffer已經就緒了,你可以拿去用了。
  • 3 上面已經通知消費者去拿buffer了,這個時候消費者就進行acquire操作將buffer拿過來,此時buffer的狀態由QUEUED->ACQUIRED轉變,buffer的擁有者由BufferQueue變成Consumer.
  • 4.當消費者已經消費了這塊buffer(已經合成,已經編碼等),就進行release操作釋放buffer,將buffer歸還給BufferQueue,buffer狀態由ACQUIRED變成FREE.buffer擁有者由Consumer變成BufferQueue.

下圖也是從Android官網上引用過來Android7.0的BufferQueue工作狀態圖(與Android5.1意思一樣,只是這個下過更好一點)。

原始碼路徑:frameworks/native/include/gui/BufferSlot.h

enum BufferState {
// FREE indicates that the buffer is available to be dequeued
// by the producer.  The buffer may be in use by the consumer for
// a finite time, so the buffer must not be modified until the
// associated fence is signaled.
//
// The slot is "owned" by BufferQueue.  It transitions to DEQUEUED
// when dequeueBuffer is called.
FREE = 0,

// DEQUEUED indicates that the buffer has been dequeued by the
// producer, but has not yet been queued or canceled.  The
// producer may modify the buffer's contents as soon as the
// associated ready fence is signaled.
//
// The slot is "owned" by the producer.  It can transition to
// QUEUED (via queueBuffer) or back to FREE (via cancelBuffer).
DEQUEUED = 1,

// QUEUED indicates that the buffer has been filled by the
// producer and queued for use by the consumer.  The buffer
// contents may continue to be modified for a finite time, so
// the contents must not be accessed until the associated fence
// is signaled.
//
// The slot is "owned" by BufferQueue.  It can transition to
// ACQUIRED (via acquireBuffer) or to FREE (if another buffer is
// queued in asynchronous mode).
QUEUED = 2,

// ACQUIRED indicates that the buffer has been acquired by the
// consumer.  As with QUEUED, the contents must not be accessed
// by the consumer until the fence is signaled.
//
// The slot is "owned" by the consumer.  It transitions to FREE
// when releaseBuffer is called.
ACQUIRED = 3
};

  雖然Android原始碼中的BufferQueue類中包含一個buffer監聽類定義和靜態建立BufferQueue物件的靜態方法。但是bufferQueue主要工作由類BufferQueueCore來完成,該類中由幾個變數非常值得我們瞭解一下(貼出來都是原始碼中的註釋)。如果想探索其它成員的話,還是擼擼原始碼吧。

  • 1.mAllocator:這個是gralloc buffer分配物件,由SurfaceFlinger提供的,這個在建立消費者Client物件時,會根據例子介紹,先了解一下。
// mAllocator is the connection to SurfaceFlinger that is used to allocate
// new GraphicBuffer objects.
    sp<IGraphicBufferAlloc> mAllocator;
  • 2.mConsumerListener:這個是消費者監聽物件,用於當有幀可用時,bufferQueue會回撥該物件中的onFrameAvailable函式,來通知消費者有幀可用了,趕緊來取buffer.
// mConsumerListener is used to notify the connected consumer of
// asynchronous events that it may wish to react to. It is initially
// set to NULL and is written by consumerConnect and consumerDisconnect.
    sp<IConsumerListener> mConsumerListener;
  • 3.mSlots:為存放buffer的地方,它的數量為64,這裡先劇透一下一個surface物件對應一個mSlot物件,一個mSlot物件最多儲存64塊Buffer,即BufferQueue最多能運轉64塊buffer。這樣的話,每一個surface最多能申請64個buffer.但為了確保buffer能非同步進行,一般有2個reserver的。
// mSlots is an array of buffer slots that must be mirrored on the producer
// side. This allows buffer ownership to be transferred between the producer
// and consumer without sending a GraphicBuffer over Binder. The entire
// array is initialized to NULL at construction time, and buffers are
// allocated for a slot when requestBuffer is called with that slot's index.
BufferQueueDefs::SlotsType mSlots;
//----------------------------------
// BufferQueue will keep track of at most this value of buffers.
// Attempts at runtime to increase the number of buffers past this
// will fail.
enum { NUM_BUFFER_SLOTS = 64 };//官方定義的是64個
typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
  • 4.mFrameCounter:表示已經入佇列和剛剛分配的buffer的數量。
    // mFrameCounter is the free running counter, incremented on every
    // successful queueBuffer call and buffer allocation.
    uint64_t mFrameCounter;
  • 5.createBufferQueue():建立bufferQueue介面。該靜態方法,內部根據surfaceFlinger傳過來的buffer分配物件allocator,然後依次建立BufferQueue,生產者,消費者,建立生產者和消費者過程中,將它們都捆綁到了同一個BufferQueue上。
void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
        sp<IGraphicBufferConsumer>* outConsumer,
        const sp<IGraphicBufferAlloc>& allocator) {
    LOG_ALWAYS_FATAL_IF(outProducer == NULL,
            "BufferQueue: outProducer must not be NULL");
    LOG_ALWAYS_FATAL_IF(outConsumer == NULL,
            "BufferQueue: outConsumer must not be NULL");

    sp<BufferQueueCore> core(new BufferQueueCore(allocator));
    LOG_ALWAYS_FATAL_IF(core == NULL,
            "BufferQueue: failed to create BufferQueueCore");

    sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core));
    LOG_ALWAYS_FATAL_IF(producer == NULL,
            "BufferQueue: failed to create BufferQueueProducer");
//請先留意一下消費者物件類是BufferQueueConsumer,該類能夠實現程序間通訊
    sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core));
    LOG_ALWAYS_FATAL_IF(consumer == NULL,
            "BufferQueue: failed to create BufferQueueConsumer");

    *outProducer = producer;//返回到client中
    *outConsumer = consumer;
}

二、消費者Consumer介紹

消費者介面類主要實現了IGraphicBufferConsumer介面。它們的繼承引用關係如下所示:

  雖然這裡定義IGraphicBufferConsumer消費者標準介面。但是在下面的事例中,沒跟蹤到建立代理物件的地方。這裡先不做深入理解吧。下面列出來一些比較熟悉的介面加上官方註釋。

virtual status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen) = 0;
virtual status_t releaseBuffer(int buf, uint64_t frameNumber,
            EGLDisplay display, EGLSyncKHR fence,
            const sp<Fence>& releaseFence) = 0;
// consumerConnect connects a consumer to the BufferQueue.  Only one
// consumer may be connected, and when that consumer disconnects the
// BufferQueue is placed into the "abandoned" state, causing most
// interactions with the BufferQueue by the producer to fail.
// controlledByApp indicates whether the consumer is controlled by
// the application.
//
// consumer may not be NULL.
//
// Return of a value other than NO_ERROR means an error has occurred:
// * NO_INIT - the buffer queue has been abandoned
// * BAD_VALUE - a NULL consumer was provided
virtual status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) = 0;

  上面列出來的幾個介面,讓我頭痛的是consumerConnect()這個介面。當時在分析註冊幀監聽物件時,始終不確定Consumer到底呼叫的是哪一個consumerConnect()方法。一開始以為呼叫的是代理物件的consumerConnect()方法。但是在建立BufferQueue物件時,建立的是BufferQueueConsumer物件,該類繼承了BnGraphicBufferConsumer().到後來我才發現,其實BufferQueueConsumer類中已經實現了consumerConnect()方法。

void Layer::onFirstRef() {
    // Creates a custom BufferQueue for SurfaceFlingerConsumer to use
    sp<IGraphicBufferProducer> producer;
    sp<IGraphicBufferConsumer> consumer;
    BufferQueue::createBufferQueue(&producer, &consumer);
    mProducer = new MonitoredProducer(producer, mFlinger);
    mSurfaceFlingerConsumer = new SurfaceFlingerConsumer(consumer, mTextureName);
    mSurfaceFlingerConsumer->setConsumerUsageBits(getEffectiveUsage(0));
    mSurfaceFlingerConsumer->setContentsChangedListener(this);
    mSurfaceFlingerConsumer->setName(mName);
    //此處省略幾行程式碼,目前不是我們關心的。
    const sp<const DisplayDevice> hw(mFlinger->getDefaultDisplayDevice());
    updateTransformHint(hw);
}

  客戶端在請求建立surface時,會在SurfaceFlinger服務中,建立一個Layer物件,而在第一次引用該layer物件時,就會呼叫onFirstRef函式。上面程式碼可以發現建立Layer物件同時建立了生產者,消費者,BufferQueue物件,其中將創建出來的IGraphicBufferConsumer consumer消費者物件傳給了SurfaceFlingerConsumer()建構函式。建立SurfaceFlingerConsumer物件有點曲折,分成下面幾步走。

  • 1.由於SurfaceFlingerConsumer類繼承了ConsumerBase類,所以會回撥基類的建構函式。該建構函式中的bufferQueue輸入引數,就是我們在建立BufferQueue物件時,建立的BufferQueueConsumer物件,該類已經實現了consumerConnect方法,所以下面ConsumerBase建構函式中直接呼叫了該方法,註冊監聽物件到BufferQueue,這裡的註冊只是註冊一個軀殼,真正的監聽物件註冊是由上面的setContentsChangedListener方法實現。
ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp) :
        mAbandoned(false),
        mConsumer(bufferQueue) {
    // Choose a name using the PID and a process-unique ID.
    mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());

    // Note that we can't create an sp<...>(this) in a ctor that will not keep a
    // reference once the ctor ends, as that would cause the refcount of 'this'
    // dropping to 0 at the end of the ctor.  Since all we need is a wp<...>
    // that's what we create.
//以當前類物件為輸入引數,重新建立一個監聽物件。
    wp<ConsumerListener> listener = static_cast<ConsumerListener*>(this);
    sp<IConsumerListener> proxy = new BufferQueue::ProxyConsumerListener(listener);

    status_t err = mConsumer->consumerConnect(proxy, controlledByApp);
    if (err != NO_ERROR) {
        CB_LOGE("ConsumerBase: error connecting to BufferQueue: %s (%d)",
                strerror(-err), err);
    } else {
        mConsumer->setConsumerName(mName);
    }
}
  • 2.註冊幀可用通知物件
    上面在ConsumerBase建構函式中,建立ProxyConsumerListener監聽物件時,將ConsumerBase物件傳入給ProxyConsumerListener,其實這裡面真正的監聽物件是通過ConsumerBase可以修改的。
    virtual status_t consumerConnect(const sp<IConsumerListener>& consumer,
            bool controlledByApp) {
        return connect(consumer, controlledByApp);//直接呼叫了connect,如下所示
    }
//在原始檔中實現
status_t BufferQueueConsumer::connect(
        const sp<IConsumerListener>& consumerListener, bool controlledByApp) {
    ATRACE_CALL();
//此處省略一些錯誤檢查程式碼。
    Mutex::Autolock lock(mCore->mMutex);

    if (mCore->mIsAbandoned) {
        BQ_LOGE("connect(C): BufferQueue has been abandoned");
        return NO_INIT;
    }
    //將消費者監聽物件註冊到BufferQueue中,這裡可以看到,監聽物件賦給了BufferQueue的mConsumerListener成員。
    mCore->mConsumerListener = consumerListener;
    mCore->mConsumerControlledByApp = controlledByApp;

    return NO_ERROR;
}
  • 3.幀可用通知函式onFrameAvailable幹了什麼
    由於在Layer時onFirstRef()函式中呼叫了setContentsChangedListener(this),其時Layer實現了FrameAvailableListener介面。下面就是在Layer實現的onFrameAvailable方法。
void Layer::onFrameAvailable(const BufferItem& item) {
    // Add this buffer from our internal queue tracker
    { // Autolock scope
        Mutex::Autolock lock(mQueueItemLock);
        mQueueItems.push_back(item);
    }

    android_atomic_inc(&mQueuedFrames);
    mFlinger->signalLayerUpdate();//通知更新Layer.
}
  • 4.何時觸發幀可用通知onFrameAvailable()回撥
    當生產者進行queueBuffer操作時,會在生產者本地物件中回撥註冊到BufferQueue中的幀可用回撥。
status_t BufferQueueProducer::queueBuffer(int slot,
        const QueueBufferInput &input, QueueBufferOutput *output) {
//.......
    sp<IConsumerListener> frameAvailableListener;
    sp<IConsumerListener> frameReplacedListener;
//......
//由於生產者和消費者屬於同一個BufferQueue物件,那麼這裡取出來的監聽物件就是
//上面我們註冊進去的。
    frameAvailableListener = mCore->mConsumerListener;//取到註冊到BufferQueue中的幀可用通知。
//......
    if (frameAvailableListener != NULL) {
        frameAvailableListener->onFrameAvailable(item);
    } else if (frameReplacedListener != NULL) {
        frameReplacedListener->onFrameReplaced(item);
    }
//......
}

三、生產者Producer介紹

  生產者實現IGraphicBufferProducer的介面,在實際執行過程中,應用端存在代理物件,SurfaceFlinger端存在本地物件。生產者代理物件通過Binder通訊,不斷的dequeueBuffer和queueBuffer操作,本地物件同樣響應這些操作請求,這樣buffer就轉了起來了。
  下面列舉出了和生產者有密切關係的幾個類。不同的是Surface類中包含了一個BpGraphicBufferProducer代理物件介面,它的DequeueBuffer和queueBuffer操作實際上是通過生產者代理物件實現的。

這裡介紹幾個非常重要的大咖,註釋是官方的,應該是最好的解釋了。

  • 1.requestBuffer:字面意思就是請求buffer,留意一下它的引數,可以知道是請求序號為slot的buffer。如果返回值是無效的,必須在進行一次dequeueBuffer操作。
// requestBuffer returns the GraphicBuffer for slot N.
//
// In normal operation, this is called the first time slot N is returned
// by dequeueBuffer.  It must be called again if dequeueBuffer returns
// flags indicating that previously-returned buffers are no longer valid.
virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);
  • 2.dequeueBuffer:重磅大咖,它的作用就是獲取下一塊可用buffer的slot索引index.
    如果該index的buffer為NULL,則會自動申請buffer.其中輸出引數fence物件,用來同步該塊Buffer.Buffer的內容在fence訊號傳送之前是不能被重寫的。如果fence物件是Fence::NO_FENCE,則該塊buffer可以直接被重寫。
// dequeueBuffer gets the next buffer slot index for the producer to use.
// If a buffer slot is available then that slot index is written to the
// location pointed to by the buf argument and a status of OK is returned.
// If no slot is available then a status of -EBUSY is returned and buf is
// unmodified.
//
// The outFence parameter will be updated to hold the fence associated with
// the buffer. The contents of the buffer must not be overwritten until the
// fence signals. If the fence is Fence::NO_FENCE, the buffer may be
// written immediately.
//
// The width and height parameters must be no greater than the minimum of
// GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv).
// An error due to invalid dimensions might not be reported until
// updateTexImage() is called.  If width and height are both zero, the
// default values specified by setDefaultBufferSize() are used instead.
//
// The pixel formats are enumerated in graphics.h, e.g.
// HAL_PIXEL_FORMAT_RGBA_8888.  If the format is 0, the default format
// will be used.
//
// The usage argument specifies gralloc buffer usage flags.  The values
// are enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER.  These
// will be merged with the usage flags specified by setConsumerUsageBits.
//
// The return value may be a negative error value or a non-negative
// collection of flags.  If the flags are set, the return values are
// valid, but additional actions must be performed.
//
// If IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION is set, the
// producer must discard cached GraphicBuffer references for the slot
// returned in buf.
// If IGraphicBufferProducer::RELEASE_ALL_BUFFERS is set, the producer
// must discard cached GraphicBuffer references for all slots.
//
// In both cases, the producer will need to call requestBuffer to get a
// GraphicBuffer handle for the returned slot.
virtual status_t dequeueBuffer(int *outSlot, sp<Fence>* outFence, bool async,
uint32_t width, uint32_t height, uint32_t format, uint32_t usage);
  • 3.queueBuffer:當buffer填充完畢之後,生產者通過該介面將buffer歸還給BufferQueue,有些額外的資訊需要提供給bufferQueue,具體如下面介紹。尤其是時間戳必須提供BufferQueue.時間戳以納秒為單位,具有單調遞增屬性。這也就是在Camera preview過程中hal上傳的每一幀都包含時間戳資訊。
// queueBuffer returns a filled buffer to the BufferQueue.
//
// Additional data is provided in the QueueBufferInput struct.  Notably,
// a timestamp must be provided for the buffer. The timestamp is in
// nanoseconds, and must be monotonically increasing. Its other semantics
// (zero point, etc) are producer-specific and should be documented by the
// producer.
//
// The caller may provide a fence that signals when all rendering
// operations have completed.  Alternatively, NO_FENCE may be used,
// indicating that the buffer is ready immediately.
//
// Some values are returned in the output struct: the current settings
// for default width and height, the current transform hint, and the
// number of queued buffers.
virtual status_t queueBuffer(int slot,
    const QueueBufferInput& input, QueueBufferOutput* output);
//需要提供的資料如下
// timestamp - a monotonically increasing value in nanoseconds
// isAutoTimestamp - if the timestamp was synthesized at queue time
// crop - a crop rectangle that's used as a hint to the consumer
// scalingMode - a set of flags from NATIVE_WINDOW_SCALING_* in <window.h>
// transform - a set of flags from NATIVE_WINDOW_TRANSFORM_* in <window.h>
// async - if the buffer is queued in asynchronous mode
// fence - a fence that the consumer must wait on before reading the buffer,
//         set this to Fence::NO_FENCE if the buffer is ready immediately
// sticky - the sticky transform set in Surface (only used by the LEGACY
//          camera mode).

四,階段小結

  這裡簡單畫了一張流程圖,來顯示生產者和消費者,bufferQueue如何工作的。圖中標號代表先後順序,其中特別的是queueBuffer和onFrameAvailable回撥是前後進行的,這裡就記作同一序號。

五、BufferQueue例項學習

  下面是一個Android自帶的一個例項程式,不過本地變異要解決一些編譯錯誤問題,修改我也在程式碼後面貼出來了。在接下來的分析中,我們就隨著這個程式,簡單探索一下Bufferqueue是如何工作起來的。首先貼出完整的測試程式。

#include <cutils/memory.h>
#include <utils/Log.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <android/native_window.h>
#include <sys/time.h>

using namespace android;
//namespace android {

int main(int argc, char** argv)
{
    // set up the thread-pool
    sp<ProcessState> proc(ProcessState::self());
    ProcessState::self()->startThreadPool();

    // create a client to surfaceflinger
    //過程1:我們詳細分析一下,它的過程。
    sp<SurfaceComposerClient> client = new SurfaceComposerClient();
    //過程2:下面我們會詳細分析,如果通過surfaceControl物件獲取surface的。
    sp<SurfaceControl> surfaceControl = client->createSurface(String8("resize"),
            160, 240, PIXEL_FORMAT_RGB_565, 0);

    sp<Surface> surface = surfaceControl->getSurface();
  //這裡像是開啟openGl的事務功能
    SurfaceComposerClient::openGlobalTransaction();
    //過程3:設定層數,見下面詳解
    surfaceControl->setLayer(100000);
    SurfaceComposerClient::closeGlobalTransaction();

    ANativeWindow_Buffer outBuffer;
    //過程4:dequeueBuffer 獲取buffer,見下面詳解
    surface->lock(&outBuffer, NULL);
     ALOGE("armwind,buffer_height:0x%x,buffer_width:0x%x,f:%d",outBuffer.height,outBuffer.width,outBuffer.format);
    ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
    //這裡會設定160*240大小的紅色色塊。
    android_memset16((uint16_t*)outBuffer.bits, 0xF800, bpr*outBuffer.height);
    //過程5:queueBuffer buffer入buffer queue.
    surface->unlockAndPost();
    ALOGE("armwind,first sleep 4s,addr:0x%x,stride:%d",outBuffer.bits,outBuffer.stride);
    sleep(4);

    surface->lock(&outBuffer, NULL);
    //這裡會設定160*240大小的青黃色塊,不知道如何形容,請看下圖。下面的過程和上面的是一樣的,這裡就不介紹了。
    android_memset16((uint16_t*)outBuffer.bits, 0x07E0, bpr*outBuffer.height);
    surface->unlockAndPost();
    ALOGE("armwind,second sleep 4s,addr:0x%x,stride:%d",outBuffer.bits,outBuffer.stride);
    sleep(4);

    SurfaceComposerClient::openGlobalTransaction();
    surfaceControl->setSize(2000, 1000);
    SurfaceComposerClient::closeGlobalTransaction();


    IPCThreadState::self()->joinThreadPool();

    return 0;
}
//}

如果有程式碼的話,可以本地實驗一把。將編譯出來的可執行程式,push進自己開發板實驗看看。

  • Makefile中存在的問題
diff --git a/Android.mk b/Android.mk
index 8a86970..e615e16 100644
--- a/Android.mk
+++ b/Android.mk
@@ -8,7 +8,8 @@ LOCAL_SHARED_LIBRARIES := \
        libcutils \
        libutils \
     libui \
-    libgui
+    libgui \
+    libbinder

 LOCAL_MODULE:= test-resize
  • 原始碼中存在的問題
    其中主要是沒有新增native_window.h標頭檔案和surface->lock引數問題。其它都是本人新增log筆記。
diff --git a/resize.cpp b/resize.cpp
index 8b051e8..b4e22bc 100644
--- a/resize.cpp
+++ b/resize.cpp
@@ -24,10 +24,12 @@

 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
+#include <android/native_window.h>
+#include <sys/time.h>

 using namespace android;

-namespace android {
+//namespace android {

 int main(int argc, char** argv)
 {
@@ -49,16 +51,21 @@ int main(int argc, char** argv)

     ANativeWindow_Buffer outBuffer;
     surface->lock(&outBuffer, NULL);
+     ALOGE("armwind,buffer_height:0x%x,buffer_width:0x%x,f:%d",outBuffer.height,outBuffer.width,outBuffer.format);
     ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
     android_memset16((uint16_t*)outBuffer.bits, 0xF800, bpr*outBuffer.height);
     surface->unlockAndPost();
+    ALOGE("armwind,first sleep 4s,addr:0x%x,stride:%d",outBuffer.bits,outBuffer.stride);
+    sleep(4);

-    surface->lock(&outBuffer);
+    surface->lock(&outBuffer, NULL);
     android_memset16((uint16_t*)outBuffer.bits, 0x07E0, bpr*outBuffer.height);
     surface->unlockAndPost();
+    ALOGE("armwind,second sleep 4s,addr:0x%x,stride:%d",outBuffer.bits,outBuffer.stride);
+    sleep(4);

     SurfaceComposerClient::openGlobalTransaction();
-    surfaceControl->setSize(320, 240);
+    surfaceControl->setSize(2000, 1000);
     SurfaceComposerClient::closeGlobalTransaction();


@@ -66,3 +73,4 @@ int main(int argc, char** argv)

     return 0;
 }
+//}

mma命令編譯完成後,會在out/target/product/nanopi2/system/bin生成test-resize可執行程式(這裡我是nanopi2開發板)

E/ ( 1973): armwind,buffer_height:0xf0,buffer_width:0xa0,f:4
E/ ( 1973): armwind,first sleep 4s,addr:0xb6c80000,stride:160
E/ ( 1973): armwind,second sleep 4s,addr:0xb6c6d000,stride:160

效果圖如下,其實是有2個過程的,首先會顯示成紅色塊,4S後顯示成下面這種顏色,先用心去感受一下。

二、詳細分析

1.過程1分析-建立SurfaceComposerClient 代理物件

  Composer是在ComposerService上又封裝了一下,Composer字面意思就是作曲家。這裡就是surface混合類。在過程1中,直接建立了surfaceComposerClient物件。建構函式中獲取Composer單例物件,可見一個程序只有一個composer物件。接下來在onFirstRef()做了很多。在進行一步步分析之前,先看一下類之間的關係,surfaceFlinger實現了ISurfaceComposer介面,而surfaceFlinger代理物件又儲存到ComposerService類中的mComposerService成員中。下面簡單畫了一個繼承和引用關係圖。

SurfaceComposerClient::SurfaceComposerClient()
    : mStatus(NO_INIT), mComposer(Composer::getInstance())
{
}

void SurfaceComposerClient::onFirstRef() {
    sp<ISurfaceComposer> sm(ComposerService::getComposerService());
    if (sm != 0) {
        sp<ISurfaceComposerClient> conn = sm->createConnection();
        if (conn != 0) {
            mClient = conn;
            mStatus = NO_ERROR;
        }
    }
}

onFirstRef()函式是在我們使用sp智慧指標時,第一次引用該物件時會呼叫的函式。可以看到上面函式中主要是獲取surfaceflinger代理物件,然後通過該代理物件獲取一個BpSurfaceComposerClient代理物件,這裡隨著程式碼一步步跟進。

/*static*/ sp<ISurfaceComposer> ComposerService::getComposerService() {
    ComposerService& instance = ComposerService::getInstance();//這裡是一個單例,
    Mutex::Autolock _l(instance.mLock);
    if (instance.mComposerService == NULL) {
        ComposerService::getInstance().connectLocked();//第一次建立會走這裡
        assert(instance.mComposerService != NULL);
        ALOGD("ComposerService reconnected");
    }
    return instance.mComposerService;
}
//下面是composerService類的宣告,可以看到ComposerService繼承了單例類Singleton.
//那麼上面呼叫的connectLocked()方法,也是ComposerService.
class ComposerService : public Singleton<ComposerService>//注意這裡模板引數就是ComposerService自己。
//-------------------------------------------------------
void ComposerService::connectLocked() {
    const String16 name("SurfaceFlinger");//注意這裡的名字是"SurfaceFlinger"
    //根據名字,從serviceManage查詢我們SurfaceFlinger服務。並將獲取到的代理物件儲存到mComposerService成員中
    while (getService(name, &mComposerService) != NO_ERROR) {
        usleep(250000);
    }
    assert(mComposerService != NULL);

   //這裡還有一些死亡監聽物件類宣告和物件建立,這裡我們就不貼程式碼了。
}

  經過上面的折騰,SurfaceFlinger的代理物件終於儲存到ComposerService::mComposerService成員中,詳細的註釋都新增到了程式碼中。這裡主要做的就是-通過單例介面getInstance(),獲取surfaceFlinger代理物件。
  接下來SurfaceFlinger代理物件通過匿名binder通訊方式,獲取ISurfaceComposerClient代理物件。其實這裡和ComposerService類類似,將ISurfaceComposerClient物件儲存到成員mClient中。下面簡圖中的Client就是在createConnet過程中在SurfaceFlinger服務端new出來的。

    virtual sp<ISurfaceComposerClient> createConnection()
    {
        uint32_t n;
        Parcel data, reply;
        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
        remote()->transact(BnSurfaceComposer::CREATE_CONNECTION, data, &reply);
        return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
    }

上面是surfaceFlinger代理端實現的介面,將獲取到的binder物件轉換成ISurfaceComposerClient物件。下面是本地物件響應CREATE_CONNECTION訊息的地方。

status_t BnSurfaceComposer::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch(code) {
        case CREATE_CONNECTION: {
        //檢查SurfaceFlinger服務的名字是否正確。
            CHECK_INTERFACE(ISurfaceComposer, data, reply);
            //直接呼叫SurfaceFlinger實現的介面。
            sp<IBinder> b = createConnection()->asBinder();
            //該操作會在kernel生成一個Binder_node和Binder_ref物件,其中binder_ref就會傳送給應用程序端。
            reply->writeStrongBinder(b);
            return NO_ERROR;
    //......
}
//surfaceFlinger中createConnection實現
sp<ISurfaceComposerClient> SurfaceFlinger::createConnection()
{
    sp<ISurfaceComposerClient> bclient;
    //new了SurfaceFlinger端的Client,同時將surfaceFlinger物件傳進去了,這裡Client物件內部會回撥SurfaceFlinger的介面。
    sp<Client> client(new Client(this)); 
    status_t err = client->initCheck();
    if (err == NO_ERROR) {
        bclient = client;
    }
    //clinet程序實現了ISurfaceComposerClient介面,這裡返回到SurfaceFlinger本地物件中,寫給引用Clinet端。
    return bclient;
}

  上面與SurfaceFlinger建立連線後,會建立一個對應當前應用程序的Client程序。需要特別注意這裡,下面會介紹Client和Layer的關係。到目前為止,我們需要知道已經建立哪幾個物件。下圖中左側是應用程序端的物件,右側是在SurfaceFlinger服務中存在的物件。其中SurfaceComposerclient物件包含Composer(可以理解成surfaceFlinger代理物件)和ISurfaceComposerClient代理物件。

2.過程2分析-createSurface()建立surface.

  • (1) SurfaceComposerClient->createSurface()
sp<SurfaceControl> SurfaceComposerClient::createSurface(
        const String8& name,
        uint32_t w,
        uint32_t h,
        PixelFormat format,
        uint32_t flags)
{
    sp<SurfaceControl> sur;
    if (mStatus == NO_ERROR) {
        sp<IBinder> handle;
        sp<IGraphicBufferProducer> gbp;
        status_t err = mClient->createSurface(name, w, h, format, flags,
                &handle, &gbp);
        ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
        if (err == NO_ERROR) {
            sur = new SurfaceControl(this, handle, gbp);
        }
    }
    return sur;
}

上面createSurface介面呼叫ISurfaceComposerClient代理物件createSurface的介面。注意這裡建立3個區域性變數sur,handle和gbp,其中sur是管理surface的類,handle可以理解成該buffer的id,gbp是buffer生產者代理物件

  • (2) createSurface代理端介面實現
    原始碼:frameworks/native/libs/gui/ISurfaceComposerClient.cpp
    virtual status_t createSurface(const String8& name, uint32_t w,
            uint32_t h, PixelFormat format, uint32_t flags,
            sp<IBinder>* handle,
            sp<IGraphicBufferProducer>* gbp) {
        Parcel data, reply;
        data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
        data.writeString8(name);
        data.writeInt32(w);
        data.writeInt32(h);
        data.writeInt32(format);
        data.writeInt32(flags);
        remote()->transact(CREATE_SURFACE, data, &reply);
        *handle = reply.readStrongBinder();
        *gbp = interface_cast<IGraphicBufferProducer>(reply.readStrongBinder());
        return reply.readInt32();
    }

上面代理端只是做了資料轉發。往下看

  • (3) createSurface本地端介面實現
    creteSurface的本地端介面還是呼叫了Client實現的CreteSurface介面.
    原始碼路徑:frameworks/native/services/surfaceflinger/Client.cpp
status_t Client::createSurface(
        const String8& name,
        uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
        sp<IBinder>* handle,
        sp<IGraphicBufferProducer>* gbp)
{
    /*
     * createSurface must be called from the GL thread so that it can
     * have access to the GL context.
     */

    class MessageCreateLayer : public MessageBase {
        SurfaceFlinger* flinger;
        Client* client;
        sp<IBinder>* handle;
        sp<IGraphicBufferProducer>* gbp;
        status_t result;
        const String8& name;
        uint32_t w, h;
        PixelFormat format;
        uint32_t flags;
    public:
        MessageCreateLayer(SurfaceFlinger* flinger,
                const String8& name, Client* client,
                uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
                sp<IBinder>* handle,
                sp<IGraphicBufferProducer>* gbp)
            : flinger(flinger), client(client),
              handle(handle), gbp(gbp),
              name(name), w(w), h(h), format(format), flags(flags) {
        }
        status_t getResult() const { return result; }
        virtual bool handler() {
        //最關鍵就是在這裡,建立CreateLayer.還記得我們前面Reszie程序中申請的surface大小嗎?
        //w=160, h=240, format=PIXEL_FORMAT_RGB_565, flags=0
            result = flinger->createLayer(name, client, w, h, format, flags,
                    handle, gbp);
            return true;
        }
    };
    //這裡定義了一個內部建立Layer的訊息類,將該訊息類物件傳送給SurfaceFlinger物件處理createLayer請求。
    //注意這裡client物件this,就是上面createConnection中new出來的client,先留意一下。
    sp<MessageBase> msg = new MessageCreateLayer(mFlinger.get(),
            name, this, w, h, format, flags, handle, gbp);
    mFlinger->postMessageSync(msg);//傳送給SurfaceFlinger物件處理。
    return static_cast<MessageCreateLayer*>( msg.get() )->getResult();
}

  本地端的createSurface內部定義了內部類MessageCreateLayer,而需要我們關心的是handler()實現。在handler中直接呼叫了SurfaceFlinger物件的CreateLayer介面來建立一個layer物件,由此可想而知,應用端的surface物件對應的就是layer物件。一個surface物件對應一個Layer物件,而一個Layer又對應多個buffer,Android5.1每一個Layer最多有64個mSlot。

status_t SurfaceFlinger::createLayer(
        const String8& name,
        const sp<Client>& client,
        uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
        sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp)
{
   //記得這個時候w=160,h=240,format=PIXEL_FORMAT_RGB_565,flag=0
   //省掉一些檢查寬高檢查程式碼---------------- 
    status_t result = NO_ERROR;
    sp<Layer> layer;

    switch (flags & ISurfaceComposerClient::eFXSurfaceMask) { //flag = 0
        case ISurfaceComposerClient::eFXSurfaceNormal:
        //上面flag=0,直接走到這裡了。w=160,h=240,format=PIXEL_FORMAT_RGB_565,flag=0
            result = createNormalLayer(client,
                    name, w, h, flags, format,
                    handle, gbp, &layer);
            break;
        case ISurfaceComposerClient::eFXSurfaceDim:
        //這個就是建立不需要顯示出來的Layer。
            result = createDimLayer(client, 
                    name, w, h, flags,
                    handle, gbp, &layer);
            break;
        default:
            result = BAD_VALUE;
            break;
    }

    if (result == NO_ERROR) {
        addClientLayer(client, *handle, *gbp, layer);
        setTransactionFlags(eTransactionNeeded);
    }
    return result;
}

該函式算主要做了下面幾件事情:

  • 1.首先會檢查寬高是否合法,然後根據我們傳進來的flag,來建立需要顯示的surface還是不需要顯示的surface。
  • 2.createNormalLayer()函式中建立Layer物件和handle物件。其中handle物件表示該Layer物件的控制代碼。
  • 3.addClientLayer()最後將新建立的Layer物件和handle物件捆綁,此外將該layer物件和BufferProducer物件儲存到SurfaceFlinger相應的陣列中,便於後面查詢。
void SurfaceFlinger::addClientLayer(const sp<Client>& client,
        const sp<IBinder>& handle,
        const sp<IGraphicBufferProducer>& gbc,
        const sp<Layer>& lbc)
{
    // attach this layer to the client
    client->attachLayer(handle, lbc);

    // add this layer to the current state list
    Mutex::Autolock _l(mStateLock);
    mCurrentState.layersSortedByZ.add(lbc);//儲存到SurfaceFlinger物件中
    mGraphicBufferProducerList.add(gbc->asBinder());//記錄BufferProducer物件
}

到這裡應用Client端請求createSurface請求已經處理完了,請求處理過程中,又誕生了下面幾個物件。

  • Layer:一個Layer對應一個surface物件。
  • buffer_producer本地物件:該物件是在建立Layer物件同時建立的。建立該物件便於應用端獲取buffer,應用Client端有了buffer_producer代理物件後,就可以detachBuffer,queueBuffer了。
  • buffer_consumer本地物件:該物件目前我的發現是由於buffer_consumer本地物件實現了FrameAvailableListener介面中的onFrameAvailable(),這樣的話,應用端拿到buffer_consumer代理物件後,當幀可用後就可以通知surfaceFliner去合成layer了。
status_t BnSurfaceComposerClient::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
     switch(code) {
        case CREATE_SURFACE: {
            CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
            String8 name = data.readString8();
            uint32_t w = data.readInt32();
            uint32_t h = data.readInt32();
            PixelFormat format = data.readInt32();
            uint32_t flags = data.readInt32();
            sp<IBinder> handle;
            sp<IGraphicBufferProducer> gbp;
            status_t result = createSurface(name, w, h, format, flags,
                    &handle, &gbp);
            //匿名binder寫入,會在kernel中生成對應的binder_node.
            reply->writeStrongBinder(handle);
            //匿名binder寫入,會在kernel中生成對應的binder_node.
            reply->writeStrongBinder(gbp->asBinder());
            reply->writeInt32(result);//寫入返回值
            return NO_ERROR;
        } break;
    //.......
    }

與此同時,應用端這樣獲取的。

        remote()->transact(CREATE_SURFACE, data, &reply);
        *handle = reply.readStrongBinder();
        //這裡直接就轉換成IGraphicBufferProducer代理物件了。
        *gbp = interface_cast<IGraphicBufferProducer>(reply.readStrongBinder()); 
        return reply.readInt32();//返回結果

到這裡我們返回到SurfaceComposerClient物件的createSurface物件中

sp<SurfaceControl> SurfaceComposerClient::createSurface(.....)
{
    sp<SurfaceControl> sur;
    if (mStatus == NO_ERROR) {
        sp<IBinder> handle;
        sp<IGraphicBufferProducer> gbp;
        status_t err = mClient->createSurface(name, w, h, format, flags, &handle, &gbp);
        ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
        if (err == NO_ERROR) {
            sur = new SurfaceControl(this, handle, gbp);//這裡根據生產者代理物件建立了SurfaceControl物件,最好這裡追追原始碼看看。
        }
    }
    return sur;
}

可以發現上面獲取到handle物件和BufferProducer代理物件後,直接出入SurfaceControl建構函式中,獲取到了一個surface物件。

SurfaceControl::SurfaceControl(
        const sp<SurfaceComposerClient>& client,
        const sp<IBinder>& handle,
        const sp<IGraphicBufferProducer>& gbp)
    : mClient(client), mHandle(handle), mGraphicBufferProducer(gbp)
{
}
sp<Surface> SurfaceControl::getSurface() const
{
    Mutex::Autolock _l(mLock);
    if (mSurfaceData == 0) {
        // This surface is always consumed by SurfaceFlinger, so the
        // producerControlledByApp value doesn't matter; using false.
        //看到了吧,這裡直接new的,666666
        mSurfaceData = new Surface(mGraphicBufferProducer, false);
    }
    return mSurfaceData;
}

上面可以看到getSurface介面,根據bufferProducer代理物件建立了surface物件。這樣有了mGraphicBufferProducer代理物件後,後續申請buffer就方便了。生產者代理物件,本地物件,Layer物件的關係如下圖所示。

4.過程3-設定層數

該過程是通過SurfaceControl物件設定當前surface所在層數,而SurfaceControl物件又通過SurfaceComposerClient代理物件來完成的。

status_t SurfaceControl::setLayer(int32_t layer) {
    status_t err = validate();