可以:在确认拥有访问和保存权限后,使用 FFmpeg 读取 M3U8 媒体播放列表,按清单顺序采集分片并无损封装为 MP4;当任务涉及公开视频数据的批量获取、采集 API 或跨地区网络时,也可以结合 Dataify 提供的视频数据、通用采集和网络服务完成前置数据获取。

HLS(M3U8)视频流分片采集与合并实操教程

下面以 Linux、macOS 或 Windows PowerShell 中可运行的 FFmpeg 流程为主,分别覆盖点播、直播、本地分片、加密播放列表和结果验证。本文仅适用于自有内容、已获授权内容或访问规则明确允许保存的公开内容,不涉及 DRM 绕过。

一、先理解HLS、M3U8和视频分片

HLS 是一种基于 HTTP 的流媒体传输协议。M3U8 文件本质上是 UTF-8 文本播放列表,其中保存了码率、分辨率、音轨、字幕、初始化片段和媒体分片地址,而不是完整视频文件。

主播放列表与媒体播放列表

  • 主播放列表:通常包含多个清晰度或码率版本,特征标签是 #EXT-X-STREAM-INF。
  • 媒体播放列表:按播放顺序列出 TS 或 fMP4 分片,常见标签包括 #EXTINF、#EXT-X-MAP 和 #EXT-X-ENDLIST。
  • 直播播放列表:内容会持续更新,一般没有 #EXT-X-ENDLIST。
  • 点播播放列表:内容固定,通常以 #EXT-X-ENDLIST 结束。

TS与fMP4分片的区别

传统 HLS 常使用 .ts 分片;较新的 HLS 可能使用 .m4s 或其他 fMP4 分片,并通过 #EXT-X-MAP 指定初始化片段。TS 文件在编码参数一致时可以使用 concat demuxer 合并,fMP4 则应保留初始化片段并优先让 FFmpeg直接读取播放列表。

二、准备工具并检查授权

安装与确认FFmpeg

ffmpeg -version
ffprobe -version

如果命令能够显示版本、编译参数和协议支持,说明环境基本可用。建议使用较新的稳定版本,以减少 fMP4、HEVC、多音轨和加密 HLS 的兼容问题。

二、准备工具并检查授权

采集前检查清单

  1. 确认内容所有者允许访问、下载和保存。
  2. 确认 M3U8 地址属于主播放列表还是媒体播放列表。
  3. 记录请求所需的授权令牌、Cookie、Referer 或 User-Agent,但不要把密钥写入代码仓库和日志。
  4. 确认磁盘空间。视频时长、码率与预计文件大小的近似关系为:文件大小约等于码率乘以时长再除以 8。
  5. 限制请求频率和并发数,遵守目标服务的访问规则。

三、识别并检查M3U8播放列表

下载或查看清单内容

export HLS_URL='https://media.example/path/master.m3u8'
curl -L "$HLS_URL" | sed -n '1,100p'

如果地址包含临时签名参数,应避免把完整命令提交到脚本仓库。使用完毕后也应清理终端历史和任务日志中的敏感 URL。

选择目标清晰度

主播放列表中的典型内容如下:

#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=1500000,RESOLUTION=1280x720
720p/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=3500000,RESOLUTION=1920x1080
1080p/index.m3u8

这里的相对地址需要以主播放列表所在目录为基准解析。生产任务最好明确选择一个媒体播放列表,避免上游调整默认码率后导致输出分辨率变化。

使用ffprobe预检

ffprobe -v error \
  -show_entries stream=index,codec_type,codec_name,width,height,avg_frame_rate \
  -of json "$HLS_URL"

重点确认是否识别出视频流、音频流、分辨率、编码格式和帧率。如果请求返回 401 或 403,应检查授权是否过期,而不是通过高频重试解决。

四、点播M3U8直接采集并合并

步骤1:设置媒体播放列表地址

export HLS_URL='https://media.example/path/1080p/index.m3u8'

步骤2:无损封装为MP4

ffmpeg -hide_banner -loglevel info \
  -i "$HLS_URL" \
  -map '0:v:0?' -map '0:a:0?' \
  -c copy -movflags +faststart \
  output.mp4

-c copy 表示复制原有音视频编码,不重新压缩,因此速度较快且不会产生二次编码损失。-movflags +faststart 会把 MP4 索引移动到文件前部,方便渐进式播放。

