为什么做这个工具
平时偶尔会遇到一些简单的视频处理需求,比如 MP4 转 WebM、裁剪画面、截掉开头几秒,或者从视频里导出音频。
这些操作用桌面软件当然可以完成,但临时处理一个文件时,打开网页会更方便。于是我做了一个视频转码工具,文件选择之后直接在当前浏览器中读取、预览、转换和下载,不需要上传到服务器。

功能
不上传文件
页面通过文件选择器读取 File,再交给浏览器里的媒体处理逻辑。项目没有上传接口,视频不会经过服务器。
不过本地处理也受浏览器能力影响,能使用哪些编码器取决于当前浏览器对 WebCodecs 的支持情况。
转换格式
目前支持的视频格式有 MP4、WebM、MOV、MKV,音频格式有 WAV、MP3、AAC。
编码器选项不是固定写死的。选择文件后,工具会根据输入轨道、目标格式和当前浏览器的编码能力列出可用选项。自动模式会优先复制原始编码,只有格式不兼容或进行了裁剪、缩放、旋转等操作时,才会重新编码。
能直接复制轨道时就不重新编码,这样处理速度更快,也不会因为重复编码造成画质损失。
预览和媒体信息
选择文件后可以直接播放、暂停、拖动进度、调节音量和全屏预览。播放器下方的时间轴会根据视频帧生成缩略图,截取视频时可以用它定位位置。
媒体信息区域会显示以下内容:
- 容器格式、时长和文件大小
- 视频尺寸、帧率和视频编码
- 音频编码、采样率和声道数
- 视频和音频轨道数量

裁剪、缩放、旋转和镜像
裁剪可以直接拖动画面上的手柄,也可以输入左、上、宽、高的像素值。输出尺寸支持按宽度或高度调整,并保持原始比例。
另外还支持旋转 90°、180°、270°,以及水平镜像和垂直镜像。
H.264 和 H.265 对画面尺寸有对齐要求,所以转换时会将相关尺寸和坐标调整为偶数,避免导出失败。
截取和音频重采样
打开截取功能后,可以拖动时间轴两端设置开始和结束位置,也可以直接输入时间。时间轴会根据帧率换算边界,比手动拖到大概位置更准确。
如果只需要调整音频采样率,可以从 8,000 Hz、16,000 Hz、22,050 Hz、44,100 Hz 和 48,000 Hz 中选择目标值。
转换过程中会显示进度、已处理时长和已写入的字节数,也可以取消任务。完成后可以下载文件,或者把结果重新放回工具继续处理。
使用流程
把文件拖进页面后,先查看媒体信息和预览,再按需要设置输出格式、裁剪区域、尺寸或截取范围,最后点击转换。
所有步骤都在当前浏览器中完成。大文件转换时会占用较多 CPU 和内存,不同浏览器支持的编码器也可能不一样。
实现方式
Next.js、React 和 TypeScript
项目使用 Next.js App Router。转码页面本身是服务端页面,负责元数据和页面布局;需要访问 File、VideoFrame、OffscreenCanvas 和浏览器存储的部分放在 Client Component 中。
转换工作区维护输入文件、媒体信息、预览地址、裁剪区域、截取范围、输出格式、编码器和转换进度等状态,并用 TypeScript 区分读取、转换、完成和错误等阶段。
Mediabunny 和 WebCodecs
媒体处理使用 mediabunny,它负责读取容器和轨道、获取媒体信息、判断轨道能否直接复制,以及组织音视频转换。
底层编解码由浏览器的 WebCodecs API 提供。AAC、FLAC、MP3、AC-3、E-AC-3 和 Apple ProRes 等能力按需加载对应的 Mediabunny 编码器或解码器,避免初始加载所有依赖。
整个转换流程大致如下:
File
-> BlobSource
-> Input:读取容器、轨道和元数据
-> Conversion:复制或转码,并应用裁剪/缩放/旋转/截取
-> StreamTarget:写入输出数据
-> Blob
-> 浏览器下载时间轴和输出缓存
时间轴使用 CanvasSink 按时间读取视频帧,再把帧绘制成缩略图。播放器和时间轴共用同一套帧率换算逻辑,拖动截取边界时可以更准确地对应到视频帧。
输出文件优先写入浏览器的 Origin Private File System,避免大文件一直占用内存。如果浏览器不支持,就退回到 Uint8Array,最后组装成 Blob。
页面样式使用 CSS Modules,基础组件使用项目里的 shadcn/ui,图标使用 Lucide React。
一些取舍
这个工具没有按视频剪辑软件来做,只保留转码前后比较常用的操作:查看文件、做简单画面处理,然后导出结果。
本地转码的限制也比较明显:大文件会占用设备资源,编码器支持情况会随浏览器变化。因此页面会根据当前环境动态显示编码器,并提供进度和取消操作。
目前它更像是一个处理临时视频文件的小工具。打开页面,拖入文件,完成转换,不需要经过上传这一步。
