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 pub sdr_white_level: f32,
20 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
33pub struct ObsVideoInfo {
39 ovi: Sendable<Pin<Box<obs_video_info>>>,
40 #[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 #[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 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 pub(crate) fn as_ptr(&self) -> *mut obs_video_info {
99 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#[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 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 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 pub fn adapter(mut self, value: u32) -> Self {
257 self.adapter = value;
258 self
259 }
260
261 pub fn graphics_module(mut self, value: ObsGraphicsModule) -> Self {
264 self.graphics_module = value;
265 self
266 }
267
268 pub fn fps_num(mut self, value: u32) -> Self {
274 self.fps_num = value;
275 self
276 }
277
278 pub fn fps_den(mut self, value: u32) -> Self {
282 self.fps_den = value;
283 self
284 }
285
286 pub fn base_width(mut self, value: u32) -> Self {
289 self.base_width = value;
290 self
291 }
292
293 pub fn base_height(mut self, value: u32) -> Self {
296 self.base_height = value;
297 self
298 }
299
300 pub fn output_width(mut self, value: u32) -> Self {
303 self.output_width = value;
304 self
305 }
306
307 pub fn output_height(mut self, value: u32) -> Self {
310 self.output_height = value;
311 self
312 }
313
314 pub fn output_format(mut self, value: ObsVideoFormat) -> Self {
317 self.output_format = value;
318 self
319 }
320
321 pub fn gpu_conversion(mut self, value: bool) -> Self {
324 self.gpu_conversion = value;
325 self
326 }
327
328 pub fn colorspace(mut self, value: ObsColorspace) -> Self {
330 self.colorspace = value;
331 self
332 }
333
334 pub fn range(mut self, value: ObsVideoRange) -> Self {
336 self.range = value;
337 self
338 }
339
340 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}