BufferQueue详解 原理

一、BufferQueue 简介

在工作中,我们经常会和Surface,ImageReader,ImageWriter BufferQueue打交道,它们之间是什么关系呢。
实际上Surface,ImageReader,ImageWriter是对BufferQueue的生产者消费者的封装,其核心实现还是BufferQueue。
而BufferQueue是一个生产者消费者模型又是GraphicBuffer管理者,它和显示系统以及Camera流媒体紧密关系着。
所以要了解显示系统,绕不开BufferQueue,要了解BufferQueue 就要先了解生产者消费者模型。
下面就从生产者消费者模型开始,深入浅出的讲解一下BufferQueue。

二、生产者消费者模型

1.生产者消费者模型基本原理

在生产者消费者模型中,存在生产者和消费者两种角色,它们通过内存缓冲区进行通信,生产者生产消费者需要的数据,消费者获取生产者生产的数据进行消费。
如果没有生产者消费者模型,那么生产者和消费者是直接调用关系,生产速度的和消费速度相互拖累,必须在消费完成之后才能进行下一次生产,如下图:
在这里插入图片描述
有了生产者和消费者模型之后,生产者生产的数据存到缓冲队列,消费者从队列中取数据去消费,互不影响,互不拖累,耦合性低。如下图
在这里插入图片描述

2.生产者消费者模型的组成

总结起来生产者消费者模型由一个场所,和两个角色组成:
. 一个场所:数据缓冲区,实现内存共享和轮转。
. 两个角色: 生产者(生产线程)生产数据,消费者(消费线程)消费数据。也可以是进程间使用。
生产者和消费着之间又需要遵循三种关系和四个基本的原则:
. 三个关系:生产者和生产者互斥关系,消费者和消费者互斥关系,生产者和消费者互斥同步关系。
. 四个原则:生产者生产的时候消费者不能消费,消费者消费的时候生产者不能生产,缓冲区空时消费者不能消费,缓冲区满时生产者不能生产。

以上就可以很清晰,简单明了的解释了生产者消费者模型。那么BufferQueue是什么,怎么应用生产者-消费者模型思想的,下面详细说明。

三、BufferQueue基本原理

1.BufferQueue的生产消费框架

BufferQueue的核心逻辑是生产者消费者逻辑,在BufferQueue这个生产者消费者框架中, BufferQueuecore可以理解为数据缓冲区的管理者,代码逻辑在BufferQueue.cpp和BufferQueuecore.cpp中。
它的原始消费者是BufferQueueConsumer,它的原始生产者是BufferQueueProducer。
那么BufferQueue.cpp 和BufferQueuecore.cpp是什么样的关系呢,可以理解为BufferQueue 是创建者,BufferQueuecore是由BufferQueue调用createBufferQueue创建的。
总的来讲 BufferQueue.cpp 主要定义了createBufferQueue接口和ProxyConsumerListener的onFrameAvailable等通知接口。
使用的时候,封装的消费者调用createBufferQueue创建BufferQueuecore,然后根据创建好的BufferQueuecore,去创建原始生产者消费者BufferQueueConsumer和BufferQueueProducer。
而消费者BufferQueueConsumer在调用connect的时候把ConsumerListener相关的回调接口注册进BufferQueue供ProxyConsumerListener回调使用。
生产者BufferQueueProducer也有一个connect接口,生产者的connect接口会注册IProducerListener到BufferQueuecore中,在消费者使用完GraphicBuffer释放的时候通过这个Listener通知生产者。
同时这个IProducerListener会注册Binder死亡通知函数,在死亡的时候回调BufferQueueProducer的binderDied,取消链接。
至此由BufferQueue BufferQueuecore,BufferQueueConsumer, BufferQueueProducer,组成的核心的生产者消费者模型就建立起来了。

这里说的是封装的消费者去创建BufferQueuecore有两点需要解释一下:
一个是消费者的封装关系,这个后面会详细说明。
另外一个就是为什么是消费者创建BufferQueuecore 而不是生产者去创建呢。
其实也是可以放到生产者中创建的,但是主要由于两点原因,正常会放在消费者中去创建:
1.出于消费者准备好消费了在去生产的思想考虑。
2.以消费者端作为核心端去管理,在消费者端创建方便统一管理。

2.BufferQueueCore概述

从BufferQueue的生产消费框架建立过程看,BufferQueueCore是核心缓冲区管理者,是由BufferQueue创建的,那么BufferQueueCore具体管理哪些事呢。
代码上看主要包含了下面这些关键参数。
可以大概划分一下:
slots相关,用于关联数据核心GraphicBuffer。(包括mSlots mQueue mFreeSlots mfreeBuffers mUnusedSlots,mActiveBuffers ),
listener相关,用于通知和回调(包括mConsumerListener mLinkedToDeath mConnectedProducerListener),
Buffercount相关,用于定BufferQuue中的Buffer数量。(包括mMaxBufferCount mMaxAcquiredBufferCount mMaxDequeuedBufferCount),
和一些设置项(包括 mConsumerName mDefaultWidth mDefaultHeight mDefaultBufferFormat mDefaultBufferDataSpace)
设置相关主要是name 宽高信息,format信息,dataspace信息等,下面会分章节重点讲一下Slots BufferCount和 Listener。

