HDS简介
HTTP Dynamic Streaming,是Adobe公司设计的协议,分为On-demand模式(点播)和live模式(直播)。Live模式与苹果公司HLS协议的直播类似,都是将直播流切片进行传输。
HDS直播流

① 请求f4m文件,只在播放开始请求一次
② 在f4m中找到abst url,请求abst文件。abst相当于切片列表,需要在直播过程中进行更新,因此播放器需要持续请求abst文件
③ 根据abst中的序号,请求fragment,播放fragment
直播是边录制边播放的过程,所以,数据流在直播过程中需要源源不断地从录制端传输到播放器。HTTP协议指定了我们对资源的请求规则和响应规则,而HDS协议则是在http协议的基础上规定了数据的传输行为以及传输内容的封装格式。以下是HDS协议的传输行为:
首先,播放器需要知道一个地址,这个地址代表某一个直播内容的地址,比如某一个电视频道的网络直播地址。这个地址其实是一个f4m文件(Flash Media Manifest),f4m文件中记录着多个视频流的地址,这些视频流都是同样的内容,不同的是他们的码率、语言等等。HDS主要用在码率自适应的场景,因此后面的介绍也围绕着码率自适应来说明。
播放器拿到f4m文件后,根据当前网络状况选择一路视频流进行播放,在播放的过程中如果遇到网络波动,播放器需要调整当前的播放码率,也就是选择f4m文件中其他码率的视频流进行传输。需要注意的是f4m只在播放器刚开始播放的时候被请求一次。
HDS播放视频流是通过短连接的方式,那么是如何实现源源不断地视频传输呢?答案就是切片列表和视频切片(f4f),服务端会随着直播的进行不断将直播流切成一个个短时间的视频片段,这些片段(fragment)被叫做f4f文件(Fragmented F4V file),这些片段能够独立播放,播放器通过不断请求视频片段和播放视频片段来实现直播的效果,播放器需要知道这些片段的名字才能知道发送一个什么样的URL来请求这些片,这些片的名字就来源于一个叫bootstrapInfo(abst)的列表中,abst会随着直播进行一直被服务器刷新,每当服务器切出新的视频片就将视频片的序号和时间戳等信息添加到列表中,播放器拿到序号后就可以去请求视频片段啦,因此,播放器需要不停去请求abst来更新列表项。协议文档中强调播放器应该拿比较新的视频片来播放,也就是接近列表末尾部分序号比较大的片。
视频切片URL构造
每个视频切片(fragment)都有一个独一无二的url,url的构造如下:
http://<ServerBaseUrl>/<MovieIdentifier><QualitySegmentUrlModifier>Seg<SegmentNumber>-Frag<FragmentNumber>
如果<ServerEntryCount>
为0,<ServerBaseUrl>
和末尾的斜杆应该被省略。
如果<QualityEntryCount>
为0,<QualitySegmentUrlModifier>
应该被省略。
括号中的字段定义在F4V协议中,序号没有前导0。
例如:http://www.adobe.com/MyMovie/highSeg1-Frag210
Abst文件URL: http://<ServerBaseUrl>/[abstURL]
切片URL: http://<ServerBaseUrl>/[mediaURL]Seg<SegmentNumber>-Frag<FragmentNumber>
“[abstURL]
”是f4m中abst标签的url属性值, “[mediaURL]
”是f4m中media标签的url属性值。<ServerBaseUrl>
使用f4m所在地址(路径不包括f4m文件名)作为ServerBaseUrl。
F4M文件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns="http://ns.adobe.com/f4m/1.0">
<id>stream1.smil</id>
<mimeType>video/mp4</mimeType>
<streamType>live</streamType>
<deliveryType>streaming</deliveryType>
<bootstrapInfo id="b1" profile="named" url="p1.abst"/>
<media bootstrapInfoId="b1" width="960" height="540" bitrate="830" url="m1.abst/">
<metadata>...</metadata>
</media>
<bootstrapInfo id="b2" profile="named" url="p2.abst"/>
<media bootstrapInfoId="b2" width="721" height="406" bitrate="488" url="m2.abst/">
<metadata>...</metadata>
</media>
<bootstrapInfo id="b3" profile="named" url="p3.abst"/>
<media bootstrapInfoId="b3" width="1024" height="576" bitrate="1464" url="m3.abst/">
<metadata>...</metadata>
</media>
<bootstrapInfo id="b4" profile="named" url="p4.abst"/>
<media bootstrapInfoId="b4" width="1280" height="720" bitrate="1953" url="m4.abst/">
<metadata>...</metadata>
</media>
</manifest>
Flash Media Manifest,表示一个媒体内容,比如一个电视频道的网络直播,f4m中记录着内容相同但类型不同的视频流,比如不同码率的视频流,播放器可以根据当前的网络状况选择一路视频流进行播放,当网络环境波动时播放器可以切换码率(码率自适应)。f4m只在播放时请求一次。f4m中有两个重要的标签:
<media>
: 表示一个视频流信息,包含该视频流宽、高、比特率及url等信息
<bootstrapInfo>
: 视频流的播放信息,包含abst文件的地址或者abst信息
<streamType>
: 流的类型,分为“live”、“recorded”、“liveOrRecorded”三种类型,直播流为“live”
<deliveryType>
: 分发类型,分为“streaming”、“progressive”,HDS协议为“streaming”
abst

