GStreamer DASH Demux 知识文档

GStreamer DASH Demux 知识文档

基于 gst-plugins-bad/ext/dash 源码整理,聚焦 DASH 协议格式、MPD 解析、dashdemux 播放端全流程


1. 模块概览

dashdemux 是 DASH 播放端的核心元素,继承自 GstAdaptiveDemux,负责:

  • 解析 MPD(Media Presentation Description)XML 清单
  • 解析 MPD 节点层级(Period → AdaptationSet → Representation → Segment)
  • 通过 SegmentTemplate / SegmentList / SegmentBase 三种寻址方式解析片段 URI
  • 下载并处理 ISO BMFF(fMP4)片段,含 SIDX 子片段拆分
  • 码率自适应切换(Representation 切换)
  • 直播流定时刷新与时钟同步(UTCTiming)
  • 多 Period 管理

Sink Padsink(ALWAYS),接收 application/dash+xml

Src Padaudio_XX / video_XX / subtitle_XX(SOMETIMES),每种流类型独立输出


2. DASH 协议格式规范

2.1 MPD 文档结构

复制代码
<MPD>
  ├── <BaseURL>*                    --- 基础 URL(可多层)
  ├── <Location>*                   --- 替代 MPD 地址
  ├── <ProgramInformation>*         --- 节目信息
  ├── <UTCTiming>*                  --- UTC 时钟同步源
  ├── <Metrics>*                    --- 度量信息
  ├── <Period>                      --- 周期(必须至少一个)
  │   ├── <BaseURL>*
  │   ├── <SegmentBase>             --- 周期级片段寻址
  │   ├── <SegmentList>
  │   ├── <SegmentTemplate>
  │   ├── <AdaptationSet>           --- 自适应集
  │   │   ├── <ContentComponent>*
  │   │   ├── <Accessibility>*
  │   │   ├── <Role>*
  │   │   ├── <Rating>*
  │   │   ├── <Viewpoint>*
  │   │   ├── <AudioChannelConfiguration>*
  │   │   ├── <ContentProtection>*  --- DRM 描述符
  │   │   ├── <BaseURL>*
  │   │   ├── <SegmentBase>
  │   │   ├── <SegmentList>
  │   │   ├── <SegmentTemplate>
  │   │   ├── <Representation>      --- 表示
  │   │   │   ├── <BaseURL>*
  │   │   │   ├── <SubRepresentation>*
  │   │   │   ├── <SegmentBase>
  │   │   │   ├── <SegmentList>
  │   │   │   └── <SegmentTemplate>
  │   │   └── ...
  │   └── ...
  └── <Period>*

2.1.1 实际 MPD 示例

以下是基于业界广泛使用的 Akamai Big Buck Bunny DASH 测试内容 (dash.akamaized.net) 的真实 MPD 结构,包含视频多码率 + 音频多码率 + SegmentTemplate + SegmentTimeline + UTCTiming 的完整示例:

xml 复制代码
<?xml version="1.0"?>
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd"
     type="static"
     profiles="urn:mpeg:dash:profile:isoff-main:2011"
     mediaPresentationDuration="PT10M0.000S"
     minBufferTime="PT2.000S">

  <ProgramInformation moreInformationURL="https://dashif.org/">
    <Title>Big Buck Bunny - DASH Test Content</Title>
  </ProgramInformation>

  <Period id="0" start="PT0.000S">

    <!-- ==================== 视频 AdaptationSet ==================== -->
    <AdaptationSet contentType="video" segmentAlignment="true"
                   subsegmentAlignment="true" par="16:9">
      <SegmentTemplate timescale="1000"
                       media="bunny_$RepresentationID$_$Number$.m4s"
                       initialization="bunny_$RepresentationID$_init.mp4"
                       startNumber="1">
        <SegmentTimeline>
          <S t="0" d="2000" r="299"/>
        </SegmentTimeline>
      </SegmentTemplate>

      <Representation id="video_480p" mimeType="video/mp4"
                      codecs="avc1.64001e" bandwidth="1500000"
                      width="854" height="480" frameRate="30"
                      sar="1:1"/>

      <Representation id="video_720p" mimeType="video/mp4"
                      codecs="avc1.64001f" bandwidth="3000000"
                      width="1280" height="720" frameRate="30"
                      sar="1:1"/>

      <Representation id="video_1080p" mimeType="video/mp4"
                      codecs="avc1.640028" bandwidth="6000000"
                      width="1920" height="1080" frameRate="30"
                      sar="1:1"/>
    </AdaptationSet>

    <!-- ==================== 音频 AdaptationSet(英语) ==================== -->
    <AdaptationSet contentType="audio" lang="en"
                   segmentAlignment="true" audioSamplingRate="44100">
      <AudioChannelConfiguration
          schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011"
          value="2"/>
      <SegmentTemplate timescale="1000"
                       media="audio_en_$RepresentationID$_$Number$.m4s"
                       initialization="audio_en_$RepresentationID$_init.mp4"
                       startNumber="1">
        <SegmentTimeline>
          <S t="0" d="2000" r="299"/>
        </SegmentTimeline>
      </SegmentTemplate>

      <Representation id="audio_en_128k" mimeType="audio/mp4"
                      codecs="mp4a.40.2" bandwidth="128000"
                      audioSamplingRate="44100"/>

      <Representation id="audio_en_256k" mimeType="audio/mp4"
                      codecs="mp4a.40.2" bandwidth="256000"
                      audioSamplingRate="44100"/>
    </AdaptationSet>

    <!-- ==================== 音频 AdaptationSet(日语配音) ==================== -->
    <AdaptationSet contentType="audio" lang="ja"
                   segmentAlignment="true" audioSamplingRate="44100">
      <AudioChannelConfiguration
          schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011"
          value="2"/>
      <SegmentTemplate timescale="1000"
                       media="audio_ja_$RepresentationID$_$Number$.m4s"
                       initialization="audio_ja_$RepresentationID$_init.mp4"
                       startNumber="1">
        <SegmentTimeline>
          <S t="0" d="2000" r="299"/>
        </SegmentTimeline>
      </SegmentTemplate>

      <Representation id="audio_ja_128k" mimeType="audio/mp4"
                      codecs="mp4a.40.2" bandwidth="128000"
                      audioSamplingRate="44100"/>
    </AdaptationSet>

  </Period>
</MPD>

要点解读

要素 说明
type="static" 点播内容,所有片段在请求时即可用
profiles="urn:mpeg:dash:profile:isoff-main:2011" ISO BMFF Main profile,支持 SegmentTemplate
segmentAlignment="true" 跨 Representation 片段对齐,允许无缝切换
par="16:9" 画面宽高比,GStreamer 用于计算显示尺寸
codecs="avc1.64001e" H.264 codec OUI,解码器能力匹配依据
AudioChannelConfiguration 声道数声明,GStreamer 据此设置音频 caps
S t="0" d="2000" r="299" 300 个片段,每片 2 秒,总时长 600 秒(10 分钟)
media="bunny_$RepresentationID$_$Number$.m4s" URL 模板:如 bunny_video_720p_1.m4s
initialization="bunny_$RepresentationID$_init.mp4" 初始化段:如 bunny_video_720p_init.mp4
lang="en" / lang="ja" 多语言轨道,GStreamer 据此创建不同 src pad

2.1.2 直播 MPD 实际示例

以下是典型 DASH 直播流的 MPD 结构(参考 DASH-IF Live Sim Server 输出):

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011"
     type="dynamic"
     availabilityStartTime="2024-06-15T08:00:00Z"
     publishTime="2024-06-15T08:30:42Z"
     minimumUpdatePeriod="PT5S"
     minBufferTime="PT4S"
     timeShiftBufferDepth="PT120S"
     suggestedPresentationDelay="PT16S"
     maxSegmentDuration="PT2S"
     profiles="urn:mpeg:dash:profile:isoff-live:2011">

  <UTCTiming schemeIdUri="urn:mpeg:dash:utc:http-xsdate:2014"
             value="https://time.akamai.com/?iso"/>
  <UTCTiming schemeIdUri="urn:mpeg:dash:utc:http-head:2014"
             value="https://time.akamai.com"/>

  <Period id="p0" start="PT0S">

    <AdaptationSet contentType="video" segmentAlignment="true"
                   maxWidth="1920" maxHeight="1080"
                   maxFrameRate="30" par="16:9">
      <SegmentTemplate timescale="90000"
                       media="video_$RepresentationID$_$Time$.m4s"
                       initialization="video_$RepresentationID$_init.mp4">
        <SegmentTimeline>
          <S t="1404000000" d="180000" r="5"/>
          <S d="180000" r="9"/>
          <S d="90000"/>
        </SegmentTimeline>
      </SegmentTemplate>

      <Representation id="v0" bandwidth="800000" width="640" height="360"
                      frameRate="30" codecs="avc1.64001e"/>
      <Representation id="v1" bandwidth="1500000" width="854" height="480"
                      frameRate="30" codecs="avc1.64001f"/>
      <Representation id="v2" bandwidth="3000000" width="1280" height="720"
                      frameRate="30" codecs="avc1.64001f"/>
      <Representation id="v3" bandwidth="6000000" width="1920" height="1080"
                      frameRate="30" codecs="avc1.640028"/>
    </AdaptationSet>

    <AdaptationSet contentType="audio" lang="en" audioSamplingRate="48000">
      <AudioChannelConfiguration
          schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011"
          value="2"/>
      <SegmentTemplate timescale="48000"
                       media="audio_$RepresentationID$_$Time$.m4s"
                       initialization="audio_$RepresentationID$_init.mp4">
        <SegmentTimeline>
          <S t="748800000" d="96256" r="5"/>
          <S d="96256" r="9"/>
          <S d="48128"/>
        </SegmentTimeline>
      </SegmentTemplate>

      <Representation id="a0" bandwidth="96000" codecs="mp4a.40.2"/>
      <Representation id="a1" bandwidth="128000" codecs="mp4a.40.2"/>
    </AdaptationSet>

  </Period>
