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 allu64
values exceptu64::MAX
which is reserved by the C constantGST_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§
- A signed wrapper.
Traits§
Buffers
formatted value constructor trait.Bytes
formatted value constructor trait.- A trait implemented on types which can hold
FormattedValue
s compatible with parameterF
. Default
formatted value constructor trait.- A
FormattedValue
which can be built from any raw value. - A trait implemented on the intrinsic type of a
FormattedValue
. Other
formatted value constructor trait.Percent
formatted value from float constructor trait.Percent
formatted value from integer constructor trait.- A trait implemented on the intrinsic type of a
SpecificFormattedValue
. ClockTime
formatted value constructor trait.Undefined
formatted value constructor trait.- A trait implemented on unsigned types which can be converted into
crate::Signed
s.