DASH(Dynamic Adaptive Streaming over HTTP,动态自适应流媒体传输)是一种基于 HTTP 的流媒体传输协议,类似于 HLS,但更通用,支持 MPEG-2 TS 和 ISO Base Media File Format(MP4) 等封装格式。它允许客户端根据网络状况动态切换码率,确保流畅播放。
特点
- 基于 HTTP:使用普通 Web 服务器进行内容分发,无需专用流媒体服务器。
- 自适应码率:客户端可根据带宽动态调整播放的码率,避免卡顿。
- 多种封装格式:支持 MP4(fMP4)、MPEG-2 TS 等容器格式。
- CDN 友好:DASH 片段是普通的 HTTP 文件,易于缓存和分发。
- 支持多种音视频编码:H.264、H.265、VP9、AV1 等。
工作流程
DASH 采用**分片(Segment)+ 清单文件(MPD,Media Presentation Description)**的方式传输流媒体内容。
基本组件
组件 | 作用 |
---|---|
MPD(Media Presentation Description) | 描述 DASH 播放列表,定义视频流的可用分辨率、码率、分片 URL。 |
Segment(媒体分片) | 视频/音频数据被切割成多个小片段,每个片段几秒钟,客户端可选择合适的分片下载。 |
Representation(表示) | 一个视频的不同码率/分辨率的版本,如 360p, 720p, 1080p。 |
AdaptationSet(适配集合) | 一组 Representation,通常是不同的音视频流,如多种语言音轨、不同清晰度视频流。 |
DASH 播放过程
- 客户端请求 MPD 文件
- 例如
https://example.com/video/manifest.mpd
- 例如
- 解析 MPD,获取可用码率、分辨率、片段 URL
- 根据网络情况,选择合适的码率(Representation)
- 下载首个分片(Segment)并开始播放
- 动态调整码率
- 如果网络带宽增加,切换到更高清的流;
- 如果网络变差,降级到低码率流,保证不卡顿。
- 持续请求新片段
- 例如
https://example.com/video/segment_1.m4s
,segment_2.m4s
,直到播放完毕。
- 例如
MPD 文件结构
MPD 是 XML 格式的清单文件,定义视频流的不同清晰度和码率。
示例:
<?xml version="1.0" encoding="UTF-8"?>
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" type="static" mediaPresentationDuration="PT10M">
<Period>
<AdaptationSet mimeType="video/mp4" segmentAlignment="true">
<!-- 1080p 版本 -->
<Representation id="1080p" bandwidth="5000000" width="1920" height="1080" codecs="avc1.640028">
<BaseURL>1080p/</BaseURL>
<SegmentTemplate timescale="1000" media="segment_$Number$.m4s" startNumber="1" />
</Representation>
<!-- 720p 版本 -->
<Representation id="720p" bandwidth="2500000" width="1280" height="720" codecs="avc1.64001F">
<BaseURL>720p/</BaseURL>
<SegmentTemplate timescale="1000" media="segment_$Number$.m4s" startNumber="1" />
</Representation>
</AdaptationSet>
</Period>
</MPD>
字段说明解释:
MPD
:根元素,定义整个 DASH 播放内容。Period
:表示一个播放周期(多个 Period 可用于直播)。AdaptationSet
:定义一组可互换的流,如不同分辨率的视频。Representation
:id="1080p"
:该流的 ID,表示 1080p 版本。bandwidth="5000000"
:码率 5Mbps。codecs="avc1.640028"
:H.264 编码。
BaseURL
:片段所在的路径。SegmentTemplate
:media="segment_$Number$.m4s"
:片段命名格式,如segment_1.m4s
、segment_2.m4s
。startNumber="1"
:从segment_1.m4s
开始播放。
DASH的模式
VOD(点播)
type="static"
(静态 MPD),MPD 预先定义好所有片段,适用于点播视频。- 片段是固定的,不会实时更新。
Live(直播)
type="dynamic"
(动态 MPD),MPD 会不断更新,支持直播流。直播 MPD 示例:
<MPD type="dynamic" minimumUpdatePeriod="PT5S"> <Period> <AdaptationSet> <SegmentTemplate media="live_$Number$.m4s" startNumber="1000" /> </AdaptationSet> </Period> </MPD>
minimumUpdatePeriod="PT5S"
:每 5 秒更新一次 MPD。startNumber="1000"
:直播流的分片编号从1000
开始。
DASH 与 HLS 对比
特性 | DASH | HLS |
---|---|---|
开发组织 | MPEG | Apple |
封装格式 | MP4(fMP4)、TS | TS、fMP4 |
码率切换 | 客户端自适应 | 客户端自适应 |
加密 | 支持 Widevine、PlayReady、FairPlay | 主要支持 FairPlay |
CDN 兼容性 | 好 | 更适合 Apple 生态 |
播放器支持 | 支持 HTML5、ExoPlayer、Shaka Player | Apple 设备原生支持 |
直播支持 | 是 | 是 |
- DASH 适用于 PC、Android、Windows 设备,但 iOS 设备上不原生支持。
- HLS 是 Apple 生态的标准,iPhone/iPad/Safari 直接支持。
DASH 播放器
常见的 DASH 播放器:
- Shaka Player(Google):开源,支持 DRM(推荐)
- dash.js(官方):MPEG-DASH 官方播放器
- ExoPlayer(Android):支持 DASH、HLS
- Video.js + Dash Plugin:支持 DASH 播放
示例:使用 dash.js 在 HTML5 中播放 DASH 视频:
<video id="video" controls></video>
<script src="https://cdn.dashjs.org/latest/dash.all.min.js"></script>
<script>
var player = dashjs.MediaPlayer().create();
player.initialize(document.getElementById("video"), "https://example.com/video/manifest.mpd", true);
</script>
DASH应用(c++)
C++ 解析 DASH MPD 文件
MPD 文件是 XML 格式,可以使用 TinyXML2、RapidXML 或 pugixml 解析它。
// c++ 使用 TinyXML2 解析MPD文件
#include <iostream>
#include <string>
#include <vector>
#include "tinyxml2.h"
using namespace tinyxml2;
using namespace std;
struct Representation {
string id;
int bandwidth;
int width, height;
string codecs;
string baseURL;
string segmentTemplate;
int startNumber;
};
vector<Representation> parseMPD(const string& xmlFile) {
XMLDocument doc;
if (doc.LoadFile(xmlFile.c_str()) != XML_SUCCESS) {
cerr << "Failed to load MPD file!" << endl;
return {};
}
vector<Representation> representations;
XMLElement* mpd = doc.FirstChildElement("MPD");
if (!mpd) return representations;
XMLElement* period = mpd->FirstChildElement("Period");
if (!period) return representations;
XMLElement* adaptationSet = period->FirstChildElement("AdaptationSet");
while (adaptationSet) {
XMLElement* representation = adaptationSet->FirstChildElement("Representation");
while (representation) {
Representation rep;
rep.id = representation->Attribute("id");
representation->QueryIntAttribute("bandwidth", &rep.bandwidth);
representation->QueryIntAttribute("width", &rep.width);
representation->QueryIntAttribute("height", &rep.height);
rep.codecs = representation->Attribute("codecs");
XMLElement* baseURL = representation->FirstChildElement("BaseURL");
if (baseURL) rep.baseURL = baseURL->GetText();
XMLElement* segmentTemplate = representation->FirstChildElement("SegmentTemplate");
if (segmentTemplate) {
rep.segmentTemplate = segmentTemplate->Attribute("media");
segmentTemplate->QueryIntAttribute("startNumber", &rep.startNumber);
}
representations.push_back(rep);
representation = representation->NextSiblingElement("Representation");
}
adaptationSet = adaptationSet->NextSiblingElement("AdaptationSet");
}
return representations;
}
int main() {
string xmlFile = "dash.mpd";
vector<Representation> reps = parseMPD(xmlFile);
for (const auto& rep : reps) {
cout << "ID: " << rep.id << " | Bandwidth: " << rep.bandwidth << " | Resolution: "
<< rep.width << "x" << rep.height << " | URL: " << rep.baseURL << endl;
}
return 0;
}
解析结果示例:
ID: 1080p | Bandwidth: 5000000 | Resolution: 1920x1080 | URL: https://example.com/dash/1080p/
ID: 720p | Bandwidth: 2500000 | Resolution: 1280x720 | URL: https://example.com/dash/720p/
C++ 下载 DASH 片段
// c++使用 libcurl 下载 DASH 片段
#include <iostream>
#include <fstream>
#include <curl/curl.h>
using namespace std;
size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {
((ofstream*)userp)->write((char*)contents, size * nmemb);
return size * nmemb;
}
bool downloadSegment(const string& url, const string& filename) {
CURL* curl = curl_easy_init();
if (!curl) return false;
ofstream file(filename, ios::binary);
if (!file.is_open()) return false;
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &file);
CURLcode res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
file.close();
return (res == CURLE_OK);
}
int main() {
string url = "https://example.com/dash/1080p/segment_1.m4s";
string filename = "segment_1.m4s";
if (downloadSegment(url, filename)) {
cout << "Download successful: " << filename << endl;
} else {
cerr << "Download failed!" << endl;
}
return 0;
}
C++ 生成 DASH MP4 片段
可以使用 FFmpeg 进行 MP4 分片:
ffmpeg -i input.mp4 -c copy -map 0 -f dash -segment_time 4 output.mpd
或者用 GPAC MP4Box:
MP4Box -dash 4000 -frag 4000 -segment-name segment_ output.mp4
DASH 播放(C+++WebRTC/SRS)
如果要搭建 DASH 服务器,可以使用 SRS(Simple Realtime Server):
./objs/srs -c conf/dash.conf
SRS 支持 DASH 直播,将流转换为 DASH 格式,MPD 地址类似:
http://yourserver/live/output.mpd
然后可以在前端使用 dash.js 播放:
<video id="video" controls></video>
<script src="https://cdn.dashjs.org/latest/dash.all.min.js"></script>
<script>
var player = dashjs.MediaPlayer().create();
player.initialize(document.getElementById("video"), "http://yourserver/live/output.mpd", true);
</script>
常见问题
1. 为什么 DASH 播放有延迟?
- DASH 直播默认会有几秒钟缓存,减少卡顿。如果要降低延迟,可以调整
SegmentTimeline
和buffering settings
。
2. DASH 适合 WebRTC 低延迟直播吗?
- 不适合。DASH 主要用于 VOD 和传统直播,WebRTC 更适合低延迟交互式直播。
总结
- DASH 是 HTTP 传输协议,不依赖专用流媒体服务器。
- 支持自适应码率,确保流畅播放。
- 适用于 VOD 和直播,兼容各种编码格式。
- 播放器支持需要额外集成(如 dash.js、Shaka Player)。