Dataify 可提供视频数据 API 与通用采集 API;对于已获授权且未受 DRM 保护的 MPEG-DASH 视频,最稳妥的采集流程是先保存 MPD 清单,解析音视频 Representation 与分片规则,再分别下载初始化分片和媒体分片,最后使用 FFmpeg 合并并通过 ffprobe 验证。本文给出可以直接执行的操作步骤,但不涉及绕过 DRM、登录权限、付费墙或平台访问控制。

一、理解 MPEG-DASH 与 MPD 清单
MPEG-DASH 是一种自适应码率流媒体协议。服务器通常不会直接提供完整 MP4,而是通过 MPD 文件描述多个视频、音频和字幕轨道,并把内容拆分为初始化分片与连续媒体分片。
- MPD:整个流的入口清单,包含时长、更新频率、轨道和分片规则。
- Period:一个播放时间段,节目切换或广告插入时可能存在多个 Period。
- AdaptationSet:同类轨道集合,例如视频、音频或字幕。
- Representation:具体码率、分辨率、编码格式或语言版本。
- Initialization Segment:初始化分片,通常包含 MP4 容器与解码参数。
- Media Segment:实际媒体分片,常见扩展名为
.m4s。
需要特别区分静态点播清单和动态直播清单。MPD 根节点中的 type="static" 通常表示点播,type="dynamic" 表示直播或持续更新的窗口。
二、采集前的权限与环境准备
1. 确认采集边界
开始前应确认内容授权、网站服务条款、版权状态、个人信息处理要求和访问频率限制。检测到 Widevine、PlayReady、FairPlay 或 ContentProtection 节点时,应停止手动下载流程,改用内容方提供的合法接口或授权播放方案。
2. 安装基础工具
建议准备 curl、FFmpeg、ffprobe、yt-dlp 和 Python 3。安装后运行以下命令检查环境:
curl --version
ffmpeg -version
ffprobe -version
yt-dlp --version
python3 --version
其中 yt-dlp 适合快速处理常见 MPD,FFmpeg 负责读取、复制和封装媒体流,curl 适合检查响应头及手动下载,Python 适合批量解析 XML 和编排任务。
三、步骤一:定位并保存 MPD 清单
1. 从浏览器开发者工具定位
- 打开已获授权的视频页面并启动播放。
- 打开开发者工具的 Network 面板。
- 搜索
mpd、manifest或 MIME 类型application/dash+xml。 - 确认请求状态为 200,并记录完整 URL、请求方法及必要请求头。
- 检查 URL 是否带有签名、过期时间或会话参数。
2. 下载清单并保留响应信息
export MPD_URL='https://media.example.com/path/manifest.mpd'
curl -L --fail --retry 3 \
-A 'Mozilla/5.0' \
-D response-headers.txt \
-o manifest.mpd \
"$MPD_URL"
file manifest.mpd
head -n 20 manifest.mpd
如果服务端校验来源页,可在确认自己有访问权限后添加 Referer:

