mirror of
https://github.com/xlmnxp/blue-recorder.git
synced 2025-03-31 23:04:55 +03:00
update core
This commit is contained in:
parent
693fd6a7cf
commit
999ac573bc
@ -29,6 +29,7 @@ pub struct Ffmpeg {
|
||||
pub video_record_bitrate: u16,
|
||||
pub follow_mouse: bool,
|
||||
pub record_mouse: bool,
|
||||
pub show_area: bool,
|
||||
}
|
||||
|
||||
impl Ffmpeg {
|
||||
@ -78,6 +79,11 @@ impl Ffmpeg {
|
||||
ffmpeg_command.size(width.into(), height.into());
|
||||
}
|
||||
|
||||
// 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"]);
|
||||
@ -148,7 +154,7 @@ impl Ffmpeg {
|
||||
if self.video_process.is_some() {
|
||||
self.video_process
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow!("not exiting the video recording process successfully"))?
|
||||
.ok_or_else(|| anyhow!("Not exiting the video recording process successfully"))?
|
||||
.borrow_mut()
|
||||
.quit()?;
|
||||
}
|
||||
@ -195,7 +201,7 @@ impl Ffmpeg {
|
||||
if self.input_audio_process.is_some() {
|
||||
self.input_audio_process
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow!("not exiting the input audio recording process successfully"))?
|
||||
.ok_or_else(|| anyhow!("Not exiting the input audio recording process successfully"))?
|
||||
.borrow_mut()
|
||||
.quit()?;
|
||||
}
|
||||
@ -235,7 +241,7 @@ impl Ffmpeg {
|
||||
if self.output_audio_process.is_some() {
|
||||
self.output_audio_process
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow!("not exiting the output audio recording process successfully"))?
|
||||
.ok_or_else(|| anyhow!("Not exiting the output audio recording process successfully"))?
|
||||
.borrow_mut()
|
||||
.quit()?;
|
||||
}
|
||||
@ -287,7 +293,7 @@ impl Ffmpeg {
|
||||
// Convert MP4 to GIF
|
||||
let filter = format!("fps={},scale={}:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse",
|
||||
self.record_frames,self.height.ok_or_else
|
||||
(|| anyhow!("unable to get height value"))?);
|
||||
(|| anyhow!("Unable to get height value"))?);
|
||||
let ffmpeg_convert = format!("ffmpeg -i file:{} -filter_complex '{}' \
|
||||
-loop 0 {} -y", &self.temp_video_filename,filter,&self.filename);
|
||||
std::process::Command::new("sh").arg("-c").arg(&ffmpeg_convert).output()?;
|
||||
@ -350,5 +356,4 @@ impl Ffmpeg {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,29 +1,36 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use anyhow::{anyhow, Error, Result};
|
||||
use ffmpeg_sidecar::child::FfmpegChild;
|
||||
use ffmpeg_sidecar::command::FfmpegCommand;
|
||||
use tempfile;
|
||||
use std::cell::RefCell;
|
||||
use std::{cell::RefCell, time::Instant};
|
||||
use std::path::Path;
|
||||
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};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Ffmpeg {
|
||||
pub audio_input_id: String,
|
||||
pub audio_output_id: String,
|
||||
pub filename: String,
|
||||
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 command: Option<String>,
|
||||
pub audio_process: Option<Rc<RefCell<FfmpegChild>>>,
|
||||
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 audio_record_bitrate: u16,
|
||||
pub record_delay: u16,
|
||||
pub record_frames: u16,
|
||||
pub video_record_bitrate: u16,
|
||||
pub record_audio: bool,
|
||||
pub follow_mouse: bool,
|
||||
pub record_mouse: bool,
|
||||
pub record_video: bool,
|
||||
pub show_area: bool,
|
||||
}
|
||||
|
||||
impl Ffmpeg {
|
||||
@ -36,23 +43,32 @@ impl Ffmpeg {
|
||||
};
|
||||
let mut ffmpeg_command = FfmpegCommand::new();
|
||||
let format = "gdigrab";
|
||||
if self.output == "gif" {
|
||||
self.output = String::from("mp4");
|
||||
}
|
||||
|
||||
// Record video to tmp if audio record enabled
|
||||
if self.record_audio {
|
||||
let video_tempfile = tempfile::Builder::new().suffix(&format!(".{}", &self.output))
|
||||
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).file_name()
|
||||
.ok_or_else(|| anyhow!("cannot get video temporary file name"))?
|
||||
.to_string_lossy()
|
||||
self.temp_video_filename = Path::new(&video_tempfile.1).to_string_lossy()
|
||||
.to_string();
|
||||
}
|
||||
// Video format
|
||||
ffmpeg_command.format(format);
|
||||
|
||||
// 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"]);
|
||||
@ -66,10 +82,12 @@ impl Ffmpeg {
|
||||
}
|
||||
|
||||
// Record video with specified width and hight
|
||||
ffmpeg_command.size(width.into(), height.into()).args([
|
||||
"-offset_x", &x.to_string(),
|
||||
"-offset_y", &y.to_string()
|
||||
]);
|
||||
if let RecordMode::Area = mode {
|
||||
ffmpeg_command.size(width.into(), height.into()).args([
|
||||
"-offset_x", &x.to_string(),
|
||||
"-offset_y", &y.to_string()
|
||||
]);
|
||||
}
|
||||
|
||||
// input
|
||||
ffmpeg_command.input(display);
|
||||
@ -83,14 +101,23 @@ impl Ffmpeg {
|
||||
}
|
||||
|
||||
// tmp file
|
||||
if !self.record_audio {
|
||||
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.record_audio {
|
||||
if !self.audio_input_id.is_empty()
|
||||
|| !self.audio_output_id.is_empty()
|
||||
|| self.output == "gif"
|
||||
{
|
||||
&self.temp_video_filename
|
||||
} else {
|
||||
&self.filename
|
||||
@ -108,15 +135,212 @@ impl Ffmpeg {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Stop video recording
|
||||
pub fn stop_video(&mut self) -> Result<()> {
|
||||
// kill the process to stop recording
|
||||
// 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"))?
|
||||
.ok_or_else(|| anyhow!("Not exiting the video recording process successfully"))?
|
||||
.borrow_mut()
|
||||
.quit()?;
|
||||
}
|
||||
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))
|
||||
.format("ogg");
|
||||
// Disable bitrate if value is zero
|
||||
if self.audio_record_bitrate > 0 {
|
||||
ffmpeg_command.args([
|
||||
"-b:a",
|
||||
&format!("{}K", self.audio_record_bitrate),
|
||||
]);
|
||||
}
|
||||
// Remove metadate
|
||||
ffmpeg_command.args(["-map_metadata", "-1"]);
|
||||
ffmpeg_command.arg(&self.temp_input_audio_filename);
|
||||
ffmpeg_command.overwrite();
|
||||
|
||||
// Sleep for delay
|
||||
if !is_video_record(&self.temp_video_filename) {
|
||||
sleep(Duration::from_secs(self.record_delay as u64));
|
||||
}
|
||||
|
||||
// Start recording and return the process id
|
||||
self.input_audio_process = Some(Rc::new(RefCell::new(ffmpeg_command.spawn()?)));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Stop audio input recording
|
||||
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()?;
|
||||
}
|
||||
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_input_id))
|
||||
.format("ogg");
|
||||
// Remove metadate
|
||||
ffmpeg_command.args(["-map_metadata", "-1"]);
|
||||
ffmpeg_command.arg(&self.temp_output_audio_filename);
|
||||
ffmpeg_command.overwrite();
|
||||
|
||||
// Sleep for delay
|
||||
if !is_video_record(&self.temp_video_filename) && !is_input_audio_record(&self.temp_input_audio_filename) {
|
||||
sleep(Duration::from_secs(self.record_delay as u64));
|
||||
}
|
||||
|
||||
// Start recording and return the process id
|
||||
self.output_audio_process = Some(Rc::new(RefCell::new(ffmpeg_command.spawn()?)));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Stop audio output recording
|
||||
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()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// 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"));
|
||||
}
|
||||
}
|
||||
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()?;
|
||||
} 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,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.filename);
|
||||
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
|
||||
let start_time = Instant::now();
|
||||
let duration = Duration::from_secs(60);
|
||||
loop {
|
||||
if is_valide(&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.filename,
|
||||
]).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.filename)
|
||||
.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)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
#[cfg(target_os = "linux")]
|
||||
pub mod ffmpeg_linux;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub mod ffmpeg_windows;
|
||||
|
||||
pub mod utils;
|
||||
|
Loading…
Reference in New Issue
Block a user