diff --git a/gstreamer-audio/src/audio_meta.rs b/gstreamer-audio/src/audio_meta.rs index 58188b53b..9c6add8c1 100644 --- a/gstreamer-audio/src/audio_meta.rs +++ b/gstreamer-audio/src/audio_meta.rs @@ -294,20 +294,19 @@ mod tests { let mut buffer = gst::Buffer::with_size(1024).unwrap(); + let start = gst::format::Default::ONE; + let stop = 2 * gst::format::Default::ONE; + { - let cmeta = AudioClippingMeta::add( - buffer.get_mut().unwrap(), - gst::format::Default(1), - gst::format::Default(2), - ); - assert_eq!(cmeta.start().try_into(), Ok(Some(gst::format::Default(1)))); - assert_eq!(cmeta.end().try_into(), Ok(Some(gst::format::Default(2)))); + let cmeta = AudioClippingMeta::add(buffer.get_mut().unwrap(), start, stop); + assert_eq!(cmeta.start().try_into(), Ok(Some(start))); + assert_eq!(cmeta.end().try_into(), Ok(Some(stop))); } { let cmeta = buffer.meta::().unwrap(); - assert_eq!(cmeta.start().try_into(), Ok(Some(gst::format::Default(1)))); - assert_eq!(cmeta.end().try_into(), Ok(Some(gst::format::Default(2)))); + assert_eq!(cmeta.start().try_into(), Ok(Some(start))); + assert_eq!(cmeta.end().try_into(), Ok(Some(stop))); } } diff --git a/gstreamer/src/format/clock_time.rs b/gstreamer/src/format/clock_time.rs index 5c6b07b59..cdf365545 100644 --- a/gstreamer/src/format/clock_time.rs +++ b/gstreamer/src/format/clock_time.rs @@ -14,7 +14,7 @@ use super::{ }; #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)] -pub struct ClockTime(pub(crate) u64); +pub struct ClockTime(u64); impl ClockTime { #[doc(alias = "GST_SECOND")] diff --git a/gstreamer/src/format/clock_time_serde.rs b/gstreamer/src/format/clock_time_serde.rs index 65d8e3301..3f100bcaf 100644 --- a/gstreamer/src/format/clock_time_serde.rs +++ b/gstreamer/src/format/clock_time_serde.rs @@ -7,14 +7,20 @@ use crate::ClockTime; impl Serialize for ClockTime { fn serialize(&self, serializer: S) -> Result { - self.0.serialize(serializer) + use std::ops::Deref; + self.deref().serialize(serializer) } } impl<'de> Deserialize<'de> for ClockTime { fn deserialize>(deserializer: D) -> Result { skip_assert_initialized!(); - u64::deserialize(deserializer).map(ClockTime::from_nseconds) + u64::deserialize(deserializer).and_then(|value| { + ClockTime::try_from(value).map_err(|_| { + use serde::de::{Error, Unexpected}; + D::Error::invalid_value(Unexpected::Unsigned(value), &"valid `ClockTime`") + }) + }) } } diff --git a/gstreamer/src/format/format_serde.rs b/gstreamer/src/format/format_serde.rs index bd1b04e1f..d4a23293d 100644 --- a/gstreamer/src/format/format_serde.rs +++ b/gstreamer/src/format/format_serde.rs @@ -75,7 +75,7 @@ mod tests { let res = serde_json::to_string(&value).unwrap(); assert_eq!("{\"Undefined\":42}".to_owned(), res); - let value = GenericFormattedValue::from(Default(42)); + let value = GenericFormattedValue::from(42 * Default::ONE); let res = ron::ser::to_string_pretty(&value, pretty_config.clone()); assert_eq!(Ok("Default(Some(42))".to_owned()), res); let res = serde_json::to_string(&value).unwrap(); @@ -87,7 +87,7 @@ mod tests { let res = serde_json::to_string(&value).unwrap(); assert_eq!("{\"Default\":null}".to_owned(), res); - let value = GenericFormattedValue::from(Bytes(42)); + let value = GenericFormattedValue::from(42 * Bytes::ONE); let res = ron::ser::to_string_pretty(&value, pretty_config.clone()); assert_eq!(Ok("Bytes(Some(42))".to_owned()), res); let res = serde_json::to_string(&value).unwrap(); @@ -99,19 +99,21 @@ mod tests { let res = serde_json::to_string(&value).unwrap(); assert_eq!("{\"Time\":42123456789}".to_owned(), res); - let value = GenericFormattedValue::from(Buffers(42)); + let value = GenericFormattedValue::from(42 * Buffers::ONE); let res = ron::ser::to_string_pretty(&value, pretty_config.clone()); assert_eq!(Ok("Buffers(Some(42))".to_owned()), res); let res = serde_json::to_string(&value).unwrap(); assert_eq!("{\"Buffers\":42}".to_owned(), res); - let value = GenericFormattedValue::from(Percent::try_from(0.42).unwrap()); + let percent = Percent::try_from(0.42).unwrap(); + let value = GenericFormattedValue::from(percent); let res = ron::ser::to_string_pretty(&value, pretty_config.clone()); assert_eq!(Ok("Percent(Some(4200))".to_owned()), res); let res = serde_json::to_string(&value).unwrap(); assert_eq!("{\"Percent\":4200}".to_owned(), res); - let value = GenericFormattedValue::Other(Format::Percent, Other::try_from(42).ok()); + let other = Other::try_from(42).ok(); + let value = GenericFormattedValue::Other(Format::Percent, other); let res = ron::ser::to_string_pretty(&value, pretty_config.clone()); assert_eq!(Ok("Other(Percent, Some(42))".to_owned()), res); let res = serde_json::to_string(&value).unwrap(); @@ -130,14 +132,14 @@ mod tests { let value_ron = "Default(Some(42))"; let value_de: GenericFormattedValue = ron::de::from_str(value_ron).unwrap(); - assert_eq!(value_de, GenericFormattedValue::from(Default(42))); + assert_eq!(value_de, GenericFormattedValue::from(42 * Default::ONE)); let value_json = "{\"Default\":42}"; let value_de: GenericFormattedValue = serde_json::from_str(value_json).unwrap(); - assert_eq!(value_de, GenericFormattedValue::from(Default(42))); + assert_eq!(value_de, GenericFormattedValue::from(42 * Default::ONE)); let value_ron = "Other(Percent, Some(42))"; - let gfv_value = GenericFormattedValue::Other(Format::Percent, Other::try_from(42).ok()); + let gfv_value = GenericFormattedValue::Other(Format::Percent, Some(42 * Other::ONE)); let value_de: GenericFormattedValue = ron::de::from_str(value_ron).unwrap(); assert_eq!(value_de, gfv_value); @@ -159,16 +161,14 @@ mod tests { } ); - test_roundrip!(GenericFormattedValue::Undefined(Undefined::from(42))); - test_roundrip!(GenericFormattedValue::from(Default(42))); - test_roundrip!(GenericFormattedValue::from(Bytes(42))); + test_roundrip!(GenericFormattedValue::Undefined(Undefined(42))); + test_roundrip!(GenericFormattedValue::from(42 * Default::ONE)); + test_roundrip!(GenericFormattedValue::from(42 * Bytes::ONE)); test_roundrip!(GenericFormattedValue::from(ClockTime::from_nseconds( 42_123_456_789 ))); - test_roundrip!(GenericFormattedValue::from(Buffers(42))); - test_roundrip!(GenericFormattedValue::from( - Percent::try_from(0.42).unwrap() - )); + test_roundrip!(GenericFormattedValue::from(42 * Buffers::ONE)); + test_roundrip!(GenericFormattedValue::from(42 * Percent::ONE)); let gfv_value = GenericFormattedValue::Other(Format::Percent, Other::try_from(42).ok()); test_roundrip!(gfv_value); test_roundrip!(GenericFormattedValue::new(Format::__Unknown(7), 42)); diff --git a/gstreamer/src/format/macros.rs b/gstreamer/src/format/macros.rs index fb0bcf109..24efc1701 100644 --- a/gstreamer/src/format/macros.rs +++ b/gstreamer/src/format/macros.rs @@ -229,9 +229,16 @@ macro_rules! impl_unsigned_int_into_signed( macro_rules! impl_common_ops_for_newtype_uint( ($typ:ty, $inner:ty) => { + impl_common_ops_for_newtype_uint!($typ, $inner, one: 1); + }; + + ($typ:ty, $inner:ty, one: $one:expr$(,)?) => { impl $typ { pub const ZERO: Self = Self(0); pub const NONE: Option = None; + // rustdoc-stripper-ignore-next + /// The unitary value. + pub const ONE: Self = Self($one); pub const MAX_SIGNED: crate::Signed::<$typ> = crate::Signed::Positive(Self::MAX); pub const MIN_SIGNED: crate::Signed::<$typ> = crate::Signed::Negative(Self::MAX); @@ -1501,22 +1508,30 @@ macro_rules! impl_format_value_traits( ); macro_rules! option_glib_newtype_from_to { - ($typ_:ident, $none_value:expr) => { + ($typ:ident, $none_value:expr) => { #[doc(hidden)] - impl IntoGlib for $typ_ { + impl IntoGlib for $typ { type GlibType = u64; fn into_glib(self) -> u64 { + assert_ne!( + self.0, $none_value, + concat!( + "attempt to build a `None` glib variant", + "from a non-`Option` type ", + stringify!($typ), + ), + ); self.0 } } #[doc(hidden)] - impl OptionIntoGlib for $typ_ { + impl OptionIntoGlib for $typ { const GLIB_NONE: u64 = $none_value; } #[doc(hidden)] - impl TryFromGlib for $typ_ { + impl TryFromGlib for $typ { type Error = GlibNoneError; #[inline] unsafe fn try_from_glib(val: u64) -> Result { @@ -1525,7 +1540,7 @@ macro_rules! option_glib_newtype_from_to { return Err(GlibNoneError); } - Ok($typ_(val)) + Ok($typ(val)) } } }; diff --git a/gstreamer/src/format/mod.rs b/gstreamer/src/format/mod.rs index cd5489d7c..c167be256 100644 --- a/gstreamer/src/format/mod.rs +++ b/gstreamer/src/format/mod.rs @@ -17,7 +17,8 @@ //! Specific formatted values are also guaranteed to always represent a valid value. //! For instance: //! -//! - [`Percent`] only allows values in the range [0, 1_000_000]. +//! - [`Percent`] only allows values in the integer range [0, 1_000_000] or +//! float range [0.0, 1.0]. //! - [`ClockTime`] can use all `u64` values except `u64::MAX` which is reserved by //! the C constant `GST_CLOCK_TIME_NONE`. //! @@ -60,6 +61,52 @@ //! assert_eq!(start.format(), gst::Format::Time); //! ``` //! +//! ### Building a specific formatted value +//! +//! ``` +//! # use gstreamer as gst; +//! use gst::format::{Buffers, Bytes, ClockTime, Default, Percent}; +//! +//! // Specific formatted values implement the faillible `try_from` constructor: +//! let default = Default::try_from(42).unwrap(); +//! assert_eq!(*default, 42); +//! assert_eq!(Default::try_from(42), Ok(default)); +//! assert_eq!(Default::try_from(42).ok(), Some(default)); +//! +//! // `ClockTime` provides specific constructors: +//! let time = ClockTime::from_nseconds(45_834_908_569_837); +//! let time = ClockTime::from_seconds(20); +//! +//! // This can be convenient: +//! assert_eq!( +//! 20 * ClockTime::MSECOND, +//! ClockTime::from_nseconds(20_000_000), +//! ); +//! assert_eq!( +//! 40 * ClockTime::SECOND, +//! ClockTime::from_nseconds(40_000_000_000), +//! ); +//! +//! // Specific formatted values provide the `ONE` value: +//! assert_eq!(*(128 * Buffers::ONE), 128); +//! +//! // `ZERO` and `NONE` can also come in handy sometimes: +//! assert_eq!(*Buffers::ZERO, 0); +//! assert!(ClockTime::NONE.is_none()); +//! +//! // `Bytes` also comes with usual multipliers: +//! assert_eq!(*(512 * Bytes::K), 512 * 1024); +//! assert_eq!(*(8 * Bytes::M), 8 * 1024 * 1024); +//! assert_eq!(*(4 * Bytes::G), 4 * 1024 * 1024 * 1024); +//! +//! // `Percent` can be built from a float: +//! let a_quarter = Percent::try_from(0.25).unwrap(); +//! // `Percent` has `SCALE` which represents 100%: +//! assert_eq!(Percent::SCALE / 4, a_quarter); +//! // ... and `ONE` which is 1%: +//! assert_eq!(25 * Percent::ONE, a_quarter); +//! ``` +//! //! ### Displaying a formatted value //! //! Formatted values implement the [`Display`] trait which allows getting @@ -334,7 +381,7 @@ //! // Signed formatted values implement the `MulDiv` trait: //! # use gst::prelude::MulDiv; //! # let rate = 48000u64; -//! let samples = gst::format::Default(1024).into_negative(); +//! let samples = (1024 * gst::format::Default::ONE).into_negative(); //! let duration = samples //! .mul_div_round(*gst::ClockTime::SECOND, rate) //! .map(|signed_default| { @@ -383,7 +430,7 @@ //! # use gstreamer as gst; //! # use gst::prelude::{Displayable, ElementExtManual}; //! # gst::init(); -//! # let event = gst::event::SegmentDone::new(gst::format::Buffers(512)); +//! # let event = gst::event::SegmentDone::new(512 * gst::format::Buffers::ONE); //! if let gst::EventView::SegmentDone(seg_done_evt) = event.view() { //! use gst::GenericFormattedValue::*; //! match seg_done_evt.get() { @@ -592,11 +639,11 @@ mod tests { fn incompatible() { with_compatible_formats( ClockTime::ZERO, - GenericFormattedValue::Buffers(Some(Buffers(42))), + GenericFormattedValue::Buffers(Some(42 * Buffers::ONE)), ) .unwrap_err(); with_compatible_formats( - GenericFormattedValue::Buffers(Some(Buffers(42))), + GenericFormattedValue::Buffers(Some(42 * Buffers::ONE)), ClockTime::NONE, ) .unwrap_err(); @@ -744,20 +791,18 @@ mod tests { assert!(signed.positive().is_none()); assert_eq!(signed.signum(), -1); - let def = Default(1); - - let signed = def.into_positive(); - assert_eq!(signed, Signed::Positive(def)); + let signed = Default::ONE.into_positive(); + assert_eq!(signed, Signed::Positive(Default::ONE)); assert!(signed.is_positive()); - assert_eq!(signed.positive(), Some(def)); + assert_eq!(signed.positive(), Some(Default::ONE)); assert!(!signed.is_negative()); assert!(signed.negative().is_none()); assert_eq!(signed.signum(), 1); - let signed = def.into_negative(); - assert_eq!(signed, Signed::Negative(def)); + let signed = Default::ONE.into_negative(); + assert_eq!(signed, Signed::Negative(Default::ONE)); assert!(signed.is_negative()); - assert_eq!(signed.negative(), Some(def)); + assert_eq!(signed.negative(), Some(Default::ONE)); assert!(!signed.is_positive()); assert!(signed.positive().is_none()); assert_eq!(signed.signum(), -1); @@ -855,14 +900,14 @@ mod tests { #[test] fn display_new_types() { - let bytes = Bytes(42); + let bytes = 42 * Bytes::ONE; assert_eq!(&format!("{bytes}"), "42 bytes"); assert_eq!(&format!("{}", bytes.display()), "42 bytes"); assert_eq!(&format!("{}", Some(bytes).display()), "42 bytes"); assert_eq!(&format!("{}", Bytes::NONE.display()), "undef. bytes"); - let gv_1 = GenericFormattedValue::Percent(Percent::try_from(0.42).ok()); + let gv_1 = GenericFormattedValue::Percent(Some(42 * Percent::ONE)); assert_eq!(&format!("{gv_1}"), "42 %"); assert_eq!( &format!("{}", GenericFormattedValue::Percent(None)), @@ -890,24 +935,25 @@ mod tests { #[test] fn display_signed() { - let p_bytes = Bytes(42).into_positive(); + let bytes_42 = 42 * Bytes::ONE; + let p_bytes = bytes_42.into_positive(); assert_eq!(&format!("{p_bytes}"), "+42 bytes"); assert_eq!(&format!("{}", p_bytes.display()), "+42 bytes"); let some_p_bytes = Some(p_bytes); assert_eq!(&format!("{}", some_p_bytes.display()), "+42 bytes"); - let p_some_bytes = Signed::Positive(Some(Bytes(42))); + let p_some_bytes = Signed::Positive(Some(bytes_42)); assert_eq!(&format!("{}", p_some_bytes.display()), "+42 bytes"); - let n_bytes = Bytes(42).into_negative(); + let n_bytes = bytes_42.into_negative(); assert_eq!(&format!("{n_bytes}"), "-42 bytes"); assert_eq!(&format!("{}", n_bytes.display()), "-42 bytes"); let some_n_bytes = Some(n_bytes); assert_eq!(&format!("{}", some_n_bytes.display()), "-42 bytes"); - let n_some_bytes = Signed::Negative(Some(Bytes(42))); + let n_some_bytes = Signed::Negative(Some(bytes_42)); assert_eq!(&format!("{}", n_some_bytes.display()), "-42 bytes"); let p_none_bytes = Signed::Positive(Bytes::NONE); diff --git a/gstreamer/src/format/specific.rs b/gstreamer/src/format/specific.rs index 91f193e7e..1d0194578 100644 --- a/gstreamer/src/format/specific.rs +++ b/gstreamer/src/format/specific.rs @@ -21,7 +21,7 @@ pub trait SpecificFormattedValueFullRange: FormattedValueFullRange {} pub trait SpecificFormattedValueIntrinsic: TryFromGlib + FormattedValueIntrinsic {} #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug, Default)] -pub struct Buffers(pub u64); +pub struct Buffers(u64); impl Buffers { #[doc(alias = "GST_BUFFER_OFFSET_NONE")] pub const OFFSET_NONE: u64 = ffi::GST_BUFFER_OFFSET_NONE; @@ -35,8 +35,17 @@ option_glib_newtype_from_to!(Buffers, Buffers::OFFSET_NONE); glib_newtype_display!(Buffers, DisplayableOptionBuffers, Format::Buffers); #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug, Default)] -pub struct Bytes(pub u64); +pub struct Bytes(u64); impl Bytes { + // rustdoc-stripper-ignore-next + /// 1K Bytes (1024). + pub const K: Self = Self(1024); + // rustdoc-stripper-ignore-next + /// 1M Bytes (1024 * 1024). + pub const M: Self = Self(1024 * 1024); + // rustdoc-stripper-ignore-next + /// 1G Bytes (1024 * 1024 * 1024). + pub const G: Self = Self(1024 * 1024 * 1024); pub const MAX: Self = Self(u64::MAX - 1); } @@ -47,7 +56,7 @@ option_glib_newtype_from_to!(Bytes, u64::MAX); glib_newtype_display!(Bytes, DisplayableOptionBytes, Format::Bytes); #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug, Default)] -pub struct Default(pub u64); +pub struct Default(u64); impl Default { pub const MAX: Self = Self(u64::MAX - 1); } @@ -61,15 +70,19 @@ glib_newtype_display!(Default, DisplayableOptionDefault, Format::Default); pub type Time = super::ClockTime; #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug, Default)] -pub struct Percent(pub u32); +pub struct Percent(u32); impl Percent { #[doc(alias = "GST_FORMAT_PERCENT_MAX")] pub const MAX: Self = Self(ffi::GST_FORMAT_PERCENT_MAX as u32); #[doc(alias = "GST_FORMAT_PERCENT_SCALE")] - pub const SCALE: u32 = ffi::GST_FORMAT_PERCENT_SCALE as u32; + pub const SCALE: Self = Self(ffi::GST_FORMAT_PERCENT_SCALE as u32); } -impl_common_ops_for_newtype_uint!(Percent, u32); +impl_common_ops_for_newtype_uint!( + Percent, + u32, + one: ffi::GST_FORMAT_PERCENT_SCALE as u32 / 100, +); impl_signed_div_mul!(Percent, u32); impl FormattedValue for Option { @@ -227,8 +240,7 @@ impl TryFrom for Percent { impl std::fmt::Display for Percent { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - const ONE_PERCENT: f32 = Percent::SCALE as f32 / 100.0; - std::fmt::Display::fmt(&(self.0 as f32 / ONE_PERCENT), f)?; + std::fmt::Display::fmt(&(self.0 as f32 / (*Percent::ONE) as f32), f)?; f.write_str(" %") } } diff --git a/gstreamer/src/format/undefined.rs b/gstreamer/src/format/undefined.rs index 8ee1350f1..41f9f8a2c 100644 --- a/gstreamer/src/format/undefined.rs +++ b/gstreamer/src/format/undefined.rs @@ -10,6 +10,10 @@ use crate::Format; #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug, Default)] pub struct Undefined(pub i64); +impl Undefined { + pub const ONE: Undefined = Undefined(1); +} + impl FormattedValue for Undefined { type FullRange = Undefined; diff --git a/tutorials/src/bin/basic-tutorial-13.rs b/tutorials/src/bin/basic-tutorial-13.rs index 3154e0ce4..de90d17e2 100644 --- a/tutorials/src/bin/basic-tutorial-13.rs +++ b/tutorials/src/bin/basic-tutorial-13.rs @@ -173,7 +173,7 @@ USAGE: Choose one of the following options, then press enter: Command::NextFrame => { if let Some(video_sink) = pipeline.property::>("video-sink") { // Send the event - let step = Step::new(gst::format::Buffers(1), rate.abs(), true, false); + let step = Step::new(gst::format::Buffers::ONE, rate.abs(), true, false); video_sink.send_event(step); println!("Stepping one frame\r"); }