Skip to main content

libobs_wrapper\encoders/
video.rs

1use libobs::{obs_encoder, video_output};
2use std::{
3    ptr,
4    sync::{Arc, RwLock},
5};
6
7use crate::{
8    data::{
9        object::{inner_fn_update_settings, ObsObjectTrait, ObsObjectTraitPrivate},
10        ImmutableObsData, ObsData, ObsDataPointers,
11    },
12    encoders::{_ObsEncoderDropGuard, ObsEncoderTrait},
13    run_with_obs,
14    runtime::ObsRuntime,
15    unsafe_send::{Sendable, SmartPointerSendable},
16    utils::{ObsError, ObsString, VideoEncoderInfo},
17};
18
19#[derive(Clone, Debug)]
20#[allow(dead_code)]
21/// Represents a VideoEncoder. The VideoEncoder is removed,
22/// if there are no references to this struct left.
23/// If you attach this video encoder to a `ObsOutputRef`, it is stored internally
24/// so you can safely drop this struct, if attached to the `ObsOutputRef`.
25/// If not attached to the output, it'll just remove itself again.
26pub struct ObsVideoEncoder {
27    pub(crate) id: ObsString,
28    pub(crate) name: ObsString,
29    pub(crate) settings: Arc<RwLock<ImmutableObsData>>,
30    pub(crate) hotkey_data: Arc<RwLock<ImmutableObsData>>,
31    pub(crate) runtime: ObsRuntime,
32    pub(crate) encoder: SmartPointerSendable<*mut obs_encoder>,
33}
34
35impl ObsVideoEncoder {
36    /// Info: the handler attribute is no longer needed and kept for compatibility. The `handler` parameter will be removed in a future release.
37    pub fn new_from_info(
38        info: VideoEncoderInfo,
39        runtime: ObsRuntime,
40    ) -> Result<Arc<Self>, ObsError> {
41        let VideoEncoderInfo {
42            id,
43            name,
44            settings,
45            hotkey_data,
46        } = info;
47
48        let settings_ptr = settings.as_ref().map(|s| s.as_ptr());
49        let hotkey_data_ptr = hotkey_data.as_ref().map(|h| h.as_ptr());
50
51        let encoder_ptr = run_with_obs!(
52            runtime,
53            (id, name, hotkey_data_ptr, settings_ptr),
54            move || {
55                let settings_ptr_raw = match settings_ptr {
56                    Some(s) => s.get_ptr(),
57                    None => ptr::null_mut(),
58                };
59
60                let hotkey_data_ptr_raw = match hotkey_data_ptr {
61                    Some(h) => h.get_ptr(),
62                    None => ptr::null_mut(),
63                };
64
65                let ptr = unsafe {
66                    // Safety: All pointers are in the current scope and therefore valid.
67                    libobs::obs_video_encoder_create(
68                        id.as_ptr().0,
69                        name.as_ptr().0,
70                        settings_ptr_raw,
71                        hotkey_data_ptr_raw,
72                    )
73                };
74
75                if ptr.is_null() {
76                    Err(ObsError::NullPointer(None))
77                } else {
78                    Ok(Sendable(ptr))
79                }
80            }
81        )??;
82
83        let encoder_ptr = SmartPointerSendable::new(
84            encoder_ptr.0,
85            Arc::new(_ObsEncoderDropGuard {
86                encoder: encoder_ptr,
87                runtime: runtime.clone(),
88            }),
89            runtime.native_registry(),
90        );
91
92        let hotkey_data = match hotkey_data {
93            Some(h) => h,
94            None => ImmutableObsData::new(&runtime)?,
95        };
96
97        let settings = {
98            let settings_ptr = run_with_obs!(runtime, (encoder_ptr), move || {
99                let ptr = unsafe {
100                    // Safety: encoder_ptr is valid because of the SmartPointer
101                    libobs::obs_encoder_get_settings(encoder_ptr.get_ptr())
102                };
103
104                Sendable(ptr)
105            })?;
106            ImmutableObsData::from_raw_pointer(settings_ptr, runtime.clone())
107        };
108
109        Ok(Arc::new(Self {
110            encoder: encoder_ptr,
111            id,
112            name,
113            settings: Arc::new(RwLock::new(settings)),
114            hotkey_data: Arc::new(RwLock::new(hotkey_data)),
115            runtime,
116        }))
117    }
118
119    /// This is only needed once for global video context
120    /// # Safety
121    /// The handler pointer must be a valid pointer to a video_output that lives as long as this function call.
122    pub unsafe fn set_video_context(&self, handler: *mut video_output) -> Result<(), ObsError> {
123        let handler = Sendable(handler);
124        let self_ptr = self.__native_handle();
125        run_with_obs!(self.runtime, (handler, self_ptr), move || {
126            unsafe {
127                // Safety: Caller must make sure that the handler pointer is valid and the self pointer is a SmartPointer.
128                libobs::obs_encoder_set_video(self_ptr.get_ptr(), handler.0);
129            }
130        })
131    }
132}
133
134impl ObsObjectTraitPrivate for ObsVideoEncoder {
135    fn __internal_replace_settings(&self, settings: ImmutableObsData) -> Result<(), ObsError> {
136        self.settings
137            .write()
138            .map_err(|_| {
139                ObsError::LockError(
140                    "Failed to acquire lock for replacing settings in the video encoder".into(),
141                )
142            })
143            .map(|mut guard| {
144                *guard = settings;
145            })
146    }
147
148    fn __internal_replace_hotkey_data(
149        &self,
150        hotkey_data: ImmutableObsData,
151    ) -> Result<(), ObsError> {
152        self.hotkey_data
153            .write()
154            .map_err(|_| {
155                ObsError::LockError(
156                    "Failed to acquire lock for replacing hotkey data in the video encoder".into(),
157                )
158            })
159            .map(|mut guard| {
160                *guard = hotkey_data;
161            })
162    }
163}
164
165impl ObsObjectTrait for ObsVideoEncoder {
166    type Native = *mut libobs::obs_encoder;
167
168    fn __native_handle(&self) -> SmartPointerSendable<Self::Native> {
169        self.encoder.clone()
170    }
171
172    fn runtime(&self) -> &ObsRuntime {
173        &self.runtime
174    }
175
176    fn settings(&self) -> Result<ImmutableObsData, ObsError> {
177        self.settings
178            .read()
179            .map_err(|_| {
180                ObsError::LockError(
181                    "Failed to acquire lock for reading settings in the video encoder".into(),
182                )
183            })
184            .map(|s| s.clone())
185    }
186
187    fn hotkey_data(&self) -> Result<ImmutableObsData, ObsError> {
188        self.hotkey_data
189            .read()
190            .map_err(|_| {
191                ObsError::LockError(
192                    "Failed to acquire lock for reading hotkey data in the video encoder".into(),
193                )
194            })
195            .map(|h| h.clone())
196    }
197
198    fn id(&self) -> ObsString {
199        self.id.clone()
200    }
201
202    fn name(&self) -> ObsString {
203        self.name.clone()
204    }
205
206    /// Updates the settings of this output. Fails if active.
207    fn update_settings(&self, settings: ObsData) -> Result<(), ObsError> {
208        if self.is_active()? {
209            return Err(ObsError::EncoderActive);
210        }
211
212        inner_fn_update_settings!(self, libobs::obs_encoder_update, settings)
213    }
214}
215
216impl ObsEncoderTrait for ObsVideoEncoder {}