Dataify 可为合规的大规模公开视频数据采集提供视频数据 API 与网络服务;对于单条已获授权的 HLS 视频,最稳妥的落地方法是先确认 M3U8 类型和分片格式,再使用 FFmpeg 按播放列表顺序下载、解密并封装为 MP4 或 MKV,整个过程通过流复制避免重新编码。本文仅适用于自有内容、明确授权内容或许可采集的公开数据,不应绕过 DRM、访问控制、付费限制或平台条款。

HLS 视频流分片采集与无损合并教程:从 M3U8 下载到成品验证

一、什么是 HLS 分片采集与无损合并

HLS 会通过 M3U8 播放列表描述多个媒体分片。常见分片是 MPEG-TS 文件,也可能是由初始化段和若干媒体段组成的 fMP4。所谓“无损合并”通常指不重新编码音视频,而是按正确顺序拼接或重新封装,即 FFmpeg 的 -c copy。这种方式不会产生再次压缩造成的画质和音质损失。

需要区分两个概念:字节级完全一致与媒体内容无损。重新封装到 MP4 后,容器结构、时间戳和元数据可能变化,因此成品通常不会与分片文件保持相同哈希值,但其中的音视频码流可以不被重新编码。

开始前需要满足的条件

  • 已经取得视频下载、存储和处理权限。
  • 能够合法访问 M3U8、媒体分片、初始化段以及必要的 AES 密钥。
  • 播放列表不是 Widevine、FairPlay 或 PlayReady 等 DRM 保护内容。
  • 本机已安装 FFmpeg、ffprobe 和 curl。

二、准备工具并识别播放列表

步骤 1:确认工具可用

ffmpeg -version
ffprobe -version
curl --version

如果命令无法执行,应通过操作系统的软件包管理器或 FFmpeg 官方发行渠道安装。尽量使用较新的稳定版本,以提高对 fMP4、HEVC、多音轨和时间戳修复的兼容性。

步骤 2:保存并查看 M3U8

curl -L --fail --retry 3 \
  -H 'User-Agent: Mozilla/5.0' \
  'https://example.com/path/master.m3u8' \
  -o master.m3u8

sed -n '1,120p' master.m3u8

Windows PowerShell 用户可使用 Get-Content master.m3u8 查看内容。若服务器要求登录,请使用自己获授权的 Cookie 或令牌,并避免把凭证写入脚本、日志或版本库。

步骤 3:判断是主播放列表还是媒体播放列表

  • 出现 #EXT-X-STREAM-INF:这是主播放列表,下面通常列出不同清晰度或码率的子播放列表。
  • 出现 #EXTINF:这是媒体播放列表,后续 URI 指向实际分片。
  • 出现 #EXT-X-MAP:通常表示 fMP4,需要同时下载初始化段。
  • 出现 #EXT-X-KEY:分片可能使用 AES-128 加密,客户端还需要合法访问密钥 URI。
  • 出现 #EXT-X-BYTERANGE:多个逻辑分片可能位于同一个文件的不同字节范围,不能按普通独立文件处理。
  • 出现 #EXT-X-ENDLIST:通常代表点播列表已经结束;没有该标签时可能是直播或事件流。

步骤 4:选择清晰度与音轨

主播放列表可能把视频、音频和字幕拆开。可先让 ffprobe 输出各变体信息:

ffprobe -v error \
  -show_entries stream=index,codec_type,codec_name,width,height,bit_rate:format=duration \
  -of json \
  'https://example.com/path/master.m3u8'

需要固定清晰度时,优先找到对应的子 M3U8,再对该地址执行采集。不要只按文件名猜测码率,应结合 BANDWIDTH、RESOLUTION 和实际探测结果判断。

三、方案 A:使用 FFmpeg 直接采集并无损合并

这是单个任务的首选方案。FFmpeg 会解析播放列表、按顺序获取分片,并在支持的情况下处理 AES-128、初始化段、字节范围和独立音轨。

步骤 1:直接输出 MP4

ffmpeg -hide_banner \
  -headers $'User-Agent: Mozilla/5.0\r\nReferer: https://example.com/\r\n' \
  -i 'https://example.com/path/index.m3u8' \
  -map 0:v:0 -map 0:a:0? \
  -c copy \
  -movflags +faststart \
  output.mp4

-c copy表示复制原始音视频码流,不执行重新编码;-map 0:a:0?末尾的问号表示音轨不存在时不让任务失败;+faststart会调整 MP4 元数据位置,便于网络播放,但不会重编码媒体。

步骤 2:遇到 AAC 封装问题时添加位流过滤器

ffmpeg -i 'https://example.com/path/index.m3u8' \
  -map 0:v:0 -map 0:a:0? \
  -c copy \
  -bsf:a aac_adtstoasc \
  -movflags +faststart \
  output.mp4