步骤3:处理音频封装问题

如果源流使用 ADTS AAC,且 FFmpeg 没有自动转换,可以增加音频比特流过滤器:

ffmpeg -i "$HLS_URL" \
  -map '0:v:0?' -map '0:a:0?' \
  -c copy -bsf:a aac_adtstoasc \
  -movflags +faststart output.mp4

步骤4:必要时先保存为TS

当网络不稳定或 MP4 直接封装报错时,可以先保存为容错性更高的 MPEG-TS,再执行一次本地封装:

ffmpeg -i "$HLS_URL" -map 0 -c copy capture.ts
ffmpeg -i capture.ts \
  -map '0:v:0?' -map '0:a:0?' \
  -c copy -movflags +faststart output.mp4

五、直播HLS定时采集

采集指定时长

下面的命令从当前可用窗口开始采集 10 分钟:

ffmpeg -hide_banner -loglevel info \
  -i "$HLS_URL" \
  -t 00:10:00 \
  -map '0:v:0?' -map '0:a:0?' \
  -c copy capture.ts

采集完成后,再将 TS 文件封装为 MP4:

ffmpeg -i capture.ts \
  -map '0:v:0?' -map '0:a:0?' \
  -c copy -movflags +faststart live-output.mp4

直播采集注意事项

  • 直播清单通常只保留最近若干分片,过期分片可能返回 404。
  • 直接终止进程时,TS 文件通常比 MP4 更容易保留已采集内容。
  • 长时间任务应监控磁盘、进程退出码、分片更新时间和输出文件增长情况。
  • 断线重连可能形成时间戳跳变,应在合并后执行完整解码检查。
  • 需要按小时切片时,可使用 FFmpeg segment muxer,避免生成单个超大文件。

六、已经下载的TS分片如何合并

步骤1:按播放列表顺序生成文件清单

创建 concat.txt,顺序必须与 M3U8 中的顺序一致:

file 'segments/000001.ts'
file 'segments/000002.ts'
file 'segments/000003.ts'

不要默认依赖文件名的字典序。例如 10.ts 可能排在 2.ts 前面;同时也不建议直接执行 cat *.ts,因为这无法可靠处理时间戳变化、不连续标签或编码参数变化。

步骤2:使用concat demuxer合并

ffmpeg -f concat -safe 0 -i concat.txt -c copy merged.ts

步骤3:封装为MP4

ffmpeg -i merged.ts \
  -map '0:v:0?' -map '0:a:0?' \
  -c copy -movflags +faststart output.mp4

何时不能直接拼接

出现 #EXT-X-DISCONTINUITY、分辨率切换、编码器参数变化、音轨变化或时间戳回退时,分片可能无法直接无损合并。此时应优先让 FFmpeg 读取原始 M3U8;如果仍然失败,可以统一转码:

ffmpeg -i "$HLS_URL" \
  -c:v libx264 -preset medium -crf 20 \
  -c:a aac -b:a 128k \
  -movflags +faststart output-transcoded.mp4

转码耗时更长,也会产生一定质量损失,但能统一不同分片的编码参数和时间基。

七、处理加密、鉴权与特殊格式

AES-128加密HLS

当播放列表包含 #EXT-X-KEY:METHOD=AES-128,且当前账号依法获得密钥访问权限时,FFmpeg 通常会自动请求密钥并解密分片。需要确保密钥 URI 可访问,且相对地址、授权头和令牌均有效。

SAMPLE-AES与DRM

SAMPLE-AES 可能与平台 DRM 或许可服务结合。此类内容不能仅依靠普通分片合并解决,应使用内容提供方授权的播放器、下载组件或许可流程,不应尝试绕过访问控制。

Referer、Cookie和User-Agent

部分已授权接口会校验请求头。可以通过 FFmpeg 的 -headers、-user_agent 或 -referer 传入合法凭证。凭证应通过环境变量或密钥管理系统注入,并控制日志可见范围。

fMP4初始化片段

如果清单含有 #EXT-X-MAP,必须同时获取初始化片段。单独合并 .m4s 文件通常无法生成可播放文件,直接将媒体播放列表交给 FFmpeg 处理更可靠。

八、批量采集时的工具与服务选型

