1. BufferQueue 是什么
-
本质 :一个环形缓冲队列 (slots + fences),承接"生产者 Producer 写帧 → 消费者 Consumer 取帧"的流。
-
关键目标 :零拷贝/少拷贝传递图像帧,降低延迟与功耗。
-
典型 Producer:MediaCodec(硬解)、MediaPlayer/ExoPlayer、OpenGL(EGL)、Canvas CPU 绘制。
-
典型 Consumer:
-
SurfaceView:SurfaceFlinger(系统合成器,独立图层)
-
TextureView:SurfaceTexture(把帧变为 GPU 纹理参与应用内合成)
-
简图:
scss
Producer(解码/GL/Canvas) ──> Surface(IGraphicBufferProducer)
│ │
│ [BufferQueueCore]
▼ │
queueBuffer() acquireBuffer()
▼
-----fence----> Consumer(SF/SurfaceTexture)
2. 内部构成(你需要知道的名词)
- Slots:固定数量的缓冲槽位(Android 源码里 NUM_BUFFER_SLOTS=64 的上限,实际使用远小于此,一般 2~4)。
- GraphicBuffer:每个 slot 上的图像缓冲(显存/共享内存)。
- Fence(栅栏) :同步原语,标记"GPU/硬件写入/读取完成"。Producer queue 时附release fence ;Consumer acquire 时拿到acquire fence。
- IGraphicBufferProducer / Consumer:BufferQueue 的 Binder 两端接口;Java Surface → native Surface.cpp → IGBP。
- BufferQueueProducer/Consumer/Core:C++ 里三分体实现(队列核心 + 生产/消费端包装)。
3. 状态机与典型时序
3.1 Producer 侧(写帧)
-
dequeueBuffer() :向队列申请一个空闲 slot
- 若没有可用 slot,会阻塞/失败。
-
请求/复用缓冲 :如果大小/格式/usage 不匹配,触发重新分配(避免每帧分配!)。
-
渲染写入:
- CPU:ANativeWindow_lock() → 绘制 → unlockAndPost()
- GL:向 egl window surface 画 → eglSwapBuffers()
- MediaCodec:解码器把帧直接写入绑定的 Surface
-
queueBuffer() :把填好的帧入队,并附上 release fence、时间戳、裁剪、变换等元数据。
- 若队列满,可能阻塞或丢帧(async 模式)。
3.2 Consumer 侧(读帧)
-
acquireBuffer() :取队首可用帧;等待 acquire fence 完成再读(保证数据有效)。
-
消费:
- SurfaceView:SurfaceFlinger 在下一次合成周期把这帧与其它图层进行硬件合成/混合。
- TextureView:SurfaceTexture 把帧更新到 GL 纹理,在应用进程的 ViewRoot 绘制时采样参与合成。
-
releaseBuffer() :用完把 slot 归还队列(可再次被 Producer 复用)。
关键点 :双/三缓冲常见。三缓冲可降低生产/消费不同步导致的阻塞,但会增加一帧延迟。
4. 容量/阻塞规则(为什么会"卡住/掉帧")
-
队列允许 Producer 同时"借出"的 buffer 数量受 maxDequeuedBufferCount 限制(默认根据 Consumer 能同时持有的数量决定)。
-
当 Producer 已借出过多 或 Consumer 持有未释放 ,再 dequeueBuffer() 可能阻塞。
-
当 Producer queueBuffer() 太快(消费者来不及合成/采样):
- 同步模式:可能在 queueBuffer() 阻塞等待。
- 异步模式 (setAsyncMode(true)):不阻塞,丢弃旧帧保留最新帧(视频预览常用)。
-
尺寸/格式改变 会导致老缓冲失配并重分配,频繁 reallocate = 抖动/卡顿。
经验:视频/相机建议 3 缓冲 (Producer 可 deque 2 个,Consumer 同时 hold 1 个),并开启异步模式以避免阻塞主渲染。
5. SurfaceView vs TextureView:BufferQueue 的去向不同
维度 | SurfaceView 管线 | TextureView 管线 |
---|---|---|
Consumer | SurfaceFlinger(系统) | SurfaceTexture(应用) |
合成 | 独立图层,与其它窗口一起合成 | 作为纹理参与 ViewRoot 的 GPU 合成 |
延迟/功耗 | 更低 | 略高(多一次纹理采样与应用内合成) |
动画/裁剪 | 受限 | 灵活(透明/圆角/Matrix) |
相同点:两者 Producer 都是对一个 Surface 写帧;BufferQueue 机制一致,只是"另一端是谁"不同。
6. 关键参数与调优点(Producer 侧常用)
-
尺寸:ANativeWindow_setBuffersGeometry() / MediaCodec format.setInteger(KEY_WIDTH/HEIGHT, ...)
→ 只在必要时改,否则频繁重分配。
-
格式:PIXEL_FORMAT_RGBA_8888 等;视频解码一般 NV12/NV21/YUV420(由 Codec & HWC 决定)。
-
Usage:GRALLOC_USAGE_SW_READ/WRITE, HW_TEXTURE, HW_COMPOSER, PROTECTED(DRM)等;决定缓冲放在哪、能否被哪类硬件高效访问。
-
Dataspace/ColorSpace:BT.709/BT.2020/HDR;和 HWC/GL 管线匹配,否则会偏色/错 gamma。
-
Scaling/Transform/Crop:通过 queueBuffer() 的元数据携带。SurfaceView 通常用 HWC 处理,TextureView 用 setTransform()。
7. 常见问题 & 对策
-
卡住在 dequeue/queue
- 增加缓冲数(setBufferCount / setMaxDequeuedBufferCount);
- 开启 async 模式(允许丢旧帧保最新);
- 检查 Consumer 是否长期不 releaseBuffer()(例如合成阻塞、GL 卡帧)。
-
频繁重分配(alloc churn)
- 避免每帧改分辨率/格式;先 detach 再统一 setBuffersGeometry,或在场景切换时一次性变更。
-
黑屏/花屏/绿屏
- TextureView 某些机型/解码器组合不稳 → 验证改用 SurfaceView;
- 确认 setSurface(null) 的时序,销毁前解绑定;
- 检查颜色空间/stride 对齐问题(尤其自研 GL)。
-
高延迟
- 三缓冲 + async;降低渲染分辨率;减少在应用进程的二次合成(TextureView->考虑 SurfaceView)。
-
截图/录屏差异
- SurfaceView 不参与应用内 View.draw() 截屏;系统录屏一般可见;DRM 可 setSecure(true) 禁止截屏。
8. 调试与观测
- adb shell dumpsys SurfaceFlinger --list / --displays / --latency:看图层、刷新、合成信息。
- Systrace/Perfetto:Graphics、View、Sched、Freq 轨道看 queue/dequeue、合成耗时与丢帧。
- dumpsys gfxinfo:帧时间分布、Janky 统计。
- 开启 debug.sf.* 系列系统属性(开发版 ROM)可见更详细日志(谨慎)。
9. 最小落地骨架(Player → Surface)
SurfaceView
kotlin
sv.holder.addCallback(object : SurfaceHolder.Callback {
override fun surfaceCreated(h: SurfaceHolder) {
player.setVideoSurface(h.surface) // Media3/ExoPlayer API
player.playWhenReady = true
}
override fun surfaceDestroyed(h: SurfaceHolder) {
player.setVideoSurface(null) // 先解绑再 release
}
})
TextureView
kotlin
tv.surfaceTextureListener = object : TextureView.SurfaceTextureListener {
override fun onSurfaceTextureAvailable(st: SurfaceTexture, w: Int, h: Int) {
player.setVideoSurface(Surface(st))
player.playWhenReady = true
}
override fun onSurfaceTextureDestroyed(st: SurfaceTexture) = true.also {
player.setVideoSurface(null)
}
}
若用 EGL/GL 自绘:以 holder.surface 或 Surface(st) 创建 EGL window surface,再 eglSwapBuffers()。
一句话总结
BufferQueue 是 Surface 的"帧中转站":Producer 通过 dequeue→渲染→queue 写帧;Consumer acquire→显示→release 读帧。
在 SurfaceView ,帧被 SurfaceFlinger 直接合成;在 TextureView,帧先变成纹理参与应用内合成。理解这一点,基本就能把"卡顿/黑屏/延迟"问题对症下药。