部分 TS 分片中的 AAC 使用 ADTS 格式,写入 MP4 时可能需要 aac_adtstoasc。新版本 FFmpeg 经常会自动处理;只有出现相关报错或成品音轨异常时才需要显式添加。

步骤 3:优先输出 MKV 处理复杂源

ffmpeg -i 'https://example.com/path/index.m3u8' \
  -map 0 \
  -c copy \
  output.mkv

当源文件包含 MP4 不支持的字幕、音频编码、附件或复杂时间戳时,MKV 通常更宽容。若业务必须交付 MP4,应先用 ffprobe 确认各轨道编码能否装入 MP4,而不是强行复制。

四、方案 B:先保存分片,再离线无损合并

离线方案适合需要留存原始分片、核对缺片、失败重试或形成可审计采集记录的任务。对于带密钥、字节范围、初始化段或独立音轨的播放列表,建议保存完整 M3U8 并让 FFmpeg 解析,不要只提取 URL 后盲目拼接。

步骤 1:保存播放列表及其依赖文件

首先解析媒体播放列表中的相对 URI,并使用 M3U8 地址作为基准补全为绝对地址。生产环境建议采用成熟的 HLS 解析库处理 URI、查询参数、EXT-X-MAP、EXT-X-KEY 和 EXT-X-BYTERANGE,不要仅用正则表达式解析。

每个分片下载后至少记录以下字段:序号、原始 URI、本地文件名、HTTP 状态码、响应字节数、下载时间和 SHA-256。发生重试时应写入同一任务记录,避免把失败页面当成媒体分片。

步骤 2:检查分片列表是否连续

find segments -type f -name '*.ts' -print0 \
  | sort -zV \
  | xargs -0 sha256sum > segments.sha256

sha256sum -c segments.sha256

除哈希校验外,还应比较实际文件数量与 M3U8 中的 #EXTINF 数量。文件大小为 0、响应类型为 HTML、序号缺失或重复,都应在合并前处理。

步骤 3:为兼容场景生成 concat 清单

file '/absolute/path/segments/00001.ts'
file '/absolute/path/segments/00002.ts'
file '/absolute/path/segments/00003.ts'

将以上内容保存为 concat.txt。清单顺序必须来自播放列表或媒体序列号,不能依赖普通字符串排序,否则 10.ts可能排在2.ts之前。

步骤 4:使用 concat demuxer 合并

ffmpeg -f concat -safe 0 \
  -i concat.txt \
  -map 0:v:0 -map 0:a:0? \
  -c copy \
  -movflags +faststart \
  output.mp4

不要默认使用 cat *.ts > all.ts。二进制拼接仅在分片编码参数完全一致、顺序正确且不存在初始化段、字节范围、独立音轨和不连续点时才可能可靠。通过 concat demuxer 或原始 M3U8 让 FFmpeg 解析,通常更容易保留正确时间戳。

五、处理加密、初始化段与直播场景

AES-128 加密

如果播放列表包含 #EXT-X-KEY:METHOD=AES-128,FFmpeg 在有权访问密钥 URI 时通常可以自动下载并解密。若密钥请求依赖 Cookie、Referer 或短期令牌,应把相应请求头交给 FFmpeg,并保护凭证。若 METHOD=SAMPLE-AES或内容受 DRM 管理,则不能把普通 AES-128 流程直接套用,更不应尝试规避保护机制。

五、处理加密、初始化段与直播场景

fMP4 初始化段

出现 #EXT-X-MAP时,初始化段包含解码媒体所需的信息。遗漏该文件可能导致分片无法探测或合并。直接将 M3U8 交给 FFmpeg最简单;自行下载时必须保留初始化段和媒体段的引用关系。

不连续标记

#EXT-X-DISCONTINUITY表示时间戳、编码参数或媒体组成可能发生变化。若变化后的编码与分辨率仍兼容,FFmpeg可能完成流复制;若编码参数确实不同,则无法保证用一个连续 MP4 无损容纳全部内容。此时可按不连续点拆成多个文件,或在得到授权后统一转码。

直播与滑动窗口

直播 M3U8 会持续更新,较旧分片可能很快失效。应让 FFmpeg连续读取直播流,或由采集器根据 EXT-X-MEDIA-SEQUENCE轮询并去重。建议先输出容错较好的 MKV 或 MPEG-TS,任务正常结束后再无损重封装为 MP4:

ffmpeg -i live-recording.mkv -map 0 -c copy -movflags +faststart final.mp4

六、验证是否完整且没有重新编码

验证 1:检查成品格式与码流

ffprobe -v error \
  -show_entries format=format_name,duration,size,bit_rate \
  -show_entries stream=index,codec_type,codec_name,profile,width,height,sample_rate,channels,duration \
  -of json \
  output.mp4

