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 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 depending on which
174    /// if the OS supports DX11 (Windows)
175    /// or not (OpenGL on MacOS and Unix).
176    pub fn new() -> Self {
177        let display_infos = DisplayInfo::all().unwrap_or_default();
178        let (mut width, mut height) = (1920, 1080);
179        for display_info in display_infos {
180            if display_info.is_primary {
181                width = display_info.width;
182                height = display_info.height;
183                break;
184            }
185        }
186
187        Self {
188            adapter: 0,
189            #[cfg(target_family = "unix")]
190            graphics_module: ObsGraphicsModule::OpenGL,
191            #[cfg(target_family = "windows")]
192            graphics_module: ObsGraphicsModule::DirectX11,
193            fps_num: 30,
194            fps_den: 1,
195            base_width: width,
196            base_height: height,
197            output_width: width,
198            output_height: height,
199            output_format: ObsVideoFormat::NV12,
200            gpu_conversion: true,
201            colorspace: ObsColorspace::CS709,
202            range: ObsVideoRange::Default,
203            scale_type: ObsScaleType::Lanczos,
204            sdr_info: ObsSdrVideoInfo::default(),
205        }
206    }
207
208    /// Consumes the `ObsVideoInfoBuilder`
209    /// to create an `ObsVideoInfo`.
210    pub fn build(self) -> ObsVideoInfo {
211        let graphics_mod_str = match self.graphics_module {
212            #[cfg(not(target_os = "linux"))]
213            ObsGraphicsModule::OpenGL => ObsString::new("libobs-opengl"),
214            #[cfg(target_os = "linux")]
215            ObsGraphicsModule::OpenGL => ObsString::new(get_linux_opengl_lib_name()),
216            ObsGraphicsModule::DirectX11 => ObsString::new("libobs-d3d11.dll"),
217        };
218
219        let ovi = obs_video_info {
220            adapter: self.adapter,
221            graphics_module: graphics_mod_str.as_ptr().0,
222            fps_num: self.fps_num,
223            fps_den: self.fps_den,
224            base_width: self.base_width,
225            base_height: self.base_height,
226            output_width: self.output_width,
227            output_height: self.output_height,
228            output_format: self.output_format as OsEnumType,
229            gpu_conversion: self.gpu_conversion,
230            colorspace: self.colorspace as OsEnumType,
231            range: self.range as OsEnumType,
232            scale_type: self.scale_type as OsEnumType,
233        };
234
235        ObsVideoInfo {
236            ovi: Sendable(Box::pin(ovi)),
237            graphics_module: graphics_mod_str,
238            sdr_info: self.sdr_info,
239        }
240    }
241
242    pub fn set_sdr_info(mut self, sdr_info: ObsSdrVideoInfo) -> Self {
243        self.sdr_info = sdr_info;
244        self
245    }
246
247    /// Sets the GPU adapter device
248    /// that the video output is coming
249    /// from.
250    pub fn adapter(mut self, value: u32) -> Self {
251        self.adapter = value;
252        self
253    }
254
255    /// Sets the graphics backend
256    /// that libobs uses to record.
257    pub fn graphics_module(mut self, value: ObsGraphicsModule) -> Self {
258        self.graphics_module = value;
259        self
260    }
261
262    /// Sets the framerate of the
263    /// output video. Note that this
264    /// value may not reflect the
265    /// final framerate if `fps_den`
266    /// is not equal to 1.
267    pub fn fps_num(mut self, value: u32) -> Self {
268        self.fps_num = value;
269        self
270    }
271
272    /// Divides the FPS numerator to
273    /// allow for fractional FPS
274    /// counts on output.
275    pub fn fps_den(mut self, value: u32) -> Self {
276        self.fps_den = value;
277        self
278    }
279
280    /// Sets the width of the screen
281    /// being recorded.
282    pub fn base_width(mut self, value: u32) -> Self {
283        self.base_width = value;
284        self
285    }
286
287    /// Sets the height of the screen
288    /// being recorded.
289    pub fn base_height(mut self, value: u32) -> Self {
290        self.base_height = value;
291        self
292    }
293
294    /// Sets the width of the video
295    /// output.
296    pub fn output_width(mut self, value: u32) -> Self {
297        self.output_width = value;
298        self
299    }
300
301    /// Sets the height of the video
302    /// output.
303    pub fn output_height(mut self, value: u32) -> Self {
304        self.output_height = value;
305        self
306    }
307
308    /// Sets the format in which the
309    /// video will be output.
310    pub fn output_format(mut self, value: ObsVideoFormat) -> Self {
311        self.output_format = value;
312        self
313    }
314
315    /// Sets whether the GPU will handle
316    /// conversion in the video.
317    pub fn gpu_conversion(mut self, value: bool) -> Self {
318        self.gpu_conversion = value;
319        self
320    }
321
322    /// Sets the video colorspace.
323    pub fn colorspace(mut self, value: ObsColorspace) -> Self {
324        self.colorspace = value;
325        self
326    }
327
328    /// Sets the video range.
329    pub fn range(mut self, value: ObsVideoRange) -> Self {
330        self.range = value;
331        self
332    }
333
334    /// Sets the video scaling type.
335    pub fn scale_type(mut self, value: ObsScaleType) -> Self {
336        self.scale_type = value;
337        self
338    }
339}
340
341#[cfg_attr(coverage_nightly, coverage(off))]
342impl Default for ObsVideoInfoBuilder {
343    fn default() -> Self {
344        Self::new()
345    }
346}