2026 年在 Rust 里处理音视频,该走哪条路?

先交代写法:六条路各按同一套写------赢在哪、真实限制、一句判词,限制那栏不会因为哪条路更新更热就手软。另外先消个歧:文中说的 Rust crate ez-ffmpeg,和 JS 圈那个上过 HN 首页的 "Ez FFmpeg" 同名不同物,两者无关。

常见的选型场景是这样的:你要在 Rust 服务里抽几帧喂模型,或者把音轨转成 16 kHz 采样喂语音识别。打开 crates.io,先看到月下载 128 万的 ffmpeg-next,点进仓库,维护者原话是 "maintenance-only mode for the most part... Any PR to improve existing API is unlikely to be merged";转头问社区,得到 Rust 老兵 kornel 的建议------"avoid ffmpeg... all incomplete and poorly maintained";最后在 rustcc 的帖子里看到被认同的退路:直接 Command::new("ffmpeg"),理由是「最靠谱,没那么多坑」。

一圈下来,很多人就停在了最后一步。本文把 2026 年 7 月实际可走的六条路摆开:每条路赢在哪、真实限制是什么、一句判词。口径说明:下载数均按月统计,数据截至 2026-07;笔者没核实过的数字一律不进正文,表中以「---」标注。

路 1:std::process::Command + ffmpeg------被低估的默认答案

赢在哪。 互联网三十年积累的 FFmpeg 知识都以命令行形式存在,拿来即用:HN 上命令行教程类内容动辄几百分(FFmpeg by Example 920 分),你遇到的每个任务几乎都有现成命令。没有 FFI、没有链接步骤、没有版本适配。rustcc 那句「最靠谱,没那么多坑」不是玩笑,是大量踩坑之后的理性收敛。

真实限制。 部署时要带上 ffmpeg 可执行文件;更要命的是,数据一旦要回到你的进程里------帧、PCM 采样------就得走 stdout 解析或临时文件。jaredly 早在 2015 年提出的 issue 里就是这个模式:先把画面渲染成图片,再用命令行 ffmpeg 拼成视频(原话 "using ffmpeg on the cli")。十年过去,很多人仍在这么干。不是因为它好,是因为别的路一直没铺平。

判词:产物是文件、不需要数据回内存,它今天仍是对的;数据要进内存,代价就来了。

路 2:ffmpeg-sidecar------还是子进程,但封装得省心

赢在哪。 月下载约 13 万,自述能把任意视频当作裸 RGB 帧数组迭代(原话 "as if it were an array of raw RGB frames")。它把「启动子进程」封装成了结构化 API:你写的是迭代器,不是 stdout 解析器。因为通过子进程的 stdin/stdout,它完全不链接 libav------编译、链接、平台适配这些麻烦,与你无关。想要帧数据、又不想碰 FFI 的人,这是体验最好的一条路。

真实限制。 本质仍是子进程:运行环境里最终要有一个 ffmpeg 可执行文件,帧数据要穿越进程边界;进程内的滤镜图、精细控制不在这个模型里。进程边界在你的负载下代价有多大,量了才算数------本文不引用数字。

判词:接受子进程模型的前提下,拿帧最省心的路。

路 3:ffmpeg-next / rsmpeg------底层绑定库,整个生态的地基

赢在哪。 要完整的 libav 控制面------自定义 IO、冷门封装格式、逐 packet 的精细操作------这一层没有替代品。ffmpeg-next 月下载 128 万,ez-ffmpeg 自己也是通过它链接 libav 的;这个数字本身就说明有多少东西压在它上面。

真实限制。 先说维护状态:ffmpeg-next 是有意冻结的("maintenance-only mode for the most part"),不是废弃,但改进型 PR 基本不会被合并;而它所在的 fork 链已有三代------ffmpeg → ffmpeg-next → ffmpeg-the-third。再说使用成本:解一帧要手写 send_packet/receive_frame,40 行起步的样板代码,连「音视频一起转码」这种任务都可能翻不到现成示例------有人看完示例后留下的原话是 "Examples... do not include transcoding of audio+video"。kornel 那句 "avoid ffmpeg... all incomplete and poorly maintained",骂的正是这一类库的整体状态。

