mirror of
https://github.com/xlmnxp/blue-recorder.git
synced 2025-03-31 14:54:54 +03:00
Compare commits
3 Commits
9c4d8c6b82
...
a6987857bd
Author | SHA1 | Date | |
---|---|---|---|
|
a6987857bd | ||
|
056bdf6db6 | ||
|
a8d6a48838 |
@ -14,8 +14,11 @@ async-std = {version = "1.12.0", features = ["attributes"]}
|
||||
chrono = { version = "0.4.19", optional = true }
|
||||
ffmpeg-sidecar = "2.0.5"
|
||||
glib = { version = "0.10.3", optional = true }
|
||||
gstreamer = "0.23.4"
|
||||
open = "5.1.4"
|
||||
subprocess = {version = "0.2.6", optional = true }
|
||||
tempfile = "3.10.1"
|
||||
zbus = "5.3.0"
|
||||
|
||||
# FreeBSD/Linux-only dependency
|
||||
[target.'cfg(not(windows))'.dependencies]
|
||||
gstreamer = "0.23.4"
|
||||
|
@ -24,6 +24,7 @@ use crate::utils::{is_video_record, is_wayland, RecordMode};
|
||||
use crate::utils::{is_input_audio_record, is_output_audio_record, is_valid};
|
||||
#[cfg(feature = "gtk")]
|
||||
use crate::utils::validate_video_file;
|
||||
#[cfg(feature = "gtk")]
|
||||
use crate::wayland_linux::{CursorModeTypes, RecordTypes, WaylandRecorder};
|
||||
|
||||
#[cfg(feature = "cmd")]
|
||||
@ -80,7 +81,7 @@ pub struct Ffmpeg {
|
||||
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() {
|
||||
//if let RecordMode::Window == mode && !self.follow_mouse.is_active() {
|
||||
// TODO pulse = gstreamer for video && add to cmd linux + add convert function to gstreamer ouput
|
||||
//} else {
|
||||
let display = format!("{}+{},{}",
|
||||
@ -94,7 +95,7 @@ impl Ffmpeg {
|
||||
self.width = Some(width);
|
||||
self.height = Some(height);
|
||||
|
||||
// Record video to tmp if audio record enabled
|
||||
// Record video to tmp if output is GIF
|
||||
if !self.audio_input_id.is_empty()
|
||||
|| !self.audio_output_id.is_empty()
|
||||
|| self.output == "gif"
|
||||
@ -104,7 +105,7 @@ impl Ffmpeg {
|
||||
} else {
|
||||
&format!(".{}", &self.output)
|
||||
};
|
||||
let video_tempfile = tempfile::Builder::new().prefix("ffmpeg-video-")
|
||||
let video_tempfile = tempfile::Builder::new().prefix(".ffmpeg-video-")
|
||||
.suffix(suffix)
|
||||
.tempfile()?
|
||||
.keep()?;
|
||||
@ -212,7 +213,7 @@ impl Ffmpeg {
|
||||
|
||||
// Start audio input recording
|
||||
pub fn start_input_audio(&mut self) -> Result<()> {
|
||||
let input_audio_tempfile = tempfile::Builder::new().prefix("ffmpeg-audio-")
|
||||
let input_audio_tempfile = tempfile::Builder::new().prefix(".ffmpeg-audio-")
|
||||
.suffix(".ogg")
|
||||
.tempfile()?
|
||||
.keep()?;
|
||||
@ -259,7 +260,7 @@ impl Ffmpeg {
|
||||
|
||||
// Start audio output recording
|
||||
pub fn start_output_audio(&mut self) -> Result<()> {
|
||||
let output_audio_tempfile = tempfile::Builder::new().prefix("ffmpeg-audio-")
|
||||
let output_audio_tempfile = tempfile::Builder::new().prefix(".ffmpeg-audio-")
|
||||
.suffix(".ogg")
|
||||
.tempfile()?
|
||||
.keep()?;
|
||||
@ -535,7 +536,7 @@ impl Ffmpeg {
|
||||
// Record video to tmp if audio record enabled
|
||||
if self.output == "gif" {
|
||||
let suffix = ".mp4";
|
||||
let video_tempfile = tempfile::Builder::new().prefix("ffmpeg-video-")
|
||||
let video_tempfile = tempfile::Builder::new().prefix(".ffmpeg-video-")
|
||||
.suffix(suffix)
|
||||
.tempfile()?
|
||||
.keep()?;
|
||||
@ -668,6 +669,9 @@ impl Ffmpeg {
|
||||
Err(error) => {
|
||||
if self.output == "gif" {
|
||||
self.clean()?;
|
||||
} else {
|
||||
self.temp_video_filename = self.saved_filename.clone();
|
||||
self.clean()?;
|
||||
}
|
||||
return Err(Error::msg(format!("{}", error)));
|
||||
},
|
||||
@ -730,11 +734,20 @@ impl Ffmpeg {
|
||||
pub fn stop_input_audio(&mut self) -> Result<()> {
|
||||
// Quit the process to stop recording
|
||||
if self.input_audio_process.is_some() {
|
||||
self.input_audio_process
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow!("Not exiting the input audio recording process successfully."))?
|
||||
.borrow_mut()
|
||||
.quit()?;
|
||||
match self.input_audio_process
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow!("Not exiting the input audio recording process successfully."))?
|
||||
.borrow_mut()
|
||||
.quit() {
|
||||
Ok(_) => {
|
||||
// Continue
|
||||
},
|
||||
Err(error) => {
|
||||
self.temp_video_filename = self.saved_filename.clone();
|
||||
self.clean()?;
|
||||
return Err(Error::msg(format!("{}", error)));
|
||||
},
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -766,11 +779,20 @@ impl Ffmpeg {
|
||||
pub fn stop_output_audio(&mut self) -> Result<()> {
|
||||
// Quit the process to stop recording
|
||||
if self.output_audio_process.is_some() {
|
||||
self.output_audio_process
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow!("Not exiting the output audio recording process successfully."))?
|
||||
.borrow_mut()
|
||||
.quit()?;
|
||||
match self.output_audio_process
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow!("Not exiting the output audio recording process successfully."))?
|
||||
.borrow_mut()
|
||||
.quit() {
|
||||
Ok(_) => {
|
||||
// Continue
|
||||
},
|
||||
Err(error) => {
|
||||
self.temp_video_filename = self.saved_filename.clone();
|
||||
self.clean()?;
|
||||
return Err(Error::msg(format!("{}", error)));
|
||||
},
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -4,19 +4,25 @@ use adw::gtk::{CheckButton, ComboBoxText, Entry, FileChooserNative, SpinButton};
|
||||
use adw::gtk::prelude::*;
|
||||
use anyhow::{anyhow, Error, Result};
|
||||
#[cfg(feature = "gtk")]
|
||||
use chrono::Utc;
|
||||
use chrono::{Datelike, Timelike, Utc};
|
||||
use ffmpeg_sidecar::child::FfmpegChild;
|
||||
use ffmpeg_sidecar::command::FfmpegCommand;
|
||||
use tempfile;
|
||||
use std::{cell::RefCell, time::Instant};
|
||||
use std::cell::RefCell;
|
||||
use std::path::Path;
|
||||
#[cfg(feature = "gtk")]
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
#[cfg(feature = "cmd")]
|
||||
use std::time::Instant;
|
||||
|
||||
use crate::utils::{is_input_audio_record, is_output_audio_record, is_valid, is_video_record, RecordMode};
|
||||
use crate::utils::{is_video_record, RecordMode};
|
||||
#[cfg(feature = "cmd")]
|
||||
use crate::utils::{is_input_audio_record, is_output_audio_record, is_valid};
|
||||
#[cfg(feature = "gtk")]
|
||||
use crate::utils::validate_video_file;
|
||||
|
||||
#[cfg(feature = "cmd")]
|
||||
#[derive(Clone)]
|
||||
@ -28,7 +34,6 @@ pub struct Ffmpeg {
|
||||
pub temp_input_audio_filename: String,
|
||||
pub temp_output_audio_filename: String,
|
||||
pub temp_video_filename: String,
|
||||
pub window_title: String,
|
||||
pub height: Option<u16>,
|
||||
pub input_audio_process: Option<Rc<RefCell<FfmpegChild>>>,
|
||||
pub output_audio_process: Option<Rc<RefCell<FfmpegChild>>>,
|
||||
@ -49,10 +54,7 @@ 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 window_title: String,
|
||||
pub saved_filename: String,
|
||||
pub height: Option<u16>,
|
||||
pub input_audio_process: Option<Rc<RefCell<FfmpegChild>>>,
|
||||
@ -73,11 +75,11 @@ pub struct Ffmpeg {
|
||||
#[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<()> {
|
||||
pub fn start_video(&mut self, x: u16, y: u16, width: u16, height: u16, mode: RecordMode, title: String) -> Result<()> {
|
||||
let display = match mode {
|
||||
RecordMode::Area => "desktop",
|
||||
RecordMode::Screen => "desktop",
|
||||
RecordMode::Window => &format!("title={}", &self.window_title),
|
||||
RecordMode::Window => &format!("title={}", title),
|
||||
};
|
||||
let mut ffmpeg_command = FfmpegCommand::new();
|
||||
let format = "gdigrab";
|
||||
@ -92,7 +94,7 @@ impl Ffmpeg {
|
||||
} else {
|
||||
&format!(".{}", &self.output)
|
||||
};
|
||||
let video_tempfile = tempfile::Builder::new().prefix("ffmpeg-video-")
|
||||
let video_tempfile = tempfile::Builder::new().prefix(".ffmpeg-video-")
|
||||
.suffix(suffix)
|
||||
.tempfile()?
|
||||
.keep()?;
|
||||
@ -188,7 +190,7 @@ impl Ffmpeg {
|
||||
|
||||
// Start audio input recording
|
||||
pub fn start_input_audio(&mut self) -> Result<()> {
|
||||
let input_audio_tempfile = tempfile::Builder::new().prefix("ffmpeg-audio-")
|
||||
let input_audio_tempfile = tempfile::Builder::new().prefix(".ffmpeg-audio-")
|
||||
.suffix(".ogg")
|
||||
.tempfile()?
|
||||
.keep()?;
|
||||
@ -235,7 +237,7 @@ impl Ffmpeg {
|
||||
|
||||
// Start audio output recording
|
||||
pub fn start_output_audio(&mut self) -> Result<()> {
|
||||
let output_audio_tempfile = tempfile::Builder::new().prefix("ffmpeg-audio-")
|
||||
let output_audio_tempfile = tempfile::Builder::new().prefix(".ffmpeg-audio-")
|
||||
.suffix(".ogg")
|
||||
.tempfile()?
|
||||
.keep()?;
|
||||
@ -440,6 +442,7 @@ impl Ffmpeg {
|
||||
impl Ffmpeg {
|
||||
// Get file name
|
||||
pub fn get_filename(&mut self) -> Result<()> {
|
||||
let utc_now = Utc::now;
|
||||
self.saved_filename =
|
||||
self.filename
|
||||
.0
|
||||
@ -450,7 +453,14 @@ impl Ffmpeg {
|
||||
.join(PathBuf::from(format!(
|
||||
"{}.{}",
|
||||
if self.filename.1.text().to_string().trim().eq("") {
|
||||
Utc::now().to_string().replace(" UTC", "").replace(' ', "-")
|
||||
format!("{}-{:02}-{:02}_{:02}-{:02}-{:02}",
|
||||
utc_now().year(),
|
||||
utc_now().month0(),
|
||||
utc_now().day0(),
|
||||
utc_now().hour(),
|
||||
utc_now().minute(),
|
||||
utc_now().second()
|
||||
)
|
||||
} else {
|
||||
self.filename.1.text().to_string().trim().to_string()
|
||||
},
|
||||
@ -463,11 +473,11 @@ impl Ffmpeg {
|
||||
}
|
||||
|
||||
// Start video recording
|
||||
pub fn start_video(&mut self, x: u16, y: u16, width: u16, height: u16, mode: RecordMode) -> Result<()> {
|
||||
pub fn start_video(&mut self, x: u16, y: u16, width: u16, height: u16, mode: RecordMode, title: String) -> Result<()> {
|
||||
let display = match mode {
|
||||
RecordMode::Area => "desktop",
|
||||
RecordMode::Screen => "desktop",
|
||||
RecordMode::Window => &format!("title={}", &self.window_title),
|
||||
RecordMode::Window => &format!("title={}", title),
|
||||
};
|
||||
let mut ffmpeg_command = FfmpegCommand::new();
|
||||
let format = "gdigrab";
|
||||
@ -476,24 +486,17 @@ impl Ffmpeg {
|
||||
.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"
|
||||
} else {
|
||||
&format!(".{}", &self.output)
|
||||
};
|
||||
let video_tempfile = tempfile::Builder::new().prefix("ffmpeg-video-")
|
||||
// Record video to tmp if output is GIF
|
||||
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();
|
||||
}
|
||||
|
||||
// Video format
|
||||
ffmpeg_command.format(format);
|
||||
|
||||
@ -525,6 +528,20 @@ impl Ffmpeg {
|
||||
// input
|
||||
ffmpeg_command.input(display);
|
||||
|
||||
// Record audio input
|
||||
if self.audio_input_switch.is_active() {
|
||||
ffmpeg_command.format("dshow");
|
||||
ffmpeg_command.input(format!("audio=\"{}\"", &self.audio_input_id.active_text()
|
||||
.ok_or_else(|| anyhow!("Failed to get audio input source."))?)
|
||||
);
|
||||
}
|
||||
|
||||
// Record audio output
|
||||
if self.audio_output_switch.is_active() {
|
||||
ffmpeg_command.format("dshow");
|
||||
ffmpeg_command.input(format!("audio=\"{}\"", &self.audio_output_id));
|
||||
}
|
||||
|
||||
// Disable bitrate if value is zero
|
||||
if self.video_record_bitrate.value() as u16 > 0 {
|
||||
ffmpeg_command.args([
|
||||
@ -533,13 +550,14 @@ impl Ffmpeg {
|
||||
]);
|
||||
}
|
||||
|
||||
// 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"]);
|
||||
// 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),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove metadate
|
||||
@ -549,10 +567,7 @@ impl Ffmpeg {
|
||||
let saved_filename = self.saved_filename.clone();
|
||||
ffmpeg_command.args([
|
||||
{
|
||||
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"
|
||||
if self.output == "gif"
|
||||
{
|
||||
&self.temp_video_filename
|
||||
} else {
|
||||
@ -575,29 +590,52 @@ impl Ffmpeg {
|
||||
pub fn stop_video(&mut self) -> Result<()> {
|
||||
// Quit the process to stop recording
|
||||
if self.video_process.is_some() {
|
||||
self.video_process
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow!("Not exiting the video recording process successfully."))?
|
||||
.borrow_mut()
|
||||
.quit()?;
|
||||
match self.video_process
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow!("Not exiting the video recording process successfully."))?
|
||||
.borrow_mut()
|
||||
.quit() {
|
||||
Ok(_) => {
|
||||
if self.output == "gif" {
|
||||
match self.merge() {
|
||||
Ok(_) => {
|
||||
self.clean()?;
|
||||
},
|
||||
Err(error) => {
|
||||
self.clean()?;
|
||||
return Err(Error::msg(format!("{}", error)));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(error) => {
|
||||
if self.output == "gif" {
|
||||
self.clean()?;
|
||||
} else {
|
||||
self.temp_video_filename = self.saved_filename.clone();
|
||||
self.clean()?;
|
||||
}
|
||||
return Err(Error::msg(format!("{}", error)));
|
||||
},
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// 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("dshow")
|
||||
.input(format!("audio=\"{}\"", &self.audio_input_id.active_text()
|
||||
.ok_or_else(|| anyhow!("Failed to get audio input source."))?)
|
||||
)
|
||||
.format("ogg");
|
||||
if self.audio_output_switch.is_active() {
|
||||
ffmpeg_command.format("dshow")
|
||||
.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([
|
||||
@ -605,13 +643,14 @@ 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);
|
||||
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));
|
||||
}
|
||||
|
||||
@ -624,34 +663,37 @@ impl Ffmpeg {
|
||||
pub fn stop_input_audio(&mut self) -> Result<()> {
|
||||
// Quit the process to stop recording
|
||||
if self.input_audio_process.is_some() {
|
||||
self.input_audio_process
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow!("Not exiting the input audio recording process successfully."))?
|
||||
.borrow_mut()
|
||||
.quit()?;
|
||||
match self.input_audio_process
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow!("Not exiting the input audio recording process successfully."))?
|
||||
.borrow_mut()
|
||||
.quit() {
|
||||
Ok(_) => {
|
||||
// Continue
|
||||
},
|
||||
Err(error) => {
|
||||
self.temp_video_filename = self.saved_filename.clone();
|
||||
self.clean()?;
|
||||
return Err(Error::msg(format!("{}", error)));
|
||||
},
|
||||
}
|
||||
}
|
||||
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("dshow")
|
||||
.input(format!("audio=\"{}\"", &self.audio_output_id))
|
||||
.format("ogg");
|
||||
// Remove metadate
|
||||
ffmpeg_command.args(["-map_metadata", "-1"]);
|
||||
ffmpeg_command.arg(&self.temp_output_audio_filename);
|
||||
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));
|
||||
}
|
||||
|
||||
@ -664,11 +706,20 @@ impl Ffmpeg {
|
||||
pub fn stop_output_audio(&mut self) -> Result<()> {
|
||||
// Quit the process to stop recording
|
||||
if self.output_audio_process.is_some() {
|
||||
self.output_audio_process
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow!("Not exiting the output audio recording process successfully."))?
|
||||
.borrow_mut()
|
||||
.quit()?;
|
||||
match self.output_audio_process
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow!("Not exiting the output audio recording process successfully."))?
|
||||
.borrow_mut()
|
||||
.quit() {
|
||||
Ok(_) => {
|
||||
// Continue
|
||||
},
|
||||
Err(error) => {
|
||||
self.temp_video_filename = self.saved_filename.clone();
|
||||
self.clean()?;
|
||||
return Err(Error::msg(format!("{}", error)));
|
||||
},
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -676,118 +727,39 @@ 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_valid(&self.temp_video_filename)? {
|
||||
break;
|
||||
} else if Instant::now().duration_since(start_time) >= duration {
|
||||
return Err(Error::msg("Unable to validate tmp video file."));
|
||||
// Validate video file integrity
|
||||
match validate_video_file(self.temp_video_filename.clone()) {
|
||||
Ok(_) => {
|
||||
// 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());
|
||||
match std::process::Command::new("sh").arg("-c").arg(&ffmpeg_convert).output() {
|
||||
Ok(_) => {
|
||||
// Do nothing
|
||||
},
|
||||
Err(error) => {
|
||||
return Err(Error::msg(format!("{}", error)));
|
||||
},
|
||||
}
|
||||
}
|
||||
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_valid(&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()?;
|
||||
},
|
||||
Err(error) => {
|
||||
return Err(Error::msg(format!("{}", error)));
|
||||
},
|
||||
}
|
||||
} 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);
|
||||
loop {
|
||||
if is_valid(&self.temp_input_audio_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_valid(&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()?;
|
||||
}
|
||||
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(())
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
pub mod wayland_linux;
|
||||
|
||||
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
|
||||
pub mod ffmpeg_linux;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub mod ffmpeg_windows;
|
||||
|
||||
pub mod utils;
|
||||
pub mod utils;
|
||||
|
||||
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
|
||||
pub mod wayland_linux;
|
@ -12,6 +12,7 @@ use blue_recorder_core::utils::{disable_input_widgets, enable_input_widgets,
|
||||
is_overwrite, is_wayland, play_record, RecordMode, validate_video_file};
|
||||
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
|
||||
use blue_recorder_core::utils::{audio_output_source, sources_descriptions_list};
|
||||
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
|
||||
use blue_recorder_core::wayland_linux::WaylandRecorder;
|
||||
#[cfg(target_os = "windows")]
|
||||
use cpal::traits::{DeviceTrait, HostTrait};
|
||||
@ -275,12 +276,15 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
|
||||
video_switch.connect_toggled(move |switch: &CheckButton| {
|
||||
config_management::set_bool("default", "videocheck", switch.is_active());
|
||||
if switch.is_active() {
|
||||
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
|
||||
_follow_mouse_switch.set_sensitive(true);
|
||||
_mouse_switch.set_sensitive(true);
|
||||
} else {
|
||||
_mouse_switch.set_active(false);
|
||||
_mouse_switch.set_sensitive(false);
|
||||
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
|
||||
_follow_mouse_switch.set_active(false);
|
||||
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
|
||||
_follow_mouse_switch.set_sensitive(false);
|
||||
}
|
||||
});
|
||||
@ -654,7 +658,7 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
|
||||
window_grab_button.set_tooltip_text(Some(&get_bundle("window-tooltip", None)));
|
||||
window_grab_label.set_label(&get_bundle("select-window", None));
|
||||
#[cfg(target_os = "windows")]
|
||||
let mut _window_title: Rc<RefCell<area_capture::Title>> = window_title.clone();
|
||||
let _window_title = window_title.clone();
|
||||
window_grab_button.connect_clicked(move |_| {
|
||||
let text_buffer = TextBuffer::new(None);
|
||||
config_management::set_bool("default", "areacheck", _area_switch.is_active());
|
||||
@ -705,6 +709,7 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
|
||||
// Disable mouse cursor capture if video record is not active
|
||||
if !video_switch.is_active() {
|
||||
mouse_switch.set_sensitive(false);
|
||||
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
|
||||
follow_mouse_switch.set_sensitive(false);
|
||||
}
|
||||
|
||||
@ -740,7 +745,6 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
|
||||
|
||||
// Disable show area check button
|
||||
if !area_grab_button.is_active() {
|
||||
input_widgets.push(area_switch.clone().into());
|
||||
area_switch.set_active(false);
|
||||
area_switch.set_sensitive(false);
|
||||
}
|
||||
@ -751,15 +755,6 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let mode = if area_grab_button.is_active() {
|
||||
RecordMode::Area
|
||||
} else if window_grab_button.is_active() {
|
||||
RecordMode::Window
|
||||
} else {
|
||||
RecordMode::Screen
|
||||
};
|
||||
#[cfg(target_os = "windows")]
|
||||
let window_title = window_title.borrow_mut().title.clone();
|
||||
|
||||
// Init record struct
|
||||
#[cfg(target_os = "windows")]
|
||||
@ -772,10 +767,7 @@ 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(),
|
||||
window_title,
|
||||
saved_filename: String::new(),
|
||||
height: None,
|
||||
input_audio_process: None,
|
||||
@ -789,7 +781,7 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
|
||||
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.clone(),
|
||||
video_switch: video_switch.clone()
|
||||
}));
|
||||
|
||||
@ -818,7 +810,7 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
|
||||
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.clone(),
|
||||
video_switch: video_switch.clone(),
|
||||
wayland_recorder: glib::MainContext::default().block_on(WaylandRecorder::new())
|
||||
}));
|
||||
@ -828,6 +820,8 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
|
||||
delay_window_title.set_label(&get_bundle("delay-title", None));
|
||||
record_button.set_tooltip_text(Some(&get_bundle("record-tooltip", None)));
|
||||
record_label.set_label(&get_bundle("record", None));
|
||||
let _area_grab_button = area_grab_button.clone();
|
||||
let _area_switch = area_switch.clone();
|
||||
let _audio_input_switch = audio_input_switch.clone();
|
||||
let _audio_output_switch = audio_output_switch.clone();
|
||||
let _delay_spin = delay_spin.clone();
|
||||
@ -849,15 +843,31 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
|
||||
is_record_button_clicked: false,
|
||||
}));
|
||||
record_button.connect_clicked(move |_| {
|
||||
let mode: RecordMode = if _area_grab_button.is_active() {
|
||||
RecordMode::Area
|
||||
} else if window_grab_button.is_active() {
|
||||
RecordMode::Window
|
||||
} else {
|
||||
RecordMode::Screen
|
||||
};
|
||||
// Disable show area check button
|
||||
if _area_grab_button.is_active() {
|
||||
_area_switch.set_sensitive(false);
|
||||
}
|
||||
// Disable mouse cursor capture during record
|
||||
if _video_switch.is_active() {
|
||||
_mouse_switch.set_sensitive(false);
|
||||
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
|
||||
_follow_mouse_switch.set_sensitive(false);
|
||||
}
|
||||
match _ffmpeg_record_interface.borrow_mut().get_filename() {
|
||||
Err(error) => {
|
||||
if _area_grab_button.is_active() {
|
||||
_area_switch.set_sensitive(true);
|
||||
}
|
||||
if _video_switch.is_active() {
|
||||
_mouse_switch.set_sensitive(true);
|
||||
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
|
||||
_follow_mouse_switch.set_sensitive(true);
|
||||
}
|
||||
enable_input_widgets(_input_widgets.clone());
|
||||
@ -904,6 +914,8 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
|
||||
}
|
||||
} else if _delay_spin.value() as u16 == 0 {
|
||||
let _area_capture = area_capture.borrow_mut();
|
||||
#[cfg(target_os = "windows")]
|
||||
let _window_title = window_title.borrow_mut();
|
||||
disable_input_widgets(_input_widgets.clone());
|
||||
start_timer(record_time_label.clone());
|
||||
record_time_label.set_visible(true);
|
||||
@ -919,8 +931,12 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
|
||||
// Do nothing
|
||||
},
|
||||
Err(error) => {
|
||||
if _area_grab_button.is_active() {
|
||||
_area_switch.set_sensitive(true);
|
||||
}
|
||||
if _video_switch.is_active() {
|
||||
_mouse_switch.set_sensitive(true);
|
||||
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
|
||||
_follow_mouse_switch.set_sensitive(true);
|
||||
}
|
||||
enable_input_widgets(_input_widgets.clone());
|
||||
@ -939,8 +955,12 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
|
||||
// Do nothing
|
||||
},
|
||||
Err(error) => {
|
||||
if _area_grab_button.is_active() {
|
||||
_area_switch.set_sensitive(true);
|
||||
}
|
||||
if _video_switch.is_active() {
|
||||
_mouse_switch.set_sensitive(true);
|
||||
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
|
||||
_follow_mouse_switch.set_sensitive(true);
|
||||
}
|
||||
enable_input_widgets(_input_widgets.clone());
|
||||
@ -954,19 +974,34 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
|
||||
}
|
||||
}
|
||||
if _video_switch.is_active() {
|
||||
match _ffmpeg_record_interface.borrow_mut().start_video(
|
||||
#[cfg(target_os = "windows")]
|
||||
let start_video = _ffmpeg_record_interface.borrow_mut().start_video(
|
||||
_area_capture.x,
|
||||
_area_capture.y,
|
||||
_area_capture.width,
|
||||
_area_capture.height,
|
||||
mode,
|
||||
_window_title.title.clone(),
|
||||
);
|
||||
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
|
||||
let start_video = _ffmpeg_record_interface.borrow_mut().start_video(
|
||||
_area_capture.x,
|
||||
_area_capture.y,
|
||||
_area_capture.width,
|
||||
_area_capture.height,
|
||||
mode
|
||||
) {
|
||||
);
|
||||
match start_video {
|
||||
Ok(_) => {
|
||||
// Do nothing
|
||||
},
|
||||
Err(error) => {
|
||||
if _area_grab_button.is_active() {
|
||||
_area_switch.set_sensitive(true);
|
||||
}
|
||||
if _video_switch.is_active() {
|
||||
_mouse_switch.set_sensitive(true);
|
||||
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
|
||||
_follow_mouse_switch.set_sensitive(true);
|
||||
}
|
||||
enable_input_widgets(_input_widgets.clone());
|
||||
@ -1010,8 +1045,12 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
|
||||
// Continue
|
||||
},
|
||||
Err(error) => {
|
||||
if area_grab_button.is_active() {
|
||||
area_switch.set_sensitive(true);
|
||||
}
|
||||
if _video_switch.is_active() {
|
||||
_mouse_switch.set_sensitive(true);
|
||||
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
|
||||
_follow_mouse_switch.set_sensitive(true);
|
||||
}
|
||||
enable_input_widgets(input_widgets.clone());
|
||||
@ -1031,8 +1070,12 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
|
||||
// Continue
|
||||
},
|
||||
Err(error) => {
|
||||
if area_grab_button.is_active() {
|
||||
area_switch.set_sensitive(true);
|
||||
}
|
||||
if _video_switch.is_active() {
|
||||
_mouse_switch.set_sensitive(true);
|
||||
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
|
||||
_follow_mouse_switch.set_sensitive(true);
|
||||
}
|
||||
enable_input_widgets(input_widgets.clone());
|
||||
@ -1052,8 +1095,12 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
|
||||
// Continue
|
||||
},
|
||||
Err(error) => {
|
||||
if area_grab_button.is_active() {
|
||||
area_switch.set_sensitive(true);
|
||||
}
|
||||
if _video_switch.is_active() {
|
||||
_mouse_switch.set_sensitive(true);
|
||||
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
|
||||
_follow_mouse_switch.set_sensitive(true);
|
||||
}
|
||||
enable_input_widgets(input_widgets.clone());
|
||||
@ -1067,8 +1114,12 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
|
||||
},
|
||||
}
|
||||
}
|
||||
if area_grab_button.is_active() {
|
||||
area_switch.set_sensitive(true);
|
||||
}
|
||||
if _video_switch.is_active() {
|
||||
_mouse_switch.set_sensitive(true);
|
||||
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
|
||||
_follow_mouse_switch.set_sensitive(true);
|
||||
}
|
||||
enable_input_widgets(input_widgets.clone());
|
||||
|
Loading…
Reference in New Issue
Block a user