边转边发:视频解析站为什么零落盘
需求很具体:手机上刷到一条视频,想马上看,不想先等它下载完,也不想为了看一次就往手机里塞几百兆。所以这个站从一开始就定了一条硬规则:服务端不落盘。取流、转封装、发给浏览器,全在同一条管道里完成,磁盘上不留临时文件。
为什么不能只给一个直链
最早的想法很天真:解析出直链,让浏览器自己去下。试过就知道不行。高清视频基本是音视频分离的流,浏览器拿到其中一条只有画面没有声音;直链还绑请求方的地址,换个网络就 403。想让人看到完整的文件,只能由服务器抓流、合成、再吐出去。
管道怎么搭
链路是三步,中间不落文件:
yt-dlp取元数据和每档画质的流地址,只把元数据留在内存里;- 用户点下载时,
ffmpeg同时读视频流和音轨,-c copy只转封装、不重新编码; - 输出接到标准输出,按 chunked 直接写进 HTTP 响应。
ffmpeg -i 视频流 -i 音轨 -c copy \
-movflags frag_keyframe+empty_moov -f mp4 pipe:1
empty_moov 是关键:普通 MP4 要把索引写在文件头,得先知道整个文件多大,那就必须落盘。改成 fragmented MP4 之后,ffmpeg 可以边合成边输出,浏览器收到第一段就能开始。用户看到的是下载框开始动,而不是一个转圈的白页。
CPU 几乎不占:-c copy 不重新编码,高分辨率也只是搬运字节。真正贵的是带宽,和上游的抓取速度。
为什么不能只让浏览器直连
| 情况 | 直接给直链 | 现在的做法 |
|---|---|---|
| 音视频分离的高清流 | 只有画面,没有声音 | 服务端合成后整条发出 |
| 直链绑定请求方地址 | 换个网络就 403 | 服务端抓流,浏览器只连本站 |
| 登录后才有高画质 | cookie 在浏览器里用不上 | cookie 留在服务端 |
一个进程的生命周期
推流开始后,浏览器断开就杀掉 ffmpeg,否则它会在后台空转,一路读到上游限速。并发也设了上限,超出的请求排队或者直接拒绝——这台机器扛不住太多路同时转封装。
登录态:1080P 的代价
B 站不登录也能看,但画质封顶在 720P;想要 1080P,得给解析器一份登录 cookie。cookie 会过期,过期之后不是报错,而是悄悄退回游客画质——用户只会觉得「今天怎么糊了」。所以我配了两层:每天定时带着 cookie 访问一次,把轮换后的值写回文件;每几小时用固定视频实跑一次,结果写进一个状态文件。
这套东西不能自己重新登录:要账号密码、要二次验证,机房 IP 登录还容易触发风控。它能做的是尽早发现失效,然后提醒我手动换一份。
不自己做签名,把门交给统一登录
这个站没有做自己的鉴权,也没有做防盗链签名。它整个挂在统一登录后面:没登录的人连解析页面都打不开,登录了才能用。这不是最精细的方案——它挡不住登录用户把直链转给别人——但它把攻击面收在一处,我不用再维护一套 token。
另一条边界是只接受白名单里的站点,参数也做白名单校验。解析服务本质上是一台「替别人发请求」的机器,不限制输入,它很快就会变成开放代理。
代价
- 不能断点续传:连接断了要重新点一次;
- 不能缓存热门视频:同一条视频被点十次,就抓十次;
- 首字节之前要等解析和 ffmpeg 起进程,冷启动比热缓存慢;
- 上游一改协议,站就跟着坏,得等提取器更新。
换来的是磁盘永远不会被视频塞满。这台机器还要跑别的东西,一块盘被缓存吃满的代价,比多抓几次流大得多。
零落盘不是技术炫耀:这台机器的磁盘就那么大,缓存热门视频这件事我负担不起。
所以我没有做「下载中心」,也没有做任务队列。点一下,看一条,服务端什么都不留。