还有一个反直觉的事实:这一层以 safe 封装为卖点的库,也有公开 API 被标注 "might trigger undefined behavior" 的先例(zmwangx/rust-ffmpeg#225)。safe 的皮,FFI 的骨------绑定层的 safe 是工程承诺,不是数学证明。

单看 rsmpeg:877 star,挂在 larksuite 组织下,支持 FFmpeg 6/7/8(0.18.0 跟进了 FFmpeg 8.0)。节奏确实慢------约 11 个月没有大动作------但说它「休眠」言过其实。要贴着 FFmpeg 大版本走的底层封装,目前它是跟得更紧的那个。

判词:当地基,极称职;当日常 API,心智负担全额自付。

路 4:video-rs------高层视频,自己说「还没做完」

赢在哪。 高层方案中,长期只有它一个像样的选项(月下载约 3.2 万)。它 2023 年就提供了 PyAV 风格的 decode/encode:十行代码内迭代出 RGB ndarray;README 示例本身就是用 ndarray 逐帧编码出一个 rainbow.mp4;还有 seek_to_frame(是否帧精确,文档没说)。今天想在 Rust 里获得 PyAV 式的视频解码体验,它是最顺手的现成答案。

真实限制。 限制也写在它自己的 README 里:"still a work-in-progress... some parts not flushed out","Use with caution"。没有音频 API。官方后继项目 rave 的目标是脱离 FFmpeg,但目前 "not ready for use yet"。

判词:视频单科很能打;音频缺席加自述未完成,决定了它是单科选手。

路 5:symphonia + rubato------纯 Rust 音频,直到撞上编解码墙

赢在哪。 纯 Rust 能从 MP4/MKV 里解出音轨并重采样到 16 kHz(symphonia + rubato,symphonium 一次调用搞定;mutter 五行就能喂给 whisper-rs)。解码和重采样整条链路都是 Rust,是本文六条路里唯一从头到尾不碰任何 C 产物的------不链 libav,也不需要 ffmpeg 二进制。供应链干净这件事,这条路独享。顺带修正一个流传的误会:whisper-rs 的 GitHub 仓库虽已 archive,但在 Codeberg 继续维护,月下载约 17 万------别当它已经死了。

真实限制。 要「任意视频、任意编码、一行到位」就撞上编解码墙:symphonia 的 HE-AAC、Opus 仍标注未完成,AC-3 根本不在支持表里------这正是 FFmpeg 后端仍不可替代的地方。另外,视频不在它的射程内。

判词:音频任务且编码在支持表内,它是最干净的方案;出了表,就得回 FFmpeg 系。

路 6:ez-ffmpeg------进程内 FFmpeg 运行时

官方定位原文如下(英文照抄):"the actively-developed high-level in-process FFmpeg runtime for Rust."

这个定位的核心不是哪一件功能,而是「把 FFmpeg CLI 原样搬进 Rust」的人体工程学。 一条链式调用读起来就是一条命令:

rust 复制代码
FfmpegContext::builder()
    .input("input.mkv")
    .output(
        Output::from("output.mp4")
            .set_video_codec("libx264")            // -c:v libx264
            .set_video_codec_opt("crf", "23")      // -crf 23
            .set_video_codec_opt("preset", "fast") // -preset fast
            .set_audio_codec("aac"),               // -c:a aac
    )
    .build()?
    .start()?
    .wait()?;

迁移是机械平移,没有要重学的概念:秒数换成微秒(-ss 10set_start_time_us(10_000_000));参数名去掉 - 原样可用(crfpresetmovflags 这类走 set_*_opt,直通 FFmpeg 的 AVOption 系统------CLI 认什么名字,这里就认什么名字);-vf 后面那串滤镜一字不改填进 filter_desc;选流也是 CLI 的 stream specifier 语法(0:a:0)。你攒了多年的 ffmpeg 命令行直觉,在这里一条都不作废。

0.15 起,连「平移」这一步都可以先省掉------命令直接粘进去跑。 开启 cli 特性后,from_cli 接受一条完整的 ffmpeg 命令字符串,在你自己的进程里解析、建流水线、跑完,不起子进程:

rust 复制代码
use ez_ffmpeg::cli::from_cli;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    from_cli("ffmpeg -i input.mp4 -c:v libx264 -crf 28 -preset veryfast -c:a aac -y output.mp4")?
        .start()?
        .wait()?;
    Ok(())
}

