M3U8 下载失败怎么办?HLS 流媒体排查

排查 M3U8 和 HLS 下载失败,包括播放列表检测、CORS、过期链接、Referer、DRM 和分片问题。

M3U8 下载失败通常是播放列表未检测到、在线工具受到跨域限制,或流媒体需要原视频页面中的访问信息。本文按 HLS 的实际下载流水线逐步拆解,告诉你失败发生在哪一环、为什么、怎么修。


先理解:HLS 下载流水线

HLS 不是一条 URL 拉一个文件那么简单。它是一条流水线,任何一环出错,最终表现都是"下载失败"或"文件坏了"。先认清这条线,排查才有方向:

① 检测 M3U8        ② 解析清单        ③ 下载分片         ④ 解密         ⑤ 合并/转封装
   ↓                  ↓                  ↓                 ↓               ↓
 webRequest        m3u8-parser       Worker Pool      WebCrypto       FFmpeg WASM
 拦截请求          区分 Master/      并发拉 .ts       AES-128-CBC     TS → MP4
                   Media             重试退避          (如需要)        或直接拼接
环节失败表现本文对应章节
① 检测弹出窗口显示"未检测到媒体"检测不到 M3U8
② 解析清单下下来了但画质/分片列表为空清单解析异常
③ 下载进度卡住、403、404、CORS分片下载失败
④ 解密解密报错或合并后无声/花屏加密流解密失败
⑤ 合并文件生成但无法播放、音画不同步合并后无法播放
HLS 协议与清单结构(Master vs Media Playlist、EXT-X-KEY、多音轨)的完整说明,请参阅 视频嗅探 — HLS 流。想看真实清单长什么样、为什么"朴素下载器"会翻车,可以翻 HLS 深度篇

快速处理

在深入排查前,先跑一遍这套 90% 场景能解决的流程:

  1. 打开原视频页面并播放几秒(很多站点懒加载,不播就不会请求 M3U8)。
  2. 安装扩展后没刷新过页面的,刷新一次(扩展的 webRequest 监听是页面加载后才生效的)。
  3. 用扩展重新检测,选择目标清晰度。
  4. 选 MP4 输出,再点一次下载。

如果还是不行,按下面的章节定位。


检测不到 M3U8

症状:打开 FlowPick 弹出窗口显示"未检测到媒体资源",或列表里只有缩略图/封面,没有 .m3u8 条目。

常见原因

原因怎么确认解决
视频还没开始加载没播过就打开扩展播放 3-5 秒再打开扩展
扩展安装后没刷新页面装扩展前页面已加载完毕刷新页面重新触发请求
使用 MSE 而非原生 HLS开发者工具里看不到 .m3u8 请求见下方"MSE 站点"小节
清单在跨域 iframe 里Network 面板能看到,但扩展拿不到直接打开 iframe 源页面再检测
广告拦截器等扩展冲突暂时禁用其他网络类扩展后能检测到加白名单或调整加载顺序
自定义/私有协议Network 里走的是 ws://blob: 或加密流FlowPick 暂不支持,见 已知限制

用开发者工具确认

F12Network 面板 → 在过滤框输入 m3u8

  • 看到请求:说明站点确实在用 HLS,问题在扩展检测侧(多半是没刷新页面或被其他扩展拦截)。
  • 看不到请求:站点可能用了 MSE(Media Source Extensions),把分片直接喂给 <video>,根本不发 .m3u8。这类站点 FlowPick 检测不到,属于浏览器层限制。

MSE 站点怎么办

如果 Network 里看到的是一堆 .m4s / .chunk 请求但没有清单文件,那它用的是 MSE + 自定义分片逻辑(YouTube 就是典型)。两条出路:

  • .mpd(DASH)入口,改用 DASH 下载器,参考 DASH 没声音排查
  • 找到 <video> 标签的 src,如果是 blob: 开头,那是 MSE 生成的,无法直接下载——只能等站点自己发原生 HLS/DASH 清单。
关于扩展检测原理(webRequest.onBeforeRequest、Content-Type 识别)和 25+ 种 MIME 类型的完整列表,请参阅 视频嗅探 — 检测原理。检测不到时的系统排查流程,见 常见问题排查 — 媒体检测问题

清单解析异常

症状:M3U8 检测到了,但画质列表是空的,或分片数量异常少(比如只有 1 个),下载出来只有几 KB。

