1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{fmt, marker::PhantomData, mem, ptr, slice};
4
5use glib::translate::{
6 from_glib, from_glib_full, from_glib_none, IntoGlib, ToGlibPtr, ToGlibPtrMut,
7};
8use gst::prelude::*;
9
10#[doc(alias = "GstAudioInfo")]
11#[derive(Clone)]
12#[repr(transparent)]
13pub struct AudioInfo(ffi::GstAudioInfo);
14
15impl 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"]
30pub 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
39impl<'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
114impl 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
316impl 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
324impl Eq for AudioInfo {}
325
326unsafe impl Send for AudioInfo {}
327unsafe impl Sync for AudioInfo {}
328
329impl 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
336impl glib::value::ValueType for AudioInfo {
337 type Type = Self;
338}
339
340#[doc(hidden)]
341unsafe 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)]
353impl 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)]
371impl 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)]
379impl 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)]
394impl glib::translate::Uninitialized for AudioInfo {
395 #[inline]
396 unsafe fn uninitialized() -> Self {
397 mem::zeroed()
398 }
399}
400
401#[doc(hidden)]
402impl glib::translate::GlibPtrDefault for AudioInfo {
403 type GlibType = *mut ffi::GstAudioInfo;
404}
405
406#[doc(hidden)]
407impl<'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)]
421impl 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)]
429impl 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)]
437impl 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)]
447mod 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