值得单说的是它的态度:命令被分成三类------验证过的形态 才允许执行(目前 6 个,每个都有语义金样与真正的 ffmpeg CLI 逐项对拍过);没验证的 只翻译、不执行;不认识的 当场拒绝,并锚定到出问题的那个 token。姊妹函数 emit_rust_code 则把同一条命令翻译成一段可直接编译的 builder 代码,你可以接着改、接着扩展。它宁可清清楚楚地拒绝你,也不做那种「跑起来了,但结果和 ffmpeg 微妙地不一样」的事。

同构不止停在 API 面,一直通到源码。 内部调度按 ffmpeg CLI 自身的架构建模:fftools 里的 ffmpeg_demux.c / ffmpeg_dec.c / ffmpeg_filter.c / ffmpeg_enc.c / ffmpeg_mux.c / ffmpeg_sched.c / sync_queue.c,在这里对应 demux_task / dec_task / filter_task / enc_task / mux_task / ffmpeg_scheduler / sync_queue------文件对文件。这带来两个实际好处:一,行为要向 CLI 对齐时,有逐行可对照的参照物,出了差异能定位到 fftools 的对应位置;二,想搞懂 FFmpeg 内部机制的人,可以把这份代码当成带类型、带所有权语义的 fftools 对照读本------比直接啃 C 源码的坡度缓得多。

功能面,先交代历史,再说新东西。 ez-ffmpeg 0.13 时代就能用 FrameFilter 在流水线里拦截、改写每一帧(仓库自带自定义 tile/volume 滤镜示例);0.14 新增了专用的 FrameExtractor / SampleExtractor / VideoWriter------把「能做到」变成「一行做到」,并默认依据色彩标签正确完成 YUV→RGB 转换。具体来说:FrameExtractor 抽帧支持采样策略(EveryNth / UniformN / Keyframes);SampleExtractor 直接给出 16 kHz mono f32,可以喂 whisper-rs;VideoWriter 将你自己渲染的帧送入编码流水线(write / write_owned),带滤镜图校验,输出端支持文件与 RTMP 直播目标(直播目标有 API 层与模块文档支持,本文未实测推流)。色彩这件小事值得单说:HD 视频默认按 BT.709 转 RGB,而老版本 PyAV 的 to_ndarray('rgb24') 是一刀切 BT.601,饱和色会偏。

真实限制。 第一,编译和链接的痛,ez-ffmpeg 一点没消除------它仍通过 ffmpeg-next 链接 libav,而这恰恰是上文刚批评过的那层地基:那一层的冻结状态与 fork 链风险,它原样继承。那位在 GitHub issue 里写下 "I've spent the last 12 hours... willing to spend another week" 的开发者,换成 ez-ffmpeg 还是要过同一道链接关。它能承诺的只有后半句:完成链接后,那 100 行手写流水线无需再写。第二,上面这三个新 API 在 README 里明确标注为 experimental;刚说的 cli 门面也有自己的边界------它是可选特性(要显式开),执行路径目前只认 FFmpeg 7.1 运行时(别的版本在任何 I/O 之前就明确报错),而且是个刻意窄的子集:只有那 6 个验证过的形态能跑,两遍编码、复杂滤镜图、硬件加速都不在里面。第三,项目约有 340 star,仍处于早期阶段,且目前主要由一人维护------bus factor 是真实风险;「safe 的 Rust API」不等于「没有 C 的 CVE」,底层仍是 libav,封装层自身也有 unsafe 胶水------「safe 的皮,FFI 的骨」这句话,对它自己同样成立。第四,decord 的 GPU 批量解码、torchaudio 的多后端 DSP,ez-ffmpeg 并不宣称与之对等。

判词:会写 ffmpeg 命令、想把它搬进 Rust 进程,这是迁移成本最低的路;帧和采样要进内存、又要 FFmpeg 级格式覆盖,这也是最短的路。要纯 Rust 供应链,或者过不了链接关,请选别条。

