1use std::{collections::HashMap, ffi::CStr, pin::Pin, sync::Arc, thread::ThreadId};
38
39use crate::{
40 data::{output::ObsOutputRef, video::ObsVideoInfo, ObsData},
41 display::{ObsDisplayCreationData, ObsDisplayRef},
42 enums::{ObsLogLevel, ObsResetVideoStatus},
43 logger::LOGGER,
44 run_with_obs,
45 runtime::{ObsRuntime, ObsRuntimeReturn},
46 scenes::ObsSceneRef,
47 sources::ObsSourceBuilder,
48 unsafe_send::Sendable,
49 utils::{
50 ObsError, ObsModules, ObsString, OutputInfo, StartupInfo,
51 },
52};
53use crate::utils::async_sync::{Mutex, RwLock};
54use getters0::Getters;
55use libobs::{audio_output, obs_scene_t, video_output};
56
57lazy_static::lazy_static! {
58 pub(crate) static ref OBS_THREAD_ID: Mutex<Option<ThreadId>> = Mutex::new(None);
59}
60
61#[derive(Debug, Getters, Clone)]
77#[skip_new]
78pub struct ObsContext {
79 startup_info: Arc<RwLock<StartupInfo>>,
83
84 #[get_mut]
85 displays: Arc<RwLock<HashMap<usize, Arc<Pin<Box<ObsDisplayRef>>>>>>,
87
88 #[allow(dead_code)]
91 #[get_mut]
92 pub(crate) outputs: Arc<RwLock<Vec<ObsOutputRef>>>,
93
94 #[get_mut]
95 pub(crate) scenes: Arc<RwLock<Vec<ObsSceneRef>>>,
96
97 #[skip_getter]
98 pub(crate) active_scene: Arc<RwLock<Option<Sendable<*mut obs_scene_t>>>>,
99
100 #[skip_getter]
101 pub(crate) _obs_modules: Arc<ObsModules>,
102
103 pub(crate) runtime: ObsRuntime,
107}
108
109#[cfg(not(feature = "bootstrapper"))]
110pub type ObsContextReturn = ObsContext;
111#[cfg(feature = "bootstrapper")]
112pub enum ObsContextReturn {
113 Done(ObsContext),
115
116 Restart,
118}
119
120impl ObsContext {
121 pub fn builder() -> StartupInfo {
122 StartupInfo::new()
123 }
124
125 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
141 pub async fn new(info: StartupInfo) -> Result<ObsContextReturn, ObsError> {
142 let runtime = ObsRuntime::startup(info).await?;
144
145 if matches!(runtime, ObsRuntimeReturn::Restart) {
146 return Ok(ObsContextReturn::Restart);
147 }
148
149 let (runtime, obs_modules, info) = match runtime {
150 ObsRuntimeReturn::Done(r) => r,
151 ObsRuntimeReturn::Restart => unreachable!(),
152 };
153
154 let context = Self {
155 _obs_modules: Arc::new(obs_modules),
156 active_scene: Default::default(),
157 displays: Default::default(),
158 outputs: Default::default(),
159 scenes: Default::default(),
160 runtime,
161 startup_info: Arc::new(RwLock::new(info)),
162 };
163
164 #[cfg(feature = "bootstrapper")]
165 return Ok(ObsContextReturn::Done(context));
166
167 #[cfg(not(feature = "bootstrapper"))]
168 return Ok(context);
169 }
170
171 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
172 pub async fn get_version(&self) -> Result<String, ObsError> {
173 let res = run_with_obs!(self.runtime, || unsafe {
174 let version = libobs::obs_get_version_string();
175 let version_cstr = CStr::from_ptr(version);
176
177 version_cstr.to_string_lossy().into_owned()
178 }).await?;
179
180 Ok(res)
181 }
182
183 pub fn log(&self, level: ObsLogLevel, msg: &str) {
184 let mut log = LOGGER.lock().unwrap();
185 log.log(level, msg.to_string());
186 }
187
188 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
203 pub async fn reset_video(&mut self, ovi: ObsVideoInfo) -> Result<(), ObsError> {
204 if self
207 .startup_info
208 .read()
209 .await
210 .obs_video_info
211 .graphics_module()
212 != ovi.graphics_module()
213 {
214 return Err(ObsError::ResetVideoFailureGraphicsModule);
215 }
216
217 let mut vid = self.startup_info.write().await;
224 let vid_ptr = Sendable(vid.obs_video_info.as_ptr());
225
226 let reset_video_status = run_with_obs!(self.runtime, (vid_ptr), move || unsafe {
227 libobs::obs_reset_video(vid_ptr)
228 }).await?;
229
230 drop(vid);
231 let reset_video_status = num_traits::FromPrimitive::from_i32(reset_video_status);
232
233 let reset_video_status = match reset_video_status {
234 Some(x) => x,
235 None => ObsResetVideoStatus::Failure,
236 };
237
238 if reset_video_status != ObsResetVideoStatus::Success {
239 return Err(ObsError::ResetVideoFailure(reset_video_status));
240 } else {
241 let outputs = self.outputs.read().await.clone();
242 let mut video_encoders = vec![];
243
244 for output in outputs.iter() {
245 let encoders = output.get_video_encoders().await;
246 video_encoders.extend(encoders.into_iter().map(|e| e.as_ptr()));
247 }
248
249 let vid_ptr = self.get_video_ptr().await?;
250 run_with_obs!(self.runtime, (vid_ptr), move || unsafe {
251 for encoder_ptr in video_encoders.into_iter() {
252 libobs::obs_encoder_set_video(encoder_ptr.0, vid_ptr);
253 }
254 }).await?;
255
256 self.startup_info.write().await.obs_video_info = ovi;
257 return Ok(());
258 }
259 }
260
261 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
262 pub async fn get_video_ptr(&self) -> Result<Sendable<*mut video_output>, ObsError> {
263 run_with_obs!(self.runtime, || unsafe {
265 Sendable(libobs::obs_get_video())
266 }).await
267 }
268
269 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
270 pub async fn get_audio_ptr(&self) -> Result<Sendable<*mut audio_output>, ObsError> {
271 run_with_obs!(self.runtime, || unsafe {
273 Sendable(libobs::obs_get_audio())
274 }).await
275 }
276
277 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
278 pub async fn data(&self) -> Result<ObsData, ObsError> {
279 ObsData::new(self.runtime.clone()).await
280 }
281
282 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
283 pub async fn output(&mut self, info: OutputInfo) -> Result<ObsOutputRef, ObsError> {
284 let output = ObsOutputRef::new(info, self.runtime.clone()).await;
285
286 return match output {
287 Ok(x) => {
288 let tmp = x.clone();
289 self.outputs.write().await.push(x);
290 Ok(tmp)
291 }
292
293 Err(x) => Err(x),
294 };
295 }
296
297 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
299 pub async fn display(
300 &mut self,
301 data: ObsDisplayCreationData,
302 ) -> Result<Pin<Box<ObsDisplayRef>>, ObsError> {
303 let display = ObsDisplayRef::new(data, self.runtime.clone())
304 .await
305 .map_err(|e| ObsError::DisplayCreationError(e.to_string()))?;
306
307 let display_clone = display.clone();
308
309 let id = display.id();
310 self.displays.write().await.insert(id, Arc::new(display));
311 Ok(display_clone)
312 }
313
314 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
315 pub async fn remove_display(&mut self, display: &ObsDisplayRef) {
316 self.remove_display_by_id(display.id()).await;
317 }
318
319 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
320 pub async fn remove_display_by_id(&mut self, id: usize) {
321 self.displays.write().await.remove(&id);
322 }
323
324 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
325 pub async fn get_display_by_id(&self, id: usize) -> Option<Arc<Pin<Box<ObsDisplayRef>>>> {
326 self.displays.read().await.get(&id).cloned()
327 }
328
329 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
330 pub async fn get_output(&mut self, name: &str) -> Option<ObsOutputRef> {
331 self.outputs
332 .read()
333 .await
334 .iter()
335 .find(|x| x.name().to_string().as_str() == name)
336 .map(|e| e.clone())
337 }
338
339 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
340 pub async fn update_output(&mut self, name: &str, settings: ObsData) -> Result<(), ObsError> {
341 match self
342 .outputs
343 .write()
344 .await
345 .iter_mut()
346 .find(|x| x.name().to_string().as_str() == name)
347 {
348 Some(output) => output.update_settings(settings).await,
349 None => Err(ObsError::OutputNotFound),
350 }
351 }
352
353 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
354 pub async fn scene<T: Into<ObsString> + Send + Sync>(
355 &mut self,
356 name: T,
357 ) -> Result<ObsSceneRef, ObsError> {
358 let scene =
359 ObsSceneRef::new(name.into(), self.active_scene.clone(), self.runtime.clone()).await?;
360
361 let tmp = scene.clone();
362 self.scenes.write().await.push(scene);
363
364 Ok(tmp)
365 }
366
367 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
368 pub async fn get_scene(&mut self, name: &str) -> Option<ObsSceneRef> {
369 self.scenes
370 .read()
371 .await
372 .iter()
373 .find(|x| x.name().to_string().as_str() == name)
374 .map(|e| e.clone())
375 }
376
377 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
378 pub async fn source_builder<T: ObsSourceBuilder, K: Into<ObsString> + Send + Sync>(
379 &self,
380 name: K,
381 ) -> Result<T, ObsError> {
382 T::new(name.into(), self.runtime.clone()).await
383 }
384}