class BufferQueueCore : public virtual RefBase { 
    String8 mConsumerName;
    sp<IConsumerListener> mConsumerListener;
    sp<IProducerListener> mLinkedToDeath;
    sp<IProducerListener> mConnectedProducerListener;
    BufferQueueDefs::SlotsType mSlots;
    Fifo mQueue;
    std::set<int> mFreeSlots;
    std::list<int> mFreeBuffers;
    std::list<int> mUnusedSlots;
    std::set<int> mActiveBuffers;
    PixelFormat mDefaultBufferFormat;
    uint32_t mDefaultWidth;
    uint32_t mDefaultHeight;
    android_dataspace mDefaultBufferDataSpace;
    int mMaxBufferCount;
    int mMaxAcquiredBufferCount;
    int mMaxDequeuedBufferCount;
}

3.BufferSlot和BufferItem详解

BufferQueueCore中管理着数据缓冲区,而数据的核心GraphicBuffer关联在BufferSlot中。
从源码中分析BufferSlot 和 GraphicBuffer的关联关系:
BufferQueueCore 定义了 BufferQueueDefs::SlotsType mSlots;
SlotsType的定义如下(代码路径:BufferQueueDefs.h)
namespace BufferQueuDefs {
typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
}
由此可以看出mSlots实际上是一个BufferSolt的数组。
BufferSolt中定义了一个GraphicBuffer的強指针用于关联mGraphicBuffer。
struct BufferSlot {
sp mGraphicBuffer;

}

这样BufferSlot 和 GraphicBuffer 就关联上了。BufferQueue框架中,消费者和生产者对缓冲区数据操作的单元核心就是一个BufferSlot,也就是说所有取GraphicBuffer,放GraphicBuffer的操作都是针对BufferSlot来完成的。
具体的BufferSlot是怎么取和放的呢。可以看到BufferQueueCore 中还定义了 Fifo mQueue;字面上看,定义了一个先进先出的对列,那么这个队列里存放的是什么呢。
可以看到这样的定义typedef Vector Fifo; 也就是说Fifo是的向量集,里面存的是BufferItem。而 BufferItem中又定义了mslots的索引值Class BufferItem { int mSlot; … }
这样就可以和BufferSlot关联上了。
总结一下可以简单理解成生产者从mQueue上获取BufferItem从而找到了对应的BufferSlot,并对它完成一系列的操作之后,放回到mQueue中供消费者使用,消费者也是从mQueue上获取BufferItem从而找到对应的BufferSlot来消费,消费完成之后放回mQueue。不过需要注意实际上不是真正的把BufferSlot取出放回mQueue,而是mSlots索引值的传递过程。

