onFrameAvailable 深度分析

6 阅读4分钟

onFrameAvailable 深度分析

基于 Android 16 AOSP 源码 覆盖:注册链路 · BBQ 触发路径 · SurfaceTexture 对比 · SF 跨进程调用场景


一、接口体系总览

onFrameAvailable 在 libgui 中有两层接口,需要区分:

层次接口所在文件说明
底层ConsumerListener::onFrameAvailableIConsumerListener.h:64BufferQueue 内部,由 queueBuffer 触发,不是 Binder 接口
上层ConsumerBase::FrameAvailableListener::onFrameAvailableConsumerBase.h:45ConsumerBase 转发给外部注册者的回调
ConsumerListener(非 Binder,纯虚)
    └── IConsumerListener(空壳继承,仍非 Binder)
            └── ConsumerBase(实现转发逻辑)
                    └── FrameAvailableListener(外部注册接口)
                             ├── BLASTBufferQueue(实现)
                             └── JNISurfaceTextureContextFrameAvailableListener(实现)

关键事实IConsumerListener 不含 DECLARE_META_INTERFACE,无 BnConsumerListener/BpConsumerListener无法跨进程传递BufferQueueCore::mConsumerListener 始终是进程内的本地对象。


二、类继承与角色关系

flowchart TB
    subgraph interfaces["接口层"]
        CL["ConsumerListener\nIConsumerListener.h\n纯虚,非 Binder"]
        FAL["ConsumerBase::FrameAvailableListener\nConsumerBase.h\n纯虚,进程内回调"]
    end

    subgraph consumers["消费者实现层"]
        CB["ConsumerBase\n实现 IConsumerListener\n持有 wp-FrameAvailableListener"]
        BIC["BufferItemConsumer\n继承 ConsumerBase"]
        BBIC["BLASTBufferItemConsumer\n继承 BufferItemConsumer"]
        GLC["GLConsumer\nSurfaceTexture native\n继承 ConsumerBase"]
    end

    subgraph listeners["FrameAvailableListener 实现层"]
        BBQ["BLASTBufferQueue\n实现 FrameAvailableListener\n收到回调主动 acquire + Transaction"]
        JNI["JNISurfaceTextureContext\nFrameAvailableListener\n收到回调通过 JNI 通知 Java 层"]
    end

    CL --> CB
    FAL --> BBQ
    FAL --> JNI
    CB --> BIC --> BBIC
    CB --> GLC

    BBIC -. "setFrameAvailableListener(bbq)" .-> BBQ
    GLC -. "setFrameAvailableListener(jni)" .-> JNI

三、注册链路(三步)

3.1 总体流程

flowchart TB
    subgraph step1["第一步:ConsumerBase 向 BufferQueue 注册"]
        CB1["ConsumerBase 构造函数\nConsumerBase.cpp:112"]
        PROXY["创建 ProxyConsumerListener\n包装 wp-ConsumerBase"]
        BQC["BufferQueueConsumer::connect()\nBufferQueueConsumer.cpp:590\nmCore-mConsumerListener = proxy"]
    end

    subgraph step2["第二步:BBQ 向 ConsumerBase 注册自己"]
        BBQ2["BLASTBufferQueue 构造函数\nBLASTBufferQueue.cpp:220"]
        SFL["mBufferItemConsumer\n->setFrameAvailableListener(this)\nmFrameAvailableListener = wp-BBQ"]
    end

    subgraph step3["结果:两级 listener 就位"]
        CORE["BufferQueueCore::mConsumerListener\n= ProxyConsumerListener"]
        CB2["ConsumerBase::mFrameAvailableListener\n= wp-BLASTBufferQueue"]
    end

    CB1 --> PROXY --> BQC --> CORE
    BBQ2 --> SFL --> CB2

3.2 弱引用设计原因

mFrameAvailableListenerwp<> 而非 sp<>ConsumerBase.h:316):

