From bff1354b74c8d1296216d1a3a42f42c44d89dbe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Laignel?= Date: Sat, 1 Oct 2022 12:39:47 +0200 Subject: [PATCH] gst/format: module doc --- gstreamer/src/format/compatible.rs | 2 +- gstreamer/src/format/mod.rs | 402 +++++++++++++++++++++++++++++ 2 files changed, 403 insertions(+), 1 deletion(-) diff --git a/gstreamer/src/format/compatible.rs b/gstreamer/src/format/compatible.rs index 0fef7d53d..9cbe7cda3 100644 --- a/gstreamer/src/format/compatible.rs +++ b/gstreamer/src/format/compatible.rs @@ -9,7 +9,7 @@ use super::{FormattedValue, SpecificFormattedValue}; /// This trait is auto-implemented based on [`FormattedValue`]s additional traits /// such as [`SpecificFormattedValue`]. /// -/// # Example: +/// # Example /// /// Consider the following function: /// diff --git a/gstreamer/src/format/mod.rs b/gstreamer/src/format/mod.rs index 3d56f9468..959350ac2 100644 --- a/gstreamer/src/format/mod.rs +++ b/gstreamer/src/format/mod.rs @@ -1,5 +1,407 @@ // Take a look at the license at the top of the repository in the LICENSE file. +// rustdoc-stripper-ignore-next +//! This modules gathers GStreamer's formatted value concepts together. +//! +//! GStreamer uses formatted values to differentiate value units in some APIs. +//! In C this is done by qualifying an integer value by a companion enum +//! [`GstFormat`]. In Rust, most APIs can use a specific type for each format. +//! Each format type embeds the actual value using the new type pattern. +//! +//! # Specific Formatted Values +//! +//! Examples of specific formatted values include [`ClockTime`], [`Buffers`], etc. +//! These types represent both the quantity and the unit making it possible for Rust +//! to perform runtime and, to a certain extent, compile time invariants enforcement. +//! +//! 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]. +//! - [`ClockTime`] can use all `u64` values except `u64::MAX` which is reserved by +//! the C constant `GST_CLOCK_TIME_NONE`. +//! +//! ## Examples +//! +//! ### Querying the pipeline for a time position +//! +//! ``` +//! # use gstreamer as gst; +//! # use gst::prelude::ElementExtManual; +//! # gst::init(); +//! # let pipeline = gst::Pipeline::new(None); +//! let res = pipeline.query_position::(); +//! ``` +//! +//! ## Seeking to a specific time position +//! +//! ``` +//! # use gstreamer as gst; +//! # use gst::prelude::ElementExtManual; +//! # gst::init(); +//! # let pipeline = gst::Pipeline::new(None); +//! # let seek_flags = gst::SeekFlags::FLUSH | gst::SeekFlags::KEY_UNIT; +//! let seek_pos = gst::ClockTime::from_seconds(10); +//! let res = pipeline.seek_simple(seek_flags, seek_pos); +//! ``` +//! +//! ### Downcasting a `Segment` for specific formatted value use +//! +//! ``` +//! # use gstreamer as gst; +//! # use gst::FormattedValue; +//! # gst::init(); +//! # let segment = gst::FormattedSegment::::new().upcast(); +//! // Downcasting the generic `segment` for `gst::ClockTime` use. +//! let time_segment = segment.downcast_ref::().expect("time segment"); +//! // Setters and getters conform to `gst::ClockTime`. +//! // This is enforced at compilation time. +//! let start = time_segment.start(); +//! assert_eq!(start.format(), gst::Format::Time); +//! ``` +//! +//! ### Displaying a formatted value +//! +//! Formatted values implement the [`Display`] trait which allows getting +//! human readable representations. +//! +//! ``` +//! # use gstreamer as gst; +//! # use gst::prelude::Displayable; +//! let time = gst::ClockTime::from_nseconds(45_834_908_569_837); +//! +//! assert_eq!(format!("{}", time), "12:43:54.908569837"); +//! assert_eq!(format!("{:.0}", time), "12:43:54"); +//! ``` +//! +//! ## Some operations available on specific formatted values +//! +//! ``` +//! # use gstreamer as gst; +//! let cur_pos = gst::ClockTime::ZERO; +//! +//! // All four arithmetic operations can be used: +//! let fwd = cur_pos + 2 * gst::ClockTime::SECOND / 3 - gst::ClockTime::MSECOND; +//! +//! // Examples of operations which make sure not to overflow: +//! let bwd = cur_pos.saturating_sub(2 * gst::ClockTime::SECOND); +//! let further = cur_pos.checked_mul(2).expect("Overflowed"); +//! +//! // Specific formatted values can be compared: +//! assert!(fwd > bwd); +//! assert_ne!(fwd, cur_pos); +//! +//! # fn next() -> gst::ClockTime { gst::ClockTime::ZERO }; +//! // Use `gst::ClockTime::MAX` for the maximum valid value: +//! let mut min_pos = gst::ClockTime::MAX; +//! for _ in 0..4 { +//! min_pos = min_pos.min(next()); +//! } +//! +//! // And `gst::ClockTime::ZERO` for the minimum value: +//! let mut max_pos = gst::ClockTime::ZERO; +//! for _ in 0..4 { +//! max_pos = max_pos.max(next()); +//! } +//! +//! // Specific formatted values implement the `MulDiv` trait: +//! # use gst::prelude::MulDiv; +//! # let (samples, rate) = (1024u64, 48000u64); +//! let duration = samples +//! .mul_div_round(*gst::ClockTime::SECOND, rate) +//! .map(gst::ClockTime::from_nseconds); +//! ``` +//! +//! ## Types in operations +//! +//! Additions and substractions are available with the specific formatted value type +//! as both left and right hand side operands. +//! +//! On the other hand, multiplications are only available with plain integers. +//! This is because multiplying a `ClockTime` by a `ClockTime` would result in +//! `ClockTime²`, whereas a `u64 * ClockTime` (or `ClockTime * u64`) still +//! results in `ClockTime`. +//! +//! Divisions are available with both the specific formatted value and plain +//! integers as right hand side operands. The difference is that +//! `ClockTime / ClockTime` results in `u64` and `ClockTime / u64` results in +//! `ClockTime`. +//! +//! # Optional specific formatted values +//! +//! Optional specific formatted values are represented as a standard Rust +//! `Option`. This departs from the C APIs which uses a sentinel that must +//! be checked in order to figure out whether the value is defined. +//! +//! Besides giving access to the usual `Option` features, this ensures the APIs +//! enforce mandatory or optional variants whenever possible. +//! +//! Note: for each specific formatted value `F`, the constant `F::NONE` is defined +//! as a shortcut for `Option::::None`. For `gst::ClockTime`, this constant is +//! equivalent to the C constant `GST_CLOCK_TIME_NONE`. +//! +//! ## Examples +//! +//! ### Building a seek `Event` with undefined `stop` time +//! +//! ``` +//! # use gstreamer as gst; +//! # gst::init(); +//! # let seek_flags = gst::SeekFlags::FLUSH | gst::SeekFlags::KEY_UNIT; +//! let seek_evt = gst::event::Seek::new( +//! 1.0f64, +//! seek_flags, +//! gst::SeekType::Set, +//! 10 * gst::ClockTime::SECOND, // start at 10s +//! gst::SeekType::Set, +//! gst::ClockTime::NONE, // stop is undefined +//! ); +//! ``` +//! +//! ### Displaying an optional formatted value +//! +//! Optional formatted values can take advantage of the [`Display`] implementation +//! of the base specific formatted value. We have to workaround the [orphan rule] +//! that forbids the implementation of [`Display`] for `Option` +//! though. This is why displaying an optional formatted value necessitates calling +//! [`display()`]. +//! +//! ``` +//! # use gstreamer as gst; +//! # use gst::prelude::Displayable; +//! let opt_time = Some(45_834_908_569_837 * gst::ClockTime::NSECOND); +//! +//! assert_eq!(format!("{}", opt_time.display()), "12:43:54.908569837"); +//! assert_eq!(format!("{:.0}", opt_time.display()), "12:43:54"); +//! assert_eq!(format!("{:.0}", gst::ClockTime::NONE.display()), "--:--:--"); +//! ``` +//! +//! ### Some operations available on optional formatted values +//! +//! ``` +//! # use gstreamer as gst; +//! # use gst::prelude::*; +//! let pts = Some(gst::ClockTime::ZERO); +//! assert!(pts.is_some()); +//! +//! // All four arithmetic operations can be used. Ex.: +//! let fwd = pts.opt_add(2 * gst::ClockTime::SECOND); +//! // `pts` is defined, so `fwd` will contain the addition result in `Some`, +//! assert!(fwd.is_some()); +//! // otherwise `fwd` would be `None`. +//! +//! // Examples of operations which make sure not to overflow: +//! let bwd = pts.opt_saturating_sub(2 * gst::ClockTime::SECOND); +//! let further = pts.opt_checked_mul(2).expect("Overflowed"); +//! +//! // Optional specific formatted values can be compared: +//! assert_eq!(fwd.opt_gt(bwd), Some(true)); +//! assert_ne!(fwd, pts); +//! assert_eq!(fwd.opt_min(bwd), bwd); +//! +//! // Optional specific formatted values operations also apply to non-optional values: +//! assert_eq!(fwd.opt_lt(gst::ClockTime::SECOND), Some(false)); +//! assert_eq!(gst::ClockTime::SECOND.opt_lt(fwd), Some(true)); +//! +//! // Comparing a defined values to an undefined value results in `None`: +//! assert_eq!(bwd.opt_gt(gst::ClockTime::NONE), None); +//! assert_eq!(gst::ClockTime::ZERO.opt_lt(gst::ClockTime::NONE), None); +//! ``` +//! +//! # Signed formatted values +//! +//! Some APIs can return a signed formatted value. See [`Segment::to_running_time_full`] +//! for an example. In Rust, we use the [`Signed`] enum wrapper around the actual +//! formatted value. +//! +//! For each signed specific formatted value `F`, the constants `F::MIN_SIGNED` and +//! `F::MAX_SIGNED` represent the minimum and maximum signed values for `F`. +//! +//! ## Examples +//! +//! ### Handling a signed formatted value +//! +//! ``` +//! # use gstreamer as gst; +//! # gst::init(); +//! # let segment = gst::FormattedSegment::::new(); +//! use gst::Signed::*; +//! match segment.to_running_time_full(2 * gst::ClockTime::SECOND) { +//! Some(Positive(pos_rtime)) => println!("positive rtime {}", pos_rtime), +//! Some(Negative(pos_rtime)) => println!("negative rtime {}", pos_rtime), +//! None => println!("undefined rtime"), +//! } +//! ``` +//! +//! ### Converting a formatted value into a signed formatted value +//! +//! ``` +//! # use gstreamer as gst; +//! # use gst::prelude::UnsignedIntoSigned; +//! let pos = gst::ClockTime::SECOND; +//! +//! let positive_one_sec = pos.into_positive(); +//! assert!(positive_one_sec.is_positive()); +//! +//! let negative_one_sec = pos.into_negative(); +//! assert!(negative_one_sec.is_negative()); +//! ``` +//! +//! ### Handling one sign only +//! +//! ``` +//! # use gstreamer as gst; +//! # use gst::prelude::UnsignedIntoSigned; +//! # struct NegativeError; +//! let p_one_sec = gst::ClockTime::SECOND.into_positive(); +//! +//! let one_sec = p_one_sec.positive().expect("positive"); +//! let one_sec_or_zero = p_one_sec.positive().unwrap_or(gst::ClockTime::ZERO); +//! +//! let one_sec_or_err = p_one_sec.positive_or(NegativeError); +//! let one_sec_or_else_err = p_one_sec.positive_or_else(|value| { +//! println!("{} is negative", value); +//! NegativeError +//! }); +//! ``` +//! +//! ### Displaying a signed formatted value +//! +//! ``` +//! # use gstreamer as gst; +//! # use gst::prelude::Displayable; +//! # gst::init(); +//! # let mut segment = gst::FormattedSegment::::new(); +//! # segment.set_start(10 * gst::ClockTime::SECOND); +//! let start = segment.start().unwrap(); +//! assert_eq!(format!("{:.0}", start), "0:00:10"); +//! +//! let p_rtime = segment.to_running_time_full(20 * gst::ClockTime::SECOND); +//! // Use `display()` with optional signed values. +//! assert_eq!(format!("{:.0}", p_rtime.display()), "+0:00:10"); +//! +//! let p_rtime = segment.to_running_time_full(gst::ClockTime::ZERO); +//! assert_eq!(format!("{:.0}", p_rtime.display()), "-0:00:10"); +//! +//! let p_rtime = segment.to_running_time_full(gst::ClockTime::NONE); +//! assert_eq!(format!("{:.0}", p_rtime.display()), "--:--:--"); +//! ``` +//! +//! ## Some operations available for signed formatted values +//! +//! All the operations available for formatted values can be used with +//! signed formatted values. +//! +//! ``` +//! # use gstreamer as gst; +//! # use gst::prelude::UnsignedIntoSigned; +//! let p_one_sec = gst::ClockTime::SECOND.into_positive(); +//! let p_two_sec = (2 * gst::ClockTime::SECOND).into_positive(); +//! let n_one_sec = gst::ClockTime::SECOND.into_negative(); +//! +//! assert_eq!(p_one_sec + p_one_sec, p_two_sec); +//! assert_eq!(p_two_sec - p_one_sec, p_one_sec); +//! assert_eq!(gst::ClockTime::ZERO - p_one_sec, n_one_sec); +//! assert_eq!(p_one_sec * 2u64, p_two_sec); +//! assert_eq!(n_one_sec * -1i64, p_one_sec); +//! assert_eq!(p_two_sec / 2u64, p_one_sec); +//! assert_eq!(p_two_sec / p_one_sec, 2); +//! +//! // Examples of operations which make sure not to overflow: +//! assert_eq!(p_one_sec.saturating_sub(p_two_sec), n_one_sec); +//! assert_eq!(p_one_sec.checked_mul(2), Some(p_two_sec)); +//! +//! // Signed formatted values can be compared: +//! assert!(p_one_sec > n_one_sec); +//! +//! # fn next() -> gst::Signed { gst::ClockTime::ZERO.into_positive() }; +//! // Use `gst::ClockTime::MAX_SIGNED` for the maximum valid signed value: +//! let mut min_signed_pos = gst::ClockTime::MAX_SIGNED; +//! for _ in 0..4 { +//! min_signed_pos = min_signed_pos.min(next()); +//! } +//! +//! // And `gst::ClockTime::MIN_SIGNED` for the minimum valid signed value: +//! let mut max_signed_pos = gst::ClockTime::MIN_SIGNED; +//! for _ in 0..4 { +//! max_signed_pos = max_signed_pos.max(next()); +//! } +//! +//! // Signed formatted values implement the `MulDiv` trait: +//! # use gst::prelude::MulDiv; +//! # let rate = 48000u64; +//! let samples = gst::format::Default(1024).into_negative(); +//! let duration = samples +//! .mul_div_round(*gst::ClockTime::SECOND, rate) +//! .map(|signed_default| { +//! let signed_u64 = signed_default.into_inner_signed(); +//! gst::Signed::::from_nseconds(signed_u64) +//! }) +//! .unwrap(); +//! assert!(duration.is_negative()); +//! ``` +//! +//! ### Some operations available for optional signed formatted values +//! +//! All the operations available for optional formatted values can be used +//! with signed formatted values. +//! +//! ``` +//! # use gstreamer as gst; +//! # use gst::prelude::*; +//! let p_one_sec = gst::ClockTime::SECOND.into_positive(); +//! let p_two_sec = (2 * gst::ClockTime::SECOND).into_positive(); +//! let n_one_sec = gst::ClockTime::SECOND.into_negative(); +//! +//! // Signed `ClockTime` addition with optional and non-optional operands. +//! assert_eq!(Some(p_one_sec).opt_add(p_one_sec), Some(p_two_sec)); +//! assert_eq!(p_two_sec.opt_add(Some(n_one_sec)), Some(p_one_sec)); +//! +//! // This can also be used with unsigned formatted values. +//! assert_eq!(Some(p_one_sec).opt_add(gst::ClockTime::SECOND), Some(p_two_sec)); +//! +//! // Etc... +//! ``` +//! +//! # Generic Formatted Values +//! +//! Sometimes, generic code can't assume a specific format will be used. For such +//! use cases, the [`GenericFormattedValue`] enum makes it possible to select +//! the appropriate behaviour at runtime. +//! +//! Most variants embed an optional specific formatted value. +//! +//! ## Example +//! +//! ### Generic handling of the position from a `SegmentDone` event +//! +//! ``` +//! # use gstreamer as gst; +//! # use gst::prelude::{Displayable, ElementExtManual}; +//! # gst::init(); +//! # let event = gst::event::SegmentDone::new(gst::format::Buffers(512)); +//! if let gst::EventView::SegmentDone(seg_done_evt) = event.view() { +//! use gst::GenericFormattedValue::*; +//! match seg_done_evt.get() { +//! Buffers(buffers) => println!("Segment done @ {}", buffers.display()), +//! Bytes(bytes) => println!("Segment done @ {}", bytes.display()), +//! Time(time) => println!("Segment done @ {}", time.display()), +//! other => println!("Unexpected format for Segment done position {other:?}"), +//! } +//! } +//! ``` +//! +//! [`GstFormat`]: https://gstreamer.freedesktop.org/documentation/gstreamer/gstformat.html?gi-language=c +//! [`ClockTime`]: struct.ClockTime.html +//! [`Buffers`]: struct.Buffers.html +//! [`Percent`]: struct.Percent.html +//! [`Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html +//! [`display()`]: ../prelude/trait.Displayable.html +//! [orphan rule]: https://doc.rust-lang.org/book/ch10-02-traits.html?highlight=orphan#implementing-a-trait-on-a-type +//! [`Segment::to_running_time_full`]: ../struct.FormattedSegment.html#method.to_running_time_full +//! [`Signed`]: enum.Signed.html +//! [`GenericFormattedValue`]: generic/enum.GenericFormattedValue.html + use thiserror::Error; #[macro_use]