BufferQueueCore 中定义的mSlots是BufferSlot数组,默认数组长度为NUM_BUFFER_SLOTS =64(在/frameworks/native/libs//ui/include/ui/BufferQueueDefs.h中定义)。
但是实际使用的时候64个Slots不一定全用,这里就引入了一个mUnusedSlots,mUnusedSlots只的是不被使用的BufferSlot集合,那么可用的BufferSlot 就是NUM_BUFFER_SLOTS - num(mUnusedSlots) 这么多个。
在可用的这些BufferSlot中,又会根据BufferSlot当前的状态即BufferState做区分。分为FREE状态下的BufferSlot和非FREE状态下的BufferSlot,非FREE状态下的BufferSlot集合在mActiveBuffers中。
FREE状态下的BufferSlot又会根据有没有和GraphicBuffer关联做区分。没有GraphicBuffer与之相关联的BufferSlot集合在mFreeSlots中,有GraphicBuffer与之关联的BufferSlot 集合在mFreeBuffers中。

在BufferQueueProducer和BufferQueueConsumer的生产消费的过程中,mslots中的BufferSlot会动态的在 mFreeSlots mFreeBuffers mActiveBuffers mUnusedSlots之间进行流转。

需要注意的是mSlots 是个数据结构数组,但是mFreeSlots mFreeBuffers mActiveBuffers mUnusedSlots 都不是结构数组,都是mSlots的 index值的集合(也就是mSlots的下标的集合)。

刚刚提到的BufferSlot会根据当前的状态即BufferState做区分为FREE状态的slot和非Free状态的slot。这个BufferState 就是BufferSlot流转过程中的状态。主要包含FREE DEQUEUED QUEUED ACQUIRED 四种状态。

FREE:FREE状态下的BufferSlot指的是可以被生产者dequeue或attach出来使用的BufferSlot,以及消费者消费完毕release或者detach的BufferSlot。
DEQUEUED:DEQUEUED状态的BufferSlot 是已经被生产者dequeue或attach出来供生产使用的BufferSlot。
QUEUED:QUEUED状态的BufferSlot 是生产者生产完成放回队列供消费者使用的BufferSlot。
ACQUIRED :消费者从 BufferQueue获取准备消费的BufferSlot。

BufferState 用isFree isDequeued isQueued isAcquired 接口来判断状态,用attachProducer,detachProducer,dequeue,queue,cancel,freeQueued,acquire,release等接口来完成状态转换。

可以用下图形象的描述一下mFreeSlots mFreeBuffers mActiveBuffers mUnusedSlots 之间的关系。其中 TotalSlots 最大支持NUM_BUFFER_SLOTS = 64个,调用getMaxBufferCountLocked 获取的是可用的BufferSlot数量后面会详细介绍。
在这里插入图片描述

4.BufferCount详解

BufferSlot中介绍中讲到getMaxBufferCountLocked,可以获取可用的BufferSlot数量maxbuffercount,那么这个可用的数量是哪里来的呢。

消费者BufferQueueConsumer 提供了setMaxBufferCount接口来设置可用的BufferSlot数量maxbuffercount,默认是数量是NUM_BUFFER_SLOTS=64。

同时消费者BufferQueueConsumer还提供了setMaxAcquiredBufferCount 接口来设置mMaxAcquiredBufferCount,

mMaxAcquiredBufferCount 是指一次 同时能被消费者使用的最大BufferCount。

另外,生产者还提供了一个setMaxDequeuedBufferCount接口来设置mMaxDequeuedBufferCount,

mMaxDequeuedBufferCount是指一次同时能被生产者用来生产的最大BufferCount

设定完成之后BufferQueueCore 中就会有 maxBufferCount个mlots, NUM_BUFFER_SLOTS- maxBufferCount个mUnusedSlots

maxBufferCount,mMaxDequeuedBufferCount ,mMaxAcquiredBufferCount三者之间的关系如下:maxbuffercount = mMaxDequeuedBufferCount + mMaxAcquiredBufferCount

四、BufferQueue中的生产者和消费者详解

1.BufferQueue中的生产者和消费者概述

上面讲了BufferQueue 创建了BufferQueueCore,然后根据BufferQueueCore创建了消费者BufferQueueConsumer和生产者BufferQueueProducer,并详细介绍了数据缓存管理区BufferQueueCore,下面重点介绍一下消费者BufferQueueConsumer和生产者BufferQueueProducer。先介绍一下BufferQueueConsumer和BufferQueueProducer的继承关系和关键方法,然后针对关键函数展开介绍一下BufferQueueConsumer和BufferQueueProducer的工作原理,和流转过程。

BufferQueueConsumer 和 BufferQueueProducer 分别对应文件BufferQueueConsumer.cpp 和 BufferQueueProducer.cpp 和 BufferQueueCore.cpp 在同级目录都在/frameworks/native/libs/gui目录下。

下面是这两个类的继承关系和重要函数:
生产者继承关系:
(BufferQueueProducer.cpp) BufferQueueProducer:BnGraphicBufferProducer
:private IBinder::DeathRecipient
BnGraphicBufferProducer: IGraphicBufferProducer
可以看到BufferQueueProducer继承了BnGraphicBufferProducer而BnGraphicBufferProducer又继承了IGraphicBufferProducer,来完成主要的BufferSlot的流转操作,也提供了远程代理接口,实现跨进程binder调用。
同时BufferQueueProducer还继承了DeathRecipient 用来处理Binder死亡通知。

生产者关键方法:
requestBuffer 获取对应BufferSlot的GraphicBuffer地址。
setMaxDequeuedBufferCount 设置最大同时可以dequeue出来的的BufferSlot数量。
dequeueBuffer 从FREE状态下的BufferSlots中队列中获取空闲的BufferSlot做生产使用,优先从mFreeBuffers中获取,如果没有则从mFreeSlots中获取。
attachBuffer 绑定已经分配好的GraphicBuffer到FREE状态下的BufferSlot中,优先从mFreeSlots中查找BufferSlot,如果没有则从mFreeBuffers中查找并绑定。
queueBuffer 生产者把生产好的BufferSlot放到队列中供消费者使用。
detachBuffer 把attachBuffer了GraphicBuffer的 Active状态下的BufferSlot的放到mFreeBuffers之后直接取消GraphicBuffer的绑定。
detachNextBuffer 把需要释放的BufferSlot中的GraphicBuffer指针赋值到outBuffer输出之后,把BufferSlot的放到mFreeBuffers并解绑定GraphicBuffer。
cancelBuffer 把BufferSlot放回到mfreeBuffers中,不会释放graphicbuffer。
connect 生产者通过该接口把IProducerListener注册到BufferQueueCore中供消费者回调,同时建立了Binder死亡通知通路。
disconnect断开BufferQueueProducer和BufferQueueCore之间建立的链接关系。

消费者继承关系:
(BufferQueueConsumer.cpp) BufferQueueConsumer:BnGraphicBufferConsumer
BnGraphicBufferConsumer:IGraphicBufferConsumer
可以看到BufferQueueConsumer的实现结构和BufferQueueProducer很相似继承了BnGraphicBufferConsumer 而BnGraphicBufferConsumer又最终继承了IGraphicBufferConsumer,来完成主要的BufferSlot的流转操作,也提供了远程代理接口,实现跨进程binder调用。

消费者关键方法:
acquireBuffer 获取QUEUE状态下的BufferSlot进行消费。
releaseBuffer消费完成之后把BufferSlot放回mFreeBuffers队列中。
attachBuffer 把消费者的GraphicBuffer绑定到BufferSlot上使用。
detachBuffer把消费者的GraphicBuffer从BufferSlot上解绑。
setMaxBufferCount 设置最大可用BufferSlot数量。
setMaxAcquiredBufferCount 设置最大同时可以acquire的BufferSlot数量。
connect 建立消费者和BufferQueueCore之间的链接,注册IConsumerListener回调。
disconnect 销毁消费者和BufferQueue之间的链接。

2.BufferQueueConsumer关键流程分析

(1). acquirebuffer的流程

主要流程是首先判断AcquireBuffers是否已经超过最大一次能够Acquire的数量,mQueue是否为空,然后从mQueue中获取第一个迭代器赋值给outBuffer输出,同时把mBufferState状态改为Acquired 并从mQueue中移除

status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
        nsecs_t expectedPresent, uint64_t maxFrameNumber) {
    ...
    {
 
 
        //判断AcquireBuffers是否已经超过最大一次能够Acquire的数量。
        if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) {
            return INVALID_OPERATION;
        }
 
 
        //判断mQueue队列是否为空
        if (mCore->mQueue.empty() && !sharedBufferAvailable) {
            return NO_BUFFER_AVAILABLE;
        }
 
        //从mQueue的第一个迭代器付给front
        BufferQueueCore::Fifo::iterator front(mCore->mQueue.begin());
 
 
        if (sharedBufferAvailable && mCore->mQueue.empty()) {//共享buffer的处理逻辑
          ...
        } else {//正常非共享模式下的逻辑
            slot = front->mSlot;    //从front获取对应的slot,front是一个BufferItem指针
            *outBuffer = *front;    //把front指向的BufferItem赋值给outBuffer
        }
 
        if (!outBuffer->mIsStale) {
            mSlots[slot].mAcquireCalled = true;
            if (mCore->mQueue.empty()) {
                mSlots[slot].mBufferState.acquireNotInQueue();
            } else {
                mSlots[slot].mBufferState.acquire();     //把BufferState修改成acquired状态
            }
        }
 
        mCore->mQueue.erase(front);       //把acquired slot 对应的BufferItem从mQueue中移除。
 
        mCore->mDequeueCondition.notify_all();
    }
 
    return NO_ERROR;
}

