首先 BufferSlot 是 Buffer 的封装, BufferQueueCore 使用 mSlots 来管理所有Buffer, mSlots 是一个 BufferSlot 数组, 包含了所有的BufferSlot. BufferSlot里面最重要的两个成员是: mGraphicBuffer 和 mBufferState
BufferItem是 mQueue 中的元素, mQueue是 BufferQueueCore中另一个重要的成员, 是一个FIFO队列。 生产者调用queueBuffer的时候, 会把这个Buffer封装成BufferItem,然后放入到 mQueue中。 消费者调用acquireBuffer的时候会取出来mQueue中的第一个元素进行消费。
我们知道Buffer有四种状态: Dequeued, Queued, Acquired, Free. 这种状态使用 BufferState来封装, 就是我们上面提到的 BufferSlot中的 mBufferState.
整个BufferQueue机制是通过 BufferQueue.cpp 的 createBufferQueue来创建的, 会创建三个角色: BufferQueueCore, BufferQueueProducer, BufferQueueConsusmer. 其中 BufferQueueProducer 和 BufferQueueConsumer都是Binder, 可以跨进程调用。
但是我们一般都是在消费者进程创建的BufferQ, 所以一般只要把 BufferQueueProducer传递给远端就可以了。
BufferQueueProducer和BufferQueueConsumer都可以直接访问 BufferQueueCore, 其中 BufferQueueProducer中还包含了BufferQueueCore中的mSlots的一个 引用, 这样会方便 BufferQueueProducer更快捷的操作 mSlots.
BufferQueueConsumer 和 BufferQueueProducer 的回调 都是在各自的 connect 方法中执行的。 会向 BufferQueueCore中分别注册自己的回调: ProxyConsumerListener 和 IProducerListener 这两个回调, 因为 Producer一般在远端, 所以 IProducerListener这个回调里面还包含了Binder死亡回调的实现。
BufferItem中定义了索引值mSlot, 这个是 mSlots这个数组的下标, 这样通过BufferItem就可以得到具体的BufferSlot了。
BufferSlot的状态分为: Active状态和Free状态, Active状态代表了正在被生产者或者消费者使用的状态, Free状态代表了空闲的BufferSlot, 而Free状态又被分为了: FreeBuffer 和 FreeSlot, FreeBuffer代表了已经拥有Buffer的BufferSlot, FreeSlot代表还没有拥有Buffer的BufferSlot.
Active状态的BufferSlot的下标 被放在了 mActiveBuffers 这个 vector中。
FreeBuffer状态的BufferSlot的下标 被放在了 mFreeBuffers 这个 vector 中。
FreeSlot状态的BufferSlot的下标被放在了mFreeSlot这个vector中。
BufferSlot没有实现任何Binder或者Flattenable接口, 不能跨进程传递, 所以只能在BufferQueueCore中接受管理, 同时 BufferQueueProducer 作为bn端, 和BufferQueueCore在同一个进程中, 也保存了 mSlots 的一个引用。
BufferItem和GraphicBuffer都实现了Flattenable, 可以跨进程传递。
生产和消费过程
首先看一下生产者最常见的操作: dequeueBuffer 和 queueBuffer
dequeueBuffer的主要工作:
- 调用 waitForFreeSlotThenRelock 方法, 这个方法的作用就是: 如果是dequeue, 就优先尝试从 mFreeBuffers 里面找slot, 然后尝试从 mFreeSlots里面找slot. 如果是attach, 就优先尝试从mFreeSlots里面找slot, 然后尝试从mFreeBuffers里面找slot. waitForFreeSlotThenRelock 这个方法只是返回slot, 并不会真的去分配GraphicBuffer.
这里面有一些细节, 比如如果开启了BufferManager, 那么 waitForFreeSlotThenRelock 就只会返回 FreeBuffer了, 但是如果这个FreeBuffer需要重新分配空间来满足Buffer的一些属性, 那么这个FreeBuffer就不能使用, 我们就要把这个FreeBuffer清空, 放入到FreeSlot中, 然后再次去循环调用 waitForFreeSlotThenRelock, 知道找到合适的slot。 - 把找到的slot放入到 mActiveBuffer中, 并更改BufferState状态为 Dequeue状态。
并看一下是不是需要分配GraphicBuffer, 如果需要分配就分配新的GraphicBuffer. 当GraphicBuffer好了之后,通知 ConsumerListener的onFrameDequeued
方法, 这个方法传入的参数是分配好的GraphicBuffer。
如果是Consumer通过 attachBuffer 添加的一个Buffer到BQ, 那么生产者在 dequeueBuffer的时候遇到了这样的Buffer, 肯定需要重新分配一遍, 因为消费者attach进来的Buffer是不符合生产者要求的。
dequeueBuffer
这个方法会返回一个可用的GraphicBuffer, 而waitForFreeSlotThenRelock只是会返回可用的slot.
requestBuffer
这个方法就是根据传入的slot, 把这个slot对应的GraphicBuffer放入到指定的buffer指针中, 这个方法并不会分配新的Buffer. 也就是说requestBuffer得到的Buffer也有可能是空的。
queueBuffer的流程:
- 创建 BufferItem, 并且更改BufferState状态为 Queued, 并且把BufferItem关联到GraphicBuffer, 然后尝试放入FIFO中, 之所以尝试放入, 是因为要看FIFO中最后一个元素是否需要被Replace. 然后会把 BufferItem关联的GraphicBuffer的引用给清空, 也就是说BufferItem应该只包含slot, 并不包含GraphicBuffer的引用。
- 然后根据是否被Replace了, 来调用 ConsumerListener的 onFrameAvailable 或者 onFrameRepleaced 方法。
queueBuffer的流程是比较简单的。
attachBuffer的流程
attachBuffer就是把本来不属于该BQ的Buffer放入该BQ中让该BQ管理。
- 首先调用 waitForFreeSlotThenRelock方法, 只不过这一次是优先从 mFreeSlots 中寻找 slot.
- 找到这个slot之后 把它放入到 mActiveBuffers中管理, 并且把状态改成Dequeued, 也就是说 生产者调用的 attachBuffer, 相当于是Dequeue出去一个Buffer.
detachBuffer的流程
- 调用 ConsumerListener 的
onFrameDetached
方法 - 更改BufferStatus状态为Dequeue--, 然后把BufferSlot 从mActiveBuffers中移除,放入到 mFreeSlots中, 并且清空这个BufferSlot.
- 调用 ConsumerListener 的
onBuffersReleased
方法。
这里会调用两个ConsumerListener的回调。
也就是说 Producer 的 attach 和 detach方法, 都不会往FIFO中放入BufferItem, 仅仅是把Buffer放入了BufferQueueCore中进行管理。 attachBuffer之后, 这个Buffer的状态是Dequeued, 是活跃的。 detachBuffer之后, 这个buffer的状态是Queued--的, 是FreeSlot状态。
从上面的分析可以知道, 生产者直接attach一个buffer到BQ中是不能被消费的, 必须 attach+queue才可以。
网友评论