简介ISO/IEC 13818-1:2019 是国际标准化组织与国际电工委员会联合发布的 MPEG-2 系统层国际标准面向音视频编码、多媒体传输及协议开发相关技术人员。该 PDF 包含完整英文版 305 页正文系统阐述系统架构、编码格式、音频与视频信号处理、同步与时序控制等核心规范并涉及专利权与商标权问题可作为多媒体系统设计与协议核对的权威依据。资源包内共 1 个文件类型为 PDF压缩后约 20.93 MB该版本为第七版正式文本整合了 2018 年修正案替代旧版内容。目前已有 328 人学习下载适合需要查阅标准原文、开展编码器/解码器调试或完成科研引用的工程师与研究者使用。无论是验证传输流语法还是理解系统时钟与同步机制这份完整版标准都能提供直接、详实的条款支撑。1. 为什么搞流媒体的人绕不开 ISO/IEC 13818-12019 这份 305 页 PDF上次接手一个 IPTV 直播流黑屏的工单业务层日志全正常我用 ffprobe 按 PID 逐个看发现视频 PES 的 PTS 字段解析出来一塌糊涂。翻出 ISO/IEC 13818-12019 一对照定位到是某个透传设备把 PES_header_data_length 改写了。所以对做封装、播放器、数字电视和 OTT 的工程师来说MPEG-2 Systems 这份标准不是拿来背的是拿来查的。它由 ISO 与 IEC 联合发布第七版2019与 ITU-T H.222.0 是同一文本定义了传输流TS、节目流PS、PES 封装、PSI/SI 表、时间戳与缓冲模型。这份 PDF 适合码流分析、协议解析、播放器兼容性调试的人反复查阅。2. TS 和 PS 怎么选从多节目复用到准无损环境的两条路线2.1 系统层管什么压缩层之外的公共骨架ISO/IEC 13818-1 这部分的职责边界很清晰视频压缩编码归 Part 2音频归 Part 3这里只定义系统层。系统层把视频、音频、字幕、私有数据打包成可复用、可同步、可缓冲的单一码流。摘要里提到的六个基本功能——多路压缩流解码同步、交织成单流、解码启动缓冲初始化、连续缓冲管理、时间标识、复用与信令——全部落在系统层。注意一个细节标准支持承载的编码格式远不止 MPEG-2 视频。从摘要正文可以看到它明确列了 ISO/IEC 14496-2、H.264/14496-10、H.265/23008-2、JPEG 2000/15444-1 Annex M音频侧有 13818-7、14496-3。这就是为什么现在很多 DVB 流里跑 H.265 仍然用 MPEG-2 TS 封装——系统层和压缩层是解耦的TS 只是容器。解码器拿到 TS 流之后的动作是固定的先找同步字节再解析 PSI 表建立 PID 与节目元素的映射然后按时间戳送显。压缩层怎么解码不是这层管的事但打包格式不对压缩层再厉害也解不出像样的画面。2.2 都叫 MPEG-2 系统层TS 和 PS 差在哪TSTransport Stream和 PSProgram Stream是系统层提供的两种复用形式。标准原文对两者的定位说得很直白PS 适用于几乎无错环境支持软件处理TS 更适用于容易出错的传输环境。从结构上拆TS 是固定长度 188 字节的包每个包 4 字节头加负载包头里有 PID、adaptation_field_control、continuity_counter 等字段PS 是变长包以 pack_start_code 0x000001BA 起始内部嵌 system_header、PES 和描述符。前者天生抗丢包和误码后者依赖信道干净换来的好处是结构简单、适合文件存储和本地播放。多节目复用能力是两者最大的分水岭。TS 能把多个节目、多个独立时基的流复用在一条物理通道里每个节目有自己的 PMT 和 PCRPS 只能承载单一时基下的一个节目内容。这是选型时首先要明确的边界。对比项Transport StreamProgram Stream包结构固定 188 字节变长 pack适用环境有误码、易丢包几乎无错多节目复用支持独立时基不支持典型应用DVB/ATSC/ISDB、OTT 直播、TS over RTPDVD/VOB、本地文件、部分监控录像起播方式依赖 PAT/PMT 建立映射依赖 pack 头和 system_header2.3 工程选型的实际判断我自己做选型时一般就三个问题链路干净吗要复几路需要随机访问吗信道是广播网、IPTV 组播或公网 RTP一律 TS别犹豫。188 字节定长包让丢包检测、FEC 前向纠错、PID 过滤都很好做。如果是做蓝光、DVD 这类文件型产品或者录制归档PS 更合适文件随机访问友好结构开销也比 TS 头小得多。还有一种混合场景编码器输出 PS 到本地传输前转成 TS标准第 2.8 节专门讲了与 ISO/IEC 11172 的兼容就是考虑这类环节。2.4 七版演进透露的行业信号这份第七版替代了 2018 年第六版同时纳入了 2018/Amd.1 修正案。从 1995 年 H.222.0 首版到现在版本史里能看到行业需求的迭代2000 年前后补 MPEG-4 数据承载2006 年前后补 DASH 的参考2014 年后持续加 H.265 和元数据承载。判断一个封装协议有没有生命力看它有没有在不断吸纳新编码格式就够了——MPEG-2 TS 至今还是数字电视事实标准不是因为老而是因为扩展机制设计得够稳。3. 从 PES 到 PAT/PMT逐字节解析节目映射关系3.1 PES压缩码流进入系统层的第一个包装PES 是压缩流和复用层之间的中间容器。标准规定PES 由 packet_start_code_prefix3 字节 0x000001、stream_id1 字节和 PES_packet_length 起始。stream_id 的高位有明确分工0xC0~0xDF 是音频0xE0~0xEF 是视频还有其他值对应私有流、字幕等。只看 stream_id 就能判断 PID 里装的是什么类型。PES 头里值得注意的字段PTS_DTS_flags 决定有没有 PTS 和 DTSPES_header_data_length 给出扩展头长度data_alignment_indicator 表示负载是否从访问单元起点开始。很多解析器翻车就是把 PES_header_data_length 当成固定值遇到视频流 PES_packet_length 为 0 的情况直接算错偏移。PES 不是 TS 专属PS 里同样有。区别在于承载方式TS 把 PES 切成 184 字节以下的块装进 TS 包PS 把 PES 放进 pack 负载。所以解析 PES 的逻辑应该独立于外层传输单独封装成函数。3.2 PAT 和 PMT节目与 PID 的两级映射表TS 的解码入口是 PATProgram Association Table固定 PID 0。PAT 本身是一张 section每个表项把 program_number 映射到 PMT 的 PID。拿到 PAT 之后再去对应 PID 上收 PMTProgram Map TablePMT 里才列出真正的音视频 PID。所以链路是 PAT → PMT → 音视频 PID一级比一级具体。PMT 的 table_id 是 0x02里面包含 PCR_PID 字段——这个字段定义了该节目 PCR 所在的 PID通常指向视频流但不强制。随后是 program descriptor 循环和 elementary stream 循环每个流用 stream_type 标明编码格式比如 0x1B 是 H.2640x24 是 H.2650x0F 是 AAC。排查黑屏时先确认这三层映射有没有断开比追业务日志快得多。3.3 用 Python 快速验证 PAT/PMT 解析下面这段脚本从 TS 文件里抽 PAT 并打印字段#!/usr/bin/env python3 # 解析 TS 文件中的 PAT 表项确认节目到 PMT PID 的映射 import sys TS_PACKET 188 SYNC 0x47 def read_section(payload): # 第一个字节是 pointer_field跳过它到达 section 起点 offset 1 payload[0] if payload[offset] ! 0x00: # PAT 的 table_id 固定为 0x00 return # section_length 只占低 10 位注意屏蔽高 4 位 sec_len ((payload[offset 1] 0x0F) 8) | payload[offset 2] if sec_len 0: return print(fsection_length{sec_len}) i offset 8 # 跳过 section 头部的固定字段 end offset 3 sec_len - 4 # 末尾 4 字节是 CRC while i end: prog_num (payload[i] 8) | payload[i 1] if prog_num 0: # program_number0 指向 NIT属于网络信息表 nit_pid ((payload[i 2] 0x1F) 8) | payload[i 3] print(fNIT - PID0x{nit_pid:X}) else: pmt_pid ((payload[i 2] 0x1F) 8) | payload[i 3] print(fprogram_number{prog_num} - PMT PID0x{pmt_pid:X}) i 4 def main(ts_path): with open(ts_path, rb) as f: while True: packet f.read(TS_PACKET) if len(packet) TS_PACKET: break if packet[0] ! SYNC: continue pid ((packet[1] 0x1F) 8) | packet[2] if pid ! 0: # 只收 PID0 的 PAT continue # 从 TS 包头第 4 字节取 adaptation_field_control afc (packet[3] 4) 0x03 if afc 2: # 只有 adaptation field没有负载 continue offset 4 if afc 3: # 头部有 adaptation field需要跳过 offset 1 packet[4] read_section(packet[offset:]) break if __name__ __main__: main(sys.argv[1])逻辑说明TS 包头的第 1 字节必须是 0x47 同步字PID 由第 2 字节低 5 位和第 3 字节组合而成。PAT 包的 PUSIpayload_unit_start_indicator为 1所以负载第一个字节是 pointer_field指向 section 实际起点。PAT 的每个表项固定 4 字节前两字节是 program_number后两字节是 PMT PID。这个脚本只处理第一个 PAT section实际产品里还要遍历所有 section 和 version 变化。3.4 PMT 解析的扩展PAT 能定位到 PMT PID 之后PMT 自身的解析结构是table_id0x02紧接着是 section_length之后有 program_number、version_number、PCR_PID然后是描述符循环长度和流循环。每个流条目也是 4 字节起步stream_type1 字节、elementary_PID2 字节、ES_info_length1 字节后面跟该流的描述符。解剖 PMT 最有价值的两个点一是 PCR_PID 与视频 PID 是否一致如果指向了音频 PID很多解码器会行为异常二是 stream_type 与 PID 里的实际编码格式是否匹配转码后 PID 没更新是常见事故。4. 时钟与缓冲模型PCR、PTS/DTS 与 T-STD 的配合边界4.1 PCR解码端时基恢复的锚点PCRProgram Clock Reference是 27MHz 时钟的采样值由 33 位 base 和 9 位 extension 组成base 的单位是 90kHzextension 是 27MHz 的余数。按标准公式PCR base * 300 extension换算到 27MHz 刻度。解码端靠 PCR 恢复系统时钟音视频的 PTS/DTS 都以这个时钟为基准PCR 抖动直接表现为音画不同步。PCR 在 TS 里的位置不固定它藏在 adaptation_field 里。adaptation_field_control2 或 3 且 PCR_flag 置位时adaptation 字段的第 1 字节起连续 6 字节是 PCR。提取逻辑如下# 假设 af 是从 TS 包 adaptation_field 起点开始的数据 # af[0] 是 flag 字节bit4 是 PCR_flag if af[0] 0x10: # PCR_base 占 33 位PCR_ext 占 9 位 pcr_base (af[1] 25) | (af[2] 17) | (af[3] 9) | (af[4] 1) | (af[5] 7) pcr_ext ((af[5] 0x01) 8) | af[6] pcr_27mhz pcr_base * 300 pcr_ext print(fPCR{pcr_27mhz} ({pcr_base}.{pcr_ext}))参数说明af[0] 的 bit4 对应 PCR_flag置 1 才说明 PCR 字段存在。PCR_base 从 af[1] 开始跨 4.5 字节af[5] 的最高位是 base 的最后一位最低位是 extension 的第一位af[6] 是 extension 的低 8 位。这个位级操作没法偷懒很多解析器就是在这少移了一位算出来的 PCR 差 300 倍。4.2 PTS 和 DTS两套时间戳解决视频重排问题PTS 是显示时间戳Presentation Time StampDTS 是解码时间戳Decoding Time Stamp。没有 B 帧的时候两者相等有 B 帧时编码顺序和显示顺序不一致DTS 先于 PTS解码器必须同时拿到两套时间才能正确重排。音频只有 PTS且通常和 DTS 一致。PTS/DTS 都是 33 位单位是 90kHz封装在 PES 头里由 PTS_DTS_flags 控制为 2 时只有 PTS为 3 时 PTS 和 DTS 都有。提取 5 字节的位操作如下# b 是 PES 头里 5 字节的 pts 字段要从 0011 前缀之后开始取值 pts (((b[0] 1) 0x07) 30) | (b[1] 22) | ((b[2] 1) 15) | (b[3] 7) | (b[4] 1) # 单位是 90kHz转换成秒 pts_sec pts / 90000.0 print(fPTS{pts} ({pts_sec:.3f}s))注意 b[0] 的高 4 位是固定标识后 3 位是 PTS 最高有效位b[2] 和 b[4] 的最低 1 位是 marker_bit取值前必须右移丢弃。PTS 缺失时播放器只能按到达顺序渲染遇到 B 帧序列就会花屏或卡顿这是判断播放器兼容性问题时的首要排查点。4.3 T-STD 缓冲模型系统目标解码器不是玄学T-STDTransport Stream System Target Decoder是标准定义的系统目标解码器模型描述 TS 流进入解码器后经过各级缓冲的约束。它规定解码器必须有传输缓冲、复用缓冲、主缓冲等层级并对每个层级的大小和输入速率做了限制。为什么 PCR 间隔不能太大为什么码率抖动不能太夸张都能从 T-STD 的缓冲模型推出答案。实际工程里T-STD 的约束往往由复用器来保证。复用器插入 PCR、切分 PES 到 TS 包时必须保证缓冲不溢出、不读空。标准第 2.4 节的可复用流语义部分就是干这个的。自己做码流分析时T-STD 不需完整实现但要知道缓冲模型的存在——它解释了为什么同样的视频有的复用器在小缓冲设备上卡顿有的没事。5. 避坑手册解析 MPEG-2 Systems 码流的五个常见翻车现场5.1 continuity_counter 连续跳变先分清丢包还是重复包现象播放过程中偶发卡顿、马赛克按 PID 统计发现 continuity_counterCC不连续。原因CC 是 4 位计数器0~15 循环每个 PID 独立计数。网络抖动或解码器缓冲溢出会导致丢包但也有网络重传机制会让同一个包重复到达CC 呈现相邻递增但负载相同的形态。解决不要见到 CC 不连续就报丢包。先比对负载内容如果连续两个包的 PID、CC 相同且负载完全相同这是重复包按规范应丢弃后者如果 CC 跳变且负载不同才是真丢包。tsduck 的 continuity 分析器会分别统计这两类建议先跑一遍再下结论。5.2 PCR 间隔超标音画慢慢偏掉的头号元凶现象直播流长时间运行后音画逐渐不同步轻微到几乎察觉不到但过一小时就明显。原因PCR 插入间隔太长解码端 27MHz 时钟校正不及时时间戳累积漂移。PCR 间隔问题很难在短时间测试里暴露属于典型的“跑久了才翻车”的坑。解决工程上常见做法是 PCR 间隔控制在 40ms 以内最坏不要超过 100ms这是 DVB 和 ATSC 的通行建议。检查复用器配置里 PCR_PID 的插入策略保证每个节目每 40ms 至少有一个携带 PCR 的包。5.3 PMT 里的 PCR_PID 写错解码器黑屏或反复尝试现象流能识别PID 也能看到但解码器一直黑屏日志里反复出现“waiting for PTS”或“PCR not found”。原因PMT 里 PCR_PID 指向了不存在的 PID或者错误指向了音频 PID。解码器找不到有效 PCR 就无法恢复系统时钟PTS/DTS 失去参照进入等待状态。解决逐层核对 PAT → PMT → PCR_PID。最直接的办法是解析 PMT 后打印全部字段确认 PCR_PID 和视频 PID 是否一致以及该 PID 上是否真的有带 PCR 的包。曾有项目因为复用器配置界面把 PCR 来源选成音频黑屏折腾了两天最后就是一行配置的问题。5.4 PES_packet_length 与真实负载不符ffprobe 报错的来源现象ffprobe 显示 invalid PES header 或 duration 抖动但流能播放。原因标准规定视频流 PES_packet_length 可以置 0表示长度由后续 TS 包边界决定某些工具或中间设备会把该字段改写成实际值一旦透传或切包不精确长度就和负载对不上。解决解析 TS 里的 PES 时不要依赖 PES_packet_length 判断负载边界要依靠 TS 包的 PUSI 标志识别 PES 起点。视频 PES 长度字段置 0 是合法的把它当错误报出来反而是解析器自己的问题。5.5 PS 流的 pack 头和 system_header 缺失文件播放器直接罢工现象拿到一个 .mpg 或 .vob 文件播放器打不开部分播放器黑屏有声或弹格式不支持。原因PS 流以 pack_start_code 0x000001BA 为起始system_header 的起始码是 0x000001BB。有些工具从 TS 抽取裸流后直接改了扩展名PS 结构根本不存在。解决先检查文件头部是否有 0x000001BA。缺失的话不能硬改扩展名需要重新用封装工具按 PS 标准生成。PS 的 PES 对 PTS 的要求也比 TS 严格很多播放器遇到 PTS 缺失会拒绝渲染这是做 DVD 兼容时要额外注意的点。6. 把排查沉淀成习惯tsduck 与 ffprobe 的配合6.1 两个命令的定位差异ffprobe 适合看单条流的时间戳和帧信息tsduck 适合看容器层级的完整结构与连续性问题。排查 MPEG-2 Systems 问题时我一般先用 tsduck 验证容器健康度再用 ffprobe 深入看视频流内部# 检查 PID 统计、continuity_counter 连续率和 PAT/PMT 结构 tsp -I file input.ts -P continuity -P pcr -O drop # 用 ffprobe 查看视频流的 PTS/DTS 明细 ffprobe -show_packets -select_streams v input.ts其中-P continuity输出每个 PID 的 CC 跳变统计和重复包计数-P pcr分析 PCR 间隔最大值、最小值和抖动范围。两条命令跑完容器层有没有问题基本就能定性。6.2 固定阈值形成基线排查了这么多年我把几条硬指标设成了默认基线PCR 间隔不超过 100msPTS 差值不超过 0.5sCC 不连续率按节目不超过 1%。每个新项目启动前先跑一遍把输出保存下来后续出问题直接对比基线。这方法治标也治本——流媒体问题大多是性价比高的“玄学”其实是基线没建立。从那以后我每次处理黑屏、音画不同步的工单都强制走一遍容器层初检再碰业务层极少再在协议层浪费时间。这份 ISO/IEC 13818-12019 标准值得每个做音视频封装的工程师存一份在本地排查时逐字段核对。希望帮到你。本文还有配套的精品资源点击获取