Master 还是 Media

HLS 清单分两层。如果你在弹出窗口里看到的是 Media Playlist(分片清单本身),可能没有画质选项可选;反过来如果你拿到的只是 Master Playlist 的 URL,但解析出来的变体都是空的,多半是清单内容不完整。

Master Playlist          ← 列出 360p / 720p / 1080p 变体
    └── v_720.m3u8       ← Media Playlist,列出几百个 .ts 分片
            └── seg-001.ts, seg-002.ts, ...

自己检查清单内容

把 M3U8 URL 复制出来,用 curl 看一眼内容(替换成你的 URL):

curl -sL "https://cdn.example.com/index.m3u8"

正常 Master Playlist 应该能看到 #EXT-X-STREAM-INF 行:

#EXTM3U
#EXT-X-VERSION:6
#EXT-X-STREAM-INF:BANDWIDTH=3000000,RESOLUTION=1280x720
v_720.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=8000000,RESOLUTION=1920x1080
v_1080.m3u8

正常 Media Playlist 应该能看到 #EXTINF + 分片 URL:

#EXTM3U
#EXT-X-TARGETDURATION:6
#EXT-X-VERSION:3
#EXTINF:6.0,
seg-001.ts
#EXTINF:6.0,
seg-002.ts
#EXTINF:4.2,
seg-003.ts
#EXT-X-ENDLIST

常见异常

看到的内容问题处理
整个 HTML 页面(登录页/错误页)URL 被防盗链重定向403 与防盗链
只有 #EXTM3U 一行清单被截断或响应不完整重新打开页面获取新 URL
#EXT-X-KEY 但没分片加密流密钥失败导致后续不输出加密流解密失败
#EXT-X-ENDLIST 但分片数 < 5直播流刚开播或已结束直播流需在结束后下载回放
分片 URL 是相对路径正常,解析器会补全基址无需处理
真实 HLS 清单远比教程示例复杂(多音轨、字幕、CODECS 字段、不连续性标签等),这些是"朴素下载器"翻车的高发地。完整解析逻辑见 HLS 深度篇视频嗅探 — Master vs Media Playlist

分片下载失败

症状:进度条走到一半报错、卡住不动,或控制台大量 403/404/CORS 报错。

403 与防盗链

分片或清单请求返回 403 Forbidden,几乎都是防盗链校验没通过。常见三类:

类型表现FlowPick 处理
Referer 校验直接 curl 能下,浏览器扩展有时 403v1.1.1 起扩展自动注入原页面 Referer
Cookie / 登录态未登录时 403,登录后正常在原视频页面使用扩展即可携带 Cookie
短时 TokenURL 里带 ?token=xxx&expires=yyy,过几分钟失效见下方"过期链接"

扩展版的防盗链代理(v1.1.1+)

从 v1.1.1 起,FlowPick 内置了防盗链代理预览与下载:图片列表、视频封面、悬停预览、图片/音频/文档/字幕下载,都会自动走代理,带上原页面的 Referer 和 Cookie,并在保存前检查响应——避免把防盗链返回的 HTML 错误页当成图片或音频存下来。

如果你装的是旧版本,升级到 v1.1.1+ 后 Bilibili 等带 Referer 校验的站点下载成功率会显著提升。升级方法见 安装指南 — 更新扩展

在线工具为什么经常 403

在线工具跑在 flowpick.com 域名下,浏览器不会把目标站点的 Cookie 和原页面 Referer 带过去,所以只要源站做了防盗链,在线工具基本必挂。带防盗链的站点请直接用扩展,在原视频页面操作。

用 curl 复现 403

确认是 Referer 问题最快的方法:

# 不带 Referer,多半 403
curl -I "https://cdn.example.com/seg-001.ts"

# 带上原页面 Referer,应该 200
curl -I -H "Referer: https://www.example.com/watch/123" "https://cdn.example.com/seg-001.ts"

第二个能 200,就坐实是 Referer 校验。

过期链接(动态 Token)

直播回放和部分平台的点播用短期 Token

https://cdn.example.com/seg-001.ts?token=ab12cd34&expires=1692300000

expires 是 Unix 时间戳,过这个时间就 403/404。表现:

  • 刚开始下载好好的,下到一半突然全部分片 403。
  • 重新打开页面后又能下,但过一会儿又挂。

