Skip to main content

libobs_wrapper\data/
video.rs

1use std::{boxed::Box, fmt::Debug, pin::Pin};
2
3use display_info::DisplayInfo;
4use libobs::obs_video_info;
5
6#[cfg(target_os = "linux")]
7use crate::utils::linux::get_linux_opengl_lib_name;
8use crate::{
9    enums::{
10        ObsColorspace, ObsGraphicsModule, ObsScaleType, ObsVideoFormat, ObsVideoRange, OsEnumType,
11    },
12    unsafe_send::Sendable,
13    utils::ObsString,
14};
15
16#[derive(Clone, Debug)]
17pub struct ObsSdrVideoInfo {
18    /// The white level in nits
19    pub sdr_white_level: f32,
20    /// The nominal peak level in nits
21    pub hdr_nominal_peak_level: f32,
22}
23
24impl Default for ObsSdrVideoInfo {
25    fn default() -> Self {
26        Self {
27            sdr_white_level: 300.0,
28            hdr_nominal_peak_level: 1000.0,
29        }
30    }
31}
32
33/// A wrapper for `obs_video_info`, which is used
34/// to pass information to libobs for the new OBS
35/// video context after resetting the old OBS
36/// video context. The obs_video_info is pinned in memory
37/// to ensure its address never changes, as required by libobs.
38pub struct ObsVideoInfo {
39    ovi: Sendable<Pin<Box<obs_video_info>>>,
40    // False positive. This is necessary to ensure
41    // that the graphics module string in the
42    // `obs_video_info` struct does not free.
43    #[allow(dead_code)]
44    graphics_module: ObsString,
45
46    sdr_info: ObsSdrVideoInfo,
47}
48
49impl Debug for ObsVideoInfo {
50    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51        f.debug_struct("ObsVideoInfo")
52            .field("fps_num", &self.get_fps_num())
53            .field("fps_den", &self.get_fps_den())
54            .field("base_width", &self.get_base_width())
55            .field("base_height", &self.get_base_height())
56            .field("output_width", &self.get_output_width())
57            .field("output_height", &self.get_output_height())
58            .field("sdr_info", &self.get_sdr_info())
59            .finish()
60    }
61}
62
63impl ObsVideoInfo {
64    /// Creates a new `ObsVideoInfo`.
65    ///
66    /// Note that this function is not meant to
67    /// be used externally. The recommended,
68    /// supported way to build new `ObsVideoInfo`
69    /// structs is through `ObsVideoInfoBuilder`.
70    #[deprecated = "Use new_with_sdr_info or the ObsVideoInfoBuilder instead"]
71    pub fn new(ovi: obs_video_info, graphics_module: ObsString) -> Self {
72        Self {
73            ovi: Sendable(Box::pin(ovi)),
74            graphics_module,
75            sdr_info: ObsSdrVideoInfo::default(),
76        }
77    }
78
79    /// Creates a new `ObsVideoInfo`.
80    ///
81    /// Note that this function is not meant to
82    /// be used externally. The recommended,
83    /// supported way to build new `ObsVideoInfo`
84    /// structs is through `ObsVideoInfoBuilder`.
85    pub fn new_with_sdr_info(
86        ovi: obs_video_info,
87        graphics_module: ObsString,
88        sdr_info: ObsSdrVideoInfo,
89    ) -> Self {
90        Self {
91            ovi: Sendable(Box::pin(ovi)),
92            graphics_module,
93            sdr_info,
94        }
95    }
96
97    /// Returns a pointer to the pinned `obs_video_info`.
98    pub(crate) fn as_ptr(&self) -> *mut obs_video_info {
99        // Safe because ovi is pinned for the lifetime of this struct
100        let ptr: *const obs_video_info = &*Pin::as_ref(&self.ovi.0);
101        ptr as *mut obs_video_info
102    }
103
104    pub fn graphics_module(&self) -> &ObsString {
105        &self.graphics_module
106    }
107
108    pub fn get_fps_num(&self) -> u32 {
109        self.ovi.0.fps_num
110    }
111
112    pub fn get_fps_den(&self) -> u32 {
113        self.ovi.0.fps_den
114    }
115
116    pub fn get_base_width(&self) -> u32 {
117        self.ovi.0.base_width
118    }
119
120    pub fn get_base_height(&self) -> u32 {
121        self.ovi.0.base_height
122    }
123
124    pub fn get_output_width(&self) -> u32 {
125        self.ovi.0.output_width
126    }
127
128    pub fn get_output_height(&self) -> u32 {
129        self.ovi.0.output_height
130    }
131
132    pub fn get_sdr_info(&self) -> &ObsSdrVideoInfo {
133        &self.sdr_info
134    }
135}
136
137impl Default for ObsVideoInfo {
138    fn default() -> Self {
139        ObsVideoInfoBuilder::new().build()
140    }
141}
142
143/// A structure intended to help make
144/// creating new `ObsVideoInfo` structs
145/// easier for resetting the OBS video
146/// context.
147#[derive(Clone, Debug)]
148pub struct ObsVideoInfoBuilder {
149    adapter: u32,
150    graphics_module: ObsGraphicsModule,
151    fps_num: u32,
152    fps_den: u32,
153    base_width: u32,
154    base_height: u32,
155    output_width: u32,
156    output_height: u32,
157    output_format: ObsVideoFormat,
158    gpu_conversion: bool,
159    colorspace: ObsColorspace,
160    range: ObsVideoRange,
161    scale_type: ObsScaleType,
162    sdr_info: ObsSdrVideoInfo,
163}
164
165impl ObsVideoInfoBuilder {
166    /// Creates a new `ObsVideoInfoBuilder`
167    /// for creating new `ObsVideoInfo` to
168    /// pass to the video context reset
169    /// function.
170    ///
171    /// This function comes with
172    /// sensible default values and chooses
173    /// the backend for the current platform: DX11 on Windows, Metal on
174    /// Apple Silicon macOS, and OpenGL on Intel macOS and other Unix systems.
175    pub fn new() -> Self {
176        let display_infos = DisplayInfo::all().unwrap_or_default();
177        let (mut width, mut height) = (1920, 1080);
178        for display_info in display_infos {
179            if display_info.is_primary {
180                width = display_info.width;
181                height = display_info.height;
182                break;
183            }
184        }
185
186        Self {
187            adapter: 0,
188            #[cfg(all(target_os = "macos", target_arch = "aarch64"))]
189            graphics_module: ObsGraphicsModule::Metal,
190            #[cfg(all(target_os = "macos", not(target_arch = "aarch64")))]
191            graphics_module: ObsGraphicsModule::OpenGL,
192            #[cfg(all(target_family = "unix", not(target_os = "macos")))]
193            graphics_module: ObsGraphicsModule::OpenGL,
194            #[cfg(target_family = "windows")]
195            graphics_module: ObsGraphicsModule::DirectX11,
196            fps_num: 30,
197            fps_den: 1,
198            base_width: width,
199            base_height: height,
200            output_width: width,
201            output_height: height,
202            output_format: ObsVideoFormat::NV12,
203            gpu_conversion: true,
204            colorspace: ObsColorspace::CS709,
205            range: ObsVideoRange::Default,
206            scale_type: ObsScaleType::Lanczos,
207            sdr_info: ObsSdrVideoInfo::default(),
208        }
209    }
210
211    /// Consumes the `ObsVideoInfoBuilder`
212    /// to create an `ObsVideoInfo`.
213    pub fn build(self) -> ObsVideoInfo {
214        let graphics_mod_str = match self.graphics_module {
215            #[cfg(target_os = "macos")]
216            ObsGraphicsModule::OpenGL => ObsString::new("libobs-opengl.dylib"),
217            #[cfg(target_os = "linux")]
218            ObsGraphicsModule::OpenGL => ObsString::new(get_linux_opengl_lib_name()),
219            #[cfg(target_os = "windows")]
220            ObsGraphicsModule::OpenGL => ObsString::new("libobs-opengl"),
221            ObsGraphicsModule::DirectX11 => ObsString::new("libobs-d3d11.dll"),
222            ObsGraphicsModule::Metal => ObsString::new("libobs-metal.dylib"),
223        };
224
225        let ovi = obs_video_info {
226            adapter: self.adapter,
227            graphics_module: graphics_mod_str.as_ptr().0,
228            fps_num: self.fps_num,
229            fps_den: self.fps_den,
230            base_width: self.base_width,
231            base_height: self.base_height,
232            output_width: self.output_width,
233            output_height: self.output_height,
234            output_format: self.output_format as OsEnumType,
235            gpu_conversion: self.gpu_conversion,
236            colorspace: self.colorspace as OsEnumType,
237            range: self.range as OsEnumType,
238            scale_type: self.scale_type as OsEnumType,
239        };
240
241        ObsVideoInfo {
242            ovi: Sendable(Box::pin(ovi)),
243            graphics_module: graphics_mod_str,
244            sdr_info: self.sdr_info,
245        }
246    }
247
248    pub fn set_sdr_info(mut self, sdr_info: ObsSdrVideoInfo) -> Self {
249        self.sdr_info = sdr_info;
250        self
251    }
252
253    /// Sets the GPU adapter device
254    /// that the video output is coming
255    /// from.
256    pub fn adapter(mut self, value: u32) -> Self {
257        self.adapter = value;
258        self
259    }
260
261    /// Sets the graphics backend
262    /// that libobs uses to record.
263    pub fn graphics_module(mut self, value: ObsGraphicsModule) -> Self {
264        self.graphics_module = value;
265        self
266    }
267
268    /// Sets the framerate of the
269    /// output video. Note that this
270    /// value may not reflect the
271    /// final framerate if `fps_den`
272    /// is not equal to 1.
273    pub fn fps_num(mut self, value: u32) -> Self {
274        self.fps_num = value;
275        self
276    }
277
278    /// Divides the FPS numerator to
279    /// allow for fractional FPS
280    /// counts on output.
281    pub fn fps_den(mut self, value: u32) -> Self {
282        self.fps_den = value;
283        self
284    }
285
286    /// Sets the width of the screen
287    /// being recorded.
288    pub fn base_width(mut self, value: u32) -> Self {
289        self.base_width = value;
290        self
291    }
292
293    /// Sets the height of the screen
294    /// being recorded.
295    pub fn base_height(mut self, value: u32) -> Self {
296        self.base_height = value;
297        self
298    }
299
300    /// Sets the width of the video
301    /// output.
302    pub fn output_width(mut self, value: u32) -> Self {
303        self.output_width = value;
304        self
305    }
306
307    /// Sets the height of the video
308    /// output.
309    pub fn output_height(mut self, value: u32) -> Self {
310        self.output_height = value;
311        self
312    }
313
314    /// Sets the format in which the
315    /// video will be output.
316    pub fn output_format(mut self, value: ObsVideoFormat) -> Self {
317        self.output_format = value;
318        self
319    }
320
321    /// Sets whether the GPU will handle
322    /// conversion in the video.
323    pub fn gpu_conversion(mut self, value: bool) -> Self {
324        self.gpu_conversion = value;
325        self
326    }
327
328    /// Sets the video colorspace.
329    pub fn colorspace(mut self, value: ObsColorspace) -> Self {
330        self.colorspace = value;
331        self
332    }
333
334    /// Sets the video range.
335    pub fn range(mut self, value: ObsVideoRange) -> Self {
336        self.range = value;
337        self
338    }
339
340    /// Sets the video scaling type.
341    pub fn scale_type(mut self, value: ObsScaleType) -> Self {
342        self.scale_type = value;
343        self
344    }
345}
346
347#[cfg_attr(coverage_nightly, coverage(off))]
348impl Default for ObsVideoInfoBuilder {
349    fn default() -> Self {
350        Self::new()
351    }
352}