Android Camera : 从 Framework 到 HAL,Kernel 的 buf 管理探究
本文来自作者 海角之南 在 GitChat 上分享 「Android Camera : 从 Framework 到 HAL,Kernel 的 buf 管理探究」,「阅读原文」查看交流实录。
「文末高能」
编辑 | 哈比
Android Camera Buf liVe
Phone Camera 的功能可谓日新月异且富有趣味,除了硬件功能强大,软件逻辑也很重要,Buf 管理是软件逻辑里非常重要的一环,弄懂它的来龙去脉有助于我们掌握一个系统、掌握一个思路以及学会一种框架,甚至领悟到优化着力点。
Android 是 Phone 上的流行系统,具有时新性、开源性以及趣味性,我们选择 Android 作为研究对象。
通过本篇文章的学习,我们可以掌握 Camera 架构下 Buf 的生命周期,伴随 App 的 Request 产生,从 FrameWork 传到 hal,从 hal 传到 kernel 空间,然后填充数据后经 kernel 到 hal 再到 FrameWork 的一个生命周期。
我们可以学到:
FrameWork 下是谁对 buf 做管理?管理 buffer 的数据都有什么,这些数据结构更新的逻辑是什么?
FrameWork 是如何和 hal 层对 buf 交互的?
简述 kernel 下 buf 的交互。
一、 FrameWork 的 buf 管理者
FW 层 buf 依靠 Camera3BufferManager 管理,它有什么关键函数呢?
Camera3BufferManager
可以 alloc gralloc buffer,在 client 端申请时它可以把 buffer 交给 (hands out) client(e.g.,Camera3OutputStream) Camera3BufferManager 有若干自己管理的 buf,在 client 申请时,优先分配这些 buf 给 client,当 buf 不足时,需要重新 alloc,在 buffer 过多时,需要 de-alloc。
registerStream
提供 stream info 给 Camera3BufferManager.c3bm 会负责该 stream 的 buffer 分配。
stream 首先要处于 CONFIGUED 状态,然后再注册到 c3bm 中。
c3bm 会通过 getBufferForStream() 函数给 stream 提供 buffer。
提供的 buffer 可以传到 hal 层用于 image output,当 hal 层有输出数据时,这个 buffer 可以 queue 到 ANativeWindow (Surface) 中,等地下游 consumer 使用。
当下游 consumer 使用完并 release 后,BufferQueueProducer 的 callback 函数 onBufferReleased 会 returnBufferForStream() 函数把这个 buffer 返还给 Camera3BufferManager。
unregisterStream
注销,注销后的 buf 供 Camera3BufferManager 管理。
getBufferForStream
Camera3BuffferManager 从 free buf list 中找到第一个 free 的 buffer,分配给 stream,如果无自己管理的 buf,就 alloc 一个 buffer 给 stream。
onBufferReleased
意味着 consumer 已经使用完该 buffer,但是它并不是直接返回给 Camera3BufferManager,而是说这个 buffer 可以从 stream 上 dequeue 了,表示这么个状态。由于这个函数存在 Camera3BufferManager 就知道一个 stream 中有多少个可用的 buffer 了。
returnBufferForStream
stream 向 Camera3BufferManager 返回一个 buffer。
我们重点关注 registerStream,getBufferForStream、onBufferReleased 函数。
1.1 registerStream
Camera3BufferManager 内部含有一组 Map,它们的关系如下:
StreamSetMap 是一组 StreamSet 合集,StreamSet 下含有一组 Map、streamInfoMap、handoutBufferCountMap 以及 attachedBufferCountMap。
第一,handout buffer Map 是给 client 使用的 buffer 的集合。
第二,freeBuffers 是属于特定某个 buffer set 的所有 buffer 的合集,由 returnBufferForStream 返回,待使用的 buffer。
第三,attached buffer 可以是 handout 的,也可以是 free 的,它实际上是这两种的合集。
图 1- Camera3BufferManager
registerStream 就是为这个 streamInfo 建立一组 StreamSet。
StreamSet 内部的 Map 也建立起来,并初始化为 (key=streamId,val=0)。
最后把 StreamSet 以 key 是 streamSetId 加到 mStreamSetMap 中。
把 stream 以 key 为 streamId 加到 mStreamMap 中。
下面这段代码描述了这个逻辑:
status_t Camera3BufferManager::registerStream(wp<Camera3OutputStream>& stream, const StreamInfo& streamInfo) { ATRACE_CALL(); int streamId = streamInfo.streamId; int streamSetId = streamInfo.streamSetId; /* 分别拿到 sTream 的 id 以及 setId. 一个 set 含有三个 Map, 描述 streamid 的 streamInfoMap, 给 stream 的 buf 个数 handoutBufferCountMap,stream attached 的 attachedBufferCountMap.*/ Mutex::Autolock l(mLock);// 这是个利用构造析构的 mutex 锁 . 包了 pthread_mutext. // 先尝试 , 一组 StreamSetMap 的 StreamMap 中找是否有 streamid 的 stream. 一个 streamSetMap 含有多个 StreamSet. 一个 StreamSet 含有多个 Stream. for (size_t i = 0; i < mStreamSetMap.size(); i++) { ssize_t streamIdx = mStreamSetMap[i].streamInfoMap.indexOfKey(streamId); } // 根据 StreamSetId 在 streamSetMap 中找是否已经注册过了 . 如果找不到 , 我们需要 new 出一个 StreamSet. // 并把这个新的 StreamSet 和 StreamSetId 关联到 mStreamSetMap 中 . ssize_t setIdx = mStreamSetMap.indexOfKey(streamSetId); if (setIdx == NAME_NOT_FOUND) {/* 没找到 , 怎么办 ?new 出一个 */ StreamSet newStreamSet; setIdx = mStreamSetMap.add(streamSetId, newStreamSet); } // 拿到刚才新的 StreamSet, 并取出其 streamid. 这里需要对 StreamSet 的 handout ,attached 的 Map 清零 . StreamSet& currentStreamSet = mStreamSetMap.editValueAt(setIdx);//currentStreamSet 是个友元ssize_t streamIdx = currentStreamSet.streamInfoMap.indexOfKey(streamId); currentStreamSet.streamInfoMap.add(streamId, streamInfo); currentStreamSet.handoutBufferCountMap.add(streamId, 0); currentStreamSet.attachedBufferCountMap.add(streamId, 0); mStreamMap.add(streamId, stream); return OK; }
registerStream 的调用时机是什么 ?
根据 buf 的尺寸以及 buf 有何用途,针对性的生成一个 StreamInfo 结构,此时会利用 registerStream 注册进 mStreamSetMap 中。
下面这段代码体现了 registerStream 是如何使用的。
status_t Camera3OutputStream::configureQueueLocked() {// 虽然没有传参 , 但是内部基本上是调用成员变量的函数 // 配置 consumer 侧的 ANativeWidow 接口 .consumer 一般是 Surface 类 . 此时还要把一个 listener 注册进 consumer. 以便 consumer 在 release 时候可以及时通知 Camera3BufferManager 做一些 release 的 buf 管理 . res = mConsumer->connect(NATIVE_WINDOW_API_CAMERA, /*listener*/mBufferReleasedListener); if (mBufferManager != 0 && mSetId > CAMERA3_STREAM_SET_ID_INVALID) { uint32_t consumerUsage = 0; getEndpointUsage(&consumerUsage); // 先生成描述这个 stream 的 streaminfo // stream info 依赖 , 这个 stream 描述的 buf 的宽 , 高 , 格式 (yuv 与否), 数据空间 . 以及这块 buf 的用途 .android 系统把待显示的区域用不同的属性描述 . StreamInfo streamInfo(getId(), getStreamSetId(), getWidth(), getHeight(), getFormat(), getDataSpace(),camera3_stream::usage | consumerUsage, mTotalBufferCount, /*isConfigured*/true); wp<Camera3OutputStream> weakThis(this); // 把 streaminfo 注册进去 , 这里的 this 就是 Camera3OutputStream 类本身 . res = mBufferManager->registerStream(weakThis, streamInfo); if (res == OK) { // 此时先进值 GraphicBuffer 的产生 . 因为对于创建多大的 GraphicBuffer 是由 Camera3BufferManager 负责的 . mConsumer->getIGraphicBufferProducer()->allowAllocation(false); mUseBufferManager = true; } } }
1.2 Camera3BufferManager::unregisterStream
显而易见,上如 Register 的逆操作,就是图 1 中 StreamSet 的数据结构的解构。
status_t Camera3BufferManager::unregisterStream(int streamId, int streamSetId) { Mutex::Autolock l(mLock); // 删掉 Stream 关联的所有 Buffer. StreamSet& currentSet = mStreamSetMap.editValueFor(streamSetId); BufferList& freeBufs = currentSet.freeBuffers;// 取出 StreamSet 的 freeBuffers 链表 . BufferCountMap& handOutBufferCounts = currentSet.handoutBufferCountMap; BufferCountMap& attachedBufferCounts = currentSet.attachedBufferCountMap;// 从 Set 中取出 handout,attached 的计数 Map InfoMap& infoMap = currentSet.streamInfoMap; removeBuffersFromBufferListLocked(freeBufs, streamId);// 这里是删掉 buf 的地方 . handOutBufferCounts.removeItem(streamId); // 删掉对应的 id attachedBufferCounts.removeItem(streamId); infoMap.removeItem(streamId); // 删掉 streamid 的 infoMap currentSet.maxAllowedBufferCount = 0; for (size_t i = 0; i < infoMap.size(); i++) { if (infoMap[i].totalBufferCount > currentSet.maxAllowedBufferCount) { currentSet.maxAllowedBufferCount = infoMap[i].totalBufferCount; } } mStreamMap.removeItem(streamId); // 删除 streamId 的 mStreamMap return OK; }
Camera3BufferManager::unregisterStream 的调用时机
status_t Camera3OutputStream::disconnectLocked() { status_t res; // 和 native window disconnect res = native_window_api_disconnect(mConsumer.get(), NATIVE_WINDOW_API_CAMERA); // 此时 ,Camera 设备是 Idle 的 , 不会再有想 Camera3BufferManager 的 Get Buffer 的请求 . 此时注销掉这个 StreamSet 是安全的 . if (mUseBufferManager) { res = mBufferManager->unregisterStream(getId(), getStreamSetId()); mUseBufferManager = false; } return OK; }
1.3 Camera3BufferManager::getBufferForStream
这个 Camera3BufferManager 的核心函数 是 Client 的 buf 的获取。
-
需要理解图 1 中几个数据结构的深层含义:
一个 StreamSetMap 含有多个 StreamSet,根据 StreamSetId 来区分。
一个 StreamSet 含有一个 handoutBufferCountMap,一个 attachedBufferCountMap,一个 freeBuffers,一个 handoutBufferCountMap 含有多个 count, 以 Stream 区分, attchedBufferCountMap 同理。
理清楚这些,下面的 get buffer 函数就明白了。
首先,我们有 streamId,根据这个 StreamId 从 StreamSetMap 中拿到 StreamSet,一个 StreamSet 含有一个
StreamInfoMap
、一个HandoutCountBufferMap
、一个attachedCountBufferMap
。handoutCountBufferMap 记录有几个 buffer 分配给 stream 了。AttachedCountBufferMap,记录了 stream 中含有多少个 attached 状态的 buf。这个所谓的 attached buf 是 client release 的个数和 handout 给 client buffer 的个数之和。
-
我们在一个 StreamSet 中轮询所有的 streamId,叠加其 attachedCount 得到一个 StreamSet 的 buffer 总和,记为 count3,如果 count3 大于 MAX WATERMARK,意味着此时需要 free 掉一些 buffer。
由于 StreamSet 是一组 streamid 的 count 的总和,我们只需要找到一个第三方(另一个 streamid)的 streamid,其满足其
count2(attached ones)>count1(handout ones)
,这种说明含有 client release 的 buffers。如下公式描述,如果 attached 个数大于 handout 的个数,说明有 free 状态 client release 的 buf。
注意区别 client release 的 buf 和处于 freeBuffers 中的 buffer。 相同点是它们都是 free 的。
不同点是 client release 的 buf 说明 client 处理完了,但尚在Camera3BufferManager
的管理范围内(也就是说尚在 attached buffer count 记录内)。
而 freeBuffers 处于 StreamSet 的,不是某个 stream 的,而是属于一组 stream 组成的 streamSet 的。
如果没有超过 watermark 的情况。直接从 freebuffers 取出描述目标 streamid 的 buf 的一个 bufferEntry 结构。如果这个 bufferEntry 描述的 graphicBuffer 尚未分配到,需要经
createGraphicBuffer
创建一个GraphicBuffer
,而要创建一个 buf,就需要 buffer 的期望尺寸、以及其 bpp 格式、还有其用途等等。
图 2- Camera3BufferManager’s StreamSet summary
status_t Camera3BufferManager::getBufferForStream(int streamId, int streamSetId, sp<GraphicBuffer>* gb, int* fenceFd) { Mutex::Autolock l(mLock); StreamSet &streamSet = mStreamSetMap.editValueFor(streamSetId);// 从 set 中拿到 handout ,attached buffer, 他们是友元 . BufferCountMap& handOutBufferCounts = streamSet.handoutBufferCountMap; size_t& bufferCount = handOutBufferCounts.editValueFor(streamId); BufferCountMap& attachedBufferCounts = streamSet.attachedBufferCountMap; size_t& attachedBufferCount = attachedBufferCounts.editValueFor(streamId); if (attachedBufferCount > bufferCount/* handout buffer count*/) { // 如果 buf attached 的个数超过了 handout 出去的个数 , 说明 Stream 本身有一些被 consumer release 的可用 buffer, 直接在此使用它们就好了 . bufferCount++; return ALREADY_EXISTS; } GraphicBufferEntry buffer = getFirstBufferFromBufferListLocked(streamSet.freeBuffers, streamId); if (mGrallocVersion < HARDWARE_DEVICE_API_VERSION(1,0)) { // Allocate one if there is no free buffer available. if (buffer.graphicBuffer == nullptr) {// 如果 Camera3BufferManager 本身没有管理的 freebuffer. 重新 alloc const StreamInfo& info = streamSet.streamInfoMap.valueFor(streamId); buffer.fenceFd = -1; // graphicbuffer 的分配 , 需要尺寸 . buffer.graphicBuffer = mAllocator->createGraphicBuffer( info.width, info.height, info.format, info.combinedUsage, &res); } bufferCount++; // 它们都是友元 , 此处递增也会同步到对应的 Map 中 , 此处是递增 handout attachedBufferCount++; *gb = buffer.graphicBuffer; *fenceFd = buffer.fenceFd;// 用于和 hal 层的同步 . 拿到 fence 的那一方才可以对 buf 读写 // 如果这个 StreamSet 的 free 状态的 buf 大于 watermark, 说明这 StreamSet 中有过多空闲的 buf, 需要回收 . if (streamSet.streamInfoMap.size() > 1) { // 还有多个 stream bool freeBufferIsAttached = false; for (size_t i = 0; i < streamSet.streamInfoMap.size(); i++) { firstOtherStreamId = streamSet.streamInfoMap[i].streamId; if (firstOtherStreamId != streamId) { // 找到同个 streamSet 里的其他的 stream, 看它的 attached 状态的 buf 是不是比 handout 的 buffer 多 . 多意味着 stream 上被 attached 的 buf 有一部分是空闲的 . size_t otherBufferCount = streamSet.handoutBufferCountMap.valueFor(firstOtherStreamId); size_t otherAttachedBufferCount = streamSet.attachedBufferCountMap.valueFor(firstOtherStreamId); if (otherAttachedBufferCount > otherBufferCount) { freeBufferIsAttached = true; // 找到了 ,attached 比 handout 的个数多的 StreamId, 说明有多个空闲的 buf. break; // 找到一个 Stream 就可以了 , 我们不需要释放太多 , 找到一个释放一个就能满足目前需求 . 因为我们本来也是只是申请一个 buf 的 , 所以释放一个 , 再申请一个应该不会差太多 . 这里先不考虑尺寸等信息 . } if (hasBufferForStreamLocked(streamSet.freeBuffers, firstOtherStreamId)) { freeBufferIsAttached = false; break; } } firstOtherStreamId = CAMERA3_STREAM_ID_INVALID; } if (firstOtherStreamId == CAMERA3_STREAM_ID_INVALID) { return OK; } size_t totalAllocatedBufferCount = streamSet.freeBuffers.size(); for (size_t I = 0; I < streamSet.attachedBufferCountMap.size(); i++) {// 每个 streamSet 含有 free 的 buffer 以及 attached 状态的 buffer. 他们的和是 Camera3BufferManage 分配给 streamSet 的所有 buffer 的个数和 totalAllocatedBufferCount += streamSet.attachedBufferCountMap[i]; } if (totalAllocatedBufferCount > streamSet.allocatedBufferWaterMark) {// 大于 watermark 就 de-alloc 一部分 . if (freeBufferIsAttached) { ALOGV("Stream %d: Freeing buffer: detach", firstOtherStreamId); sp<Camera3OutputStream> stream = mStreamMap.valueFor(firstOtherStreamId).promote();//de-alloc 的是” 其他” 的 stream 的 buffer, 也因为是他们的 attachedbuf 超过了 handout 的 buf. // Detach and then drop the buffer. { mLock.unlock(); sp<GraphicBuffer> buffer; stream->detachBuffer(&buffer, /*fenceFd*/ nullptr); mLock.lock(); } size_t& otherAttachedBufferCount = streamSet.attachedBufferCountMap.editValueFor(firstOtherStreamId); otherAttachedBufferCount--; } else { // Droppable buffer is in the free buffer list, grab and drop getFirstBufferFromBufferListLocked(streamSet.freeBuffers, firstOtherStreamId); } } } } else { // TODO: implement this. return BAD_VALUE; } return OK; }
getFreeBufferForStream 的用法。
下面的代码会逐渐解释mConsumer
对象的 attached buffer 的逻辑实现。
status_t Camera3OutputStream::getBufferLocked(camera3_stream_buffer *buffer) { if ((res = getBufferPreconditionCheckLocked()) != OK) { return res; } ANativeWindowBuffer* anb; int fenceFd = -1; bool gotBufferFromManager = false; if (mUseBufferManager) { sp<GraphicBuffer> gb; // 使用 Camera3BufferManager 管理 buffer. 并从中拿到 GraphicBuffer. 这里作为 gb. res = mBufferManager->getBufferForStream(getId(), getStreamSetId(), &gb, &fenceFd); if (res == OK) { // 把 gb 转成 ANativeWindowBuffer. 并添加到 consumer 的 activeBuffer 中 . anb = gb.get(); res = mConsumer->attachBuffer(anb); gotBufferFromManager = true; // 表明从 Camera3BufferManager 获得来的 buf } else if (res == ALREADY_EXISTS) { // 如果这个 buf 是本身 stream release 下的 , 此时并没有重新 create buf. 说明这个 buf 不是由 Camera3BufferManager 管理的 . gotBufferFromManager = false; } else if (res != OK) { return res;// error } } if (!gotBufferFromManager) { /** * 我们先 unlock 锁 , 因为会有如下的死锁场景: * Thread 1: StreamingProcessor::startStream -> Camera3Stream::isConfiguring(). * 线程 1 会请求 StreamingProcessor lock 并且锁住 Camera3Stream lock. * * Thread 2: Camera3Stream::returnBuffer->StreamingProcessor::onFrameAvailable(). * 线程 2 会请求 Camera3Stream lock 和 bufferQueue lock, 并锁住 * StreamingProcessor lock. * * Thread 3: Camera3Stream::getBuffer(). * 线程 3 会请求 Camera3Stream lock 并锁住 bufferQueue lock. * 此时会有一个循环锁依赖 . */ sp<ANativeWindow> currentConsumer = mConsumer; mLock.unlock(); // 使用 consumer 自己的空闲 buffer res = currentConsumer->dequeueBuffer(currentConsumer.get(), &anb, &fenceFd); mLock.lock(); return res; } } handoutBufferLocked(*buffer, &(anb->handle), /*acquireFence*/fenceFd, /*releaseFence*/-1, CAMERA3_BUFFER_STATUS_OK, /*output*/true); return OK; }
1.4 Camera3BufferManager::onBufferReleased
了解了这几个 Map 之间的关系,也不难理解如下逻辑。这里需要特别注意所有的操作都是用的友元方法,会直接更新到本体类。
status_t Camera3BufferManager::onBufferReleased(int streamId, int streamSetId) { Mutex::Autolock l(mLock); // Camera3BufferManager 会在收到这个回调时候,统计当前的 stream 的 handout 的 buffer count。此处也是释放。 StreamSet& streamSet = mStreamSetMap.editValueFor(streamSetId); BufferCountMap& handOutBufferCounts = streamSet.handoutBufferCountMap; size_t& bufferCount = handOutBufferCounts.editValueFor(streamId); bufferCount--; /* * 此处只是 handout 的 buffer count 减一 , 注意此处的 attachedbuffer count 并没有减一 . * 这就是这两者的不同 ,attached buffer count 包含”handout” 给 client 的 , * 也包含 client release 回来的 . * */ return OK; }
什么时候会调用
onBufferReleased
?
第一步,在configureQueueLocked
中添加 release 时候的 listener。即mBufferReleasedListener
描述的Camera3OutputStream::BufferReleasedListener
类。
这个类含有onBufferReleaseed
函数。
status_t Camera3OutputStream::configureQueueLocked() { res = mConsumer->connect(NATIVE_WINDOW_API_CAMERA, /*listener*/mBufferReleasedListener);// 注册一个 release 时候 consuemr 可以调用 Camera3OutputStream 的回调函数 . }
-
listener 去哪儿了 ?
Connect 中有记录 listener 的部分
status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, int api/*4*/, bool producerControlledByApp, QueueBufferOutput *output) { Mutex::Autolock lock(mCore->mMutex); mConsumerName = mCore->mConsumerName; int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode, mDequeueTimeout < 0 ? mCore->mConsumerControlledByApp && producerControlledByApp : false, mCore->mMaxBufferCount) - mCore->getMaxBufferCountLocked(); switch (api) { case NATIVE_WINDOW_API_EGL: case NATIVE_WINDOW_API_CPU: case NATIVE_WINDOW_API_MEDIA: case NATIVE_WINDOW_API_CAMERA: mCore->mConnectedApi = api; output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight, mCore->mTransformHint, static_cast<uint32_t>(mCore->mQueue.size()), mCore->mFrameCounter + 1); // Set up a death notification so that we can disconnect // automatically if the remote producer dies if (listener != NULL && IInterface::asBinder(listener)->remoteBinder() != NULL) { status = IInterface::asBinder(listener)->linkToDeath( static_cast<IBinder::DeathRecipient*>(this)); } // 把 Camera3OutputStream 类的 mBufferReleasedListener 结构的 listener 暂存起来 mCore->mConnectedProducerListener = listener; break; default: } mCore->mConnectedPid = IPCThreadState::self()->getCallingPid(); mCore->mBufferHasBeenQueued = false; mCore->mDequeueBufferCannotBlock = false; }
-
listener 我直到它被保存起来了,可是什么时候使用 listener?
在 comsumer 使用完 buffer 需要 release 时,从
mActiveBuffers
中取出来一个 slot,放到mFreeBuffers
中,并调用 listener 的onReleaseBuffer
函数。
图 3 - BufferQueue’s Slots arch
status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber, const sp<Fence>& releaseFence, EGLDisplay eglDisplay, EGLSyncKHR eglFence) { sp<IProducerListener> listener; { // Autolock scope Mutex::Autolock lock(mCore->mMutex); // 如果在 release 前时候,buf 又被重新 alloc 了,那忽略这次 release 行为。 // 特别对于 shared 类型的 buf,高概率在 release 过程中出现的 queue 和 acquire 请求。这时候忽略这次的 release 行为。 if (frameNumber != mSlots[slot].mFrameNumber && !mSlots[slot].mBufferState.isShared()) { return STALE_BUFFER_SLOT; } mSlots[slot].mEglDisplay = eglDisplay; mSlots[slot].mEglFence = eglFence; mSlots[slot].mFence = releaseFence; mSlots[slot].mBufferState.release(); // After leaving shared buffer mode, the shared buffer will // still be around. Mark it as no longer shared if this // operation causes it to be free. if (!mCore->mSharedBufferMode && mSlots[slot].mBufferState.isFree()) { mSlots[slot].mBufferState.mShared = false; } // Don't put the shared buffer on the free list. if (!mSlots[slot].mBufferState.isShared()) { mCore->mActiveBuffers.erase(slot); mCore->mFreeBuffers.push_back(slot); // 把待释放的 buf 放到 freeBuffers 中 } // 此时正好取出暂存的回调函数 . listener = mCore->mConnectedProducerListener;// 取出 connect 时候准备的 Listener mCore->mDequeueCondition.broadcast(); } // Autolock scope // 调用回调 . if (listener != NULL) { listener->onBufferReleased();// 执行 Camera3BufferManage 的 onBufferReleased } return NO_ERROR; }
-
神秘的 listener 究竟是什么 ?
Listener 是
Camera3OutputStream::BufferReleasedListener
类,Camera3OutputStream::BufferReleasedListener
类是Camera3OutputStream
类的子类,而Camera3OutputStream::BufferReleasedListener
类的成员函数onReleaseBuffer
即为在 release 时候调用的回调函数。
void Camera3OutputStream::BufferReleasedListener::onBufferReleased() { sp<Camera3OutputStream> stream = mParent.promote(); Mutex::Autolock l(stream->mLock); status_t res = stream->mBufferManager->onBufferReleased( stream->getId(), stream->getStreamSetId()); }
1.5 Camera3BufferManager::returnBufferForStream
主要是涉及几个 count map 的管理,将streamId
和GraphicBuffer
组合成BufferEntry
,放到freeBuffers
中,前面我们也遇到过BufferEntry
结构。
status_t Camera3BufferManager::returnBufferForStream(int streamId, int streamSetId, const sp<GraphicBuffer>& buffer, int fenceFd) { Mutex::Autolock l(mLock); if (mGrallocVersion < HARDWARE_DEVICE_API_VERSION(1,0)) { // 取出对应 StreamSetId 的 StreamSet. StreamSet& streamSet = mStreamSetMap.editValueFor(streamSetId); if (buffer != 0) { BufferEntry entry; entry.add(streamId, GraphicBufferEntry(buffer, fenceFd)); status_t res = addBufferToBufferListLocked(streamSet.freeBuffers, entry);// 把返回的 buf 再次加回 freeBuffers 列表 . } // Update the handed out and attached buffer count for this buffer. // 把返回的 buf 放到 freeBuffers 中 .handout 描述的给 client buffer 的个数应该减一 . BufferCountMap& handOutBufferCounts = streamSet.handoutBufferCountMap; size_t& bufferCount = handOutBufferCounts.editValueFor(streamId); bufferCount--; // 由于返回的 buf, 我们认为是 free, 给到 freebuffers. 这个 freeBuffers 属于全体 stream 组成的 streamSet 的 . // 而 attachedBufferCount 描述的是给 stream 的 count 的个数 , 它包含 handout 和从 client 上 release 的 buffer 的集合 . 此 // 处也会减一 , 以作为 handout 的相应 . size_t& attachedBufferCount = streamSet.attachedBufferCountMap.editValueFor(streamId); attachedBufferCount--; } else { // TODO: implement this. return BAD_VALUE; } return OK; } status_t Camera3BufferManager::addBufferToBufferListLocked(BufferList& bufList, const BufferEntry& buffer) { bufList.push_back(buffer); return OK; }
二、 GUI/BufferQueueProducer 之 buf 产生者
第一个问题,何为 Producer?
我们先看一下BufferQueueProducer
类的关键成员函数:
requestBuffer 返回 slot 对应的 GraphicBuffer. 正常情况下 ,dequeuBuffer 会返回一个 slot 值 , 然后我们按照这个 slot 值取请求 GraphicBuffer. 如果之前的 dequeueBuffer 返回 flags 揭示之前的 buffer 不再合法 , 一定要再次调用 requestBuffer 函数 . dequeueBuffer 为 BufferQueueProducer 拿到下一个 buffer 的 index。从 BufferQueue 中。 outFense 是说,只有在 NO_FENSE 时候,才能立即写。其他的情况会返回-EBUSY. width,height,format,musage 是描述 buffer 的属性。 返回值如果是 OK 的话,紧接着调用 requestBuffer 拿到对应这个槽的 buffer。 queueBuffer 会把填充好的 buffer 放到 BufferQueue 中。 需要一个 timeStamp 做输入,描述 buffer,单位 ns。 输出是含有 buffer 信息的结构,包括已经 queued 的 buffer 的个数。 Query 寻味 native window 的属性。输入 window.h 定义的,比如 NATIVE_WINDOW_FORMAT Connect 尝试链接 producer api 到 bufferQueue 中。填充 app 的 listener。
一般的步骤:
先调用 dequeueBuffer 拿到一个 slot;
然后依据这个 slot,调用 requestBuffer,拿到 buffer。
重要数据结构:mSlot,它是 BufferSlot 结构。
namespace android { class BufferQueueCore; namespace BufferQueueDefs { enum { NUM_BUFFER_SLOTS = 64 }; typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS]; } // namespace BufferQueueDefs } // namespace android struct BufferSlot { BufferSlot() : mGraphicBuffer(nullptr), mEglDisplay(EGL_NO_DISPLAY), mBufferState(), mRequestBufferCalled(false), mFrameNumber(0), mEglFence(EGL_NO_SYNC_KHR), mFence(Fence::NO_FENCE), mAcquireCalled(false), mNeedsReallocation(false) { } // 指向分配的 GraphicBuffer sp<GraphicBuffer> mGraphicBuffer; // 用来构建 EGLSyncKHR 对象组 EGLDisplay mEglDisplay; // 当前这个 slot 的状态 BufferState mBufferState; // debug 用 , 描述了 producer 曾发起过 requestBuffer 请求 . bool mRequestBufferCalled; // mFrameNumber 该 slot 上装的 frame 的 framenumber, 可以利用它以 LRU 顺序 dequeue buffer uint64_t mFrameNumber; // EGL 同步对象 . 必须保证在 slot 槽 dequeue 之前触发 (signal). EGLSyncKHR mEglFence; // 同步信号 . 揭示上任主人的读写操作是否完成 . 如果是 free 态 , 说明上个 consumer 的读完成了 , 或者上个 Producer 的写完成了 . sp<Fence> mFence; // buffer 是否被 consumer 请求过 . bool mAcquireCalled; // 是否需要在不通知 producer 的情况下重新分配 buffer, 可能是由于现有的 buffer 的尺寸 , 用途不合适 . bool mNeedsReallocation; };
mCore 是什么?
如图 4 所示。所谓的 mCore,其实是BufferQueueCore
结构 。它主要是几个数据成员:mFreeSlots
、 mFreeBuffers
以及mActiveBuffers
。他们都是int
型 数组,数据成员是 slot 的 id。
所谓的 slot,是SlotType
数组,共 64 个SlotType
的结构 。每个SlotType
的结构都含有mGraphicBuffer
。
这几个结构的区别是:
mFreeSlots
描述的一组 slot id,他们是 free 并没有关联的 buffer。mFreeBuffers
描述的一组 slot id,他们是 free 但有关联的 buffer。mActiveBuffers
描述一组有 non-free 态 buffer 的 slot id。mSharedBufferSlot
描述一个 shared 的GraphicBuffer
,所谓共享是在 “物理内存” 层面的共享。
图-4-BufferQueueCore 中 slots 和 slotsType 结构
class BufferQueueCore : public virtual RefBase { // mFreeSlots contains all of the slots which are FREE and do not currently // have a buffer attached. // 包含所有 free 态的并且尚未和 buffer attached 的 slot id std::set<int> mFreeSlots; // mFreeBuffers contains all of the slots which are FREE and currently have // a buffer attached. // 包含所有 free 态的并且已有 buffer attached 上的 slot id std::list<int> mFreeBuffers; // mUnusedSlots contains all slots that are currently unused. They should be // free and not have a buffer attached. // 尚未使用的 slot id , 他们是 free 并且没有 buffer attached 上的 . std::list<int> mUnusedSlots; // mActiveBuffers contains all slots which have a non-FREE buffer attached. // 含有所有非 free 态 buffer attached 的 slot id std::set<int> mActiveBuffers; // mDequeueCondition is a condition variable used for dequeueBuffer in // synchronous mode. mutable Condition mDequeueCondition; // 是否该 slot 使能了共享模式 . bool mSharedBufferMode; // When shared buffer mode is enabled, this indicates whether the consumer // should acquire buffers even if BufferQueue doesn't indicate that they are // available. bool mAutoRefresh; // When shared buffer mode is enabled, this tracks which slot contains the // shared buffer. // 共享 buf 模式使能时 , 揭示究竟是哪一个 BufferSlot 关联的是共享 buf, 它也是个 slot 的 id. int mSharedBufferSlot; }
2.1 BufferQueueProducer::dequeueBuffer
从mFreeSlots
、mFreeBuffers
中找到合适的 slot 的 id。
status_t BufferQueueProducer::dequeueBuffer(int *outSlot,sp<android::Fence> *outFence, uint32_t width, uint32_t height,PixelFormat format, uint32_t usage) { // 初始化 status_t returnFlags = NO_ERROR; EGLDisplay eglDisplay = EGL_NO_DISPLAY; EGLSyncKHR eglFence = EGL_NO_SYNC_KHR; bool attachedByConsumer = false; { // Autolock scope Mutex::Autolock lock(mCore->mMutex); mCore->waitWhileAllocatingLocked(); int found = BufferItem::INVALID_BUFFER_SLOT; // 这里会阻塞等待可用的 slot. while (found == BufferItem::INVALID_BUFFER_SLOT) { status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue, &found); const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer); // 如果 BufferQueueCore 不允许 allocation, 我们就把这个 buffer 再次添加回 mFreeSlots 链表中 . 然后继续等待 freeSlot 的 buffer. 本来 mFreeSlots 里描述的 slot 就是没有关联 buffer 的 , 此处再不让 allocation, 岂不是拿不到我们的 buffer 了 . 所以此处再把 buffer 放回 mFreeSlots 中 . if (!mCore->mAllowAllocation) { if (buffer->needsReallocation(width, height, format, usage)) { if (mCore->mSharedBufferSlot == found) { BQ_LOGE("dequeueBuffer: cannot re-allocate a shared" "buffer"); return BAD_VALUE; } mCore->mFreeSlots.insert(found); mCore->clearBufferSlotLocked(found); found = BufferItem::INVALID_BUFFER_SLOT; continue; } } } // shared buffer 不能重新分配 const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer); if (mCore->mSharedBufferSlot == found && buffer->needsReallocation(width, height, format, usage)) { BQ_LOGE("dequeueBuffer: cannot re-allocate a shared" "buffer"); return BAD_VALUE; } if (mCore->mSharedBufferSlot != found) { mCore->mActiveBuffers.insert(found);// 添加到 mActiveBuffers 中 } *outSlot = found; ATRACE_BUFFER_INDEX(found); attachedByConsumer = mSlots[found].mNeedsReallocation; mSlots[found].mNeedsReallocation = false; mSlots[found].mBufferState.dequeue(); if ((buffer == NULL) || buffer->needsReallocation(width, height, format, usage))// 由于尺寸 , 用途等不满足 , 需要重新分配 . { mSlots[found].mAcquireCalled = false; mSlots[found].mGraphicBuffer = NULL; mSlots[found].mRequestBufferCalled = false; mSlots[found].mEglDisplay = EGL_NO_DISPLAY; mSlots[found].mEglFence = EGL_NO_SYNC_KHR; mSlots[found].mFence = Fence::NO_FENCE; mCore->mBufferAge = 0; mCore->mIsAllocating = true; returnFlags |= BUFFER_NEEDS_REALLOCATION; } else { // We add 1 because that will be the frame number when this buffer // is queued mCore->mBufferAge = mCore->mFrameCounter + 1 - mSlots[found].mFrameNumber; } eglDisplay = mSlots[found].mEglDisplay; eglFence = mSlots[found].mEglFence; } // Autolock scope if (returnFlags & BUFFER_NEEDS_REALLOCATION) {// 如果需要分配 . status_t error; BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot); sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer( width, height, format, usage, {mConsumerName.string(), mConsumerName.size()}, &error)); { // Autolock scope Mutex::Autolock lock(mCore->mMutex); if (graphicBuffer != NULL && !mCore->mIsAbandoned) { graphicBuffer->setGenerationNumber(mCore->mGenerationNumber); mSlots[*outSlot].mGraphicBuffer = graphicBuffer; } } // Autolock scope } return returnFlags; }
在上面的第一步就是要等到 free 的 slot。
status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller, int* found) const { auto callerString = (caller == FreeSlotCaller::Dequeue) ?"dequeueBuffer" : "attachBuffer";// 我们这里是 dequeueBuffer. bool tryAgain = true; while (tryAgain) { int dequeuedCount = 0; int acquiredCount = 0; for (int s : mCore->mActiveBuffers) { if (mSlots[s].mBufferState.isDequeued()) { ++dequeuedCount; } if (mSlots[s].mBufferState.isAcquired()) { ++acquiredCount; } } *found = BufferQueueCore::INVALID_BUFFER_SLOT; // If we disconnect and reconnect quickly, we can be in a state where // our slots are empty but we have many buffers in the queue. This can // cause us to run out of memory if we outrun the consumer. Wait here if // it looks like we have too many buffers queued up. const int maxBufferCount = mCore->getMaxBufferCountLocked(); bool tooManyBuffers = mCore->mQueue.size() > static_cast<size_t>(maxBufferCount); if (tooManyBuffers) { BQ_LOGV("%s: queue size is %zu, waiting", callerString, mCore->mQueue.size()); } else { // 如果处于共享 buf 模式 , 返回已有的共享 buf. 所谓共享 buf, 就是一个 StreamSet 里边多个 Stream 共享一个 buf. if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot != BufferQueueCore::INVALID_BUFFER_SLOT) { // 一 , 如果使能 shared 模式 . 并且有 shared buffer slot, 优先选择使用 shared buffer *found = mCore->mSharedBufferSlot; } else { if (caller == FreeSlotCaller::Dequeue) { // 二 , 从 FreeeBuffer 和 FreeSlot 中依次拿到 slot.mFreeBuffers 是含有关联 buf 的空闲 slot.mFreeSlot 是尚未关联 buf 的 slot. int slot = getFreeBufferLocked(); if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) { *found = slot; } else if (mCore->mAllowAllocation) { *found = getFreeSlotLocked(); } } else { // If we're calling this from attach, prefer free slots int slot = getFreeSlotLocked(); if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) { *found = slot; } else { *found = getFreeBufferLocked(); } } } } } // while (tryAgain) return NO_ERROR; }
进一步的 buffer 的 “槽” 来源于mFreeBuffers
和mFreeSlots
。
int BufferQueueProducer::getFreeBufferLocked() const { if (mCore->mFreeBuffers.empty()) { return BufferQueueCore::INVALID_BUFFER_SLOT; } int slot = mCore->mFreeBuffers.front(); mCore->mFreeBuffers.pop_front(); return slot; } int BufferQueueProducer::getFreeSlotLocked() const { if (mCore->mFreeSlots.empty()) { return BufferQueueCore::INVALID_BUFFER_SLOT; } int slot = *(mCore->mFreeSlots.begin()); mCore->mFreeSlots.erase(slot); return slot; }
2.2 BufferQueueProducer::requestBuffer
返回刚才 dequeue 的 slot 对应的mGraphicBuffer
。
status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) { Mutex::Autolock lock(mCore->mMutex); mSlots[slot].mRequestBufferCalled = true; *buf = mSlots[slot].mGraphicBuffer; return NO_ERROR; }
2.3 典型用法
图-5- camera3OutputStream::getBufferLocked 典型用法
扫描下方二维码,阅读完整原文