处理

  • 检测到清单后尽快开始下载,别在弹出窗口里犹豫太久。
  • 大文件被 Token 截断时,刷新原页面,让扩展重新抓一份新清单,继续未完成的部分(FlowPick 的失败重试会处理临时波动,但 Token 过期是硬失败,必须换新清单)。
FlowPick 的下载重试采用指数退避策略,能处理临时网络抖动,但不能绕过 Token 过期这类硬失败。重试机制细节见 下载引擎架构 — 重试机制

404 分片已删除

多见于直播回放——直播结束后平台只保留有限时间的回放分片,超期分片直接 404。

  • 换个清晰度试试(不同清晰度可能对应不同分片文件)。
  • 确认回放是否仍在有效期内。

CORS 错误

控制台报 has been blocked by CORS policy,基本都是在线工具遇到的。扩展有 host_permissions,不受同源策略限制。

工具CORS 表现处理
浏览器扩展不会触发 CORS
在线工具清单/分片跨域拉取被拦改用扩展;或确认源站是否返回 Access-Control-Allow-Origin
CORS 是浏览器安全机制,不是 bug。在线工具与扩展的功能差异和选型建议,见 在线工具 — 与扩展的对比

下载中途网络中断

临时网络抖动由重试机制覆盖。持续失败时:

  • 检查网络稳定性(特别是代理/VPN 环境,见 常见问题排查 — 代理/VPN 环境)。
  • 降低并发线程数到 2-3,减少同时连接数(部分 CDN 会拒绝过高并发)。
  • 检查防火墙是否拦截了流媒体 CDN 域名。
并发数怎么配?默认 2 保守,桌面网络建议 4-6,CDN 限速严重的站点降到 2-3。配置方法见 配置参考。并发与性能关系见 下载引擎架构 — 并发数与性能

加密流解密失败

症状:清单里有 #EXT-X-KEY,下载能跑完,但合并后无声/花屏/无法播放,或控制台报解密错误。

先区分 AES-128 还是 DRM

FlowPick 只支持 AES-128 这种"裸"加密。清单里看到这些标签的,统统下不了:

# AES-128 —— 支持
#EXT-X-KEY:METHOD=AES-128,URI="https://cdn.example.com/key.bin",IV=0x...

# SAMPLE-AES / FairPlay —— 不支持(DRM 邻近)
#EXT-X-KEY:METHOD=SAMPLE-AES,URI="skd://..."

# Widevine / PlayReady(DASH 里常见)—— 不支持
<ContentProtection schemeIdUri="..."/>

AES-128 解密失败的常见原因

原因怎么确认处理
密钥 URL 403/404控制台报密钥拉取失败在原页面用扩展(带 Cookie/Referer);密钥也可能带 Token
密钥需要登录态未登录时密钥 403,登录后正常先登录再下载
IV 缺失清单里 #EXT-X-KEY 没有 IV=FlowPick 会按规范用分片序号推导 IV,无需手动处理
密钥不是 16 字节curl 下来长度不对源站异常,非 FlowPick 可修

用 curl 验证密钥

# 看密钥是否拿得到,注意要带 Referer/Cookie
curl -sL -H "Referer: https://www.example.com/watch/123" \
     "https://cdn.example.com/key.bin" | wc -c
# 正常应该输出 16

输出不是 16,密钥本身有问题;输出 16 但扩展还是解密失败,多半是扩展请求时没带上 Cookie/Referer(升级到 v1.1.1+,防盗链代理会自动带)。

DRM 内容怎么办

Widevine、PlayReady、FairPlay、SAMPLE-AES 这些 DRM 保护内容,FlowPick 明确拒绝处理——这是特性不是 bug。声称能解 DRM 的下载器要么在撒谎要么违法。法律和技术底线见 流媒体下载合法性指南

DRM 检测方式和完整不支持列表见 已知限制 — DRM 保护内容。AES-128 解密的 Web Crypto 实现见 视频嗅探 — 加密流

合并后无法播放

症状:文件下下来了,大小也正常,但 VLC/播放器打不开,或只有画面没声音,或音画不同步。

先用 VLC 测试

VLC 是兼容性最好的播放器,先用它排除"播放器问题":

  • VLC 能播、系统播放器不能播 → 编解码/容器兼容问题,换播放器。
  • VLC 也播不了 → 文件本身有问题。

常见原因

