1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use paste::item;

use std::marker::PhantomData;

use obs_sys::{obs_output_info, OBS_OUTPUT_AUDIO, OBS_OUTPUT_VIDEO};

pub mod context;
mod ffi;
pub mod traits;

pub use context::*;
pub use traits::*;

pub struct OutputInfo {
    info: Box<obs_output_info>,
}

impl OutputInfo {
    /// # Safety
    /// Creates a raw pointer from a box and could cause UB is misused.
    pub unsafe fn into_raw(self) -> *mut obs_output_info {
        Box::into_raw(self.info)
    }
}

impl AsRef<obs_output_info> for OutputInfo {
    fn as_ref(&self) -> &obs_output_info {
        self.info.as_ref()
    }
}

/// The OutputInfoBuilder that handles creating the [OutputInfo](https://obsproject.com/docs/reference-outputs.html#c.obs_output_info) object.
///
/// For each trait that is implemented for the Output, it needs to be enabled
/// using this builder. If an struct called `FocusFilter` implements
/// `CreateOutput` and `GetNameOutput` it would need to enable those features.
///
/// ```rs
/// let output = load_context
///  .create_output_builder::<FocusFilter>()
///  .enable_get_name()
///  .enable_create()
///  .build();
/// ```
pub struct OutputInfoBuilder<D: Outputable> {
    __data: PhantomData<D>,
    info: obs_output_info,
}

impl<D: Outputable> OutputInfoBuilder<D> {
    pub(crate) fn new() -> Self {
        Self {
            __data: PhantomData,
            info: obs_output_info {
                id: D::get_id().as_ptr(),
                create: Some(ffi::create::<D>),
                destroy: Some(ffi::destroy::<D>),
                start: Some(ffi::start::<D>),
                stop: Some(ffi::stop::<D>),
                type_data: std::ptr::null_mut(),
                ..Default::default()
            },
        }
    }

    pub fn build(mut self) -> OutputInfo {
        if self.info.raw_video.is_some() {
            self.info.flags |= OBS_OUTPUT_VIDEO;
        }

        if self.info.raw_audio.is_some() || self.info.raw_audio2.is_some() {
            self.info.flags |= OBS_OUTPUT_AUDIO;
        }

        OutputInfo {
            info: Box::new(self.info),
        }
    }
}

macro_rules! impl_output_builder {
    ($($f:ident => $t:ident)*) => ($(
        item! {
            impl<D: Outputable + [<$t>]> OutputInfoBuilder<D> {
                pub fn [<enable_$f>](mut self) -> Self {
                    self.info.[<$f>] = Some(ffi::[<$f>]::<D>);
                    self
                }
            }
        }
    )*)
}

impl_output_builder! {
    get_name => GetNameOutput
    // this two is required
    // start => StartOutput
    // stop => StopOutput
    raw_video => RawVideoOutput
    raw_audio => RawAudioOutput
    raw_audio2 => RawAudio2Output
    encoded_packet => EncodedPacketOutput
    update => UpdateOutput
    get_defaults => GetDefaultsOutput
    // TODO: version?
    // get_defaults2 => GetDefaults2Output
    get_properties => GetPropertiesOutput
    // get_properties2
    // unused1
    get_total_bytes => GetTotalBytesOutput
    get_dropped_frames => GetDroppedFramesOutput
    // type_data
    // free_type_data
    get_congestion => GetCongestionOutput
    get_connect_time_ms => GetConnectTimeMsOutput
}