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