调试与日志分析:Stagefright日志系统、常用调试工具、常见问题排查

做多媒体开发,说白了就是跟各种异常打交道。解码卡了、画面花屏、音画不同步……这些问题你迟早都会遇到。我刚开始接触Stagefright那会儿,也是被各种诡异bug折磨得够呛。后来慢慢摸清了它的日志系统和调试工具,才算是真正入了门。

今天我就把这块的经验梳理一下。嗯,咱们从日志系统开始聊。

Stagefright的日志体系

Stagefright的日志输出,主要依赖Android标准的ALOG宏。但它的日志标签(LOG_TAG)分布很散,不同模块用不同的tag。你如果不知道这些tag,看logcat就像大海捞针。

我个人习惯,遇到问题第一件事就是先过滤这几个关键tag:

LOG_TAG 所属模块 典型输出内容
NuPlayer 播放器引擎 状态切换、seek操作、缓冲事件
AwesomePlayer 旧版播放器 基本播放控制(已逐渐废弃)
Stagefright 核心框架 OMX组件加载、编解码器选择
OMXNodeInstance OMX接口层 buffer分配、状态回调、错误码
ACodec 编解码器封装 configure、start、flush流程
MediaCodec 上层API 应用层调用、queue/dequeue buffer
小技巧: 我一般用 logcat -s NuPlayer:V Stagefright:V ACodec:V 来同时过滤多个tag。如果问题跟硬件编解码有关,记得加上 OMXNodeInstance:V

常用调试工具

光看logcat还不够,很多时候需要借助系统工具来定位问题。下面这几个是我项目里用得最多的。

1. dumpsys media.player

这个命令能dump出当前所有MediaPlayer实例的状态。包括正在播放的URL、当前播放位置、缓冲百分比、底层解码器信息等。

adb shell dumpsys media.player

输出内容很长,但重点看这几行:

  • mPlayerState —— 播放器当前状态(IDLE/INITIALIZED/PREPARED/STARTED等)
  • mVideoWidth/mVideoHeight —— 视频分辨率,如果为0说明解码没成功
  • mCodecName —— 实际使用的编解码器名称
注意: 如果 mCodecName 显示的是软件解码器(如OMX.google.h264.decoder),而你的设备明明有硬件解码器,那就要检查一下硬件解码器是不是挂了。

2. bugreport + 多媒体部分

遇到难复现的问题,我习惯让测试同事抓一份完整的bugreport。里面包含了所有媒体服务的状态快照。

adb bugreport

解压后,重点关注 media/ 目录下的文件。特别是 media_server.txt,里面记录了MediaServer进程的详细状态,包括每个OMX组件的存活情况。

3. strace 跟踪系统调用

有些问题logcat里根本看不出线索,比如解码器卡在某个ioctl上。这时候我会用strace去跟踪mediaserver进程。

adb shell strace -p $(pidof mediaserver) -e trace=ioctl -t

输出里能看到每次ioctl调用的时间戳和参数。如果某个ioctl卡了很久(超过几百毫秒),那基本就是驱动层的问题了。

常见问题排查思路

下面这几个场景,是我在项目中反复遇到过的。我把排查步骤整理出来,你遇到类似问题可以直接照着查。

场景一:播放黑屏,但有声音

这个问题很典型。有声音说明解码和播放流程基本通了,问题出在视频渲染环节。

排查步骤:

  1. 先看logcat里有没有 Surface 相关的错误。比如 Surface::dequeueBuffer failed,说明SurfaceFlinger那边出问题了。
  2. 检查 dumpsys media.player 里的 mSurface 是否有效。如果显示 mSurface=null,那说明应用层没把Surface传进来。
  3. dumpsys SurfaceFlinger 查看对应窗口的layer状态。如果layer的 buffer count 一直为0,说明视频帧根本没提交到SurfaceFlinger。

我曾经遇到过一个案例: 某款手机在播放HDR视频时黑屏,但声音正常。查了两天才发现是SurfaceFlinger的HDR pipeline有个buffer queue溢出的bug。最后通过限制视频帧的入队数量解决了。

场景二:播放卡顿,频繁掉帧

卡顿的原因很多,但最常见的是解码速度跟不上渲染速度。

排查步骤:

  1. 打开 NuPlayer 的verbose日志:logcat -s NuPlayer:V。看有没有 kWhatVideoLagkWhatAudioEOS 这类事件。
  2. dumpsys media.playermVideoDecoderInputBuffersmVideoDecoderOutputBuffers 的计数。如果input buffer堆积很多,说明解码器处理不过来。
  3. 检查CPU/GPU负载。用 top -H -p $(pidof mediaserver) 看mediaserver进程里各线程的CPU占用。如果某个解码线程长期跑满,那就是性能瓶颈。
我的经验: 很多卡顿问题其实是码率太高导致的。你可以试试把视频转码成更低码率的版本,如果问题消失,那就确认是解码性能不足。这时候要么换硬件解码器,要么降低视频规格。

场景三:音画不同步

音画不同步,说白了就是音频和视频的时钟没对齐。Stagefright里用的是音频时钟作为主时钟。

排查步骤:

  1. 看logcat里有没有 audio timestamp 相关的警告。比如 AudioSink: calculated timestamp is too far,说明音频驱动返回的时间戳有问题。
  2. dumpsys media.player 查看 mVideoTimeUsmAudioTimeUs 的差值。如果差值超过100ms,那肯定不同步了。
  3. 检查音频输出设备。有些蓝牙耳机或USB声卡会有额外的延迟,导致音频比视频慢。
注意: 如果音画不同步只在特定视频文件上出现,那可能是文件本身的PTS(显示时间戳)就有问题。你可以用 ffprobe 检查一下视频流的PTS是否连续递增。

核心调试流程图

下面这张图,是我自己总结的Stagefright调试流程。遇到问题按这个顺序查,基本能覆盖90%的场景。

Stagefright 调试排查流程图 ① 收集日志 ② 过滤关键 LOG_TAG ③ 判断问题类型 黑屏/无画面 卡顿/掉帧 音画不同步 检查Surface/SurfaceFlinger 检查解码器性能/CPU负载 检查音频时钟/PTS

这张图的核心思路就是:先收集日志,再过滤关键信息,然后根据现象走不同的排查分支。你多练几次,遇到问题就能条件反射般地找到方向了。

写在最后

调试Stagefright,说白了就是跟日志和工具打交道。没有捷径,但掌握了正确的方法,效率能提升一大截。我个人建议你平时多看看 NuPlayerACodec 的源码,把日志打印的逻辑和代码流程对应起来。这样看logcat的时候,脑子里就能自动浮现出代码的执行路径。

嗯,今天就先聊到这儿。调试工具和思路都给你了,剩下的就是多动手、多踩坑。踩过的坑多了,自然就成了专家。


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