(2). releasebuffer的流程

releasebuffer主要流程是先做slot frameNumber 以及 BufferState有效性检查,修改mBufferState状态成FREE状态。然后把对应的slot从mActiveBuffers中移除并放回mFreeBuffers的过程。
这个过程中不做GraphicBuffer和BufferSlot的解绑定操作,也就是说GraphicBuffer不会被释放。详细流程如下面的代码。

status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber,
        const sp<Fence>& releaseFence, EGLDisplay eglDisplay,
        EGLSyncKHR eglFence) {
 
    //slot的合法性判断。
    if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS ||
            releaseFence == nullptr) {
        BQ_LOGE("releaseBuffer: slot %d out of range or fence %p NULL", slot,
                releaseFence.get());
        return BAD_VALUE;
    }
 
    sp<IProducerListener> listener;
    {
        //判断frameNumber是否有效,如果做过reallocated frameNumber将会改变。
        if (frameNumber != mSlots[slot].mFrameNumber &&
                !mSlots[slot].mBufferState.isShared()) {
            return STALE_BUFFER_SLOT;
        }
 
        //判断BufferState是否是Acquired状态
        if (!mSlots[slot].mBufferState.isAcquired()) {
            return BAD_VALUE;
        }
 
        mSlots[slot].mBufferState.release();   //调用mBufferState.release()将acqiure状态释放。
 
        if (!mSlots[slot].mBufferState.isShared()) {
            mCore->mActiveBuffers.erase(slot);   //将slot从mActiveBuffers中移除
            mCore->mFreeBuffers.push_back(slot); //将slot放入到mFreeBuffers队列中
        }
 
        listener = mCore->mConnectedProducerListener;  //获取ProducerListener
 
        mCore->mDequeueCondition.notify_all();
 
    }
 
    if (listener != nullptr) {
        listener->onBufferReleased();  //调用Producer的onBufferReleased回调通知Producer完成释放。
    }
 
    return NO_ERROR;
}

3.BufferQueueProducer关键流程分析

(1). dequeuebuffer的流程

dequeuebuffer是生产者端从BufferQueueCore上获取一个GraphicBuffer进行生产的过程,生产者BufferQueueProducer 会在去获取一个FREE状态的的BufferSlot。
同时把mBufferState状态修改成Dequeue状态,把BufferSlot放到mActiveBuffers中管理。也由此可见GraphicBuffer是在这里实际创建的。
下面是dequeueBuffer的具体流程:

status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* outFence,
                                            uint32_t width, uint32_t height, PixelFormat format,
                                            uint64_t usage, uint64_t* outBufferAge,
                                            FrameEventHistoryDelta* outTimestamps) {
    
        int found = BufferItem::INVALID_BUFFER_SLOT;
        //调用waitForFreeSlotThenRelock 找到可以dequeue的 FREE状态下的BufferSlot
        while (found == BufferItem::INVALID_BUFFER_SLOT) {
            status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue, lock, &found);
        }
 
        const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer); //把获取到的mGraphicBuffer地址赋值给buffer。
 
        if (mCore->mSharedBufferSlot != found) {
            mCore->mActiveBuffers.insert(found);   //把找到的slot放到mActiveBuffers中管理
        }
        *outSlot = found;  //赋值给outSlot输出
 
        mSlots[found].mBufferState.dequeue();  //修改BufferState 状态成dequeue状态。
 
    }
 
    return returnFlags;
}

FREE状态的BufferSlot又包含了mFreeSlots和mFreebuffers两组slots,dequeue的时候会先从mFreebuffers查找如果有可用的就使用,如果没有就从mFreeSlots获取BufferSlot并分配GraphicBuffer。
这个过程在waitForFreeSlotThenRelock中实现
下面是waitForFreeSlotThenRelock的流程:

status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller,
        std::unique_lock<std::mutex>& lock, int* found) const {
    auto callerString = (caller == FreeSlotCaller::Dequeue) ?
            "dequeueBuffer" : "attachBuffer";
    bool tryAgain = true;
    while (tryAgain) {
 
            if (){
            } else {
                if (caller == FreeSlotCaller::Dequeue) {   //Dequeuebuffer调用这段代码,先调用getFreeBufferLocked从mFreeBuffers中获取,如果找到了就返回。
                    // If we're calling this from dequeue, prefer free buffers
                    int slot = getFreeBufferLocked();
                    if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) {
                        *found = slot;
                    } else if (mCore->mAllowAllocation) {      // 如果没找到,在调用getFreeSlotLocked从mFreeSlots中获取。
                        *found = getFreeSlotLocked();
                    }
                } else {
                    // If we're calling this from attach, prefer free slots
                    int slot = getFreeSlotLocked();         //attachbuffer调用这段代码,优先先调用getFreeSlotLocked从mFreeSlots中获取,如果找到了就返回。
                    if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) {
                        *found = slot;
                    } else {
                        *found = getFreeBufferLocked();    如果没找到,在调用getFreeBufferLocked从mFreeBuffers中获取。
                    }
                }
            }
        }
    }
 
    return NO_ERROR;
}

