做了几年视频类应用,或者接触过爬虫、流媒体、在线教育的开发者,大概率都经历过一个很“憋屈”的时刻:页面上一个视频明明能正常播放,右键却没有下载入口,浏览器缓存里要么是一堆像segment_001.ts这样的小分片,要么怎么都找不到一个完整的 MP4 文件。如果你打开开发者工具去分析网络请求,看到的是几个.m3u8索引文件,里面写满了几百行 ts 文件路径。这时候你大概会明白,传统“点击下载”的思路已经失效了,视频早就不是一个大文件的年代了。
这篇文章要讲的就是这类场景的完整解法:M3U8(HLS)、DASH、MP4 视频下载器到底是怎么工作的,以及如何自己完成 ts 分片下载、ts 合并、m3u8 转 MP4、视频格式转换这条链路。
先说我的判断:这类下载器的核心难点,不在“下载”这两个字,而在“理解索引格式”。只要你看懂了 M3U8 和 MPD 这两个流媒体清单文件,你就能用 FFmpeg、Python 脚本,甚至一个 20 行的 Node.js 程序,自己拼出一个可用的下载器。为什么很多人在这一步失败?因为他们把问题当成“断点续传下载大文件”,而真正的流程是“解析清单 → 并发拉取分片 → 按序合并 → 重新封装格式”。
读完这篇文章,你会掌握:
- M3U8、TS、DASH、MPD、MP4 这几个格式到底什么关系;
- 用 FFmpeg 一行命令下载 m3u8 并转 MP4;
- 当 FFmpeg 失灵或遇到加密流时,如何用 Python 自己写一套下载逻辑;
- AES-128 加密分片的破解思路(指合法授权场景下的解析);
- 常见失败原因和工程化建议。
1. 为什么需要专门的视频下载器
很多人会觉得,浏览器能播放的视频,就应该能找到原始文件地址。这个直觉在 2015 年之前的互联网大体成立——那时候大多数视频站点的做法是先传一个 MP4 到服务器,网页里用<video src="xxx.mp4">直接播放,你抓包拿到的就是 MP4 地址。
但现在不是了。
今天的视频平台,为了保证播放流畅、支持拖动进度、适配不同网速,采用的技术路线是将一个大视频切成几千个小片段,再用一个索引文件告诉播放器“按什么顺序播放这些片段”。浏览器每次只下载当前需要的那几秒,播放完再拉下一段。这种设计下,你根本拿不到完整文件,甚至连 URL 都是动态签名的。
专门做视频下载工具,就是为了解决三个问题:
第一,把“流式播放”变成“本地文件”。流式设计的目标是省流量、快启动,但代价是用户无法直接保存。下载器要把分片从远端拉回来,按索引顺序拼成一个完整文件。
第二,处理格式转换。视频平台为了兼容性,底层封装格式五花八门:Apple 生态用 HLS 和 TS 分片,Android 大屏和 YouTube 用 DASH 和 MPD,短视频 App 导出的是 m4s 片段。这些格式播放在线没问题,但本地播放器、剪辑软件不一定认。下载器的另一个职责就是把这些“直播态”格式转换成通用 MP4。
第三,保存的是“可用的视频”,不是一堆碎片。如果你手动去抓包保存 ts 文件,会发现每个文件只有几秒内容,用普通播放器打不开。下载器通过重新封装,把这些碎片变成时间轴连续、音画同步的单个视频文件。
这里要特别强调一个边界:下载器本身是中性的工具。请确保你只对有权限下载的内容执行下载,比如自己的教学内容、已授权的开放视频、测试素材,或者符合平台条款的离线缓存。私下抓取未授权商业平台视频用于传播,既违反平台规则,也可能涉及侵权。
2. 必须搞懂的基础概念:M3U8、HLS、DASH、TS、MPD
要写好下载器,首先要理解这一串缩写,否则一看到.m3u8文件和.ts分片就发懵,后面的所有步骤都无从谈起。
2.1 HLS 与 M3U8
HLS(HTTP Live Streaming)是 Apple 提出的一套流媒体传输协议,全称是 HTTP Live Streaming。它的思路很直接:把视频切成一段一段的小文件,通过 HTTP 协议逐个传输,播放器边下边播。
M3U8 是 HLS 协议的清单文件。它的本质是一个文本文件,里面记录了视频分片的 URL、排列顺序、时长和编码信息。扩展名.m3u8表示这个文件是 UTF-8 编码的 M3U 格式。
一个最简单的 M3U8 文件长这样:
#EXTM3U #EXT-X-VERSION:3 #EXT-X-TARGETDURATION:10 #EXT-X-MEDIA-SEQUENCE:0 #EXTINF:10.0, https://example.com/segments/segment_001.ts #EXTINF:10.0, https://example.com/segments/segment_002.ts #EXT-X-ENDLIST关键行的含义:
#EXTINF:10.0:后面这个分片的时长是 10 秒。https://example.com/segments/segment_001.ts:分片的实际下载地址。#EXT-X-TARGETDURATION:所有分片的最大时长。#EXT-X-ENDLIST:标记清单结束。如果直播流没有这一行,说明是一个持续更新的动态列表。
还有一类 M3U8 是多码率嵌套结构,最外层是一个“主索引”,里面不直接写 ts 文件,而是指向多个不同画质的子索引:
#EXTM3U #EXT-X-STREAM-INF:BANDWIDTH=1280000,RESOLUTION=1280x720 https://example.com/hls/720p/index.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=2560000,RESOLUTION=1920x1080 https://example.com/hls/1080p/index.m3u8下载时,要么手动选择一个清晰的子索引,要么写代码自动挑选码率最高的那个。
2.2 TS 分片:M3U8 里的“积木块”
TS 是 MPEG Transport Stream 的缩写,是一种面向传输的封装容器格式。它和 MP4 最大的区别是:TS 被设计成“可以出错也要能继续播放”的流格式,非常适合网络传输;MP4 则像一个结构严密的档案柜,文件头保存了所有索引信息,多用于完整文件存储。
HLS 把视频切成几秒一段的 TS 文件,每个 TS 可以独立解码。这带来一个好处:某一小段坏了不影响其他段,播放器可以跳过错误继续播。但也带来一个坏处:单独的 TS 文件往往无法直接用普通播放器播放,因为没有正确的上下文信息。
2.3 DASH 与 MPD
DASH(Dynamic Adaptive Streaming over HTTP)是另一种流媒体标准,由 MPEG 提出。它在思路上和 HLS 很像,也是把视频切成很多小片段,通过一个清单文件描述。只是 DASH 的清单文件不叫 M3U8,也不是以.ts作为默认分片扩展名,而是叫MPD(Media Presentation Description)。
MPD 是一个 XML 文件,描述了视频的各种编码、码率、分片时长和分片地址。DASH 的分片可能是 MP4 片段(.m4s),也可能是 WebM 片段。
一个简化版 MPD 的片段结构:
<?xml version="1.0" encoding="UTF-8"?> <MPD xmlns="urn:mpeg:dash:schema:mpd:2011" type="static" mediaPresentationDuration="PT2M0S"> <Period> <AdaptationSet mimeType="video/mp4" segmentAlignment="true"> <Representation id="720p" bandwidth="2500000" width="1280" height="720"> <SegmentTemplate duration="4" timescale="1" media="video-$Number$.m4s" startNumber="1"/> </Representation> </AdaptationSet> </Period> </MPD>看到media="video-$Number$.m4s"这种模板,表示视频分片命名为 video-1.m4s、video-2.m4s。
2.4 MP4 与 TS 的区别
| 维度 | TS 分片 | MP4 文件 |
|---|---|---|
| 设计场景 | 网络传输、直播流 | 本地存储、剪辑、播放 |
| 文件结构 | 流式打包,容忍损坏 | 文件头集中索引,结构性更强 |
| 播放兼容性 | 通常需配合 M3U8 播放 | 几乎所有播放器和设备都支持 |
| 拖动进度条 | 依赖播放器预加载 | 文件头有索引,支持随机访问 |
| 关键用途 | HLS 流媒体的基础分片 | 最终交付格式 |
下载器最后一步把 TS 合并并转成 MP4,本质上是改变封装容器,不一定重新编码视频内容。如果源视频编码格式本身是 H.264/H.265,那么用-c copy方式转换不需要重新压缩,速度极快,画质无损。这是理解m3u8 转 MP4的关键:它多数时候是「remux(重新混合)」,不是「transcode(转码)」。
2.5 格式关系一图流
如果用一句话总结它们的关系:M3U8 和 MPD 是“目录”,TS 和 m4s 是“货物”,MP4 是“打包后的成品”。下载器的任务就是拿着目录,把散落的货物按顺序装进一个标准化箱子里。
3. 下载器核心能力拆解
一个完整的 M3U8/HLS/DASH 视频下载器,功能上可以拆成五个模块:
模块一:解析索引。读取 M3U8 或 MPD 文件,提取分片地址列表。这一步要处理相对路径、嵌套索引、多码率选择、分片编号规则。
模块二:下载分片。按照解析出的 URL 列表,逐个或并发下载 TS/m4s 文件。这里要考虑网络超时、重试机制、并发数和服务器限流。
模块三:处理加密。HLS 支持对分片进行 AES-128 加密。索引文件里会包含#EXT-X-KEY:METHOD=AES-128,URI="key.key",IV=0x...这样的密钥声明。下载器需要读取密钥,用相同算法解密每个分片。
模块四:合并分片。把多个 TS 文件按顺序合并成一个大文件。最简单的办法是直接用二进制拼接,也可以用 FFmpeg 的 concat demuxer。
模块五:格式转换。把合并后的 TS/M4S 文件转成播放器兼容的 MP4。这一步既可以用 FFmpeg 命令行,也可以通过 API 嵌入到自己的程序里。
这五个模块,前三个是“下载器”,后两个是“转换器”。市面上的下载工具基本都是这五个模块的不同组合。
4. 环境准备:FFmpeg、Python 与 Node.js
做这类工具不需要太重的环境。我个人推荐三件套:
4.1 FFmpeg
这是视频处理领域事实上的标准工具。下载、合并、转码、解密,几乎所有任务都能用它完成。它的安装方式取决于你的操作系统。
Ubuntu/Debian:
sudo apt update sudo apt install ffmpegmacOS(通过 Homebrew):
brew install ffmpegWindows:建议直接下载官方编译版,把bin目录加入系统PATH。版本选择上,使用较新的稳定版即可,不必追求最新。
安装后验证:
ffmpeg -version4.2 Python 环境
Python 适合写自动化下载脚本。推荐 Python 3.8 以上版本,配合requests库。
pip install requests如果你需要更复杂的并发下载,可以使用httpx、aiohttp或concurrent.futures线程池。
4.3 Node.js 环境
Node.js 生态里也有不少视频下载相关的库,比如m3u8-parser、dashjs,但实际项目中,很多人用 Node 只是因为写并发任务比 Python 顺手,而且对前端开发者更友好。本文不做 Node 与 Python 的高低比较,你可以根据自己的技术栈选择。
5. 核心流程拆解:一条从 M3U8 到 MP4 的完整链路
下面的步骤是这篇文章的实操主线,我会按照真实生产环境的处理顺序来拆解。
5.1 第一步:从网页里找到 M3U8 地址
这不是本文的重点,但很多读者确实卡在这一步。通常方法有三种:
- 开发者工具 Network 面板:播放视频时,过滤
m3u8或mpd关键字,能看到索引文件的请求。这种地址通常有签名,有时效性。 - 抓包代理工具:移动端 App 里的视频,用 Charles 或 mitmproxy 这类 MITM 代理工具可以看到 HTTPS 明文流量。
- 直接查看页面源码:少数网站会把视频地址写在
script标签里。
拿到地址后,先用浏览器打开,看是否能正常返回文本内容。这一步的关键是确认地址是否过期、是否需要 Referer 或 Cookie。
5.2 第二步:先试试 FFmpeg 一行命令
常规的 M3U8 地址,FFmpeg 往往可以直接处理:
ffmpeg -i "https://example.com/path/video.m3u8" -c copy -bsf:a aac_adtstoasc output.mp4参数解释:
-i:输入文件,也就是 M3U8 播放列表地址。-c copy:视频流和音频流直接复制,不做重新编码。这样速度快、画质不损失。-bsf:a aac_adtstoasc:将音频流从 ADTS 格式转换为 ASC 格式,解决某些 HLS 音频转 MP4 后无法播放的问题。
如果这条命令成功了,你会看到 FFmpeg 快速下载所有 TS 分片并输出一个 MP4 文件。这个命令本质上就是一个小型 M3U8 下载器。
5.3 第三步:FFmpeg 失灵时,用 Python 自己写
FFmpeg 不是万能的。遇到以下情况,你可能需要自己写脚本:
- 分片 URL 需要逐个动态签名,无法通过一条命令直接下载;
- M3U8 里包含多个嵌套索引,需要先选流;
- 某些源站对 CCU(并发连接数)有限制,FFmpeg 默认并发策略会触发限流;
- 你需要对下载进度做精细化控制、断点续传、自动重试。
下面是一个最小可用的 Python 下载器示例。
# 文件路径:m3u8_downloader.py import re import requests from urllib.parse import urljoin def download_m3u8_video(m3u8_url, output_name="output.ts"): headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" } # 1. 请求 m3u8 索引文件 resp = requests.get(m3u8_url, headers=headers, timeout=10) resp.raise_for_status() content = resp.text # 2. 提取 ts 分片路径 # 注意:m3u8 里每行可能是相对路径,也可能是绝对 URL ts_paths = [] for line in content.splitlines(): line = line.strip() if line.startswith("#"): continue ts_paths.append(urljoin(m3u8_url, line)) if not ts_paths: print("未在 m3u8 文件中找到 ts 分片,请检查索引是否嵌套") return # 3. 逐个下载 ts 分片 ts_data = [] total = len(ts_paths) for idx, ts_url in enumerate(ts_paths, start=1): ts_resp = requests.get(ts_url, headers=headers, timeout=15) ts_resp.raise_for_status() ts_data.append(ts_resp.content) print(f"下载分片 {idx}/{total} 成功:{ts_url}") # 4. 按顺序合并为单个 ts 文件 with open(output_name, "wb") as f: for data in ts_data: f.write(data) print(f"合并完成,文件已保存为 {output_name}") if __name__ == "__main__": download_m3u8_video("https://example.com/video/index.m3u8", "merged.ts")这段代码的逻辑很直白:
- 用
requests请求 M3U8 文本; - 过滤掉
#开头的注释行,剩下的就是 TS 分片路径; - 用
urljoin处理相对路径问题; - 逐个下载 TS 文件,存到内存列表;
- 按顺序写入同一个 TS 文件。
这段代码的问题也很明显:没有并发、没有重试、把大量数据堆在内存里。真实场景中,建议用临时目录保存分片文件,再配合线程池并发下载。但作为理解原理的最小示例,它足够了。
5.4 第四步:用 FFmpeg 合并 TS 并转 MP4
用 Python 合并出来的merged.ts文件,已经可以在很多播放器里播放了,但你可能还是想要 MP4。合并+转换这一步,FFmpeg 依然是最优选。
先建立一个文件列表:
# 文件路径:filelist.txt file 'merged.ts'注意,这个文本文件里的路径要写对,如果是同一个目录,直接写文件名即可。然后执行:
ffmpeg -f concat -safe 0 -i filelist.txt -c copy -bsf:a aac_adtstoasc merged.mp4这条命令的含义:
-f concat:使用 FFmpeg 的 concat 拼接模式。-safe 0:允许文件列表中出现相对路径和非常规路径。-i filelist.txt:输入文件列表。-c copy:直接复制流,不重新编码。-bsf:a aac_adtstoasc:转换音频封装,否则某些播放器会没有声音。
到这里,你已经实现了“m3u8 下载 → ts 合并 → m3u8 转 MP4”的完整链路。
6. 进阶场景:加密 M3U8、DASH 流与更多格式转换
基础链路能覆盖大约六成场景。剩下的四成,才是真正区分“能用”和“好用”的地方。
6.1 AES-128 加密的 M3U8
HLS 协议支持对 TS 分片做 AES-128 加密。如果 M3U8 文件里有类似这样一行:
#EXT-X-KEY:METHOD=AES-128,URI="key.key",IV=0x1234567890abcdef1234567890abcdef说明这些 TS 分片下载下来是密文,直接合并播放会花屏或完全黑屏。需要在下载后用密钥解密。
FFmpeg 解密的命令非常简单:
ffmpeg -allowed_extensions ALL -protocol_whitelist "file,http,https,tcp,tls,crypto" -i encrypted_index.m3u8 -c copy decrypted.mp4其中-protocol_whitelist是为了让 FFmpeg 允许通过 file 协议读取本地密钥文件。FFmpeg 在解析 M3U8 时看到#EXT-X-KEY会自动加载密钥并解密,这一步对你来说是透明的。
如果要在 Python 里手动解密,一般流程是:
- 读取 M3U8 中
#EXT-X-KEY里的密钥 URL 和 IV; - 下载密钥文件;
- 对每个 TS 分片用 AES-128-CBC 解密;
- 只保留解密后的数据用于合并。
# 文件路径:decrypt_ts.py from Crypto.Cipher import AES import requests def decrypt_ts(ts_content: bytes, key: bytes, iv: bytes) -> bytes: cipher = AES.new(key, AES.MODE_CBC, iv=iv) return cipher.decrypt(ts_content)这里需要安装pycryptodome:
pip install pycryptodome注意:解密的前提是你有权访问密钥文件,并且有合法的解密权限。不要用这个能力去绕过付费墙或未经授权的访问控制。
6.2 DASH / MPD 流的下载
DASH 的 MPD 是 XML 格式,它的分片命名规则往往比 M3U8 更灵活。处理方式通常是:
- 用 Python 的
xml.etree.ElementTree解析 MPD; - 找到视频和音频的
Representation; - 按
SegmentTemplate拼出所有分片 URL; - 下载视频分片和音频分片;
- 用 FFmpeg 合并音视频流。
简化版解析代码:
# 文件路径:parse_mpd.py import requests import xml.etree.ElementTree as ET def parse_mpd(mpd_url: str): ns = {"mpd": "urn:mpeg:dash:schema:mpd:2011"} resp = requests.get(mpd_url, timeout=10) resp.raise_for_status() root = ET.fromstring(resp.text) templates = [] for adaptation in root.findall(".//mpd:AdaptationSet", ns): mime_type = adaptation.get("mimeType", "") for representation in adaptation.findall("mpd:Representation", ns): template = representation.find("mpd:SegmentTemplate", ns) if template is not None: media = template.get("media") start = template.get("startNumber") duration = template.get("duration") timescale = template.get("timescale") templates.append({ "mime_type": mime_type, "media": media, "start": start, "duration": duration, "timescale": timescale, }) return templates if __name__ == "__main__": templates = parse_mpd("https://example.com/video/manifest.mpd") for item in templates: print(item)拿到templates后,按编号从 startNumber 递增构造分片 URL,剩下的事情就和 M3U8 一样了。
6.3 其他格式转换:QLV、M4S、m3u8 合并
除了 TS 和 MP4,日常还经常遇到:
- QLV:腾讯视频的加密直播流格式,本地播放器无法直接打开。早期版本的 QLV 视频流可以直接通过 FFmpeg 转封装,新版已改为加密格式,需要官方客户端或 SDK 才能处理。
- M4S:YouTube 和某些 DASH 网站使用的分片格式,本质是 MP4 的一部分。合并思路和 TS 一样,用 FFmpeg concat 处理。
- EV2/EV4A:爱奇艺的私有格式,离线视频用。这些是闭源加密格式,不建议也不应该尝试破解。
遇到这些私有格式,最稳妥的判断是:如果 FFmpeg 能识别,就转;如果不能识别,就不要继续折腾。以合法手段获取官方渠道的视频导出能力,远比研究破解省心和安全。
7. 运行结果与效果验证
写完下载器,怎么判断它真的成功了?不能只看文件大小不为 0。
7.1 验证命令
对输出文件执行:
ffprobe -v error -show_format -show_streams output.mp4ffprobe是 FFmpeg 自带的探针工具。执行后,重点看这几个字段:
format_name:应该是mov,mp4,m4a,3gp,3g2,mj2或mov之类,说明容器是 MP4。duration:时长应该和源视频基本一致。- 视频流里的
codec_name:通常是h264或h265。 - 音频流里的
codec_name:通常是aac或mp3。
7.2 播放验证
用系统自带的视频播放器打开文件,拖动到视频中段和末尾,确认没有花屏、没有音画不同步、可以正常拖动进度条。对于 MP4,这三个能力是最基本的要求,任何一个失败都说明转换过程有问题。
7.3 失败时先看哪里
如果最终文件无法播放,第一步不是重跑脚本,而是检查命令输出里的错误码。FFmpeg 是一个报错信息相对明确的工具:
- 提示
Invalid data found when processing input,说明输入文件本身有问题,可能是 TS 分片不完整或 M3U8 列表被截断。 - 提示
Error while decoding stream,说明在尝试重新编码时遇到解码问题,此时应检查源文件格式是否被 FFmpeg 完整支持。 - 提示
Permission denied,说明输出目录没写入权限。
8. 常见问题与排查思路
从搜索引擎的反馈来看,视频下载卡壳的点高度集中。我整理了一份排查表:
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| m3u8 视频转换失败 | M3U8 为嵌套索引,没有获取到真正的分片列表 | 用文本编辑器打开 M3U8,查看是否有#EXT-X-STREAM-INF | 先解析主索引,再请求子索引 |
| 下载后播放花屏 | 分片顺序错乱或下载不完整 | 检查下载日志中有没有重试或超时 | 增加重试机制,改为按顺序写入文件 |
| 转换 MP4 后没有声音 | 音频流封装格式未转换 | 用 ffprobe 查看音频流格式 | 在 FFmpeg 命令中加-bsf:a aac_adtstoasc |
| FFmpeg 报 403 | 下载分片需要 Referer 或 Cookie | 抓包确认请求头 | 给 FFmpeg 加-headers参数,或先下载后合并 |
| 视频时长对,画面卡在某一帧 | 某一个 TS 分片损坏 | 用 ffprobe 逐个检查分片 | 重新下载损坏分片,或从源头更换 CDN |
| 加密 M3U8 无法播放 | 没有正确传密钥或 IV | 查看#EXT-X-KEY是否被解析 | 使用 FFmpeg 的-allowed_extensions ALL参数 |
一个容易忽略的细节是:M3U8 的地址有时带签名参数,比如?token=xxx&exp=yyy,并且签名很快过期。遇到这种情况,先保存好完整的 URL,在浏览器中打开测试,确认还能不能访问,再执行下载任务。
9. 最佳实践与工程建议
把下载器从“能跑”提升到“工程可维护”,需要考虑下面几点。
9.1 不要把所有分片放进内存
小视频没问题,但一部电影可能有几千个分片,每个几 MB,全拆到内存里会直接打爆进程。工程上应该按“下载一个 → 落盘一个 → 记录索引”的方式处理,最后用 FFmpeg concat 合并文件。
9.2 并发数要克制,重试要合理
高并发能提速,但也容易被 CDN 限流封禁。建议设置 5 到 10 个并发,配指数退避重试。不要为了追求速度把并发拉到上百,那会让源站把所有请求都拒掉。
9.3 每次输出都留日志
至少记录:分片总数、下载成功数、失败数、平均下载耗时、失败原因。日志的价值在排错时才会体现出来。没有日志的下载器,就像没有仪表盘的飞机。
9.4 优先使用-c copy
凡是能不重新编码的,就不要重新编码。-c copy模式转换一个 2 小时视频只需要几十秒,而重新编码可能耗时数小时,还会损失画质。只有当源视频编码格式实在不被播放器支持时,才考虑转码。
9.5 授权边界要写在工具说明里
作为个人工具或内部工具,建议在代码注释和 README 中明确说明:本工具仅用于下载个人有权限或已获得授权的内容,不得用于绕过付费墙、访问控制或非法分发。
9.6 处理生产环境时要先备份
如果是为线上业务做视频转码,先在小样本上跑通流程,确认输出文件时长、音画同步、清晰度正常,再批量执行。涉及删除源文件的操作,先备份,再清理。
10. 总结与后续学习方向
这篇文章的核心思路可以浓缩成一句话:视频下载器不是“下载器”,而是“流媒体格式转换器”。它的本质工作是读懂 M3U8/MPD 索引,拉取 TS/M4S 分片,然后重新封装成 MP4 文件。理解了这条链路,FFmpeg 一行命令是解法,Python 脚本是解法,换成 Go、Node.js、Java 也同样是解法。
实操层面,你至少应该走通下面这条路径:
- 拿到一个 M3U8 地址;
- 用 FFmpeg 成功转成 MP4;
- 在 FFmpeg 失败的情况下,用 Python 手动下载所有 TS 合并;
- 遇到 AES-128 加密时,能识别
#EXT-X-KEY并正确解密; - 遇到 DASH/MPD 时,能解析 XML 并拼出分片 URL。
下一步可以继续深入的方向有很多:学习 HLS 协议中#EXT-X-DISCONTINUITY、#EXT-X-BYTERANGE等高级标签,掌握 DASH 的多音轨切换逻辑,或者研究如何把下载器改造成一个基于队列的异步服务。这些内容会让你的工具逐步从“能用”变成“好用”。
如果你正在做视频相关的项目,建议把这篇文章收藏起来,等真遇到 m3u8 转 MP4、ts 合并、DASH 下载时再对照着处理。这组流程应该是你工具箱里的常备选项。