WRITING / 2026.09.23

理解音视频:编码、封装、码率与时间戳

从一个可生成的测试视频出发,区分编码、封装和传输,理解码率、GOP、PTS/DTS 与音画同步,并用 ffprobe 建立流媒体排障的第一组证据。

“这个 MP4 为什么不能播放?”听起来是一个文件问题,实际可能涉及编码格式、封装结构、时间戳、解码能力和下载方式。如果只按扩展名判断,后端很容易给出一个存在、能下载,却无法被目标设备解码的结果。理解流媒体,第一步是知道自己正在观察哪一层。

本系列面向熟悉 Java 和 HTTP 的后端工程师,用同一份测试素材走过媒体探测、直播传输、协议选择、转码、播放体验和故障恢复。实验不要求摄像头或真实直播账号,输入画面和声音都由 FFmpeg 生成。真实服务中的鉴权、容量和地域部署会在对应章节单独讨论。

这六篇怎样连起来

这一篇建立媒体数据的基本认识;第二篇跑通推流到播放;第三篇讨论协议选择;第四篇生成多清晰度视频;第五篇解释卡顿;第六篇处理断流和重复任务。完整示例、版本与执行步骤见实验说明

学习时最好保留每一步产物。探测报告用于判断输入,播放清单用于判断输出组织方式,播放器事件用于判断体验。它们回答不同的问题,不应被一个“任务成功”的日志替代。后面会不断沿着这三类证据交叉核对。

编码、封装与传输分别负责什么

摄像头产生图像帧,麦克风产生音频采样。未经压缩的数据量很大,编码器利用图像的空间冗余、相邻画面的时间冗余或音频特征,把它们变成压缩后的码流。H.264 是视频编码规范,AAC 是音频编码格式,它们决定怎样表达和恢复媒体内容。

封装负责组织这些码流。MP4、Matroska、FLV 可以包含视频、音频及其时间信息,有的还包含字幕和索引。一个 MP4 可能装 H.264,也可能装另一种编码;“支持 MP4”不足以证明支持里面的所有编码、档次、位深和声道组合。

传输再决定内容怎样从一端到达另一端。RTMP 负责持续推送媒体消息,HLS 通过清单和 HTTP 媒体分片组织播放。不能把 H.264、MP4 和 HLS 放在同一列比较“谁更清晰”:一个描述压缩,一个描述组织,一个描述交付方式。工程上的能力判断必须同时看这几层。

原始帧经过编码成为码流,再由封装和传输送到解码器

图中的每次转换都应有明确的输入输出。换封装可以不重新编码,换分辨率通常需要解码、缩放和再编码。媒体服务器转发一条流,也不意味着它自动把编码转换成所有浏览器都能播放的形式。这一边界在 WebRTC 与 HLS 互转时尤其重要。

分辨率、帧率和码率怎样一起影响体验

分辨率是每帧包含多少像素,帧率是单位时间呈现多少帧,码率是单位时间消耗多少编码数据。它们有关联,但不能互相推出。两段同为 720p、30 fps 的视频,因为运动复杂度、编码器和参数不同,可以有不同的码率和主观质量。

静态讲课画面通常比快速运动场景更容易压缩。在固定码率下,后者可能出现块状失真;在固定质量模式下,后者可能使用更多数据。因此业务需要先定义“可接受的画质和带宽”,再选择编码策略,不能把某个清晰度名称直接等同于质量承诺。

码率也不是每个瞬间都恒定。平均码率限制长期数据量,峰值约束影响缓冲和网络承受能力。即使平均下载速度够用,连续几个大关键帧也可能让短窗口下载赶不上播放。分析卡顿时,需要观察分片大小和下载时间,而不是只看整段文件的平均值。

举例说,视频目标码率为 1,200 kbps,音频还有独立码率,封装和网络也有开销。容量估算必须把这些相加,再结合并发观看人数、缓存命中和实际带宽计费口径。这里的乘法是预算模型,真实成本仍要用服务商账单和业务流量校准。

