test alternative core

This commit is contained in:
ochibani 2025-01-20 18:45:37 +02:00
parent e353283f49
commit ab74b64f9a
No known key found for this signature in database
GPG Key ID: 2C6B61CE0C704ED4
6 changed files with 747 additions and 975 deletions

932
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -5,13 +5,13 @@ edition = "2021"
[features]
cmd = []
gtk = ["adw", "chrono", "glib", "subprocess"]
gtk = ["adw", "chrono", "glib", "subprocess", "ffmpeg-sidecar"]
[dependencies]
adw = { version = "0.2.1", package = "libadwaita", features = ["gtk_v4_6"], optional = true }
anyhow = "1.0.86"
chrono = { version = "0.4.19", optional = true }
ffmpeg-sidecar = "1.1.0"
ffmpeg-sidecar = { version = "2.0.5", optional = true }
glib = { version = "0.10.3", optional = true }
open = "5.1.4"
subprocess = {version = "0.2.6", optional = true }

View File

@ -4,19 +4,24 @@ use adw::gtk::{CheckButton, ComboBoxText, Entry, FileChooserNative, SpinButton};
use adw::gtk::prelude::*;
use anyhow::{anyhow, Error, Result};
#[cfg(feature = "gtk")]
use ffmpeg_sidecar::{child::FfmpegChild, command::FfmpegCommand};
#[cfg(feature = "gtk")]
use chrono::Utc;
use ffmpeg_sidecar::child::FfmpegChild;
use ffmpeg_sidecar::command::FfmpegCommand;
use tempfile;
use std::{cell::RefCell, time::Instant};
use std::path::Path;
#[cfg(feature = "gtk")]
use std::path::PathBuf;
#[cfg(feature = "cmd")]
use std::process::{Child, Command};
use std::rc::Rc;
use std::thread::sleep;
use std::time::Duration;
use crate::utils::{is_input_audio_record, is_output_audio_record, is_valide, is_video_record, RecordMode};
use crate::utils::{is_valide, is_video_record, RecordMode};
#[cfg(feature = "cmd")]
use crate::utils::{is_input_audio_record, is_output_audio_record};
#[cfg(feature = "cmd")]
#[derive(Clone)]
@ -29,9 +34,9 @@ pub struct Ffmpeg {
pub temp_output_audio_filename: String,
pub temp_video_filename: String,
pub height: Option<u16>,
pub input_audio_process: Option<Rc<RefCell<FfmpegChild>>>,
pub output_audio_process: Option<Rc<RefCell<FfmpegChild>>>,
pub video_process: Option<Rc<RefCell<FfmpegChild>>>,
pub input_audio_process: Option<Rc<RefCell<Child>>>,
pub output_audio_process: Option<Rc<RefCell<Child>>>,
pub video_process: Option<Rc<RefCell<Child>>>,
pub audio_record_bitrate: u16,
pub record_delay: u16,
pub record_frames: u16,
@ -48,8 +53,6 @@ pub struct Ffmpeg {
pub audio_output_id: String,
pub filename: (FileChooserNative, Entry, ComboBoxText),
pub output: String,
pub temp_input_audio_filename: String,
pub temp_output_audio_filename: String,
pub temp_video_filename: String,
pub saved_filename: String,
pub height: Option<u16>,
@ -60,138 +63,155 @@ pub struct Ffmpeg {
pub record_delay: SpinButton,
pub record_frames: SpinButton,
pub video_record_bitrate: SpinButton,
pub audio_input_switch: CheckButton,
pub audio_output_switch: CheckButton,
pub follow_mouse: CheckButton,
pub record_mouse: CheckButton,
pub show_area: CheckButton,
pub video_switch: CheckButton,
}
#[cfg(feature = "cmd")]
impl Ffmpeg {
// Start video recording
pub fn start_video(&mut self, x: u16, y: u16, width: u16, height: u16, mode: RecordMode) -> Result<()> {
let display = format!("{}+{},{}",
std::env::var("DISPLAY").unwrap_or_else(|_| ":0".to_string())
.as_str(),
x,
y
);
let mut ffmpeg_command = FfmpegCommand::new();
let format = "x11grab";
self.height = Some(height);
// Record video to tmp if audio record enabled
if !self.audio_input_id.is_empty()
|| !self.audio_output_id.is_empty()
|| self.output == "gif"
{
let suffix = if self.output == "gif" {
".mp4"
} else {
&format!(".{}", &self.output)
};
let video_tempfile = tempfile::Builder::new().prefix("ffmpeg-video-")
.suffix(suffix)
.tempfile()?
.keep()?;
self.temp_video_filename = Path::new(&video_tempfile.1).to_string_lossy()
.to_string();
}
// Record video with specified width and hight
if self.follow_mouse {
match mode {
RecordMode::Screen => {
let width = width as f32 * 0.95;
let height = height as f32 * 0.95;
ffmpeg_command.size(width as u32, height as u32);
},
_=> {
ffmpeg_command.size(width.into(), height.into());
}
}
if mode == RecordMode::Window && !self.follow_mouse.is_active() {
// pulse = gstreamer for video && add to cmd linux + add convert function to gstreamer ouput
} else {
ffmpeg_command.size(width.into(), height.into());
}
let display = format!("{}+{},{}",
std::env::var("DISPLAY").unwrap_or_else(|_| ":0".to_string())
.as_str(),
x,
y
);
let mut ffmpeg_command = Command::new("ffmpeg");
let format = "x11grab";
self.height = Some(height);
// Show grabbed area
if self.show_area {
ffmpeg_command.args(["-show_region", "1"]);
}
// If show mouse switch is enabled, draw the mouse to video
if self.record_mouse {
ffmpeg_command.args(["-draw_mouse", "1"]);
} else {
ffmpeg_command.args(["-draw_mouse", "0"]);
};
// Follow the mouse
if self.follow_mouse {
ffmpeg_command.args(["-follow_mouse", "centered"]);
}
// Disable frame rate if value is zero
if self.record_frames > 0 {
ffmpeg_command.args(["-framerate", &self.record_frames.to_string()]);
}
// Video format && input
ffmpeg_command.format(format)
.input(display);
// Disable bitrate if value is zero
if self.video_record_bitrate > 0 {
ffmpeg_command.args([
"-b:v",
&format!("{}K", self.video_record_bitrate),
]);
}
// tmp file
if self.audio_input_id.is_empty() &&
self.audio_output_id.is_empty() &&
self.output != "gif"
{
ffmpeg_command.args(["-hls_flags", "temp_file"]);
}
// Remove metadate
ffmpeg_command.args(["-map_metadata", "-1"]);
// Output
ffmpeg_command.args([
// Record video to tmp if audio record enabled
if !self.audio_input_id.is_empty()
|| !self.audio_output_id.is_empty()
|| self.output == "gif"
{
if !self.audio_input_id.is_empty()
|| !self.audio_output_id.is_empty()
|| self.output == "gif"
{
&self.temp_video_filename
let suffix = if self.output == "gif" {
".mp4"
} else {
&self.filename
&format!(".{}", &self.output)
};
let video_tempfile = tempfile::Builder::new().prefix("ffmpeg-video-")
.suffix(suffix)
.tempfile()?
.keep()?;
self.temp_video_filename = Path::new(&video_tempfile.1).to_string_lossy()
.to_string();
}
// Record video with specified width and hight
if self.follow_mouse {
match mode {
RecordMode::Screen => {
let width = width as f32 * 0.95;
let height = height as f32 * 0.95;
ffmpeg_command.args(["-s", &format!("{}x{}", width.to_string(), height.to_string())]);
},
_=> {
ffmpeg_command.args(["-s", &format!("{}x{}", width.to_string(), height.to_string())]);
}
}
},
]);
ffmpeg_command.overwrite();
} else {
ffmpeg_command.args(["-s", &format!("{}x{}", width.to_string(), height.to_string())]);
}
// Sleep for delay
sleep(Duration::from_secs(self.record_delay as u64));
// Show grabbed area
if self.show_area {
ffmpeg_command.args(["-show_region", "1"]);
}
// Start recording and return the process id
self.video_process = Some(Rc::new(RefCell::new(ffmpeg_command.spawn()?)));
// If show mouse switch is enabled, draw the mouse to video
if self.record_mouse {
ffmpeg_command.args(["-draw_mouse", "1"]);
} else {
ffmpeg_command.args(["-draw_mouse", "0"]);
};
// Follow the mouse
if self.follow_mouse {
ffmpeg_command.args(["-follow_mouse", "centered"]);
}
// Disable frame rate if value is zero
if self.record_frames > 0 {
ffmpeg_command.args(["-framerate", &self.record_frames.to_string()]);
}
// Video format && input
ffmpeg_command.args(["-f", format])
.args(["-i", &display]);
// Disable bitrate if value is zero
if self.video_record_bitrate > 0 {
ffmpeg_command.args([
"-b:v",
&format!("{}K", self.video_record_bitrate),
]);
}
// tmp file
if self.audio_input_id.is_empty() &&
self.audio_output_id.is_empty() &&
self.output != "gif"
{
ffmpeg_command.args(["-hls_flags", "temp_file"]);
}
// Remove metadate
ffmpeg_command.args(["-map_metadata", "-1"]);
// Output
ffmpeg_command.args([
{
if !self.audio_input_id.is_empty()
|| !self.audio_output_id.is_empty()
|| self.output == "gif"
{
&self.temp_video_filename
} else {
&self.filename
}
},
]);
ffmpeg_command.arg("-y");
// Sleep for delay
sleep(Duration::from_secs(self.record_delay as u64));
// Start recording and return the process id
self.video_process = Some(Rc::new(RefCell::new(ffmpeg_command.spawn()?)));
}
Ok(())
}
// Stop video recording
pub fn stop_video(&mut self) -> Result<()> {
// Quit the process to stop recording
// Kill the process to stop recording
if self.video_process.is_some() {
Command::new("kill")
.arg(format!(
"{}",
self.video_process
.clone()
.ok_or_else(|| anyhow!("Failed to get video_process ID."))?
.borrow_mut().id()
))
.output()?;
self.video_process
.clone()
.ok_or_else(|| anyhow!("Not exiting the video recording process successfully."))?
.borrow_mut()
.quit()?;
.wait()?;
}
Ok(())
}
@ -203,10 +223,10 @@ impl Ffmpeg {
.keep()?;
self.temp_input_audio_filename = Path::new(&input_audio_tempfile.1).to_string_lossy()
.to_string();
let mut ffmpeg_command = FfmpegCommand::new();
ffmpeg_command.format("pulse")
.input(&self.audio_input_id)
.format("ogg");
let mut ffmpeg_command = Command::new("ffmpeg");
ffmpeg_command.args(["-f", "pulse"])
.args(["-i", &self.audio_input_id])
.args(["-f", "ogg"]);
// Disable bitrate if value is zero
if self.audio_record_bitrate > 0 {
ffmpeg_command.args([
@ -217,7 +237,7 @@ impl Ffmpeg {
// Remove metadate
ffmpeg_command.args(["-map_metadata", "-1"]);
ffmpeg_command.arg(&self.temp_input_audio_filename);
ffmpeg_command.overwrite();
ffmpeg_command.arg("-y");
// Sleep for delay
if !is_video_record(&self.temp_video_filename) {
@ -231,14 +251,24 @@ impl Ffmpeg {
// Stop audio input recording
pub fn stop_input_audio(&mut self) -> Result<()> {
// Quit the process to stop recording
// Kill the process to stop recording
if self.input_audio_process.is_some() {
Command::new("kill")
.arg(format!(
"{}",
self.input_audio_process
.clone()
.ok_or_else(|| anyhow!("Failed to get input_audio_process ID."))?
.borrow_mut().id()
))
.output()?;
self.input_audio_process
.clone()
.ok_or_else(|| anyhow!("Not exiting the input audio recording process successfully."))?
.borrow_mut()
.quit()?;
}
.wait()?;
}
Ok(())
}
@ -250,14 +280,14 @@ impl Ffmpeg {
.keep()?;
self.temp_output_audio_filename = Path::new(&output_audio_tempfile.1).to_string_lossy()
.to_string();
let mut ffmpeg_command = FfmpegCommand::new();
ffmpeg_command.format("pulse")
.input(&self.audio_output_id)
.format("ogg");
let mut ffmpeg_command = Command::new("ffmpeg");
ffmpeg_command.args(["-f", "pulse"])
.args(["-i", &self.audio_output_id])
.args(["-f", "ogg"]);
// Remove metadate
ffmpeg_command.args(["-map_metadata", "-1"]);
ffmpeg_command.arg(&self.temp_output_audio_filename);
ffmpeg_command.overwrite();
ffmpeg_command.arg("-y");
// Sleep for delay
if !is_video_record(&self.temp_video_filename) && !is_input_audio_record(&self.temp_input_audio_filename) {
@ -271,13 +301,23 @@ impl Ffmpeg {
// Stop audio output recording
pub fn stop_output_audio(&mut self) -> Result<()> {
// Quit the process to stop recording
// Kill the process to stop recording
if self.output_audio_process.is_some() {
Command::new("kill")
.arg(format!(
"{}",
self.output_audio_process
.clone()
.ok_or_else(|| anyhow!("Failed to get output_audio_process ID."))?
.borrow_mut().id()
))
.output()?;
self.output_audio_process
.clone()
.ok_or_else(|| anyhow!("Not exiting the output audio recording process successfully."))?
.borrow_mut()
.quit()?;
.wait()?;
}
Ok(())
}
@ -288,7 +328,7 @@ impl Ffmpeg {
if self.output != "gif" {
// Validate video file integrity
let start_time = Instant::now();
let duration = Duration::from_secs(60);
let duration = Duration::from_secs(300);
loop {
if is_valide(&self.temp_video_filename)? {
break;
@ -296,27 +336,32 @@ impl Ffmpeg {
return Err(Error::msg("Unable to validate tmp video file."));
}
}
let mut ffmpeg_command = FfmpegCommand::new();
ffmpeg_command.input(&self.temp_video_filename);
ffmpeg_command.format("ogg");
if is_input_audio_record(&self.temp_input_audio_filename) {
ffmpeg_command.input(&self.temp_input_audio_filename);
}
if is_output_audio_record(&self.temp_output_audio_filename) {
ffmpeg_command.input(&self.temp_output_audio_filename);
}
ffmpeg_command.args([
"-c:a",
"aac",
&self.filename,
]);
ffmpeg_command.overwrite()
.spawn()?
.wait()?;
if is_input_audio_record(&self.temp_input_audio_filename) ||
is_output_audio_record(&self.temp_output_audio_filename) {
let mut ffmpeg_command = Command::new("ffmpeg");
ffmpeg_command.args(["-i", &self.temp_video_filename]);
ffmpeg_command.args(["-f", "ogg"]);
if is_input_audio_record(&self.temp_input_audio_filename) {
ffmpeg_command.args(["-i", &self.temp_input_audio_filename]);
}
if is_output_audio_record(&self.temp_output_audio_filename) {
ffmpeg_command.args(["-i", &self.temp_output_audio_filename]);
}
ffmpeg_command.args([
"-c:a",
"aac",
&self.saved_filename.clone()
]);
ffmpeg_command.arg("-y")
.spawn()?
.wait()?;
} else {
std::fs::copy(&self.temp_video_filename, &self.saved_filename)?;
}
} else {
// Validate video file integrity
let start_time = Instant::now();
let duration = Duration::from_secs(60);
let duration = Duration::from_secs(300);
loop {
if is_valide(&self.temp_video_filename)? {
break;
@ -335,7 +380,7 @@ impl Ffmpeg {
} else if is_input_audio_record(&self.temp_input_audio_filename) {
// Validate audio file integrity
let start_time = Instant::now();
let duration = Duration::from_secs(60);
let duration = Duration::from_secs(300);
loop {
if is_valide(&self.temp_input_audio_filename)? {
break;
@ -344,23 +389,23 @@ impl Ffmpeg {
}
}
// If only audio is recording then convert it to chosen format
let mut ffmpeg_command = FfmpegCommand::new();
ffmpeg_command.format("ogg");
ffmpeg_command.input(&self.temp_input_audio_filename);
let mut ffmpeg_command = Command::new("ffmpeg");
ffmpeg_command.args(["-f", "ogg"]);
ffmpeg_command.args(["-i", &self.temp_input_audio_filename]);
if is_output_audio_record(&self.temp_output_audio_filename) {
ffmpeg_command.input(&self.temp_output_audio_filename);
ffmpeg_command.args(["-i", &self.temp_output_audio_filename]);
}
ffmpeg_command.args([
"-c:a",
"aac",
&self.filename,
]).overwrite()
]).arg("-y")
.spawn()?
.wait()?;
} else {
// Validate audio file integrity
let start_time = Instant::now();
let duration = Duration::from_secs(60);
let duration = Duration::from_secs(300);
loop {
if is_valide(&self.temp_output_audio_filename)? {
break;
@ -369,11 +414,11 @@ impl Ffmpeg {
}
}
// If only output audio is recording then convert it to chosen format
let mut ffmpeg_command = FfmpegCommand::new();
ffmpeg_command.format("ogg");
ffmpeg_command.input(&self.temp_output_audio_filename);
let mut ffmpeg_command = Command::new("ffmpeg");
ffmpeg_command.args(["-f", "ogg"]);
ffmpeg_command.args(["-i", &self.temp_output_audio_filename]);
ffmpeg_command.arg(&self.filename)
.overwrite()
.arg("-y")
.spawn()?
.wait()?;
}
@ -458,127 +503,131 @@ impl Ffmpeg {
// Start video recording
pub fn start_video(&mut self, x: u16, y: u16, width: u16, height: u16, mode: RecordMode) -> Result<()> {
//if mode == RecordMode::Window && !self.follow_mouse.is_active() { //REVIEW //TODO
if mode == RecordMode::Window && !self.follow_mouse.is_active() {
// pulse = gstreamer for video && add to cmd linux + add convert function to gstreamer ouput
//} else {}
let display = format!("{}+{},{}",
std::env::var("DISPLAY").unwrap_or_else(|_| ":0".to_string())
.as_str(),
x,
y
);
let mut ffmpeg_command = FfmpegCommand::new();
let format = "x11grab";
self.height = Some(height);
let filename = self.saved_filename.clone();
self.output = Path::new(&filename).extension()
.ok_or_else(|| anyhow!("Failed to get file extension."))?
.to_string_lossy().to_string();
} else {
let display = format!("{}+{},{}",
std::env::var("DISPLAY").unwrap_or_else(|_| ":0".to_string())
.as_str(),
x,
y
);
let mut ffmpeg_command = FfmpegCommand::new();
let format = "x11grab";
self.height = Some(height);
let filename = self.saved_filename.clone();
self.output = Path::new(&filename).extension()
.ok_or_else(|| anyhow!("Failed to get file extension."))?
.to_string_lossy().to_string();
// Record video to tmp if audio record enabled
if !self.audio_input_id.active_id().ok_or_else(|| anyhow!("Failed to get audio input device ID."))?
.to_string().is_empty()
|| !self.audio_output_id.is_empty()
|| self.output == "gif"
{
let suffix = if self.output == "gif" {
".mp4"
// Record video to tmp if audio record enabled
if self.output == "gif" {
let suffix = ".mp4";
let video_tempfile = tempfile::Builder::new().prefix("ffmpeg-video-")
.suffix(suffix)
.tempfile()?
.keep()?;
self.temp_video_filename = Path::new(&video_tempfile.1).to_string_lossy()
.to_string();
}
// Record video with specified width and hight
if self.follow_mouse.is_active() {
match mode {
RecordMode::Screen => {
let width = width as f32 * 0.95;
let height = height as f32 * 0.95;
ffmpeg_command.size(width as u32, height as u32);
},
_=> {
ffmpeg_command.size(width as u32, height as u32);
}
}
} else {
&format!(".{}", &self.output)
};
let video_tempfile = tempfile::Builder::new().prefix("ffmpeg-video-")
.suffix(suffix)
.tempfile()?
.keep()?;
self.temp_video_filename = Path::new(&video_tempfile.1).to_string_lossy()
.to_string();
}
ffmpeg_command.size(width as u32, height as u32);
}
// Record video with specified width and hight
if self.follow_mouse.is_active() {
match mode {
RecordMode::Screen => {
let width = width as f32 * 0.95;
let height = height as f32 * 0.95;
ffmpeg_command.size(width as u32, height as u32);
},
_=> {
ffmpeg_command.size(width.into(), height.into());
// Show grabbed area
if self.show_area.is_active() {
ffmpeg_command.args(["-show_region", "1"]);
}
// If show mouse switch is enabled, draw the mouse to video
if self.record_mouse.is_active() {
ffmpeg_command.args(["-draw_mouse", "1"]);
} else {
ffmpeg_command.args(["-draw_mouse", "0"]);
};
// Follow the mouse
if self.follow_mouse.is_active() {
ffmpeg_command.args(["-follow_mouse", "centered"]);
}
// Disable frame rate if value is zero
if self.record_frames.value() as u16 > 0 {
ffmpeg_command.args(["-framerate", &self.record_frames.value().to_string()]);
}
// Video format && input
ffmpeg_command.format(format)
.input(display);
// Record audio input
if self.audio_input_switch.is_active() {
ffmpeg_command.format("pulse")
.input(&self.audio_input_id.active_id()
.ok_or_else(|| anyhow!("Failed to get audio input ID."))?
);
}
// Record audio output
if self.audio_output_switch.is_active() {
ffmpeg_command.format("pulse")
.input(&self.audio_output_id);
}
// Disable video bitrate if value is zero
if self.video_record_bitrate.value() as u16 > 0 {
ffmpeg_command.args([
"-b:v",
&format!("{}K", self.video_record_bitrate.value() as u16),
]);
}
// Disable audio bitrate if value is zero
if self.audio_input_switch.is_active() || self.audio_output_switch.is_active() {
if self.audio_record_bitrate.value() as u16 > 0 {
ffmpeg_command.args([
"-b:a",
&format!("{}K", self.audio_record_bitrate.value() as u16),
]);
}
}
} else {
ffmpeg_command.size(width.into(), height.into());
}
// Show grabbed area
if self.show_area.is_active() {
ffmpeg_command.args(["-show_region", "1"]);
}
// Remove metadate
ffmpeg_command.args(["-map_metadata", "-1"]);
// If show mouse switch is enabled, draw the mouse to video
if self.record_mouse.is_active() {
ffmpeg_command.args(["-draw_mouse", "1"]);
} else {
ffmpeg_command.args(["-draw_mouse", "0"]);
};
// Follow the mouse
if self.follow_mouse.is_active() {
ffmpeg_command.args(["-follow_mouse", "centered"]);
}
// Disable frame rate if value is zero
if self.record_frames.value() as u16 > 0 {
ffmpeg_command.args(["-framerate", &self.record_frames.value().to_string()]);
}
// Video format && input
ffmpeg_command.format(format)
.input(display);
// Disable bitrate if value is zero
if self.video_record_bitrate.value() as u16 > 0 {
// Output
let saved_filename = self.saved_filename.clone();
ffmpeg_command.args([
"-b:v",
&format!("{}K", self.video_record_bitrate.value() as u16),
]);
}
// tmp file
if self.audio_input_id.active_id().ok_or_else(|| anyhow!("Failed to get audio input device ID."))?
.to_string().is_empty() &&
self.audio_output_id.is_empty() &&
self.output != "gif"
{
ffmpeg_command.args(["-hls_flags", "temp_file"]);
}
// Remove metadate
ffmpeg_command.args(["-map_metadata", "-1"]);
// Output
let saved_filename = self.saved_filename.clone();
ffmpeg_command.args([
{
if !self.audio_input_id.active_id()
.ok_or_else(|| anyhow!("Failed to get active audio input device ID."))?
.to_string().is_empty()
|| !self.audio_output_id.is_empty()
|| self.output == "gif"
{
&self.temp_video_filename
} else {
&saved_filename
}
},
]);
ffmpeg_command.overwrite();
if self.output == "gif"
{
&self.temp_video_filename
} else {
&saved_filename
}
},
]);
ffmpeg_command.overwrite();
// Sleep for delay
sleep(Duration::from_secs(self.record_delay.value() as u64));
// Sleep for delay
sleep(Duration::from_secs(self.record_delay.value() as u64));
// Start recording and return the process id
self.video_process = Some(Rc::new(RefCell::new(ffmpeg_command.spawn()?)));
// Start recording and return the process id
self.video_process = Some(Rc::new(RefCell::new(ffmpeg_command.spawn()?)));
}
Ok(())
}
@ -598,18 +647,18 @@ impl Ffmpeg {
// Start audio input recording
pub fn start_input_audio(&mut self) -> Result<()> {
let input_audio_tempfile = tempfile::Builder::new().prefix("ffmpeg-audio-")
.suffix(".ogg")
.tempfile()?
.keep()?;
self.temp_input_audio_filename = Path::new(&input_audio_tempfile.1).to_string_lossy()
.to_string();
let mut ffmpeg_command = FfmpegCommand::new();
ffmpeg_command.format("pulse")
.input(&self.audio_input_id.active_id()
.ok_or_else(|| anyhow!("Failed to get audio input ID."))?
)
.format("ogg");
);
ffmpeg_command.format("ogg");
if self.audio_output_switch.is_active() {
ffmpeg_command.format("pulse")
.input(&self.audio_output_id);
}
ffmpeg_command.format("ogg");
// Disable bitrate if value is zero
if self.audio_record_bitrate.value() as u16 > 0 {
ffmpeg_command.args([
@ -617,13 +666,16 @@ impl Ffmpeg {
&format!("{}K", self.audio_record_bitrate.value() as u16),
]);
}
// Remove metadate
ffmpeg_command.args(["-map_metadata", "-1"]);
ffmpeg_command.arg(&self.temp_input_audio_filename);
// Output
ffmpeg_command.arg(&self.saved_filename);
ffmpeg_command.overwrite();
// Sleep for delay
if !is_video_record(&self.temp_video_filename) {
if !self.video_switch.is_active() {
sleep(Duration::from_secs(self.record_delay.value() as u64));
}
@ -641,29 +693,25 @@ impl Ffmpeg {
.ok_or_else(|| anyhow!("Not exiting the input audio recording process successfully."))?
.borrow_mut()
.quit()?;
}
}
Ok(())
}
// Start audio output recording
pub fn start_output_audio(&mut self) -> Result<()> {
let output_audio_tempfile = tempfile::Builder::new().prefix("ffmpeg-audio-")
.suffix(".ogg")
.tempfile()?
.keep()?;
self.temp_output_audio_filename = Path::new(&output_audio_tempfile.1).to_string_lossy()
.to_string();
let mut ffmpeg_command = FfmpegCommand::new();
ffmpeg_command.format("pulse")
.input(&self.audio_output_id)
.format("ogg");
// Remove metadate
ffmpeg_command.args(["-map_metadata", "-1"]);
ffmpeg_command.arg(&self.temp_output_audio_filename);
// Output
ffmpeg_command.arg(&self.saved_filename);
ffmpeg_command.overwrite();
// Sleep for delay
if !is_video_record(&self.temp_video_filename) && !is_input_audio_record(&self.temp_input_audio_filename) {
if !self.video_switch.is_active() && !self.audio_input_switch.is_active() {
sleep(Duration::from_secs(self.record_delay.value() as u64));
}
@ -688,118 +736,34 @@ impl Ffmpeg {
// Merge tmp to target format
pub fn merge(&mut self) -> Result<()> {
if is_video_record(&self.temp_video_filename) {
if self.output != "gif" {
// Validate video file integrity
let start_time = Instant::now();
let duration = Duration::from_secs(60);
loop {
if is_valide(&self.temp_video_filename)? {
break;
} else if Instant::now().duration_since(start_time) >= duration {
return Err(Error::msg("Unable to validate tmp video file."));
}
}
if is_input_audio_record(&self.temp_input_audio_filename) ||
is_output_audio_record(&self.temp_output_audio_filename) {
let mut ffmpeg_command = FfmpegCommand::new();
ffmpeg_command.input(&self.temp_video_filename);
ffmpeg_command.format("ogg");
if is_input_audio_record(&self.temp_input_audio_filename) {
ffmpeg_command.input(&self.temp_input_audio_filename);
}
if is_output_audio_record(&self.temp_output_audio_filename) {
ffmpeg_command.input(&self.temp_output_audio_filename);
}
ffmpeg_command.args([
"-c:a",
"aac",
&self.saved_filename.clone()
]);
ffmpeg_command.overwrite()
.spawn()?
.wait()?;
} else {
std::fs::copy(&self.temp_video_filename, &self.saved_filename)?;
}
} else {
// Validate video file integrity
let start_time = Instant::now();
let duration = Duration::from_secs(60);
loop {
if is_valide(&self.temp_video_filename)? {
break;
} else if Instant::now().duration_since(start_time) >= duration {
return Err(Error::msg("Unable to validate tmp video file."));
}
}
// Convert MP4 to GIF
let filter = format!("fps={},scale={}:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse",
self.record_frames.value() as u16,
self.height.ok_or_else
(|| anyhow!("Unable to get height value"))?);
let ffmpeg_convert = format!("ffmpeg -i file:{} -filter_complex '{}' \
-loop 0 {} -y", &self.temp_video_filename,filter,
&self.saved_filename
.clone());
std::process::Command::new("sh").arg("-c").arg(&ffmpeg_convert).output()?;
}
} else if is_input_audio_record(&self.temp_input_audio_filename) {
// Validate audio file integrity
// Validate video file integrity
let start_time = Instant::now();
let duration = Duration::from_secs(60);
let duration = Duration::from_secs(300);
loop {
if is_valide(&self.temp_input_audio_filename)? {
if is_valide(&self.temp_video_filename)? {
break;
} else if Instant::now().duration_since(start_time) >= duration {
return Err(Error::msg("Unable to validate tmp video file."));
}
}
// If only audio is recording then convert it to chosen format
let mut ffmpeg_command = FfmpegCommand::new();
ffmpeg_command.format("ogg");
ffmpeg_command.input(&self.temp_input_audio_filename);
if is_output_audio_record(&self.temp_output_audio_filename) {
ffmpeg_command.input(&self.temp_output_audio_filename);
}
ffmpeg_command.args([
"-c:a",
"aac",
&self.saved_filename
.clone()
]).overwrite()
.spawn()?
.wait()?;
} else {
// Validate audio file integrity
let start_time = Instant::now();
let duration = Duration::from_secs(60);
loop {
if is_valide(&self.temp_output_audio_filename)? {
break;
} else if Instant::now().duration_since(start_time) >= duration {
return Err(Error::msg("Unable to validate tmp video file."));
}
}
// If only output audio is recording then convert it to chosen format
let mut ffmpeg_command = FfmpegCommand::new();
ffmpeg_command.format("ogg");
ffmpeg_command.input(&self.temp_output_audio_filename);
ffmpeg_command.arg(&self.saved_filename
.clone())
.overwrite()
.spawn()?
.wait()?;
// Convert MP4 to GIF
let filter = format!("fps={},scale={}:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse",
self.record_frames.value() as u16,
self.height.ok_or_else
(|| anyhow!("Unable to get height value"))?);
let ffmpeg_convert = format!("ffmpeg -i file:{} -filter_complex '{}' \
-loop 0 {} -y", &self.temp_video_filename,filter,
&self.saved_filename
.clone());
std::process::Command::new("sh").arg("-c").arg(&ffmpeg_convert).output()?;
}
Ok(())
}
// Clean tmp
pub fn clean(&mut self) -> Result<()> {
let tmp_files = vec![ &self.temp_input_audio_filename, &self.temp_output_audio_filename, &self.temp_video_filename ];
for file in tmp_files {
if Path::new(file).try_exists()? {
std::fs::remove_file(file)?;
}
if Path::new(&self.temp_video_filename).try_exists()? {
std::fs::remove_file(&self.temp_video_filename)?;
}
Ok(())
}

View File

@ -37,12 +37,9 @@ pub struct Ffmpeg {
pub record_delay: u16,
pub record_frames: u16,
pub video_record_bitrate: u16,
pub audio_input_switch: bool,
pub audio_output_switch: bool,
pub follow_mouse: bool,
pub record_mouse: bool,
pub show_area: bool,
pub video_switch: bool,
}
#[cfg(feature = "gtk")]
@ -304,13 +301,13 @@ impl Ffmpeg {
ffmpeg_command.args([
"-c:a",
"aac",
&self.saved_filename.clone()
&self.filename
]);
ffmpeg_command.overwrite()
.spawn()?
.wait()?;
} else {
std::fs::copy(&self.temp_video_filename, &self.saved_filename)?;
std::fs::copy(&self.temp_video_filename, &self.filename)?;
}
} else {
// Validate video file integrity

View File

@ -2,7 +2,7 @@ use anyhow::Result;
use std::process::Command;
// Select recording mode
#[derive(Clone, Copy)]
#[derive(Clone, Copy, PartialEq)]
pub enum RecordMode {
Area,
Screen,

View File

@ -779,9 +779,12 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
record_delay: delay_spin.clone(),
record_frames: frames_spin,
video_record_bitrate: video_bitrate_spin,
audio_input_switch: audio_input_switch.clone(),
audio_output_switch: audio_output_switch.clone(),
follow_mouse: follow_mouse_switch.clone(),
record_mouse: mouse_switch.clone(),
show_area: area_switch
show_area: area_switch,
video_switch: video_switch.clone()
}));
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
let ffmpeg_record_interface: Rc<RefCell<Ffmpeg>> = Rc::new(RefCell::new(Ffmpeg {
@ -793,8 +796,6 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
format_chooser_combobox,
),
output: String::new(),
temp_input_audio_filename: String::new(),
temp_output_audio_filename: String::new(),
temp_video_filename: String::new(),
saved_filename: String::new(),
height: None,
@ -805,9 +806,12 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
record_delay: delay_spin.clone(),
record_frames: frames_spin,
video_record_bitrate: video_bitrate_spin,
audio_input_switch: audio_input_switch.clone(),
audio_output_switch: audio_output_switch.clone(),
follow_mouse: follow_mouse_switch.clone(),
record_mouse: mouse_switch.clone(),
show_area: area_switch
show_area: area_switch,
video_switch: video_switch.clone()
}));
// Record button
@ -817,7 +821,6 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
record_label.set_label(&get_bundle("record", None));
let _audio_input_switch = audio_input_switch.clone();
let _audio_output_switch = audio_output_switch.clone();
//let bundle_msg = get_bundle("already-exist", None);
let _delay_spin = delay_spin.clone();
let _delay_window = delay_window.clone();
let _delay_window_button = delay_window_button.clone();
@ -903,7 +906,7 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
_play_button.hide();
_record_button.hide();
_stop_button.show();
if _audio_input_switch.is_active() {
if _audio_input_switch.is_active() && !_video_switch.is_active() {
match _ffmpeg_record_interface.borrow_mut().start_input_audio() {
Ok(_) => {
// Do nothing
@ -923,7 +926,7 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
},
}
}
if _audio_output_switch.is_active() {
if _audio_output_switch.is_active() && !_audio_input_switch.is_active() && !_video_switch.is_active() {
match _ffmpeg_record_interface.borrow_mut().start_output_audio() {
Ok(_) => {
// Do nothing
@ -991,7 +994,7 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
let mut show_play = true;
_record_time_label.set_visible(false);
stop_timer(_record_time_label.clone());
if _audio_input_switch.is_active() {
if _audio_input_switch.is_active() && !_video_switch.is_active() {
match _ffmpeg_record_interface.borrow_mut().stop_input_audio() {
Ok(_) => {
// Continue
@ -1012,7 +1015,7 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
},
}
}
if _audio_output_switch.is_active() {
if _audio_output_switch.is_active() && !_audio_input_switch.is_active() && !_video_switch.is_active() {
match _ffmpeg_record_interface.borrow_mut().stop_output_audio() {
Ok(_) => {
// Continue
@ -1054,19 +1057,6 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
},
}
}
// Save tmp files
match _ffmpeg_record_interface.borrow_mut().merge() {
Ok(_) => {
// Continue
},
Err(error) => {
show_play = false;
let text_buffer = TextBuffer::new(None);
text_buffer.set_text(&format!("{}", error));
_error_message.set_buffer(Some(&text_buffer));
_error_dialog.show();
},
};
if _video_switch.is_active() {
_mouse_switch.set_sensitive(true);
_follow_mouse_switch.set_sensitive(true);
@ -1078,17 +1068,6 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
_play_button.show();
}
_record_button.show();
match _ffmpeg_record_interface.borrow_mut().clean() {
Ok(_) => {
// Continue
},
Err(error) => {
let text_buffer = TextBuffer::new(None);
text_buffer.set_text(&format!("{}", error));
_error_message.set_buffer(Some(&text_buffer));
_error_dialog.show();
},
};
});
// Delay window button