(2). attachbuffer的流程

正如上面的waitForFreeSlotThenRelock流程,attachBuffer也是从FREE状态的slots上获取BufferSlot,但是和dequeueBuffer不同attachBuffer是优先从mfreeslots上获取,如果mfreeslots没有,在从mfreebuffers上获取。
waitForFreeSlotThenRelock获取到BufferSlot之后,再把已有的申请好的GraphicBuffer绑定到这个BufferSlot上。同时把mBufferState状态修改成Dequeued状态。把BufferSlot放到mActiveBuffers中管理。
具体流程如下:

status_t BufferQueueProducer::attachBuffer(int* outSlot,
        const sp<android::GraphicBuffer>& buffer) {
 
    //调用waitForFreeSlotThenRelock 找到可以FREE状态下的BufferSlot
    status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Attach, lock, &found);
  
    *outSlot = found; //把找到的slot赋值给outSlot
 
    mSlots[*outSlot].mGraphicBuffer = buffer;  //把准备好的buffer关联到slot的mGraphicBuffer上
    mSlots[*outSlot].mBufferState.attachProducer(); // 修改BufferState成Dequued状态
  
    mCore->mActiveBuffers.insert(found);            // 把slot放到mActiveBuffers中管理。
 
    return returnFlags;
}

(3). queuebuffer的流程

queuebuffer是生产者完成对GraphicBuffer的处理之后调用queuebuffer把GraphicBuffer放回mQueue的操作,同时把mBufferState修改成QUEUE状态。
具体queuebuffer流程如下:

status_t BufferQueueProducer::queueBuffer(int slot,
        const QueueBufferInput &input, QueueBufferOutput *output) {
 
    sp<IConsumerListener> frameAvailableListener;
    sp<IConsumerListener> frameReplacedListener;
 
    BufferItem item;
    { // Autolock scope
  
        mSlots[slot].mBufferState.queue();  //修改mBufferState状态为QUEUE状态。
 
        //增加mFrameCounter
        ++mCore->mFrameCounter;
        currentFrameNumber = mCore->mFrameCounter;
        mSlots[slot].mFrameNumber = currentFrameNumber;
 
        //给BufferItem赋值
        item.mGraphicBuffer = mSlots[slot].mGraphicBuffer;
        item.mSlot = slot;
 
        output->bufferReplaced = false;
        if (mCore->mQueue.empty()) { //如果mQueue为空,就直接把BufferItem push到mQueue尾部。
            mCore->mQueue.push_back(item);
            frameAvailableListener = mCore->mConsumerListener;
        } else {  //如mQueue不为空,需要判断一下last BufferItem是否被替换,如果可以替换就替换,如果不可以替换就直接把BufferItem放到mQueue尾部。
            const BufferItem& last = mCore->mQueue.itemAt(
                    mCore->mQueue.size() - 1);
            if (last.mIsDroppable) {
 
                if (!last.mIsStale) {
                    mSlots[last.mSlot].mBufferState.freeQueued();
 
                    // Don't put the shared buffer on the free list.
                    if (!mSlots[last.mSlot].mBufferState.isShared()) {
                        mCore->mActiveBuffers.erase(last.mSlot);
                        mCore->mFreeBuffers.push_back(last.mSlot);
                        output->bufferReplaced = true;
                    }
                }
 
                mCore->mQueue.editItemAt(mCore->mQueue.size() - 1) = item;
                frameReplacedListener = mCore->mConsumerListener;
            } else {
                mCore->mQueue.push_back(item);
                frameAvailableListener = mCore->mConsumerListener;
            }
        }
        if (frameAvailableListener != nullptr) {
            frameAvailableListener->onFrameAvailable(item);   //调用消费者的onFrameAvailable通知消费者,有queue状态的BufferSlot可以使用。
        } else if (frameReplacedListener != nullptr) {
            frameReplacedListener->onFrameReplaced(item);     //调用消费者的onFrameReplaced通知消费者,有queue状态的BufferSlot可以被替换。
        }
    } // Autolock scope
 
    return NO_ERROR;
}

(4). detachBuffer的流程

detachBuffer主要是对应生产者端的attachbuffer操作,将attachbuffer之后的BufferSlot,放回到mFreeSlots中,并解除对GraphicBuffer的绑定,并通知消费者Buffer释放。

status_t BufferQueueProducer::detachBuffer(int slot) {
 
    sp<IConsumerListener> listener;
    
        mSlots[slot].mBufferState.detachProducer();  //修改BufferState的Dequeued状态成FREE状态
        mCore->mActiveBuffers.erase(slot);           //把slot从mActiveBuffers中移除
        mCore->mFreeSlots.insert(slot);              //把slot加到mFreeSlots中。
        mCore->clearBufferSlotLocked(slot);          //清除slot和Graphicbuffer的绑定关系。
        mCore->mDequeueCondition.notify_all();
        listener = mCore->mConsumerListener;   //把消费者回调的listener赋值给listener
    }
 
    if (listener != nullptr) {
        listener->onBuffersReleased();//调用消费者的listener接口通知消费者Buffer释放
    }
 
    return NO_ERROR;
}

五、BufferQueue的完整生产消费体系

1.BufferQueue的生产消费和BufferSlot状态关系

上面分别详细介绍了BufferQueueCore BufferSlot BufferState BufferQueueConsumer BufferQueueProducer,也涉及到了部分流转关系。
这一节将用图表和文字说明详细的介绍一下 BufferQueueConsumer BufferQueueProducer 和 BufferSlot BufferState 之间的流转关系。