即Bootstrap Info,切片列表信息,定义在f4v协议中的box类型,记录切片的时间戳、序号和时长等信息,列表信息会随着直播的进行不断更新,播放器可以根据列表中的序号去请求对应的切片。abst中包含asrt、afrt。
字段 | 类型 | 描述 |
---|---|---|
Header | BOXHEADER | F4V BoxHeader, BoxType= ‘abst’ (0x61627374) |
Version | UI8 | 0或1 |
Flags | UI24 | 保留 |
BootstrapVertion | UI32 | abst序号 |
Profile | UI2 | |
Live | UI1 | =1时表示直播流 |
Update | UI1 | |
Reserved | UI4 | |
Timescale | UI32 | 用来计算时间单位,单位=1/Timescale(s) |
CurrentMediaTime | UI64 | 列表中最后一个切片的时间戳 |
SmpteTimeCodeOffset | UI64 | |
MovieIdentifier | String | |
ServerEntryCount | UI8 | ServerEntryTable的大小(数组元素个数) |
ServerEntryTable | SERVERENTRY [ServerEntryCount] | |
QualityEntryCount | UI8 | |
QualityEntryTable | QUALITYENTRY [QualityEntryCount] | |
DrmData | String | |
Metadata | String | |
SegmentRunTableCount | UI8 | SegmentRunTableEntries的大小(数组元素个数) |
SegmentRunTableEntries | SegmentRunTable [SegmentRunTableCount] | asrt数组 |
FragmentRunTableCount | UI8 | FragmentRunTableEntries的大小(数组元素个数) |
FragmentRunTableEntries | FragmentRunTable [FragmentRunTableCount] | afrt数组 |
字段 | 类型 | 描述 |
---|---|---|
ServerBaseURL | String |
字段 | 类型 | 描述 |
---|---|---|
QualitySegmentUrlModifier | String |
asrt
asrt用来定位Segment。segment的序号从asrt的SegmentRunEntryTable中计算出来。 SegmentRunEntryTable是一个数组,每个元素有两个比较重要的字段。
字段 | 类型 | 描述 |
---|---|---|
Header | BOXHEADER | BoxType = ‘asrt’ (0x61737274) |
Version | UI8 | 0或1 |
Flags | UI24 | |
QualityEntryCount | UI8 | |
QualitySegmentUrlModifiers | String | |
SegmentRunEntryCount | UI32 | SegmentRunEntryTable的大小(数组元素个数) |
SegmentRunEntryTable | SEGMENTRUNENTRY [SegmentRunEntryCount] | Segment数组 |
字段 | 类型 | 描述 |
---|---|---|
FirstSegment | UI32 | 表示一组携带相同数目fragment的segment第一个segment序号 |
FragmentsPerSegment | UI32 | 每个segment包含多少个fragment |
举个栗子:
SegmentRunEntryTable[0].FirstSegment = 10
SegmentRunEntryTable[0].FragmentsPerSegment = 1
SegmentRunEntryTable[1].FirstSegment = 20
SegmentRunEntryTable[1].FragmentsPerSegment = 2
表示segment10 ~ segment19每个segment包含1个fragment,从segment20开始每个segment的fragment数量为2
afrt
afrt用来定位fragment(切片)。abst中FragmentRunEntryTable记录切片的序号、时间戳、时长等信息。FragmentRunEntryTable是数组,每个FragmentRunEntryTable对应一个切片。
字段 | 类型 | 描述 |
---|---|---|
Header | BOXHEADER | BoxType =’afrt’ (0x61667274) |
Version | UI8 | 0或1 |
Flags | UI24 | |
Timescale | UI32 | 用来计算时间单位,单位=1/Timescale(s) |
QualityEntryCount | UI8 | |
QualityEntryTable | QUALITYENTRY [QualityEntryCount] | |
FragmentRunEntryCount | UI32 | FragmentRunEntryTable的大小(数组元素个数) |
FragmentRunEntryTable | FRAGMENTRUNENTRY [FragmentRunEntryCount] | fragment数组 |
字段 | 类型 | 描述 |
---|---|---|
FirstFragment | UI32 | 切片序号 |
FirstFragmentTimestamp | UI64 | 时间戳,单位根据timescale计算 |
FragmentDuration | UI32 | 时长,单位根据timescale计算 |
DiscontinuityIndicator | IF FragmentDuration == 0 UI8 | 不连续描述符 |
F4V

f4v是mp4格式的一个变种,将数据放在各种box中存放,f4v在mp4的基础上加了几种类型的box,如abst、afra、afrt、asrt等等。Mp4类型的协议通常将整个媒体文件的某些类型信息统一存放在某一类box中,比如moov box中记录了整个文件里所有帧的dts、cts、帧大小以及位置等信息。
F4F

f4f是f4v文件的切片,包含4个box:afra、abst、moof、mdat。

其中:
afra:用于时移,其中记录了帧的pts和dataOffset等信息,通过pts和dataOffset这两个数据可以算出某个时间的帧在数据的哪个位置
abst:f4f中的abst和之前所说的abst不同。f4f中的abst记录当前abst文件与前一个abst文件的差异
moof:类似与moov,记录每个帧的dts、cts、位置等信息
mdat:音频帧和视频帧数据
PS: 实际应用中可以只包含mdat部分
mdat

HDS的mdat容纳的是flv tag数据,每个切片都包括解码所需的音频头和视频头,因此每个切片可以独立播放。因为数据是flv tag,所以每个tag已经携带了dts和cts信息,所以可以不依赖moof进行解码播放。