Module gstreamer::format

source ·
Expand description

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 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.

§Examples

§Querying the pipeline for a time position

let res = pipeline.query_position::<gst::ClockTime>();

§Seeking to a specific time position

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

// Downcasting the generic `segment` for `gst::ClockTime` use.
let time_segment = segment.downcast_ref::<gst::ClockTime>().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);

§Building a specific formatted value

use gst::prelude::*;
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 `const` constructors,
// which can panic if the requested value is out of range.
let time = ClockTime::from_nseconds(45_834_908_569_837);
let time = ClockTime::from_seconds(20);

// Other formatted values also come with (panicking) `const` constructors:
let buffers_nb = Buffers::from_u64(512);
let received = Bytes::from_u64(64);
let quantity = Default::from_u64(42);

// `Bytes` can be built from an `usize` too (not `const`):
let sample_size = Bytes::from_usize([0u8; 4].len());

// This can be convenient (not `const`):
assert_eq!(
    7.seconds() + 250.mseconds(),
    ClockTime::from_nseconds(7_250_000_000),
);

// Those too (not `const`):
assert_eq!(512.buffers(), Buffers::from_u64(512));
assert_eq!(64.bytes(), Bytes::from_u64(64));
assert_eq!(42.default_format(), Default::from_u64(42));

// The `ZERO` and `NONE` constants can come in handy sometimes:
assert_eq!(*Buffers::ZERO, 0);
assert!(ClockTime::NONE.is_none());

// Specific formatted values provide the constant `ONE` value:
assert_eq!(*Buffers::ONE, 1);

// `Bytes` also comes with usual multipliers (not `const`):
assert_eq!(*(512.kibibytes()), 512 * 1024);
assert_eq!(*(8.mebibytes()), 8 * 1024 * 1024);
assert_eq!(*(4.gibibytes()), 4 * 1024 * 1024 * 1024);

// ... and the matching constants:
assert_eq!(512 * Bytes::KiB, 512.kibibytes());

// `Percent` can be built from a percent integer value:
let a_quarter = 25.percent();
assert_eq!(a_quarter.percent(), 25);
assert_eq!(a_quarter.ppm(), 250000);
assert_eq!(a_quarter.ratio(), 0.25);
// ... from a floating point ratio:
let a_quarter_from_ratio = 0.25.percent_ratio();
assert_eq!(a_quarter, a_quarter_from_ratio);
// ... from a part per million integer value:
let a_quarter_from_ppm = (25 * 10_000).ppm();
assert_eq!(a_quarter, a_quarter_from_ppm);
// ... `MAX` which represents 100%:
assert_eq!(Percent::MAX / 4, a_quarter);
// ... `ONE` which is 1%:
assert_eq!(25 * Percent::ONE, a_quarter);
// ... and `SCALE` which is 1% in ppm:
assert_eq!(Percent::SCALE, 10_000.ppm());

§Displaying a formatted value

Formatted values implement the Display trait which allows getting human readable representations.

let time = 45_834_908_569_837.nseconds();

assert_eq!(format!("{time}"), "12:43:54.908569837");
assert_eq!(format!("{time:.0}"), "12:43:54");

let percent = 0.1234.percent_ratio();
assert_eq!(format!("{percent}"), "12.34 %");
assert_eq!(format!("{percent:5.1}"), " 12.3 %");

§Some operations available on specific formatted values

let cur_pos = gst::ClockTime::ZERO;

// All four arithmetic operations can be used:
let fwd = cur_pos + 2.seconds() / 3 - 5.mseconds();

// Examples of operations which make sure not to overflow:
let bwd = cur_pos.saturating_sub(2.seconds());
let further = cur_pos.checked_mul(2).expect("Overflowed");

// Specific formatted values can be compared:
assert!(fwd > bwd);
assert_ne!(fwd, cur_pos);

// 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:
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<F>. This departs from the C APIs which use 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::<F>::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

let seek_evt = gst::event::Seek::new(
    1.0f64,
    seek_flags,
    gst::SeekType::Set,
    10.seconds(),         // 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<FormattedValue> though. This is why displaying an optional formatted value necessitates calling display().

let opt_time = Some(45_834_908_569_837.nseconds());

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

let pts = Some(gst::ClockTime::ZERO);
assert!(pts.is_some());

// All four arithmetic operations can be used. Ex.:
let fwd = pts.opt_add(2.seconds());
// `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.seconds());
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 gst::Signed::*;
match segment.to_running_time_full(2.seconds()) {
    Some(Positive(pos_rtime)) => println!("positive rtime {pos_rtime}"),
    Some(Negative(neg_rtime)) => println!("negative rtime {neg_rtime}"),
    None => println!("undefined rtime"),
}

§Converting a formatted value into a signed formatted value

let step = 10.mseconds();

let positive_step = step.into_positive();
assert!(positive_step.is_positive());

let negative_step = step.into_negative();
assert!(negative_step.is_negative());

§Handling one sign only

let pos_step = 10.mseconds().into_positive();
assert!(pos_step.is_positive());

let abs_step_or_panic = pos_step.positive().expect("positive");
let abs_step_or_zero = pos_step.positive().unwrap_or(gst::ClockTime::ZERO);

let abs_step_or_err = pos_step.positive_or(NegativeError);
let abs_step_or_else_err = pos_step.positive_or_else(|step| {
    println!("{step} is negative");
    NegativeError
});

§Displaying a signed formatted value

let start = segment.start().unwrap();
assert_eq!(format!("{start:.0}"), "0:00:10");

let p_rtime = segment.to_running_time_full(20.seconds());
// 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.

let p_one_sec = gst::ClockTime::SECOND.into_positive();
let p_two_sec = 2.seconds().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);

// 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:
let samples = 1024.default_format().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::<gst::ClockTime>::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.

let p_one_sec = 1.seconds().into_positive();
let p_two_sec = 2.seconds().into_positive();
let n_one_sec = 1.seconds().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

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:?}"),
    }
}

Modules§

Structs§

Enums§

Traits§

Type Aliases§