第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左右。

我的排查步骤:

  1. 第一步:dumpsys看状态

    adb shell dumpsys media.camera | grep -A 20 "RequestThread",发现pending请求数量在波动,峰值达到8个。正常情况应该不超过2个。

  2. 第二步:抓trace

    用atrace抓10秒trace,在Perfetto里打开。看到HAL层的processCaptureRequest函数,每次调用耗时在20-30ms之间,远超16ms的帧间隔。

  3. 第三步:CPU profiling

    用simpleperf采样,发现有个函数叫 HAL3Module::configureStreams 被频繁调用。这不对劲,configureStreams应该在初始化时调用一次,不应该在预览过程中反复调用。

  4. 第四步:定位根因

    查看代码,发现上层每次请求帧时,都会重新配置stream。原因是某个vendor tag设置错误,导致HAL认为stream配置发生了变化。

  5. 第五步:修复

    修正vendor tag的设置逻辑,确保只在必要的时候才调用configureStreams。修复后,processCaptureRequest耗时稳定在8-10ms,帧率稳定30fps。

避坑指南:我曾经遇到一个更隐蔽的问题——预览卡顿是因为CPU调频策略导致的。Camera线程跑在小核上,频率上不去。解决方案是设置CPU affinity,把Camera线程绑定到大核上。

12.6 本章知识体系

下面这张图,是我总结的Camera调试工具链全景图。建议你保存下来,遇到问题按图索骥:

Camera调试工具链全景图 预览卡顿 / 帧率不稳 第一层:快速诊断(dumpsys) 第二层:详细分析(Trace + Profiling) atrace / systrace(时序分析) simpleperf / perfetto(CPU/内存) 定位根因:锁竞争 / 内存泄漏 / 配置错误 按此流程排查,90%的Camera性能问题都能在30分钟内定位

这张图的核心逻辑是:先快速诊断,再详细分析,最后定位根因。不要一上来就抓trace,那是浪费生命。

最后说一句:调试工具只是手段,真正重要的是你对Camera pipeline的理解。工具告诉你哪里卡,你得知道为什么卡。这个「为什么」,就是咱们这门课一直在讲的东西。


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