</MPD>

直播 MPD 关键差异

属性 点播 vs 直播 说明
type static / dynamic 直播为 dynamic
availabilityStartTime 无 / 必需 直播必须声明流起始时间
minimumUpdatePeriod 无 / 必需 客户端刷新 MPD 的最短间隔
timeShiftBufferDepth 无 / 推荐 DVR 回看窗口(如 120 秒)
suggestedPresentationDelay 无 / 推荐 建议延迟(如 16 秒,避免追尾)
publishTime 无 / 推荐 当前 MPD 发布时间
UTCTiming 可选 / 推荐 直播必须同步时钟
SegmentTimeline 通常 r 固定 持续增长,每次刷新追加新 S 元素
URL 模板 常用 $Number$ 直播常用 $Time$(基于时间戳寻址)
timescale 视频常用 1000 直播视频常用 90000(MPEG-TS 时基),音频用采样率

2.2 MPD 根节点属性

属性 类型 说明
type static / dynamic static=点播,dynamic=直播
profiles 字符串 DASH 配置文件标识,如 urn:mpeg:dash:profile:isoff-on-demand:2011
availabilityStartTime dateTime 直播流开始可用时间(dynamic 必须有)
availabilityEndTime dateTime 直播流结束时间
publishTime dateTime MPD 发布时间
mediaPresentationDuration duration(ms) 整体呈现时长
minimumUpdatePeriod duration(ms) 直播 MPD 最小刷新间隔
minBufferTime duration(ms) 最小缓冲时间
timeShiftBufferDepth duration(ms) DVR 回看窗口深度
suggestedPresentationDelay duration(ms) 建议的直播延迟
maxSegmentDuration duration(ms) 最大片段时长
maxSubsegmentDuration duration(ms) 最大子片段时长

2.3 Period 属性

属性 类型 说明
id 字符串 周期标识
start duration(ms) 周期起始时间
duration duration(ms) 周期时长
bitstreamSwitching boolean 是否允许比特流切换

2.4 AdaptationSet 属性

属性 类型 说明
id uint 自适应集 ID
group uint 分组
lang 字符串 语言(BCP47)
contentType 字符串 内容类型(video/audio/text/...)
par ratio 宽高比
minBandwidth / maxBandwidth uint 带宽范围
minWidth / maxWidth uint 宽度范围
minHeight / maxHeight uint 高度范围
segmentAlignment boolean 片段对齐
subsegmentAlignment boolean 子片段对齐

2.5 Representation 属性

属性 类型 说明
id 字符串 表示标识(模板 R e p r e s e n t a t i o n I D RepresentationID RepresentationID 用)
bandwidth uint(bps) 带宽
qualityRanking uint 质量排名
dependencyId 字符串 依赖的表示 ID

2.6 RepresentationBase 共享属性

AdaptationSet / Representation / SubRepresentation 均继承这些属性:

属性 类型 说明
profiles 字符串 编码配置文件
width / height uint 视频分辨率
sar ratio 采样宽高比
frameRate framerate 帧率
minFrameRate / maxFrameRate framerate 帧率范围
audioSamplingRate 字符串 音频采样率
mimeType 字符串 MIME 类型
codecs 字符串 编码格式列表
startWithSAP SAP类型 流访问点类型(0-6)
scanType 字符串 扫描类型

2.7 片段寻址方式

DASH 定义三种片段寻址方式,可出现在 Period / AdaptationSet / Representation 任一层级:

SegmentBase

用于单文件点播(ISO BMFF on-demand profile),通过字节范围寻址:

xml 复制代码
<SegmentBase
  timescale="90000"
  presentationTimeOffset="0"
  indexRange="1234-5678"
  indexRangeExact="true">
  <Initialization sourceURL="init.mp4" range="0-1233"/>
  <RepresentationIndex sourceURL="index.mp4" range="100-200"/>
</SegmentBase>
属性 说明
timescale 时间刻度(1 秒 = timescale 个单位)
presentationTimeOffset 呈现时间偏移
indexRange SIDX box 的字节范围
indexRangeExact indexRange 是否精确
Initialization 初始化段 URL + 字节范围
RepresentationIndex 表示索引 URL + 字节范围

典型 on-demand 模式

  • 初始化段:[0, indexRange.first_byte_pos - 1]
  • SIDX 索引:indexRange 指定的范围
  • 媒体数据:SIDX 解析后按子片段字节范围下载
SegmentList

通过 URL 列表逐片段寻址:

xml 复制代码
<SegmentList timescale="90000" duration="900000" startNumber="1">
  <Initialization sourceURL="init.mp4"/>
  <SegmentURL media="seg1.m4s" mediaRange="0-999" index="seg1_idx" indexRange="0-99"/>
  <SegmentURL media="seg2.m4s"/>
</SegmentList>
属性 说明
duration 片段时长(timescale 单位)
startNumber 起始片段编号
SegmentTimeline 可选时间线(替代固定 duration)
SegmentURL 片段 URL + 字节范围

完整 MPD 示例

xml 复制代码
<MPD type="static" mediaPresentationDuration="PT30S" minBufferTime="PT2S">
  <Period>
    <AdaptationSet mimeType="video/mp4">
      <Representation id="1" bandwidth="1500000">
        <SegmentList timescale="90000" duration="900000" startNumber="1">
          <Initialization sourceURL="init.mp4"/>
          <SegmentURL media="seg1.m4s"/>
          <SegmentURL media="seg2.m4s"/>
          <SegmentURL media="seg3.m4s"/>
        </SegmentList>
      </Representation>
    </AdaptationSet>
  </Period>
</MPD>
SegmentTemplate

通过 URL 模板 + 编号/时间计算寻址:

xml 复制代码
<SegmentTemplate
  timescale="90000"
  duration="900000"
  startNumber="1"
  media="segment_$Number%05d$.m4s"
  initialization="init_$RepresentationID$.mp4"
  index="index_$Number%05d$.m4x"
  bitstreamSwitching="bs_$RepresentationID$.mp4">
  <SegmentTimeline>
    <S t="0" d="900000" r="9"/>
    <S d="450000"/>
  </SegmentTimeline>
</SegmentTemplate>
属性 说明
media 媒体片段 URL 模板
initialization 初始化段 URL 模板
index 索引段 URL 模板
bitstreamSwitching 比特流切换段 URL 模板
duration 固定片段时长(无 SegmentTimeline 时使用)
startNumber 起始片段编号
SegmentTimeline 时间线(精确控制每段时长)

2.8 URL 模板替换规则

模板中 $...$ 标记按以下规则替换:

标记 替换值 格式说明
$RepresentationID$ Representation 的 id 属性 字符串,需 URL 安全字符
$Number<format>$ 片段编号 默认 %01d,可指定如 %05d
$Bandwidth<format>$ Representation 的 bandwidth 整数格式
$Time<format>$ 片段起始时间(timescale 单位) 64 位整数格式
$$ 字面量 $ 转义

示例

  • 模板 segment_$Number%05d$.m4s,Number=3 → segment_00003.m4s
  • 模板 $RepresentationID$_$Time$.m4s,ID="720p",Time=1800000 → 720p_1800000.m4s

2.9 SegmentTimeline 的 S 元素

xml 复制代码
<SegmentTimeline>
  <S t="0" d="900000" r="9"/>   <!-- 从 t=0 开始,时长 900000,重复 9 次(共 10 段)-->
  <S d="450000"/>                <!-- 时长 450000,不重复(1 段)-->
</SegmentTimeline>
属性 说明
t 起始时间(timescale 单位),仅首个 S 必须指定,后续自动续接
d 时长(timescale 单位),必须
r 重复次数(0=不重复即 1 段,>0=额外重复即 r+1 段,<0=重复至 Period 结束)

2.10 直播 vs 点播判断

复制代码
gst_mpd_client_is_live() = (mpd_root_node->type == GST_MPD_FILE_TYPE_DYNAMIC)
类型 MPD type 特征
点播 (Static) static(默认) 固定 MPD,总时长已知
直播 (Dynamic) dynamic 需要 availabilityStartTime,MPD 定期刷新,片段按时可用

2.11 片段可用性窗口(Live)

复制代码
片段可用时间 = availabilityStartTime + period_start + segment_end_time
当前可下载范围 = [availabilityStartTime, now]
可 Seek 范围 = [0, now - AST - maxSegmentDuration]
                 或 [stop - timeShiftBufferDepth, stop](如有 timeShiftBufferDepth)

2.12 UTCTiming 时钟同步

xml 复制代码
<UTCTiming schemeIdUri="urn:mpeg:dash:utc:http-xsdate:2014" value="https://time.example.com/xsdate"/>
schemeIdUri 方法 说明
urn:mpeg:dash:utc:ntp:2014 NTP 标准 NTP 协议
urn:mpeg:dash:utc:sntp:2014 SNTP 简化 NTP
urn:mpeg:dash:utc:http-head:2014 HTTP HEAD 解析 Date: 响应头
urn:mpeg:dash:utc:http-xsdate:2014 HTTP XSD 解析响应体为 XML Schema date
urn:mpeg:dash:utc:http-iso:2014 HTTP ISO 解析响应体为 ISO 8601 日期
urn:mpeg:dash:utc:http-ntp:2014 HTTP NTP 解析 8 字节 NTP 时间戳
urn:mpeg:dash:utc:direct:2014 直接值 value 即为 UTC 时间字符串

2.13 ContentProtection DRM 描述

xml 复制代码
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed" value="Widevine"/>
<ContentProtection schemeIdUri="urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95" value="PlayReady">
  <cenc:pssh>...</cenc:pssh>
</ContentProtection>
  • schemeIdUriurn:uuid: 开头 → 识别为特定 DRM 系统
  • PSSH 数据从子元素提取,通过 GST_EVENT_PROTECTION 事件发送到下游

