22、Android CTS测试:CTS音视频同步测试用例、通过CTS的关键点、常见失败原因
CTS,全称Compatibility Test Suite,是Google用来保证Android设备兼容性的“照妖镜”。
我当年第一次跑CTS音视频测试时,心里想的是:“不就是播个片吗,能有多难?”结果一跑就是三天三夜,挂了十几个case。嗯,从那以后我再也不敢小看CTS了。
说白了,CTS就是Google给你定的一套规矩。你卖手机可以,但必须通过这套测试。音视频同步这块,尤其容易翻车。今天我就把压箱底的避坑经验拿出来聊聊。
22.1 CTS音视频同步测试用例有哪些?
CTS里跟音视频同步相关的测试,主要集中在android.media.cts这个包下面。我挑几个最要命的说说:
| 测试类 | 测试方法 | 测试内容 |
|---|---|---|
| MediaPlayerTest | testAudioVideoSync | 播放标准测试视频,检查音画是否同步 |
| AudioTrackTest | testTimestamp | 验证AudioTrack的时间戳是否准确 |
| SurfaceTextureTest | testTimestampSync | 检查SurfaceTexture的时间戳传递是否正确 |
| MediaCodecTest | testOutputSurface | 验证解码器输出到Surface的同步机制 |
其中testAudioVideoSync是最容易挂的。它播放一个包含特定时间戳标记的视频,然后通过摄像头或者人工判断音画是否对齐。我记得有一次,某款芯片的解码器在播放H.265视频时,音频总是比视频快80ms,怎么调都过不了。
核心要点:CTS音视频同步测试的核心,就是验证从解码器输出到渲染显示这条链路上,时间戳是否被正确传递和解析。
22.2 通过CTS的关键点
想通过CTS,光靠运气可不行。我总结了三个关键点,你照着做,至少能少踩一半的坑。
关键点一:时间戳必须精确到微秒级
Android的MediaTimestamp类要求时间戳精度达到纳秒级。但实际测试中,微秒级就够了。问题是很多硬件解码器返回的时间戳是“四舍五入”的,比如29.97fps的视频,每帧间隔是33.3667ms,有些解码器直接给你截成33ms。这误差累积几秒后,音画就差了半秒。
我建议你在解码器的onOutputBufferAvailable回调里,打印出每个buffer的presentationTimeUs,看看是不是均匀递增的。如果发现跳变或者重复,那基本就是硬件的问题。
// 检查时间戳是否均匀
long lastTimestamp = -1;
for (MediaCodec.BufferInfo info : outputBuffers) {
if (lastTimestamp != -1) {
long delta = info.presentationTimeUs - lastTimestamp;
if (delta < 30_000 || delta > 40_000) { // 33ms左右
Log.w("CTS", "时间戳异常: delta=" + delta);
}
}
lastTimestamp = info.presentationTimeUs;
}
关键点二:渲染管线不能有额外延迟
你想想看,解码器输出是及时的,但如果你在渲染线程里加了一个队列,或者用了异步Surface,那延迟就不可控了。CTS测试里有一个隐藏条件:从解码器输出到屏幕显示,延迟不能超过一个帧周期(约33ms)。
我曾经遇到一个案子,厂商为了做“画质增强”,在SurfaceFlinger前面加了一个后处理模块。结果这个模块引入了两帧的延迟,CTS直接挂了。最后只能把这个模块在CTS测试时关掉。
避坑指南:如果你用了自定义的渲染管线,一定要在CTS测试时走“直通模式”。也就是解码器输出直接给Surface,中间不要加任何buffer。
关键点三:音频路径不能有重采样
CTS测试用的音频采样率是48000Hz。如果你的音频通路做了重采样(比如从48000重采样到44100),那延迟就会飘。我见过最离谱的情况是,某款手机的音频DSP默认把所有音频都重采样到96000Hz,结果音画同步测试直接差了200ms。
检查方法很简单:用AudioManager.getProperty(PROPERTY_OUTPUT_SAMPLE_RATE)看看实际输出采样率是不是48000。
22.3 常见失败原因
跑CTS这么多年,我总结出几个“高频翻车点”。你对照着排查,基本能解决90%的问题。
失败原因一:解码器时间戳溢出
有些硬件解码器内部用32位整数存时间戳,单位是微秒。32位有符号整数的最大值是2^31-1,约2147秒,也就是35分钟。如果你的测试视频超过35分钟,时间戳就溢出了,音画直接乱套。
解决方案:要么换64位时间戳的解码器,要么把测试视频剪短。
失败原因二:VSync信号不稳定
CTS测试会检查视频帧是否在VSync信号到来时显示。如果屏幕刷新率不稳定(比如从60Hz掉到30Hz),那视频帧就会丢或者重复。我遇到过一款手机,在低电量模式下会自动降刷新率,结果CTS全挂。
注意:CTS测试时,请确保设备处于“高性能模式”,关闭省电模式、自动亮度、动态刷新率等功能。
失败原因三:音频时钟和视频时钟不同源
这是最隐蔽的问题。音频用晶振A,视频用晶振B,两个晶振的ppm(百万分之一)误差不一样。比如音频晶振是100ppm,视频晶振是50ppm,那么每秒钟就会产生0.05ms的漂移。10分钟后,漂移达到30ms,CTS就判定失败。
我建议你在硬件设计阶段就要求音频和视频共用同一个时钟源。如果做不到,那就在软件层做时钟同步,比如用AudioTrack.getTimestamp获取音频时钟,然后动态调整视频播放速度。
失败原因四:SurfaceFlinger的BufferQueue溢出
CTS测试中,解码器会快速输出帧。如果SurfaceFlinger来不及消费,BufferQueue就会满,然后解码器被阻塞。这一阻塞,音频还在继续播放,音画就不同步了。
解决方法:增大BufferQueue的容量,或者确保渲染线程的优先级足够高。
22.4 我的调试工具链
最后分享一套我常用的调试工具,帮你快速定位问题:
- dumpsys media.audio_policy:查看音频路由和采样率
- dumpsys SurfaceFlinger:查看图层合成状态和帧率
- logcat -s MediaCodec:过滤解码器日志,看时间戳
- systrace:抓取整个渲染管线的时序图
我个人习惯是先用systrace抓一次完整的播放流程,看看音频和视频的时间戳是否在同一个时间轴上。如果发现音频时间戳和视频时间戳的基准点不同,那基本就是时钟不同源的问题。
嗯,CTS这东西,说白了就是“细节决定成败”。你硬件做得再好,软件上一个小疏忽就能让你翻车。希望今天讲的这些,能帮你少走点弯路。
总结一下:CTS音视频同步测试,考的不是你的播放器有多花哨,而是你的时间戳传递链路是否干净、准确。把解码器、渲染器、音频输出这三块的时间戳对齐了,CTS自然就过了。
公众号:蓝海资料掘金营,微信deep3321