我写了个不用上传的浏览器视频压缩工具,顺便记一个坑了我小半天的 bug

起因很土。我经常要把录屏发到 Discord 或者塞进邮件,Discord 免费版限 10MB,邮箱附件常见 25MB,而一段两三分钟的 1080p 录屏动不动几十兆。网上的在线压缩工具能用,但每次都得先把视频传到人家服务器,压完再下回来。慢是一回事,更别扭的是,我不太想把公司内部的录屏传到一个不知道是谁的服务器上。

于是我做了 videocompress.dev,压缩全程跑在你自己浏览器里,视频不上传,一个字节都不出你的电脑。

它是怎么跑起来的

主力是 WebCodecs,浏览器原生的编解码接口,能直接调硬件编码器,速度接近实时。碰上不支持的浏览器,退回到 ffmpeg.wasm(把 FFmpeg 编译成 WebAssembly 在本地跑)。两条路都不碰服务器,我这边的成本基本只有一个静态托管。压到指定大小还是保画质都行,Discord、邮件、WhatsApp 这些场景我做了预设。

一个让我排查了小半天的 bug

本以为这就完事了。直到我拿一个 46 分钟的录屏去测,压出来是个"没有画面、只有声音"的文件。最诡异的是:全程不报错,程序自认为成功了。

我在解码管线上加了个计数器,数到底多少帧真进了编码器,结果是 0。视频轨一帧都没解出来,音频轨却好好的。换个 60 秒的短片又完全正常。我一开始赌是时长或内存问题,后来发现都不是。

真正的原因是容器。这个文件第一帧的 DTS 是负的(B 帧重排很常见,首帧的解码时间戳会落在 0 之前一点点)。我用的解码库默认从时间戳 0 开始读,在 0 这个点找不到能解码的起始帧,就一帧不吐,还不报错,直接甩给你一个纯音频文件。更阴的是 ffprobe 默认会把这个负值归一化,光看它根本发现不了,得去读原始 packet 的 DTS 才看得见。

修法说穿了不复杂:先用 ffmpeg 无损重封装一下(-c copy 加上 -avoid_negative_ts make_zero,把时间戳掰正,不重新编码,几十秒的事),再把干净的文件喂回 WebCodecs。同一个 46 分钟的文件,纯 ffmpeg 软件转码要跑 87 分钟,重封装加 WebCodecs 之后是 3 到 4 分钟。

写这段一半是记录,一半是提醒自己:浏览器里搞音视频,坑基本都在边缘情况上,而且经常是静默失败,连个 error 都不给你留。

最后

现在 videocompress.dev 免费、免注册、无水印,视频不上传。你要是也常被"文件太大发不出去"卡住,可以拿去用。有压不动或者出问题的文件,欢迎丢给我,我最想收集的就是这种能触发边缘 case 的样本。

相关推荐
YMWM_16 天前
video.preset的值为null和ultrafast的区别
linux·video
我在人间贩卖青春6 个月前
C++之继承的方式
c++·private·public·protected·继承方式
bkspiderx7 个月前
C++中的访问控制:private、public与protected的深度解析
开发语言·c++·算法·访问控制·private·public·protected
跟着珅聪学java7 个月前
HTML5 Video Controls 属性深度教程
video
core5128 个月前
[硬核解析] 从感知到交互:InternVideo 1/2/2.5 全系列架构演进与原理解析
架构·大模型·交互·视频·video·intern
davenian8 个月前
< Chrome Extension: Video DownloadHelper > 获得 Premium 权限 Ver10.0.271.2
chrome·edge·windows 11·video·downloadhelper
打小就很皮...8 个月前
React VideoPlay 组件封装与使用指南
前端·react.js·video
梵得儿SHI9 个月前
Java 反射机制深度解析:从运行时 “解剖” 类的底层逻辑
java·开发语言·反射·反射机制·private·类成员·反射的三大核心功能
清水迎朝阳1 年前
火山 RTC 引擎9 ----集成 appkey
实时音视频·video·rtc·appkey