BufferSlot 包含了FREE DEQUEUED QUEUED ACQUIRED 这几种BufferState对应基本操作方法dequeue/queue/acquire/release的关系如下:

首先初始状态下,所有可用BufferSlot全是FREE状态,在mFreeSlots中管理。
BufferQueueProducer 发出dequeueBuffer请求会优先在mFreeSlots找的一个FREE状态的BufferSlot,后面在做dequeueBuffer的时候优先从mFreeBuffers中获取,发现没有对应的GraphicBuffer,就去申请,申请完成之后把GraphicBuffer与Slot绑定,然后把BufferSlot状态修改程Dequeue状态交由生产者生产。
生产完成之后,BufferQueueProducer 调用queueBuffer,把 BufferSlot放入队列mQueue中供消费者使用,并把BufferSlot的状态设置成QUEUE状态,并调用消费者的onFrameAvailable回调通知消费者,有可消费的BufferSlot可以消费。
消费者接到通知之后,调用acquireBuffer到mQueue队列中取出BufferSlot消费,并将BufferSlot状态修改程Acquired状态。消费完成之后调用releaseBuffer把BufferSlot放到mFreeBuffers中管理,这时不回去解绑GraphicBuffer和BufferSlot,所以GraphicBuffer不会被释放。
同时把BufferSlot状态设置为FREE状态,并调用BufferQueueProducer的回调函数通知生产者BufferSlot释放。这样就完成了一次流转。对应下图。
在这里插入图片描述
BufferQueue的生产消费关系还有另外一种使用方法。下面是这种操作方法attachbuffer/detachbuffer/cancelbuffer和状态的关系图:

同样首先初始状态下,所有可用BufferSlot全是FREE状态,在mFreeSlots中管理。
BufferQueueProducer调用attachBuffer 首先优先从mFreeSlots中FREE状态BufferSlot,如果mFreeSlots中没有在从mFreeBuffers中获取,然后把已经分配好的GraphicBuffer关联到该BufferSlot上。
并将BufferSlot状态修改成DEQUEUED 这里和dequeueBuffer方式不同,dequeueBuffer是BufferQueueCore负责申请管理GraphicBuffer,而attachBuffer是把申请好的GraphicBuffer关联到BufferQueueCore上。
然后BufferQueueProducer 调用queueBuffer,把 BufferSlot放入队列mQueue中供消费者使用,并把BufferSlot的状态设置成QUEUE状态,并调用消费者的onFrameAvailable回调通知消费者消费。
消费者接到通知之后,调用acquireBuffer到mQueue队列中取出BufferSlot消费,并将BufferSlot状态修改程Acquired状态。消费完成之后调用releaseBuffer把BufferSlot放到mFreeBuffers中管理,并调用BufferQueueProducer的回调函数通知生产者调用detachBuffer释放GrphicBuffer。这里也可以调用消费者的dettachBuffer完成释放。
在这里插入图片描述

2.BufferQueue的封装关系

BufferQueue的代码在/frameworks/native/libs/gui中,主要有BufferQueue.cpp BufferQueueCore.cpp BufferQueueProducer.cpp BufferQueueConsumer.cpp 这几个文件前面已经详细介绍过了,BufferQueue框架中的核心实现文件。
IConsumerListener.cpp IProducerListener.cpp 是生产者消费者相关的回调接口文件。
IGraphicBufferProducer.cpp IGraphicBufferConsumer.cpp 为BufferQueueProducer 和BufferQueueConsumer 提供接口,实现跨进程访问。
BufferItemConsumer.cpp CpuConsumer.cpp GLConsumer.cpp 等都继承ConsumerBase.cpp 来完成对BufferQueueConsumer的封装。
Surface.cpp SurfaceComposerClient.cpp SurfaceControl.cpp surface相关的文件是对BufferQueueProducer的封装。

我们正常使用的时候实际上是接触不到BufferQueue的,都是使用的层层封装之后的类,由此可以总结出:
BufferQueue的消费者是层层封装的,核心实现是BufferQueueConsumer。
BufferItemConsumer是对BufferQueueConsumer的封装,ImageReader又是对BufferItemConsumer的封装,BufferQueueConsumer BufferItemConsumer ImageReader都是消费者。

BufferQueue的生产者也是层层封装的,核心实现是BufferQueueProducer。
Surface 是对BufferQueueProducer的封装,ImageWriter又是对Surface的封装。BufferQueueProducer,Surface ,BufferQueueProducer 都是生产者。
每次封装都是有目的,都是为了实现某种特殊的功能。

3.BufferQueue的使用模型

BufferQueue 有两种使用方式:
一种是生产者dequeue buffer用来生产,生产完成之后调用queueBuffer把GraphicBuffer放回BufferQueue并通过回调通知消费者使用,消费者调用acquireBuffer 获取GraphicBuffer进行消费,消费完成之后调用releaseBuffer 把GraphicBuffer放回BufferQueue,并通知生产者detachBuffer。
在这里插入图片描述
另一种是生产者调用attachBuffer,获取BufferQueue上的一个freeSlot,并将已经分配好的GraphicBuffer与之连接,然后调用queueBuffer放回到BufferQueue上,并通知消费者消费,消费者调用acquireBuffer获取GraphicBuffer进行消费,消费完成之后调用releaseBuffer 把GraphicBuffer放回BufferQueue,并通知生产者detachBuffer。消费完成之后,消费者也可以调用detachBuffer进行释放GraphicBuffer。
在这里插入图片描述

六 、BufferQueue在java层的应用

1.ImageReader消费者关系

