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::{ObsEncoderTrait, _ObsEncoderDropGuard},
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        );
90
91        let hotkey_data = match hotkey_data {
92            Some(h) => h,
93            None => ImmutableObsData::new(&runtime)?,
94        };
95
96        let settings = {
97            let settings_ptr = run_with_obs!(runtime, (encoder_ptr), move || {
98                let ptr = unsafe {
99                    // Safety: encoder_ptr is valid because of the SmartPointer
100                    libobs::obs_encoder_get_settings(encoder_ptr.get_ptr())
101                };
102
103                Sendable(ptr)
104            })?;
105            ImmutableObsData::from_raw_pointer(settings_ptr, runtime.clone())
106        };
107
108        Ok(Arc::new(Self {
109            encoder: encoder_ptr,
110            id,
111            name,
112            settings: Arc::new(RwLock::new(settings)),
113            hotkey_data: Arc::new(RwLock::new(hotkey_data)),
114            runtime,
115        }))
116    }
117
118    /// This is only needed once for global video context
119    /// # Safety
120    /// The handler pointer must be a valid pointer to a video_output that lives as long as this function call.
121    pub unsafe fn set_video_context(
122        &mut self,
123        handler: Sendable<*mut video_output>,
124    ) -> Result<(), ObsError> {
125        let self_ptr = self.as_ptr();
126        run_with_obs!(self.runtime, (handler, self_ptr), move || {
127            unsafe {
128                // Safety: Caller must make sure that the handler pointer is valid and the self pointer is a SmartPointer.
129                libobs::obs_encoder_set_video(self_ptr.get_ptr(), handler.0);
130            }
131        })
132    }
133}
134
135impl ObsObjectTraitPrivate for ObsVideoEncoder {
136    fn __internal_replace_settings(&self, settings: ImmutableObsData) -> Result<(), ObsError> {
137        self.settings
138            .write()
139            .map_err(|_| {
140                ObsError::LockError(
141                    "Failed to acquire lock for replacing settings in the video encoder".into(),
142                )
143            })
144            .map(|mut guard| {
145                *guard = settings;
146            })
147    }
148
149    fn __internal_replace_hotkey_data(
150        &self,
151        hotkey_data: ImmutableObsData,
152    ) -> Result<(), ObsError> {
153        self.hotkey_data
154            .write()
155            .map_err(|_| {
156                ObsError::LockError(
157                    "Failed to acquire lock for replacing hotkey data in the video encoder".into(),
158                )
159            })
160            .map(|mut guard| {
161                *guard = hotkey_data;
162            })
163    }
164}
165
166impl ObsObjectTrait<*mut libobs::obs_encoder> for ObsVideoEncoder {
167    fn runtime(&self) -> &ObsRuntime {
168        &self.runtime
169    }
170
171    fn settings(&self) -> Result<ImmutableObsData, ObsError> {
172        self.settings
173            .read()
174            .map_err(|_| {
175                ObsError::LockError(
176                    "Failed to acquire lock for reading settings in the video encoder".into(),
177                )
178            })
179            .map(|s| s.clone())
180    }
181
182    fn hotkey_data(&self) -> Result<ImmutableObsData, ObsError> {
183        self.hotkey_data
184            .read()
185            .map_err(|_| {
186                ObsError::LockError(
187                    "Failed to acquire lock for reading hotkey data in the video encoder".into(),
188                )
189            })
190            .map(|h| h.clone())
191    }
192
193    fn id(&self) -> ObsString {
194        self.id.clone()
195    }
196
197    fn name(&self) -> ObsString {
198        self.name.clone()
199    }
200
201    /// Updates the settings of this output. Fails if active.
202    fn update_settings(&self, settings: ObsData) -> Result<(), ObsError> {
203        if self.is_active()? {
204            return Err(ObsError::EncoderActive);
205        }
206
207        inner_fn_update_settings!(self, libobs::obs_encoder_update, settings)
208    }
209
210    fn as_ptr(&self) -> SmartPointerSendable<*mut obs_encoder> {
211        self.encoder.clone()
212    }
213}
214
215impl ObsEncoderTrait for ObsVideoEncoder {}