如何使用ffmpeg-next API按起止时间裁剪视频?求示例
使用ffmpeg-next实现视频裁剪(Rust)
ffmpeg-next 并没有提供直接的"裁剪"API,而是通过输入流seek定位起始时间 + 控制输出帧数量/时长的方式实现视频裁剪。下面是一个完整的示例,实现从指定起始时间开始,裁剪指定时长的视频内容,同时支持编译为单个静态二进制文件。
1. 依赖配置
在Cargo.toml中添加ffmpeg-next依赖,启用静态编译特性:
[dependencies] ffmpeg-next = { version = "0.31", features = ["build", "static", "codec", "format", "filter"] }
build:让库自动编译FFmpeg静态库(需确保系统有编译依赖,比如Linux下的build-essential、yasm等)static:强制静态链接,最终生成单个二进制文件
2. 完整裁剪示例代码
use ffmpeg::format::{input, output, Pixel}; use ffmpeg::media::Type; use ffmpeg::software::scaling::{context::Context, flag::Flags}; use ffmpeg::{codec, frame, Rational}; use std::path::Path; fn crop_video(input_path: &str, output_path: &str, start_sec: f64, duration_sec: f64) -> Result<(), ffmpeg::Error> { // 初始化FFmpeg ffmpeg::init().unwrap(); // 打开输入文件 let mut ictx = input(&Path::new(input_path))?; let input_stream = ictx .streams() .best(Type::Video) .ok_or_else(|| ffmpeg::Error::StreamNotFound)?; let stream_index = input_stream.index(); // 获取视频流的时间基和帧率,计算起始时间戳与目标总帧数 let time_base = input_stream.time_base(); let frame_rate = input_stream.rate(); let start_ts = (start_sec * time_base.0 as f64 / time_base.1 as f64) as i64; let total_frames = (duration_sec * frame_rate.0 as f64 / frame_rate.1 as f64) as usize; // 定位输入流到起始时间 ictx.seek(start_ts, stream_index)?; // 初始化解码器与输出流 let mut decoder = input_stream.codec().decoder().video()?; let mut octx = output(&Path::new(output_path))?; let mut ost = octx.add_stream(decoder.codec())?; ost.set_time_base(time_base); ost.set_rate(frame_rate); ost.set_parameters(input_stream.parameters()); octx.write_header()?; // 初始化格式转换器(这里保持原视频格式输出) let mut scaler = Context::get( decoder.format(), decoder.width(), decoder.height(), decoder.format(), decoder.width(), decoder.height(), Flags::BILINEAR, )?; let mut decoded = frame::Video::empty(); let mut encoded = frame::Video::empty(); let mut packet = ffmpeg::Packet::empty(); let mut frame_count = 0; // 读取并处理帧,直到达到指定时长 while frame_count < total_frames && ictx.read_frame(&mut packet).is_ok() { if packet.stream_index() != stream_index { continue; } // 解码帧 if decoder.send_packet(&packet).is_ok() { while decoder.receive_frame(&mut decoded).is_ok() { scaler.run(&decoded, &mut encoded)?; // 调整帧时间戳,以起始时间为零点 encoded.set_pts(Some(decoded.pts().unwrap() - start_ts)); // 编码并写入输出 let mut encode_packet = ffmpeg::Packet::empty(); if ost.codec().encoder().send_frame(&encoded).is_ok() { while ost.codec().encoder().receive_packet(&mut encode_packet).is_ok() { encode_packet.set_stream_index(ost.index()); octx.write_frame(&encode_packet)?; } } frame_count += 1; if frame_count >= total_frames { break; } } } packet.unref(); } // 写入文件尾,完成输出 ost.codec().encoder().send_eof()?; while ost.codec().encoder().receive_packet(&mut packet).is_ok() { packet.set_stream_index(ost.index()); octx.write_frame(&packet)?; } octx.write_trailer()?; Ok(()) } fn main() { // 示例:裁剪input.mp4中从10秒开始的5秒内容,输出到output.mp4 if let Err(e) = crop_video("input.mp4", "output.mp4", 10.0, 5.0) { eprintln!("裁剪失败: {}", e); } else { println!("裁剪完成"); } }
3. 关键逻辑说明
- 起始时间定位:通过
ictx.seek(start_ts, stream_index)将输入流定位到指定时间戳,时间戳需要转换为视频流的时间基单位 - 时长控制:通过计算目标时长对应的总帧数,循环处理直到达到帧数量上限
- 静态编译:依赖中的
static特性确保所有FFmpeg依赖被静态链接到二进制文件中,编译后可直接在同架构的系统上运行
4. 编译注意事项
- Linux系统:需先安装编译依赖:
sudo apt install build-essential yasm libx264-dev - macOS系统:可通过Homebrew安装依赖:
brew install yasm x264 - Windows系统:建议使用MSYS2环境,安装对应编译工具链和依赖
内容的提问来源于stack exchange,提问作者Michael Savchenko
相关产品推荐
相关产品推荐