表现原因处理
文件只有几 KB下到了清单不是视频重新选择资源,确认选的是视频不是 .m3u8
有画面没声音多音轨清单只下了视频轨道见下方"多音轨"
音画不同步HLS 分片 PTS 不连续切 TS 输出格式,或用 FFmpeg 重编码
花屏/损坏个别分片下载失败导致合并不完整重新下载,确保所有分片完整
MP4 打不开但 TS 能播转封装时 stsd box 写错见下方"TS vs MP4"

多音轨清单

真实 HLS 清单(Netflix、Disney+ 那种)常把音频和视频分开放:

#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac-128",NAME="English",DEFAULT=YES,URI="audio/eng_128.m3u8"
#EXT-X-STREAM-INF:BANDWIDTH=3000000,...,AUDIO="aac-128"
v_720.m3u8

如果你的下载器只抓 v_720.m3u8 然后拼分片,得到的就是无声视频。FlowPick 会自动同时拉音频轨道并合并,但前提是你选了包含音频的变体而不是只选视频流。多音轨清单的完整处理见 HLS 深度篇 — EXT-X-MEDIA

TS vs MP4 输出格式

遇到 MP4 打不开的情况,先切到 TS 输出格式试试。原因:

  • TS 分片是裸拼接,MPEG-TS 自带 codec 信息,几乎不会错。
  • MP4 转封装需要解析 CODECS 字段、写对 stsd box,遇到非标准 codec 标识会翻车。

TS 能播、MP4 不能播,多半是转封装问题,可以先用 TS 救急;要 MP4 的话用 FFmpeg 命令行手动转一次:

ffmpeg -i output.ts -c copy output.mp4

-c copy 不重新编码,秒级完成。

TS/MP4 转封装原理和 FFmpeg WASM 实现见 格式转换。浏览器内合并的完整流程见 浏览器内合并深度篇

大文件 / 浏览器卡死

症状:下载大文件(>1GB)时浏览器卡顿、崩溃,或下载到一定比例直接中断。

这是写入策略问题,不是 HLS 本身的问题。FlowPick 三级写入策略:

策略触发条件上限
File System Access APIChrome/Edge 86+无硬上限
StreamSaver.js支持 Service Worker 的浏览器无硬上限
Blob 兜底其他情况1.5GB 硬限制

Blob 模式下超 1.5GB 会被直接拒绝。解决方案:

  • 用 Chrome 86+ 或 Edge 86+,启用"选择保存目录"功能。
  • Firefox 不支持 FSA,大文件走 StreamSaver 或 OPFS 临时存储(v1.1.1+ 优化)。
  • 实在不行,降低并发数到 1-2,减少内存占用。
三级写入策略对比和降级逻辑见 下载引擎架构 — 策略对比总结。大文件排查见 常见问题排查 — 大文件下载失败。Firefox 的 OPFS 临时存储行为见 安装指南 — Firefox

诊断工具速查

排查时按 F12 打开开发者工具,下面是几个最常用的检查点:

检查面板操作
M3U8 有没有被请求Network过滤框输入 m3u8,刷新页面播放视频
分片请求状态码Network过滤框输入 .ts.m4s,看 Status 列
请求带了什么头Network → 选中请求 → Headers看 Request Headers 的 Referer/Cookie
解密报错ConsoledecryptAES
合并报错Consoleffmpegwasm

生成环境诊断报告

复杂问题可以在 Console 里跑下面这段,生成环境信息便于反馈:

const report = {
  ua: navigator.userAgent,
  fsa: 'showDirectoryPicker' in window,
  sab: typeof SharedArrayBuffer !== 'undefined',
  coi: self.crossOriginIsolated,
  storage: await navigator.storage?.estimate().catch(() => null),
  ts: new Date().toISOString(),
}
console.log(JSON.stringify(report, null, 2))

报告只含浏览器环境信息,不含任何浏览记录或个人数据。


什么时候真的下不了

FlowPick 适合公开的标准 HLS 播放列表。以下场景是设计上不支持,不是 bug:

  • DRM 保护(Widevine / PlayReady / FairPlay / SAMPLE-AES)
  • 需要破解登录或付费墙的内容
  • 使用 MSE 自定义分片、不发原生 M3U8/MPD 的站点
  • 私有协议、WebSocket、WebRTC 传输的媒体

碰到这些,正确的行为是报错而不是静默产出损坏文件——这也是 FlowPick 的态度。法律和技术底线详见 流媒体下载合法性指南


相关文档