use ez_ffmpeg::{FfmpegContext, Input, Output};
use std::sync::{Arc, Mutex};
fn main() {
use std::fs::File;
use std::io::{Read, Seek, SeekFrom, Write};
let input_file = "test.mp4";
let output_file = "output.mp4";
let input_file = Arc::new(Mutex::new(
File::open(input_file).expect("Failed to open input file"),
));
let read_callback: Box<dyn FnMut(&mut [u8]) -> i32 + Send> = {
let input = Arc::clone(&input_file);
Box::new(move |buf: &mut [u8]| -> i32 {
let mut input = input.lock().unwrap();
match input.read(buf) {
Ok(0) => ffmpeg_sys_next::AVERROR_EOF, Ok(bytes_read) => bytes_read as i32, Err(_) => ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::EIO), }
})
};
let seek_callback: Box<dyn FnMut(i64, i32) -> i64 + Send> = {
let input = Arc::clone(&input_file);
Box::new(move |offset: i64, whence: i32| -> i64 {
let mut input = input.lock().unwrap();
if whence == ffmpeg_sys_next::AVSEEK_SIZE {
if let Ok(size) = input.metadata().map(|m| m.len() as i64) {
println!("FFmpeg requested stream size: {}", size);
return size; }
return ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::EIO) as i64;
}
let seek_result = match whence {
ffmpeg_sys_next::SEEK_SET => input.seek(SeekFrom::Start(offset as u64)),
ffmpeg_sys_next::SEEK_CUR => input.seek(SeekFrom::Current(offset)),
ffmpeg_sys_next::SEEK_END => input.seek(SeekFrom::End(offset)),
_ => {
println!("Unsupported seek mode: {whence}");
return ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::ESPIPE) as i64;
}
};
match seek_result {
Ok(new_pos) => new_pos as i64,
Err(_) => ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::EIO) as i64,
}
})
};
let mut input: Input = read_callback.into();
input = input.set_seek_callback(seek_callback);
let output_file = Arc::new(Mutex::new(
File::create(output_file).expect("Failed to create output file"),
));
let write_callback: Box<dyn FnMut(&[u8]) -> i32 + Send> = {
let output_file = Arc::clone(&output_file);
Box::new(move |buf: &[u8]| -> i32 {
let mut output_file = output_file.lock().unwrap();
match output_file.write_all(buf) {
Ok(_) => buf.len() as i32, Err(_) => ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::EIO), }
})
};
#[allow(unreachable_patterns)]
let seek_callback: Box<dyn FnMut(i64, i32) -> i64 + Send> = {
let output_file = Arc::clone(&output_file);
Box::new(move |offset: i64, whence: i32| -> i64 {
let mut file = output_file.lock().unwrap();
match whence {
ffmpeg_sys_next::AVSEEK_SIZE => {
if let Ok(size) = file.metadata().map(|m| m.len() as i64) {
println!("FFmpeg requested stream size: {size}");
return size; }
return ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::EIO) as i64;
}
ffmpeg_sys_next::AVSEEK_FLAG_BYTE => {
println!(
"FFmpeg requested byte-based seeking. Seeking to byte offset: {offset}"
);
if let Ok(new_pos) = file.seek(SeekFrom::Start(offset as u64)) {
return new_pos as i64; }
return ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::EIO) as i64;
}
ffmpeg_sys_next::SEEK_SET => file.seek(SeekFrom::Start(offset as u64)),
ffmpeg_sys_next::SEEK_CUR => file.seek(SeekFrom::Current(offset)),
ffmpeg_sys_next::SEEK_END => file.seek(SeekFrom::End(offset)),
_ => Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"Unsupported seek mode",
)),
}
.map_or(
ffmpeg_sys_next::AVERROR(ffmpeg_sys_next::EIO) as i64,
|pos| pos as i64,
)
})
};
let mut output: Output = write_callback.into();
output = output.set_seek_callback(seek_callback).set_format("mp4");
FfmpegContext::builder()
.input(input)
.output(output)
.build()
.unwrap()
.start()
.unwrap()
.wait()
.unwrap();
}