(1). ImageReader基本概念

ImageReader是消费者,消费者承担创建BufferQueue的责任。所以在ImageReader初始化的时候创建了BufferQueue以及最原始的生产者BufferQueueProducer和消费者BufferItemConsumer。

ImageReader工作的时候调用acquireNextImage 经过层层调用获取到BufferQueue中的GraphicBuffer来使用,并将状态改成aquired状态。使用完成之后调用releaseBuffer接口放回到mFreeBuffers队列中并通知生产者释放GraphicBuffer。

(2). ImageReader的初始化

ImageReader的初始化是调用nativeInit来完成的,nativeinit调用JNI接口ImageReader_init来实现,
在ImageReader_init中会调用BufferQueue::createBufferQueue(&gbProducer, &gbConsumer);创建最原始的生产者消费者和BufferQueueCore,之后会讲原始消费者封装到BufferItemConsumer中,然后调用bufferConsumer→setFrameAvailableListener(ctx);把JNIImageReaderContext实现的回调注册到ConsumerBase中去。生产完成之后发现有可以消费的Buffer,会先触发ConsumerBase::onFrameAvailable,在ConsumerBase::onFrameAvailable中会调用setFrameAvailableListener设置下来的回调,通知ImageReader进行消费。
然后会调用ctx->setProducer(gbProducer);保存原始生产者,供后面获取并封装使用。

(3). ImageReader的acquirebuffer

ImageReader的acquireBuffer主要根据mFormat创建SurfaceImage,然后用这个SurfaceImage做为参数继续调用acquireNextSurfaceImage完成acquire操作,
acquireNextSurfaceImage调用nativeImageSetup进而调用JNI接口ImageReader_imageSetup完成acquire操作,之后把SurfaceImage存储到mAcquiredImages中保存,在Release的时候释放。
ImageReader_imageSetup 中首先调用getBufferConsumer获取BufferItemConsumer,然后获取BufferItem,接着调用bufferConsumer->acquireBuffer(buffer, 0);继续完成acquireBuffer操作。
并把获取到的BufferItem通过Image_setBufferItem接口设置到image的mNativeBuffer属性中去
acquireBuffer调用ConsumerBase::acquireBufferLocked来完成item的填充,然后用mSlots[item->mSlot].mGraphicBuffer给item→mGraphicBuffer赋值。
acquireBufferLocked则又是调用mConsumer->acquireBuffer完成的,acquireBuffer流程上面已经分析过了,负责获取对应的BufferItem输出。

frameworks/base/media/java/android/media/ImageReader.java
public Image acquireNextImage() {
        SurfaceImage si = new SurfaceImage(mFormat); //创建一个SurfaceImage
        int status = acquireNextSurfaceImage(si);   //调用acquireNextSurfaceImage继续acquire操作
}
 
 
private int acquireNextSurfaceImage(SurfaceImage si) {
        synchronized (mCloseLock) {
            if (mIsReaderValid) {
                status = nativeImageSetup(si);
            }
 
            if (status == ACQUIRE_SUCCESS) {
                mAcquiredImages.add(si);
            }
            return status;
        }
}
frameworks/base/media/jni/android_media_ImageReader.cpp
static jint ImageReader_imageSetup(JNIEnv* env, jobject thiz, jobject image) {
 
    JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz);
 
    BufferItemConsumer* bufferConsumer = ctx->getBufferConsumer();
    BufferItem* buffer = ctx->getBufferItem();
 
    status_t res = bufferConsumer->acquireBuffer(buffer, 0);
 
    Image_setBufferItem(env, image, buffer);
 
    return ACQUIRE_SUCCESS;
}
 
static void Image_setBufferItem(JNIEnv* env, jobject thiz,
        const BufferItem* buffer)
{
    env->SetLongField(thiz, gSurfaceImageClassInfo.mNativeBuffer, reinterpret_cast<jlong>(buffer));
}
 
frameworks/native/libs/gui/BufferItemConsumer.cpp
status_t BufferItemConsumer::acquireBuffer(BufferItem *item,
        nsecs_t presentWhen, bool waitForFence) {
 
    err = acquireBufferLocked(item, presentWhen);
 
    item->mGraphicBuffer = mSlots[item->mSlot].mGraphicBuffer;
 
    return OK;
}
 
frameworks/native/libs/gui/ConsumerBase.cpp
status_t ConsumerBase::acquireBufferLocked(BufferItem *item,
        nsecs_t presentWhen, uint64_t maxFrameNumber) {
   
    status_t err = mConsumer->acquireBuffer(item, presentWhen, maxFrameNumber);  //调用acquireBuffer完成acquireBuffer操作
    if (item->mGraphicBuffer != nullptr) {
        if (mSlots[item->mSlot].mGraphicBuffer != nullptr) {
            freeBufferLocked(item->mSlot);
        }
        mSlots[item->mSlot].mGraphicBuffer = item->mGraphicBuffer;      //如果item->mGraphicBuffer不为空,就用item->mGraphicBuffer覆盖mSlots[item->mSlot].mGraphicBuffer
    }
 
    return OK;
}

(4). ImageReader的release

使用graphicbuffer完成之后的释放需要注意一下:

消费者的最原始的buffer释放流程(BufferItemConsumer中的流程)应该是使用完成之后先调用releasebuffer将mlots 置位成FREE状态,link到mFreeBuffers中,
此时GraphicBuffer还是link在mSlots中,但是处于可用状态。在这个时候需要调用discardFreeBuffers来解除GraphicBuffer和mSlots link关系,
然后把mSlots标志成mFreeSlots状态完成释放。但是实际释放过程没有这么简单,ImageReader封装了releaseBuffer接口成releaseImage,但是这个接口是私有的,不能被公开调用。
ImageReader还封装了一个close接口,close接口完成了release操作,但是同时也清理掉了ImageReader消费者本身。

