背景
某回放视频在客户端播放或拖动进度后提示"加载失败"。客户端日志显示播放器进入错误状态,表现为 ExoPlayer 音频解码失败。
本文记录该类问题的分析过程、关键证据和可复用排查指令。
现象
客户端播放回放视频时,起播可以成功,拖动到后半段或某个固定时间点之后会快速失败。
典型日志特征如下:
text
MediaCodecAudioRenderer error
format=audio/mp4a-latm, mp4a.40.2, 64000, [1, 44100]
format_supported=YES
Decoder failed: c2.android.aac.decoder
ERROR_CODE_DECODING_FAILED
MediaCodec$CodecException: Error 0xe
这类日志说明:
- 音频格式为 AAC-LC,播放器和系统认为格式受支持。
- 失败发生在解码阶段,不是普通网络加载失败。
- 报错模块是音频渲染器
MediaCodecAudioRenderer,不是视频渲染器。
分析结论
源文件本身存在音轨/封装异常。
实际有效视频轨只有约 3377s,即约 56分17秒。但音频轨末尾存在异常 AAC packet,其 duration 被写成约 5011s,导致音频轨和容器总时长被错误拉长到约 8651s,即约 2小时24分。
因此,当客户端播放或 seek 到 3377s 之后时:
- 视频轨已经没有正常画面数据。
- 音频轨进入异常拖尾区间。
- 系统 AAC decoder 解码异常音频数据失败。
- 播放器收到
ERROR_CODE_DECODING_FAILED,进入 error 状态。
准确描述是:3377 秒之后没有视频轨数据;音频轨还有异常拖尾,但不是正常可播放音频。
排查过程
1. 查看源文件基础信息
bash
ffprobe -hide_banner -v error \
-show_format \
-show_streams \
/path/to/input.mp4
重点关注:
format.duration- 视频流
duration - 音频流
duration - 音频编码、采样率、声道数
异常文件的典型表现:
text
video duration ~= 3377s
audio duration ~= 8651s
format duration ~= 8651s
audio codec = aac
sample_rate = 44100
channels = 1
视频轨和音频轨时长严重不一致,是本问题的重要线索。
2. 检查异常时间点附近的音频 packet
bash
ffprobe -hide_banner -v error \
-select_streams a:0 \
-show_packets \
-read_intervals 3376%+5 \
-show_entries packet=pts_time,dts_time,duration_time,size,flags,pos \
/path/to/input.mp4
正常 AAC packet 的时长通常约为 0.023s。异常文件在末尾会出现类似:
text
pts_time=3377.050000
duration_time=5011.263991
size=188
这说明音频轨最后出现了一个异常长 duration 的 packet,直接把音频轨和容器总时长拖长。
3. 单独验证音频解码
bash
ffmpeg -hide_banner -v error \
-ss 3376 \
-t 10 \
-i /path/to/input.mp4 \
-map 0:a:0 \
-f null -
异常文件会报类似错误:
text
[aac] channel element 2.8 is not allocated
Error submitting packet to decoder: Invalid data found when processing input
这说明问题可以在本地通过 FFmpeg 解码复现,不依赖客户端环境。
4. 单独验证视频轨
bash
ffmpeg -hide_banner -v error \
-ss 3376 \
-t 10 \
-i /path/to/input.mp4 \
-map 0:v:0 \
-f null -
若视频轨无报错,同时音频轨报错,则可以进一步确认主问题在音频轨/封装。
5. 客户端现场日志验证
通过 ADB 抓取播放现场日志:
bash
adb devices -l
adb -s <device_serial> logcat -c
adb -s <device_serial> logcat -v threadtime > full.log
同时过滤播放器关键日志:
bash
adb -s <device_serial> logcat -v threadtime \
| rg --line-buffered 'JZMediaExo|JzvdPro|ExoPlayer|MediaCodecAudioRenderer|MediaCodec|CodecException|Decoder failed|ERROR_CODE_DECODING_FAILED|onPlayerError|Audio Codec Error|seekTo|onError'
现场日志中如果出现以下组合,基本可以和源文件异常对齐:
text
seekTo <大于视频轨实际时长的时间点>
Audio Codec Error
MediaCodecAudioRenderer error
Decoder failed: c2.android.aac.decoder
ERROR_CODE_DECODING_FAILED
onError 1000 - 1000
修复验证
如果只需要保留有效视频内容,可裁掉异常音频拖尾:
bash
ffmpeg -hide_banner -y -v error \
-i /path/to/input.mp4 \
-to 3377.027 \
-map 0:v:0 -map 0:a:0 \
-c copy -movflags +faststart \
/path/to/output_trimmed.mp4
验证裁剪后的文件:
bash
ffprobe -hide_banner -v error \
-show_format \
-show_streams \
/path/to/output_trimmed.mp4
ffmpeg -hide_banner -v error \
-i /path/to/output_trimmed.mp4 \
-f null -
预期结果:
- 输出文件总时长约
3377s,即56分17秒。 - 视频轨和音频轨时长基本一致。
- 全量解码不再报 AAC 解码错误。
注意:-shortest 对这类问题不一定有效,因为异常音频 packet 可能仍会保留在输出文件中。更可靠的方式是裁剪到坏 packet 之前。