wp<FrameAvailableListener> mFrameAvailableListener;

BLASTBufferQueue 持有 mBufferItemConsumersp<>),若 ConsumerBase 反向用 sp<> 持有 BBQ,则形成循环引用导致泄漏。弱引用打破环:BBQ 销毁时 promote() 返回 nullptr,回调自然跳过。


四、BBQ 路径:触发链路与两条分支

4.1 从 queueBuffer 到 onFrameAvailable

App RenderThread
eglSwapBuffers()
Surface::queueBuffer()
BufferQueueProducer::queueBuffer()
BufferQueueProducer.cpp:1134
持 mMutex 锁
buffer 入 mCore-mQueue
取出 mConsumerListener
释放大锁
等待 callbackTicket 轮次
保证多帧按帧号顺序回调
ProxyConsumerListener
::onFrameAvailable()
BufferQueue.cpp:62
ConsumerBase::onFrameAvailable()
ConsumerBase.cpp:231
promote wp to sp
BLASTBufferQueue::onFrameAvailable()
BLASTBufferQueue.cpp:751
flowchart TB
    subgraph RT["App RenderThread"]
        EGL["eglSwapBuffers()"]
        SQ["Surface::queueBuffer()"]
        BQP["BufferQueueProducer::queueBuffer()\nBufferQueueProducer.cpp:1134"]
        LOCK["持 mMutex 锁\nbuffer 入 mCore-mQueue\n取出 mConsumerListener"]
        UNLOCK["释放大锁"]
        TICKET["等待 callbackTicket 轮次\n保证多帧按帧号顺序回调"]
        PROXY2["ProxyConsumerListener\n::onFrameAvailable()\nBufferQueue.cpp:62"]
        CBF["ConsumerBase::onFrameAvailable()\nConsumerBase.cpp:231\npromote wp to sp"]
        BBQ3["BLASTBufferQueue::onFrameAvailable()\nBLASTBufferQueue.cpp:751"]
    end

    EGL --> SQ --> BQP --> LOCK --> UNLOCK --> TICKET --> PROXY2 --> CBF --> BBQ3