那么如何去释放呢,发现ImageReader的创建的SurfaceImage可以通过acquireNextImage获取,而SurfaceImage中有close方法,这个方法调用了 releaseImage完成SurfaceImage自身的释放。
到此为止看起来,该release的都release了,在调用一下discardFreeBuffers就能解决战斗,其实并没有。在生产者进程中Surface自身还维护了一个BufferSlot mSlots[NUM_BUFFER_SLOTS],
attachBuffer的时候 GraphicBuffer link上,在收到消费者onBufferReleased回调之后调用detachNextBuffer取消关联。
但是实际上 mFormat != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED 的时候是走不到detachNextBuffer的所以还是不能即时释放。

void JNIImageWriterContext::onBufferReleased() {
        // Detach the buffer every time when a buffer consumption is done,
        // need let this callback give a BufferItem, then only detach if it was attached to this
        // Writer. Do the detach unconditionally for opaque format now. see b/19977520
        if (mFormat == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
            mProducer->detachNextBuffer(&buffer, &fence);
        }
        env->CallStaticVoidMethod(mClazz, gImageWriterClassInfo.postEventFromNative, mWeakThiz);
}

下面是ImageReader的初始化,acquirebuffer,releasebuffer的操作流程图:
在这里插入图片描述

2.imageWriter的生产者关系

(1). ImageWriter基本概念

ImageWriter是生产者,根据消费者的创建的原始生产者封装并创建自己,然后link到消费者进程。
然后通过dequeueInputImage最终调用BufferQueueProducer:dequeueBuffer拿到GraphicBuffer进行处理。
处理完成之后调用queueInputImage 最终调用BufferQueueProducer:queueBuffer 把 buffer放回队列供消费者使用。

(2). ImageWriter的初始化

与ImageReader类似,ImageWiter也是调用nativeInit来完成创建工作,nativeInit会调用JNI接口ImageWriter_init,在ImageWriter_init中首先通过surface->getIGraphicBufferProducer调用获取原始的Producer
然后调用new Surface进行封装,然后调用ctx->setProducer(producer)保存封装好的生产者,封装完成之后调用 producer->connect链接BufferQueueCOre并注册回调。

(3). ImageWriter的dequeue

ImageWriter提供dequeueInputImage接口来完成dequeue操作,dequeueInputImage主要通过nativeDequeueInputImage来实现,获取image之后会放到到mDequeuedImages保存。
nativeDequeueInputImage调用JNI接口ImageWriter_dequeueImage实现,ImageWriter_dequeueImage的主要操作是 通过ctx→getProducer()获取Surface,之后用获取到的Surface调用Surface的dequeueBuffer完成dequeue操作。
Surface的dequeueBuffer函数则是调用BufferQueueProducer的dequeueBuffer函数最终实现。

(4). ImageWriter的queue

dequeBuffer主要就是调用nativeQueueInputImage来完成,nativeQueueInputImage调用JNI接口ImageWriter_queueImage来实现,在ImageWriter_queueImage中通过ctx→getProducer获取生产者
然后根据获取到的生产者继续调用anw->queueBuffer完成queueBuffer的操作,最终调用BufferQueuProducer的queueBuffer函数完成queue操作。

下面是imagewriter 初始化,dequebuffer,queuebuffer的详细的操作流程图:
在这里插入图片描述

(5).ImageWriter的attachandqueue

如果这个imageBuffer是不属于ImageWriter自己的,就会走attach流程,attach之后调用queue放到BufferQueue中供消费者使用。
具体的是先调用ownedByMe判断是否属于自己的,如果不是则先找到之前的Owner,然后调用detachImage断开和之前owner的关系。在然后就是调用attachAndQueueInputImage完成attach和queue操作。

public void queueInputImage(Image image) {
        boolean ownedByMe = isImageOwnedByMe(image);
 
        // For images from other components, need to detach first, then attach.
        if (!ownedByMe) {
            ImageReader prevOwner = (ImageReader) image.getOwner();       //获取之前的owner
            prevOwner.detachImage(image);            //调用detachImage释放和之前owner的关联。
            attachAndQueueInputImage(image);       //调用attachAndQueueInputImage完成attach和queue操作。
            image.close();                        //关闭image
            return;                                 //直接返回
        }
 
        nativeQueueInputImage(mNativeContext, image, image.getTimestamp(), crop.left, crop.top,
                crop.right, crop.bottom, image.getTransform(), image.getScalingMode());         //如果ownedByMe则调用这里做queue操作。
    }

具体attach流程如下图:
在这里插入图片描述

七、总结

综上介绍了消费者模型,BufferQueue的缓存区管理以及关键概念BufferSlot BufferState BufferItem BufferQueue。
然后介绍了生产者消费者BufferQueueProducer和 BufferQueueConsumer 的关键方法dequeue/queue/acquire/release。
再之后然后介绍了将生产者消费者的生产消费关系和BufferQueue完整的串起来,状态变化,接口回调,Buffer流转。
最后介绍了实际应用中BufferQueue的封装层imagereader,imagewriter。
BufferQueue的生产消费关系终结如下图,消费者和生产者层层封装,消费者可以跨进程也可以不跨进程,一般不跨。
生产者可以跨进程也可以不跨进程一般是跨进程的。
在这里插入图片描述
这些就是BufferQueue的核心框架,另外还有一些具体的小细节,可以在工作过程中进一步的学习掌握。

  • 24
    点赞
  • 112
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值