29、音视频测试与质量评估:PSNR/SSIM计算、音画同步测试、弱网模拟

做音视频开发这么多年,我踩过最大的坑,就是「自测没问题,上线就翻车」。

你想想看,本地跑得飞快的视频播放器,到了用户手里,画面卡成PPT,声音还对不上嘴型。这种问题,光靠肉眼盯是盯不出来的。必须上工具、上指标、上模拟环境。

今天这一章,我就把压箱底的三板斧掏出来:PSNR/SSIM 算画质、音画同步 测延迟、弱网模拟 找极限。说白了,就是给你的音视频App做一次全身体检。

29.1 画质评估:PSNR 与 SSIM

先聊画质。你辛辛苦苦把视频压缩了一遍,到底损失了多少?不能光靠「我觉得还行」来判断。

29.1.1 PSNR:峰值信噪比

PSNR 是最经典的客观指标。它算的是原始图像和压缩后图像之间的像素误差。数值越高,失真越小。

公式长这样:

PSNR = 10 * log10( (MAX^2) / MSE )

其中 MSE 是均方误差,MAX 是像素最大值(比如 255)。

我个人习惯,PSNR 在 40dB 以上,说明画质几乎无损;30~40dB 算良好;低于 30dB,嗯,肉眼就能看出明显瑕疵了。

经验之谈: 我在项目中遇到过,H.264 编码器参数调错了,PSNR 直接从 38dB 掉到 28dB。当时测试同事说「画面有点糊」,我还不信,一算 PSNR 就老实了。

29.1.2 SSIM:结构相似性

PSNR 有个毛病——它只算像素差异,不考虑人眼感知。比如稍微调亮一点,PSNR 可能掉很多,但人眼觉得没啥区别。

SSIM 就聪明多了。它从亮度、对比度、结构三个维度去比较。取值范围 0~1,越接近 1 越好。

一般 SSIM 在 0.95 以上,人眼基本分不出区别。0.9 以下,就能看出压缩痕迹了。

29.1.3 代码实现

Android 上怎么算?我一般用 OpenCV 或者直接撸像素。下面给个纯 Java 的 PSNR 计算片段:

public static double calculatePSNR(Bitmap original, Bitmap compressed) {
    int width = original.getWidth();
    int height = original.getHeight();
    double mse = 0;
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            int pixel1 = original.getPixel(x, y);
            int pixel2 = compressed.getPixel(x, y);
            int r = Color.red(pixel1) - Color.red(pixel2);
            int g = Color.green(pixel1) - Color.green(pixel2);
            int b = Color.blue(pixel1) - Color.blue(pixel2);
            mse += (r * r + g * g + b * b) / 3.0;
        }
    }
    mse /= (width * height);
    if (mse == 0) return 100; // 完全一致
    return 10 * Math.log10((255 * 255) / mse);
}
小提示: 实际项目中,别逐像素算,太慢了。可以用 RenderScript 或者 OpenGL 做并行计算,速度能快几十倍。

29.2 音画同步测试

音画不同步,是用户骂娘最多的问题。我见过最离谱的案例——声音比画面快了 2 秒,用户以为是「预知未来」功能。

29.2.1 测试方法

测音画同步,我推荐两种方式:

  • 打点法: 在视频特定帧插入一个视觉标记(比如闪白),同时在音频轨道对应位置插入一个短促的「嘀」声。播放时用示波器或者录屏软件,数一下闪白和嘀声之间的帧数差。
  • 自动化工具: 用 FFmpeg 的 av_sync 或者自己写脚本,逐帧比对音视频时间戳。

29.2.2 可接受范围

ITU-R BT.1359 标准建议:

场景可接受延迟可察觉延迟
影视内容±40ms±80ms
直播/通话±20ms±40ms
游戏±15ms±30ms
注意: 我曾经在某个直播项目中,因为音频采集缓冲区设得太大,导致音画偏差到了 120ms。用户反馈「口型对不上」,我们排查了两天才找到根因。所以,缓冲区大小一定要根据场景动态调整。

29.3 弱网模拟

弱网环境,是音视频 App 的照妖镜。你在办公室 Wi-Fi 下测出来的流畅度,到了地铁、电梯、地下室,可能直接崩掉。

29.3.1 模拟工具

Android 上做弱网模拟,我常用这几招:

  • Android Emulator 自带网络限制: 模拟器设置里可以选 EDGE、3G、4G 等模式,还能自定义延迟和丢包率。
  • Facebook ATC(Augmented Traffic Control): 一个强大的弱网模拟工具,可以精确控制带宽、延迟、丢包、抖动。
  • Charles / Fiddler: 通过代理做限速,适合 HTTP/HTTPS 流媒体测试。
  • Linux tc 命令: 如果你有 root 权限,直接在设备上用 tc qdisc 模拟弱网。

29.3.2 关键指标

弱网测试,重点看这几个:

  • 丢包率: 1% 以下基本无感,3% 开始卡顿,5% 以上画面马赛克严重。
  • 延迟: 100ms 以内体验良好,300ms 以上交互感明显下降。
  • 抖动: 延迟忽高忽低,比高延迟更致命。抖动超过 50ms,播放器缓冲区就很容易饿死。

29.3.3 实战脚本

下面是我常用的一个弱网模拟脚本(需要 root 权限):

# 模拟 3G 网络:延迟 100ms,丢包 2%,带宽 1Mbps
tc qdisc add dev wlan0 root netem delay 100ms loss 2% rate 1mbit

# 恢复网络
tc qdisc del dev wlan0 root
避坑指南: 我曾经在弱网测试时,只测了丢包,没测抖动。结果上线后,用户在电梯里遇到「声音断断续续,画面卡成幻灯片」——就是因为网络抖动导致缓冲区频繁溢出。从那以后,我每次弱网测试都必加抖动参数。

29.4 知识体系总览

下面这张图,把本章的核心逻辑串起来了:

音视频质量评估 画质评估 PSNR:像素误差 SSIM:结构相似性 音画同步测试 打点法 / 自动化 延迟标准:±20~40ms 弱网模拟 工具:ATC / tc 指标:丢包/延迟/抖动 目标:保证用户体验,避免上线翻车

29.5 总结

音视频测试,说白了就是三个字:量化。画质用 PSNR/SSIM 量化,音画同步用时间戳量化,弱网用丢包/延迟/抖动量化。

我个人的经验是:不要等到上线前才做测试。从编码器选型、网络协议设计、播放器缓冲区策略,每一步都要带着测试思维去做。否则,等用户骂上门来,就晚了。

最后一句: 测试不是为了找茬,而是为了让你睡得安稳。嗯,我每次发版前,都会把这三项跑一遍,然后安心下班。

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