MPEG-TS 封装的核心逻辑

MPEG-TS 封装的核心要求

假设一个 IDR 帧数据为 5000 字节:

| 包序号 | 内容 | 数据量 |
| 包1 | TS头 + 适配域(PCR) + PES头(14) + AUD(4) + 数据 | ~150 字节数据 |
| 包2 | TS头 + 数据 | 184 字节数据 |
| 包3 | TS头 + 数据 | 184 字节数据 |
| ... | ... | ... |

包N TS头 + 数据 剩余数据
一、 MPEG-TS封装流程
1.等待第一个完整的 IDR 帧及其参数集就绪

IDR 帧(Instantaneous Decoding Refresh)是 H.264/H.265 视频中的关键帧,具有以下特性:

特性 说明
独立解码 不依赖任何前面的帧,可以独立解码出完整画面
刷新参考帧 解码器收到 IDR 帧后会清空所有参考帧缓存
随机访问点 用户切台、Seek 时从 IDR 帧开始解码

没有 IDR 帧,解码器无法开始解码。如果直接发送 P 帧或 B 帧,解码器会因为缺少参考帧而无法解码,导致花屏或黑屏。

SPS(Sequence Parameter Set)和 PPS(Picture Parameter Set)包含解码所需的参数:

参数集 包含的关键信息
SPS 分辨率、帧率、编码档次(Profile)、级别(Level)等
PPS 熵编码模式、量化参数初始值、片组信息等
VPS (H.265) 视频参数集,包含更高层级的编码信息

没有 SPS/PPS,解码器不知道如何解码视频数据,即使收到 IDR 帧也无法正确解析。解码器从开始接收到能够正常显示画面,需要经历以下过程:

复制代码
收到 PAT → 找到 PMT → 找到视频 PID → 等待 IDR 帧 → 等待 SPS/PPS → 开始解码                

如果缺少 SPS/PPS:解码器无法初始化,画面无法解码,如果缺少 IDR 帧:解码器没有解码起点,只能等下一个 IDR


2.写PES负载数据

如何将一个完整的 PES 负载数据(如包含 SPS、PPS、IDR 帧数据)分片封装成多个 188 字节的 TS 包。

  1. 清空 TS 包缓冲区 (m_tsBuffer)

  2. 判断是否需要写 PCR(第一帧视频帧,每100ms插入PRC数据)

  3. 计算开销:PES头(14) + AUD(视频4字节)

开销类型 字节数 说明
PES 头 14 包含起始码、流ID、PTS/DTS 等
AUD (视频) 4 访问单元分隔符,H.264/H.265 需要
  1. 决定是否需要适配域(PCR/关键帧标志/填充)

适配域结构

字段 字节数 说明
适配域长度 1 后续字节数
标志位 1 指示是否包含 PCR、RAI 等
PCR(可选) 6 节目时钟参考
填充字节 N 0xFF 填充
  • 计算基础头部大小

  • 计算可用空间

  • 计算可复制的数据量,核心逻辑逻辑:只有当可用空间足够容纳 PES 头 + AUD 时,才复制数据。

  • 计算已使用空间(used = 基础头部(6/12) + PES头+AUD(18) + 负载数据(copySize))

  • 计算填充字节数,stuffingBytes = 188 - used,剩余空间用0xff填充

  • 写入 TS 头,核心逻辑和填充字段上篇文章已经解析

5.写pes头部信息

TS 封装中最核心的部分,负责将 PES 负载数据分片写入 TS 包。根据是否为PES第一个包标志,分为第一个包和后续包两种处理方式。

复制代码
if (m_pesFirstPacket) {
    // ========== 第一个 TS 包 ==========
    // 包含:TS头 + 适配域 + PES头(14B) + AUD(视频4B) + 部分负载
} else {
    // ========== 后续 TS 包 ==========
    // 包含:TS头 + 适配域 + 负载数据
}
参数 说明
streamId 流类型(0xE0视频 / 0xC0音频)
m_pesPts PTS 时间戳(90kHz)
0 PES 长度设为 0(表示未指定长度)
headerSize TS 头大小(用于定位写入位置)
isAudioStream 是否音频流
  • 写入音频的PES,头部 (14字节)

  • 视频特有:写入 AUD(4个字节)(访问单元分隔符)

    // H264_AUD 通常定义为
    const uint8_t H264_AUD[] = {0x00, 0x00, 0x00, 0x01, 0x09, 0xF0};
    // 实际是:起始码(4) + NAL头(1) + 类型(1) = 6字节?

  • 写部分负载数据

视频数据第一个包整体布局结构:

音频数据第一个包整体布局结构:

后续包结构:没有 PES 头

  • 没有 AUD

  • 直接从 headerSize 偏移处开始写数据

相关推荐
小曾同学.com4 个月前
SRT协议推拉流
ffmpeg·实时音视频·vlc·obs·srt协议·srt推拉流
却道天凉_好个秋1 年前
音视频学习(二十七):SRT协议
音视频·srt协议