01. 媒体链路与时间戳
一段视频从摄像头到屏幕,至少经过采集、压缩、封装、传输、解封装、解码和显示。音频也有类似路径,只是输入通常是 PCM,输出还要经过混音、DAC 和功放。先把这些对象分开,才能判断“文件打不开”究竟是容器、编码还是播放器能力问题。
容器、码流与帧
容器保存轨道、元数据、时间戳和压缩数据包。MP4、MKV、FLV 都是容器;它们里面可以装不同的视频和音频编码。H.264、H.265、AAC 描述的是压缩码流,不是文件扩展名。一个叫 video.mp4 的文件可能因为编码 Profile、音频轨或封装布局不被目标播放器支持。
播放器通常按下面的顺序工作:
- 从文件、网络或设备读取字节。
- 由 demuxer 解封装,发现轨道并取出 packet。
- 把 packet 送给对应的音频或视频 decoder,得到 PCM 或原始图像帧。
- 音频进入混音和播放设备,视频进入颜色转换、渲染和显示时钟。
网络播放在这条链上还要加入缓冲、丢包恢复、抖动处理和时钟校正。某一步没有输出时,先查看这一步的输入和计数,不要直接换播放器。
PTS、DTS 与 time base
DTS(解码时间戳)表示 packet 进入解码器的顺序,PTS(显示时间戳)表示解码后的帧应该何时呈现。含 B 帧的视频可能需要先解码后面的参考帧,所以 DTS 与 PTS 不同。时间戳不是固定的毫秒整数,而是结合 time_base 解释的整数刻度。
例如某轨道的 time_base 是 1/90000,一个 PTS 为 4500 的帧表示 4500 / 90000 = 0.05 秒。把不同轨道的整数时间戳直接相减,会把音频和视频的时间基混在一起,得到错误的同步偏差。
排查音画不同步时,分别记录:输入 packet 的 PTS/DTS、解码帧的 PTS、进入播放队列的时间和实际渲染时间。若问题只在网络播放出现,再加入网络到达时间和缓冲深度。
转封装与转码
转封装(remux)只换容器,尽量原样复制压缩 packet。它通常很快,不会再次产生有损压缩,但目标容器必须支持原有的编码、参数和时间戳。FFmpeg 的 -c copy 表示不重新编码,不是“任何格式都能转换”。
转码(transcode)要先解码,再用新编码器压缩,最后写入目标容器。它能改变 H.264 为 H.265、改变分辨率或码率,但会增加 CPU/GPU 负载,也可能损失画质。修改扩展名不会完成转码。
先用 ffprobe 建立事实
在嵌入式设备上复现前,先在开发机查看输入:
bash
ffprobe -hide_banner -show_streams -show_format input.mp4重点记录 codec_name、profile、width、height、pix_fmt、r_frame_rate、sample_rate、channels、time_base 和时长。需要观察编码帧顺序时,再执行:
bash
ffprobe -select_streams v:0 \
-show_frames -show_entries frame=pict_type,pts_time,pkt_dts_time \
-of csv input.mp4这些命令只读取文件,不代表目标板的硬件解码器支持同一组参数。部署前仍要用板上的驱动、插件和播放器做验证。
失败定位
| 现象 | 先确认 | 常见边界 |
|---|---|---|
| 文件无法打开 | 容器索引、轨道、编码 Profile | 扩展名正确不代表内部编码可解 |
| 有图无声 | 音频轨、Codec、采样率和声道 | 播放器可能只支持部分 AAC 配置 |
| 音画逐渐错位 | 两条轨道的 time base、时钟漂移 | 只修正起始偏移不能解决漂移 |
| 转封装失败 | 目标容器对 Codec、时间戳和 bitstream filter 的要求 | 必要时退回转码 |
| 解码占满 CPU | 是否真的走硬件后端、颜色转换是否复制 | 先看插件和设备日志,再改参数 |
下一篇把这些对象换成可计算的参数,说明 1080p、NV12、PCM 和 Buffer 各自占多少数据量。
