卡顿的定义:什么算启动卡顿,衡量指标

到底什么是"启动卡顿"

做 MediaCodec 优化这么多年,我见过最多的问题就是——"这算卡顿吗?"

不同团队对卡顿的理解差太多了。产品经理觉得"转圈超过1秒就是卡",测试同学说"掉帧就算",开发往往认为"只要没 ANR 就不算卡"。嗯,这里面其实有个共识问题。

启动卡顿,说白了就是用户从点击播放到看到流畅画面之间的"不舒服体验"。

从技术角度看,我习惯把它拆成两段:

  • 第一段:等待期 —— 用户点了播放,画面还没出来。这段时间是"黑屏"或"转圈",属于延迟范畴。
  • 第二段:适应期 —— 画面出来了,但一顿一顿的,帧率不稳。这属于帧率范畴。

我在项目中遇到过这样一个案例:一个短视频 App,首帧出来只要 200ms,但前 3 秒一直在掉帧。用户照样觉得"卡得要死"。你看,光快没用,还得流畅。

核心定义:启动卡顿 = 首次画面呈现延迟 + 初始播放阶段的帧率不稳定。两者缺一不可。

延迟指标:用户等多久才算"卡"

延迟是最直观的。你想想看,点完播放按钮,屏幕一直黑着,超过 500ms 大多数人就开始焦躁了。

我常用的延迟指标有两个:

首次帧显示时间(FFDT)

从 MediaCodec 调用 start() 到第一帧渲染到 Surface 的时间差。这是最核心的启动延迟指标。

测量方式很简单:

// 测量首次帧延迟
long startNs = System.nanoTime();
MediaCodec codec = MediaCodec.createByCodecName(mimeType);
codec.configure(format, surface, null, 0);
codec.start();

BufferInfo info = new BufferInfo();
while (!isFirstFrameRendered) {
    int index = codec.dequeueOutputBuffer(info, 10000);
    if (index == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
        // 格式变化,继续
    } else if (index >= 0) {
        // 首次输出缓冲区,算第一帧
        long ffdt = System.nanoTime() - startNs;
        Log.d("Latency", "首次帧延迟: " + ffdt / 1_000_000 + "ms");
        codec.releaseOutputBuffer(index, true);
        isFirstFrameRendered = true;
    }
}

我建议:FFDT 的基准线设在 300ms 以内。超过 500ms 用户就能明显感知到"卡"了。

启动总耗时

从用户点击到画面稳定播放的完整时间。包含:

  • MediaCodec 创建 + 配置耗时
  • 首帧解码 + 渲染耗时
  • 前 N 帧的缓冲区填充耗时

这个指标更贴近用户真实感受。我一般取"从点击到第 3 帧显示"的时间作为总耗时。

延迟等级 FFDT 启动总耗时 用户感知
优秀 < 100ms < 500ms 几乎无感
良好 100~300ms 500~1000ms 轻微等待
可接受 300~500ms 1000~2000ms 明显等待
严重 > 500ms > 2000ms 焦躁、退出

我曾经踩过这个坑:只盯着 FFDT 看,忽略了总耗时。有一次 FFDT 优化到了 80ms,但用户还是抱怨卡。一查发现后面几帧间隔了 400ms,总耗时超过 1.5s。所以别只看单点,要看完整链路。

帧率指标:画面多流畅才算"不卡"

延迟解决了"有没有画面"的问题,帧率解决的是"画面稳不稳"的问题。

我常用的帧率指标有三个:

平均帧率(Average FPS)

启动后前 N 秒的平均帧率。一般取前 3 秒或前 5 秒的数据。

计算公式:

平均帧率 = 总帧数 / 总时长(秒)

对于 30fps 的内容,平均帧率低于 20fps 就算卡。对于 60fps 的内容,低于 40fps 就不及格。

帧率抖动(Jank)

帧间隔时间的标准差。这个指标比平均帧率更敏感。

举个例子:

  • 场景 A:帧间隔均匀 33ms,平均 30fps → 流畅
  • 场景 B:大部分帧 20ms,突然跳一帧 80ms,平均也是 30fps → 明显卡顿

平均帧率看不出 B 的问题,但帧率抖动能。我个人习惯把抖动超过 10ms 的帧标记为 Jank 帧。

掉帧率(Frame Drop Rate)

掉帧是最直观的卡顿表现。我一般统计"实际帧数 / 期望帧数"的比值。

掉帧率 = (期望帧数 - 实际帧数) / 期望帧数 × 100%

举个例子,30fps 的内容,3 秒内期望 90 帧,实际只渲染了 60 帧,掉帧率就是 33%。超过 20% 用户就能明显感知到卡了。

关键结论:帧率指标不能只看平均值。Jank 率和掉帧率才是反映"卡顿感"的核心指标。

综合评判:怎么定量衡量卡顿

讲完指标,问题来了——这些数据怎么组合起来判断是否卡顿?

我自己的经验是:延迟指标做筛选,帧率指标做分级。

  1. 第一步:看 FFDT 是否超过 500ms。超过的直接判定为"严重卡顿"。
  2. 第二步:如果 FFDT 达标,看前 3 秒的 Jank 帧占比。超过 30% 就算"中度卡顿"。
  3. 第三步:再看掉帧率。低于 10% 算流畅,10%~20% 算轻度卡顿,超过 20% 算中度以上。

我建议你在工程中这样做:把 FFDT、Jank 率、掉帧率三个指标合成一个"卡顿指数",权重可以设为 4:3:3。指数低于 30 算流畅,30~60 算轻度,60~80 算中度,80 以上算严重。

提醒一下:不同场景的容忍度不一样。短视频用户对延迟更敏感,长视频用户对帧率稳定性更敏感。别拿一套标准套所有场景。

启动卡顿衡量指标体系 启动卡顿定义 延迟指标 帧率指标 首次帧显示时间 启动总耗时 平均帧率 帧率抖动 / 掉帧率 综合卡顿判定 延迟做筛选 → 帧率做分级 → 加权合成卡顿指数 FFDT权重0.4 + Jank率权重0.3 + 掉帧率权重0.3

回到实际工作里,我习惯用这张图来跟团队对齐"什么是卡顿"。它把延迟和帧率两条线串起来了,也给出了最终的判定逻辑。

说白了,定义卡顿不是为了学术研究,是为了让团队有个统一的标准去排查和优化。没有标准,你说卡我说不卡,根本没法干活。

拿我自己的项目来说,自从定了"FFDT < 300ms + Jank率 < 20%"这个标准,测试和开发的扯皮少了一大半。数据说话,比感觉靠谱多了。

一句话总结本章:启动卡顿 = 延迟超标 + 帧率不稳。衡量它,盯紧 FFDT、Jank 率、掉帧率这三个数就够了。