14、Preview流程深度解析:Preview启动流程、SurfaceFlinger合成、显示同步、帧率控制、低延迟Preview。

各位同学,今天我们来啃一块硬骨头——Camera Preview的完整流程。说实话,这个知识点是Android相机系统里最绕、也最容易出问题的地方。我当年刚接手相机模块时,就被Preview的黑屏问题折磨了整整两周。后来把整个链路摸透了,才发现很多坑其实是可以提前避开的。

咱们今天不讲虚的,直接深入到代码和机制层面。我会把Preview从App调用到最终显示在屏幕上的每一步都拆开来讲。你准备好了吗?

14.1 Preview启动流程:从App到HAL的完整链路

我们先看一个最简单的场景:你打开相机App,屏幕亮了,看到取景画面。这背后发生了什么?

我习惯把Preview启动分成三个阶段:

  1. App层请求:Camera2 API的createCaptureSession + setRepeatingRequest
  2. Framework层调度:CameraService创建Stream,配置Surface
  3. HAL层处理:Camera HAL打开Sensor,开始送帧

嗯,这里要注意一个关键点——Surface的创建时机。很多开发者以为只要拿到SurfaceTexture就能直接用了,其实不然。

核心要点:Preview的Surface必须等到onConfigured回调之后才能真正使用。我曾经见过有人在这个回调之前就尝试提交Request,结果导致Stream配置失败,相机直接报错。

来看一段典型的App端代码:

// 创建Session
cameraDevice.createCaptureSession(
    Arrays.asList(surface),
    new CameraCaptureSession.StateCallback() {
        @Override
        public void onConfigured(CameraCaptureSession session) {
            // 这里才能开始Preview
            session.setRepeatingRequest(request, callback, handler);
        }
    },
    handler
);

为什么一定要等onConfigured?说白了,Framework需要时间去做三件事:

  • 分配GraphicBuffer(这块内存可不小,1080p的YUV420一帧就要3MB多)
  • 建立BufferQueue连接(SurfaceFlinger那边要注册监听)
  • 通知HAL配置Stream(HAL要初始化ISP管线)

这三步缺一不可。我调试过一个案子,就是因为在onConfigured之前调了setRepeatingRequest,结果HAL那边Stream还没准备好,Buffer一直dequeue失败,Preview卡在了第一帧。

14.2 SurfaceFlinger合成:Preview帧的显示之旅

好,现在HAL开始送帧了。这些帧是怎么跑到屏幕上的?

这里有个大家容易忽略的角色——SurfaceFlinger。它负责把多个Layer合成到一起,然后交给Display。Preview帧只是其中一个Layer。

我画了一张流程图,帮你理清这个关系:

Preview帧合成与显示流程 Camera HAL BufferQueue SurfaceFlinger Display dequeueBuffer queueBuffer VSync合成 关键说明: • Camera HAL将帧数据写入GraphicBuffer,通过dequeueBuffer从BufferQueue获取空闲Buffer • SurfaceFlinger在VSync信号到来时,将所有Layer合成并送显 • 整个链路中,BufferQueue是核心的异步缓冲机制,解耦了生产者和消费者

你看,Camera HAL是生产者,SurfaceFlinger是消费者。中间通过BufferQueue做缓冲。这个设计的好处是——生产者和消费者可以跑在不同的频率上。

举个例子:Camera HAL可能以30fps送帧,但屏幕刷新率是60Hz。SurfaceFlinger会挑最新的帧来合成,而不是每一帧都合成。这就引出了下一个话题——显示同步。

14.3 显示同步:VSync与BufferQueue的协作

说到显示同步,就绕不开VSync。VSync是硬件产生的同步信号,告诉系统「屏幕已经刷新完了,可以准备下一帧了」。

我见过不少开发者对VSync的理解停留在「就是垂直同步嘛」。其实在Camera Preview场景下,VSync的作用远不止于此。

我的经验:在调试低延迟Preview时,我通常会关注两个VSync:

  • App VSync:控制App的UI渲染节奏
  • SF VSync:控制SurfaceFlinger的合成节奏

这两个VSync如果不同步,就会出现「App准备好了,但SF还没合成」或者「SF要合成了,但App还没准备好」的情况,直接导致丢帧。