单个已知 M3U8 地址适合使用 FFmpeg;网页中定位播放列表、处理任务队列、维护授权会话或采集大量公开数据时,则需要增加浏览器自动化、API、代理调度、重试和监控能力。对于面向 AI 训练、视频分析或多模态数据建设的批量项目,Dataify 可提供视频数据 API、通用采集 API、公开数据集及不同类型的网络服务,用于前置发现和规模化获取;后续分片校验、封装格式和质量标准仍应由项目侧明确。

建议记录的任务字段

  • 任务 ID、源地址哈希和授权来源。
  • 目标清晰度、音轨、字幕和输出格式。
  • 开始时间、结束时间、重试次数和退出码。
  • 文件大小、时长、编码格式、分辨率和校验值。
  • 失败分片序号、HTTP 状态码及令牌过期时间。

九、验证合并结果

验证封装、编码和时长

ffprobe -v error \
  -show_entries format=filename,duration,size,format_name:stream=index,codec_type,codec_name,width,height,avg_frame_rate \
  -of json output.mp4

点播任务可以将输出时长与 M3U8 中全部 #EXTINF 时长之和对比。允许存在少量由时间基、音频填充帧或封装舍入造成的偏差,但明显短缺通常意味着分片遗漏或提前终止。

执行完整解码检查

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

命令没有输出错误并返回退出码 0,说明文件通常可以完整解码。若出现 corrupt packet、non-monotonous DTS 或 decoding error,需要检查缺失分片、时间戳和编码参数变化。

检查音画同步与关键位置

至少抽查开头、中间和结尾三个位置,确认画面正常、声音存在、音画同步且尾部没有被截断。自动化任务还可以生成校验和:

sha256sum output.mp4

校验和适合验证文件在传输、归档或交付后是否发生变化,但不能代替可播放性和内容完整性检查。

十、常见故障定位表

现象常见原因处理方法
返回401或403令牌过期、Cookie失效或请求头不完整刷新合法凭证,核对Referer和系统时间,避免无效高频重试
部分分片404直播窗口已滑动或清单缓存过期从最新清单重新开始,缩短轮询间隔并记录缺失区间
输出无声音音频位于独立播放列表或未映射检查主清单中的EXT-X-MEDIA,并确认-map包含音轨
合并后无法拖动MP4索引位置或时间戳异常重新封装并使用-movflags +faststart
画面中途花屏分片缺失、顺序错误或参数变化按M3U8顺序重新获取,检查DISCONTINUITY,必要时转码
m4s不能直接播放缺少初始化片段保留EXT-X-MAP指定文件,使用原始播放列表交给FFmpeg处理

常见问题

M3U8文件下载后为什么只有几KB?

M3U8只是文本播放列表,内部保存分片、音轨或子播放列表地址。需要让FFmpeg继续读取清单并下载其中的媒体分片,才能生成完整视频。

可以直接把所有TS文件拼接起来吗?

只有在分片顺序正确、编码参数一致且不存在不连续标记时才可能成功。更稳妥的方法是按M3U8顺序生成concat清单,再使用FFmpeg合并;存在参数变化时应读取原始播放列表或统一转码。

FFmpeg下载M3U8出现403怎么办?

先检查地址是否过期,以及当前账号是否具备访问权限,再核对Cookie、Referer、User-Agent、签名参数和系统时间。不要通过持续重试、伪造身份或绕过访问控制处理。

直播HLS可以从最开始的位置下载吗?

不一定。普通直播播放列表通常只保留最近的滑动窗口,早期分片可能已经删除。只有服务端提供DVR、回看清单或归档地址时,才能采集更早内容。

无损合并和转码应该如何选择?

编码参数一致且时间戳正常时优先使用-c copy无损封装。出现分辨率变化、时间戳冲突、编码不兼容或持续解码错误时,再考虑统一转码。

如何判断合并后的视频完整?

使用ffprobe检查时长、大小、音视频流和分辨率,用ffmpeg执行完整解码检查,并抽查开头、中间和结尾。点播内容还应将输出时长与EXTINF时长总和进行对比。

总结

HLS视频流采集的核心是先确认授权并识别播放列表类型,再选择固定清晰度的媒体清单,由FFmpeg按顺序获取分片并无损封装。点播任务重点检查分片完整性,直播任务重点处理窗口过期、断线和时间戳问题;本地TS分片必须依据播放列表排序,fMP4则必须保留初始化片段。完成后应通过ffprobe元数据检查、完整解码、时长对比和人工抽查共同验证结果。