diff --git a/.gitignore b/.gitignore index ea8c4bf..2b05abf 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,6 @@ /target +*.mp4 +scratch/ +*.orig +*.obOCNTS +*.py diff --git a/record_1788315688.mp4 b/record_1788315688.mp4 deleted file mode 100644 index 53f4651..0000000 Binary files a/record_1788315688.mp4 and /dev/null differ diff --git a/record_1788315823.mp4 b/record_1788315823.mp4 deleted file mode 100644 index 17e5e5f..0000000 Binary files a/record_1788315823.mp4 and /dev/null differ diff --git a/record_1788315881.mp4 b/record_1788315881.mp4 deleted file mode 100644 index 32fa41a..0000000 Binary files a/record_1788315881.mp4 and /dev/null differ diff --git a/replay_1788315659.mp4 b/replay_1788315659.mp4 deleted file mode 100644 index 1f241f3..0000000 Binary files a/replay_1788315659.mp4 and /dev/null differ diff --git a/scratch/audio_patch.rs b/scratch/audio_patch.rs deleted file mode 100644 index 98d63d5..0000000 --- a/scratch/audio_patch.rs +++ /dev/null @@ -1,16 +0,0 @@ -use std::fs; - -fn main() { - let mut code = fs::read_to_string("src/capture/audio.rs").unwrap(); - code = code.replace( - "pub fn new(sender: Sender>) -> Result> {", - "pub fn new(sender: Sender>) -> Result<(Self, u32, u16), Box> {" - ); - - code = code.replace( - "Ok(Self { _stream: stream })", - "Ok((Self { _stream: stream }, sample_rate, channels))" - ); - - fs::write("src/capture/audio.rs", code).unwrap(); -} diff --git a/scratch/daemon_patch.rs b/scratch/daemon_patch.rs deleted file mode 100644 index 0e966ef..0000000 --- a/scratch/daemon_patch.rs +++ /dev/null @@ -1,72 +0,0 @@ -use std::fs; - -fn main() { - let mut code = fs::read_to_string("src/bin/vrec-daemon.rs").unwrap(); - - code = code.replace( - "let (mux_tx, mux_rx) = bounded::>(1);", - "let (mux_tx, mux_rx) = bounded::>(1);\n let (audio_tx, audio_rx) = bounded::>(100);\n let audio_info = vrec::capture::audio::AudioCapture::new(audio_tx).ok();" - ); - - code = code.replace( - "let codec_ctx_ptr = encoder.codec_ctx() as usize;", - r#"let codec_ctx_ptr = encoder.codec_ctx() as usize; - - let mut audio_encoder = if let Some(ref info) = audio_info { - vrec::encoder::AudioEncoder::new(info.1 as i32, info.2 as i32).ok() - } else { None }; - let audio_codec_ctx_ptr = audio_encoder.as_ref().map(|e| e.codec_ctx() as usize);"# - ); - - code = code.replace( - "let mut ring = HeapRb::::new((config.replay_duration_sec * 60).max(60) as usize);", - "let mut ring = HeapRb::::new((config.replay_duration_sec * 120).max(120) as usize);" - ); - - code = code.replace( - "let new_capacity = (config.replay_duration_sec * 60).max(60) as usize;", - "let new_capacity = (config.replay_duration_sec * 120).max(120) as usize;" - ); - - code = code.replace( - "let mut muxer = unsafe { Muxer::new(&name, codec_ctx, audio_codec_ctx).unwrap() };", - "let audio_codec_ctx = audio_codec_ctx_ptr.map(|p| p as *mut ffmpeg_next::ffi::AVCodecContext);\n let mut muxer = unsafe { Muxer::new(&name, codec_ctx, audio_codec_ctx).unwrap() };" - ); - - code = code.replace( - "normal_muxer = unsafe { Muxer::new(&name, codec_ctx, audio_codec_ctx).ok() };", - "let audio_codec_ctx = audio_codec_ctx_ptr.map(|p| p as *mut ffmpeg_next::ffi::AVCodecContext);\n normal_muxer = unsafe { Muxer::new(&name, codec_ctx, audio_codec_ctx).ok() };" - ); - - // Add the audio pumping logic inside the while let Ok(frame) loop - code = code.replace( - "if let Ok(packets) = encoder.encode_frame(&frame) {", - r#"while let Ok(audio_chunk) = audio_rx.try_recv() { - if let Some(enc) = audio_encoder.as_mut() { - if let Ok(audio_packets) = enc.encode_pcm(&audio_chunk) { - for mut pkt in audio_packets { - pkt.set_stream_index(1); - if config.replay_enabled { - ring.push_overwrite(pkt.clone()); - } - if normal_recording && !normal_waiting_keyframe { - if let Some(muxer) = normal_muxer.as_mut() { - let _ = muxer.write_packet(&pkt); - } - } - } - } - } - } - - if let Ok(packets) = encoder.encode_frame(&frame) {"# - ); - - // For video packets, set stream index to 0 - code = code.replace( - "for pkt in packets {", - "for mut pkt in packets {\n pkt.set_stream_index(0);" - ); - - fs::write("src/bin/vrec-daemon.rs", code).unwrap(); -} diff --git a/scratch/encoder_fix.patch b/scratch/encoder_fix.patch deleted file mode 100644 index dc82984..0000000 --- a/scratch/encoder_fix.patch +++ /dev/null @@ -1,20 +0,0 @@ ---- src/capture/audio.rs -+++ src/capture/audio.rs -@@ -12,7 +12,7 @@ - let device = host.default_input_device().ok_or("No audio input device available")?; - - let config = device.default_input_config()?; -- let sample_rate = config.sample_rate().0; -+ let sample_rate = config.sample_rate().0; // Let's just fix the into() first - let channels = config.channels(); - - println!("Audio capture initialized: {} Hz, {} channels", sample_rate, channels); -@@ -22,7 +22,7 @@ - let stream = match config.sample_format() { - cpal::SampleFormat::F32 => { - device.build_input_stream( -- &config.into(), -+ &config.clone().into(), - move |data: &[f32], _: &_| { - let _ = sender.try_send(data.to_vec()); - }, diff --git a/scratch/fix.patch b/scratch/fix.patch deleted file mode 100644 index cfbbd8a..0000000 --- a/scratch/fix.patch +++ /dev/null @@ -1,29 +0,0 @@ ---- src/capture/audio.rs -+++ src/capture/audio.rs -@@ -11,7 +11,7 @@ - let device = host.default_input_device().ok_or("No audio input device available")?; - - let config = device.default_input_config()?; -- let sample_rate = config.sample_rate().0; -+ let sample_rate = config.sample_rate().0; // will fix this with sed - let channels = config.channels(); - - println!("Audio capture initialized: {} Hz, {} channels", sample_rate, channels); -@@ -21,7 +21,7 @@ - let stream = match config.sample_format() { - cpal::SampleFormat::F32 => { - device.build_input_stream( -- &stream_config, -+ stream_config.clone(), - move |data: &[f32], _: &_| { - let _ = sender.try_send(data.to_vec()); - }, -@@ -31,7 +31,7 @@ - } - cpal::SampleFormat::I16 => { - device.build_input_stream( -- &stream_config, -+ stream_config, - move |data: &[i16], _: &_| { - let f32_data: Vec = data.iter().map(|&s| s as f32 / i16::MAX as f32).collect(); - let _ = sender.try_send(f32_data); diff --git a/scratch/fix_audio.patch b/scratch/fix_audio.patch deleted file mode 100644 index 01eb7ab..0000000 --- a/scratch/fix_audio.patch +++ /dev/null @@ -1,25 +0,0 @@ ---- src/capture/audio.rs -+++ src/capture/audio.rs -@@ -11,7 +11,7 @@ - let device = host.default_input_device().ok_or("No audio input device available")?; - - let config = device.default_input_config()?; -- let sample_rate = config.sample_rate().0; // Let's just use .0 for now but wait, the compiler said u32! -+ let sample_rate = config.sample_rate().0; - let channels = config.channels(); - - println!("Audio capture initialized: {} Hz, {} channels", sample_rate, channels); -@@ -19,10 +19,10 @@ - let err_fn = |err| eprintln!("Audio stream error: {}", err); - let stream_config: cpal::StreamConfig = config.clone().into(); - -- let stream = match stream_config.sample_rate.0 { -- _ => { -+ let stream = match config.sample_format() { -+ cpal::SampleFormat::F32 => { - device.build_input_stream( -- &stream_config, -+ &stream_config, - move |data: &[f32], _: &_| { - let _ = sender.try_send(data.to_vec()); - }, diff --git a/scratch/gtk_fix.patch b/scratch/gtk_fix.patch deleted file mode 100644 index 04db932..0000000 --- a/scratch/gtk_fix.patch +++ /dev/null @@ -1,19 +0,0 @@ ---- src/overlay.rs -+++ src/overlay.rs -@@ -16,11 +16,11 @@ - .default_height(80) - .build(); - -- window.init_layer_shell(); -- window.set_layer(Layer::Overlay); -- window.set_namespace("vrec-overlay"); -- window.set_margin(gtk_layer_shell::Edge::Top, 40); -- window.set_anchor(gtk_layer_shell::Edge::Top, true); -+ gtk_layer_shell::init_for_window(&window); -+ gtk_layer_shell::set_layer(&window, Layer::Overlay); -+ gtk_layer_shell::set_namespace(&window, "vrec-overlay"); -+ gtk_layer_shell::set_margin(&window, gtk_layer_shell::Edge::Top, 40); -+ gtk_layer_shell::set_anchor(&window, gtk_layer_shell::Edge::Top, true); - - // Make it transparent and borderless - let css_provider = CssProvider::new(); diff --git a/scratch/gtk_fix2.patch b/scratch/gtk_fix2.patch deleted file mode 100644 index 9acf5c0..0000000 --- a/scratch/gtk_fix2.patch +++ /dev/null @@ -1,19 +0,0 @@ ---- src/overlay.rs -+++ src/overlay.rs -@@ -16,11 +16,11 @@ - .default_height(80) - .build(); - -- gtk_layer_shell::init_for_window(&window); -- gtk_layer_shell::set_layer(&window, Layer::Overlay); -- gtk_layer_shell::set_namespace(&window, "vrec-overlay"); -- gtk_layer_shell::set_margin(&window, gtk_layer_shell::Edge::Top, 40); -- gtk_layer_shell::set_anchor(&window, gtk_layer_shell::Edge::Top, true); -+ window.init_layer_shell(); -+ window.set_layer(Layer::Overlay); -+ window.set_layer_shell_namespace("vrec-overlay"); -+ window.set_layer_shell_margin(gtk_layer_shell::Edge::Top, 40); -+ window.set_anchor(gtk_layer_shell::Edge::Top, true); - - // Make it transparent and borderless - let css_provider = CssProvider::new(); diff --git a/scratch/ipc_fix.patch b/scratch/ipc_fix.patch deleted file mode 100644 index c9e9fb7..0000000 --- a/scratch/ipc_fix.patch +++ /dev/null @@ -1,28 +0,0 @@ ---- src/config.rs -+++ src/config.rs -@@ -1,6 +1,9 @@ - use serde::{Deserialize, Serialize}; - use std::fs; - use std::path::PathBuf; -+use std::env; -+use std::os::unix::net::UnixStream; -+use std::io::Write; - - #[derive(Debug, Serialize, Deserialize, Clone)] - pub struct VrecConfig { -@@ -51,4 +54,14 @@ - fs::write(path, data)?; - Ok(()) - } -+ -+ pub fn notify_daemon_reload() { -+ let socket_path = format!("{}/vrec.sock", env::var("XDG_RUNTIME_DIR").unwrap_or_else(|_| "/tmp".to_string())); -+ if let Ok(mut stream) = UnixStream::connect(&socket_path) { -+ if let Ok(payload) = serde_json::to_vec(&crate::ipc::Command::ReloadConfig) { -+ let len_buf = (payload.len() as u32).to_le_bytes(); -+ let _ = stream.write_all(&len_buf); -+ let _ = stream.write_all(&payload); -+ } -+ } -+ } - } diff --git a/scratch/muxer_fix.patch b/scratch/muxer_fix.patch deleted file mode 100644 index 83d06bc..0000000 --- a/scratch/muxer_fix.patch +++ /dev/null @@ -1,60 +0,0 @@ ---- src/bin/vrec-daemon.rs -+++ src/bin/vrec-daemon.rs -@@ -62,6 +62,8 @@ - let mut config = vrec::config::VrecConfig::load(); - let mut ring = HeapRb::::new((config.replay_duration_sec * 60).max(60) as usize); - let mut normal_muxer: Option = None; -+ let mut normal_recording = false; -+ let mut normal_waiting_keyframe = false; - - thread::spawn(move || { - while let Ok(drain) = mux_rx.recv() { -@@ -108,16 +110,14 @@ - } - }, - Command::StartRecording => { -- if config.record_enabled && normal_muxer.is_none() { -- let codec_ctx = codec_ctx_ptr as *mut ffmpeg_next::ffi::AVCodecContext; -- let ts = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs(); -- let name = format!("record_{}.mp4", ts); -- normal_muxer = unsafe { Muxer::new(&name, codec_ctx).ok() }; -- println!("Started normal recording to {}", name); -+ if config.record_enabled && !normal_recording { -+ normal_recording = true; -+ normal_waiting_keyframe = true; -+ println!("StartRecording requested, waiting for keyframe..."); - } - }, - Command::StopRecording => { -- if let Some(mut m) = normal_muxer.take() { -+ if let Some(mut m) = normal_muxer.take() { - let _ = m.finalize(); - println!("Stopped normal recording."); - } -+ normal_recording = false; - }, - _ => {} - } -@@ -129,7 +129,19 @@ - ring.push_overwrite(pkt.clone()); - } -- if let Some(muxer) = normal_muxer.as_mut() { -- let _ = muxer.write_packet(&pkt); -+ if normal_recording { -+ if normal_waiting_keyframe { -+ if pkt.is_keyframe() { -+ normal_waiting_keyframe = false; -+ let codec_ctx = codec_ctx_ptr as *mut ffmpeg_next::ffi::AVCodecContext; -+ let ts = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs(); -+ let name = format!("record_{}.mp4", ts); -+ normal_muxer = unsafe { Muxer::new(&name, codec_ctx).ok() }; -+ println!("Started normal recording to {}", name); -+ } -+ } -+ if !normal_waiting_keyframe { -+ if let Some(muxer) = normal_muxer.as_mut() { -+ let _ = muxer.write_packet(&pkt); -+ } -+ } - } - } diff --git a/scratch/muxer_patch.rs b/scratch/muxer_patch.rs deleted file mode 100644 index 40d2381..0000000 --- a/scratch/muxer_patch.rs +++ /dev/null @@ -1,33 +0,0 @@ -use std::fs; - -fn main() { - let mut code = fs::read_to_string("src/muxer.rs").unwrap(); - code = code.replace( - "pub unsafe fn new(path: &str, codec_ctx: *mut AVCodecContext) -> Result {", - "pub unsafe fn new(path: &str, codec_ctx: *mut AVCodecContext, audio_codec_ctx: Option<*mut AVCodecContext>) -> Result {" - ); - - code = code.replace( - r#" let ret = avcodec_parameters_from_context((*stream).codecpar, codec_ctx); - if ret < 0 { - return Err("Failed to copy codec parameters".into()); - }"#, - r#" (*stream).id = 0; - let ret = avcodec_parameters_from_context((*stream).codecpar, codec_ctx); - if ret < 0 { - return Err("Failed to copy video codec parameters".into()); - } - - if let Some(actx) = audio_codec_ctx { - let astream = avformat_new_stream(fmt_ctx, std::ptr::null()); - if !astream.is_null() { - (*astream).id = 1; - if avcodec_parameters_from_context((*astream).codecpar, actx) < 0 { - eprintln!("Failed to copy audio codec parameters"); - } - } - }"# - ); - - fs::write("src/muxer.rs", code).unwrap(); -} diff --git a/scratch/patch_audio_timebase.rs b/scratch/patch_audio_timebase.rs deleted file mode 100644 index b6c02a4..0000000 --- a/scratch/patch_audio_timebase.rs +++ /dev/null @@ -1,9 +0,0 @@ -use std::fs; -fn main() { - let mut code = fs::read_to_string("src/encoder.rs").unwrap(); - code = code.replace( - "(*codec_ctx).sample_rate = sample_rate;", - "(*codec_ctx).sample_rate = sample_rate;\n (*codec_ctx).time_base = AVRational { num: 1, den: sample_rate };" - ); - fs::write("src/encoder.rs", code).unwrap(); -} diff --git a/scratch/patch_enc.rs b/scratch/patch_enc.rs deleted file mode 100644 index fa9b311..0000000 --- a/scratch/patch_enc.rs +++ /dev/null @@ -1,43 +0,0 @@ -use std::fs; - -fn main() { - let mut code = fs::read_to_string("src/encoder.rs").unwrap(); - code = code.replace( - "(*codec_ctx).channel_layout = if channels == 2 { AV_CH_LAYOUT_STEREO } else { AV_CH_LAYOUT_MONO } as u64;", - "av_channel_layout_default(&mut (*codec_ctx).ch_layout, channels);" - ); - - // For SwrContext, swr_alloc_set_opts is deprecated in favor of swr_alloc_set_opts2 - // But swr_alloc_set_opts2 requires passing pointer to AVChannelLayout. - code = code.replace( - r#" let mut swr_ctx = swr_alloc(); - av_opt_set_int(swr_ctx as _, c"in_channel_layout".as_ptr(), (*codec_ctx).channel_layout as i64, 0); - av_opt_set_int(swr_ctx as _, c"in_sample_rate".as_ptr(), sample_rate as i64, 0); - av_opt_set_sample_fmt(swr_ctx as _, c"in_sample_fmt".as_ptr(), AVSampleFormat::AV_SAMPLE_FMT_FLT, 0); - - av_opt_set_int(swr_ctx as _, c"out_channel_layout".as_ptr(), (*codec_ctx).channel_layout as i64, 0); - av_opt_set_int(swr_ctx as _, c"out_sample_rate".as_ptr(), sample_rate as i64, 0); - av_opt_set_sample_fmt(swr_ctx as _, c"out_sample_fmt".as_ptr(), AVSampleFormat::AV_SAMPLE_FMT_FLTP, 0); - - swr_init(swr_ctx);"#, - r#" let mut swr_ctx: *mut SwrContext = std::ptr::null_mut(); - swr_alloc_set_opts2( - &mut swr_ctx, - &(*codec_ctx).ch_layout, - AVSampleFormat::AV_SAMPLE_FMT_FLTP, - sample_rate, - &(*codec_ctx).ch_layout, - AVSampleFormat::AV_SAMPLE_FMT_FLT, - sample_rate, - 0, - std::ptr::null_mut() - ); - swr_init(swr_ctx);"# - ); - - code = code.replace("(*in_frame).channel_layout = (*self.codec_ctx).channel_layout;", "(*in_frame).ch_layout = (*self.codec_ctx).ch_layout;"); - code = code.replace("(*out_frame).channel_layout = (*self.codec_ctx).channel_layout;", "(*out_frame).ch_layout = (*self.codec_ctx).ch_layout;"); - code = code.replace("(*enc_frame).channel_layout = (*self.codec_ctx).channel_layout;", "(*enc_frame).ch_layout = (*self.codec_ctx).ch_layout;"); - - fs::write("src/encoder.rs", code).unwrap(); -} diff --git a/scratch/patch_muxer_timebase.rs b/scratch/patch_muxer_timebase.rs deleted file mode 100644 index efc174b..0000000 --- a/scratch/patch_muxer_timebase.rs +++ /dev/null @@ -1,13 +0,0 @@ -use std::fs; -fn main() { - let mut code = fs::read_to_string("src/muxer.rs").unwrap(); - code = code.replace( - "if ret < 0 {\n return Err(\"Failed to copy video codec parameters\".into());\n }", - "if ret < 0 {\n return Err(\"Failed to copy video codec parameters\".into());\n }\n (*stream).time_base = (*codec_ctx).time_base;" - ); - code = code.replace( - "eprintln!(\"Failed to copy audio codec parameters\");\n }", - "eprintln!(\"Failed to copy audio codec parameters\");\n }\n (*astream).time_base = (*actx).time_base;" - ); - fs::write("src/muxer.rs", code).unwrap(); -} diff --git a/scratch/ring_patch.rs b/scratch/ring_patch.rs deleted file mode 100644 index 989de6d..0000000 --- a/scratch/ring_patch.rs +++ /dev/null @@ -1,17 +0,0 @@ -use std::fs; -fn main() { - let mut code = fs::read_to_string("src/ring.rs").unwrap(); - code = code.replace( - "pub fn is_keyframe(&self) -> bool {", - "pub fn stream_index(&self) -> i32 { - unsafe { (*self.ptr).stream_index } - } - - pub fn set_stream_index(&mut self, idx: i32) { - unsafe { (*self.ptr).stream_index = idx; } - } - - pub fn is_keyframe(&self) -> bool {" - ); - fs::write("src/ring.rs", code).unwrap(); -} diff --git a/scratch/test.rs b/scratch/test.rs deleted file mode 100644 index 45590d8..0000000 --- a/scratch/test.rs +++ /dev/null @@ -1 +0,0 @@ -fn main() { } diff --git a/scratch/test_aac.rs b/scratch/test_aac.rs deleted file mode 100644 index f94c9a2..0000000 --- a/scratch/test_aac.rs +++ /dev/null @@ -1,11 +0,0 @@ -use ffmpeg_next::ffi::*; -fn main() { - unsafe { - let codec = avcodec_find_encoder(AVCodecID::AV_CODEC_ID_AAC); - if codec.is_null() { - println!("AAC encoder not found"); - } else { - println!("AAC encoder found"); - } - } -} diff --git a/scratch/ui_clean.patch b/scratch/ui_clean.patch deleted file mode 100644 index 0caac81..0000000 --- a/scratch/ui_clean.patch +++ /dev/null @@ -1,21 +0,0 @@ ---- src/bin/vrec-ui.rs -+++ src/bin/vrec-ui.rs -@@ -1,18 +1,8 @@ --use vrec::ipc::Command; -+use vrec::ipc::{Command, send_command}; - use std::os::unix::net::UnixStream; - use std::io::Write; - use std::env; - use global_hotkey::{GlobalHotKeyManager, hotkey::{HotKey, Modifiers, Code}, GlobalHotKeyEvent}; -- --fn send_command(cmd: Command) -> Result<(), Box> { -- let socket_path = format!("{}/vrec.sock", env::var("XDG_RUNTIME_DIR").unwrap_or_else(|_| "/tmp".to_string())); -- let mut stream = UnixStream::connect(&socket_path)?; -- let payload = serde_json::to_vec(&cmd)?; -- let len_buf = (payload.len() as u32).to_le_bytes(); -- stream.write_all(&len_buf)?; -- stream.write_all(&payload)?; -- Ok(()) --} - - fn main() -> Result<(), Box> { diff --git a/scratch/ui_cli.patch b/scratch/ui_cli.patch deleted file mode 100644 index 7a37820..0000000 --- a/scratch/ui_cli.patch +++ /dev/null @@ -1,21 +0,0 @@ ---- src/bin/vrec-ui.rs -+++ src/bin/vrec-ui.rs -@@ -16,6 +16,19 @@ - } - - fn main() -> Result<(), Box> { -+ let args: Vec = env::args().collect(); -+ if args.len() > 1 { -+ match args[1].as_str() { -+ "--save" => { -+ return send_command(Command::SaveReplay).map_err(|e| e.into()); -+ } -+ "--menu" => { -+ vrec::overlay::show_saved_overlay(); -+ return Ok(()); -+ } -+ _ => {} -+ } -+ } -+ - println!("Starting vrec UI/Hotkey process..."); diff --git a/send_ipc.py b/send_ipc.py deleted file mode 100644 index c0b6f4c..0000000 --- a/send_ipc.py +++ /dev/null @@ -1,14 +0,0 @@ -import socket -import json -import struct -import os - -def send(cmd): - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - sock.connect(os.environ.get("XDG_RUNTIME_DIR", "/tmp") + "/vrec.sock") - payload = json.dumps(cmd).encode('utf-8') - sock.sendall(struct.pack(' Result<(), Box> { - let session_type = env::var("XDG_SESSION_TYPE").unwrap_or_else(|_| "x11".to_string()); - println!("Detected session type: {}", session_type); - - let mut source: Box = if std::env::args().any(|a| a == "--mock") { - println!("Initializing MOCK capture..."); - Box::new(vrec::capture::mock::MockCapture::new()) - } else if session_type.to_lowercase() == "wayland" { - println!("Initializing Wayland capture..."); - Box::new(vrec::capture::wayland::WaylandCapture::new().await?) - } else { - println!("Initializing X11 capture..."); - Box::new(vrec::capture::x11::X11Capture::new()?) - }; - - let first_frame = source.next_frame()?; - let (width, height) = match &first_frame { - Frame::Raw { width, height, .. } => (*width, *height), - Frame::DmaBuf { width, height, .. } => (*width, *height), - }; - - let (frame_tx, frame_rx) = bounded::(5); - let (cmd_tx, cmd_rx) = bounded::(10); - let (mux_tx, mux_rx) = bounded::>(1); - - let _ = frame_tx.send(first_frame); - - let capture_tx = frame_tx.clone(); - thread::spawn(move || { - loop { - match source.next_frame() { - Ok(frame) => { - if capture_tx.send(frame).is_err() { - break; - } - } - Err(e) => { - eprintln!("Capture error: {}", e); - break; - } - } - } - }); - - thread::spawn(move || { - let mut encoder = VaapiEncoder::new(width, height).expect("Failed to init encoder"); - let codec_ctx_ptr = encoder.codec_ctx() as usize; - - let mut config = vrec::config::VrecConfig::load(); - let mut ring = HeapRb::::new((config.replay_duration_sec * 60).max(60) as usize); - let mut normal_muxer: Option = None; - - thread::spawn(move || { - while let Ok(drain) = mux_rx.recv() { - let ts = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs(); - let name = format!("replay_{}.mp4", ts); - println!("Saving replay to {}...", name); - let mut start_idx = 0; - for (i, p) in drain.iter().enumerate() { - if p.is_keyframe() { - start_idx = i; - break; - } - } - - if start_idx < drain.len() { - let codec_ctx = codec_ctx_ptr as *mut ffmpeg_next::ffi::AVCodecContext; - let mut muxer = unsafe { Muxer::new(&name, codec_ctx).unwrap() }; - for p in drain.into_iter().skip(start_idx) { - let _ = muxer.write_packet(&p); - } - let _ = muxer.finalize(); - println!("Replay saved!"); - } else { - println!("No keyframe found in buffer, skipping save."); - } - } - }); - - while let Ok(frame) = frame_rx.recv() { - while let Ok(cmd) = cmd_rx.try_recv() { - match cmd { - Command::ReloadConfig => { - config = vrec::config::VrecConfig::load(); - println!("Daemon config reloaded!"); - let new_capacity = (config.replay_duration_sec * 60).max(60) as usize; - if ring.capacity().get() != new_capacity { - ring = HeapRb::::new(new_capacity); - println!("Replay buffer resized to {} frames.", new_capacity); - } - }, - Command::SaveReplay => { - if config.replay_enabled { - let drain = ring.iter().cloned().collect::>(); - let _ = mux_tx.try_send(drain); - } - }, - Command::StartRecording => { - if config.record_enabled && normal_muxer.is_none() { - let codec_ctx = codec_ctx_ptr as *mut ffmpeg_next::ffi::AVCodecContext; - let ts = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs(); - let name = format!("record_{}.mp4", ts); - normal_muxer = unsafe { Muxer::new(&name, codec_ctx).ok() }; - println!("Started normal recording to {}", name); - } - }, - Command::StopRecording => { - if let Some(mut m) = normal_muxer.take() { - let _ = m.finalize(); - println!("Stopped normal recording."); - } - }, - _ => {} - } - } - - if let Ok(packets) = encoder.encode_frame(&frame) { - for pkt in packets { - if config.replay_enabled { - ring.push_overwrite(pkt.clone()); - } - if let Some(muxer) = normal_muxer.as_mut() { - let _ = muxer.write_packet(&pkt); - } - } - } - } - }); - - let socket_path = format!("{}/vrec.sock", env::var("XDG_RUNTIME_DIR").unwrap_or_else(|_| "/tmp".to_string())); - let _ = std::fs::remove_file(&socket_path); - let listener = UnixListener::bind(&socket_path)?; - println!("Daemon listening on IPC socket: {}", socket_path); - - for stream in listener.incoming() { - match stream { - Ok(mut stream) => { - let mut len_buf = [0u8; 4]; - if stream.read_exact(&mut len_buf).is_ok() { - let len = u32::from_le_bytes(len_buf) as usize; - let mut payload = vec![0u8; len]; - if stream.read_exact(&mut payload).is_ok() - && let Ok(cmd) = serde_json::from_slice::(&payload) { - match cmd { - Command::StopDaemon => { - break; - }, - other => { - let _ = cmd_tx.try_send(other); - } - } - } - } - } - Err(err) => { - eprintln!("Socket error: {}", err); - } - } - } - - Ok(()) -} diff --git a/src/bin/vrec-ui.rs.orig b/src/bin/vrec-ui.rs.orig deleted file mode 100644 index 7935e60..0000000 --- a/src/bin/vrec-ui.rs.orig +++ /dev/null @@ -1,79 +0,0 @@ -use vrec::ipc::Command; -use std::os::unix::net::UnixStream; -use std::io::Write; -use std::env; -use global_hotkey::{GlobalHotKeyManager, hotkey::{HotKey, Modifiers, Code}, GlobalHotKeyEvent}; - -fn send_command(cmd: Command) -> Result<(), Box> { - let socket_path = format!("{}/vrec.sock", env::var("XDG_RUNTIME_DIR").unwrap_or_else(|_| "/tmp".to_string())); - let mut stream = UnixStream::connect(&socket_path)?; - let payload = serde_json::to_vec(&cmd)?; - let len_buf = (payload.len() as u32).to_le_bytes(); - stream.write_all(&len_buf)?; - stream.write_all(&payload)?; - Ok(()) -} - -fn main() -> Result<(), Box> { - let args: Vec = env::args().collect(); - if args.len() > 1 { - match args[1].as_str() { - "--save" => { - vrec::overlay::show_notification_overlay(); - return send_command(Command::SaveReplay).map_err(|e| e.into()); - } - "--menu" => { - vrec::overlay::show_menu_overlay(); - return Ok(()); - } - _ => {} - } - } - - println!("Starting vrec UI/Hotkey process..."); - - let session_type = env::var("XDG_SESSION_TYPE").unwrap_or_else(|_| "x11".to_string()); - let desktop = env::var("XDG_CURRENT_DESKTOP").unwrap_or_else(|_| "".to_string()).to_lowercase(); - - let is_gnome = desktop.contains("gnome") || desktop.contains("ubuntu"); - let is_kde = desktop.contains("kde"); - - if session_type.to_lowercase() == "wayland" { - if is_gnome || is_kde { - println!("WARNING: Wayland native layer shell used. Ensure your compositor allows it."); - println!("Note: Global hotkeys may fail to register on Wayland compositors."); - } else { - println!("wlroots-based compositor detected."); - } - } else { - println!("X11 session detected."); - } - - let manager = GlobalHotKeyManager::new().unwrap(); - let save_hotkey = HotKey::new(Some(Modifiers::CONTROL | Modifiers::SHIFT), Code::KeyR); - let menu_hotkey = HotKey::new(Some(Modifiers::ALT), Code::KeyZ); - - if let Err(e) = manager.register(save_hotkey) { - eprintln!("Failed to register global hotkey! Wayland compositor might be blocking it: {}", e); - eprintln!("Fallback: running without global hotkey. Send commands via socket manually."); - } else { - manager.register(menu_hotkey).unwrap(); - println!("Registered global hotkeys:\n - Ctrl+Shift+R (Save Replay)\n - Alt+Z (Open GUI Menu)"); - } - - let receiver = GlobalHotKeyEvent::receiver(); - loop { - if let Ok(event) = receiver.recv() { - if event.state == global_hotkey::HotKeyState::Pressed { - if event.id == save_hotkey.id() { - println!("Hotkey triggered! Sending SaveReplay command to daemon..."); - vrec::overlay::show_notification_overlay(); - let _ = send_command(Command::SaveReplay); - } else if event.id == menu_hotkey.id() { - println!("Alt+Z triggered! Opening GUI Menu Overlay..."); - vrec::overlay::show_menu_overlay(); - } - } - } - } -} diff --git a/src/capture/audio.rs.obOCNTS b/src/capture/audio.rs.obOCNTS deleted file mode 100644 index e69de29..0000000 diff --git a/src/capture/audio.rs.orig b/src/capture/audio.rs.orig deleted file mode 100644 index 356e4a7..0000000 --- a/src/capture/audio.rs.orig +++ /dev/null @@ -1,51 +0,0 @@ -use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; -use crossbeam_channel::Sender; - -pub struct AudioCapture { - _stream: cpal::Stream, -} - -impl AudioCapture { - pub fn new(sender: Sender>) -> Result> { - let host = cpal::default_host(); - let device = host.default_input_device().ok_or("No audio input device available")?; - - let config = device.default_input_config()?; - let sample_rate = config.sample_rate().0; - let channels = config.channels(); - - println!("Audio capture initialized: {} Hz, {} channels", sample_rate, channels); - - let err_fn = |err| eprintln!("Audio stream error: {}", err); - let stream_config: cpal::StreamConfig = config.clone().into(); - - let stream = match config.sample_format() { - cpal::SampleFormat::F32 => { - device.build_input_stream( - &stream_config, - move |data: &[f32], _: &_| { - let _ = sender.try_send(data.to_vec()); - }, - err_fn, - None, - )? - } - cpal::SampleFormat::I16 => { - device.build_input_stream( - &stream_config, - move |data: &[i16], _: &_| { - let f32_data: Vec = data.iter().map(|&s| s as f32 / i16::MAX as f32).collect(); - let _ = sender.try_send(f32_data); - }, - err_fn, - None, - )? - } - _ => return Err("Unsupported audio format".into()), - }; - - stream.play()?; - - Ok(Self { _stream: stream }) - } -} diff --git a/test_ipc.py b/test_ipc.py deleted file mode 100644 index 2af96a1..0000000 --- a/test_ipc.py +++ /dev/null @@ -1,11 +0,0 @@ -import socket -import json -import struct -import os - -sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) -sock.connect(os.environ.get("XDG_RUNTIME_DIR", "/tmp") + "/vrec.sock") -payload = b'"SaveReplay"' -sock.sendall(struct.pack('