diff --git a/Cargo.lock b/Cargo.lock index 524137c..791a2a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1113,8 +1113,8 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows 0.62.2", - "windows-core 0.62.2", + "windows 0.61.3", + "windows-core 0.58.0", ] [[package]] @@ -1318,7 +1318,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.2", - "windows-sys 0.61.0", + "windows-sys 0.60.2", ] [[package]] @@ -1766,7 +1766,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.0", + "windows-sys 0.60.2", ] [[package]] @@ -2342,7 +2342,7 @@ dependencies = [ "gobject-sys 0.21.2", "libc", "system-deps 7.0.7", - "windows-sys 0.61.0", + "windows-sys 0.60.2", ] [[package]] @@ -2886,7 +2886,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.2", + "windows-core 0.58.0", ] [[package]] @@ -3174,7 +3174,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.61.0", + "windows-sys 0.60.2", ] [[package]] @@ -3388,8 +3388,9 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libobs" -version = "4.0.2+32.0.2" -source = "git+https://github.com/libobs-rs/libobs-rs.git#9406f52e1bc8a93b992be9b04725ff8ab54bf3c6" +version = "5.0.0+32.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eef3e1fb1b9265a01c927e1915cfa4c5760359d01a07291bad6e6ba61e34a39" dependencies = [ "bindgen", "pkg-config", @@ -3397,10 +3398,12 @@ dependencies = [ [[package]] name = "libobs-simple" -version = "5.0.6+32.0.2" -source = "git+https://github.com/libobs-rs/libobs-rs.git#9406f52e1bc8a93b992be9b04725ff8ab54bf3c6" +version = "8.0.1+32.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb0021c0d3497415b7e558605a74d2a297a46f8301bb3ca2564bcd11aee6181d" dependencies = [ "display-info", + "lazy_static", "libobs", "libobs-simple-macro", "libobs-window-helper", @@ -3409,12 +3412,15 @@ dependencies = [ "num-derive", "num-traits", "paste", + "tokio", + "windows 0.62.2", ] [[package]] name = "libobs-simple-macro" -version = "6.0.1" -source = "git+https://github.com/libobs-rs/libobs-rs.git#9406f52e1bc8a93b992be9b04725ff8ab54bf3c6" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3bdb09c0709df286ccd0fd07994d1fee8877e039fee6e3c21f043d90da76db" dependencies = [ "proc-macro2", "quote", @@ -3423,16 +3429,18 @@ dependencies = [ [[package]] name = "libobs-window-helper" -version = "0.2.1" -source = "git+https://github.com/libobs-rs/libobs-rs.git#9406f52e1bc8a93b992be9b04725ff8ab54bf3c6" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69207f23b413243491c3703c6c1c9434fd0ff46f158ef4d945d1dc8d889bad80" dependencies = [ "windows 0.62.2", ] [[package]] name = "libobs-wrapper" -version = "6.0.4+32.0.2" -source = "git+https://github.com/libobs-rs/libobs-rs.git#9406f52e1bc8a93b992be9b04725ff8ab54bf3c6" +version = "9.0.4+32.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c081d77e7aa29a0a72415e0ad2be91e4ee579bf69ccf834077d8206c87bbf6" dependencies = [ "chrono", "display-info", @@ -3927,7 +3935,7 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" dependencies = [ - "proc-macro-crate 3.3.0", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 2.0.106", @@ -5426,7 +5434,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.61.0", + "windows-sys 0.60.2", ] [[package]] @@ -6216,7 +6224,7 @@ dependencies = [ "getrandom 0.3.3", "once_cell", "rustix 1.1.2", - "windows-sys 0.61.0", + "windows-sys 0.60.2", ] [[package]] @@ -7410,7 +7418,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.0", + "windows-sys 0.48.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 91371f9..1a07edc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,12 +45,11 @@ wgpu = "27.0.0" obws = "0.14.0" async-trait = "0.1" -libobs-wrapper = { git = "https://github.com/libobs-rs/libobs-rs.git", features = [ +libobs-wrapper = { version = "9", features = [ "logging_crash_handler", - "enable_runtime", ], default-features = false } -libobs-simple = { git = "https://github.com/libobs-rs/libobs-rs.git" } -libobs-window-helper = { git = "https://github.com/libobs-rs/libobs-rs.git" } +libobs-simple = "8" +libobs-window-helper = "0.4" [package] name = "owl-control" diff --git a/crates/constants/src/unsupported_games.json b/crates/constants/src/unsupported_games.json index be26a3f..83326aa 100644 --- a/crates/constants/src/unsupported_games.json +++ b/crates/constants/src/unsupported_games.json @@ -1451,6 +1451,16 @@ "binaries": ["hl"], "reason": "EnoughData" }, + { + "name": "House Builder", + "binaries": ["housebuilder"], + "reason": "EnoughData" + }, + { + "name": "House Builder 2", + "binaries": ["housebuilder2"], + "reason": "EnoughData" + }, { "name": "I Am Your Beast", "binaries": ["i am your beast"], @@ -1641,6 +1651,16 @@ "binaries": ["risk of rain 2"], "reason": "EnoughData" }, + { + "name": "Roboquest", + "binaries": ["roboquest-win64-shipping"], + "reason": "EnoughData" + }, + { + "name": "Rust", + "binaries": ["rustclient"], + "reason": "EnoughData" + }, { "name": "S.T.A.L.K.E.R. 2: Heart of Chornobyl", "binaries": ["stalker2-win64-shipping"], diff --git a/src/config.rs b/src/config.rs index b510043..94fee39 100644 --- a/src/config.rs +++ b/src/config.rs @@ -306,7 +306,7 @@ impl EncoderSettings { VideoEncoderType::Amf => self.amf.apply_to_data_updater(updater), VideoEncoderType::Qsv => self.qsv.apply_to_data_updater(updater), }; - updater.update()?; + updater.apply()?; Ok(data) } diff --git a/src/record/obs_embedded_recorder.rs b/src/record/obs_embedded_recorder.rs index ec0c8c4..2759685 100644 --- a/src/record/obs_embedded_recorder.rs +++ b/src/record/obs_embedded_recorder.rs @@ -16,17 +16,18 @@ use constants::{FPS, RECORDING_HEIGHT, RECORDING_WIDTH, encoding::VideoEncoderTy use windows::Win32::Foundation::HWND; use libobs_simple::sources::{ - ObsObjectUpdater, ObsSourceBuilder, + ObsEitherSource, ObsObjectUpdater, ObsSourceBuilder, windows::{ - GameCaptureSourceBuilder, GameCaptureSourceUpdater, ObsGameCaptureMode, - WindowCaptureSourceBuilder, WindowCaptureSourceUpdater, WindowInfo, + GameCaptureSource, GameCaptureSourceBuilder, ObsGameCaptureMode, ObsHookableSourceTrait, + WindowCaptureSource, WindowCaptureSourceBuilder, WindowInfo, }, }; use libobs_wrapper::{ context::ObsContext, data::{ - ObsDataGetters as _, - output::ObsOutputRef, + ObsDataGetters as _, ObsDataSetters, + object::ObsObjectTrait, + output::{ObsOutputRef, ObsOutputTrait}, video::{ObsVideoInfo, ObsVideoInfoBuilder}, }, encoders::{ @@ -34,10 +35,9 @@ use libobs_wrapper::{ }, enums::ObsScaleType, logger::ObsLogger, - scenes::ObsSceneRef, - sources::ObsSourceRef, + scenes::{ObsSceneItemRef, ObsSceneRef, SceneItemExtSceneTrait, SceneItemTrait}, unsafe_send::SendableComp, - utils::{AudioEncoderInfo, ObsPath, OutputInfo, VideoEncoderInfo, traits::ObsUpdatable}, + utils::{AudioEncoderInfo, ObsPath, OutputInfo, VideoEncoderInfo}, }; use crate::{ @@ -292,7 +292,7 @@ struct RecorderState { adapter_index: usize, skipped_frames: Arc>>, output: ObsOutputRef, - source: Option, + scene_item: Option>>, last_encoder_settings: Option, was_hooked: Arc, last_video_encoder_type: Option, @@ -377,7 +377,7 @@ impl RecorderState { adapter_index, skipped_frames, output, - source: None, + scene_item: None, last_encoder_settings: None, was_hooked: Arc::new(AtomicBool::new(false)), last_video_encoder_type: None, @@ -408,7 +408,7 @@ impl RecorderState { scene } else { tracing::info!("Creating new scene"); - self.obs_context.scene(OWL_SCENE_NAME)? + self.obs_context.scene(OWL_SCENE_NAME, Some(0))? }; self.obs_context @@ -417,21 +417,18 @@ impl RecorderState { let source_creation_state = SourceCreationState { use_window_capture: request.game_config.use_window_capture, }; - let source = prepare_source( + let scene_item = prepare_source( &mut self.obs_context, &request.game_exe, request.hwnd.0, &mut scene, - self.source.take(), + self.scene_item.take(), &source_creation_state, self.last_source_creation_state.as_ref(), )?; - // Register the source - scene.set_to_channel(0)?; - // Ensure the source takes up the entire scene - scene.fit_source_to_screen(&source)?; + scene_item.fit_source_to_screen()?; // Register the video encoder with encoder-specific settings let video_encoder_data = self.obs_context.data()?; @@ -486,18 +483,19 @@ impl RecorderState { // output let mut start_signal_rx = self .output - .signal_manager() + .signals() .on_start() .context("failed to register output on_start signal")?; let mut stop_signal_rx = self .output - .signal_manager() + .signals() .on_stop() .context("failed to register output on_stop signal")?; // source - let mut hook_signal_rx = source - .signal_manager() + let mut hook_signal_rx = scene_item + .inner_source() + .source_specific_signals() .on_hooked() .context("failed to register source on_hooked signal")?; @@ -571,7 +569,7 @@ impl RecorderState { self.output.start()?; - self.source = Some(source); + self.scene_item = Some(scene_item); self.last_application = Some((request.game_exe.clone(), request.hwnd)); self.last_source_creation_state = Some(source_creation_state); self.is_recording = true; @@ -637,9 +635,9 @@ impl RecorderState { { tracing::warn!("Game no longer open, removing source"); if let Some(mut scene) = self.obs_context.get_scene(OWL_SCENE_NAME)? - && let Some(source) = self.source.take() + && let Some(scene_item) = self.scene_item.take() { - scene.remove_source(&source)?; + scene.remove_scene_item(scene_item)?; self.last_application = None; } } @@ -708,23 +706,25 @@ fn prepare_source( game_exe: &str, hwnd: HWND, scene: &mut ObsSceneRef, - mut last_source: Option, + mut last_scene_item: Option< + ObsSceneItemRef>, + >, state: &SourceCreationState, last_state: Option<&SourceCreationState>, -) -> Result { +) -> Result>> { let capture_audio = true; // Check if source creation state changed - if so, we can't reuse the old source if let Some(last) = last_state && last != state - && last_source.is_some() + && last_scene_item.is_some() { tracing::info!( "Source creation state changed ({last:?} -> {state:?}), discarding old source", ); - if let Some(source) = last_source.take() { + if let Some(scene_item) = last_scene_item.take() { tracing::info!("Removing old source"); - dbg!(scene.remove_source(&source))?; + dbg!(scene.remove_scene_item(scene_item))?; tracing::info!("Old source removed"); } } @@ -737,23 +737,34 @@ fn prepare_source( // capture full screen. if this is set to true there's black borders around the window capture. let client_area = false; - if let Some(mut source) = last_source.take() { - tracing::info!("Reusing existing window capture source"); - source - .create_updater::()? - .set_window(&window) - .set_capture_audio(capture_audio)? - .set_client_area(client_area) - .update()?; - Ok(source) - } else { + if let Some(mut scene_item) = last_scene_item.take() { + if let ObsEitherSource::Left(window_source) = scene_item.inner_source_mut() { + tracing::info!("Reusing existing window capture source"); + window_source + .create_updater()? + .set_window(&window) + .set_capture_audio(capture_audio)? + .set_client_area(client_area) + .update()?; + return Ok(scene_item); + } else { + // Source type mismatch - remove old source and create new one below + tracing::info!("Source type mismatch (expected window capture), recreating"); + scene.remove_scene_item(scene_item)?; + } + } + + { tracing::info!("Creating new window capture source"); - obs_context + let window_source = obs_context .source_builder::(OWL_WINDOW_CAPTURE_NAME)? .set_window(&window) .set_capture_audio(capture_audio)? .set_client_area(client_area) - .add_to_scene(scene) + .build()?; + + let source = ObsEitherSource::Left(window_source); + scene.add_source(source) } } else { let window = find_game_capture_window(Some(game_exe), hwnd)?; @@ -766,16 +777,24 @@ fn prepare_source( let capture_mode = ObsGameCaptureMode::CaptureSpecificWindow; - if let Some(mut source) = last_source.take() { - tracing::info!("Reusing existing game capture source"); - source - .create_updater::()? - .set_capture_mode(capture_mode) - .set_window_raw(window.obs_id.as_str()) - .set_capture_audio(capture_audio)? - .update()?; - Ok(source) - } else { + if let Some(mut scene_item) = last_scene_item.take() { + if let ObsEitherSource::Right(game_source) = scene_item.inner_source_mut() { + tracing::info!("Reusing existing game capture source"); + game_source + .create_updater()? + .set_capture_mode(capture_mode) + .set_window_raw(window.obs_id.as_str()) + .set_capture_audio(capture_audio)? + .update()?; + return Ok(scene_item); + } else { + // Source type mismatch - remove old source and create new one below + tracing::info!("Source type mismatch (expected game capture), recreating"); + scene.remove_scene_item(scene_item)?; + } + } + + { tracing::info!("Creating new game capture source"); if GameCaptureSourceBuilder::is_window_in_use_by_other_instance(window.pid)? { @@ -785,12 +804,16 @@ fn prepare_source( ); } - obs_context + let source = obs_context .source_builder::(OWL_GAME_CAPTURE_NAME)? .set_capture_mode(capture_mode) .set_window(&window) .set_capture_audio(capture_audio)? - .add_to_scene(scene) + .build()?; + + let source = ObsEitherSource::Right(source); + + scene.add_source(source) } };