:回调在 BufferQueue 大锁释放后才执行,避免死锁(IConsumerListener.h:35

4.2 onFrameAvailable 内部两条路径

flowchart TB
    OFA["BLASTBufferQueue::onFrameAvailable()\nBLASTBufferQueue.cpp:751"]
    CNT["mNumFrameAvailable++"]
    CHK{"mTransactionReadyCallback\n是否为空"}

    subgraph normal["普通路径(绝大多数情况)"]
        N1["acquireNextBufferLocked(nullopt)\n立即 acquire"]
        N2["创建 localTransaction"]
        N3["组装 Transaction\nsetBuffer / setTransform\nsetFrameTimelineInfo ..."]
        N4["Transaction::apply oneWay\n单向 Binder 发往 SF"]
    end

    subgraph sync["Sync 路径(窗口动画/首帧/截图)"]
        S1["mSyncedFrameNumbers\n.emplace(frameNumber)"]
        S2["acquireNextBufferLocked\n填入外部共享 mSyncTransaction"]
        S3["addTransactionCommitted\nCallback()"]
        S4["等待外部调用\nmTransactionReadyCallback\n决定提交时机"]
    end

    OFA --> CNT --> CHK
    CHK -- "== nullptr 普通" --> N1 --> N2 --> N3 --> N4
    CHK -- "!= nullptr Sync" --> S1 --> S2 --> S3 --> S4

4.3 acquireNextBufferLocked 内部细节

flowchart TB
    ANB["acquireNextBufferLocked()\nBLASTBufferQueue.cpp:553"]
    CHK1{"无可用帧\n或超过最大 acquire 数"}
    NO_BUF["return NO_BUFFER_AVAILABLE\n等待 releaseBuffer 唤醒"]
    ACQ["BufferItemConsumer::acquireBuffer()\nQUEUED-ACQUIRED\nmNumFrameAvailable--\nmNumAcquired++"]
    PICK{"transaction 参数\n是否有值"}
    LOCAL["使用 localTransaction\napplyTransaction = true"]
    SHARED["使用外部 mSyncTransaction\napplyTransaction = false"]
    FILL["setBuffer / setDataspace\nsetSurfaceDamageRegion / setTransform\nsetFrameTimelineInfo\naddTransactionCompletedCallback\nmergePendingTransactions"]
    APPLY{"applyTransaction?"}
    DO_APPLY["setApplyToken\napply false oneWay\n单向 Binder 发往 SF"]
    SKIP["不 apply\n由外部 mTransactionReadyCallback 决定"]

    ANB --> CHK1
    CHK1 -- "是" --> NO_BUF
    CHK1 -- "否" --> ACQ --> PICK
    PICK -- "无值 普通路径" --> LOCAL --> FILL
    PICK -- "有值 Sync路径" --> SHARED --> FILL
    FILL --> APPLY
    APPLY -- "true" --> DO_APPLY
    APPLY -- "false" --> SKIP

4.4 影子队列背压机制

BLASTBufferQueue 维护两个计数器防止 Buffer Stuffing:

计数器含义
mNumFrameAvailable已 queueBuffer 但未 acquireBuffer 的帧数
mNumAcquired已 acquireBuffer 但 SF 未 release 的帧数
背压条件(BLASTBufferQueue.cpp:564):
  mNumAcquired >= (mMaxAcquiredBuffers + 2)
    → acquireNextBufferLocked 返回 NO_BUFFER_AVAILABLE
    → onFrameAvailable 不 acquire,等 releaseBuffer 后 mCallbackCV.notify_all() 唤醒

mMaxAcquiredBuffers 由 SF 决定(通常 = 1),即最多允许 2 帧同时在飞行


五、SurfaceTexture 路径对比

5.1 行为差异

维度BLASTBufferQueueSurfaceTexture(GLConsumer)
回调后动作立即 acquire buffer + 推 Transaction 给 SF只发 Handler 消息通知 Java 层
acquire 时机在 onFrameAvailable 内同步 acquire由调用方调 updateTexImage() 时 acquire
Buffer 去向打包进 Transaction,SF 合成显示绑定到 GL_TEXTURE_EXTERNAL_OES,App GL 渲染用
是否通知 SF是(Transaction Binder)
模式主动推(Push)通知拉(Notify-Pull)

5.2 SurfaceTexture 回调路径

flowchart TB
    subgraph prod["生产者线程(可能是 Binder 线程)"]
        QBP["BufferQueueProducer::queueBuffer()"]
        PROXY3["ProxyConsumerListener\n::onFrameAvailable()"]
        CBF2["ConsumerBase::onFrameAvailable()"]
        JNI2["JNISurfaceTextureContext\nFrameAvailableListener\nonFrameAvailable()\nandroid_graphics_SurfaceTexture.cpp:190"]
        JCALL["JNIEnv::CallStaticVoidMethod()\n调用 Java 静态方法"]
        PEN["SurfaceTexture\n.postEventFromNative()\nSurfaceTexture.java:473"]
        HANDLER["mOnFrameAvailableHandler\n.sendEmptyMessage(0)\n投递到目标 Looper 线程"]
    end

    subgraph target["目标线程(Handler 指定的 Looper)"]
        HMSG["Handler.handleMessage()"]
        JCB["listener.onFrameAvailable\n用户注册的 Java 回调"]
        UTI["surfaceTexture.updateTexImage()\n用户在回调里手动调用"]
        GACQ["GLConsumer::acquireBufferLocked()\nQUEUED-ACQUIRED"]
        BIND["glBindTexture\nGL_TEXTURE_EXTERNAL_OES\n绑定到 OpenGL ES 纹理"]
    end

    QBP --> PROXY3 --> CBF2 --> JNI2 --> JCALL --> PEN --> HANDLER
    HANDLER --> HMSG --> JCB --> UTI --> GACQ --> BIND

注意:Binder 线程没有绑定 GL Context,不能在回调中直接调 updateTexImage(),必须通过 Handler 投递到正确线程(SurfaceTexture.java:77-79)。


六、SF 进程是否会触发 App 的 onFrameAvailable

6.1 普通路径:SF 不参与

IConsumerListener 不是 Binder 接口IConsumerListener.h:110):