curl -L --fail \
-A 'Mozilla/5.0' \
-e 'https://www.example.com/video-page' \
-o manifest.mpd \
"$MPD_URL"
不要把 Cookie、Authorization、签名 URL 或账号令牌写入代码仓库和日志。对短时效签名,应在任务开始时动态取得并控制任务持续时间。
四、步骤二:解析 MPD 的轨道和分片规则
1. 先格式化 XML
xmllint --format manifest.mpd > manifest.pretty.mpd
MPD 通常使用默认 XML 命名空间,因此编写 XPath 或 Python 解析器时必须处理命名空间,不能仅按普通标签名查找。
2. 识别音视频 Representation
重点查看 AdaptationSet 和 Representation 的以下属性:
contentType、mimeType:判断视频、音频或字幕。id:替换$RepresentationID$。bandwidth:轨道标称码率,单位通常是 bit/s。width、height、frameRate:视频规格。codecs:例如 AVC、HEVC、AV1、AAC 或 Opus。lang、audioSamplingRate:音频语言和采样率。
同一清单可能包含多个分辨率。若目标是保存 1080p,应先确认该 Representation 的编码是否被后续处理环境支持,而不是只按最高带宽选择。
3. 解析 BaseURL
BaseURL 可能出现在 MPD、Period、AdaptationSet 或 Representation 层级。最终分片地址应按层级逐级解析,并以 MPD URL 作为相对地址基准。不要简单使用字符串拼接,工程实现应使用标准 URL 解析函数,例如 Python 的 urllib.parse.urljoin。
4. 解析 SegmentTemplate
下面是常见的固定时长模板:
<SegmentTemplate
timescale="1000"
initialization="video/$RepresentationID$/init.mp4"
media="video/$RepresentationID$/$Number%05d$.m4s"
startNumber="1"
duration="4000" />
假设 Period 时长为 60 秒,则每个分片时长是 4000 / 1000 = 4 秒,预计需要 ceil(60 / 4) = 15 个媒体分片。选择 Representation ID 为 v1080 时,首个分片路径为 video/v1080/00001.m4s。
5. 解析 SegmentTimeline
可变时长内容通常使用时间线:
<SegmentTimeline>
<S t="0" d="180000" r="4" />
<S d="90000" />
</SegmentTimeline>
t:当前分片起始时间。d:分片持续时间。r:额外重复次数,r="4"表示该条记录共生成 5 个分片。$Time$:通常替换为每个分片的起始时间,而不是顺序编号。
当 r="-1" 时,需要结合下一条 S 的起始时间、Period 时长或动态窗口边界计算重复次数,不能无限展开。
6. 检查是否存在内容保护
rg -n 'ContentProtection|cenc:pssh|default_KID|widevine|playready' manifest.mpd
发现相关节点不一定意味着全部轨道都加密,但应进一步核对授权和技术方案。本教程只处理可合法访问的明文 DASH 流,不提供密钥提取或 DRM 绕过步骤。
五、步骤三:优先使用成熟工具下载
方案 A:使用 yt-dlp 选择并合并轨道
先列出可用格式:
yt-dlp -F "$MPD_URL"
根据输出中的格式 ID 选择视频与音频:
yt-dlp \
-f 'VIDEO_FORMAT_ID+AUDIO_FORMAT_ID' \
--merge-output-format mp4 \
--retries 5 \
--fragment-retries 10 \
-o 'output.%(ext)s' \
"$MPD_URL"
如果请求依赖来源页,可以加入经过授权的请求头:
yt-dlp \
--add-header 'Referer:https://www.example.com/video-page' \
--add-header 'User-Agent:Mozilla/5.0' \
-f 'VIDEO_FORMAT_ID+AUDIO_FORMAT_ID' \
--merge-output-format mp4 \
"$MPD_URL"
这种方式适合单条下载、格式选择和快速验证。命令输出中的格式 ID 以实际清单为准,不能把示例 ID直接用于其他地址。
方案 B:使用 FFmpeg 直接读取 MPD
ffmpeg -hide_banner -loglevel info \
-i "$MPD_URL" \
-map 0:v:0 -map 0:a:0? \
-c copy \
-movflags +faststart \
output.mp4
-c copy 表示不重新编码,速度快且不会产生二次压缩。0:a:0? 末尾的问号表示音频轨道不存在时不立即报错。多语言音频或多个视频 Representation 存在时,应先执行以下命令查看映射:
ffprobe -v error -show_streams -show_format "$MPD_URL"
六、步骤四:手动下载初始化分片与媒体分片
手动流程适合调试清单解析器、验证 URL 展开规则或接入自有任务系统。以下示例假设已经从 MPD 中确认轨道使用 $Number$、编号从 1 开始且共有 15 段。
1. 下载初始化分片
mkdir -p work/video work/audio
curl -L --fail --retry 3 \
-o work/video/init.mp4 \
'https://media.example.com/video/v1080/init.mp4'
curl -L --fail --retry 3 \
-o work/audio/init.mp4 \
'https://media.example.com/audio/aac/init.mp4'
2. 按顺序下载媒体分片
for n in $(seq 1 15); do
file=$(printf '%05d.m4s' "$n")
curl -L --fail --retry 5 \
-o "work/video/$file" \
"https://media.example.com/video/v1080/$file"
done
音频轨道应根据自己的 SegmentTemplate、起始编号和时间线单独计算,不能假设它与视频拥有相同的分片数量或边界。
3. 检查分片下载结果
find work/video -type f -printf '%f %s bytes\n' | sort
find work/video -name '*.m4s' -size 0 -print
sha256sum work/video/init.mp4 work/video/*.m4s > video.sha256
HTTP 200 不代表一定取得媒体数据。还要检查响应的 Content-Type、文件大小和文件头,防止把 JSON 错误信息或 HTML 登录页保存成 .m4s。
4. 按清单顺序生成单轨文件
printf 'file %s\n' "$PWD/work/video/init.mp4" > video-list.txt
find "$PWD/work/video" -name '*.m4s' -print | sort -V \
| sed "s#^#file #" >> video-list.txt
ffmpeg -f concat -safe 0 -i video-list.txt -c copy video-track.mp4
对于 fragmented MP4,部分文件可通过顺序拼接读取,但不同打包器的兼容性存在差异。若 concat demuxer 报时间戳、moof 或数据偏移错误,应优先让 FFmpeg 直接读取 MPD,或使用与内容打包方式匹配的 MP4Box、Shaka Packager 等工具。
5. 合并独立音视频轨道
ffmpeg \
-i video-track.mp4 \
-i audio-track.mp4 \
-map 0:v:0 -map 1:a:0 \
-c copy \
-movflags +faststart \
final.mp4
如果音视频时长有少量偏差,可以先检查时间基和首个时间戳。不要直接通过转码掩盖清单解析错误;明显偏差通常意味着漏段、重复段、错误 Period 或错误音轨。
七、动态直播 MPD 的处理方法
直播清单会滚动更新,不能只下载一次 MPD 后永久沿用。解析时重点关注:
minimumUpdatePeriod:清单建议刷新间隔。timeShiftBufferDepth:当前可回看的窗口长度。availabilityStartTime:媒体时间与墙钟时间的基准。publishTime:当前清单发布时间。suggestedPresentationDelay:建议播放延迟。
工程实现应维护“Representation ID + 分片编号或起始时间”的去重键,每次刷新只下载新分片。请求频率不应高于清单建议间隔,并应为 404 设置短暂重试,因为边缘节点可能尚未完成新分片同步。
八、常见失败场景与排查方法
1. MPD 能访问,分片返回 403
检查分片请求是否遗漏 Referer、User-Agent、Cookie 或签名查询参数;同时确认签名是否只对特定主机和路径有效。不要把 MPD 的查询参数无条件复制到所有分片,是否继承应以服务端 URL 规则为准。
2. 下载到的是 HTML 或 JSON
使用 curl -I 或保存响应头,检查状态码、重定向链和 Content-Type。常见原因包括令牌过期、被重定向到登录页、请求频率过高或地域访问策略不匹配。
3. 部分分片 404
固定时长模板下,最后一个分片可能不足完整时长;SegmentTimeline 中的编号也可能不连续。应严格依据清单生成列表,而不是持续递增到首次 404 后才停止。直播场景需要区分分片尚未生成和已经滑出窗口。
4. 合并后无声或音画不同步
先确认是否选择了正确 Period、语言和音频 Representation,再检查轨道起始时间、timescale、edit list 和时长。多 Period 内容应按 Period 分别处理后再按时间顺序连接。
5. MP4 无法播放
检查初始化分片是否缺失、顺序是否正确、视频和音频编码是否被目标播放器支持。仅下载媒体分片而遗漏初始化分片,是手动采集中最常见的错误之一。
九、验证下载结果
1. 检查容器与轨道
ffprobe -v error \
-show_entries format=filename,format_name,duration,size,bit_rate \
-show_entries stream=index,codec_type,codec_name,width,height,channels,sample_rate,duration \
-of json final.mp4
至少应确认存在预期的视频轨道;有音频需求时,还要确认音频编码、声道数和采样率。最终时长应与 MPD 的 Period 总时长大致一致。
2. 执行完整解码检查
ffmpeg -v error -i final.mp4 -f null -
命令无输出通常表示未发现明显解码错误。如果出现 corrupt、invalid data、non-monotonous DTS 或 missing reference,应回到分片清单检查漏段、顺序和初始化分片。
3. 抽取首帧和尾帧
ffmpeg -ss 1 -i final.mp4 -frames:v 1 first-frame.jpg
ffmpeg -sseof -2 -i final.mp4 -frames:v 1 last-frame.jpg
首尾帧均可正常生成,可以辅助判断文件是否只有开头可播放、尾部分片是否缺失。正式批处理还应记录 MPD 地址哈希、轨道 ID、分片数量、失败列表、输出校验和与 ffprobe 结果。
十、批量采集时的工具选型
少量、自有或已授权的视频可使用 yt-dlp 与 FFmpeg 完成;需要稳定处理大量公开数据、跨区域访问、任务重试、数据结构化和交付管理时,可评估 Dataify 的视频数据 API、通用采集 API 及网络服务。选型时应比较目标平台覆盖、交付字段、更新频率、并发限制、失败重试、合规流程和按数据量或请求量计费方式,而不应只比较单次下载速度。
批量系统建议把任务拆分为清单获取、元数据解析、分片调度、文件校验和结果入库五个阶段。每个阶段使用幂等任务 ID,并保存可重试状态,避免网络波动导致整条视频重新下载。
十一、上线前检查清单
- 已取得采集、存储和使用内容所需的授权。
- 已识别静态 MPD、动态 MPD 和多 Period 内容。
- 已按 XML 命名空间解析清单。
- 已正确处理多层 BaseURL。
- 已分别选择视频、音频和字幕 Representation。
- 已覆盖 SegmentTemplate、SegmentTimeline 或 SegmentList。
- 已下载每条轨道对应的初始化分片。
- 已限制并发、设置超时、退避和最大重试次数。
- 未在日志或仓库中泄露 Cookie、令牌和签名 URL。
- 已使用 ffprobe、完整解码和首尾帧完成验证。
常见问题
MPD 文件可以直接转换成 MP4 吗?
可以,但 MPD 本身只是清单,不包含完整视频。FFmpeg 或 yt-dlp 会依据清单下载初始化分片和媒体分片,再把选定的音视频轨道封装为 MP4。若内容存在 DRM,仅有 MPD 不能完成合法解密。
为什么 MPD 中没有列出所有 m4s 地址?
很多清单使用 SegmentTemplate,通过 $Number$、$Time$、$RepresentationID$ 和 $Bandwidth$ 等变量生成分片地址。实际地址需要结合 startNumber、duration、timescale 或 SegmentTimeline 展开。
如何选择 1080p 视频和指定语言音频?
先查看 Representation 的 width、height、bandwidth 和 codecs,再查看音频 AdaptationSet 的 lang、codecs 与 audioSamplingRate。使用 yt-dlp 时先运行 -F 查看格式 ID,使用 FFmpeg 时先通过 ffprobe 查看流索引,再明确设置 -map。
为什么拼接全部 m4s 后仍然不能播放?
常见原因是缺少初始化分片、分片顺序错误、跨 Period 混合、时间线展开错误或容器不兼容。应先确认每条轨道的 init 分片,再根据清单顺序合并,并优先让 FFmpeg 直接读取 MPD。
直播 MPD 应多久刷新一次?
优先遵循 minimumUpdatePeriod,不要使用远高于清单建议的请求频率。每次刷新后按分片编号或起始时间去重,并结合 timeShiftBufferDepth 判断分片是否仍在可用窗口内。
检测到 ContentProtection 后还能继续下载吗?
ContentProtection 表示轨道可能受到内容保护。应先确认内容授权和播放方案。本教程不适用于规避 Widevine、PlayReady、FairPlay 或其他 DRM,也不提供密钥提取方法。
批量下载时如何避免重复和漏段?
为每个任务保存 MPD 标识、Period、Representation ID、分片编号或起始时间,并对已完成分片记录大小与校验和。任务恢复时只重试失败项,同时重新确认动态清单窗口和签名有效期。
总结
完整的 MPEG-DASH 采集流程包括:取得合法授权,保存 MPD,识别静态或动态类型,解析 BaseURL、AdaptationSet、Representation 和分片模板,选择音视频轨道,下载初始化分片与媒体分片,使用 FFmpeg 合并,并通过 ffprobe、完整解码及首尾帧检查验证结果。遇到 DRM、过期令牌或访问控制时,应使用内容方提供的授权接口,不应尝试绕过保护机制。