Skip to content

gst: decouple GenericFormattedValue type bounds

APIs with multiple FormattedValues

Function Element::seek takes 2 FormattedValues: one for start and one for stop. Currently, theses args are defined as being of the same type using a single bind clause:

    fn seek<V: Into<GenericFormattedValue>>(
        ...
        start: V,
        ...
        stop: V,
    ) -> ...

When called with a SpecificFormattedValue such ClockTime, this signature ensures at compilation time that start and stop use the same format. However, this prevents the user from calling seek with e.g. an intrinsic value for start and an Option for stop. E.g.:

    element.seek(
       ...
       gst::ClockTime::ZERO,
       ...
       gst::ClockTime::NONE,
    ).unwrap();

Another example that would fail at compilation time would be using a SpecificFormattedValue for start and a GenericFormattedValue for stop.

Changes in this MR

This MR implements a solution to make the above APIs flexible and safe to use. It strives to enforce type conformity at compilation time when possible and fallbacks to runtime checks in the case of GenericFormattedValues.

Preparation

In order to ease the transition to a model which would implement the above, a first pass on the format module was performed.

The first commit implements the trait FormattedValue for any type that can be used as a FormattedValue. Previously, only types which were able to represent a "full range" FormattedValue implemented the trait. E.g.: GenericFormattedValues, Option<ClockTime> implemented it, but ClockTime didn't.

APIs which would accept any formatted value used a bind clause V: Into<GenericFormattedValue> and the provided value was forced into a GenericFormattedValue in the function.

This change allows using a V: FormattedValue bind instead. In 95% of cases, this is all the function needs: no more conversion to GenericFormattedValue required.

When a function needs a "full range" formatted value instead, we can use the new trait FormattedValueFullRange. The trait FormattedValue uses an associated type FullRange, in order to refer to the full range type. This is needed when building a FormattedValue from a raw value.

Note: I'm not sure about the "full range" name here. This could also be "full scale", ...

Compatible Formatted Values

The second commit introduces the trait CompatibleFormattedValue. This trait uses a FormattedValue type parameter against which we want to check format compatibility for Self. This trait is implemented on compatible combinations only.

For SpecificFormattedValues, the implementation ensures type system level checks.

For checks involving at least one GenericFormattedValue, checks are performed at runtime via the function try_into_checked.

  • Function try_into_checked should result to a no-op when the check can be performed at compilation.
  • User can't use the argument as a FormattedValue unless try_into_checked was called.

In some cases (e.g. Segment<_>), the format to check is not enforced by another argument but by a field of Self. For those cases, the try_into_checked_explicit performs the checks against an explicit Format with as low an overhead as possible.

Note: I'm not 100% sure about the CompatibleFormattedValue and try_into_checked* names here. Alternatives could be:

  • CheckedFormattedValue: shorter but I think we loose a bit of the semantic.
  • Conforming...: not sure if that would be more correct that Compatible.

Initial Proposal

The following § is the initial proposal. It is left here so as to understand the discussion in the first comment.

A workaround could be using different types for start and stop and binding them with the following clause:

    where
        Start: SpecificFormattedValue,
        Stop: SpecificFormattedValue<FormattedValueType = <Start as SpecificFormattedValue>::FormattedValueType>

However, this would prevent the user from calling seek with a GenericFormattedValue.

This commit decouples the types for functions with multiple formatted arguments. Format equivalence will no longer be enforced at compilation time, it will still be controlled at runtime thanks to the existing assertions.

The drawback is that we sometimes need to help the compiler with type inference. See basic-tutorial-13.rs as an example.

The commit also fixes message::StepDone: the duration argument is said to be of Time format in the documentation for gst_message_new_step_done while it was bound to the format of the amount argument.

See also: gst-plugins-rs!797 (merged)

Edited by François Laignel

Merge request reports