class IConsumerListener : public ConsumerListener {};  // 无 Binder 继承,无法跨进程

BBQ 场景的 BufferQueueCore 在 App 进程内,queueBuffer() 由 App 的 RenderThread 本地调用,SF 从不直接触发 onFrameAvailable

6.2 VirtualDisplay 路径:SF 间接触发

SF 以生产者身份 通过 Binder 调用 queueBuffer(),进而在 App 进程内触发 onFrameAvailable

VirtualDisplay 建立(SurfaceFlinger.cpp:3999-4007)

getFactory().createBufferQueue(&bqProducer, &bqConsumer,
                               /*consumerIsSurfaceFlinger=*/false);
​
auto surface = sp<VirtualDisplaySurface>::make(
        getHwComposer(), *virtualDisplayIdVariantOpt,
        state.surface,   // App 提供的 IGraphicBufferProducer(Binder proxy)
        bqProducer, bqConsumer, state.displayName);
// VirtualDisplaySurface.cpp:79:mSource[SOURCE_SINK] = sink

6.3 VirtualDisplay 完整跨进程链路

flowchart TB
    subgraph sfproc["SF 进程(合成线程)"]
        COMP["composite() 合成完成"]
        VDS["VirtualDisplaySurface\n::framePrepared()\nVirtualDisplaySurface.cpp:263"]
        BQ_CALL["mSource-SOURCE_SINK\n->queueBuffer()\nIGraphicBufferProducer Binder 调用"]
    end

    CROSS["Binder IPC 跨进程边界"]

    subgraph appproc["App 进程(Binder 线程,非 RenderThread)"]
        BQP2["BufferQueueProducer::queueBuffer()\n本地执行"]
        MCL["mConsumerListener\n->onFrameAvailable()"]
        PROXY4["ProxyConsumerListener\n::onFrameAvailable()"]
        CBF3["ConsumerBase::onFrameAvailable()"]
        FAL2["mFrameAvailableListener\n->onFrameAvailable()\n用户注册的 listener"]
        HANDLER2["Handler.sendEmptyMessage()\n转发到目标线程"]
    end

    COMP --> VDS --> BQ_CALL --> CROSS --> BQP2 --> MCL --> PROXY4 --> CBF3 --> FAL2 --> HANDLER2

6.4 三条路径对比

flowchart LR
    subgraph path1["路径1:普通窗口 BBQ"]
        direction TB
        P1A["App RenderThread\neglSwapBuffers"]
        P1B["queueBuffer 本地调用"]
        P1C["onFrameAvailable RT线程"]
        P1D["acquireBuffer + Transaction"]
        P1E["Binder 发往 SF"]
        P1A --> P1B --> P1C --> P1D --> P1E
    end

    subgraph path2["路径2:SurfaceTexture\nCamera/MediaCodec 作为生产者"]
        direction TB
        P2A["Media Server 进程\n或 Camera 进程"]
        P2B["queueBuffer Binder跨进程"]
        P2C["onFrameAvailable App Binder线程"]
        P2D["Handler投递"]
        P2E["updateTexImage 目标线程"]
        P2A --> P2B --> P2C --> P2D --> P2E
    end

    subgraph path3["路径3:VirtualDisplay\nSF 作为生产者"]
        direction TB
        P3A["SF 合成线程"]
        P3B["queueBuffer Binder跨进程"]
        P3C["onFrameAvailable App Binder线程"]
        P3D["Handler投递"]
        P3E["用户处理帧 目标线程"]
        P3A --> P3B --> P3C --> P3D --> P3E
    end