关键帧与 GOP 为什么影响首帧和切片

视频编码不一定独立保存每一帧。部分帧可以引用其他帧来减少重复信息,解码时就必须具备相应参考数据。通常所说的关键帧,是进入解码链路的重要入口;实际能否独立随机访问,还与编码器生成的随机访问点、参考结构以及参数集有关。

GOP 描述一组图像的编码组织方式。关键帧间隔较长,可能减少某些冗余,却让随机进入或恢复等待更久;间隔较短,通常增加随机访问机会,也可能增加码率。它不是“越小越好”的开关,应该与直播延迟、清晰度切换和编码成本一起决定。

本实验使用固定 30 fps 和每 60 帧的关键帧间隔,目标是约每两秒提供一个关键帧。HLS 分片目标为四秒,切片点就可以落在相应边界。三个清晰度使用同一条输入时间线和相同关键帧约束,有利于播放器在切换时接续画面。

这里的关键字是“目标”和“验证”。可变帧率、场景切换、时间戳异常及编码配置都会影响实际输出。不能看到命令中出现了 -g 60,就直接在清单里宣称所有分片独立可解码。后续实验会同时检查分片首帧、时长和完整解码结果。

PTS 与 DTS:播放顺序不总是解码顺序

PTS 表示呈现时间,DTS 表示解码时间。含双向预测等参考结构的视频中,解码器可能需要先得到后面显示的参考画面,因此编码包的解码顺序与最终呈现顺序可能不同。按文件包顺序查看 PTS 时出现回退,并不自动意味着文件损坏。

时间戳还带有时间基。一个整数时间戳只有乘上对应时间基,才成为秒或其他统一时间单位。不同轨道可以使用不同的时间基,封装转换也可能重新量化时间戳。跨组件比较时,应先转换为统一单位,再考虑起始偏移,不能直接比较整数大小。

音频按采样时钟推进,视频按帧的时间推进。音画同步需要把两条轨道映射到一致的播放时间轴。开始时间不同、采样不连续、时钟漂移和错误的时间基换算,都可能造成声音领先或落后。仅确认音视频轨道都存在,还不足以证明它们同步。

应根据数据层次判断“单调”:同一连续解码序列中观察 DTS 是否合理;按呈现顺序观察画面时间是否推进;遇到明确的流切换或 discontinuity,要结合新的时间线解释。把所有输入 PTS 强行改成单调递增,可能破坏原本正确的帧重排。

同样,丢帧不等于必须补成完全均匀的帧率。实时系统可能选择丢弃过期视频以追赶当前时刻,录制任务则可能优先保持时间跨度。应先明确业务要保留真实时间还是生成固定帧率输出,然后决定补帧、丢帧或音频重采样策略。

实验:生成并探测一份已知输入

下载配套脚本,在独立目录执行。脚本生成六十秒测试图和正弦音,输出原始 MP4、转封装 MKV、三个 HLS 档位和探测报告。完整转码命令留到第四篇分析,这里先观察输入。本文基线为 FFmpeg/ffprobe 7.1,更多环境信息在实验说明中列出。

bash generate.sh "$PWD/output"
ffprobe -v error -show_streams -show_format -of json output/source.mp4
ffprobe -v error -select_streams v:0 -show_frames \
  -show_entries frame=key_frame,pict_type,best_effort_timestamp_time \
  -of csv output/source.mp4
ffprobe -v error -select_streams v:0 -show_packets \
  -show_entries packet=pts_time,dts_time,flags -of csv output/source.mp4

第一条报告用于核对编码、像素格式、宽高、采样率、轨道数及容器信息。帧报告用于定位关键帧和呈现时间,包报告用于观察解码与呈现时间的关系。不要把 r_frame_rate 当作任何输入都适用的实际平均帧率;先结合 avg_frame_rate 和帧时间分布判断。

