第12章:Camera调试工具与性能优化
各位同学,欢迎来到第12章。说实话,Camera驱动开发做到后面,你会发现写代码只占30%的时间,剩下70%都在调试和优化。今天我就把压箱底的调试工具链和优化方法,一次性讲透。
12.1 adb shell dumpsys media.camera 详解
这个命令,是我日常使用频率最高的调试工具。没有之一。
先看基本用法:
adb shell dumpsys media.camera
输出内容会分成几个关键区块:
| 区块 | 含义 | 重点关注 |
|---|---|---|
| CameraService | 服务状态 | 是否正常启动、客户端数量 |
| CameraDevice | 设备实例 | 打开的Camera ID、Session配置 |
| StreamConfiguration | 流配置 | 分辨率、格式、帧率 |
| RequestThread | 请求处理线程 | 是否有pending请求、超时情况 |
我个人习惯,遇到预览卡顿问题,第一件事就是跑这个命令。看什么?看RequestThread里有没有大量pending的请求。如果有,说明HAL层处理不过来。
实战技巧:加参数 -v 可以看更详细的vendor tag信息。我曾经靠这个定位过一个三星平台上的metadata丢失问题。
12.2 Camera Trace分析(atrace/systrace)
Trace分析,说白了就是给系统拍X光片。它能告诉你每一帧到底卡在哪里。
启动Trace的命令:
adb shell atrace --async_start -b 16384 camera gfx input view
# 操作手机,复现问题
adb shell atrace --async_dump > trace_output.txt
adb shell atrace --async_stop
为什么我强调要用atrace而不是systrace?因为atrace是systrace的轻量级替代品,在Android 10以上更稳定。你想想看,如果Trace工具本身都卡死了,还怎么分析问题?
拿到trace文件后,用Perfetto UI打开(ui.perfetto.dev)。重点看这几个区间:
- Camera HAL process:HAL层处理时间,超过16ms就要警惕
- SurfaceFlinger:合成时间,这里卡顿通常是GPU瓶颈
- App::onFrameAvailable:应用层接收帧的时间
我的经验:有一次客户反馈预览卡顿,我打开trace一看,发现HAL层有个锁竞争,两个线程在抢同一个buffer。加了个mutex优化,帧率从15fps直接拉到30fps。
12.3 帧率与延迟分析
帧率分析,我推荐两个工具:Choreographer和FrameMetrics。
12.3.1 Choreographer
Choreographer是Android的帧率调度器。在代码里加个监听:
Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
long now = System.nanoTime();
long jitter = now - frameTimeNanos;
if (jitter > 16_000_000) { // 超过16ms
Log.w("Camera", "Frame dropped! Jitter: " + jitter / 1_000_000 + "ms");
}
Choreographer.getInstance().postFrameCallback(this);
}
});
这段代码我用了好几年。它能精确告诉你每一帧的抖动情况。说白了,帧率稳定不等于每一帧都准时,抖动才是卡顿的元凶。
12.3.2 FrameMetrics
Android 7.0以上可以用FrameMetrics,更系统化:
window.addOnFrameMetricsAvailableListener(
{ window, frameMetrics, dropCountSinceLastInvocation ->
val totalDuration = frameMetrics.getMetric(TOTAL_DURATION)
if (totalDuration > 16_000_000) {
Log.w("Camera", "Jank frame: " + totalDuration / 1_000_000 + "ms")
}
}, Handler(Looper.getMainLooper()))
注意:FrameMetrics的回调是在主线程,不要在回调里做耗时操作。我曾经见过有人在这里写日志写到文件,结果把主线程堵死了。
12.4 内存与CPU Profiling
性能优化,内存和CPU是绕不开的两座大山。
12.4.1 simpleperf
simpleperf是Android自带的CPU采样工具,比perf更友好:
# 采样10秒
adb shell simpleperf record -e cpu-cycles -f 1000 -p $(pidof cameraserver) --duration 10
adb shell simpleperf report --sort comm,dso,symbol
输出会告诉你哪个函数占用了最多的CPU周期。我遇到过一个案例,HAL层的某个函数占了60%的CPU,一看代码,里面有个不必要的memcpy,去掉后CPU占用直接降到15%。
12.4.2 perfetto
perfetto是新一代的profiling工具,可以同时看CPU、内存、I/O:
adb shell perfetto -c - --txt <<EOF
buffers: {
size_kb: 65536
}
data_sources: {
config {
name: "linux.process_stats"
target_buffer: 0
}
}
duration_ms: 10000
EOF
我个人更推荐perfetto,因为它能关联多个维度。比如你可以同时看到:CPU使用率飙升的同时,内存也在增长,那大概率是内存泄漏导致的GC频繁。
12.5 实战:定位并解决预览卡顿问题
好了,理论讲完了,咱们来真刀真枪干一场。
问题现象:某款手机打开Camera预览,画面每隔几秒就卡一下,帧率从30fps掉到10fps左右。
我的排查步骤:
- 第一步:dumpsys看状态
跑
adb shell dumpsys media.camera | grep -A 20 "RequestThread",发现pending请求数量在波动,峰值达到8个。正常情况应该不超过2个。 - 第二步:抓trace
用atrace抓10秒trace,在Perfetto里打开。看到HAL层的processCaptureRequest函数,每次调用耗时在20-30ms之间,远超16ms的帧间隔。
- 第三步:CPU profiling
用simpleperf采样,发现有个函数叫
HAL3Module::configureStreams被频繁调用。这不对劲,configureStreams应该在初始化时调用一次,不应该在预览过程中反复调用。 - 第四步:定位根因
查看代码,发现上层每次请求帧时,都会重新配置stream。原因是某个vendor tag设置错误,导致HAL认为stream配置发生了变化。
- 第五步:修复
修正vendor tag的设置逻辑,确保只在必要的时候才调用configureStreams。修复后,processCaptureRequest耗时稳定在8-10ms,帧率稳定30fps。
避坑指南:我曾经遇到一个更隐蔽的问题——预览卡顿是因为CPU调频策略导致的。Camera线程跑在小核上,频率上不去。解决方案是设置CPU affinity,把Camera线程绑定到大核上。
12.6 本章知识体系
下面这张图,是我总结的Camera调试工具链全景图。建议你保存下来,遇到问题按图索骥:
这张图的核心逻辑是:先快速诊断,再详细分析,最后定位根因。不要一上来就抓trace,那是浪费生命。
最后说一句:调试工具只是手段,真正重要的是你对Camera pipeline的理解。工具告诉你哪里卡,你得知道为什么卡。这个「为什么」,就是咱们这门课一直在讲的东西。
公众号:蓝海资料掘金营,微信deep3321