路径触发线程(App 侧)queueBuffer 类型SF 是否参与触发
普通窗口 BBQApp RenderThread本地调用
Camera/MediaCodec → SurfaceTextureApp Binder 线程Binder 跨进程
VirtualDisplayApp Binder 线程Binder 跨进程

七、完整端到端时序(普通窗口路径)

App RenderThread                       SF 主线程
──────────────────────────────────────────────────────────────────
eglSwapBuffers()
  └─ Surface::queueBuffer()
       └─ BufferQueueProducer::queueBuffer()
            └─ [大锁内] buffer 入 mCore->mQueue
            └─ [锁外] onFrameAvailable()
                 └─ BLASTBufferQueue::onFrameAvailable()
                      └─ mNumFrameAvailable++
                      └─ acquireNextBufferLocked()
                           ├─ acquireBuffer()  QUEUED→ACQUIRED
                           ├─ mNumAcquired++
                           ├─ setBuffer / setFrameTimelineInfo ...
                           └─ Transaction::apply(oneWay=true) ─────────────→
                                                               queueTransaction()
                                                               setTransactionFlags()
                                                               scheduleCommit()
​
                                               Vsync N+1 到来
                                                 └─ commit()
                                                      └─ flushTransactions()
                                                           └─ latchBufferImpl()
                                                 └─ composite()
                                                      └─ Output::present()
                                                           └─ HWC / RenderEngine
                                                                └─ presentFence
                                                                     └─ releaseBuffer()
                                                                          ACQUIRED→FREE
                                                                          mNumAcquired--

八、关键源码位置

文件位置内容
IConsumerListener.hL37-110ConsumerListener 接口定义;L61 注释说明 GraphicBuffer pointer 仅 SF 消费者时非 null
ConsumerBase.hL43-47FrameAvailableListener 接口定义
ConsumerBase.hL316mFrameAvailableListener 弱引用声明
ConsumerBase.cppL64-115构造函数:创建 ProxyConsumerListener,注册到 BufferQueue
ConsumerBase.cppL231-243onFrameAvailable() 转发实现
ConsumerBase.cppL350-354setFrameAvailableListener() 实现
BufferQueue.cppL62-68ProxyConsumerListener::onFrameAvailable() 转发
BufferQueueConsumer.cppL571-593connect() 写入 mConsumerListener
BufferQueueProducer.cppL1134-1251queueBuffer():锁内入队、锁外回调、callbackTicket 顺序机制
BLASTBufferQueue.hL97BLASTBufferQueue : public ConsumerBase::FrameAvailableListener
BLASTBufferQueue.cppL220构造函数:setFrameAvailableListener(this)
BLASTBufferQueue.cppL553-728acquireNextBufferLocked():acquire + 组装 + apply Transaction
BLASTBufferQueue.cppL751-842onFrameAvailable():影子队列 + 普通/Sync 路径分支
GLConsumer.cppL285-332updateTexImage():acquire + 绑定 GL 纹理
android_graphics_SurfaceTexture.cppL156-163JNI onFrameAvailable:JNI 回调 Java 静态方法
SurfaceTexture.javaL103-105OnFrameAvailableListener Java 接口定义
SurfaceTexture.javaL228-244setOnFrameAvailableListener():创建 Handler 转发
SurfaceTexture.javaL473-480postEventFromNative():native 回调入口
SurfaceFlinger.cppL3999-4007VirtualDisplay 建立:传入 state.surface(App 的 Binder proxy)
VirtualDisplaySurface.cppL79mSource[SOURCE_SINK] = sink(App 的 IGraphicBufferProducer)
VirtualDisplaySurface.cppL263-270SF 合成后调 mSource[SOURCE_SINK]->queueBuffer()(跨进程)