1mod builder;
5pub use builder::*;
6
7mod traits;
8pub use traits::*;
9
10mod macros;
11
12mod filter;
13pub use filter::*;
14
15use libobs::obs_source_t;
16
17use crate::{
18 data::{
19 object::{inner_fn_update_settings, ObsObjectTrait, ObsObjectTraitPrivate},
20 ImmutableObsData, ObsDataPointers,
21 },
22 impl_obs_drop, impl_signal_manager, run_with_obs,
23 runtime::ObsRuntime,
24 unsafe_send::{Sendable, SmartPointerSendable},
25 utils::{ObsDropGuard, ObsError, ObsString, SourceInfo},
26};
27
28use std::sync::{Arc, RwLock};
29
30#[derive(Debug, Clone)]
31#[allow(dead_code)]
32pub struct ObsSourceRef {
33 signal_manager: Arc<ObsSourceSignals>,
35
36 id: ObsString,
37 name: ObsString,
38 settings: Arc<RwLock<ImmutableObsData>>,
39 hotkey_data: Arc<RwLock<ImmutableObsData>>,
40
41 attached_filters: Arc<RwLock<Vec<ObsFilterGuardPair>>>,
42
43 runtime: ObsRuntime,
44 source: SmartPointerSendable<*mut obs_source_t>,
45}
46
47impl ObsSourceRef {
48 pub fn new_from_info(info: SourceInfo, runtime: ObsRuntime) -> Result<Self, ObsError> {
49 let SourceInfo {
50 id,
51 name,
52 settings,
53 hotkey_data,
54 } = info;
55
56 Self::new(id, name, settings, hotkey_data, runtime)
57 }
58
59 pub fn new<T: Into<ObsString> + Sync + Send, K: Into<ObsString> + Sync + Send>(
60 id: T,
61 name: K,
62 settings: Option<ImmutableObsData>,
63 hotkey_data: Option<ImmutableObsData>,
64 runtime: ObsRuntime,
65 ) -> Result<Self, ObsError> {
66 let id = id.into();
67 let name = name.into();
68
69 let hotkey_data = match hotkey_data {
71 Some(x) => x,
72 None => ImmutableObsData::new(&runtime)?,
73 };
74
75 let hotkey_data_ptr = hotkey_data.as_ptr();
76 let settings_ptr = settings.map(|x| x.as_ptr());
77
78 let source_ptr = run_with_obs!(
79 runtime,
80 (hotkey_data_ptr, settings_ptr, id, name),
81 move || {
82 let id_ptr = id.as_ptr().0;
83 let name_ptr = name.as_ptr().0;
84
85 let settings_raw_ptr = match settings_ptr {
86 Some(s) => s.get_ptr(),
87 None => std::ptr::null_mut(),
88 };
89
90 let source_ptr = unsafe {
91 libobs::obs_source_create(
93 id_ptr,
94 name_ptr,
95 settings_raw_ptr,
96 hotkey_data_ptr.get_ptr(),
97 )
98 };
99
100 if source_ptr.is_null() {
101 Err(ObsError::NullPointer(None))
102 } else {
103 Ok(Sendable(source_ptr))
104 }
105 }
106 )??;
107
108 let source_ptr = SmartPointerSendable::new(
109 source_ptr.0,
110 Arc::new(_ObsSourceGuard {
111 source: source_ptr.clone(),
112 runtime: runtime.clone(),
113 }),
114 );
115
116 let settings = {
118 let default_settings_ptr = run_with_obs!(runtime, (source_ptr), move || {
119 unsafe {
120 Sendable(libobs::obs_source_get_settings(source_ptr.get_ptr()))
122 }
123 })?;
124
125 ImmutableObsData::from_raw_pointer(default_settings_ptr, runtime.clone())
126 };
127
128 let signals = ObsSourceSignals::new(&source_ptr, runtime.clone())?;
129 Ok(Self {
130 source: source_ptr.clone(),
131 id,
132 name,
133 settings: Arc::new(RwLock::new(settings)),
134 hotkey_data: Arc::new(RwLock::new(hotkey_data)),
135 attached_filters: Arc::new(RwLock::new(Vec::new())),
136 runtime,
137 signal_manager: Arc::new(signals),
138 })
139 }
140}
141
142impl ObsObjectTraitPrivate for ObsSourceRef {
143 fn __internal_replace_settings(&self, settings: ImmutableObsData) -> Result<(), ObsError> {
144 let mut guard = self
145 .settings
146 .write()
147 .map_err(|_| ObsError::LockError("Failed to acquire write lock on settings".into()))?;
148
149 *guard = settings;
150 Ok(())
151 }
152
153 fn __internal_replace_hotkey_data(
154 &self,
155 hotkey_data: ImmutableObsData,
156 ) -> Result<(), ObsError> {
157 let mut guard = self.hotkey_data.write().map_err(|_| {
158 ObsError::LockError("Failed to acquire write lock on hotkey data".into())
159 })?;
160
161 *guard = hotkey_data;
162 Ok(())
163 }
164}
165
166impl ObsObjectTrait<*mut libobs::obs_source_t> for ObsSourceRef {
167 fn runtime(&self) -> &ObsRuntime {
168 &self.runtime
169 }
170
171 fn settings(&self) -> Result<ImmutableObsData, ObsError> {
172 let res = self
173 .settings
174 .read()
175 .map_err(|_| ObsError::LockError("Failed to acquire read lock on settings".into()))?
176 .clone();
177
178 Ok(res)
179 }
180
181 fn hotkey_data(&self) -> Result<ImmutableObsData, ObsError> {
182 let res = self
183 .hotkey_data
184 .read()
185 .map_err(|_| ObsError::LockError("Failed to acquire read lock on hotkey data".into()))?
186 .clone();
187
188 Ok(res)
189 }
190
191 fn id(&self) -> ObsString {
192 self.id.clone()
193 }
194
195 fn name(&self) -> ObsString {
196 self.name.clone()
197 }
198
199 fn update_settings(&self, settings: crate::data::ObsData) -> Result<(), ObsError> {
200 inner_fn_update_settings!(self, libobs::obs_source_update, settings)
201 }
202
203 fn as_ptr(&self) -> SmartPointerSendable<*mut libobs::obs_source_t> {
204 self.source.clone()
205 }
206}
207
208impl ObsSourceTrait for ObsSourceRef {
209 fn signals(&self) -> &Arc<ObsSourceSignals> {
210 &self.signal_manager
211 }
212
213 fn get_active_filters(&self) -> Result<Vec<ObsFilterGuardPair>, ObsError> {
214 let guard = self.attached_filters.read().map_err(|_| {
215 ObsError::LockError("Failed to acquire read lock on attached filters".into())
216 })?;
217
218 Ok(guard.clone())
219 }
220
221 fn apply_filter(&self, filter: &ObsFilterRef) -> Result<(), ObsError> {
222 let mut guard = self.attached_filters.write().map_err(|_| {
223 ObsError::LockError("Failed to acquire write lock on attached filters".into())
224 })?;
225
226 let source_ptr = self.as_ptr();
227 let filter_ptr = filter.as_ptr();
228
229 let has_filter = guard
230 .iter()
231 .any(|f| f.get_inner().as_ptr().get_ptr() == filter.as_ptr().get_ptr());
232
233 if has_filter {
234 return Err(ObsError::FilterAlreadyApplied);
235 }
236
237 run_with_obs!(self.runtime(), (source_ptr, filter_ptr), move || unsafe {
238 libobs::obs_source_filter_add(source_ptr.get_ptr(), filter_ptr.get_ptr());
240 Ok(())
241 })??;
242
243 let runtime = self.runtime().clone();
244 let drop_guard = _ObsRemoveFilterOnDrop::new(self.as_ptr(), filter.as_ptr(), None, runtime);
245
246 guard.push(ObsFilterGuardPair::new(
247 filter.clone(),
248 Arc::new(drop_guard),
249 ));
250
251 Ok(())
252 }
253}
254
255impl_signal_manager!(|ptr: SmartPointerSendable<*mut libobs::obs_source_t>| unsafe {
256 libobs::obs_source_get_signal_handler(ptr.get_ptr())
258}, ObsSourceSignals for *mut libobs::obs_source_t, [
259 "destroy": {},
260 "remove": {},
261 "update": {},
262 "save": {},
263 "load": {},
264 "activate": {},
265 "deactivate": {},
266 "show": {},
267 "hide": {},
268 "mute": { struct MuteSignal {
269 muted: bool
270 } },
271 "push_to_mute_changed": {struct PushToMuteChangedSignal {
272 enabled: bool
273 }},
274 "push_to_mute_delay": {struct PushToMuteDelaySignal {
275 delay: i64
276 }},
277 "push_to_talk_changed": {struct PushToTalkChangedSignal {
278 enabled: bool
279 }},
280 "push_to_talk_delay": {struct PushToTalkDelaySignal {
281 delay: i64
282 }},
283 "enable": {struct EnableSignal {
284 enabled: bool
285 }},
286 "rename": {struct NewNameSignal {
287 new_name: String,
288 prev_name: String
289 }},
290 "update_properties": {},
291 "update_flags": {struct UpdateFlagsSignal {
292 flags: i64
293 }},
294 "audio_sync": {struct AudioSyncSignal {
295 offset: i64,
296 }},
297 "audio_balance": {struct AudioBalanceSignal {
298 balance: f64,
299 }},
300 "audio_mixers": {struct AudioMixersSignal {
301 mixers: i64,
302 }},
303 "audio_activate": {},
304 "audio_deactivate": {},
305 "filter_add": {struct FilterAddSignal {
306 POINTERS {
307 filter: *mut libobs::obs_source_t,
308 }
309 }},
310 "filter_remove": {struct FilterRemoveSignal {
311 POINTERS {
312 filter: *mut libobs::obs_source_t,
313 }
314 }},
315 "reorder_filters": {},
316 "transition_start": {},
317 "transition_video_stop": {},
318 "transition_stop": {},
319 "media_started": {},
320 "media_ended":{},
321 "media_pause": {},
322 "media_play": {},
323 "media_restart": {},
324 "media_stopped": {},
325 "media_next": {},
326 "media_previous": {},
327]);
328
329#[derive(Debug)]
330struct _ObsSourceGuard {
331 source: Sendable<*mut obs_source_t>,
332 runtime: ObsRuntime,
333}
334
335impl ObsDropGuard for _ObsSourceGuard {}
336
337impl_obs_drop!(_ObsSourceGuard, (source), move || unsafe {
338 libobs::obs_source_release(source.0);
340});