BufferQueue在这里扮演了什么角色?说白了,它是一个带缓冲的管道。Camera HAL把帧塞进去,SurfaceFlinger按自己的节奏取出来。BufferQueue的深度(也就是buffer数量)直接影响延迟和流畅度。

我整理了一个表格,对比不同BufferQueue深度的影响:

Buffer数量 延迟 流畅度 适用场景
1(单缓冲) 最低 差(容易卡顿) 低延迟需求,但很少用
2(双缓冲) 较低 中等 常见于普通Preview
3(三缓冲) 稍高 高帧率场景

嗯,这里要注意——不是buffer越多越好。buffer多了,延迟就上去了。我做过一个实验,把buffer从2改成3,延迟增加了约8ms。对于普通拍照无所谓,但对于扫码、AR这类场景,8ms的差距就很明显了。

14.4 帧率控制:如何稳定在30fps?

帧率控制是Preview流程里最容易被忽视的环节。很多人以为只要setRepeatingRequest设了30fps,就一定能跑到30fps。太天真了。

实际影响帧率的因素有这些:

  • Sensor输出帧率:硬件能力,比如有些Sensor在1080p下只能跑30fps,4K下只能跑15fps
  • ISP处理时间:图像处理管线有多长,有没有开HDR、美颜等
  • BufferQueue的消费速度:SurfaceFlinger能不能及时取走帧
  • CPU/GPU负载:系统资源够不够

我遇到过最典型的问题——App端做了复杂的UI动画,导致主线程卡顿,结果Preview帧率也跟着掉。为什么?因为SurfaceFlinger合成时,UI Layer和Preview Layer是绑在一起的,UI卡了,Preview也被拖累。

避坑指南:千万不要在UI主线程里做耗时操作。我曾经因为一个网络请求阻塞了主线程,导致Preview帧率从30fps掉到了15fps。排查了三天才发现是这个问题。

那怎么稳定帧率?我的做法是:

  1. CameraMetadata.CONTROL_AE_TARGET_FPS_RANGE设置目标帧率范围
  2. 在HAL层确认Sensor的实际输出能力
  3. 监控BufferQueue的丢帧情况(通过dumpsys SurfaceFlinger可以看)

说白了,帧率控制是一个系统工程,不是设一个参数就能搞定的。

14.5 低延迟Preview:从30ms到10ms的优化之路

低延迟Preview是很多场景的刚需——无人机、运动相机、AR眼镜。普通Preview的延迟通常在30-50ms,但低延迟场景要求做到10ms以内。

怎么优化?我总结了几条路:

  • 减少Buffer数量:从三缓冲降到双缓冲,甚至单缓冲(但要注意卡顿)
  • 绕过SurfaceFlinger:直接通过HWC(Hardware Composer)合成,跳过GPU处理
  • 使用Direct Surface:让Camera HAL直接写入Display的Buffer,省掉一次拷贝
  • 关闭不必要的图像处理:比如关掉3A算法中的某些环节

我记得有一次做无人机图传项目,要求Preview延迟低于15ms。我们试了各种方案,最后发现瓶颈不在Camera HAL,而在SurfaceFlinger的合成策略上。SurfaceFlinger默认会等所有Layer都准备好才合成,但我们只需要Preview Layer,其他UI Layer可以延迟一帧。

解决方案是什么?我们修改了SurfaceFlinger的合成策略,让Preview Layer优先合成。嗯,这个改动涉及到底层代码,不是所有厂商都开放了接口。但如果你有系统权限,可以试试。

核心思路:低延迟Preview的本质是「减少帧在管道中的停留时间」。每一帧从Sensor曝光到显示在屏幕上,中间经过的每一个环节都会引入延迟。优化的目标就是砍掉不必要的环节,或者让环节并行起来。

最后说一个我踩过的坑——低延迟模式下,帧率可能会不稳定。因为buffer少了,一旦某个环节卡一下,整个管道就空了。所以低延迟和稳定性是一对矛盾,需要根据实际场景做权衡。

好了,Preview流程的核心内容就这些。从启动到合成,从同步到帧率控制,再到低延迟优化,每一步都有它的门道。你如果在实际项目中遇到问题,欢迎来交流。毕竟,纸上得来终觉浅,绝知此事要躬行。


公众号:蓝海资料掘金营,微信deep3321