libobs_sources\windows\sources/
monitor_capture.rs

1///! Monitor capture source for Windows using libobs-rs
2/// ! This source captures the entire monitor and is used for screen recording.
3/// Note: This does not update the capture method directly, instead the capture method gets
4/// stored in the struct. The capture method is being set to WGC at first, then the source is created and then the capture method is updated to the desired method.
5use display_info::DisplayInfo;
6use libobs_source_macro::obs_object_impl;
7use libobs_wrapper::{
8    data::{ObsObjectBuilder, ObsObjectUpdater},
9    scenes::ObsSceneRef,
10    sources::{ObsSourceBuilder, ObsSourceRef},
11    unsafe_send::Sendable,
12    utils::ObsError,
13};
14use num_traits::ToPrimitive;
15
16use crate::macro_helper::define_object_manager;
17
18use super::ObsDisplayCaptureMethod;
19
20// Usage example
21define_object_manager!(
22    /// Provides a easy to use builder for the monitor capture source.
23    #[derive(Debug)]
24    struct MonitorCaptureSource("monitor_capture") for ObsSourceRef {
25        #[obs_property(type_t = "string", settings_key = "monitor_id")]
26        monitor_id_raw: String,
27
28        #[obs_property(type_t = "bool")]
29        /// Sets whether the cursor should be captured.
30        capture_cursor: bool,
31
32        #[obs_property(type_t = "bool")]
33        /// Compatibility mode for the monitor capture source.
34        compatibility: bool,
35
36        capture_method: Option<ObsDisplayCaptureMethod>,
37    }
38);
39
40#[obs_object_impl]
41impl MonitorCaptureSource {
42    /// Gets all available monitors
43    pub fn get_monitors() -> anyhow::Result<Vec<Sendable<DisplayInfo>>> {
44        Ok(DisplayInfo::all()?
45            .into_iter()
46            .map(|e| Sendable(e))
47            .collect())
48    }
49
50    pub fn set_monitor(self, monitor: &Sendable<DisplayInfo>) -> Self {
51        self.set_monitor_id_raw(monitor.0.name.as_str())
52    }
53}
54
55impl<'a> MonitorCaptureSourceUpdater<'a> {
56    pub fn set_capture_method(mut self, method: ObsDisplayCaptureMethod) -> Self {
57        self.get_settings_updater()
58            .set_int_ref("method", method.to_i32().unwrap() as i64);
59
60        self
61    }
62}
63
64impl MonitorCaptureSourceBuilder {
65    /// Sets the capture method for the monitor capture source.
66    /// Only MethodWgc works for now as the other DXGI method does not work and only records a black screen (Failed to DuplicateOutput1)
67    /// Workaround for black screen bug: [issue](https://github.com/joshprk/libobs-rs/issues/5)
68    pub fn set_capture_method(mut self, method: ObsDisplayCaptureMethod) -> Self {
69        self.capture_method = Some(method);
70        self
71    }
72}
73
74#[cfg_attr(not(feature = "blocking"), async_trait::async_trait)]
75impl ObsSourceBuilder for MonitorCaptureSourceBuilder {
76    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
77    async fn add_to_scene<'a>(
78        mut self,
79        scene: &'a mut ObsSceneRef,
80    ) -> Result<ObsSourceRef, ObsError>
81    where
82        Self: Sized,
83    {
84        // Because of a black screen bug, we need to set the method to WGC first and then update
85        self.get_settings_updater().set_int_ref(
86            "method",
87            ObsDisplayCaptureMethod::MethodWgc.to_i32().unwrap() as i64,
88        );
89
90        let method_to_set = self.capture_method.clone();
91        let runtime = self.runtime.clone();
92
93        let b = self.build().await?;
94        let mut res = scene.add_source(b).await?;
95
96        if let Some(method) = method_to_set {
97            MonitorCaptureSourceUpdater::create_update(runtime, &mut res)
98                .await?
99                .set_capture_method(method)
100                .update()
101                .await?;
102        }
103
104        Ok(res)
105    }
106}