尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

MPEG-2系统层:ISO/IEC 13818-1 TS流解析与PCR踩坑指南

发布时间:2026/9/29 1:12:21

资讯中心
01
ARTICLE

MPEG-2系统层:ISO/IEC 13818-1 TS流解析与PCR踩坑指南

MPEG-2系统层:ISO/IEC 13818-1 TS流解析与PCR踩坑指南
简介这份PDF为ISO/IEC 13818-1:2019《信息技术——运动图像及其伴音信息的通用编码 第1部分系统》完整英文版标准原文共305页面向从事数字电视、流媒体传输、多媒体系统开发与标准研究的工程师、研究人员及高年级学生。标准系统阐述了MPEG-2系统层的核心规范涵盖系统架构、编码格式、音视频信号处理、同步与时序控制等关键模块并明确了编码器、解码器、系统控制器及存储组件间的交互与传输机制。凭借对时钟同步、节目复用及时戳等细节的权威定义该标准是理解传输流TS与节目流PS格式、实现音视频高质量传输与播放不可或缺的基础参考资料。资源包含1个PDF文件压缩包整体大小约20.93MB便于收藏与查阅。已有326人获取学习适合需要直接对照国际标准原文进行学术研究或工程落地的读者。1. ISO/IEC 13818-1 到底管什么一份不直接编码的编码标准做流媒体开发的同行应该都有过这种时刻拿着一条 DVB 或者 IPTV 的码流文件播放器花屏、音画不同步、搜台只出黑屏用 FFprobe 看半天只看到一堆 PID 和 table_id说不清问题出在哪一层。这时候翻到 ISO/IEC 13818-1 这份标准——正确叫法是 Generic coding of moving pictures and associated audio information: Systems——你才会意识到视频编码本身根本不归它管它管的是更底层、也更折磨人的部分怎么把视频、音频、字幕、私有数据打成包怎么在一条流里交织传输怎么用时间戳让它们解码时重新对齐。这份标准定义了 MPEG-2 Systems也就是我们天天挂在嘴边的 TS 流和 PS 流的全部语法和语义。它适合三类人写播放器或解码器内核的、做码流分析仪或采集网关的、以及被运营商抓去排查直播卡顿的。一个反直觉的结论先放在这2019 年的第七版和 1994 年发布的第一版相比核心语法几乎没有变化——变的是它承载的内容对象H.264、H.265、JPEG 2000、DASH 这些后来者全都被它收编成了允许承载的负载。所以这份文档过期不失效到今天它依然是数字电视、IPTV、安防录像系统里最通用的那条底线。2. 为什么是 2019 第七版版本脉络与 305 页阅读地图2.1 第七版到底是什么替换关系与增补内容ISO/IEC 13818-1 的版本脉络看着乱其实规律很明显。第一版 1994 年发布对应 ITU-T H.222.0。此后每轮大改就是一次换版中间夹着一堆 Amendment 和 Technical Corrigendum。2019 年这一版是第七版它做了一件关键的事直接取消并替换第六版2018 版并且在替换时把 2018 版之后的 Amendment 1:2018 的内容吸收了进来。换句话说你手里如果拿着 2018 版再加一个补丁文件那两份合的集合约等于这一份 305 页的第七版但阅读体验完全不同——标准不是给你做补丁叠加用的合订本才是。第七版的正文开头就写明它由 ITU-T 起草编号为 Rec. ITU-T H.222.0 (08/2018)在 ISO/IEC 这边则走 JTC 1/SC 29 的流程审批。所以你在很多工具和论文里会看到H.222.0和13818-1混用。引用标准时写哪一个都有理但如果要精确到条款号两边页码编排是完全一致的这点不用纠结。版本选择上我的建议很直接能用第七版就别用旧版。原因不是新版多出了多少语法表而是你在和别人对齐问题、截图报 bug 时条款号对不上最浪费生命。旧版里那些 Amendment 的条款编号是插进去的经常出现2.6.5 后面跟着 2.6.5.1 和 2.6.5.2然后跳到 2.6.6这种跳变而合订版已经把编号整理顺了。2.2 305 页的阅读地图先看哪、后看哪、可以跳哪拿到一份 300 多页的英文标准第一反应是硬啃这其实是最差策略。13818-1 的结构是 Section 1 加 Section 2Section 1 只有 Scope 和 Normative references加起来三页不到。真正的主体在 Section 2,里面有定义、语法描述方法、然后是两大块硬骨头2.4 Transport stream bitstream requirements 和 2.5 Program stream bitstream requirements。再往后是 2.6Program and program element descriptors这节是所有 descriptor 的字典篇幅最大但它不是读的是查的。我建议的阅读顺序是这样的先把 2.4 的 TS 部分整体过一遍重点看 TS 包的 188 字节布局、adaptation field 的语义和 PCR 的编码方式然后跳到 2.7 Restrictions on the multiplexed stream semantics这一节讲的是复用约束比如同一节目内视频音频的 PTS 间隔限制这是排查音画不同步的第一现场接着看 2.11 Carriage of ISO/IEC 14496 data也就是 TS 流里怎么封装 H.264/H.265这是现代播放器开发绕不开的部分最后才把 2.6 当作字典随查随用。章节阅读优先级可以按下面这张表规划章节范围内容主题阅读策略Section 1 (1.1-1.2)范围与引用标准通读约 10 分钟2.1-2.3定义、缩写、语法记法第一次可跳过遇到再看2.4TS 流全部语法语义重点精读配合实际码流验证2.5PS 流全部语法语义做 DVD/录制节目才需要细读2.6descriptor 字典按需查询不建议通读2.7复用约束与语义限制排查问题时优先翻这里2.11-2.13承载 H.264/H.265、metadata写播放器必读注意 2.4 里有一个很容易被新手忽略的点TS 包是固定 188 字节但标准允许在特定条件下用 204 字节的变体多出 16 字节 RS 校验比如 DVB-C 里常见。这个在标准正文里写的是transport stream packet may be extended很多人在解析时默认 188遇到 204 长度的文件直接解析错位后文会专门讲这个坑。3. PS/TS/PES 三件套复用与同步的核心机制3.1 Program Stream 与 Transport Stream 的选型逻辑13818-1 的最核心决策就是定义了两条平行的复用流Program Stream节目流和 Transport Stream传输流。两者的底层构建单元都是 PES 包也就是 Packetized Elementary Stream 包区别在于 PES 包之后怎么切、怎么封装。Program Stream 的设计目标是近乎无错误的环境典型的场景是 DVD 光盘。它的特点是把一组共享同一时间基准的 PES 包按顺序组织成一个个 Pack每个 Pack 以 Pack Start Code 开头后面跟着系统头、PES 包、填充数据。PS 流适合软件处理和本地回放因为它的结构是顺序流式的随机访问靠导航包不需要额外的时间刻度和节目表维护。PS 流里没有 PID 这个概念识别流靠 Stream ID——0xE0 开头是视频、0xC0 开头是音频这个设计简单到粗暴。而 Transport Stream 是为错误容易发生的环境设计的也就是有线、卫星、地面广播和 IP 网络。TS 流将 PES 包切成固定 188 字节的小包每个小包带一个 PID通过 PID 来区分它属于哪个节目、哪种流。更重要的是TS 流天然支持多节目复用多个节目可以共享一条 TS每个节目有自己的节目时钟基准接收端通过解码 PSIProgram Specific Information表来发现节目。选型逻辑一句话就能说清做本地文件回放或 DVD 抓轨处理的是 PS 流做直播、广播、安防平台接入处理的是 TS 流。现代从业者面对 99% 的场景都是 TSPS 流通常只出现在老旧的采集设备和 DVD 相关的兼容需求里。标准里定了一条很实际的原则TS 流即使出现丢包或误码接收机也能凭借 PSI 表重新同步而 PS 流一旦出错恢复的成本高得多。3.2 PES 包TS 与 PS 共同的结构单元PES 包是理解整个 13818-1 的钥匙。无论是 PS 还是 TS压缩层的数据也就是真正的 H.264 码流或 AAC 码流都不会直接裸露在系统层里而是先被打包成 PES 包PES 包的负载部分才是编码后的音视频数据。PES 头的核心职责是承载时间信息。PES 头里有四个关键字段值得记住字段长度作用stream_id8 bit标识流类型视频通常 0xE0PTS33 bit解码后呈现时间戳单位是 90 kHzDTS33 bit解码时间戳仅在 B 帧场景下出现PES_header_data_length8 bit后面可选字段的总长度注意 PTS 计数器的单位是 90 kHz也就是每个时间戳增量是 1/90000 秒大约 11.1 微秒。PCR 的时间基准同样用 90 kHz 作为基础频率但 PCR 是 42 bit 的计数器前 33 位以 90 kHz 计后 9 位以 27 MHz 计。这个 27 MHz 和 90 kHz 的比例关系正好 300 倍是很多时序问题的根源——后面排查音画不同步时会反复用到。写解析器时最容易被骗的地方是 PTS 的 33 bit 会有回绕2^33 / 90000 ≈ 26.5 小时如果长时间播放一个直播流时间戳跨过回绕点直接用差值判断先后顺序会得出完全错误的结论。标准里对解码器有明确的约束要求按回绕感知的方式处理 PTS 差值但很多第三方解析工具并没有这么干。你能做的就是在自己的代码里预留这个逻辑下文会给实现。3.3 PSI 表PAT、PMT 与节目的发现机制TS 流的节目信息完全靠 PSI 表来承载。PSI 分四张表PATProgram Association Table、PMTProgram Map Table、CATConditional Access Table和 NIT 等扩展表。PAT 固定分配 PID 0x00它的负载里列着当前 TS 内的所有节目编号和对应的 PMT PID。拿到 PAT 之后根据节目号找到 PMT 的 PID再读 PMTPMT 里才会列出这个节目包含哪些流视频、音频、字幕各在哪个 PID以及 PCR PID 用哪一个。这个三级跳是解析 TS 流最基本的路数。很多新手第一次解析时直接拿一个视频 PID 去抓包却忽略了必须先经过 PAT 和 PMT 才能知道哪个 PID 是视频。更隐蔽的问题是PMT 里的 PCR_PID 字段经常和视频 PID 不一致有的复用器会把 PCR 单独放在一个空流 PID 上或者放在音频 PID 上。如果解析器默认PCR_PID 视频 PID遇到这种流就会在时钟恢复上踩坑。PMT 的 table_id 固定是 0x02section_length 字段指示这个 section 的有效长度。菜单位于 section 内的 program_info_length 之后每个 es_info_length 又描述了每个流对应的 descriptor。descriptor 是标准里留给私有数据的栖息地注册表项通过 registration_descriptor 的 format_identifier 区分归属。13818-1 里明确规定descriptor 里可以带任意私有数据但长度字段必须严格计算否则整个 section 的解析都会错乱。这个长度字段错一位后面全错位的特性是码流解析翻车的第一大原因。4. 把条款变成代码从解析 PID 到调 PCR 参数4.1 用 FFprobe 快速验证一条 TS 流的节目结构标准在手第一步不是写代码是先验证手头这条码流的真实结构。FFprobe 内置了完整的 MPEG-TS demuxer 和对 PSI 表的解析能力用它把 PAT/PMT 打出来是最快的摸底方式。下面这个命令直接列出所有节目和流ffprobe -v error -show_programs -show_streams -select_streams v:0 input.ts逻辑说明-show_programs让 ffprobe 解析并输出 PSI 里的节目信息-show_streams输出每个 PID 的流详情-select_streams v:0只保留第一个视频流以减小输出噪音。跑完你会看到类似program_id1、stream_index0、codec_nameh264、time_base1/90000的输出这些信息直接对应 PMT 里的流条目。参数说明如果希望连 PCR PID 一起打出来可以追加-show_entries programpcr_pid:streampid,codec_name来精确控制输出字段。FFprobe 显示的时间基如果是 1/90000说明 PTS 是按标准走的如果显示 1/1000说明码流里用了非标的时间戳频率这种流在很多播放器里会出现进度条跳动的现象。4.2 手写一个最小 PMT 解析片段FFprobe 能帮你确认码流是好的但毕竟是个黑匣子。真到要排查私有 descriptor 或者诊断复用器问题时还得自己解析。下面是 PMT 解析的核心片段只处理最基本的 section 头但能跑通def parse_pmt(data: bytes) - dict: if data[0] ! 0x02: raise ValueError(fnot a PMT section, table_id{data[0]:#x}) # section_length 是标准字段占 12 bit从第 1 字节的低 4 位开始 section_length ((data[1] 0x0F) 8) | data[2] # 该字段只包含从 program_number 开始的字节数 program_number (data[3] 8) | data[4] version_number (data[5] 1) 0x1F current_next data[5] 0x01 pcr_pid ((data[8] 0x1F) 8) | data[9] program_info_length ((data[10] 0x0F) 8) | data[11] program_info_start 12 program_info_end program_info_start program_info_length # program_info 里是节目级 descriptor跳过 idx program_info_end streams [] while idx 4 len(data) and idx - 3 section_length: stream_type data[idx] elementary_pid ((data[idx 1] 0x1F) 8) | data[idx 2] es_info_length ((data[idx 3] 0x0F) 8) | data[idx 4] streams.append({ stream_type: stream_type, pid: elementary_pid, es_info_length: es_info_length, }) idx 5 es_info_length return { program_number: program_number, version: version_number, current_next: current_next, pcr_pid: pcr_pid, streams: streams, }逻辑说明PMT 的 section 布局是固定的——table_id 占一字节section_length 占两字节但只有 12 bit 有效program_number 两字节接着是 version、current_next、section_number 等字段然后第 8、9 字节才是 PCR_PID。注意 PCR_PID 的取值可能和第一个视频流的 PID 不同这个字段是独立的。解析时把所有流条目循环读完每个条目都是 5 字节头加一段 ES_info 描述符ES_info 的长度由 es_info_length 给出。整个循环的终止条件是位置越过 section_length 的边界。参数说明section_length的掩码处理是这里最容易错的地方——它的高 4 位其实在 data[1] 的低 4 位里所以必须data[1] 0x0F再左移 8 位。如果你直接用data[1] 8会把保留位的值也带进来轻则解析错位重则整个 section 长度直接翻倍。另外version_number是 5 bit 的它在加 1 到 32 时会回绕成 0做 PSI 缓存更新时一定要按回绕处理不然 32 次更新之后你的缓存就永远认为版本没变过。4.3 stream_type 到编码格式的映射表解析 PMT 后拿到的 stream_type 是数字必须映射成编码格式才知道这个流是 H.264 还是 AAC。13818-1 沿用了 13818-1 里注册的 stream_type 值日常开发最常碰到的几个stream_type含义0x01MPEG-1 视频0x02MPEG-2 视频0x0FAAC 音频ADTS0x1BH.264 / AVC 视频0x24HEVC / H.265 视频0x06私有数据PES 里带私有头0x81-0xFF用户私有需结合 registration descriptor这里有个非常关键的坑stream_type 0x06 代表私有数据但很多复用器把字幕、EPG、甚至加密后的音视频都藏在 0x06 里。你光看 stream_type 是分不出来里面是什么的必须再检查 PMT 里对应流条目有没有 registration_descriptor它的 format_identifier 才能告诉你这路流到底属于谁。13818-1 的 2.6 章节里定义了 registration_descriptor 的完整语法这也是我在前面说 descriptor 是字典的原因——它不是用来读的是遇到了问题再去查的。5. 踩坑实录TS 流开发最常翻车的五个现场5.1 PCR 不连续导致解码器反复重启现象播放一条自建的 TS 流画面每隔十几秒卡一下日志里反复出现buffer underflow或者resync。原因PCR 是解码器的时钟基准它的值应当严格单调递增增量与真实时间成正比。如果复用过程中 PCR 被重写、或者两个节目拼接时没有重新生成 PCR解码器会认为时钟发生了跳变于是重新对齐缓冲表现出来就是周期性卡顿。解决用 FFprobe 验证 PCR 连续性。跑一条命令看一眼 PCR 增量是否均匀ffprobe -v error -show_entries packetpts_time,flags input.ts | head -50如果发现 PCR 增量有大幅跳变直接用 TS 复用工具如 mpegtsmux 或自研打包器对整条流重新生成一次 PCR不要试图手工修手工修 42 bit 计数器很容易因为进位处理出错埋下更大的雷。5.2 把 PCR_PID 默认当成视频 PID现象解码器报no valid PCR画面有但声音不同步唇形对不上。原因PMT 里的 PCR_PID 字段独立存在并不保证等于视频 PID。有的复用器为了让音频解码优先建立时钟把 PCR 放在音频流上更有甚者单独开一路空流专门传 PCR。解析器如果写死PCR 第一个视频 PID在遇到合法但不常规的码流时就直接失效。解决按标准走完整流程——先读 PAT再读 PMT取出 PCR_PID 字段后把它单独保存。不要用任何默认值兜底。在解析器里PCR_PID 必须在拿到 PMT 之后显式赋值如果值为 0x1FFF 表示无 PCR这时候要触发告警而不是静默忽略。5.3 加密流里 0x06 私有数据全是密文拿不到 PTS现象抓包分析一个加密节目在 PMT 里看到视频和音频 PID 都是 0x06负载全是不可读的二进制。原因这就是典型的 CSA 加密 TS。加密发生在 PES 层PES 头里的 PTS/DTS 也会被加密或置零只有 PSI 表是明文。外部工具看不出任何流结构。解决不要试图解析密文直接从 PMT 读取 PCR_PID 和加密标志确认是否包含 CA_descriptor。如果要做分析需要先接入解密模块得到明文再解析。这条坑的真正价值在于提醒你看到 0x06 别急着当私有数据跳过先查 CA descriptor。5.4 188 字节对齐假设失效现象某些文件解析到一半开始全部错位table_id 出现大量乱值。原因TS 流在部分传输系统里会扩展成 204 字节多出的 16 字节是 RS 纠错码位于包尾。如果你的读取逻辑固定按 188 字节步进扫描遇到这种流会把 204 字节包里的校验尾巴当成下一个包的开头导致从某个位置开始连续解析错误。解决读取时不要硬编码 188。标准允许的合法包长有 188、204 等种。稳妥做法是先读取前 4 个字节检查 sync_byte 0x47 是否对齐如果第一个包的 0x47 在偏移 188、204 处都能找到按最小公倍数去判断实际包长。这也是为什么很多老工程师反对用固定步长快进的方式扫流的原因。5.5 节目名编码用了 UTF-8 导致 EPG 乱码现象EPG 里节目名显示成一片乱码英文正常中文全是问号。原因DVB 标准里节目名字符串默认用 UTF-8 编码但很多老式复用器实际输出的是 ISO-8859-1 或者 GBK。NIT/SDT 表里的字符编码是跟着表里的 encoding_type 走的解析器不检查这个字段直接按 UTF-8 解码就会出现乱码。解决解析 SDT 的 service name 之前先读旁边字节的 encoding 标志如果标记不是 UTF-8回退到自定义字符集转换。遇到这种码流不要改解析器默认编码而是老老实实把原字节打出来看一眼再定策略——这是标准的兼容性要求不是解析器的 bug。6. 进阶验证不写解析器也能做的合规性自查6.1 四步命令行自检套路有没有解析器源码都能用现成工具做一次 TS 合规性体检。我长期用这套四步检查法跑完基本能定位 80% 的复用层问题。第一步检查 PAT 和 PMT 是否齐全ffprobe -v error -show_programs input.ts 21 | grep -E program_id|pcr_pid这一步确认码流里有节目信息表且 PCR_PID 存在且合法。注意 PCR_PID 不能是 0x1FFF否则解码器无法恢复时钟。第二步检查 PID 的流类型是否匹配实际负载ffprobe -v error -show_streams input.ts 21 | grep -E codec_name|profile|level这里会输出每个流实际解码出来的编码格式。如果 PMT 里标的是 H.264实际 codec_name 却是 mpeg2video说明复用器在 PMT 里写了错误的 stream_type是典型的复用错误。第三步检查时间戳回绕逻辑这一步可以写成脚本ffprobe -v error -show_entries packetpts_time,flags -of csv input.ts \ | awk -F, NR1 { if ($1 ! N/A) { diff $1 - prev; if (diff -90000) print wrap or jump, $1, prev } prev $1 }这里的 awk 逻辑是如果相邻 PTS 差值为负且超过 900001 秒说明要么发生了回绕要么 PTS 跳变。正常直播流的 PTS 差值应当恒为非负小值。第四步验证音频和视频 PTS 是否交织ffprobe -v error -show_entries packetpts_time,stream_index -of csv input.ts | awk -F, {print $2, $3} | sort -k2 -n | head -30音频和视频的 PTS 应该交错上升不能出现所有视频 PTS 在前、所有音频 PTS 在后的情况。如果是后者播放器会一直缓冲音频直到视频追上表现出来就是启动慢。6.2 排查流程之外的三个习惯第一解复用器里的所有长度字段都按读到的值 已消费的字节双重校验防止错位继续蔓延。第二对 PCR 的增量做统计而不是只看瞬时值均值和方差才是判断时钟稳定性的依据。第三所有版本比较逻辑都按 5 bit 或 33 bit 回绕处理不要直接比较大小。说个我自己的习惯从那以后我每次拿到一条来源不明的 TS 流做的第一件事永远是先跑一遍上面这套四步把 PAT/PMT/时间戳的截图存下来再动手改代码。很多玄学 bug其实都是码流复用参数不干净导致的早五分钟验证能省下后面一整个下午的解码器调试。希望帮到你——毕竟 MEPG 2 系统层是条老河但年年都有人掉进同一个坑里。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。