2017-08-10 08:24:16 +00:00
|
|
|
// Copyright (C) 2017 Sebastian Dröge <sebastian@centricular.com>
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
2019-03-19 07:58:20 +00:00
|
|
|
use gst_audio_sys;
|
2017-08-10 08:24:16 +00:00
|
|
|
|
|
|
|
use std::ffi::CStr;
|
|
|
|
use std::fmt;
|
|
|
|
use std::str;
|
|
|
|
|
2019-10-04 06:11:30 +00:00
|
|
|
use glib::translate::{from_glib, FromGlib, ToGlib, ToGlibPtr};
|
2020-05-28 10:57:35 +00:00
|
|
|
use once_cell::sync::Lazy;
|
|
|
|
|
|
|
|
#[cfg(feature = "v1_18")]
|
|
|
|
pub static AUDIO_FORMATS_ALL: Lazy<Box<[::AudioFormat]>> = Lazy::new(|| unsafe {
|
|
|
|
let mut len: u32 = 0;
|
|
|
|
let mut res = Vec::with_capacity(len as usize);
|
|
|
|
let formats = gst_audio_sys::gst_audio_formats_raw(&mut len);
|
|
|
|
for i in 0..len {
|
|
|
|
let format = formats.offset(i as isize);
|
|
|
|
res.push(from_glib(*format));
|
|
|
|
}
|
|
|
|
res.into_boxed_slice()
|
|
|
|
});
|
|
|
|
|
|
|
|
#[cfg(not(feature = "v1_18"))]
|
|
|
|
pub static AUDIO_FORMATS_ALL: Lazy<Box<[::AudioFormat]>> = Lazy::new(|| {
|
2020-06-05 10:22:08 +00:00
|
|
|
#[cfg(target_endian = "little")]
|
|
|
|
{
|
|
|
|
Box::new([
|
|
|
|
::AudioFormat::F64le,
|
|
|
|
::AudioFormat::F64be,
|
|
|
|
::AudioFormat::F32le,
|
|
|
|
::AudioFormat::F32be,
|
|
|
|
::AudioFormat::S32le,
|
|
|
|
::AudioFormat::S32be,
|
|
|
|
::AudioFormat::U32le,
|
|
|
|
::AudioFormat::U32be,
|
|
|
|
::AudioFormat::S2432le,
|
|
|
|
::AudioFormat::S2432be,
|
|
|
|
::AudioFormat::U2432le,
|
|
|
|
::AudioFormat::U2432be,
|
|
|
|
::AudioFormat::S24le,
|
|
|
|
::AudioFormat::S24be,
|
|
|
|
::AudioFormat::U24le,
|
|
|
|
::AudioFormat::U24be,
|
|
|
|
::AudioFormat::S20le,
|
|
|
|
::AudioFormat::S20be,
|
|
|
|
::AudioFormat::U20le,
|
|
|
|
::AudioFormat::U20be,
|
|
|
|
::AudioFormat::S18le,
|
|
|
|
::AudioFormat::S18be,
|
|
|
|
::AudioFormat::U18le,
|
|
|
|
::AudioFormat::U18be,
|
|
|
|
::AudioFormat::S16le,
|
|
|
|
::AudioFormat::S16be,
|
|
|
|
::AudioFormat::U16le,
|
|
|
|
::AudioFormat::U16be,
|
|
|
|
::AudioFormat::S8,
|
|
|
|
::AudioFormat::U8,
|
|
|
|
])
|
|
|
|
}
|
|
|
|
#[cfg(target_endian = "big")]
|
|
|
|
{
|
|
|
|
Box::new([
|
|
|
|
::AudioFormat::F64be,
|
|
|
|
::AudioFormat::F64le,
|
|
|
|
::AudioFormat::F32be,
|
|
|
|
::AudioFormat::F32le,
|
|
|
|
::AudioFormat::S32be,
|
|
|
|
::AudioFormat::S32le,
|
|
|
|
::AudioFormat::U32be,
|
|
|
|
::AudioFormat::U32le,
|
|
|
|
::AudioFormat::S2432be,
|
|
|
|
::AudioFormat::S2432le,
|
|
|
|
::AudioFormat::U2432be,
|
|
|
|
::AudioFormat::U2432le,
|
|
|
|
::AudioFormat::S24be,
|
|
|
|
::AudioFormat::S24le,
|
|
|
|
::AudioFormat::U24be,
|
|
|
|
::AudioFormat::U24le,
|
|
|
|
::AudioFormat::S20be,
|
|
|
|
::AudioFormat::S20le,
|
|
|
|
::AudioFormat::U20be,
|
|
|
|
::AudioFormat::U20le,
|
|
|
|
::AudioFormat::S18be,
|
|
|
|
::AudioFormat::S18le,
|
|
|
|
::AudioFormat::U18be,
|
|
|
|
::AudioFormat::U18le,
|
|
|
|
::AudioFormat::S16be,
|
|
|
|
::AudioFormat::S16le,
|
|
|
|
::AudioFormat::U16be,
|
|
|
|
::AudioFormat::U16le,
|
|
|
|
::AudioFormat::S8,
|
|
|
|
::AudioFormat::U8,
|
|
|
|
])
|
|
|
|
}
|
2020-05-28 10:57:35 +00:00
|
|
|
});
|
2017-08-10 08:24:16 +00:00
|
|
|
|
|
|
|
impl ::AudioFormat {
|
|
|
|
pub fn build_integer(
|
|
|
|
sign: bool,
|
|
|
|
endianness: ::AudioEndianness,
|
|
|
|
width: i32,
|
|
|
|
depth: i32,
|
|
|
|
) -> ::AudioFormat {
|
2017-08-30 11:39:09 +00:00
|
|
|
assert_initialized_main_thread!();
|
|
|
|
|
2017-08-10 08:24:16 +00:00
|
|
|
unsafe {
|
2019-03-19 07:58:20 +00:00
|
|
|
from_glib(gst_audio_sys::gst_audio_format_build_integer(
|
2017-08-10 08:24:16 +00:00
|
|
|
sign.to_glib(),
|
|
|
|
endianness.to_glib(),
|
|
|
|
width,
|
|
|
|
depth,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-04 06:11:30 +00:00
|
|
|
pub fn to_str<'a>(self) -> &'a str {
|
2018-07-20 07:21:06 +00:00
|
|
|
if self == ::AudioFormat::Unknown {
|
2017-12-16 08:56:59 +00:00
|
|
|
return "UNKNOWN";
|
|
|
|
}
|
|
|
|
|
2017-08-10 08:24:16 +00:00
|
|
|
unsafe {
|
2019-03-19 07:58:20 +00:00
|
|
|
CStr::from_ptr(gst_audio_sys::gst_audio_format_to_string(self.to_glib()))
|
2017-08-10 08:24:16 +00:00
|
|
|
.to_str()
|
|
|
|
.unwrap()
|
|
|
|
}
|
|
|
|
}
|
2020-05-28 10:57:35 +00:00
|
|
|
|
|
|
|
pub fn iter_raw() -> AudioFormatIterator {
|
|
|
|
AudioFormatIterator::default()
|
|
|
|
}
|
2017-08-10 08:24:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl str::FromStr for ::AudioFormat {
|
2019-12-17 19:00:42 +00:00
|
|
|
type Err = glib::BoolError;
|
2017-08-10 08:24:16 +00:00
|
|
|
|
2019-12-17 19:00:42 +00:00
|
|
|
fn from_str(s: &str) -> Result<Self, glib::BoolError> {
|
2019-10-04 06:11:30 +00:00
|
|
|
assert_initialized_main_thread!();
|
2017-08-30 11:39:09 +00:00
|
|
|
|
2019-10-04 06:11:30 +00:00
|
|
|
unsafe {
|
|
|
|
let fmt = ::AudioFormat::from_glib(gst_audio_sys::gst_audio_format_from_string(
|
|
|
|
s.to_glib_none().0,
|
|
|
|
));
|
|
|
|
if fmt == ::AudioFormat::Unknown {
|
2019-12-17 19:00:42 +00:00
|
|
|
Err(glib_bool_error!("Failed to parse audio format from string"))
|
2019-10-04 06:11:30 +00:00
|
|
|
} else {
|
|
|
|
Ok(fmt)
|
|
|
|
}
|
2017-08-10 08:24:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for ::AudioFormat {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
2019-10-04 06:11:30 +00:00
|
|
|
f.write_str((*self).to_str())
|
2017-08-10 08:24:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-05 09:57:29 +00:00
|
|
|
impl PartialOrd for ::AudioFormat {
|
|
|
|
fn partial_cmp(&self, other: &::AudioFormat) -> Option<std::cmp::Ordering> {
|
|
|
|
::AudioFormatInfo::from_format(*self).partial_cmp(&::AudioFormatInfo::from_format(*other))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Ord for ::AudioFormat {
|
|
|
|
fn cmp(&self, other: &::AudioFormat) -> std::cmp::Ordering {
|
|
|
|
::AudioFormatInfo::from_format(*self).cmp(&::AudioFormatInfo::from_format(*other))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-02 21:24:43 +00:00
|
|
|
pub const AUDIO_FORMAT_UNKNOWN: ::AudioFormat = ::AudioFormat::Unknown;
|
|
|
|
pub const AUDIO_FORMAT_ENCODED: ::AudioFormat = ::AudioFormat::Encoded;
|
|
|
|
pub const AUDIO_FORMAT_S8: ::AudioFormat = ::AudioFormat::S8;
|
|
|
|
pub const AUDIO_FORMAT_U8: ::AudioFormat = ::AudioFormat::U8;
|
|
|
|
|
2017-08-10 08:24:16 +00:00
|
|
|
#[cfg(target_endian = "big")]
|
|
|
|
pub const AUDIO_FORMAT_S16: ::AudioFormat = ::AudioFormat::S16be;
|
|
|
|
#[cfg(target_endian = "big")]
|
|
|
|
pub const AUDIO_FORMAT_U16: ::AudioFormat = ::AudioFormat::U16be;
|
|
|
|
#[cfg(target_endian = "big")]
|
|
|
|
pub const AUDIO_FORMAT_S2432: ::AudioFormat = ::AudioFormat::S2432be;
|
|
|
|
#[cfg(target_endian = "big")]
|
|
|
|
pub const AUDIO_FORMAT_U2432: ::AudioFormat = ::AudioFormat::U2432be;
|
|
|
|
#[cfg(target_endian = "big")]
|
|
|
|
pub const AUDIO_FORMAT_S32: ::AudioFormat = ::AudioFormat::S32be;
|
|
|
|
#[cfg(target_endian = "big")]
|
|
|
|
pub const AUDIO_FORMAT_U32: ::AudioFormat = ::AudioFormat::U32be;
|
|
|
|
#[cfg(target_endian = "big")]
|
|
|
|
pub const AUDIO_FORMAT_S24: ::AudioFormat = ::AudioFormat::S24be;
|
|
|
|
#[cfg(target_endian = "big")]
|
|
|
|
pub const AUDIO_FORMAT_U24: ::AudioFormat = ::AudioFormat::U24be;
|
|
|
|
#[cfg(target_endian = "big")]
|
|
|
|
pub const AUDIO_FORMAT_S20: ::AudioFormat = ::AudioFormat::S20be;
|
|
|
|
#[cfg(target_endian = "big")]
|
|
|
|
pub const AUDIO_FORMAT_U20: ::AudioFormat = ::AudioFormat::U20be;
|
|
|
|
#[cfg(target_endian = "big")]
|
|
|
|
pub const AUDIO_FORMAT_S18: ::AudioFormat = ::AudioFormat::S18be;
|
|
|
|
#[cfg(target_endian = "big")]
|
|
|
|
pub const AUDIO_FORMAT_U18: ::AudioFormat = ::AudioFormat::U18be;
|
|
|
|
#[cfg(target_endian = "big")]
|
|
|
|
pub const AUDIO_FORMAT_F32: ::AudioFormat = ::AudioFormat::F32be;
|
|
|
|
#[cfg(target_endian = "big")]
|
|
|
|
pub const AUDIO_FORMAT_F64: ::AudioFormat = ::AudioFormat::F64be;
|
|
|
|
|
|
|
|
#[cfg(target_endian = "little")]
|
|
|
|
pub const AUDIO_FORMAT_S16: ::AudioFormat = ::AudioFormat::S16le;
|
|
|
|
#[cfg(target_endian = "little")]
|
|
|
|
pub const AUDIO_FORMAT_U16: ::AudioFormat = ::AudioFormat::U16le;
|
|
|
|
#[cfg(target_endian = "little")]
|
|
|
|
pub const AUDIO_FORMAT_S2432: ::AudioFormat = ::AudioFormat::S2432le;
|
|
|
|
#[cfg(target_endian = "little")]
|
|
|
|
pub const AUDIO_FORMAT_U2432: ::AudioFormat = ::AudioFormat::U2432le;
|
|
|
|
#[cfg(target_endian = "little")]
|
|
|
|
pub const AUDIO_FORMAT_S32: ::AudioFormat = ::AudioFormat::S32le;
|
|
|
|
#[cfg(target_endian = "little")]
|
|
|
|
pub const AUDIO_FORMAT_U32: ::AudioFormat = ::AudioFormat::U32le;
|
|
|
|
#[cfg(target_endian = "little")]
|
|
|
|
pub const AUDIO_FORMAT_S24: ::AudioFormat = ::AudioFormat::S24le;
|
|
|
|
#[cfg(target_endian = "little")]
|
|
|
|
pub const AUDIO_FORMAT_U24: ::AudioFormat = ::AudioFormat::U24le;
|
|
|
|
#[cfg(target_endian = "little")]
|
|
|
|
pub const AUDIO_FORMAT_S20: ::AudioFormat = ::AudioFormat::S20le;
|
|
|
|
#[cfg(target_endian = "little")]
|
|
|
|
pub const AUDIO_FORMAT_U20: ::AudioFormat = ::AudioFormat::U20le;
|
|
|
|
#[cfg(target_endian = "little")]
|
|
|
|
pub const AUDIO_FORMAT_S18: ::AudioFormat = ::AudioFormat::S18le;
|
|
|
|
#[cfg(target_endian = "little")]
|
|
|
|
pub const AUDIO_FORMAT_U18: ::AudioFormat = ::AudioFormat::U18le;
|
|
|
|
#[cfg(target_endian = "little")]
|
|
|
|
pub const AUDIO_FORMAT_F32: ::AudioFormat = ::AudioFormat::F32le;
|
|
|
|
#[cfg(target_endian = "little")]
|
|
|
|
pub const AUDIO_FORMAT_F64: ::AudioFormat = ::AudioFormat::F64le;
|
2019-06-18 10:15:33 +00:00
|
|
|
|
2020-05-28 10:57:35 +00:00
|
|
|
pub struct AudioFormatIterator {
|
|
|
|
idx: usize,
|
|
|
|
len: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for AudioFormatIterator {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
idx: 0,
|
|
|
|
len: AUDIO_FORMATS_ALL.len(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Iterator for AudioFormatIterator {
|
|
|
|
type Item = ::AudioFormat;
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
if self.idx >= self.len {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
let fmt = AUDIO_FORMATS_ALL[self.idx];
|
|
|
|
self.idx += 1;
|
|
|
|
Some(fmt)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ExactSizeIterator for AudioFormatIterator {
|
|
|
|
fn len(&self) -> usize {
|
|
|
|
self.len
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DoubleEndedIterator for AudioFormatIterator {
|
|
|
|
fn next_back(&mut self) -> Option<Self::Item> {
|
|
|
|
if self.idx >= self.len {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
let fmt = AUDIO_FORMATS_ALL[self.len - 1];
|
|
|
|
self.len -= 1;
|
|
|
|
Some(fmt)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pub trait AudioFormatIteratorExt {
|
|
|
|
fn into_audio_caps(
|
|
|
|
self,
|
|
|
|
layout: ::AudioLayout,
|
|
|
|
) -> Option<gst::caps::Builder<gst::caps::NoFeature>>;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> AudioFormatIteratorExt for T
|
|
|
|
where
|
|
|
|
T: Iterator<Item = ::AudioFormat>,
|
|
|
|
{
|
|
|
|
fn into_audio_caps(
|
|
|
|
self,
|
|
|
|
layout: ::AudioLayout,
|
|
|
|
) -> Option<gst::caps::Builder<gst::caps::NoFeature>> {
|
|
|
|
let formats: Vec<::AudioFormat> = self.collect();
|
|
|
|
if !formats.is_empty() {
|
|
|
|
Some(::functions::audio_make_raw_caps(&formats, layout))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub trait AudioFormatIteratorExtRef {
|
|
|
|
fn into_audio_caps(
|
|
|
|
self,
|
|
|
|
layout: ::AudioLayout,
|
|
|
|
) -> Option<gst::caps::Builder<gst::caps::NoFeature>>;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, T> AudioFormatIteratorExtRef for T
|
|
|
|
where
|
|
|
|
T: Iterator<Item = &'a ::AudioFormat>,
|
|
|
|
{
|
|
|
|
fn into_audio_caps(
|
|
|
|
self,
|
|
|
|
layout: ::AudioLayout,
|
|
|
|
) -> Option<gst::caps::Builder<gst::caps::NoFeature>> {
|
|
|
|
let formats: Vec<::AudioFormat> = self.copied().collect();
|
|
|
|
if !formats.is_empty() {
|
|
|
|
Some(::functions::audio_make_raw_caps(&formats, layout))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-18 10:15:33 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use gst;
|
2020-06-05 10:22:08 +00:00
|
|
|
use itertools::Itertools;
|
2019-06-18 10:15:33 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_display() {
|
|
|
|
gst::init().unwrap();
|
|
|
|
|
|
|
|
format!("{}", ::AudioFormat::S16be);
|
|
|
|
}
|
2020-05-28 10:57:35 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn iter() {
|
|
|
|
use super::*;
|
|
|
|
gst::init().unwrap();
|
|
|
|
|
|
|
|
assert!(::AudioFormat::iter_raw().count() > 0);
|
|
|
|
assert_eq!(
|
|
|
|
::AudioFormat::iter_raw().count(),
|
|
|
|
::AudioFormat::iter_raw().len()
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut i = ::AudioFormat::iter_raw();
|
|
|
|
let mut count = 0;
|
|
|
|
loop {
|
|
|
|
if i.next().is_none() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
count += 1;
|
|
|
|
if i.next_back().is_none() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
count += 1;
|
|
|
|
}
|
|
|
|
assert_eq!(count, ::AudioFormat::iter_raw().len());
|
|
|
|
|
|
|
|
assert!(::AudioFormat::iter_raw().any(|f| f == ::AudioFormat::F64be));
|
|
|
|
assert!(::AudioFormat::iter_raw()
|
|
|
|
.find(|f| *f == ::AudioFormat::Encoded)
|
|
|
|
.is_none());
|
|
|
|
|
|
|
|
let caps = ::AudioFormat::iter_raw().into_audio_caps(::AudioLayout::Interleaved);
|
|
|
|
assert!(caps.is_some());
|
|
|
|
|
|
|
|
let caps = ::AudioFormat::iter_raw()
|
|
|
|
.filter(|f| ::AudioFormatInfo::from_format(*f).is_little_endian())
|
|
|
|
.into_audio_caps(::AudioLayout::Interleaved);
|
|
|
|
assert!(caps.is_some());
|
|
|
|
|
|
|
|
let caps = ::AudioFormat::iter_raw()
|
|
|
|
.skip(1000)
|
|
|
|
.into_audio_caps(::AudioLayout::Interleaved);
|
|
|
|
assert!(caps.is_none());
|
|
|
|
|
|
|
|
let caps = [::AudioFormat::S16le, ::AudioFormat::S16be]
|
|
|
|
.iter()
|
|
|
|
.into_audio_caps(::AudioLayout::Interleaved)
|
|
|
|
.unwrap()
|
|
|
|
.build();
|
|
|
|
assert_eq!(caps.to_string(), "audio/x-raw, format=(string){ S16LE, S16BE }, rate=(int)[ 1, 2147483647 ], channels=(int)[ 1, 2147483647 ], layout=(string)interleaved");
|
|
|
|
}
|
2020-06-05 09:57:29 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn sort() {
|
|
|
|
gst::init().unwrap();
|
|
|
|
|
|
|
|
assert!(
|
|
|
|
::AudioFormatInfo::from_format(::AudioFormat::F64be)
|
|
|
|
> ::AudioFormatInfo::from_format(::AudioFormat::U8)
|
|
|
|
);
|
|
|
|
assert!(::AudioFormat::S20be > ::AudioFormat::S18be);
|
2020-06-05 10:22:08 +00:00
|
|
|
|
|
|
|
let sorted: Vec<::AudioFormat> = ::AudioFormat::iter_raw().sorted().rev().collect();
|
|
|
|
// FIXME: use is_sorted_by() once API is in stable
|
|
|
|
assert_eq!(
|
|
|
|
sorted,
|
|
|
|
::AudioFormat::iter_raw().collect::<Vec<::AudioFormat>>()
|
|
|
|
);
|
2020-06-05 09:57:29 +00:00
|
|
|
}
|
2019-06-18 10:15:33 +00:00
|
|
|
}
|