ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

m3u8下载全攻略:从ffmpeg命令到加密解密避坑指南

m3u8下载全攻略:从ffmpeg命令到加密解密避坑指南 简介面向Python开发者的m3u8视频流下载脚本针对需要将HLS协议下的分段TS文件转为本地MP4播放的场景提供从解析播放列表、批量下载分片到调用FFmpeg合并的完整实现。压缩包仅含1个py文件大小约2KB以单个可运行脚本的形式封装核心逻辑适合用来理解m3u8链接获取、TS片断抓取及加密流处理等流程。资源目前已有1343人学习浏览。借助这份脚本读者可以快速搭建自己的m3u8下载工具并结合代码结构掌握requests、正则解析、subprocess调用FFmpeg等关键用法若遇到网络中断或分片缺失还可在此基础上扩充断点续传与多线程下载能力。整体轻量且实用适合有基础Python经验、希望处理在线视频离线缓存问题的开发者参考。1. m3u8 下载为什么总是“下回一个几 KB 的文本”索引和分片的关系要先立住把 m3u8 链接粘进浏览器地址栏回车之后往往只得到一个几 KB 的文本文件里面一行行全是.ts后缀的路径——这是 m3u8 下载最经典的第一课。m3u8 本身不是视频而是一份索引它告诉播放器去哪里找真正承载画面的分片、按什么顺序播放、每个分片播多久。视频网站把一段完整视频切成几十个甚至几百个几秒的 TS 分片再配上这份索引于是直播可以边推边播回放可以按需补片码率切换也只影响后续分片。你的下载目标也跟着变成两步先抓到这份 m3u8 索引再把索引里指向的全部分片按顺序取回并合并必要时还要还原加密。这篇文章就按这两步往下拆给出可以直接复制的命令、输出参数选择以及我在实际下载中踩过的一些坑。适合谁拿直播回放想离线看的用户、做内容备份的编辑、需要二次剪辑素材的编导以及刚接触 HLS 的新手。2. m3u8 下载前先看懂三样东西索引结构、分片序列与转码链路2.1 用文本编辑器打开 m3u8从 EXTM3U 到 EXTINF 的语法m3u8 是 UTF-8 文本文件用记事本或 VSCode 就能打开。它的标签行以#开头非#行是分片地址。下面是一个典型的点播播放列表#EXTM3U #EXT-X-VERSION:3 #EXT-X-TARGETDURATION:10 #EXT-X-MEDIA-SEQUENCE:0 #EXTINF:10.0, https://cdn.example.com/hls/seg_0000.ts #EXTINF:10.0, https://cdn.example.com/hls/seg_0001.ts #EXTINF:9.56, https://cdn.example.com/hls/seg_0002.ts #EXT-X-ENDLIST重点看三处。#EXTINF:后面第一个数字是分片实际时长单位秒它决定合并时的时间轴#EXT-X-MEDIA-SEQUENCE是起始分片序号很多加密规则要拿它当 IV 基准#EXT-X-ENDLIST表示这是一个完整的点播列表没有这行说明是直播源列表会持续滑动。分片地址还可能是相对路径比如../segment/0000.ts下载时要按 m3u8 所在的目录去拼完整地址否则工具只会报 404。2.2 分片为什么是几秒一段直播回放、码率切换与失败重试视频网站把流切成 4 到 10 秒的分片不是闲得没事。切分之后服务端可以边生成边发布直播时只要不断追加分片文件、刷新索引播放器就会按新列表继续拉流这就是“m3u8 直播源能看”的基本原理。对播放器来说分片粒度小还能做码率切换带宽下降时播放器只请求更低位率的下一组分片画面在几秒内完成降级不会整个视频重新加载。这个粒度对下载者同样是好处。理论上你可以逐片下载、检查、合并而不是一次性拉一个可能中断的大文件某个分片坏了也只重试那一片不用重来整个视频。很多 m3u8 downloader 工具会把分片先落到本地再合并正是利用了这个特性。反过来你也该意识到一个损坏的分片如果没被发现会直接影响最终 MP4 的可用性所以后面第 5 章里我会专门讲“转码失败”这类问题。2.3 主播放列表与子播放列表同一地址给了多个清晰度很多 m3u8 地址并不是分片列表而是“主播放列表”。它里面不直接写.ts而是通过#EXT-X-STREAM-INF指向多个不同码率的子列表#EXTM3U #EXT-X-STREAM-INF:BANDWIDTH2500000,RESOLUTION1280x720 https://cdn.example.com/hls/720p/index.m3u8 #EXT-X-STREAM-INF:BANDWIDTH600000,RESOLUTION640x360 https://cdn.example.com/hls/360p/index.m3u8BANDWIDTH 是码率字段RESOLUTION 是分辨率。下载器如果只拿到第一行子列表下载的就是 720p如果你想要 720p却把整个主列表丢给 ffmpegffmpeg 默认会自己选一路通常是最高的但也不排除选错。更稳的做法是先用文本编辑器打开这个 m3u8确认里面有没有#EXT-X-STREAM-INF有的话就把对应子列表完整提取出来用它作为下载输入。还有少数站点会用#EXT-X-MEDIA提供独立音轨或字幕轨那种情况要留意下载器是否会一并处理。2.4 直播源与点播回放的差别有没有 ENDLIST 是分水岭#EXT-X-ENDLIST这行是最快判断标准。有它说明索引固定是一个点了就能从头下到尾没有它索引是动态窗口服务端只保留最近一段时间的分片下载器跑得再快也补不回你已经错过的部分。很多人把“群晖电视 m3u8 源”这样的直播源当成下载地址结果发现工具永远在“下载中”其实不是工具坏了而是协议类型不允许你拿到历史画面。回放在大多数站点会生成独立的 VOD 播放列表URL 里常常带有playback、record、vod之类的关键字。要做离线保存优先去找回放地址而不是在直播流上硬抓。这个区别想清楚能省掉大量无效等待。3. 用 ffmpeg 下载 m3u8最小命令、防盗链参数与三种输出容器3.1 先跑通最小命令-i 加列表地址-c copy 加输出文件名最常见的做法是直接使用 ffmpeg不需要额外安装客户端。它自己会去请求索引、依次拉取分片、解密、合并一条命令完成。最小命令是这样ffmpeg -i https://cdn.example.com/hls/index.m3u8 -c copy -f mp4 output.mp4逻辑不复杂-i指定输入的 m3u8 地址-c copy表示不重新编码把源编码原样搬运CPU 占用几乎为零-f mp4强制输出容器为 MP4。如果源视频是 H.264/AAC这条命令足够如果源编码是 MPEG-2 或 AC-3封装进 MP4 经常报错这时把后缀改成.mkv更稳。换句话说下载器最终给你.ts或.mkv不一定是你选错了可能是源编码不适合 MP4 封装。3.2 防盗链场景下补三个参数Referer、User-Agent 与超时第一次下载大概率会遇到 403。原因是 ffmpeg 默认请求带上的是User-Agent: FFmpeg/短横线版本这类字符串不少 CDN 见到就拒绝。浏览器能放、ffmpeg 不能下基本都是这个原因。解决方法是把播放页面里的请求头补进去ffmpeg -headers $Referer: https://www.example.com/video/123\r\nOrigin: https://www.example.com\r\n \ -user_agent Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 \ -i https://cdn.example.com/hls/index.m3u8 \ -c copy -f mp4 output.mp4这里有两个坑。第一个是-headers里的多个请求头必须用\r\n分隔写成一行或只用空格分隔都会失效第二个是 Referer 的地址要精确到播放页而不是站点根域名CDN 校验的是完整路径。遇到还要登录的站点把 Cookie 也以Cookie: xxx的形式加进-headers。另外建议补一个-rw_timeout 10000000意思是单个分片请求 10 秒超时避免网络抖动时 ffmpeg 直接放弃。3.3 三种输出形态直接 MP4、中间 MKV、按需转码结合我自己的使用场景下载输出一般分三种不要一上来就贪“一条命令出 MP4”。# 中间先封装成 MKV保留原始时间轴适合下载拷贝 ffmpeg -i https://cdn.example.com/hls/index.m3u8 -c copy videos/output.mkv # 确认音视频流都是 H.264/AAC 后从 MKV 无损转封装成 MP4 ffmpeg -i videos/output.mkv -c copy videos/output.mp4 # 源编码不适合 MP4 时转成通用 H.264/AAC代价是耗时 ffmpeg -i videos/output.mkv -c:v libx264 -preset medium -crf 20 -c:a aac videos/output.mp4为什么要中间先做成 MKV因为 HLS 分片流里的时间戳常常不是规整的MP4 对时间戳要求更严MKV 能保留可变帧率、异常字幕轨几乎不会因为“容器不接受”而中断。确认能安全封装后再用-c copy转成 MP4这一步不重编码速度很快。第三条命令开始真正转码-crf 20在体积和画面质量之间比较均衡非常在意的可以降到18但文件会更大。3.4 没有 ffmpeg 的服务器一份用 curl 逐片下载的最小脚本有时候你在远程服务器上想下载但环境里没装 ffmpeg或者想保留原始 TS 分片直接抽帧。这种场景我一般用脚本解析 m3u8再用 curl 逐片拉取# 从 m3u8 逐行下载分片要求 m3u8 内是完整 URL while IFS read -r line; do case $line in \#*|) continue ;; *) curl -sS -O -- $line ;; esac done playlist.m3u8这个脚本把#开头的标签行跳过只处理分片地址-sS表示静默但保留错误信息。很多 m3u8 里写的是相对路径需要先拼上基础目录base_urlhttps://cdn.example.com/hls while IFS read -r line; do case $line in \#*|) continue ;; /*) line${base_url}${line} ;; *) line${base_url}/${line} ;; esac curl -sS -O -- $line done playlist.m3u8这里的拼接逻辑比较粗糙以/开头就当作站内绝对路径否则直接拼在基础目录后面。遇到../这种上层路径还是不行需要单独处理。逐片下载的另一个好处是能对比每个 TS 文件大小快速定位坏分片这一点在后面的避坑章会用到。4. 加密 m3u8 的下载看懂 EXT-X-KEY再让 ffmpeg 自动解密4.1 判断分片是否加密看 EXT-X-KEY 和 METHOD 字段要判断 m3u8 是不是加密的打开索引文件搜索EXT-X-KEY。出现下面的标签说明分片用 AES-128 做了加密#EXTM3U #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-KEY:METHODAES-128,URIhttps://cdn.example.com/hls/key.key #EXTINF:10.0, https://cdn.example.com/hls/enc_0000.ts #EXTINF:10.0, https://cdn.example.com/hls/enc_0001.ts #EXT-X-ENDLIST其中METHOD是加密算法标准 HLS 最常见的写法是AES-128URI是密钥下载地址如果后面还有IV0x…说明服务端显式指定了初始向量。没写 IV 时标准做法是取#EXT-X-MEDIA-SEQUENCE的序号转成 16 字节十六进制当作 IV。这个规则像一个黑匣子但排错时很好用很多花屏问题的根源就是下载器用的 IV 和加密端不一致。需要注意的是如果标签写的是METHODSAMPLE-AES不再是一整片分片全部加密而是只加密部分字节faig... 不展开这类内容通常和 FairPlay/Widevine 等 DRM 绑定ffmpeg 支持很有限。4.2 让 ffmpeg 自动解密密钥地址可达命令不变只要密钥地址能被 ffmpeg 正常请求到下载命令和普通 m3u8 几乎一样ffmpeg -headers $Referer: https://www.example.com/video/123\r\n \ -user_agent Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 \ -i https://cdn.example.com/hls/encrypted.m3u8 \ -c copy -f mp4 decrypted.mp4ffmpeg 会先请求 m3u8读到EXT-X-KEY后再去请求密钥文件然后边拉分片边解密最终输出的是明文内容。关键条件是密钥端点要能访问有些站点给密钥地址做了鉴权需要带 Referer 或临时 token那就用-headers把请求头补上。密钥地址也常见写成相对路径ffmpeg 会按 m3u8 所在目录自动拼接一般不用你手动处理。如果密钥请求失败ffmpeg 会直接中断输出文件停留在几 KB 或 0 字节不会给你一个“半解密”的文件。遇到这种情况先用ffmpeg -loglevel debug重跑一遍看日志里到底请求了哪个 URL、返回了什么状态码多半能直接定位到是 403 还是证书问题。4.3 解密失败的三个经典症状与对应排查第一种输出文件为 0 字节。原因是密钥或第一个分片没有成功获取优先检查网络和请求头尤其是 key 端点的独立鉴权。第二种输出文件能播放但画面花屏、有声音或没声音。这多半是 IV 不匹配m3u8 里写IV0x…时 ffmpeg 会照用没写时它会拿MEDIA-SEQUENCE算如果服务端加密用的是另一套序号就会花。第三种时长正常但整段黑屏。这可能不是解密失败而是视频轨本来就只有黑场或源流里带了额外黑场分片和加密无关。遇到加密 m3u8我的原则是先确认自己有没有权利下载再动手。能解决标准 AES-128 的工具已经很多但 DRM 保护的商业内容本来就不该通过浏览器抓包绕过这部分不在“m3u8 下载”的合理范围内。5. m3u8 下载避坑实录403、转码失败与花屏的排查样本5.1 中途中断在某个分片Invalid data found when processing input现象ffmpeg 下载到 80%最后几行报Invalid data found when processing input输出文件停在一个没法播放的中间状态。原因某个 TS 分片不完整或者分片 URL 返回的是 HTML 错误页而不是视频数据。ffmpeg 按顺序拉分片拿到非法数据后会终止没有“跳过这一个片继续下一个”的默认策略。解决先给命令加-rw_timeout 10000000提高单分片等待时间如果还是失败就按 3.4 的脚本逐片下载比对每个 TS 文件大小找到那个明显偏小的分片单独重拉。重拉成功后可以用cat把它并回原序列再手动让 ffmpeg 合并。这一步虽然麻烦但比盲目换下载器管用。5.2 浏览器能播放ffmpeg 却 403 Forbidden现象在浏览器里打开视频正常把同一个 m3u8 地址粘到 ffmpeg立刻 403。原因CDN 校验请求头ffmpeg 默认的 User-Agent 和缺失的 Referer 被识破。浏览器里能放是因为浏览器自动带了完整上下文ffmpeg 什么也没带。解决用浏览器的开发者工具在 Network 面板找到播放列表请求把 Request Headers 里的User-Agent、Referer、Origin、Cookie原样复制到 ffmpeg 的-headers和-user_agent参数里。注意 header 列表要以\r\n分隔Referer里的地址不要和网页地址不一致CDN 对这类校验非常敏感。5.3 合并后的视频音画不同步时长还少了十几秒现象下载成功能播放但人物对白和口型明显错位总时长比播放器里显示的也短。原因m3u8 列表里存在#EXT-X-DISCONTINUITY标签。这个标签表示前后分片不是连续录制中间可能插了广告或切过流编码参数、时间基都可能变了。直接-c copy会把旧时间轴延续下去导致错位。解决先数一下列表里有多少处中断grep -c EXT-X-DISCONTINUITY playlist.m3u8结果大于 0 时改用 MKV 中间容器若仍错位就做一次完整转码ffmpeg -i playlist.m3u8 -c:v libx264 -crf 20 -c:a aac -af aresampleasync1 fixed.mp4aresampleasync1的作用是让音频流重新对齐到视频时间轴虽然会增加转码时间但能消掉大部分不同步问题。5.4 直播源没有 ENDLIST下载器永远在“下载中”现象用某个 m3u8 downloader 下载直播源进度条一直在涨跑了一个小时也没有结束。原因这个 m3u8 是直播列表没有#EXT-X-ENDLIST服务端不断追加新分片同时丢弃旧分片。下载器每刷新一次列表就看到新的分片自然不会停止。解决先判断你要的是不是回放。如果站点保留点播回放去找带playback或record关键字的播放列表那个才是完整的。如果必须抓直播加上播放时长限制比如-t 3600抓一小时输出用 MKV 而不是 MP4并且要有专人值守直播中断时立即重跑。5.5 下载完拖动进度条卡顿剪辑软件不认现象播放器能放但拖动进度条明显卡顿Premiere、剪映等软件直接提示文件不支持。原因很多下载器生成的 MP4 把元数据moov块放在文件尾部播放器必须读完整个文件才能建立索引表现就是“能放但拖不动”。剪辑软件对这类 MP4 兼容性很差。解决用 ffmpeg 把这个 MP4 重新整理一遍不重编码只把moov移到文件开头ffmpeg -i downloaded.mp4 -c copy -movflags faststart output_optimized.mp4如果剪辑软件还要求 H.264 编码再用第 3.3 节的做法转一次码。这条命令我几乎每次保存 MP4 都会跑一遍已经成了固定习惯。6. 验证下载产物用 ffprobe 给 m3u8 下载结果做体检下载完成之后我不急着打开播放器而是先跑一遍 ffprobe确认下载结果真的“健康”。这里说的健康不是能不能播放而是时长、编码、轨道是否和源保持一致。命令很简单ffprobe -v error \ -show_entries formatduration,format_name,size \ -show_entries streamcodec_name,codec_type,width,height,avg_frame_rate \ -of defaultnoprint_wrappers1 output.mp4这条命令会输出容器格式、总时长、文件体积、每个流的编码类型、分辨率、平均帧率。我通常把它和 m3u8 里的EXTINF总时长对一下误差应该在半秒以内。检查项预期结果异常时的可能原因format_namemp4 或 matroska输出容器选错或源流编码不适配duration与播放器显示时长一致漏分片、EXT-X-DISCONTINUITY 导致时间轴断裂stream 数量至少 1 个 video 和 1 个 audio只下了视频轨音频轨单独在别的 m3u8 里avg_frame_rate25、30、60 等整数值附近源流里混入异常分片需要重新下载或转码这一步大概花不到十秒却能把很多隐患拦在交给剪辑软件之前。比如发现时长偏短我就知道某个分片没拉全立刻去重试对应范围而不是最后渲染到一半才发现黑屏。我现在的习惯是每次下载完先看 ffprobe 输出再看文件大小最后才打开播放器。顺序反过来往往就是反复试播、反复删除、再重新下载的循环。这个习惯帮我少走了很多弯路。希望帮到你。本文还有配套的精品资源点击获取
返回列表