diff --git a/record-daemon/Cargo.lock b/record-daemon/Cargo.lock index 31c8923..9dcd723 100644 --- a/record-daemon/Cargo.lock +++ b/record-daemon/Cargo.lock @@ -2988,6 +2988,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e513e435a8898a0002270f29d0a708b7879708fb5c4d00e46983ca2d2d378cf0" dependencies = [ + "futures-core", "libc", "signal-hook", "tokio", diff --git a/record-daemon/Cargo.toml b/record-daemon/Cargo.toml index 05cb8ea..3d3f7a2 100644 --- a/record-daemon/Cargo.toml +++ b/record-daemon/Cargo.toml @@ -73,7 +73,7 @@ display-info = "0.5" # Signal handling for graceful shutdown (Unix only) [target.'cfg(unix)'.dependencies] signal-hook = "0.4" -signal-hook-tokio = { version = "0.4" } +signal-hook-tokio = { version = "0.4", features = ["futures-v0_3"] } # Windows-specific dependencies [target.'cfg(windows)'.dependencies] diff --git a/record-daemon/src/main.rs b/record-daemon/src/main.rs index db2574f..1265cc8 100644 --- a/record-daemon/src/main.rs +++ b/record-daemon/src/main.rs @@ -501,16 +501,16 @@ async fn main() -> Result<()> { let mut daemon = Daemon::new(settings); // Handle shutdown signals - let _shutdown_tx = daemon.shutdown_tx.clone(); + let shutdown_tx = daemon.shutdown_tx.clone(); #[cfg(unix)] { + use futures::StreamExt; use signal_hook::consts::signal::*; use signal_hook_tokio::Signals; let mut signals = Signals::new([SIGTERM, SIGINT, SIGQUIT])?; - let handle = signals.handle(); let tx = shutdown_tx.clone(); tokio::spawn(async move { diff --git a/record-daemon/src/recording/encoder.rs b/record-daemon/src/recording/encoder.rs index ca24b8f..d6a878c 100644 --- a/record-daemon/src/recording/encoder.rs +++ b/record-daemon/src/recording/encoder.rs @@ -369,11 +369,6 @@ fn is_nvenc_available() -> bool { || std::path::Path::new("C:\\Windows\\System32\\nvEncMFTH265.dll").exists() } -#[cfg(not(target_os = "windows"))] -fn is_nvenc_available() -> bool { - false -} - /// Check if AMD AMF is available. #[cfg(target_os = "windows")] fn is_amf_available() -> bool { @@ -381,11 +376,6 @@ fn is_amf_available() -> bool { std::path::Path::new("C:\\Windows\\System32\\amdocl64.dll").exists() } -#[cfg(not(target_os = "windows"))] -fn is_amf_available() -> bool { - false -} - /// Check if Intel QuickSync is available. #[cfg(target_os = "windows")] fn is_quicksync_available() -> bool { @@ -393,11 +383,6 @@ fn is_quicksync_available() -> bool { std::path::Path::new("C:\\Windows\\System32\\mfx64.dll").exists() } -#[cfg(not(target_os = "windows"))] -fn is_quicksync_available() -> bool { - false -} - /// Get the best available encoder capability. pub fn best_available_encoder() -> EncoderCapability { let capabilities = detect_hardware_encoders(); diff --git a/record-daemon/src/recording/obs_context.rs b/record-daemon/src/recording/obs_context.rs index 79d96fa..f67853d 100644 --- a/record-daemon/src/recording/obs_context.rs +++ b/record-daemon/src/recording/obs_context.rs @@ -495,6 +495,7 @@ impl ObsContext { } /// Set up capture source with fallback from game capture to monitor capture. + #[cfg(target_os = "windows")] fn setup_game_capture(&mut self) -> Result<()> { use std::io::Write; @@ -519,7 +520,19 @@ impl ObsContext { } } - /// Try to set up game capture source. + /// Set up capture source on Linux using screen capture. + #[cfg(target_os = "linux")] + fn setup_game_capture(&mut self) -> Result<()> { + use std::io::Write; + + info!("[CAPTURE] Setting up screen capture for Linux..."); + std::io::stderr().flush().ok(); + + self.setup_linux_screen_capture() + } + + /// Try to set up game capture source (Windows only). + #[cfg(target_os = "windows")] fn try_game_capture(&mut self) -> Result<()> { use libobs_simple::sources::windows::{GameCaptureSourceBuilder, ObsGameCaptureMode}; use libobs_simple::sources::ObsSourceBuilder; @@ -665,7 +678,8 @@ impl ObsContext { Ok(()) } - /// Set up monitor capture as fallback. + /// Set up monitor capture as fallback (Windows only). + #[cfg(target_os = "windows")] fn setup_monitor_capture(&mut self) -> Result<()> { use libobs_simple::sources::windows::MonitorCaptureSourceBuilder; use libobs_simple::sources::ObsSourceBuilder; @@ -821,6 +835,140 @@ impl ObsContext { Ok(()) } + /// Set up screen capture on Linux using X11 or PipeWire. + #[cfg(target_os = "linux")] + fn setup_linux_screen_capture(&mut self) -> Result<()> { + use libobs_simple::sources::linux::LinuxGeneralScreenCaptureBuilder; + use libobs_simple::sources::ObsSourceBuilder; + use std::io::Write; + + info!("[LINUX_CAPTURE] Setting up Linux screen capture..."); + std::io::stderr().flush().ok(); + + let context = self.context.as_mut().ok_or_else(|| { + error!("[LINUX_CAPTURE] OBS not initialized"); + std::io::stderr().flush().ok(); + RecordingError::ObsInitError("OBS not initialized".to_string()) + })?; + + // Create a scene + info!("[LINUX_CAPTURE] Creating scene 'main'..."); + std::io::stderr().flush().ok(); + let mut scene = context.scene("main", None).map_err(|e| { + error!("[LINUX_CAPTURE] Failed to create scene: {:?}", e); + std::io::stderr().flush().ok(); + RecordingError::StartError(format!("Failed to create scene: {:?}", e)) + })?; + info!("[LINUX_CAPTURE] Scene created successfully"); + std::io::stderr().flush().ok(); + + // Build screen capture source using LinuxGeneralScreenCaptureBuilder + // This automatically uses X11 or PipeWire depending on the platform + info!("[LINUX_CAPTURE] Creating screen capture source builder..."); + std::io::stderr().flush().ok(); + + let builder_result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + LinuxGeneralScreenCaptureBuilder::new("screen_capture", context.runtime().clone()) + })); + + let builder = match builder_result { + Ok(Ok(b)) => { + info!("[LINUX_CAPTURE] Screen capture builder created"); + std::io::stderr().flush().ok(); + b + } + Ok(Err(e)) => { + error!( + "[LINUX_CAPTURE] Failed to create screen capture builder: {:?}", + e + ); + std::io::stderr().flush().ok(); + return Err(RecordingError::StartError(format!( + "Failed to create screen capture builder: {:?}", + e + )) + .into()); + } + Err(panic_info) => { + error!( + "[LINUX_CAPTURE] PANIC creating screen capture builder: {:?}", + panic_info + ); + std::io::stderr().flush().ok(); + eprintln!( + "[LINUX_CAPTURE] PANIC creating screen capture builder: {:?}", + panic_info + ); + return Err(RecordingError::StartError( + "Panic creating screen capture builder".to_string(), + ) + .into()); + } + }; + + // Build the source + let source_result = + std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| builder.build())); + + let source = match source_result { + Ok(Ok(s)) => { + info!("[LINUX_CAPTURE] Screen capture source created"); + std::io::stderr().flush().ok(); + s + } + Ok(Err(e)) => { + error!( + "[LINUX_CAPTURE] Failed to create screen capture source: {:?}", + e + ); + std::io::stderr().flush().ok(); + return Err(RecordingError::StartError(format!( + "Failed to create screen capture source: {:?}", + e + )) + .into()); + } + Err(panic_info) => { + error!( + "[LINUX_CAPTURE] PANIC creating screen capture source: {:?}", + panic_info + ); + std::io::stderr().flush().ok(); + eprintln!( + "[LINUX_CAPTURE] PANIC creating screen capture source: {:?}", + panic_info + ); + return Err(RecordingError::StartError( + "Panic creating screen capture source".to_string(), + ) + .into()); + } + }; + + // Add source to scene + info!("[LINUX_CAPTURE] Adding source to scene..."); + std::io::stderr().flush().ok(); + scene.add_source(source).map_err(|e| { + error!("[LINUX_CAPTURE] Failed to add source to scene: {:?}", e); + std::io::stderr().flush().ok(); + RecordingError::StartError(format!("Failed to add source to scene: {:?}", e)) + })?; + info!("[LINUX_CAPTURE] Source added to scene"); + std::io::stderr().flush().ok(); + + // Set the scene as active + info!("[LINUX_CAPTURE] Setting scene as active on channel 0..."); + std::io::stderr().flush().ok(); + scene.set_to_channel(0).map_err(|e| { + error!("[LINUX_CAPTURE] Failed to set scene: {:?}", e); + RecordingError::StartError(format!("Failed to set scene: {:?}", e)) + })?; + + info!("[LINUX_CAPTURE] Screen capture source configured successfully"); + std::io::stderr().flush().ok(); + Ok(()) + } + /// Stop recording. pub fn stop_recording(&mut self) -> Result<()> { if !self.recording {