From f39f2685a147037a22c19a26349894284dac70af Mon Sep 17 00:00:00 2001 From: Salem Yaslem Date: Sat, 15 May 2021 14:08:54 +0300 Subject: [PATCH] split audio from video recording in Xorg then merge it with video --- src/ffmpeg_interface.rs | 328 ++++++++++++++++++++++------------------ src/main.rs | 3 +- 2 files changed, 183 insertions(+), 148 deletions(-) diff --git a/src/ffmpeg_interface.rs b/src/ffmpeg_interface.rs index 019d0cf..69e4e42 100644 --- a/src/ffmpeg_interface.rs +++ b/src/ffmpeg_interface.rs @@ -1,7 +1,10 @@ extern crate subprocess; use chrono::prelude::*; use gtk::prelude::*; -use gtk::{CheckButton, ComboBoxText, Entry, FileChooser, ProgressBar, SpinButton, Window, WindowType, WindowPosition}; +use gtk::{ + CheckButton, ComboBoxText, Entry, FileChooser, ProgressBar, SpinButton, Window, WindowPosition, + WindowType, +}; use std::collections::HashMap; use std::path::PathBuf; use std::process::Command; @@ -96,15 +99,22 @@ pub struct Ffmpeg { pub record_frames: SpinButton, pub record_delay: SpinButton, pub command: Entry, - pub process_id: Option, + pub video_process_id: Option, + pub audio_process_id: Option, pub saved_filename: Option, pub unbound: Option>, pub progress_widget: ProgressWidget, } impl Ffmpeg { - pub fn start_record(&mut self, x: u16, y: u16, width: u16, height: u16) -> u32 { - if self.process_id.is_some() { + pub fn start_record( + &mut self, + x: u16, + y: u16, + width: u16, + height: u16, + ) -> (Option, Option) { + if self.video_process_id.is_some() || self.audio_process_id.is_some() { self.stop_record(); } @@ -127,6 +137,22 @@ impl Ffmpeg { .to_string(), ); + if self.record_audio.get_active() { + let mut ffmpeg_command = Command::new("ffmpeg"); + ffmpeg_command.arg("-f"); + ffmpeg_command.arg("pulse"); + ffmpeg_command.arg("-i"); + ffmpeg_command.arg(self.audio_id.get_active_id().unwrap().to_string()); + ffmpeg_command.arg("-f"); + ffmpeg_command.arg("ogg"); + ffmpeg_command.arg(format!( + "{}.temp.audio", + self.saved_filename.as_ref().unwrap().to_string() + )); + ffmpeg_command.arg("-y"); + self.audio_process_id = Some(ffmpeg_command.spawn().unwrap().id()); + } + if is_wayland() { if self.record_video.get_active() { if self.unbound.is_some() { @@ -145,118 +171,106 @@ impl Ffmpeg { ); } - if self.record_audio.get_active() { - let mut ffmpeg_command = Command::new("ffmpeg"); - ffmpeg_command.arg("-f"); - ffmpeg_command.arg("pulse"); - ffmpeg_command.arg("-i"); - ffmpeg_command.arg(self.audio_id.get_active_id().unwrap().to_string()); - ffmpeg_command.arg("-f"); - ffmpeg_command.arg("ogg"); - ffmpeg_command.arg(format!( - "{}.temp.audio", - self.saved_filename.as_ref().unwrap().to_string() - )); - ffmpeg_command.arg("-y"); - self.process_id = Some(ffmpeg_command.spawn().unwrap().id()); - } - - return self.process_id.unwrap(); + return (None, self.audio_process_id); } - let mut ffmpeg_command: Command = Command::new("ffmpeg"); - - // if recorder video switch is enabled, record video with specified width and hight if self.record_video.get_active() { + let mut ffmpeg_command: Command = Command::new("ffmpeg"); + + // record video with specified width and hight ffmpeg_command.arg("-video_size"); ffmpeg_command.arg(format!("{}x{}", width, height)); - } - - // if show mouse switch is enabled, draw the mouse to video - ffmpeg_command.arg("-draw_mouse"); - if self.record_mouse.get_active() { - ffmpeg_command.arg("1"); - } else { - ffmpeg_command.arg("0"); - } - - // if follow mouse switch is enabled, follow the mouse - if self.follow_mouse.get_active() { - ffmpeg_command.arg("-follow_mouse"); - ffmpeg_command.arg("centered"); - } - - ffmpeg_command.arg("-framerate"); - ffmpeg_command.arg(format!("{}", self.record_frames.get_value())); - ffmpeg_command.arg("-f"); - ffmpeg_command.arg("x11grab"); - ffmpeg_command.arg("-i"); - ffmpeg_command.arg(format!( - "{}+{},{}", - std::env::var("DISPLAY") - .unwrap_or(":1".to_string()) - .as_str(), - x, - y - )); - - // if follow audio switch is enabled, record the audio - if self.record_audio.get_active() { + // if show mouse switch is enabled, draw the mouse to video + ffmpeg_command.arg("-draw_mouse"); + if self.record_mouse.get_active() { + ffmpeg_command.arg("1"); + } else { + ffmpeg_command.arg("0"); + } + // if follow mouse switch is enabled, follow the mouse + if self.follow_mouse.get_active() { + ffmpeg_command.arg("-follow_mouse"); + ffmpeg_command.arg("centered"); + } + ffmpeg_command.arg("-framerate"); + ffmpeg_command.arg(format!("{}", self.record_frames.get_value())); ffmpeg_command.arg("-f"); - ffmpeg_command.arg("pulse"); + ffmpeg_command.arg("x11grab"); ffmpeg_command.arg("-i"); - ffmpeg_command.arg(self.audio_id.get_active_id().unwrap().to_string()); - ffmpeg_command.arg("-strict"); - ffmpeg_command.arg("-2"); + ffmpeg_command.arg(format!( + "{}+{},{}", + std::env::var("DISPLAY") + .unwrap_or(":1".to_string()) + .as_str(), + x, + y + )); + ffmpeg_command.arg("-q"); + ffmpeg_command.arg("1"); + ffmpeg_command.arg(self.saved_filename.as_ref().unwrap().to_string()); + ffmpeg_command.arg("-y"); + // sleep for delay + sleep(Duration::from_secs(self.record_delay.get_value() as u64)); + // start recording and return the process id + self.video_process_id = Some(ffmpeg_command.spawn().unwrap().id()); + return (self.video_process_id, self.audio_process_id); } - ffmpeg_command.arg("-q"); - ffmpeg_command.arg("1"); - - ffmpeg_command.arg(self.saved_filename.as_ref().unwrap()); - ffmpeg_command.arg("-y"); - - // sleep for delay - sleep(Duration::from_secs(self.record_delay.get_value() as u64)); - - // start recording and return the process id - self.process_id = Some(ffmpeg_command.spawn().unwrap().id()); - self.process_id.unwrap() + (None, None) } pub fn stop_record(&self) { &self.progress_widget.show(); // kill the process to stop recording - if self.process_id.is_some() { + if self.video_process_id.is_some() { &self .progress_widget - .set_progress("Stop Recording".to_string(), 1, 5); + .set_progress("Stop Recording Video".to_string(), 1, 6); Command::new("kill") - .arg(format!("{}", self.process_id.unwrap())) + .arg(format!("{}", self.video_process_id.unwrap())) .output() .unwrap(); } - if is_wayland() { + if self.audio_process_id.is_some() { + &self + .progress_widget + .set_progress("Stop Recording Audio".to_string(), 2, 6); + Command::new("kill") + .arg(format!("{}", self.audio_process_id.unwrap())) + .output() + .unwrap(); + } + + let is_video_record = std::path::Path::new( + format!( + "{}{}", + self.saved_filename.as_ref().unwrap_or(&String::from("")), + if is_wayland() { ".temp" } else { "" } + ) + .as_str(), + ) + .exists(); + let is_audio_record = std::path::Path::new( + format!( + "{}.temp.audio", + self.saved_filename.as_ref().unwrap_or(&String::from("")) + ) + .as_str(), + ) + .exists(); + + if is_video_record && is_wayland() { // create new dbus session let connection = zbus::Connection::new_session().unwrap(); // bind the connection to gnome screencast proxy let gnome_screencast_proxy = GnomeScreencastProxy::new(&connection).unwrap(); gnome_screencast_proxy.stop_screencast().unwrap(); - let is_audio_record = std::path::Path::new( - format!("{}.temp.audio", self.saved_filename.as_ref().unwrap_or(&String::from(""))).as_str() - ) - .exists(); - let is_video_record = std::path::Path::new( - format!("{}.temp", self.saved_filename.as_ref().unwrap_or(&String::from(""))).as_str() - ) - .exists(); - if self.unbound.is_some() { &self.progress_widget.set_progress( "Stop Wayland Video Recording".to_string(), - 2, - 5, + 3, + 6, ); self.unbound .as_ref() @@ -288,86 +302,106 @@ impl Ffmpeg { ffmpeg_convert_command.output().unwrap(); std::fs::remove_file(format!("{}.temp", self.saved_filename.as_ref().unwrap())) .unwrap(); - - if is_audio_record { - &self.progress_widget.set_progress( - "Stop Wayland Audio Recording".to_string(), - 3, - 5, - ); - - // merge audio with video - let mut ffmpeg_audio_merge_command = Command::new("ffmpeg"); - ffmpeg_audio_merge_command.arg("-i"); - ffmpeg_audio_merge_command.arg(format!( - "{}.temp.without.audio.{}", - self.saved_filename.as_ref().unwrap(), - self.filename.2.get_active_id().unwrap().to_string() - )); - ffmpeg_audio_merge_command.arg("-i"); - ffmpeg_audio_merge_command.arg(format!( - "{}.temp.audio", - self.saved_filename.as_ref().unwrap() - )); - ffmpeg_audio_merge_command.arg("-c:v"); - ffmpeg_audio_merge_command.arg("copy"); - ffmpeg_audio_merge_command.arg("-c:a"); - ffmpeg_audio_merge_command.arg("aac"); - ffmpeg_audio_merge_command.arg(self.saved_filename.as_ref().unwrap()); - ffmpeg_audio_merge_command.arg("-y"); - ffmpeg_audio_merge_command.output().unwrap(); - std::fs::remove_file(format!( - "{}.temp.audio", - self.saved_filename.as_ref().unwrap() - )) - .unwrap(); - std::fs::remove_file(format!( - "{}.temp.without.audio.{}", - self.saved_filename.as_ref().unwrap(), - self.filename.2.get_active_id().unwrap().to_string() - )) - .unwrap(); - } } - } else if is_audio_record { - &self.progress_widget.set_progress( - "Convert Audio to choosen format".to_string(), - 3, - 5, - ); - println!("convert audio"); - Command::new("ffmpeg") - .arg("-f") - .arg("ogg") - .arg("-i") - .arg(format!( - "{}.temp.audio", - self.saved_filename.as_ref().unwrap() - )) - .arg(format!("{}", self.saved_filename.as_ref().unwrap())) - .output() - .unwrap(); + } + } + if is_video_record { + let mut move_command = Command::new("mv"); + move_command.arg(format!( + "{}{}", + self.saved_filename.as_ref().unwrap(), + if is_wayland() { ".temp" } else { "" } + )); + move_command.arg(format!( + "{}{}", + self.saved_filename.as_ref().unwrap_or(&String::new()), + if is_audio_record { + format!( + ".temp.without.audio.{}", + self.filename.2.get_active_id().unwrap().to_string() + ) + } else { + "".to_string() + } + )); + move_command.output().unwrap(); + + // if audio record, then merge video with audio + if is_audio_record { + &self + .progress_widget + .set_progress("Save Audio Recording".to_string(), 4, 6); + + let mut ffmpeg_audio_merge_command = Command::new("ffmpeg"); + ffmpeg_audio_merge_command.arg("-i"); + ffmpeg_audio_merge_command.arg(format!( + "{}.temp.without.audio.{}", + self.saved_filename.as_ref().unwrap(), + self.filename.2.get_active_id().unwrap().to_string() + )); + ffmpeg_audio_merge_command.arg("-i"); + ffmpeg_audio_merge_command.arg(format!( + "{}.temp.audio", + self.saved_filename.as_ref().unwrap() + )); + ffmpeg_audio_merge_command.arg("-c:v"); + ffmpeg_audio_merge_command.arg("copy"); + ffmpeg_audio_merge_command.arg("-c:a"); + ffmpeg_audio_merge_command.arg("aac"); + ffmpeg_audio_merge_command.arg(self.saved_filename.as_ref().unwrap()); + ffmpeg_audio_merge_command.arg("-y"); + sleep(Duration::from_secs(1)); + ffmpeg_audio_merge_command.output().unwrap(); std::fs::remove_file(format!( "{}.temp.audio", self.saved_filename.as_ref().unwrap() )) .unwrap(); + std::fs::remove_file(format!( + "{}.temp.without.audio.{}", + self.saved_filename.as_ref().unwrap(), + self.filename.2.get_active_id().unwrap().to_string() + )) + .unwrap(); } } + // if only audio is recording then convert it to chosen fromat + else if is_audio_record { + &self + .progress_widget + .set_progress("Convert Audio to choosen format".to_string(), 4, 6); + sleep(Duration::from_secs(1)); + Command::new("ffmpeg") + .arg("-f") + .arg("ogg") + .arg("-i") + .arg(format!( + "{}.temp.audio", + self.saved_filename.as_ref().unwrap() + )) + .arg(format!("{}", self.saved_filename.as_ref().unwrap())) + .output() + .unwrap(); + std::fs::remove_file(format!( + "{}.temp.audio", + self.saved_filename.as_ref().unwrap() + )) + .unwrap(); + } // execute command after finish recording if !(self.command.get_text().trim() == "") { &self.progress_widget.set_progress( "execute custom command after finish".to_string(), - 4, 5, + 6, ); Exec::shell(self.command.get_text().trim()).popen().unwrap(); } &self .progress_widget - .set_progress("Finish".to_string(), 5, 5); + .set_progress("Finish".to_string(), 6, 6); &self.progress_widget.hide(); } diff --git a/src/main.rs b/src/main.rs index c37d8f6..3a81362 100644 --- a/src/main.rs +++ b/src/main.rs @@ -290,7 +290,8 @@ fn main() { record_frames: frames_spin, record_delay: delay_spin, command: command_entry, - process_id: None, + video_process_id: None, + audio_process_id: None, saved_filename: None, unbound: None, progress_widget: ProgressWidget::new(&main_window),