这个输入的优点是参数明确,排除了摄像设备、授权素材和不确定源站。缺点是它不能代表所有内容复杂度,也不能用于宣称商业编码质量更好。测试图适合检查结构和处理链路;主观画质或压缩收益需要另选具有代表性的内容集合。

转封装与重新编码:用证据区分变化

脚本将 MP4 中的轨道以 -c copy 写入 MKV。这一步不经过解码和编码,输出容器、索引或时间基可以变化,压缩媒体内容则应保留。两个文件的整体 SHA-256 通常不同,所以“文件哈希不同”无法证明发生了转码。

比较时可以先核对轨道和时长,再对解码后的帧计算摘要;必要时进一步比较编码包。不同封装对比特流表示和参数集保存方式的处理可能不同,包字节比较也需要解释上下文。实验报告采用完整解码及轨道检查,避免仅凭扩展名下结论。

ffmpeg -v error -i output/source.mp4 -map 0:v:0 -f hash -hash sha256 -
ffmpeg -v error -i output/remux.mkv -map 0:v:0 -f hash -hash sha256 -
ffmpeg -v error -i output/source.mp4 -map 0 -f null -

最后一条命令实际解码所有选中轨道到空输出,比只读取文件头更接近验证可用性。但它仍不等于目标手机一定能播放:软件解码器的能力可能高于某台设备的硬件解码器,浏览器支持和内存限制也需要单独检查。

识别时间问题时保留原始证据

排查时建议同时保存原文件探测报告和经过处理后的报告,标记工具版本及输入摘要。只保存最终截图,会丢失轨道起点、包顺序和时间基等信息;只保存日志里的“修复成功”,也无法解释程序到底重写了什么。遇到异常输入,应先制作最短可复现样例,再验证修复有没有改变声音或画面节奏。

容器声明的时长、最后一帧的呈现时间和实际完整解码的跨度也可能略有差异。判断异常时要说明采用哪一种口径,特别是比较不同封装或音视频长度时。先统一证据和单位,才能避免在不同层面反复讨论同一个“时长不一致”。

从媒体属性回到后端契约

如果要设计一个转码接口,不应只接收“转成 MP4”。至少要表达目标编码、分辨率约束、音频策略、封装类型和兼容性要求;服务端再把允许的组合映射到固定配置。把任意 FFmpeg 参数直接开放给调用方,会让行为、资源和安全边界都难以管理。

探测结果也不是绝对可信的用户输入验证。文件可以截断、损坏或包含异常的声明值。实际处理需要超时、资源上限和解码校验,不能因为探测命令返回成功就无限制地启动高成本任务。探测、处理和发布应是三个可分别失败的阶段。

一个实用的排障顺序是:先确认文件是否完整,再确认轨道与编码,再观察时间戳和关键帧,最后进入网络与播放器。若输入本身没有可用视频轨道,增加 CDN 节点毫无帮助;若输入正常但目标终端不支持编码,则应修正输出能力协商。

完成本篇后,读者应能回答三个问题:这份文件包含什么媒体;哪些转换真正修改了编码内容;时间信息是否足以支持后续播放。带着这三份证据,下一篇再把文件变成持续推进的直播流。

可下载完整实验包,按说明在本地复现。

参考资料

流媒体工程:从音视频基础到可靠直播系统

  1. 理解音视频:编码、封装、码率与时间戳 · 当前文章
  2. 一条直播流是怎样到达用户的:从 RTMP 推流到 HLS 播放
  3. WebRTC、HLS 与 LL-HLS:流媒体协议该如何选择
  4. 从 FFmpeg 命令到转码服务:多清晰度视频如何生成
  5. 为什么视频会卡顿:自适应码率、播放器缓冲与 CDN
  6. 构建可靠的直播系统:转码、故障恢复与可观测性

按时间浏览