onFrameAvailable 深度分析
基于 Android 16 AOSP 源码 覆盖:注册链路 · BBQ 触发路径 · SurfaceTexture 对比 · SF 跨进程调用场景
一、接口体系总览
onFrameAvailable 在 libgui 中有两层接口,需要区分:
| 层次 | 接口 | 所在文件 | 说明 |
|---|---|---|---|
| 底层 | ConsumerListener::onFrameAvailable | IConsumerListener.h:64 | BufferQueue 内部,由 queueBuffer 触发,不是 Binder 接口 |
| 上层 | ConsumerBase::FrameAvailableListener::onFrameAvailable | ConsumerBase.h:45 | ConsumerBase 转发给外部注册者的回调 |
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 弱引用设计原因
mFrameAvailableListener 用 wp<> 而非 sp<>(ConsumerBase.h:316):
wp<FrameAvailableListener> mFrameAvailableListener;
BLASTBufferQueue 持有 mBufferItemConsumer(sp<>),若 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 行为差异
| 维度 | BLASTBufferQueue | SurfaceTexture(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 是否参与触发 |
|---|---|---|---|
| 普通窗口 BBQ | App RenderThread | 本地调用 | 否 |
| Camera/MediaCodec → SurfaceTexture | App Binder 线程 | Binder 跨进程 | 否 |
| VirtualDisplay | App 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.h | L37-110 | ConsumerListener 接口定义;L61 注释说明 GraphicBuffer pointer 仅 SF 消费者时非 null |
ConsumerBase.h | L43-47 | FrameAvailableListener 接口定义 |
ConsumerBase.h | L316 | mFrameAvailableListener 弱引用声明 |
ConsumerBase.cpp | L64-115 | 构造函数:创建 ProxyConsumerListener,注册到 BufferQueue |
ConsumerBase.cpp | L231-243 | onFrameAvailable() 转发实现 |
ConsumerBase.cpp | L350-354 | setFrameAvailableListener() 实现 |
BufferQueue.cpp | L62-68 | ProxyConsumerListener::onFrameAvailable() 转发 |
BufferQueueConsumer.cpp | L571-593 | connect() 写入 mConsumerListener |
BufferQueueProducer.cpp | L1134-1251 | queueBuffer():锁内入队、锁外回调、callbackTicket 顺序机制 |
BLASTBufferQueue.h | L97 | BLASTBufferQueue : public ConsumerBase::FrameAvailableListener |
BLASTBufferQueue.cpp | L220 | 构造函数:setFrameAvailableListener(this) |
BLASTBufferQueue.cpp | L553-728 | acquireNextBufferLocked():acquire + 组装 + apply Transaction |
BLASTBufferQueue.cpp | L751-842 | onFrameAvailable():影子队列 + 普通/Sync 路径分支 |
GLConsumer.cpp | L285-332 | updateTexImage():acquire + 绑定 GL 纹理 |
android_graphics_SurfaceTexture.cpp | L156-163 | JNI onFrameAvailable:JNI 回调 Java 静态方法 |
SurfaceTexture.java | L103-105 | OnFrameAvailableListener Java 接口定义 |
SurfaceTexture.java | L228-244 | setOnFrameAvailableListener():创建 Handler 转发 |
SurfaceTexture.java | L473-480 | postEventFromNative():native 回调入口 |
SurfaceFlinger.cpp | L3999-4007 | VirtualDisplay 建立:传入 state.surface(App 的 Binder proxy) |
VirtualDisplaySurface.cpp | L79 | mSource[SOURCE_SINK] = sink(App 的 IGraphicBufferProducer) |
VirtualDisplaySurface.cpp | L263-270 | SF 合成后调 mSource[SOURCE_SINK]->queueBuffer()(跨进程) |