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 的管理,将streamIdGraphicBuffer组合成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 的 “槽” 来源于mFreeBuffersmFreeSlots

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 典型用法

扫描下方二维码,阅读完整原文

  • 1
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
相机API2的拍照流程与拍摄RAW照片的流程类似,也涉及到应用层(app)、框架层(framework)和硬件抽象层(HAL),下面是在这三个层面上使用相机API2拍照的流程: 1. 应用层(app):应用程序使用相机管理器(CameraManager)打开相机设备,并创建一个用于拍照的会话。 2. 框架层(framework):框架层的相机服务(CameraService)接收应用程序的请求,并与相机驱动程序(camera HAL)进行通信,以控制相机硬件。 3. 硬件抽象层(HAL):相机HAL模块接收来自框架层的请求,并通过与相机硬件的驱动程序进行通信,以控制相机硬件。 4. HAL接收请求:当应用程序发起拍照请求时,请求会被传递到相机HAL模块,即app-fw-hal。 5. HAL配置相机:相机HAL模块会根据请求设置相机的参数,例如曝光时间、ISO等参数。 6. HAL拍照:相机HAL模块通过相机驱动程序控制相机硬件进行拍照操作。 7. HAL输出数据:当拍摄完成后,相机HAL模块将拍摄的JPEG数据传递给框架层。 8. 框架层输出数据:框架层的相机服务将拍摄的JPEG数据传递给应用程序,应用程序可以在该数据上进行后续的处理操作。 9. 应用层处理数据:应用程序可以将JPEG数据保存到文件系统中,或者进行其他的处理操作。 以上就是在相机API2的三个层面上拍照的流程,其中app-fw-hal作为相机HAL模块提供了应用程序、框架层和相机驱动程序之间的接口。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值