完整 MPD 示例

xml 复制代码
<AdaptationSet mimeType="video/mp4" contentType="video">
  <ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"
                     value="Widevine"/>
  <ContentProtection schemeIdUri="urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95"
                     value="PlayReady">
    <cenc:pssh>AAAAZ3Bzc2g=</cenc:pssh>
  </ContentProtection>
  <Representation id="1" bandwidth="3000000">
    <BaseURL>encrypted_video.mp4</BaseURL>
    <SegmentBase indexRange="876-5432">
      <Initialization range="0-875"/>
    </SegmentBase>
  </Representation>
</AdaptationSet>

2.14 DASH Profiles

Profile 标识 说明
ISOFF On-Demand urn:mpeg:dash:profile:isoff-on-demand:2011 单文件 + SIDX,字节范围寻址
ISOFF Main urn:mpeg:dash:profile:isoff-main:2011 主配置文件
ISOFF Live urn:mpeg:dash:profile:isoff-live:2011 SegmentTemplate 直播

代码中 profile_isoff_ondemand 标志影响 SIDX 解析和初始化段字节范围计算。

2.15 BaseURL 解析规则

BaseURL 按 MPD → Period → AdaptationSet → Representation 四层逐级拼接:

复制代码
最终 URL = MPD_base_uri
           + Period.BaseURL[idx]
           + AdaptationSet.BaseURL[idx]
           + Representation.BaseURL[idx]
           + 片段 URI
  • 每层可选,缺失则跳过
  • baseURL_idx 默认为 0,用于多 BaseURL 负载均衡
  • BaseURL 可含查询字符串,提取后存入 queryURL 单独附加

2.16 EventStream

EventStream 用于在 MPD 中声明定时事件(广告插入、定时元数据、自定义信令):

xml 复制代码
<EventStream schemeIdUri="urn:com:example:ad:2019" value="ad-insertion">
  <Event id="1" presentationTime="9000000" duration="1800000">
    <metadata>...</metadata>
  </Event>
  <Event id="2" presentationTime="27000000" duration="3600000"/>
</EventStream>
属性 说明
schemeIdUri 事件方案标识(与 ContentProtection 类似,用 urn:uuid: 标识特定系统)
value 方案特定值
Event@id 事件标识
Event@presentationTime 事件呈现时间(timescale 单位,相对于 Period 起始)
Event@duration 事件持续时长
Event@contentEncoding 事件内容的编码方式(如 base64)

GStreamer 处理 :解析 EventStreamEvent 元素,按 presentationTime 排序存储。播放到对应时间时,通过 GST_EVENT_CUSTOM_DOWNSTREAM 发送事件到下游。

2.17 SupplementalProperty 与 EssentialProperty

这两种描述符出现在 AdaptationSet / Representation 层级,用于声明补充属性或必要条件:

xml 复制代码
<AdaptationSet contentType="video">
  <!-- 补充属性:不影响流选择,提供额外信息 -->
  <SupplementalProperty
      schemeIdUri="urn:mpeg:dash:adaptation-set-switching:2016"
      value="1"/>

  <!-- 必要属性:客户端必须识别才能选择此流 -->
  <EssentialProperty
      schemeIdUri="urn:mpeg:dash:fdh:2018"
      value="true"/>
  ...
</AdaptationSet>
描述符 语义 客户端行为
SupplementalProperty 补充信息,可忽略 不识别也不影响流选择
EssentialProperty 必要条件,必须识别 不识别则应跳过该 AdaptationSet/Representation

DASH-IF IOP 常用 schemeIdUri

schemeIdUri 用途
urn:mpeg:dash:adaptation-set-switching:2016 标识可互相切换的 AdaptationSet 组
urn:mpeg:dash:fdh:2018 Fully Decodable Hierarchical(分层编码必须理解)
urn:mpeg:dash:picture:audio:2019 图片+音频组合流声明

GStreamer 处理:解析后存储在 AdaptationSet/Representation 节点上,EssentialProperty 不识别时跳过对应流。

2.18 Role 描述符

Role 描述符声明流的角色(主轨、副轨、评论等),用于客户端的流选择逻辑:

xml 复制代码
<AdaptationSet contentType="audio" lang="en">
  <Role schemeIdUri="urn:mpeg:dash:role:2011" value="main"/>
  ...
</AdaptationSet>

<AdaptationSet contentType="audio" lang="en">
  <Role schemeIdUri="urn:mpeg:dash:role:2011" value="commentary"/>
  ...
</AdaptationSet>

<AdaptationSet contentType="video">
  <Role schemeIdUri="urn:mpeg:dash:role:2011" value="main"/>
  <Accessibility schemeIdUri="urn:mpeg:dash:role:2011" value="description"/>
  ...
</AdaptationSet>

DASH Role 值urn:mpeg:dash:role:2011):

value 说明
main 主轨道(默认选择)
alternate 备用轨道
supplementary 补充内容
commentary 评论音轨
dub 配音
caption / subtitle 字幕
description 音频描述(无障碍)
sign 手语视频
metadata 元数据轨道
enhanced-audio-intelligibility 增强语音清晰度

GStreamer 处理 :解析 Role 值,main 角色的流优先选择。description 标记用于无障碍音频轨道。Role 信息附加到 src pad 的 tag 事件中。

2.19 SubRepresentation

SubRepresentation 声明 Representation 内的子集,用于提取层(scalable coding)或内容拆分:

xml 复制代码
<Representation id="1080p" bandwidth="6000000" width="1920" height="1080">
  <SubRepresentation level="0" dependencyLevel="0" bandwidth="1500000"
                     contentComponent="1"/>
  <SubRepresentation level="1" dependencyLevel="0,1" bandwidth="3000000"
                     contentComponent="2"/>
  <SegmentTemplate .../>
</Representation>
属性 说明
level 层级编号(SVC/MVC 分层编码)
dependencyLevel 依赖的层级
contentComponent 内容组件引用
bandwidth 该子表示的带宽

用途

  • 分层编码:H.264 SVC / H.265 SHVC 中,基础层可独立解码,增强层依赖基础层
  • 内容组件拆分:一个复用 Representation 拆分为多个逻辑组件

GStreamer 状态 :解析 SubRepresentation 节点属性,但 contentComponentlevel 在播放流程中未实际使用。


3. MPD 解析流程详解

3.1 MPD XML 解析 (gst_mpdparser_get_mpd_root_node)

复制代码
输入: MPD XML 原始数据 + 大小
  │
  ├─ libxml2 解析为 DOM 树
  ├─ 遍历根元素属性:
  │   ├─ default_namespace / namespace_xsi / namespace_ext / schemaLocation
  │   ├─ id / profiles
  │   ├─ type → GST_MPD_FILE_TYPE_STATIC / DYNAMIC
  │   ├─ availabilityStartTime / availabilityEndTime / publishTime → GstDateTime
  │   ├─ mediaPresentationDuration / minimumUpdatePeriod / minBufferTime
  │   ├─ timeShiftBufferDepth / suggestedPresentationDelay
  │   └─ maxSegmentDuration / maxSubsegmentDuration
  │
  ├─ 遍历子元素,递归解析各节点:
  │   ├─ <BaseURL> → GstMPDBaseURLNode 列表
  │   ├─ <Location> → GstMPDLocationNode 列表
  │   ├─ <ProgramInformation> → GstMPDProgramInformationNode 列表
  │   ├─ <UTCTiming> → GstMPDUTCTimingNode 列表
  │   ├─ <Metrics> → GstMPDMetricsNode 列表
  │   └─ <Period> → GstMPDPeriodNode 列表
  │       └─ 每个 Period 递归解析:
  │           ├─ <BaseURL> / <SegmentBase> / <SegmentList> / <SegmentTemplate>
  │           └─ <AdaptationSet> → GstMPDAdaptationSetNode
  │               ├─ <ContentComponent> / <ContentProtection>
  │               ├─ <AudioChannelConfiguration> / <Role> / <Accessibility>
  │               ├─ <SegmentBase> / <SegmentList> / <SegmentTemplate>
  │               └─ <Representation> → GstMPDRepresentationNode
  │                   ├─ <SubRepresentation>
  │                   ├─ <BaseURL>
  │                   └─ <SegmentBase> / <SegmentList> / <SegmentTemplate>
  │                       └─ <SegmentTimeline> → GstMPDSegmentTimelineNode
  │                           └─ <S> → GstMPDSNode (t, d, r)
  │
  └─ 返回 GstMPDRootNode

3.2 媒体表示设置 (gst_mpd_client_setup_media_presentation)

复制代码
输入: GstMPDClient + 目标时间/Period索引/Period ID
  │
  ├─ 检查已构建的 periods 是否覆盖目标(快速路径)
  │
  ├─ 否则完全重建 periods 列表:
  │   ├─ 清空旧 periods
  │   ├─ 遍历 MPD 的 Period 列表:
  │   │   ├─ xlink:href 解析(外部 Period 下载)
  │   │   ├─ Period 起始时间计算(ISO 23009-1 5.3.2.1):
  │   │   │   ├─ period.start 有值 → start = period.start * GST_MSECOND
  │   │   │   ├─ 前一 Period duration 有值 → start += prev_duration
  │   │   │   ├─ 首个 Period (static) → start = 0
  │   │   │   ├─ dynamic → 允许无显式 start
  │   │   │   └─ 否则 → "Early Available Period"(跳到 early)
  │   │   │
  │   │   ├─ Period 时长计算:
  │   │   │   ├─ 下一 Period 有 start → duration = next_start - current_start
  │   │   │   ├─ period.duration 有值 → duration = period.duration * GST_MSECOND
  │   │   │   ├─ dynamic → 允许无显式 duration
  │   │   │   └─ 最后 Period + mediaPresentationDuration → duration = total - start
  │   │   │
  │   │   └─ 创建 GstStreamPeriod 存入列表
  │   │
  │   └─ 到达目标时间/索引/ID 时停止
  │
  └─ 返回 TRUE

