Skip to content

Instantly share code, notes, and snippets.

@ilsubyeega
Created December 29, 2024 08:02
Show Gist options
  • Save ilsubyeega/2e08867e3ed79d0268fc2455e098c629 to your computer and use it in GitHub Desktop.
Save ilsubyeega/2e08867e3ed79d0268fc2455e098c629 to your computer and use it in GitHub Desktop.
use mpv::FrameTimeInfo;
use std::process;
mod mpv;
const VIDEO_URL: &str = "/home/sob/Downloads/2024-12-09_23-11-21-00.22.12.245-00.22.40.000.mp4";
#[tokio::main]
async fn main() {
// Arg parsing
let args = std::env::args().collect::<Vec<String>>();
//if args.len() < 2 {
// println!("Usage: {} <url>", args[0]);
// return;
//}
let video_url = args.get(1).cloned()
.or_else(|| Some(String::from(VIDEO_URL)))
.unwrap();
println!("Start time");
let start_time = get_time_until_exit(&video_url).await;
println!("End time");
let end_time = get_time_until_exit(&video_url).await;
println!("\n\
# Result \n\
- Start: {start_time:?} \n\
- End: {end_time:?}
");
// Run ffmpeg to cut the video
// ffmpeg -i input.mp4 -ss 00:00:00 -to 00:00:10 -c copy output.mp4
process::Command::new("ffmpeg")
.args(["-i", &video_url])
// Start time
.args(["-ss", &format!("{}s", start_time.time)])
// End time
.args(["-to", &format!("{}s", end_time.time)])
// Copy codec. ** No re-encoding but not precise timing. NEED TO INVESTIGATE **
.args(["-c:v", "copy"]) // Video
.args(["-c:a", "copy"]) // Audio
// See https://ffmpeg.org/ffmpeg-formats.html
// make_zero: Shift timestamps so that the first timestamp is 0.
.args(["-avoid_negative_ts", "make_zero"])
.arg("output.mp4")
.spawn()
.expect("Failed to execute ffmpeg");
}
async fn get_time_until_exit(video_url: &str) -> FrameTimeInfo {
let mpv = mpv::Mpv::initialize(&"Pause the video and then exit to continue.").unwrap();
mpv.load_file(video_url).unwrap();
let mut event_context = mpv.get_event_context();
let mut frametime = FrameTimeInfo::default();
loop {
// Polling without delays
if let Some(result) = event_context.wait_event(0.0) {
match result {
Ok(libmpv2::events::Event::EndFile(_)) => {
println!("End of file");
break;
}
Ok(e) => println!("Ok: {:?}", e),
Err(err) => println!("Error: {:?}", err),
}
}
// Retrive frameinfo and print it whether it's available or not.
let frame_time_info = mpv.get_frame_time_info();
if let Ok(frame_time_info) = frame_time_info {
println!("{frame_time_info:?}");
frametime = frame_time_info;
}
}
frametime
}
pub struct Mpv {
mpv: libmpv2::Mpv
}
#[derive(Debug)]
pub struct FrameTimeInfo {
/// Time in seconds.
pub time: f64,
/// Frame number.
pub frame_number: i64,
/// Total frame count of video.
pub frame_count: i64
}
impl Default for FrameTimeInfo {
fn default() -> Self {
FrameTimeInfo {
time: 0.0,
frame_number: 0,
frame_count: 0
}
}
}
impl Mpv {
/// Initialize mpv.
pub fn initialize(title: &str) -> Result<Mpv, libmpv2::Error> {
Ok(Mpv {
mpv: libmpv2::Mpv::with_initializer(|init| {
// Set Title
init.set_property("title", title)?;
// Enable mouse and keyboard control
init.set_property("osc", "yes")?;
init.set_property("input-default-bindings", "yes")?;
// Use precise seeking
init.set_property("hr-seek", "yes")?;
// Make window size 50% and 50%.
init.set_property("geometry", "50%x50%")?;
init.set_property("osd-msg1", title)?;
init.set_property("script-opts", "osc-layout=slimbox")?;
Ok(())
})?
})
}
/// Load a file into mpv.
pub fn load_file(&self, file: &str) -> Result<(), libmpv2::Error> {
self.mpv.command("loadfile", &[file, "append-play"])
}
pub fn get_frame_time_info(&self) -> Result<FrameTimeInfo, libmpv2::Error> {
let time: f64 = self.mpv.get_property("time-pos/full")?;
let frame_number: i64 = self.mpv.get_property("estimated-frame-number")?;
let frame_count: i64 = self.mpv.get_property("estimated-frame-count")?;
Ok(FrameTimeInfo {
time,
frame_number,
frame_count
})
}
pub fn get_event_context(&self) -> libmpv2::events::EventContext {
libmpv2::events::EventContext::new(self.mpv.ctx)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment