1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use std::{fmt, marker::PhantomData, mem, ptr, slice}; |
4 | |
5 | use glib::translate::{ |
6 | from_glib, from_glib_full, from_glib_none, IntoGlib, ToGlibPtr, ToGlibPtrMut, |
7 | }; |
8 | use gst::prelude::*; |
9 | |
10 | #[doc (alias = "GstAudioInfo" )] |
11 | #[derive (Clone)] |
12 | #[repr (transparent)] |
13 | pub struct AudioInfo(ffi::GstAudioInfo); |
14 | |
15 | impl fmt::Debug for AudioInfo { |
16 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
17 | f&mut DebugStruct<'_, '_>.debug_struct("AudioInfo" ) |
18 | .field("format-info" , &self.format_info()) |
19 | .field("rate" , &self.rate()) |
20 | .field("channels" , &self.channels()) |
21 | .field("positions" , &self.positions()) |
22 | .field("flags" , &self.flags()) |
23 | .field(name:"layout" , &self.layout()) |
24 | .finish() |
25 | } |
26 | } |
27 | |
28 | #[derive (Debug)] |
29 | #[must_use = "The builder must be built to be used" ] |
30 | pub struct AudioInfoBuilder<'a> { |
31 | format: crate::AudioFormat, |
32 | rate: u32, |
33 | channels: u32, |
34 | positions: Option<&'a [crate::AudioChannelPosition]>, |
35 | flags: Option<crate::AudioFlags>, |
36 | layout: Option<crate::AudioLayout>, |
37 | } |
38 | |
39 | impl<'a> AudioInfoBuilder<'a> { |
40 | #[must_use = "The built AudioInfo must be used" ] |
41 | pub fn build(self) -> Result<AudioInfo, glib::error::BoolError> { |
42 | unsafe { |
43 | let mut info = mem::MaybeUninit::uninit(); |
44 | |
45 | if let Some(p) = self.positions { |
46 | if p.len() != self.channels as usize || p.len() > 64 { |
47 | return Err(glib::bool_error!("Invalid positions length" )); |
48 | } |
49 | |
50 | let valid: bool = from_glib(ffi::gst_audio_check_valid_channel_positions( |
51 | p.as_ptr() as *mut _, |
52 | self.channels as i32, |
53 | true.into_glib(), |
54 | )); |
55 | if !valid { |
56 | return Err(glib::bool_error!("channel positions are invalid" )); |
57 | } |
58 | } |
59 | |
60 | let positions_ptr = self |
61 | .positions |
62 | .as_ref() |
63 | .map(|p| p.as_ptr()) |
64 | .unwrap_or(ptr::null()); |
65 | |
66 | ffi::gst_audio_info_set_format( |
67 | info.as_mut_ptr(), |
68 | self.format.into_glib(), |
69 | self.rate as i32, |
70 | self.channels as i32, |
71 | positions_ptr as *mut _, |
72 | ); |
73 | |
74 | let mut info = info.assume_init(); |
75 | |
76 | if info.finfo.is_null() || info.rate <= 0 || info.channels <= 0 { |
77 | return Err(glib::bool_error!("Failed to build AudioInfo" )); |
78 | } |
79 | |
80 | if let Some(flags) = self.flags { |
81 | info.flags = flags.into_glib(); |
82 | } |
83 | |
84 | if let Some(layout) = self.layout { |
85 | info.layout = layout.into_glib(); |
86 | } |
87 | |
88 | Ok(AudioInfo(info)) |
89 | } |
90 | } |
91 | |
92 | pub fn positions(self, positions: &'a [crate::AudioChannelPosition]) -> AudioInfoBuilder<'a> { |
93 | Self { |
94 | positions: Some(positions), |
95 | ..self |
96 | } |
97 | } |
98 | |
99 | pub fn flags(self, flags: crate::AudioFlags) -> Self { |
100 | Self { |
101 | flags: Some(flags), |
102 | ..self |
103 | } |
104 | } |
105 | |
106 | pub fn layout(self, layout: crate::AudioLayout) -> Self { |
107 | Self { |
108 | layout: Some(layout), |
109 | ..self |
110 | } |
111 | } |
112 | } |
113 | |
114 | impl AudioInfo { |
115 | pub fn builder<'a>( |
116 | format: crate::AudioFormat, |
117 | rate: u32, |
118 | channels: u32, |
119 | ) -> AudioInfoBuilder<'a> { |
120 | assert_initialized_main_thread!(); |
121 | |
122 | AudioInfoBuilder { |
123 | format, |
124 | rate, |
125 | channels, |
126 | positions: None, |
127 | flags: None, |
128 | layout: None, |
129 | } |
130 | } |
131 | |
132 | #[inline ] |
133 | pub fn is_valid(&self) -> bool { |
134 | !self.0.finfo.is_null() && self.0.channels > 0 && self.0.rate > 0 && self.0.bpf > 0 |
135 | } |
136 | |
137 | #[doc (alias = "gst_audio_info_from_caps" )] |
138 | pub fn from_caps(caps: &gst::CapsRef) -> Result<Self, glib::error::BoolError> { |
139 | skip_assert_initialized!(); |
140 | |
141 | unsafe { |
142 | let mut info = mem::MaybeUninit::uninit(); |
143 | if from_glib(ffi::gst_audio_info_from_caps( |
144 | info.as_mut_ptr(), |
145 | caps.as_ptr(), |
146 | )) { |
147 | Ok(Self(info.assume_init())) |
148 | } else { |
149 | Err(glib::bool_error!("Failed to create AudioInfo from caps" )) |
150 | } |
151 | } |
152 | } |
153 | |
154 | #[doc (alias = "gst_audio_info_to_caps" )] |
155 | pub fn to_caps(&self) -> Result<gst::Caps, glib::error::BoolError> { |
156 | unsafe { |
157 | let result = from_glib_full(ffi::gst_audio_info_to_caps(&self.0)); |
158 | match result { |
159 | Some(c) => Ok(c), |
160 | None => Err(glib::bool_error!("Failed to create caps from AudioInfo" )), |
161 | } |
162 | } |
163 | } |
164 | |
165 | #[doc (alias = "gst_audio_info_convert" )] |
166 | pub fn convert<U: gst::format::SpecificFormattedValueFullRange>( |
167 | &self, |
168 | src_val: impl gst::format::FormattedValue, |
169 | ) -> Option<U> { |
170 | assert_initialized_main_thread!(); |
171 | unsafe { |
172 | let mut dest_val = mem::MaybeUninit::uninit(); |
173 | if from_glib(ffi::gst_audio_info_convert( |
174 | &self.0, |
175 | src_val.format().into_glib(), |
176 | src_val.into_raw_value(), |
177 | U::default_format().into_glib(), |
178 | dest_val.as_mut_ptr(), |
179 | )) { |
180 | Some(U::from_raw(U::default_format(), dest_val.assume_init())) |
181 | } else { |
182 | None |
183 | } |
184 | } |
185 | } |
186 | |
187 | pub fn convert_generic( |
188 | &self, |
189 | src_val: impl gst::format::FormattedValue, |
190 | dest_fmt: gst::Format, |
191 | ) -> Option<gst::GenericFormattedValue> { |
192 | assert_initialized_main_thread!(); |
193 | unsafe { |
194 | let mut dest_val = mem::MaybeUninit::uninit(); |
195 | if from_glib(ffi::gst_audio_info_convert( |
196 | &self.0, |
197 | src_val.format().into_glib(), |
198 | src_val.into_raw_value(), |
199 | dest_fmt.into_glib(), |
200 | dest_val.as_mut_ptr(), |
201 | )) { |
202 | Some(gst::GenericFormattedValue::new( |
203 | dest_fmt, |
204 | dest_val.assume_init(), |
205 | )) |
206 | } else { |
207 | None |
208 | } |
209 | } |
210 | } |
211 | |
212 | #[inline ] |
213 | pub fn format(&self) -> crate::AudioFormat { |
214 | if self.0.finfo.is_null() { |
215 | return crate::AudioFormat::Unknown; |
216 | } |
217 | |
218 | unsafe { from_glib((*self.0.finfo).format) } |
219 | } |
220 | |
221 | #[inline ] |
222 | pub fn format_info(&self) -> crate::AudioFormatInfo { |
223 | crate::AudioFormatInfo::from_format(self.format()) |
224 | } |
225 | |
226 | #[inline ] |
227 | pub fn layout(&self) -> crate::AudioLayout { |
228 | unsafe { from_glib(self.0.layout) } |
229 | } |
230 | |
231 | #[inline ] |
232 | pub fn flags(&self) -> crate::AudioFlags { |
233 | unsafe { from_glib(self.0.flags) } |
234 | } |
235 | |
236 | #[inline ] |
237 | pub fn rate(&self) -> u32 { |
238 | self.0.rate as u32 |
239 | } |
240 | |
241 | #[inline ] |
242 | pub fn channels(&self) -> u32 { |
243 | self.0.channels as u32 |
244 | } |
245 | |
246 | #[inline ] |
247 | pub fn bpf(&self) -> u32 { |
248 | self.0.bpf as u32 |
249 | } |
250 | |
251 | #[inline ] |
252 | pub fn bps(&self) -> u32 { |
253 | self.format_info().depth() >> 3 |
254 | } |
255 | |
256 | #[inline ] |
257 | pub fn depth(&self) -> u32 { |
258 | self.format_info().depth() |
259 | } |
260 | |
261 | #[inline ] |
262 | pub fn width(&self) -> u32 { |
263 | self.format_info().width() |
264 | } |
265 | |
266 | #[inline ] |
267 | pub fn endianness(&self) -> crate::AudioEndianness { |
268 | self.format_info().endianness() |
269 | } |
270 | |
271 | #[inline ] |
272 | pub fn is_big_endian(&self) -> bool { |
273 | self.format_info().is_big_endian() |
274 | } |
275 | |
276 | #[inline ] |
277 | pub fn is_little_endian(&self) -> bool { |
278 | self.format_info().is_little_endian() |
279 | } |
280 | |
281 | #[inline ] |
282 | pub fn is_float(&self) -> bool { |
283 | self.format_info().is_float() |
284 | } |
285 | |
286 | #[inline ] |
287 | pub fn is_integer(&self) -> bool { |
288 | self.format_info().is_integer() |
289 | } |
290 | |
291 | #[inline ] |
292 | pub fn is_signed(&self) -> bool { |
293 | self.format_info().is_signed() |
294 | } |
295 | |
296 | #[inline ] |
297 | pub fn positions(&self) -> Option<&[crate::AudioChannelPosition]> { |
298 | if self.0.channels > 64 || self.is_unpositioned() { |
299 | return None; |
300 | } |
301 | |
302 | Some(unsafe { |
303 | slice::from_raw_parts( |
304 | &self.0.position as *const i32 as *const crate::AudioChannelPosition, |
305 | self.0.channels as usize, |
306 | ) |
307 | }) |
308 | } |
309 | |
310 | #[inline ] |
311 | pub fn is_unpositioned(&self) -> bool { |
312 | self.flags().contains(crate::AudioFlags::UNPOSITIONED) |
313 | } |
314 | } |
315 | |
316 | impl PartialEq for AudioInfo { |
317 | #[doc (alias = "gst_audio_info_is_equal" )] |
318 | #[inline ] |
319 | fn eq(&self, other: &Self) -> bool { |
320 | unsafe { from_glib(val:ffi::gst_audio_info_is_equal(&self.0, &other.0)) } |
321 | } |
322 | } |
323 | |
324 | impl Eq for AudioInfo {} |
325 | |
326 | unsafe impl Send for AudioInfo {} |
327 | unsafe impl Sync for AudioInfo {} |
328 | |
329 | impl glib::types::StaticType for AudioInfo { |
330 | #[inline ] |
331 | fn static_type() -> glib::types::Type { |
332 | unsafe { glib::translate::from_glib(val:ffi::gst_audio_info_get_type()) } |
333 | } |
334 | } |
335 | |
336 | impl glib::value::ValueType for AudioInfo { |
337 | type Type = Self; |
338 | } |
339 | |
340 | #[doc (hidden)] |
341 | unsafe impl<'a> glib::value::FromValue<'a> for AudioInfo { |
342 | type Checker = glib::value::GenericValueTypeOrNoneChecker<Self>; |
343 | |
344 | unsafe fn from_value(value: &'a glib::Value) -> Self { |
345 | skip_assert_initialized!(); |
346 | from_glib_none( |
347 | ptr:glib::gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *mut ffi::GstAudioInfo |
348 | ) |
349 | } |
350 | } |
351 | |
352 | #[doc (hidden)] |
353 | impl glib::value::ToValue for AudioInfo { |
354 | fn to_value(&self) -> glib::Value { |
355 | let mut value: Value = glib::Value::for_value_type::<Self>(); |
356 | unsafe { |
357 | glib::gobject_ffi::g_value_set_boxed( |
358 | value:value.to_glib_none_mut().0, |
359 | self.to_glib_none().0 as *mut _, |
360 | ) |
361 | } |
362 | value |
363 | } |
364 | |
365 | fn value_type(&self) -> glib::Type { |
366 | Self::static_type() |
367 | } |
368 | } |
369 | |
370 | #[doc (hidden)] |
371 | impl From<AudioInfo> for glib::Value { |
372 | fn from(v: AudioInfo) -> glib::Value { |
373 | skip_assert_initialized!(); |
374 | glib::value::ToValue::to_value(&v) |
375 | } |
376 | } |
377 | |
378 | #[doc (hidden)] |
379 | impl glib::value::ToValueOptional for AudioInfo { |
380 | fn to_value_optional(s: Option<&Self>) -> glib::Value { |
381 | skip_assert_initialized!(); |
382 | let mut value: Value = glib::Value::for_value_type::<Self>(); |
383 | unsafe { |
384 | glib::gobject_ffi::g_value_set_boxed( |
385 | value:value.to_glib_none_mut().0, |
386 | v_boxed:s.to_glib_none().0 as *mut _, |
387 | ) |
388 | } |
389 | value |
390 | } |
391 | } |
392 | |
393 | #[doc (hidden)] |
394 | impl glib::translate::Uninitialized for AudioInfo { |
395 | #[inline ] |
396 | unsafe fn uninitialized() -> Self { |
397 | mem::zeroed() |
398 | } |
399 | } |
400 | |
401 | #[doc (hidden)] |
402 | impl glib::translate::GlibPtrDefault for AudioInfo { |
403 | type GlibType = *mut ffi::GstAudioInfo; |
404 | } |
405 | |
406 | #[doc (hidden)] |
407 | impl<'a> glib::translate::ToGlibPtr<'a, *const ffi::GstAudioInfo> for AudioInfo { |
408 | type Storage = PhantomData<&'a Self>; |
409 | |
410 | #[inline ] |
411 | fn to_glib_none(&'a self) -> glib::translate::Stash<'a, *const ffi::GstAudioInfo, Self> { |
412 | glib::translate::Stash(&self.0, PhantomData) |
413 | } |
414 | |
415 | fn to_glib_full(&self) -> *const ffi::GstAudioInfo { |
416 | unimplemented!() |
417 | } |
418 | } |
419 | |
420 | #[doc (hidden)] |
421 | impl glib::translate::FromGlibPtrNone<*const ffi::GstAudioInfo> for AudioInfo { |
422 | #[inline ] |
423 | unsafe fn from_glib_none(ptr: *const ffi::GstAudioInfo) -> Self { |
424 | Self(ptr::read(src:ptr)) |
425 | } |
426 | } |
427 | |
428 | #[doc (hidden)] |
429 | impl glib::translate::FromGlibPtrNone<*mut ffi::GstAudioInfo> for AudioInfo { |
430 | #[inline ] |
431 | unsafe fn from_glib_none(ptr: *mut ffi::GstAudioInfo) -> Self { |
432 | Self(ptr::read(src:ptr)) |
433 | } |
434 | } |
435 | |
436 | #[doc (hidden)] |
437 | impl glib::translate::FromGlibPtrFull<*mut ffi::GstAudioInfo> for AudioInfo { |
438 | #[inline ] |
439 | unsafe fn from_glib_full(ptr: *mut ffi::GstAudioInfo) -> Self { |
440 | let info: AudioInfo = from_glib_none(ptr); |
441 | glib::ffi::g_free(mem:ptr as *mut _); |
442 | info |
443 | } |
444 | } |
445 | |
446 | #[cfg (test)] |
447 | mod tests { |
448 | use super::*; |
449 | |
450 | #[test ] |
451 | fn test_new() { |
452 | gst::init().unwrap(); |
453 | |
454 | let info = AudioInfo::builder(crate::AudioFormat::S16le, 48000, 2) |
455 | .build() |
456 | .unwrap(); |
457 | assert_eq!(info.format(), crate::AudioFormat::S16le); |
458 | assert_eq!(info.rate(), 48000); |
459 | assert_eq!(info.channels(), 2); |
460 | assert_eq!( |
461 | &info.positions().unwrap(), |
462 | &[ |
463 | crate::AudioChannelPosition::FrontLeft, |
464 | crate::AudioChannelPosition::FrontRight, |
465 | ] |
466 | ); |
467 | |
468 | let positions = [ |
469 | crate::AudioChannelPosition::RearLeft, |
470 | crate::AudioChannelPosition::RearRight, |
471 | ]; |
472 | let info = AudioInfo::builder(crate::AudioFormat::S16le, 48000, 2) |
473 | .positions(&positions) |
474 | .build() |
475 | .unwrap(); |
476 | assert_eq!(info.format(), crate::AudioFormat::S16le); |
477 | assert_eq!(info.rate(), 48000); |
478 | assert_eq!(info.channels(), 2); |
479 | assert_eq!( |
480 | &info.positions().unwrap(), |
481 | &[ |
482 | crate::AudioChannelPosition::RearLeft, |
483 | crate::AudioChannelPosition::RearRight, |
484 | ] |
485 | ); |
486 | } |
487 | |
488 | #[test ] |
489 | fn test_from_to_caps() { |
490 | gst::init().unwrap(); |
491 | |
492 | let caps = crate::AudioCapsBuilder::new_interleaved() |
493 | .format(crate::AudioFormat::S16le) |
494 | .rate(48000) |
495 | .channels(2) |
496 | .fallback_channel_mask() |
497 | .build(); |
498 | let info = AudioInfo::from_caps(&caps).unwrap(); |
499 | assert_eq!(info.format(), crate::AudioFormat::S16le); |
500 | assert_eq!(info.rate(), 48000); |
501 | assert_eq!(info.channels(), 2); |
502 | assert_eq!( |
503 | &info.positions().unwrap(), |
504 | &[ |
505 | crate::AudioChannelPosition::FrontLeft, |
506 | crate::AudioChannelPosition::FrontRight, |
507 | ] |
508 | ); |
509 | |
510 | let caps2 = info.to_caps().unwrap(); |
511 | assert_eq!(caps, caps2); |
512 | |
513 | let info2 = AudioInfo::from_caps(&caps2).unwrap(); |
514 | assert!(info == info2); |
515 | } |
516 | } |
517 | |