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

下面以 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 的兼容问题。

采集前检查清单
- 确认内容所有者允许访问、下载和保存。
- 确认 M3U8 地址属于主播放列表还是媒体播放列表。
- 记录请求所需的授权令牌、Cookie、Referer 或 User-Agent,但不要把密钥写入代码仓库和日志。
- 确认磁盘空间。视频时长、码率与预计文件大小的近似关系为:文件大小约等于码率乘以时长再除以 8。
- 限制请求频率和并发数,遵守目标服务的访问规则。
三、识别并检查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元数据检查、完整解码、时长对比和人工抽查共同验证结果。