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 runtime.native_registry(),
115 );
116
117 let settings = {
119 let default_settings_ptr = run_with_obs!(runtime, (source_ptr), move || {
120 unsafe {
121 Sendable(libobs::obs_source_get_settings(source_ptr.get_ptr()))
123 }
124 })?;
125
126 ImmutableObsData::from_raw_pointer(default_settings_ptr, runtime.clone())
127 };
128
129 let signals = ObsSourceSignals::new(&source_ptr, runtime.clone())?;
130 Ok(Self {
131 source: source_ptr.clone(),
132 id,
133 name,
134 settings: Arc::new(RwLock::new(settings)),
135 hotkey_data: Arc::new(RwLock::new(hotkey_data)),
136 attached_filters: Arc::new(RwLock::new(Vec::new())),
137 runtime,
138 signal_manager: Arc::new(signals),
139 })
140 }
141}
142
143impl ObsObjectTraitPrivate for ObsSourceRef {
144 fn __internal_replace_settings(&self, settings: ImmutableObsData) -> Result<(), ObsError> {
145 let mut guard = self
146 .settings
147 .write()
148 .map_err(|_| ObsError::LockError("Failed to acquire write lock on settings".into()))?;
149
150 *guard = settings;
151 Ok(())
152 }
153
154 fn __internal_replace_hotkey_data(
155 &self,
156 hotkey_data: ImmutableObsData,
157 ) -> Result<(), ObsError> {
158 let mut guard = self.hotkey_data.write().map_err(|_| {
159 ObsError::LockError("Failed to acquire write lock on hotkey data".into())
160 })?;
161
162 *guard = hotkey_data;
163 Ok(())
164 }
165}
166
167impl ObsObjectTrait for ObsSourceRef {
168 type Native = *mut libobs::obs_source_t;
169
170 fn __native_handle(&self) -> SmartPointerSendable<Self::Native> {
171 self.source.clone()
172 }
173
174 fn runtime(&self) -> &ObsRuntime {
175 &self.runtime
176 }
177
178 fn settings(&self) -> Result<ImmutableObsData, ObsError> {
179 let res = self
180 .settings
181 .read()
182 .map_err(|_| ObsError::LockError("Failed to acquire read lock on settings".into()))?
183 .clone();
184
185 Ok(res)
186 }
187
188 fn hotkey_data(&self) -> Result<ImmutableObsData, ObsError> {
189 let res = self
190 .hotkey_data
191 .read()
192 .map_err(|_| ObsError::LockError("Failed to acquire read lock on hotkey data".into()))?
193 .clone();
194
195 Ok(res)
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 fn update_settings(&self, settings: crate::data::ObsData) -> Result<(), ObsError> {
207 inner_fn_update_settings!(self, libobs::obs_source_update, settings)
208 }
209}
210
211impl ObsSourceTrait for ObsSourceRef {
212 fn signals(&self) -> &Arc<ObsSourceSignals> {
213 &self.signal_manager
214 }
215
216 fn get_active_filters(&self) -> Result<Vec<ObsFilterGuardPair>, ObsError> {
217 let guard = self.attached_filters.read().map_err(|_| {
218 ObsError::LockError("Failed to acquire read lock on attached filters".into())
219 })?;
220
221 Ok(guard.clone())
222 }
223
224 fn apply_filter(&self, filter: &ObsFilterRef) -> Result<(), ObsError> {
225 self.runtime.ensure_same_runtime(filter.runtime())?;
226 let mut guard = self.attached_filters.write().map_err(|_| {
227 ObsError::LockError("Failed to acquire write lock on attached filters".into())
228 })?;
229
230 let source_ptr = self.__native_handle();
231 let filter_ptr = filter.__native_handle();
232
233 let has_filter = guard.iter().any(|f| {
234 f.get_inner().__native_handle().native_id() == filter.__native_handle().native_id()
235 });
236
237 if has_filter {
238 return Err(ObsError::FilterAlreadyApplied);
239 }
240
241 run_with_obs!(self.runtime(), (source_ptr, filter_ptr), move || unsafe {
242 libobs::obs_source_filter_add(source_ptr.get_ptr(), filter_ptr.get_ptr());
244 Ok(())
245 })??;
246
247 let runtime = self.runtime().clone();
248 let drop_guard = _ObsRemoveFilterOnDrop::new(
249 self.__native_handle(),
250 filter.__native_handle(),
251 None,
252 runtime,
253 );
254
255 guard.push(ObsFilterGuardPair::new(
256 filter.clone(),
257 Arc::new(drop_guard),
258 ));
259
260 Ok(())
261 }
262}
263
264impl_signal_manager!(|ptr: SmartPointerSendable<*mut libobs::obs_source_t>| unsafe {
265 libobs::obs_source_get_signal_handler(ptr.get_ptr())
267}, ObsSourceSignals for *mut libobs::obs_source_t, [
268 "destroy": {},
269 "remove": {},
270 "update": {},
271 "save": {},
272 "load": {},
273 "activate": {},
274 "deactivate": {},
275 "show": {},
276 "hide": {},
277 "mute": { struct MuteSignal {
278 muted: bool
279 } },
280 "push_to_mute_changed": {struct PushToMuteChangedSignal {
281 enabled: bool
282 }},
283 "push_to_mute_delay": {struct PushToMuteDelaySignal {
284 delay: i64
285 }},
286 "push_to_talk_changed": {struct PushToTalkChangedSignal {
287 enabled: bool
288 }},
289 "push_to_talk_delay": {struct PushToTalkDelaySignal {
290 delay: i64
291 }},
292 "enable": {struct EnableSignal {
293 enabled: bool
294 }},
295 "rename": {struct NewNameSignal {
296 new_name: String,
297 prev_name: String
298 }},
299 "update_properties": {},
300 "update_flags": {struct UpdateFlagsSignal {
301 flags: i64
302 }},
303 "audio_sync": {struct AudioSyncSignal {
304 offset: i64,
305 }},
306 "audio_balance": {struct AudioBalanceSignal {
307 balance: f64,
308 }},
309 "audio_mixers": {struct AudioMixersSignal {
310 mixers: i64,
311 }},
312 "audio_activate": {},
313 "audio_deactivate": {},
314 "filter_add": {struct FilterAddSignal {
315 POINTERS {
316 filter: *mut libobs::obs_source_t,
317 }
318 }},
319 "filter_remove": {struct FilterRemoveSignal {
320 POINTERS {
321 filter: *mut libobs::obs_source_t,
322 }
323 }},
324 "reorder_filters": {},
325 "transition_start": {},
326 "transition_video_stop": {},
327 "transition_stop": {},
328 "media_started": {},
329 "media_ended":{},
330 "media_pause": {},
331 "media_play": {},
332 "media_restart": {},
333 "media_stopped": {},
334 "media_next": {},
335 "media_previous": {},
336]);
337
338#[derive(Debug)]
339struct _ObsSourceGuard {
340 source: Sendable<*mut obs_source_t>,
341 runtime: ObsRuntime,
342}
343
344impl ObsDropGuard for _ObsSourceGuard {}
345
346impl_obs_drop!(_ObsSourceGuard, (source), move || unsafe {
347 libobs::obs_source_release(source.0);
349});