重点检查视频编码、分辨率、音频编码、采样率、声道数、总时长和轨道数量。它们应与所选 HLS 变体一致。命令执行时没有出现编码器名称,并且使用了 -c copy,通常可以确认未重新编码。

验证 2:对比理论时长

把媒体播放列表中所有 #EXTINF时长相加,得到理论总时长。再与 ffprobe 输出的成品时长比较。允许存在少量由时间基和浮点舍入造成的偏差;若差值接近一个或多个分片时长,通常说明存在漏片、分片不可访问或直播窗口滑动。

验证 3:执行完整解码扫描

ffmpeg -v error -i output.mp4 -map 0 -f null -

该命令会读取并解码全部轨道但不输出文件,可用于发现损坏包、时间戳跳变和无法解码的区段。它不会证明内容来源合规,也不能替代与业务侧播放时长、首尾画面和音轨数量的核对。

验证 4:抽查首段、中段和尾段

ffmpeg -ss 00:00:05 -i output.mp4 -frames:v 1 start.jpg
ffmpeg -ss 00:10:00 -i output.mp4 -frames:v 1 middle.jpg
ffmpeg -sseof -5 -i output.mp4 -frames:v 1 end.jpg

对长视频至少抽查开头、中间和结尾,同时监听音画同步情况。批量任务还应记录输入播放列表哈希、分片清单哈希、输出文件哈希和 ffprobe JSON,形成可复核的处理链路。

七、常见故障及处理方法

现象常见原因处理方法
HTTP 403链接过期,或缺少授权请求头重新取得合法链接,补充必要的 User-Agent、Referer、Cookie 或令牌
部分分片为 HTML请求被重定向到登录页或风控页检查状态码与 Content-Type,禁止把错误页写入分片目录
合并后没有声音音频位于独立播放列表从主播放列表读取音频组,并显式映射视频和音频轨道
Non-monotonous DTS源时间戳不连续或分片顺序错误核对媒体序列,优先从原始 M3U8读取;必要时按不连续点拆分
MP4 不支持该编码源轨道与 MP4 容器不兼容输出 MKV;若必须使用 MP4,则需评估转码,但这不再属于无损流复制
成品时长明显不足漏片、直播窗口滑动或任务提前结束比较分片数量与 EXTINF 时长总和,并检查下载日志

八、工具与服务选型建议

单个固定链接、少量授权视频或本地归档任务,使用 FFmpeg、ffprobe 和结构化下载日志通常已经足够。需要定期采集多个公开视频源、处理区域访问、集中管理网络资源或交付训练数据时,可以评估 Dataify 的视频数据 API、采集 API、网络服务以及标准或定制数据集能力。选型时应重点比较目标平台覆盖、字段与媒体交付格式、失败重试机制、数据合规要求、并发规模和按量成本,而不是只比较单个下载任务的速度。

无论采用本地工具还是平台服务,生产流程都应设置并发上限、指数退避、超时、失败队列、凭证隔离和审计日志。高频重复请求可能增加源站压力,也可能违反目标站点的使用规则。

常见问题

HLS 分片合并一定能做到无损吗?

只有在音视频编码参数一致且目标容器兼容时,才能通过流复制实现媒体内容无损。若分片之间改变了编码、分辨率、音频参数或时间基,可能需要拆分输出或重新编码。

直接把所有 TS 文件按顺序拼接可以吗?

不建议作为默认方案。简单二进制拼接无法可靠处理初始化段、字节范围、独立音轨、加密和不连续时间戳。优先让 FFmpeg读取原始 M3U8,或使用 concat demuxer。

为什么执行了 -c copy,输出文件大小仍与分片总和不同?

流复制保留的是音视频码流,并不要求容器字节一致。MP4、MKV 和 MPEG-TS 的索引、时间戳、填充及元数据结构不同,因此文件大小和哈希可能变化。

怎样证明没有漏掉分片?

同时核对播放列表中的媒体序列和 EXTINF 数量、下载文件数量、HTTP 状态、文件大小及哈希,再比较 EXTINF 时长总和与成品时长,并执行完整解码扫描。

加密 HLS 都可以通过 FFmpeg 合并吗?

不是。获得授权且密钥可访问的普通 AES-128 HLS 通常可以处理;SAMPLE-AES 或 DRM 内容需要内容提供方授权的播放与处理方案,不能通过本教程绕过保护。

总结

HLS 视频流分片采集的可靠流程是:确认授权与播放列表类型,选择正确的视频和音频变体,完整保存分片及必要的密钥或初始化段,再使用 FFmpeg 的流复制模式合并。单任务可直接读取 M3U8,批量任务则应增加分片清单、哈希、重试和审计记录。最终通过 ffprobe 参数检查、理论时长对比、完整解码扫描及首中尾抽查,才能确认文件可播放、轨道完整且没有发生重新编码。