一张总对照表

路线 运行模型 覆盖亮点(有据) 关键限制(有据) 数据点(2026-07)
Command + ffmpeg CLI 子进程 全部命令行能力;全网现成命令直接可用 部署带二进制;数据回内存要过 stdout/临时文件 FFmpeg 8.0 已发布,其 Whisper 支持上过 HN 首页(1033 分)
ffmpeg-sidecar 子进程 把视频变成 RGB 帧迭代器("as if it were an array of raw RGB frames");不链 libav 仍是子进程;运行环境需有 ffmpeg 可执行文件 约 13 万下载/月
ffmpeg-next 进程内(底层绑定) 完整 libav 控制面;生态地基 maintenance-only;40 行起步的手写流水线;三代 fork 史 128 万下载/月
rsmpeg 进程内(safe 封装) 支持 FFmpeg 6/7/8(0.18.0 含 8.0) 节奏慢,约 11 个月无大动作 877 star
video-rs 进程内(高层) 十行 RGB ndarray;ndarray 编出 rainbow.mp4;seek_to_frame 无音频 API;自述 WIP、"Use with caution";继任 rave 未就绪 约 3.2 万下载/月
symphonia + rubato 进程内(纯 Rust) 解音轨 + 重采样 16 kHz;symphonium 一次调用;mutter 五行喂 whisper-rs HE-AAC/Opus 标注未完成;AC-3 不在支持表;不做视频 symphonia 约 107 万下载/月、0.6.0(2026-05);whisper-rs 约 17 万/月(Codeberg 维护)
ac-ffmpeg 进程内 视频/音频/编码三域都覆盖 停在 packet 循环层;近 14 个月未发版 ---
ez-ffmpeg 进程内(运行时) API 与内部调度按 FFmpeg CLI 同构建模,命令行经验一对一平移;0.15 起可把 ffmpeg 命令直接粘进去在进程内跑(cli 门面,验证子集);FrameExtractor / SampleExtractor / VideoWriter 三件一等公民 API;按色彩标签正确转 RGB 仍链 libav,编译痛不消失;新 API 标 experimental;早期阶段 约 340 star

「---」为笔者未核实的数据,本文不依赖它们成立。

地图边界说明:gstreamer-rs 也是一个真实存在的选项------月下载约 56 万、维护活跃的成熟流水线生态------但它既非 FFmpeg 系,也不是「几行代码」风格的 API,属于另一张地图,本文不展开。

差集:Python 三个库的工作,由一个进程内运行时接手

先把话说全,免得被断章取义:Rust 并非拿不到解码帧------video-rs 十行内就能迭代 RGB ndarray,ffmpeg-sidecar 更是把任意视频变成 RGB 帧迭代器(子进程方式)。真正缺的是:在一个进程内、FFmpeg 级格式覆盖的运行时里,把这件事做成调用只需一行、支持采样策略和色彩管理的 API。

所以差集长这样:Python 那边是 PyAV + decord + torchaudio 三个库;Rust 这边 video-rs 不管音频、symphonia 不管视频、sidecar 需要启动子进程------ez-ffmpeg 0.14 第一次把这三类工作放进同一个进程内 FFmpeg 运行时。

先把范围划清楚:按笔者截至 2026-07 的检索,Rust 生态中,同时把「解码 RGB 帧导出、16 kHz f32 音频导出、自产帧推入编码」做成一等公民 API、且仍在积极维护的进程内 FFmpeg 运行时,只有 ez-ffmpeg 一个:video-rs 连音频 API 都没有,自述 WIP(官方后继项目 rave 尚 not ready);ac-ffmpeg 覆盖三域,但停留在 packet 循环层、近 14 个月未发版。gstreamer-rs 的 appsink/appsrc 确实也能在进程内做到这三类事,但它是另一套流水线模型、非 FFmpeg 系------这正是前文把它划出本图的原因,此处不重复计入。