3.3 流设置 (gst_mpd_client_setup_streaming)

复制代码
输入: GstMPDClient + GstMPDAdaptationSetNode
  │
  ├─ 创建 GstActiveStream
  ├─ MIME 类型检测 (gst_mpdparser_representation_get_mimetype)
  │   ├─ video/* → GST_STREAM_VIDEO
  │   ├─ audio/* → GST_STREAM_AUDIO
  │   ├─ application/* → GST_STREAM_APPLICATION
  │   └─ 未知 → 返回 FALSE
  │
  ├─ 选择初始 Representation(慢启动策略):
  │   └─ 最低带宽 Representation
  │       (注释中有最高带宽备选策略,但未启用)
  │
  ├─ 调用 gst_mpd_client_setup_representation() 配置
  └─ 追加到 client->active_streams

3.4 Representation 配置 (gst_mpd_client_setup_representation)

复制代码
输入: GstMPDClient + GstActiveStream + GstMPDRepresentationNode
  │
  ├─ 存储当前 Representation
  ├─ 清理旧片段列表
  │
  ├─ 计算片段寻址节点(层级查找: Representation > AdaptationSet > Period):
  │   ├─ SegmentBase / SegmentList / SegmentTemplate
  │   └─ resolveExternal: xlink:href 解析
  │
  ├─ 三大分支构建片段列表:
  │
  │   ├─ 分支 A: SegmentBase 或 SegmentList
  │   │   ├─ 无 SegmentList 但有 SegmentBase → 单文件,整个 Period 为一个片段
  │   │   ├─ SegmentList + SegmentTimeline → 遍历 <S> 元素构建 GstMediaSegment
  │   │   │   ├─ number 从 startNumber 开始,每个 S 按 r+1 递增
  │   │   │   ├─ scale_start 从 S.t 累积,scale_duration = S.d
  │   │   │   └─ start/duration 从 timescale 转换为纳秒
  │   │   └─ SegmentList 无 SegmentTimeline → 遍历 SegmentURL
  │   │       ├─ 每个 SegmentURL 一个 GstMediaSegment
  │   │       └─ number 从 startNumber 逐个递增
  │   │
  │   ├─ 分支 B: SegmentTemplate
  │   │   ├─ 有 SegmentTimeline → 同分支 A 的 S 元素遍历
  │   │   │   └─ SegmentURL = NULL(按需模板替换生成 URL)
  │   │   └─ 无 SegmentTimeline → 不构建片段列表
  │   │       └─ 片段按需计算(segment_index + startNumber)
  │   │
  │   └─ 无任何寻址节点 → 单片段(BaseURL 直接指向文件)
  │
  ├─ Period 边界裁剪: 超出 Period 时长的片段被截断或丢弃
  │
  ├─ BaseURL 解析: 四层拼接 (MPD → Period → AdaptationSet → Representation)
  │
  └─ presentationTimeOffset 计算:
      └─ PTO = presentationTimeOffset * GST_SECOND / timescale

3.5 片段 URI 解析 (gst_mpd_client_get_next_fragment)

复制代码
输入: GstMPDClient + stream_index
  │
  ├─ 路径 A: 有显式片段列表 (stream->segments != NULL)
  │   ├─ 索引 stream->segment_index
  │   ├─ SegmentURL 存在:
  │   │   ├─ mediaURL = gst_mpdparser_get_mediaURL(stream, SegmentURL)
  │   │   └─ indexURL = SegmentURL->index
  │   └─ SegmentURL 为 NULL(SegmentTemplate + SegmentTimeline):
  │       ├─ mediaURL = build_URL_from_template(template->media, id, number, bandwidth, time)
  │       ├─ indexURL = build_URL_from_template(template->index, ...)
  │       └─ number/time 根据当前片段的 scale_start/duration 计算
  │
  ├─ 路径 B: 无片段列表(SegmentTemplate 无 SegmentTimeline)
  │   ├─ segment_number = segment_index + startNumber
  │   ├─ segment_time = segment_index * fragment_duration
  │   └─ mediaURL = build_URL_from_template(template->media, id, number, bandwidth, time)
  │
  └─ URI 最终化:
      ├─ gst_uri_from_string_with_base(baseURL, mediaURL)
      ├─ 附加 queryURL
      └─ 填充 GstMediaFragmentInfo (uri, range, timestamp, duration, discontinuity)

3.6 初始化段解析 (gst_mpd_client_get_next_header)

复制代码
输入: GstMPDClient + stream_index
  │
  ├─ SegmentBase 路径:
  │   ├─ Initialization 存在 → 获取 URL + 字节范围
  │   └─ Initialization 不存在 但 indexRange 存在
  │       └─ init 数据 = [0, indexRange.first_byte_pos - 1]
  │           (SIDX 前的所有数据即为初始化段)
  │
  └─ SegmentTemplate 路径:
      └─ initialization 模板存在 → build_URL_from_template
          (使用 $RepresentationID$, Number=0, Time=0)

4. DASH Demux 播放流程

4.1 初始化

复制代码
process_manifest()
  ├─ 创建 GstMPDClient
  ├─ 存储 mpd_uri / mpd_base_uri
  ├─ gst_mpd_client_parse() 解析 MPD XML
  ├─ gst_mpd_client_check_profiles() 检查 profile
  ├─ gst_mpd_client_fetch_on_load_external_resources() 解析 xlink:onLoad
  ├─ gst_mpd_client_setup_media_presentation() 构建周期列表
  └─ gst_dash_demux_setup_streams() 设置流
      │
      ├─ Live 流:
      │   ├─ 检查 availabilityStartTime(必须有)
      │   ├─ UTCTiming 时钟同步初始化 + 首次同步
      │   ├─ server_now = client_now + clock_compensation
      │   ├─ 起始时间 = server_now - suggestedPresentationDelay
      │   │              或 - default_presentation_delay(属性)
      │   ├─ 找到目标 Period 索引
      │   └─ seek 到起始时间
      │
      ├─ VOD 流:
      │   ├─ 从 Period 0 开始
      │   └─ seek 到首个片段
      │
      └─ gst_dash_demux_setup_all_streams()
          ├─ 遍历所有 AdaptationSet → gst_mpd_client_setup_streaming()
          ├─ 为每个 ActiveStream:
          │   ├─ 创建 src pad (audio_XX / video_XX / subtitle_XX)
          │   ├─ 获取输入 caps (video: width/height/framerate; audio: rate/channels)
          │   ├─ 提取语言标签
          │   ├─ 创建 GstDashDemuxStream
          │   ├─ 初始化 SIDX 解析器
          │   ├─ ISOBMFF 检测 (video/quicktime, audio/x-m4a)
          │   ├─ allow_sidx = profile_isoff_ondemand
          │   └─ ContentProtection 事件发送 (PSSH 提取)
          └─ trickmode_no_audio 时跳过音频流

4.2 片段下载与处理

复制代码
stream_update_fragment_info()
  ├─ gst_mpd_client_get_next_fragment() 获取下一个片段
  │   └─ 返回 GstMediaFragmentInfo (uri, range, index_uri, index_range, timestamp, duration)
  ├─ 设置 fragment.uri / range / duration
  ├─ 设置 header (init segment): gst_mpd_client_get_next_header()
  └─ 设置 index (sidx): gst_mpd_client_get_next_header_index()

start_fragment()
  ├─ 重置 current_index_header_or_data
  └─ 重置 current_offset
  └─ ISOBMFF 视频 key-unit trick mode → discont = TRUE

data_received()
  ├─ 判断当前下载上下文 (index / header / data)
  ├─ 上下文切换时清空 adapter
  ├─ 累积数据到 adapter
  │
  ├─ ISOBMFF 流 → gst_dash_demux_handle_isobmff()
  │   ├─ 解析 ISOBMFF box (moov, moof, sidx, mdat)
  │   ├─ SIDX 完成:
  │   │   ├─ 计算 sidx_base_offset
  │   │   ├─ 检查 ref_type (不支持 reference_type=1)
  │   │   └─ 执行 pending seek 或定位到 entry 0
  │   ├─ MOOF 解析:
  │   │   ├─ 存储 moof box + offset
  │   │   ├─ 查找 sync samples (关键帧表)
  │   │   └─ 更新 moof 平均大小统计
  │   └─ MDAT 处理:
  │       ├─ Key-unit trick mode → 修剪到当前关键帧边界
  │       ├─ SIDX 子片段 → 按 SIDX entry 边界切片推送
  │       └─ 普通模式 → 推送整个 mdat
  │
  ├─ 非 ISOBMFF + SIDX 完成 → 按 SIDX entry 边界切片
  │
  └─ 其他 → 全部推送下游

finish_fragment()
  ├─ ISOBMFF 视频 key-unit trick mode → discont
  ├─ SIDX 流 + 有 pending seek 或更多子片段 → GST_FLOW_OK
  └─ 否则 → gst_adaptive_demux_stream_advance_fragment(duration)

4.3 码率自适应

复制代码
stream_select_bitrate()
  ├─ Key-unit trick mode → 不切换(锁定码率)
  ├─ 视频流: 限制码率 ≤ max_bitrate 属性
  ├─ 倍速播放: bitrate /= ABS(rate)(更快速度 → 更低质量)
  ├─ gst_mpd_client_get_rep_idx_with_max_bandwidth()
  │   ├─ 遍历所有 Representation
  │   ├─ 过滤: bandwidth ≤ max_bandwidth
  │   ├─ 过滤: width ≤ max_video_width
  │   ├─ 过滤: height ≤ max_video_height
  │   ├─ 过滤: framerate ≤ max_video_framerate
  │   ├─ 选择: 最高带宽的合格 Representation
  │   └─ 无合格 → 最低带宽 Representation(兜底)
  │
  ├─ 选择了不同的 Representation:
  │   ├─ gst_mpd_client_setup_representation() 重新配置
  │   ├─ 更新 caps
  │   ├─ 重置 SIDX 解析器 (allow_sidx = TRUE)
  │   ├─ 清空 ISOBMFF 状态、adapter、moof、sync samples
  │   └─ 返回 TRUE → 触发流切换
  │
  └─ 未切换 → 返回 FALSE

4.4 直播刷新

复制代码
get_manifest_update_interval()
  └─ MIN(mpd_root_node->minimumUpdatePeriod * 1000, 30分钟)

update_manifest_data()
  ├─ 解析新 MPD 到新 GstMPDClient
  ├─ 转移流位置状态:
  │   ├─ 按 Period ID 或索引定位当前周期
  │   ├─ 遍历所有活跃流:
  │   │   ├─ 从旧客户端获取下一片段时间戳
  │   │   └─ 在新客户端 seek 到该时间 (+10μs 补偿 timescale 舍入)
  │   └─ 重新进行时钟漂移补偿
  └─ 失败 → GST_FLOW_ERROR

stream_get_fragment_waiting_time()
  ├─ 获取下一片段的可用开始时间
  ├─ 差值 = availability_start - client_now
  └─ 减去 clock_compensation(如果服务器时钟落后,等待更久)

4.5 时钟同步

复制代码
UTCTiming 时钟同步流程:
  │
  ├─ setup_streams() 中初始化:
  │   ├─ 检查 MPD UTCTiming 元素
  │   ├─ 选择支持的方法 (优先级: NTP > HTTP HEAD > HTTP XSD > HTTP ISO)
  │   └─ 创建 GstDashDemuxClockDrift
  │
  ├─ poll_clock_drift() 定期同步:
  │   ├─ NTP: 连接 NTP 服务器,等待同步(5s 超时),读取时间
  │   ├─ HTTP HEAD: 获取 URL,解析 Date: 头(RFC 5322)
  │   ├─ HTTP XSDATE/ISO: 获取 URL,解析响应体为日期时间
  │   ├─ HTTP NTP: 获取 URL,解析 8 字节 NTP 时间戳
  │   │
  │   ├─ clock_compensation = server_now - client_now
  │   │   (client_now = (request_start + request_end) / 2,取中点估计)
  │   │
  │   └─ 更新间隔: 成功 → 30 分钟,失败/NTP → 30 秒
  │
  └─ gst_dash_demux_get_server_now_utc()
      └─ return client_now + clock_compensation

4.6 Seek

复制代码
seek()
  ├─ 解析 seek 事件 (rate, format, flags, start, stop)
  ├─ 计算 target_pos
  ├─ 遍历所有 Period 找到包含 target_pos 的 Period
  │   ├─ 跨 Period → 释放旧流,设置新 Period,重建所有流
  │   └─ 同 Period 但 trickmode_no_audio 变化 → 也重建流
  └─ 对每个流调用 stream_seek()

stream_seek()
  ├─ 清空 adapter、ISOBMFF 状态、moof、sync samples
  ├─ gst_mpd_client_stream_seek()
  │   ├─ 有片段列表 → 线性扫描找到目标片段
  │   │   ├─ SNAP_NEAREST: 比较距离,可能前进到下一个边界
  │   │   ├─ SNAP_AFTER (forward) / SNAP_BEFORE (reverse)
  │   │   └─ 设置 segment_index + segment_repeat_index
  │   └─ 模板模式 → index = ts / duration
  │
  ├─ ISOBMFF: 调整时间戳 (offset - period_start)
  ├─ SIDX 已解析 → gst_dash_demux_stream_sidx_seek()
  │   └─ 二分查找 SIDX entry,处理 SNAP 标志
  └─ SIDX 未解析 → 存储 pending_seek_ts,等待 SIDX 后延迟 seek

4.7 Period 切换

复制代码
has_next_period()
  └─ Forward: gst_mpd_client_has_next_period()
      └─ Reverse: gst_mpd_client_has_previous_period()

advance_period()
  ├─ Forward: period_idx++
  ├─ Reverse: period_idx--
  ├─ gst_dash_demux_setup_all_streams() --- 为新 Period 创建所有流
  └─ gst_mpd_client_seek_to_first_segment()

4.8 直播 Seek 范围

复制代码
get_live_seek_range()
  ├─ server_now = client_now + clock_compensation
  ├─ stream_now = server_now - availabilityStartTime
  ├─ stop = stream_now - maxSegmentDuration (DASH 规范 5.3.9.5.3)
  ├─ start = 0 (无 timeShiftBufferDepth)
  │   或 stop - timeShiftBufferDepth (有 timeShiftBufferDepth)
  └─ 返回 [start, stop]

5. SIDX 解析与子片段拆分

5.1 SIDX 概述

SIDX(Segment Index Box)是 ISO BMFF 中的 sidx box,描述文件内子片段的字节位置和时长。用于 ISOFF on-demand profile,使单文件内容支持部分下载。

5.2 SIDX 解析流程

复制代码
片段下载流程中的 SIDX 处理:
  │
  ├─ need_header = TRUE → 下载初始化段 [0, indexRange.first-1]
  ├─ downloading_index = TRUE → 下载 SIDX [indexRange]
  │
  ├─ data_received() 中解析 ISOBMFF box:
  │   ├─ 识别 sidx box → 填充 GstSidXParser
  │   ├─ SIDX 完成后:
  │   │   ├─ sidx_base_offset = current_offset + sidx_box_size + first_offset
  │   │   ├─ 检查 ref_type (不支持 =1 的引用类型)
  │   │   └─ 执行 pending seek 或定位到 entry 0
  │   │
  │   └─ 后续数据按 SIDX entry 边界切片
  │       ├─ 每个 entry 有 referenced_size(字节大小)
  │       ├─ 到达 entry 边界 → 推送当前累积数据,advance subfragment
  │       └─ 所有 entry 完成 → finish_fragment
  │
  └─ SIDX seek (gst_dash_demux_stream_sidx_seek):
      ├─ 二分查找 entry 包含目标时间戳
      ├─ SNAP_NEAREST / SNAP_AFTER / SNAP_BEFORE 处理
      └─ 设置 entry_index + sidx_position

5.3 SIDX 与片段下载的交互

复制代码
单文件 on-demand 下载序列:
  1. 下载 [0, indexRange.first-1]           → 初始化段 (moov)
  2. 下载 [indexRange]                       → SIDX box
  3. 解析 SIDX → 得到子片段字节范围列表
  4. 按 SIDX entry 顺序下载:
     entry[0]: [sidx_base_offset, sidx_base_offset + size0 - 1]
     entry[1]: [prev_end + 1, prev_end + size1]
     ...
  5. 每个 entry 完成后推送数据并 advance

6. Key-Unit Trick Mode

6.1 概述

Key-unit trick mode 实现仅下载关键帧的快速浏览模式(快进/快退),适用于 DASH + ISO BMFF 内容。

6.2 实现机制

复制代码
关键帧 Trick Mode 流程:
  │
  ├─ 触发: trickmode 事件 + allow_trickmode_key_units=TRUE
  │
  ├─ 分块下载 (need_another_chunk):
  │   ├─ 初始请求 ~8192 字节 + 平均 moof 大小
  │   └─ 可能包含首关键帧数据
  │
  ├─ MOOF 解析时提取关键帧表:
  │   ├─ 解析 traf/trun/tfhd box
  │   ├─ 构建关键帧偏移表 (moof_sync_samples)
  │   ├─ 计算平均关键帧大小和间距
  │   └─ 标记首个关键帧是否紧跟 moof
  │
  ├─ 关键帧推进 (advance_sync_sample):
  │   ├─ Forward: 移动到下一个关键帧
  │   ├─ Reverse: 向前遍历关键帧表
  │   └─ 支持按 target_time 跳到目标关键帧
  │
  ├─ 目标时间计算 (get_target_time):
  │   ├─ 考虑 QoS 反馈(下游最早时间)
  │   ├─ 考虑平均下载时间
  │   ├─ 接近下游 → 强制跳更远
  │   ├─ 指数移动平均平滑跳过大小
  │   └─ 考虑 max_video_framerate 和 max_bitrate 约束
  │
  └─ 数据路由:
      └─ mdat 中仅推送当前关键帧范围的数据

7. CMAF 与分块传输编码

7.1 CMAF 概述

CMAF(Common Media Application Format,ISO 23009-7)是 DASH 和 HLS 共享的媒体片段格式标准,目标是实现一次编码、双协议分发

核心思想:DASH 和 HLS 传统上使用不同的容器格式(DASH 用 fMP4,HLS 用 MPEG-TS),CMAF 统一采用 ISO BMFF(fMP4)作为唯一容器,使同一套编码输出可同时服务于 DASH MPD 和 HLS M3U8。

CMAF 层级结构
复制代码
CMAF Switching Set(切换集)
├── 多个 CMAF Track(轨道)--- 同一内容的不同码率/分辨率
│   ├── CMAF Track 由多个 CMAF Segment 组成
│   │   ├── CMAF Header(初始化段)--- moov box
│   │   └── CMAF Fragment(媒体片段)--- moof + mdat
│   │       ├── 完整 CMAF Segment = CMAF Header + 若干 CMAF Fragment
│   │       └── 每个 Fragment 以 SAP(Stream Access Point)开始
│   └── 同一 Switching Set 内的 Track 必须满足:
│       ├── 相同 codec(如都是 H.264)
│       ├── 相同 timescale
│       ├── 片段边界对齐(segmentAlignment=true)
│       └── 可无缝切换
CMAF vs 传统 fMP4
特性 传统 fMP4 CMAF fMP4
初始化段 自由结构 严格规范(moov 仅含初始化元数据)
媒体片段 moof+mdat moof+mdat(必须以 SAP 开始)
加密 各自定义 CMAF Encryption(cenc/cbcs 统一)
多轨复用 不规范 CMAF Selection Set(多 Switching Set 组合)
低延迟 不支持 CMAF Chunk(分块传输)

7.2 CMAF Chunk 与分块传输编码

传统 DASH/HLS 的最小传输单位是完整片段(一个 moof+mdat),客户端必须等整个片段编码+封装完毕才能开始下载。对于 2-6 秒的片段,这意味着至少 2-6 秒的端到端延迟。

CMAF Chunk(分块) 将一个 CMAF Fragment 进一步拆分为多个 CMAF Chunk,每个 Chunk 包含若干已编码的视频帧:

复制代码
传统方式 --- 完整片段下载:
  ┌─────────────────────────────────────┐
  │ CMAF Fragment (moof + mdat, 2-6s)   │ ← 等全部编码完才能下载
  └─────────────────────────────────────┘

CMAF Chunk 分块传输:
  ┌──────────┬──────────┬──────────┬──────┐
  │ Chunk 0  │ Chunk 1  │ Chunk 2  │ ...  │ ← 编码一部分就推送一部分
  │ moof+帧1 │ 帧2-3    │ 帧4-5    │      │
  └──────────┴──────────┴──────────┴──────┘
   ↑ 推送     ↑ 推送     ↑ 推送
   t=0       t=0.5s     t=1.0s

关键约束

  • Chunk 0 必须包含 moof box(片段元数据)和至少一个关键帧
  • 后续 Chunk 只包含 mdat 数据(连续的编码帧)
  • 每个 Chunk 边界不要求是 SAP,但 Chunk 0 必须以 SAP 开始
  • 客户端需要边接收边解析 ISOBMFF box 结构

7.3 Low-Latency DASH(LL-DASH)

LL-DASH(DASH-IF Low-Latency DASH)基于 CMAF Chunk + HTTP 分块传输编码实现亚秒级延迟:

协议层设计
复制代码
                    编码器                     CDN                    客户端
                      │                        │                       │
  编码 Chunk 0 ──────►│── HTTP Chunked ───────►│── HTTP Chunked ──────►│
  (moof+关键帧)       │   Transfer-Encoding    │   Transfer-Encoding   │ 解析 moof
                      │   : chunked            │   : chunked           │ 解码关键帧
  编码 Chunk 1 ──────►│── 推送 ───────────────►│── 推送 ──────────────►│ 解码帧
  (后续帧)            │                        │                       │
  编码 Chunk 2 ──────►│── 推送 ───────────────►│── 推送 ──────────────►│
  ...                 │                        │                       │
                      │                        │                       │
  端到端延迟 ≈ 编码延迟 + 1 个 Chunk 传输时间 + 解码延迟
             ≈ 200ms ~ 1s(vs 传统 3-6s)

使用分块传输编码时,服务器不再发送 Content-Length 头部,而是在 HTTP 响应头中加上 Transfer-Encoding: chunked,数据分成一系列小块逐个发送:

复制代码
GET /video_720p_100.m4s HTTP/1.1
Host: cdn.example.com

────────────────────────────────────────

HTTP/1.1 200 OK
Content-Type: video/mp4
Transfer-Encoding: chunked

1a4c                          ← 十六进制 chunk 大小 (6,732 字节)
[moof box: 32字节 + traf/trun/tfhd 元数据]
[mdat 前部: 关键帧 NAL 单元数据]
                              ← 空行分隔

0f3a                          ← 下一个 chunk (3,898 字节)
[mdat 后续: P 帧数据]
                              ← 空行分隔

0a18                          ← 下一个 chunk (2,584 字节)
[mdat 后续: 更多帧数据]
                              ← 空行分隔

0                             ← 结束标记(0 长度 chunk)

要点

  • 每个 chunk 以十六进制长度行开头,后跟实际数据,空行分隔
  • 编码器产出一部分数据就立即以 chunk 推送,无需等整个片段编码完毕
  • 客户端边接收边解析 ISOBMFF box 结构(moof → 提取 PTS → 解码关键帧)
  • 最后以长度为 0 的 chunk 标记传输结束
MPD 扩展属性

LL-DASH 在 MPD 中通过以下方式声明低延迟能力:

xml 复制代码
<MPD type="dynamic"
     profiles="urn:mpeg:dash:profile:isoff-live:2011,http://dashif.org/guidelines/low-latency-v5"
     minimumUpdatePeriod="PT5S"
     minBufferTime="PT0.6S">              <!-- 极短缓冲 -->
  <Period>
    <AdaptationSet contentType="video" segmentAlignment="true">
      <SegmentTemplate timescale="90000"
                       media="chunk_$RepresentationID$_$Number$.m4s"
                       initialization="init_$RepresentationID$.mp4"
                       duration="180000">  <!-- 2 秒片段 -->
        <SegmentTimeline>
          <S t="0" d="180000" r="100"/>
        </SegmentTimeline>
      </SegmentTemplate>
      <Representation id="v1" bandwidth="2000000" width="1280" height="720"
                      codecs="avc1.64001f"/>
    </AdaptationSet>
  </Period>
</MPD>

LL-DASH 关键 MPD 特征

特征 传统 DASH LL-DASH
minBufferTime 2-4 秒 0.5-1 秒
availabilityStartTime 精度 秒级 毫秒级
片段可用性 整段可用后 逐 Chunk 可用
HTTP 传输 普通 GET Chunked Transfer Encoding
suggestedPresentationDelay 10-30 秒 1-3 秒
Profile isoff-live isoff-live + low-latency-v5
DASH-IF LL-DASH 关键规范点
  1. AvailabilityStartTime 精确计算 :Chunk 级别的可用时间 = AST + period_start + chunk_time_offset
  2. 部分片段请求:客户端可在片段未完全生成时发起请求,CDN 通过 chunked transfer 逐块推送
  3. $Time$ 模板寻址 :推荐使用 $Time$ 而非 $Number$,避免编号计算歧义
  4. Resync 机制@maxSegmentDuration + @availabilityStartTime 用于客户端重同步

7.4 Low-Latency HLS(LHLS)

LHLS 采用类似思路,通过 M3U8 新增标签实现:

复制代码
#EXTM3U
#EXT-X-TARGETDURATION:4
#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,PART-HOLD-BACK=1.0
#EXT-X-PART-INF:PART-TARGET=0.5
...
#EXT-X-PART:DURATION=0.5,URI="segment1_part1.m4s",INDEPENDENT=YES
#EXT-X-PART:DURATION=0.5,URI="segment1_part2.m4s"
#EXT-X-PART:DURATION=0.5,URI="segment1_part3.m4s"
#EXT-X-PART:DURATION=0.333,URI="segment1_part4.m4s"
#EXTINF:4.0,
segment1.m4s
LHLS 标签 说明
#EXT-X-PART-INF 声明部分片段目标时长
#EXT-X-PART 单个部分片段(= CMAF Chunk)
#EXT-X-SERVER-CONTROL 服务器控制参数(CAN-BLOCK-RELOAD、PART-HOLD-BACK)
INDEPENDENT=YES 标记该 Part 包含关键帧
_HLS_msn / _HLS_part 阻塞式请求参数,等待特定片段/部分可用

7.5 LL-DASH vs LHLS 对比

特性 LL-DASH LHLS
分块单位 CMAF Chunk Partial Segment(= CMAF Chunk)
传输方式 HTTP Chunked Transfer Encoding HTTP Chunked / 阻塞式请求
清单声明 MPD Profile + 短 minBufferTime #EXT-X-PART-INF + #EXT-X-PART
阻塞请求 无(CDN 自动推送 chunked) _HLS_msn + _HLS_part 参数
关键帧标记 CMAF SAP INDEPENDENT=YES
延迟目标 < 1 秒 < 2 秒
CDN 友好度 高(标准 HTTP chunked) 中(需支持阻塞请求)
规范成熟度 DASH-IF IOP v5+ HLS RFC 8216bis

7.6 当前 GStreamer 实现状态

此代码版本不支持 CMAF Chunk 分块传输和低延迟流

功能 状态 说明
CMAF 格式识别 无 CMAF profile 解析或标识
CMAF Chunk 下载 need_another_chunk 仅用于 trick mode,非低延迟流
HTTP Chunked Transfer UriDownloader 不支持 chunked transfer 编码的流式解析
LL-DASH Profile 不识别 low-latency-v5 profile
LHLS Part Segment 不解析 #EXT-X-PART 标签
阻塞式请求 不支持 _HLS_msn / _HLS_part 参数
边收边解 ISOBMFF ⚠️ 部分 trick mode 中有 moof 解析能力,但非用于低延迟播放

实现低延迟 DASH 需要的核心改动

  1. UriDownloader:支持 HTTP Chunked Transfer Encoding,边接收边回调数据
  2. ISOBMFF 流式解析:在数据不完整时解析 moof/traf/trun,提取 PTS 和解码时间
  3. MPD 解析:识别 LL-DASH profile,处理 Chunk 级别可用性计算
  4. 播放引擎:不等整段下载完就推送到解码器
  5. ABR 策略:基于 Chunk 级别带宽测量而非整段级别

8. 关键设计要点

8.1 直播延迟构成

复制代码
直播延迟构成:
  │
  ├─ 1. 编码延迟(不可控)
  ├─ 2. 片段可用性延迟 = maxSegmentDuration
  │     └─ 片段完整可用后才能请求
  ├─ 3. 建议延迟 = suggestedPresentationDelay 或 default_presentation_delay
  │     └─ 客户端主动从 live edge 往后退的距离
  └─ 4. 时钟偏差补偿
        └─ clock_compensation 修正客户端与服务器时间差

实际起始位置 = server_now - suggestedPresentationDelay

9. 与 HLS 的对比

特性 DASH HLS
清单格式 XML (MPD) M3U8 文本
片段寻址 SegmentTemplate / SegmentList / SegmentBase EXTINF + URI
URL 模板 $Number$/$Time$/$Bandwidth$/$RepresentationID$ 无(显式 URI)
自适应集 AdaptationSet(多表示) 变体流(多码率)
多周期 Period(内建支持) 无内建周期
直播刷新 minimumUpdatePeriod targetduration
时钟同步 UTCTiming(NTP/HTTP) 无内建时钟同步
加密/DRM ContentProtection(多 DRM 系统) AES-128(仅一种)
子片段 SIDX box
初始化段 Initialization (SegmentBase/Template) EXT-X-MAP
时间线 SegmentTimeline(精确每段时长) EXTINF(逐段声明)
基类 GstAdaptiveDemux(共享) GstAdaptiveDemux(共享)
低延迟 无 LHLS 等效 LHLS(部分片段)
MIME 类型 application/dash+xml application/x-hls
字节范围 indexRange / mediaRange EXT-X-BYTERANGE
多语言 AdaptationSet @lang + Role EXT-X-MEDIA LANGUAGE

10. 优缺点分析

10.1 MPD 解析模块(gstmpdparser.c / gstmpdclient.c)

优点

  • 节点层次完整:MPD 规范中所有主要元素(Period、AdaptationSet、Representation、Segment*、UTCTiming、ContentProtection)均有对应 GType
  • 三种片段寻址方式完整支持:SegmentBase(单文件+SIDX)、SegmentList(URL 列表)、SegmentTemplate(URL 模板)
  • SegmentTimeline 精确控制:S 元素的 t/d/r 参数完整解析,支持负数 r(重复至结束)
  • URL 模板替换严格遵循规范:$RepresentationID$$Number<format>$$Bandwidth<format>$$Time<format>$$$ 全部支持,含格式校验
  • BaseURL 四层拼接:MPD → Period → AdaptationSet → Representation 逐级组合,含查询字符串分离
  • 外部资源 (xlink) 解析:onLoad 模式自动下载替换,resolve-to-zero 特殊 URI 处理
  • Period 时长计算严格遵循 ISO 23009-1 5.3.2.1 规范

缺点

  • XML 解析基于 libxml2 DOM:大 MPD 文件(含大量 S 元素的长直播时间线)全量加载到内存
  • 无增量 MPD 更新:每次刷新都完整重新解析,不能只处理变化部分
  • xlink actuate="onRequest" 模式未支持:仅处理 onLoad
  • urn:mpeg:dash:profile:full:2011 等高级 profile 未识别
  • SubRepresentation 的 contentComponent 属性解析后未在播放流程中实际使用
  • Metrics / Reporting 节点解析后未在播放逻辑中使用

10.2 DASH Demux 播放流程(gstdashdemux.c)

优点

  • Period 管理完善:跨 Period 自动切换、流重建、时间戳连续性处理
  • SIDX 子片段拆分精细:支持 SIDX 解析后按 entry 边界切片、二分查找 seek、延迟 seek(pending_seek_ts)
  • ISOBMFF 解析深度:moof/traf/trun/tfhd 解析、关键帧表提取、moof 平均大小统计
  • Key-unit trick mode 完整实现:仅下载关键帧、QoS 反馈驱动、目标时间计算、指数移动平均平滑
  • 时钟同步丰富:支持 NTP、HTTP HEAD、HTTP XSD/ISO/NTP 多种 UTCTiming 方式
  • ContentProtection PSSH 提取:urn:uuid scheme 自动识别并发送 Protection 事件
  • Representation 切换约束全面:带宽、分辨率、帧率多维过滤

缺点

  • 无低延迟 DASH 支持:无 Chunked Transfer 编码、无 LTVD (Low-Latency DASH) profile 处理
  • DRM 解密未内建:ContentProtection 仅提取 PSSH 发送事件,不做实际解密,依赖下游 cenc 元素
  • 字幕支持不完整:APPLICATION 类型流仅在 contains_subtitles 时创建 pad,对 WebVTT timed text 等格式处理有限
  • trickmode_no_audio 时直接丢弃音频流:而非静音保留流结构,可能影响下游解码器状态
  • SIDX 不支持 reference_type=1:遇到引用类型为 1 的 SIDX entry 时无法处理
  • Representation 切换时完全重置 ISOBMFF 状态:moof、sync samples、adapter 全部清空,切换后首个 buffer 可能延迟

10.3 码率自适应算法

优点

  • 多维约束选择:带宽 + 分辨率 + 帧率同时过滤,防止选择解码器不支持的 Representation
  • 倍速适配:播放速率 > 1.0 时自动降低目标码率(bitrate /= ABS(rate)),保持实时性
  • 兜底策略:无合格 Representation 时回退到最低带宽
  • max_bitrate 属性限制:可限制视频解码器能力上限

缺点

  • 慢启动策略偏保守:初始选择最低带宽 Representation,首屏画质可能较差
  • 无带宽测量与自适应:stream_select_bitrate 仅依赖基类传入的 current_download_rate,自身不做带宽估算
  • 无码率爬升/下降策略:直接跳到目标 Representation,无渐进试探
  • 无防振荡机制:无最小切换间隔、无上升/下降不对称阈值
  • Profile 过滤缺失:选择 Representation 时不检查 codecsprofiles 兼容性

10.4 直播刷新与时钟同步

优点

  • UTCTiming 多协议支持:NTP / HTTP HEAD / HTTP XSD / HTTP ISO / HTTP NTP 五种方式
  • 时钟漂移补偿:clock_compensation 持续修正客户端与服务器时间差
  • 请求中点估算:client_now = (start + end) / 2 减少网络延迟误差
  • 自动重试:同步失败时缩短更新间隔(30s vs 30min)

缺点

  • 刷新间隔固定为 minimumUpdatePeriod:无法根据网络状况动态调整
  • NTP 同步 5 秒超时可能不够:高延迟网络环境下可能频繁超时
  • 首次时钟同步阻塞 setup_streams:如果 UTCTiming 服务器响应慢,启动延迟增加
  • 无 HTTP Date 头时区偏移处理:依赖 RFC 5322 解析,某些服务器返回非标准格式可能失败
  • minimumUpdatePeriod 上限 30 分钟:某些场景可能需要更频繁的刷新

10.5 Seek 处理

优点

  • SIDX 精确 seek:二分查找 + SNAP 标志处理
  • 延迟 seek:SIDX 未解析时存储 pending_seek_ts,解析后执行
  • 跨 Period seek:自动定位目标 Period 并重建流
  • Reverse seek:支持倒放,segment_repeat_index 边界处理

缺点

  • 有片段列表时线性扫描:大型 SegmentTimeline 可能扫描大量 S 元素
  • ISOBMFF seek 后 SNAP 标志被剥离:可能导致精确度下降
  • Period 切换 seek 时完全重建流:开销较大
  • 直播 seek 范围不考虑已下载片段:仅基于 MPD 时长参数计算

10.6 基类架构(GstAdaptiveDemux)

优点

  • 共享框架:HLS/DASH/Smooth Streaming 复用下载循环、ABR、流管理
  • header + index + data 三阶段下载:完美适配 DASH 的 init segment + sidx + media 模式
  • Preroll 同步暴露:所有流同时就绪才暴露,避免部分流配置
  • 每流独立 GstTask:多轨并行下载
  • need_another_chunk 虚函数:支持 DASH 的分块下载(key-unit trick mode)

缺点

  • manifest_lock 持有时间过长:下载循环持续持有,多流并发可能瓶颈
  • 下载速率测量在 queue2 之后:受下游反压影响,ABR 估算偏低
  • download_error_count 不区分错误类型:网络临时错误和内容错误同等对待

11. DASH 面试常考知识点

11.1 三种片段寻址方式的区别与适用场景

几乎必问。DASH 定义三种方式,核心区别在于如何定位片段:

寻址方式 定位方式 适用场景 Profile
SegmentBase SIDX 字节范围 单文件点播(一个 mp4 包含所有内容) ISOFF On-Demand
SegmentList URL 列表 片段数较少的点播 任意
SegmentTemplate URL 模板 + 编号/时间 直播 / 大量片段点播 ISOFF Live / Main

关键细节

  • SegmentBase 不需要为每个片段生成单独文件,但需要 SIDX box 索引,客户端按字节范围请求
  • SegmentTemplate 是唯一支持直播的方式(片段按需生成 URL,无需预知总数)
  • 三种方式可出现在 Period / AdaptationSet / Representation 任一层级,子层覆盖父层
  • SegmentTemplate + SegmentTimeline 提供最精确的片段时长控制(每段可不同)

11.2 SegmentTimeline S 元素详解

直播场景核心考点

xml 复制代码
<SegmentTimeline>
  <S t="0" d="900000" r="9"/>    <!-- 从 t=0,时长 900000,重复 9 次 → 共 10 段 -->
  <S d="450000"/>                  <!-- 时长 450000,r 默认 0 → 1 段 -->
  <S d="900000" r="-1"/>          <!-- r=-1:重复直到 Period 结束 -->
</SegmentTimeline>

r 值含义

  • r 缺省 = 0 → 该条目只代表 1 段
  • r = N (N>0) → 额外重复 N 次,共 N+1 段
  • r = -1 → 无限重复直到 Period 结束(直播常用)

t 值规则

  • 仅首个 S 必须指定 t
  • 后续 S 的 t 自动 = 前一个 S 的 t + d × (r+1)
  • 直播刷新 MPD 时,SegmentTimeline 追加新 S 元素

11.3 URL 模板 N u m b e r Number Number vs T i m e Time Time

标记 替换值 适用场景
$Number$ 片段编号(从 startNumber 开始递增) 点播 / 固定时长片段
$Time$ 片段起始时间(timescale 单位) 直播 / 可变时长片段

为什么直播推荐 $Time$

  • $Number$ 依赖 startNumber 和固定 duration 计算编号,片段时长不均匀时计算出错
  • $Time$ 直接使用 SegmentTimeline 中的 t 值,精确对应每段起始时间
  • 点播中片段时长通常固定,$Number$ 足够

格式化$Number%05d$ → 5 位零填充;$Time$ → 默认 %01d(64 位整数)

11.4 直播 vs 点播关键参数

参数 点播 (static) 直播 (dynamic) 说明
type static dynamic 必问:区分点播/直播
availabilityStartTime 不需要 必须 流开始可用的时间点
minimumUpdatePeriod 不需要 推荐 客户端刷新 MPD 的最短间隔
timeShiftBufferDepth 不需要 推荐 DVR 回看窗口(如 120 秒)
suggestedPresentationDelay 不需要 推荐 建议从 live edge 后退的距离
publishTime 不需要 推荐 MPD 发布时间,用于判断时效性
MPD 刷新 不需要 周期性 按 minimumUpdatePeriod 刷新

直播延迟构成(常考推导):

复制代码
总延迟 ≈ 编码延迟 + maxSegmentDuration + suggestedPresentationDelay + clock_compensation
  • maxSegmentDuration:片段必须完整可用才能请求
  • suggestedPresentationDelay:客户端主动后退,避免追到 live edge 导致缓冲
  • clock_compensation:客户端与服务器时钟偏差

11.5 BaseURL 四层拼接

拼接顺序:MPD → Period → AdaptationSet → Representation → 片段 URI

复制代码
最终 URL = resolve(MPD_base_uri,
              resolve(Period.BaseURL[0],
                resolve(AdaptationSet.BaseURL[0],
                  resolve(Representation.BaseURL[0],
                    fragment_uri))))

常考陷阱

  • 每层可选,缺失则跳过(不是每层必须有 BaseURL)
  • BaseURL 含查询字符串时,提取后存入 queryURL 单独附加(不是直接拼接)
  • 多 BaseURL 用于负载均衡(CDN 故障转移),baseURL_idx 默认 0
  • BaseURL 可以是相对路径(相对于上一层解析)或绝对路径

11.6 ABR 码率自适应策略

选择 Representation 的约束维度

  1. bandwidth ≤ 当前可用带宽
  2. width ≤ max_video_width(解码器能力)
  3. height ≤ max_video_height
  4. framerate ≤ max_video_framerate
  5. 倍速播放时:bitrate /= ABS(rate)(更快速度 → 更低质量)

常见 ABR 策略对比

策略 原理 优点 缺点
基于带宽 测量下载速率,选择 ≤ 带宽的最高码率 简单直接 测量有滞后
基于缓冲 缓冲充足时升级,不足时降级 平滑 反应慢
混合策略 带宽 + 缓冲联合决策 稳定 复杂度高

防振荡机制(GStreamer 缺失):

  • 最小切换间隔:避免频繁切换
  • 上升/下降不对称阈值:升级需更高带宽确认,降级更敏感
  • 粘性因子:倾向保持当前码率

11.7 DRM 与 ContentProtection

ContentProtection 识别流程

  1. schemeIdUriurn:uuid: 开头 → 识别为特定 DRM 系统
  2. 常见 UUID:
    • Widevine:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed
    • PlayReady:9a04f079-9840-4286-ab92-e65be0885f95
    • FairPlay(HLS):94ce86fb-07ff-4f43-adb8-93d2fa968ca2
  3. 从子元素 cenc:pssh 提取 PSSH box 数据
  4. 发送 GST_EVENT_PROTECTION 事件到下游,由解密插件处理

多 DRM 同时声明:一个 AdaptationSet 可包含多个 ContentProtection(Widevine + PlayReady),客户端选择自身支持的 DRM 系统。

11.8 CMAF 与低延迟 DASH

CMAF 核心价值:一次编码、双协议分发(DASH MPD + HLS M3U8 共享同一套 fMP4 片段)

LL-DASH 实现原理

复制代码
传统 DASH:等整个片段编码完 → 下载 → 解码(延迟 3-6s)
LL-DASH:  编码器产出 Chunk → HTTP Chunked Transfer 立即推送 → 边收边解(延迟 < 1s)

LL-DASH vs LHLS 对比

特性 LL-DASH LHLS
分块单位 CMAF Chunk Partial Segment (= CMAF Chunk)
传输方式 HTTP Chunked Transfer Encoding 阻塞式请求 (_HLS_msn + _HLS_part)
清单声明 MPD Profile + 短 minBufferTime #EXT-X-PART-INF + #EXT-X-PART
CDN 友好度 高(标准 HTTP chunked) 中(需支持阻塞请求)
延迟目标 < 1 秒 < 2 秒

11.9 SIDX 子片段拆分

适用场景:ISOFF On-Demand profile,单文件点播

下载序列

  1. 下载初始化段 [0, indexRange.first-1] → moov box
  2. 下载 SIDX 索引 [indexRange] → sidx box
  3. 解析 SIDX → 得到子片段字节范围列表
  4. 按 SIDX entry 顺序逐片段下载

SIDX entry 结构 :每个 entry 包含 referenced_size(字节大小)和 subsegment_duration(时长),用于精确定位和 seek。

11.10 UTCTiming 时钟同步

支持的同步方式

方式 原理 精度 适用场景
NTP/SNTP 连接 NTP 服务器 毫秒级 精度要求高
HTTP HEAD 解析 Date: 响应头 秒级 最简单
HTTP XSD/ISO 解析响应体日期 秒级 NTP 不可用时
direct value 即为 UTC 时间 秒级 静态偏移

clock_compensation 计算

复制代码
client_now = (request_start_time + request_end_time) / 2   // 取中点减少网络延迟误差
clock_compensation = server_now - client_now
server_now_actual = client_now + clock_compensation

11.11 ISOBMFF Box 结构与播放流程

DASH 片段基于 ISO Base Media File Format(ISOBMFF),理解 box 结构是排查播放问题的关键:

复制代码
初始化段(init segment):
  ┌─────────────────────────────┐
  │ ftyp box --- 文件类型声明       │
  │ moov box --- 电影元数据         │
  │   ├── mvhd --- 电影头(时长等)  │
  │   ├── trak --- 轨道             │
  │   │   ├── tkhd --- 轨道头       │
  │   │   ├── mdia --- 媒体         │
  │   │   │   ├── mdhd --- 媒体头(timescale)
  │   │   │   ├── hdlr --- 处理器类型
  │   │   │   └── minf/stbl --- 采样表
  │   │   │       ├── stsd --- 采样描述(codec 配置)
  │   │   │       ├── stts --- 时间-采样映射
  │   │   │       ├── stss --- 关键帧表
  │   │   │       └── stsc/stsz/stco --- 采样布局
  │   │   └── ...
  │   └── ...
  └─────────────────────────────┘

媒体片段(media segment):
  ┌─────────────────────────────┐
  │ moof box --- 电影片段元数据     │
  │   ├── mfhd --- 片段头(序号)    │
  │   └── traf --- 轨道片段         │
  │       ├── tfhd --- 轨道片段头   │
  │       ├── tfdt --- 基础解码时间  │
  │       └── trun --- 轨道片段运行 │
  │           ├── 样本数          │
  │           ├── 样本时长列表    │
  │           ├── 样本大小列表    │
  │           └── 样本偏移列表    │
  ├─────────────────────────────┤
  │ mdat box --- 媒体数据           │
  │   └── 实际编码帧数据          │
  └─────────────────────────────┘

SIDX 索引(on-demand profile):
  ┌─────────────────────────────┐
  │ sidx box --- 片段索引          │
  │   ├── reference_ID           │
  │   ├── timescale              │
  │   ├── earliest_presentation_time
  │   ├── first_offset           │
  │   └── entries[]:             │
  │       ├── reference_type (0=media, 1=sidx)
  │       ├── referenced_size (字节大小)
  │       └── subsegment_duration
  └─────────────────────────────┘

关键 box 在播放流程中的角色

Box 作用 使用场景
moov 全局元数据(轨道定义、codec 配置) 初始化段,只需下载一次
moof 片段元数据(采样布局、时间戳) 每个媒体片段开头
mdat 实际编码帧数据 跟在 moof 后面
sidx 子片段字节索引 on-demand profile,支持部分下载和精确 seek
tfdt 基础解码时间戳 计算 PTS,处理 presentationTimeOffset
trun 采样布局(时长、大小、偏移) trick mode 中提取关键帧位置

moof+mdat 配对:每个媒体片段由一个 moof(描述数据布局)+ 一个 mdat(实际数据)组成。GStreamer 解析 moof 获取关键帧表和采样偏移,用于 trick mode 和 seek。

相关推荐
ltlovezh15 小时前
ROI 编码学习指南:Android 与 FFmpeg 的真实实现边界
android·ffmpeg·音视频开发
m0_747124532 天前
多媒体框架 FFmpeg 和 GStreamer
ffmpeg·gstreamer
小鹿研究点东西2 天前
AI直播系统怎么搭?
人工智能·ffmpeg·自动化·音视频·语音识别
Nightwish52 天前
Oracle 数据库巡检检查清单
数据库·oracle·ffmpeg
luoyayun3612 天前
Qt/QML + FFmpeg 实现多音频文件顺序拼接功能
qt·ffmpeg·音频拼接
wbcuc3 天前
ffmpeg工具把m4s合并为mp4 powershell脚本
ffmpeg·m4s
luoyayun3614 天前
Qt + FFmpeg 实战:实现音频格式转换功能
qt·ffmpeg·音频格式转换
都在酒里5 天前
【极致低延时】香橙派部署 MediaMTX 实现 WebRTC 推流,延时仅 500-800ms,比局域网 ffmpeg 拉流快近 10 倍!(附踩坑全记录)
linux·arm开发·ffmpeg·webrtc·orangepi·嵌入式软件
Empty-Filled5 天前
用 Kap + FFmpeg 把录屏转成小体积 GIF:产品操作演示图制作实践
ffmpeg·kap