在 Rust 里调用 silk-v3 解码器解码微信语音
微信语音文件是 silk v3 格式(.amr 或 .silk),要在 Rust 程序里解码,需要调用 C 写的 silk-v3-decoder。整个过程是:把 C 代码编译成静态库,再用 Rust FFI 调用。
编译 silk-v3-decoder 静态库
克隆并编译
git clone https://github.com/kn007/silk-v3-decoder
cd silk-v3-decoder
make
make 完成后会生成 libSKP_SILK_SDK.a 和一堆 .o 文件。
修改 Decoder.c 的入口函数名
test/Decoder.c 里的 main 函数需要改名,否则和 Rust 的 main 冲突。把 main 改成 silkv3_decoder,然后重新编译这个文件:
gcc -c -Wall -O3 -Iinterface -Isrc -Itest -o test/Decoder.o test/Decoder.c
合并成一个静态库
把 Decoder.o 和 make 生成的所有 .o 文件合并:
# 先解压 libSKP_SILK_SDK.a 里的 .o 文件
mkdir temp && cd temp
ar -x ../libSKP_SILK_SDK.a
cd ..
# 合并所有 .o 文件
ar -r libcombined.a temp/*.o test/Decoder.o
得到 libcombined.a,把它放到 Rust 项目里某个固定路径,比如 silk/libcombined.a。
Rust 项目配置
Cargo.toml
[package]
name = "silkv3-rs"
version = "0.1.0"
edition = "2021"
build = "build.rs"
[dependencies]
libc = "0.2"
build.rs,告诉 Rust 编译器静态库的位置和名称:
fn main() {
println!("cargo:rustc-link-search=native=/path/to/your/project/silk");
println!("cargo:rustc-link-lib=static=combined");
}
路径改成 libcombined.a 实际所在的目录。
src/main.rs,声明外部函数并调用:
use libc::c_int;
use std::ffi::CString;
use std::os::raw::c_char;
extern "C" {
fn silkv3_decoder(argc: c_int, argv: *const *const c_char) -> c_int;
}
fn main() {
let input = CString::new("/path/to/input.amr").unwrap();
let output = CString::new("/path/to/output.pcm").unwrap();
// argv[0] 是程序名占位,传 null
let args: [*const c_char; 3] = [
std::ptr::null(),
input.as_ptr(),
output.as_ptr(),
];
unsafe {
silkv3_decoder(3, args.as_ptr());
}
}
解码出来的是 PCM 格式,如果需要 mp3 或 wav,再用 ffmpeg 转一下:
ffmpeg -f s16le -ar 24000 -ac 1 -i output.pcm output.mp3
运行
cargo run
第一次会编译 C 静态库链接,之后就快了。