chore: Clean up MP4 files and test scripts from repository

This commit is contained in:
katR 2026-09-02 19:30:07 +05:30
parent 0dc63cf9ad
commit 5cb25e3ba9
32 changed files with 5 additions and 809 deletions

5
.gitignore vendored
View file

@ -1 +1,6 @@
/target
*.mp4
scratch/
*.orig
*.obOCNTS
*.py

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -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<Vec<f32>>) -> Result<Self, Box<dyn std::error::Error>> {",
"pub fn new(sender: Sender<Vec<f32>>) -> Result<(Self, u32, u16), Box<dyn std::error::Error>> {"
);
code = code.replace(
"Ok(Self { _stream: stream })",
"Ok((Self { _stream: stream }, sample_rate, channels))"
);
fs::write("src/capture/audio.rs", code).unwrap();
}

View file

@ -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::<Vec<Packet>>(1);",
"let (mux_tx, mux_rx) = bounded::<Vec<Packet>>(1);\n let (audio_tx, audio_rx) = bounded::<Vec<f32>>(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::<Packet>::new((config.replay_duration_sec * 60).max(60) as usize);",
"let mut ring = HeapRb::<Packet>::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();
}

View file

@ -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());
},

View file

@ -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<f32> = data.iter().map(|&s| s as f32 / i16::MAX as f32).collect();
let _ = sender.try_send(f32_data);

View file

@ -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());
},

View file

@ -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();

View file

@ -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();

View file

@ -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);
+ }
+ }
+ }
}

View file

@ -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::<Packet>::new((config.replay_duration_sec * 60).max(60) as usize);
let mut normal_muxer: Option<Muxer> = 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);
+ }
+ }
}
}

View file

@ -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<Self, String> {",
"pub unsafe fn new(path: &str, codec_ctx: *mut AVCodecContext, audio_codec_ctx: Option<*mut AVCodecContext>) -> Result<Self, String> {"
);
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();
}

View file

@ -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();
}

View file

@ -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();
}

View file

@ -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();
}

View file

@ -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();
}

View file

@ -1 +0,0 @@
fn main() { }

View file

@ -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");
}
}
}

View file

@ -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<dyn std::error::Error + Send + Sync>> {
- 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<dyn std::error::Error + Send + Sync>> {

View file

@ -1,21 +0,0 @@
--- src/bin/vrec-ui.rs
+++ src/bin/vrec-ui.rs
@@ -16,6 +16,19 @@
}
fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
+ let args: Vec<String> = 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...");

View file

@ -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('<I', len(payload)))
sock.sendall(payload)
import sys
send(sys.argv[1])

View file

@ -1,176 +0,0 @@
use vrec::capture::{Frame, FrameSource};
use vrec::encoder::VaapiEncoder;
use vrec::ring::Packet;
use vrec::muxer::Muxer;
use vrec::ipc::Command;
use ringbuf::HeapRb;
use ringbuf::traits::{RingBuffer, Consumer, Observer};
use crossbeam_channel::bounded;
use std::thread;
use std::env;
use std::os::unix::net::UnixListener;
use std::io::Read;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let session_type = env::var("XDG_SESSION_TYPE").unwrap_or_else(|_| "x11".to_string());
println!("Detected session type: {}", session_type);
let mut source: Box<dyn FrameSource> = 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::<Frame>(5);
let (cmd_tx, cmd_rx) = bounded::<Command>(10);
let (mux_tx, mux_rx) = bounded::<Vec<Packet>>(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::<Packet>::new((config.replay_duration_sec * 60).max(60) as usize);
let mut normal_muxer: Option<Muxer> = 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::<Packet>::new(new_capacity);
println!("Replay buffer resized to {} frames.", new_capacity);
}
},
Command::SaveReplay => {
if config.replay_enabled {
let drain = ring.iter().cloned().collect::<Vec<_>>();
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::<Command>(&payload) {
match cmd {
Command::StopDaemon => {
break;
},
other => {
let _ = cmd_tx.try_send(other);
}
}
}
}
}
Err(err) => {
eprintln!("Socket error: {}", err);
}
}
}
Ok(())
}

View file

@ -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<dyn std::error::Error + Send + Sync>> {
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<dyn std::error::Error + Send + Sync>> {
let args: Vec<String> = 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();
}
}
}
}
}

View file

@ -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<Vec<f32>>) -> Result<Self, Box<dyn std::error::Error>> {
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<f32> = 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 })
}
}

View file

@ -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('<I', len(payload)))
sock.sendall(payload)
print("Sent SaveReplay")

View file

@ -1,7 +0,0 @@
import socket, struct, os
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(os.environ.get("XDG_RUNTIME_DIR", "/tmp") + "/vrec.sock")
payload = b'"StartRecording"'
sock.sendall(struct.pack('<I', len(payload)))
sock.sendall(payload)
print("Sent StartRecording")

View file

@ -1,7 +0,0 @@
import socket, struct, os
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(os.environ.get("XDG_RUNTIME_DIR", "/tmp") + "/vrec.sock")
payload = b'"ReloadConfig"'
sock.sendall(struct.pack('<I', len(payload)))
sock.sendall(payload)
print("Sent ReloadConfig")

View file

@ -1,7 +0,0 @@
import socket, struct, os
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(os.environ.get("XDG_RUNTIME_DIR", "/tmp") + "/vrec.sock")
payload = b'"StopRecording"'
sock.sendall(struct.pack('<I', len(payload)))
sock.sendall(payload)
print("Sent StopRecording")