obs_wrapper/output/
mod.rs

1use paste::item;
2
3use std::marker::PhantomData;
4
5use obs_sys::{obs_output_info, OBS_OUTPUT_AUDIO, OBS_OUTPUT_VIDEO};
6
7pub mod context;
8mod ffi;
9pub mod traits;
10
11pub use context::*;
12pub use traits::*;
13
14pub struct OutputInfo {
15    info: Box<obs_output_info>,
16}
17
18impl OutputInfo {
19    /// # Safety
20    /// Creates a raw pointer from a box and could cause UB is misused.
21    pub unsafe fn into_raw(self) -> *mut obs_output_info {
22        Box::into_raw(self.info)
23    }
24}
25
26impl AsRef<obs_output_info> for OutputInfo {
27    fn as_ref(&self) -> &obs_output_info {
28        self.info.as_ref()
29    }
30}
31
32/// The OutputInfoBuilder that handles creating the [OutputInfo](https://obsproject.com/docs/reference-outputs.html#c.obs_output_info) object.
33///
34/// For each trait that is implemented for the Output, it needs to be enabled
35/// using this builder. If an struct called `FocusFilter` implements
36/// `CreateOutput` and `GetNameOutput` it would need to enable those features.
37///
38/// ```rs
39/// let output = load_context
40///  .create_output_builder::<FocusFilter>()
41///  .enable_get_name()
42///  .enable_create()
43///  .build();
44/// ```
45pub struct OutputInfoBuilder<D: Outputable> {
46    __data: PhantomData<D>,
47    info: obs_output_info,
48}
49
50impl<D: Outputable> OutputInfoBuilder<D> {
51    pub(crate) fn new() -> Self {
52        Self {
53            __data: PhantomData,
54            info: obs_output_info {
55                id: D::get_id().as_ptr(),
56                create: Some(ffi::create::<D>),
57                destroy: Some(ffi::destroy::<D>),
58                start: Some(ffi::start::<D>),
59                stop: Some(ffi::stop::<D>),
60                type_data: std::ptr::null_mut(),
61                ..Default::default()
62            },
63        }
64    }
65
66    pub fn build(mut self) -> OutputInfo {
67        if self.info.raw_video.is_some() {
68            self.info.flags |= OBS_OUTPUT_VIDEO;
69        }
70
71        if self.info.raw_audio.is_some() || self.info.raw_audio2.is_some() {
72            self.info.flags |= OBS_OUTPUT_AUDIO;
73        }
74
75        OutputInfo {
76            info: Box::new(self.info),
77        }
78    }
79}
80
81macro_rules! impl_output_builder {
82    ($($f:ident => $t:ident)*) => ($(
83        item! {
84            impl<D: Outputable + [<$t>]> OutputInfoBuilder<D> {
85                pub fn [<enable_$f>](mut self) -> Self {
86                    self.info.[<$f>] = Some(ffi::[<$f>]::<D>);
87                    self
88                }
89            }
90        }
91    )*)
92}
93
94impl_output_builder! {
95    get_name => GetNameOutput
96    // this two is required
97    // start => StartOutput
98    // stop => StopOutput
99    raw_video => RawVideoOutput
100    raw_audio => RawAudioOutput
101    raw_audio2 => RawAudio2Output
102    encoded_packet => EncodedPacketOutput
103    update => UpdateOutput
104    get_defaults => GetDefaultsOutput
105    // TODO: version?
106    // get_defaults2 => GetDefaults2Output
107    get_properties => GetPropertiesOutput
108    // get_properties2
109    // unused1
110    get_total_bytes => GetTotalBytesOutput
111    get_dropped_frames => GetDroppedFramesOutput
112    // type_data
113    // free_type_data
114    get_congestion => GetCongestionOutput
115    get_connect_time_ms => GetConnectTimeMsOutput
116}