再补两句实话。其一,PyAV 本身也是 libav 的绑定库------ez-ffmpeg 用的是同一个引擎,所以这不是「Rust 重写了 PyAV」,而是同一个引擎、只是在外面套了一层 Rust 原生的高层 API,不用掉进裸 FFI,也不用起子进程。其二,为什么现在值得填补这个差集:candle、burn、ort、tract、whisper-rs 都在快速成长,这些库要的是「帧和音频变成数据」,而今天喂给它们的第一步,常常是一行 shell。生态缺口有时荒诞到什么程度?有人用 Rust 写了 decord 式的视频读取器 video_reader-rs------却只发布到 PyPI,cargo add 不到。

什么时候,答案就该是 CLI

选型文最容易犯的毛病,是把所有场景都往最新的那条路上引。下面这些场景,直接用命令行才是对的:

  • 产物是文件,命令现成。 转码、切片、一次性批处理------机器学习音频圈至今仍把 ffmpeg -i in.mp4 -ar 16000 -ac 1 -c:a pcm_s16le out.wav 作为第一步。如果这一行就是你的全部需求,别引入任何 crate。
  • 运行环境本来就带 ffmpeg 二进制。 部署的代价已经付过一次,不必再付一遍链接的代价。
  • 要的正是进程隔离。 转码任务崩溃不连累服务主进程------这是子进程模型的特性,不是缺陷(这是工程判断,非引文)。
  • 团队熟悉命令行、不熟悉 libav API,而任务不在关键路径上。 迁移成本大于收益。

反过来,一旦帧或采样必须落进你的进程内存------喂模型、实时处理、和业务逻辑交织------CLI 的代价就显出来了:解析 stdout、写临时 WAV、参数容易对不上。jaredly 在 2015 年提出的变通办法沿用至今,正是这些代价积累十年的证据。

写在最后:按场景选,不按信仰选

  • 产物是文件、命令现成 → CLI
  • 要帧数据、不想碰 FFI 和链接 → ffmpeg-sidecar
  • 要完整 libav 控制面 → ffmpeg-next (要跟 FFmpeg 大版本跟得更紧,看 rsmpeg)
  • 只做视频帧、能接受 WIP → video-rs
  • 只做音频、要纯 Rust、编码在支持表内 → symphonia + rubato
  • 会写 ffmpeg 命令,想以最低迁移成本搬进 Rust 进程 → ez-ffmpeg(参数名、滤镜串、流选择符全是 CLI 语法)
  • 帧、采样、自产帧都要进程内,且要 FFmpeg 级覆盖 → ez-ffmpeg(记住两条:链接的痛不消失;新 API 标 experimental)

「语言只是工具,还是要根据场景选用更合适的」------crate 更是如此。六条路各有赢面,谁也替代不了谁;取长补短,比站队有用。

Rust crate ez-ffmpeg 的项目地址:github.com/YeautyYE/ez-ffmpeg。

相关推荐
weixin_495248402 小时前
短剧视频翻译配音指南:投流素材和完整剧集译制有何不同?
音视频
气泡音人声分离16 小时前
音频 Key 和 BPM 识别原理:歌曲调性和速度是如何分析出来的?
音视频·升降调·音频变速
海带紫菜菠萝汤19 小时前
WebCodecs API 实战:浏览器原生视频编解码的原理与性能测试
前端·javascript·音视频·视频编解码
TDengine (老段)19 小时前
TDengine Go 与 Rust 连接器 — 高性能异步访问
大数据·数据库·物联网·golang·rust·时序数据库·tdengine
kriston202620 小时前
2026音频指纹识别方案准确率对比:主流厂商差距与选型指南
音视频
薛定谔的猫-菜鸟程序员21 小时前
基于 Electron 的本地短视频解析与下载工具:架构设计与工程实践
java·electron·音视频
程序员大辉1 天前
seedance2.0做的视频太贵?开源免费最佳替代方案:bernini生视频,ltx2.3来配音
音视频·视频大模型·ltx2.3·bernini
魔力女仆1 天前
【RUST AI】把 TTS 搬进浏览器:kokoroi-rs 的 WASM 实践
人工智能·rust·wasm
勿忘初心12211 天前
【Windows流媒体实战1】FFmpeg+Nginx-RTMP Windows详细搭建教程
ffmpeg·nginx-rtmp·windows流媒体搭建·rtmp直播