2. 音视频基础
在进入 MediaCodec 与 Codec2 的适配细节之前,必须先建立一套稳定的音视频“坐标系”。因为后续所有关于 buffer、时间戳、格式描述、同步模式、输出回调的讨论,都建立在本节这些基础概念之上。如果这些概念混淆,代码往往能跑起来,但画面会花、声音会漂、音画会不同步,甚至出现无法定位的偶发卡顿。
一、编解码的本质:压缩与还原
原始音视频数据量极大。以 1080p、30fps、YUV420 为例,一帧约 3MB,一秒就是 90MB。编码器的作用,就是利用空间冗余、时间冗余、视觉/听觉心理模型,把数据压缩到原来的几十分之一甚至几百分之一;解码器则负责把压缩码流还原成可供渲染或播放的原始帧。
编码分为帧内编码与帧间编码。帧内编码(I 帧)只利用当前帧内部信息,可独立解码;帧间编码(P/B 帧)依赖前后参考帧,压缩率高但不能独立解码。这就引出了 GOP(Group of Pictures)概念:一个 I 帧到下一个 I 帧之前的所有帧构成一个 GOP。GOP 越长,压缩率越高,但随机访问和抗丢包能力越弱。
二、常见音视频格式与容器、码流的关系
很多人把“MP4”“H.264”“AAC”混为一谈,其实它们分属不同层次:
- 编码格式(Codec):描述如何压缩,如 H.264/AVC、H.265/HEVC、VP9、AV1、AAC、Opus。
- 容器格式(Container):描述如何把视频码流、音频码流、字幕、元数据组织在一个文件中,如 MP4、MKV、TS、WebM。
- 码流(Bitstream/Elementary Stream):编码器直接输出的裸数据,如 .h264、.aac,不含封装信息。
一个 MP4 文件里可能同时包含 H.264 视频轨和 AAC 音频轨;一个 MKV 里也可能装 H.265 和 Opus。容器负责索引、时间戳、轨道关系,码流负责实际压缩数据。MediaCodec 的输入输出是码流级别的,而 MediaExtractor/MediaMuxer 负责容器级别的读写。
三、帧、时间戳与音画同步
视频由一帧帧图像组成,音频由一段段采样组成。要让它们同步播放,必须依赖时间戳。Android 中常见两个时间概念:
- PTS(Presentation Time Stamp):这一帧应该被显示或播放的时刻,单位通常是微秒(μs)。
- DTS(Decode Time Stamp):这一帧应该被送入解码器的时刻。存在 B 帧时,DTS 与 PTS 顺序不同。
MediaCodec 的输入 buffer 需要携带 PTS,输出 buffer 也会带 PTS。播放器根据 PTS 决定何时渲染。如果 PTS 错乱,就会出现音画不同步、画面跳跃或声音忽快忽慢。音频通常按采样率推进,视频按帧率推进,二者通过系统时钟对齐。
// 典型视频帧时间戳计算(微秒)
frameIndex = 0, 1, 2, ...
ptsUs = frameIndex * 1_000_000 / fps
// 例:30fps
frame 0 -> 0 us
frame 1 -> 33333 us
frame 2 -> 66666 us
frame 3 -> 100000 us
四、常见应用场景与参数取舍
不同场景对编码格式、分辨率、码率、GOP、延迟的要求差异很大。下面用表格对比典型场景:
| 场景 | 常见编码 | 容器 | 关键诉求 | 注意事项 |
|---|---|---|---|---|
| 短视频录制 | H.264 / H.265 | MP4 | 画质与体积平衡 | GOP 适中,支持快速预览 |
| 实时直播 | H.264 / AV1 | TS / FLV | 低延迟、抗丢包 | GOP 短,常开 B 帧需谨慎 |
| 视频通话 | H.264 / VP8 | RTP | 超低延迟 | 通常关闭 B 帧,码率自适应 |
| 本地播放 | H.265 / AV1 | MKV / MP4 | 高压缩比、高画质 | 需考虑硬解兼容性 |
| 音频播放 | AAC / Opus | MP4 / OGG | 低码率、低延迟 | 采样率与声道数需匹配 |
五、常见误区与复盘
误区二:忽略 PTS 单位。MediaCodec 使用微秒,而某些网络协议使用毫秒或 90kHz 时钟,混用会导致严重同步偏差。
误区三:认为所有帧都能独立解码。只有 I 帧可以,P/B 帧依赖参考帧,丢帧后必须等待下一个 I 帧才能恢复。
误区四:在低延迟场景使用长 GOP 和 B 帧,导致首帧等待时间过长、互动体验差。
理解这些基础概念后,再去看 MediaCodec 的 configure、queueInputBuffer、dequeueOutputBuffer,就能明白每个参数背后的含义:format 描述的是码流格式,PTS 描述的是时间关系,flags 描述的是关键帧与结束帧。Codec2 作为新一代编解码框架,同样围绕这些核心概念构建组件与数据流。基础越清晰,适配越稳定。