Compare commits

...

3 commits

Author SHA1 Message Date
Jan Alexander Steffens b8ee041397 Merge branch 'mul-div-frac' into 'main'
value: Provide MulDiv extensions for fractions

See merge request gstreamer/gstreamer-rs!1423
2024-04-26 18:56:34 +00:00
François Laignel 953e3747f2 Pad: allow building a Pad with an automatically generated name
For convenience, the `Pad` builder checks a name is provided when a wildcard-
named template is used. For `GhostPad`s, the builder tries to assign the name of
the target `Pad` making sure the provided `name` conforms to the `PadTemplate`.

This commit adds a function to optionally keep the `gst::Object` automatically
generated unique `Pad` name (such as `ghostpad4`) and reorganises name handling
so it is processed when `build` is invoked.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/-/merge_requests/1428>
2024-04-26 09:57:28 +00:00
Jan Alexander Steffens (heftig) 5a553a1ff6
value: Provide MulDiv extensions for fractions 2024-04-16 16:25:32 +02:00
4 changed files with 268 additions and 136 deletions

View file

@ -56,6 +56,8 @@ impl GhostPad {
// rustdoc-stripper-ignore-next // rustdoc-stripper-ignore-next
/// Creates a new [`GhostPad`] with an automatically generated name. /// Creates a new [`GhostPad`] with an automatically generated name.
/// ///
/// The [`Pad`] will be assigned the usual `gst::Object` generated unique name.
///
/// Use [`GhostPad::builder_from_template()`] to get a [`PadBuilder`](crate::PadBuilder) /// Use [`GhostPad::builder_from_template()`] to get a [`PadBuilder`](crate::PadBuilder)
/// and define options. /// and define options.
#[doc(alias = "gst_ghost_pad_new_no_target")] #[doc(alias = "gst_ghost_pad_new_no_target")]
@ -65,10 +67,7 @@ impl GhostPad {
} }
// rustdoc-stripper-ignore-next // rustdoc-stripper-ignore-next
/// Creates a [`PadBuilder`](crate::PadBuilder) for a [`PadBuilder`] with an automatically generated name. /// Creates a [`PadBuilder`](crate::PadBuilder) with the specified [`PadDirection`](crate::PadDirection).
///
/// Use [`PadBuilder::name`](crate::PadBuilder::name) or [`PadBuilder::maybe_name`](crate::PadBuilder::maybe_name)
/// to specify a different name.
#[doc(alias = "gst_ghost_pad_new_no_target")] #[doc(alias = "gst_ghost_pad_new_no_target")]
pub fn builder(direction: crate::PadDirection) -> PadBuilder<Self> { pub fn builder(direction: crate::PadDirection) -> PadBuilder<Self> {
skip_assert_initialized!(); skip_assert_initialized!();
@ -82,12 +81,15 @@ impl GhostPad {
/// i.e. if it's not a wildcard-name containing `%u`, `%s` or `%d`, /// i.e. if it's not a wildcard-name containing `%u`, `%s` or `%d`,
/// the `GhostPad` will automatically be named after the `name_template`. /// the `GhostPad` will automatically be named after the `name_template`.
/// ///
/// Use [`GhostPad::builder_from_template()`] to get a [`PadBuilder`](crate::PadBuilder)
/// and define options.
///
/// Use [`generated_name()`](crate::PadBuilder::generated_name`) to keep the `gst::Object`
/// automatically generated unique name.
///
/// # Panics /// # Panics
/// ///
/// Panics if the `name_template` is a wildcard-name. /// Panics if the `name_template` is a wildcard-name.
///
/// Use [`GhostPad::builder_from_template()`] to get a [`PadBuilder`](crate::PadBuilder)
/// and define options.
#[doc(alias = "gst_ghost_pad_new_no_target_from_static_template")] #[doc(alias = "gst_ghost_pad_new_no_target_from_static_template")]
pub fn from_static_template(templ: &StaticPadTemplate) -> Self { pub fn from_static_template(templ: &StaticPadTemplate) -> Self {
skip_assert_initialized!(); skip_assert_initialized!();
@ -103,6 +105,9 @@ impl GhostPad {
/// ///
/// Use [`PadBuilder::name`](crate::PadBuilder::name) or [`PadBuilder::maybe_name`](crate::PadBuilder::maybe_name) /// Use [`PadBuilder::name`](crate::PadBuilder::name) or [`PadBuilder::maybe_name`](crate::PadBuilder::maybe_name)
/// to specify a different name. /// to specify a different name.
///
/// Use [`generated_name()`](crate::PadBuilder::generated_name`) to keep the `gst::Object`
/// automatically generated unique name.
#[doc(alias = "gst_ghost_pad_new_no_target_from_static_template")] #[doc(alias = "gst_ghost_pad_new_no_target_from_static_template")]
pub fn builder_from_static_template(templ: &StaticPadTemplate) -> PadBuilder<Self> { pub fn builder_from_static_template(templ: &StaticPadTemplate) -> PadBuilder<Self> {
skip_assert_initialized!(); skip_assert_initialized!();
@ -116,12 +121,15 @@ impl GhostPad {
/// i.e. if it's not a wildcard-name containing `%u`, `%s` or `%d`, /// i.e. if it's not a wildcard-name containing `%u`, `%s` or `%d`,
/// the `GhostPad` will automatically be named after the `name_template`. /// the `GhostPad` will automatically be named after the `name_template`.
/// ///
/// Use [`GhostPad::builder_from_template()`] to get a [`PadBuilder`](crate::PadBuilder)
/// and define options.
///
/// Use [`generated_name()`](crate::PadBuilder::generated_name`) to keep the `gst::Object`
/// automatically generated unique name.
///
/// # Panics /// # Panics
/// ///
/// Panics if the `name_template` is a wildcard-name. /// Panics if the `name_template` is a wildcard-name.
///
/// Use [`GhostPad::builder_from_template()`] to get a [`PadBuilder`](crate::PadBuilder)
/// and define options.
#[doc(alias = "gst_ghost_pad_new_no_target_from_template")] #[doc(alias = "gst_ghost_pad_new_no_target_from_template")]
pub fn from_template(templ: &crate::PadTemplate) -> Self { pub fn from_template(templ: &crate::PadTemplate) -> Self {
skip_assert_initialized!(); skip_assert_initialized!();
@ -137,6 +145,9 @@ impl GhostPad {
/// ///
/// Use [`PadBuilder::name`](crate::PadBuilder::name) or [`PadBuilder::maybe_name`](crate::PadBuilder::maybe_name) /// Use [`PadBuilder::name`](crate::PadBuilder::name) or [`PadBuilder::maybe_name`](crate::PadBuilder::maybe_name)
/// to specify a different name. /// to specify a different name.
///
/// Use [`generated_name()`](crate::PadBuilder::generated_name`) to keep the `gst::Object`
/// automatically generated unique name.
#[doc(alias = "gst_ghost_pad_new_no_target_from_template")] #[doc(alias = "gst_ghost_pad_new_no_target_from_template")]
pub fn builder_from_template(templ: &crate::PadTemplate) -> PadBuilder<Self> { pub fn builder_from_template(templ: &crate::PadTemplate) -> PadBuilder<Self> {
skip_assert_initialized!(); skip_assert_initialized!();
@ -150,6 +161,9 @@ impl GhostPad {
/// ///
/// Use [`GhostPad::builder_with_target()`] to get a [`PadBuilder`](crate::PadBuilder) /// Use [`GhostPad::builder_with_target()`] to get a [`PadBuilder`](crate::PadBuilder)
/// and define options. /// and define options.
///
/// Use [`generated_name()`](crate::PadBuilder::generated_name`) to keep the `gst::Object`
/// automatically generated unique name.
#[doc(alias = "gst_ghost_pad_new")] #[doc(alias = "gst_ghost_pad_new")]
pub fn with_target<P: IsA<Pad> + IsA<crate::Object>>( pub fn with_target<P: IsA<Pad> + IsA<crate::Object>>(
target: &P, target: &P,
@ -165,13 +179,15 @@ impl GhostPad {
/// ///
/// Use [`PadBuilder::name`](crate::PadBuilder::name) or [`PadBuilder::maybe_name`](crate::PadBuilder::maybe_name) /// Use [`PadBuilder::name`](crate::PadBuilder::name) or [`PadBuilder::maybe_name`](crate::PadBuilder::maybe_name)
/// to specify a different name. /// to specify a different name.
///
/// Use [`generated_name()`](crate::PadBuilder::generated_name`) to keep the `gst::Object`
/// automatically generated unique name.
#[doc(alias = "gst_ghost_pad_new_no_target_from_template")] #[doc(alias = "gst_ghost_pad_new_no_target_from_template")]
pub fn builder_with_target<P: IsA<Pad> + IsA<crate::Object>>( pub fn builder_with_target<P: IsA<Pad> + IsA<crate::Object>>(
target: &P, target: &P,
) -> Result<PadBuilder<Self>, glib::BoolError> { ) -> Result<PadBuilder<Self>, glib::BoolError> {
skip_assert_initialized!(); skip_assert_initialized!();
let mut builder = Self::builder(target.direction()); let builder = Self::builder(target.direction());
builder.needs_specific_name = true;
builder.with_target(target) builder.with_target(target)
} }
@ -186,6 +202,9 @@ impl GhostPad {
/// If the `name_template` is a wildcard-name, then the `target` `name` is used, /// If the `name_template` is a wildcard-name, then the `target` `name` is used,
/// if it is compatible. Otherwise, a specific name must be provided using /// if it is compatible. Otherwise, a specific name must be provided using
/// [`PadBuilder::name`](crate::PadBuilder::name) or [`PadBuilder::maybe_name`](crate::PadBuilder::maybe_name). /// [`PadBuilder::name`](crate::PadBuilder::name) or [`PadBuilder::maybe_name`](crate::PadBuilder::maybe_name).
///
/// Use [`generated_name()`](crate::PadBuilder::generated_name`) to keep the `gst::Object`
/// automatically generated unique name.
#[doc(alias = "gst_ghost_pad_new_from_template")] #[doc(alias = "gst_ghost_pad_new_from_template")]
pub fn from_template_with_target<P: IsA<Pad> + IsA<crate::Object>>( pub fn from_template_with_target<P: IsA<Pad> + IsA<crate::Object>>(
templ: &crate::PadTemplate, templ: &crate::PadTemplate,
@ -206,6 +225,9 @@ impl GhostPad {
/// If the `name_template` is a wildcard-name, then the `target` `name` is used, /// If the `name_template` is a wildcard-name, then the `target` `name` is used,
/// if it is compatible. Otherwise, a specific name must be provided using /// if it is compatible. Otherwise, a specific name must be provided using
/// [`PadBuilder::name`](crate::PadBuilder::name) or [`PadBuilder::maybe_name`](crate::PadBuilder::maybe_name). /// [`PadBuilder::name`](crate::PadBuilder::name) or [`PadBuilder::maybe_name`](crate::PadBuilder::maybe_name).
///
/// Use [`generated_name()`](crate::PadBuilder::generated_name`) to keep the `gst::Object`
/// automatically generated unique name.
#[doc(alias = "gst_ghost_pad_new_from_template")] #[doc(alias = "gst_ghost_pad_new_from_template")]
pub fn builder_from_template_with_target<P: IsA<Pad> + IsA<crate::Object>>( pub fn builder_from_template_with_target<P: IsA<Pad> + IsA<crate::Object>>(
templ: &crate::PadTemplate, templ: &crate::PadTemplate,
@ -689,6 +711,9 @@ impl<T: IsA<GhostPad> + IsA<Pad>> PadBuilder<T> {
/// If the `name_template` is a wildcard-name, then the `target` `name` is used, /// If the `name_template` is a wildcard-name, then the `target` `name` is used,
/// if it is compatible. Otherwise, a specific name must be provided using /// if it is compatible. Otherwise, a specific name must be provided using
/// [`PadBuilder::name`](crate::PadBuilder::name) or [`PadBuilder::maybe_name`](crate::PadBuilder::maybe_name). /// [`PadBuilder::name`](crate::PadBuilder::name) or [`PadBuilder::maybe_name`](crate::PadBuilder::maybe_name).
///
/// Use [`generated_name()`](crate::PadBuilder::generated_name`) to keep the `gst::Object`
/// automatically generated unique name.
pub fn with_target<P: IsA<Pad> + IsA<crate::Object>>( pub fn with_target<P: IsA<Pad> + IsA<crate::Object>>(
mut self, mut self,
target: &P, target: &P,
@ -696,92 +721,8 @@ impl<T: IsA<GhostPad> + IsA<Pad>> PadBuilder<T> {
assert_eq!(self.pad.direction(), target.direction()); assert_eq!(self.pad.direction(), target.direction());
self.pad.set_target(Some(target))?; self.pad.set_target(Some(target))?;
self.name =
if self.needs_specific_name { crate::pad::PadBuilderName::CandidateForWildcardTemplate(target.name().to_string());
let mut can_assign_target_name = true;
if let Some(pad_template) = self.pad.pad_template() {
if pad_template.presence() == crate::PadPresence::Request {
// Check if the target name is compatible with the name template.
use crate::CAT_RUST;
let target_name = target.name();
let mut target_parts = target_name.split('_');
for template_part in pad_template.name_template().split('_') {
let Some(target_part) = target_parts.next() else {
crate::debug!(
CAT_RUST,
"Not using target Pad name '{target_name}': not enough parts compared to template '{}'",
pad_template.name_template(),
);
can_assign_target_name = false;
break;
};
if let Some(conv_spec_start) = template_part.find('%') {
if conv_spec_start > 0
&& !target_part.starts_with(&template_part[..conv_spec_start])
{
crate::debug!(
CAT_RUST,
"Not using target Pad name '{target_name}': mismatch template '{}' prefix",
pad_template.name_template(),
);
can_assign_target_name = false;
break;
}
let conv_spec_pos = conv_spec_start + 1;
match template_part.get(conv_spec_pos..=conv_spec_pos) {
Some("s") => {
// *There can be only one* %s
break;
}
Some("u") => {
if target_part
.get(conv_spec_start..)
.map_or(true, |s| s.parse::<u32>().is_err())
{
crate::debug!(
CAT_RUST,
"Not using target Pad name '{target_name}': can't parse '%u' from '{target_part}' (template '{}')",
pad_template.name_template(),
);
can_assign_target_name = false;
break;
}
}
Some("d") => {
if target_part
.get(conv_spec_start..)
.map_or(true, |s| s.parse::<i32>().is_err())
{
crate::debug!(
CAT_RUST,
"Not using target Pad name '{target_name}': can't parse '%i' from '{target_part}' (template '{}')",
pad_template.name_template(),
);
can_assign_target_name = false;
break;
}
}
other => unreachable!("Unexpected conversion specifier {other:?}"),
}
} else if target_part != template_part {
can_assign_target_name = false;
break;
}
}
}
}
if can_assign_target_name {
self.pad.set_property("name", target.name());
self.needs_specific_name = false;
}
}
Ok(self) Ok(self)
} }
@ -911,6 +852,13 @@ mod tests {
.name("ghost_test") .name("ghost_test")
.build(); .build();
assert_eq!(ghost_pad.name(), "ghost_test"); assert_eq!(ghost_pad.name(), "ghost_test");
let target = crate::Pad::from_template(&templ);
let ghost_pad = GhostPad::builder_with_target(&target)
.unwrap()
.generated_name()
.build();
assert!(ghost_pad.name().starts_with("ghostpad"));
} }
#[test] #[test]
@ -952,6 +900,13 @@ mod tests {
.build(); .build();
assert_eq!(ghost_pad.name(), "my-sink"); assert_eq!(ghost_pad.name(), "my-sink");
let target = crate::Pad::from_template(&sink_templ);
let ghost_pad = GhostPad::builder_from_template_with_target(&ghost_templ, &target)
.unwrap()
.generated_name()
.build();
assert!(ghost_pad.name().starts_with("ghostpad"));
// # Request template %u // # Request template %u
let wildcard_u_templ = crate::PadTemplate::new( let wildcard_u_templ = crate::PadTemplate::new(
"sink_%u", "sink_%u",
@ -983,6 +938,13 @@ mod tests {
.build(); .build();
assert_eq!(ghost_pad.name(), "sink_0"); assert_eq!(ghost_pad.name(), "sink_0");
let target = crate::Pad::from_template(&sink_0_templ);
let ghost_pad = GhostPad::builder_from_template_with_target(&wildcard_u_templ, &target)
.unwrap()
.generated_name()
.build();
assert!(ghost_pad.name().starts_with("ghostpad"));
// # Request template %d_%u // # Request template %d_%u
let wildcard_u_templ = crate::PadTemplate::new( let wildcard_u_templ = crate::PadTemplate::new(
"sink_%d_%u", "sink_%d_%u",

View file

@ -353,7 +353,7 @@ pub mod prelude {
task_pool::{TaskHandle, TaskPoolExtManual}, task_pool::{TaskHandle, TaskPoolExtManual},
typefind::TypeFindImpl, typefind::TypeFindImpl,
utils::Displayable, utils::Displayable,
value::GstValueExt, value::{GstValueExt, MulDivExtFraction},
}; };
} }

View file

@ -1428,7 +1428,7 @@ impl Pad {
// rustdoc-stripper-ignore-next // rustdoc-stripper-ignore-next
/// Creates a new [`Pad`] with the specified [`PadDirection`](crate::PadDirection). /// Creates a new [`Pad`] with the specified [`PadDirection`](crate::PadDirection).
/// ///
/// An automatically generated name will be assigned. /// The [`Pad`] will be assigned the usual `gst::Object` generated unique name.
/// ///
/// Use [`Pad::builder()`] to get a [`PadBuilder`] and define options. /// Use [`Pad::builder()`] to get a [`PadBuilder`] and define options.
#[doc(alias = "gst_pad_new")] #[doc(alias = "gst_pad_new")]
@ -1439,8 +1439,6 @@ impl Pad {
// rustdoc-stripper-ignore-next // rustdoc-stripper-ignore-next
/// Creates a [`PadBuilder`] with the specified [`PadDirection`](crate::PadDirection). /// Creates a [`PadBuilder`] with the specified [`PadDirection`](crate::PadDirection).
///
/// An automatically generated name will be assigned.
#[doc(alias = "gst_pad_new")] #[doc(alias = "gst_pad_new")]
pub fn builder(direction: crate::PadDirection) -> PadBuilder<Self> { pub fn builder(direction: crate::PadDirection) -> PadBuilder<Self> {
skip_assert_initialized!(); skip_assert_initialized!();
@ -1454,11 +1452,14 @@ impl Pad {
/// i.e. if it's not a wildcard-name containing `%u`, `%s` or `%d`, /// i.e. if it's not a wildcard-name containing `%u`, `%s` or `%d`,
/// the `Pad` will automatically be named after the `name_template`. /// the `Pad` will automatically be named after the `name_template`.
/// ///
/// Use [`Pad::builder_from_static_template()`] to get a [`PadBuilder`] and define options.
///
/// Use [`generated_name()`](crate::PadBuilder::generated_name`) to keep the `gst::Object`
/// automatically generated unique name.
///
/// # Panics /// # Panics
/// ///
/// Panics if the `name_template` is a wildcard-name. /// Panics if the `name_template` is a wildcard-name.
///
/// Use [`Pad::builder_from_static_template()`] to get a [`PadBuilder`] and define options.
#[doc(alias = "gst_pad_new_from_static_template")] #[doc(alias = "gst_pad_new_from_static_template")]
pub fn from_static_template(templ: &StaticPadTemplate) -> Self { pub fn from_static_template(templ: &StaticPadTemplate) -> Self {
skip_assert_initialized!(); skip_assert_initialized!();
@ -1472,7 +1473,8 @@ impl Pad {
/// i.e. if it's not a wildcard-name containing `%u`, `%s` or `%d`, /// i.e. if it's not a wildcard-name containing `%u`, `%s` or `%d`,
/// the `Pad` will automatically be named after the `name_template`. /// the `Pad` will automatically be named after the `name_template`.
/// ///
/// Use [`PadBuilder::name`] or [`PadBuilder::maybe_name`] to specify a different name. /// Use [`generated_name()`](crate::PadBuilder::generated_name`) to keep the `gst::Object`
/// automatically generated unique name.
#[doc(alias = "gst_pad_new_from_static_template")] #[doc(alias = "gst_pad_new_from_static_template")]
pub fn builder_from_static_template(templ: &StaticPadTemplate) -> PadBuilder<Self> { pub fn builder_from_static_template(templ: &StaticPadTemplate) -> PadBuilder<Self> {
skip_assert_initialized!(); skip_assert_initialized!();
@ -1486,11 +1488,11 @@ impl Pad {
/// i.e. if it's not a wildcard-name containing `%u`, `%s` or `%d`, /// i.e. if it's not a wildcard-name containing `%u`, `%s` or `%d`,
/// the `Pad` will automatically be named after the `name_template`. /// the `Pad` will automatically be named after the `name_template`.
/// ///
/// Use [`Pad::builder_from_template()`] to get a [`PadBuilder`] and define options.
///
/// # Panics /// # Panics
/// ///
/// Panics if the `name_template` is a wildcard-name. /// Panics if the `name_template` is a wildcard-name.
///
/// Use [`Pad::builder_from_template()`] to get a [`PadBuilder`] and define options.
#[doc(alias = "gst_pad_new_from_template")] #[doc(alias = "gst_pad_new_from_template")]
pub fn from_template(templ: &crate::PadTemplate) -> Self { pub fn from_template(templ: &crate::PadTemplate) -> Self {
skip_assert_initialized!(); skip_assert_initialized!();
@ -1504,7 +1506,8 @@ impl Pad {
/// i.e. if it's not a wildcard-name containing `%u`, `%s` or `%d`, /// i.e. if it's not a wildcard-name containing `%u`, `%s` or `%d`,
/// the `Pad` will automatically be named after the `name_template`. /// the `Pad` will automatically be named after the `name_template`.
/// ///
/// Use [`PadBuilder::name`] or [`PadBuilder::maybe_name`] to specify a different name. /// Use [`generated_name()`](crate::PadBuilder::generated_name`) to keep the `gst::Object`
/// automatically generated unique name.
#[doc(alias = "gst_pad_new_from_template")] #[doc(alias = "gst_pad_new_from_template")]
pub fn builder_from_template(templ: &crate::PadTemplate) -> PadBuilder<Self> { pub fn builder_from_template(templ: &crate::PadTemplate) -> PadBuilder<Self> {
skip_assert_initialized!(); skip_assert_initialized!();
@ -1558,18 +1561,22 @@ impl Pad {
} }
} }
pub(crate) enum PadBuilderName {
Undefined,
KeepGenerated,
UserDefined(String),
CandidateForWildcardTemplate(String),
}
#[must_use = "The builder must be built to be used"] #[must_use = "The builder must be built to be used"]
pub struct PadBuilder<T> { pub struct PadBuilder<T> {
pub(crate) pad: T, pub(crate) pad: T,
pub(crate) needs_specific_name: bool, pub(crate) name: PadBuilderName,
} }
impl<T: IsA<Pad> + IsA<glib::Object> + glib::object::IsClass> PadBuilder<T> { impl<T: IsA<Pad> + IsA<glib::Object> + glib::object::IsClass> PadBuilder<T> {
// rustdoc-stripper-ignore-next // rustdoc-stripper-ignore-next
/// Creates a `PadBuilder` with the specified [`PadDirection`](crate::PadDirection). /// Creates a `PadBuilder` with the specified [`PadDirection`](crate::PadDirection).
///
/// An automatically generated name will be assigned. Use [`PadBuilder::name`] or
/// [`PadBuilder::maybe_name`] to define a specific name.
pub fn new(direction: crate::PadDirection) -> Self { pub fn new(direction: crate::PadDirection) -> Self {
assert_initialized_main_thread!(); assert_initialized_main_thread!();
@ -1588,7 +1595,7 @@ impl<T: IsA<Pad> + IsA<glib::Object> + glib::object::IsClass> PadBuilder<T> {
PadBuilder { PadBuilder {
pad, pad,
needs_specific_name: false, name: PadBuilderName::Undefined,
} }
} }
@ -1599,7 +1606,8 @@ impl<T: IsA<Pad> + IsA<glib::Object> + glib::object::IsClass> PadBuilder<T> {
/// i.e. if it's not a wildcard-name containing `%u`, `%s` or `%d`, /// i.e. if it's not a wildcard-name containing `%u`, `%s` or `%d`,
/// the `Pad` will automatically be named after the `name_template`. /// the `Pad` will automatically be named after the `name_template`.
/// ///
/// Use [`PadBuilder::name`] or [`PadBuilder::maybe_name`] to specify a different name. /// Use [`generated_name()`](crate::PadBuilder::generated_name`) to keep the `gst::Object`
/// automatically generated unique name.
pub fn from_static_template(templ: &StaticPadTemplate) -> Self { pub fn from_static_template(templ: &StaticPadTemplate) -> Self {
skip_assert_initialized!(); skip_assert_initialized!();
@ -1614,7 +1622,8 @@ impl<T: IsA<Pad> + IsA<glib::Object> + glib::object::IsClass> PadBuilder<T> {
/// i.e. if it's not a wildcard-name containing `%u`, `%s` or `%d`, /// i.e. if it's not a wildcard-name containing `%u`, `%s` or `%d`,
/// the `Pad` will automatically be named after the `name_template`. /// the `Pad` will automatically be named after the `name_template`.
/// ///
/// Use [`PadBuilder::name`] or [`PadBuilder::maybe_name`] to specify a different name. /// Use [`generated_name()`](crate::PadBuilder::generated_name`) to keep the `gst::Object`
/// automatically generated unique name.
pub fn from_template(templ: &crate::PadTemplate) -> Self { pub fn from_template(templ: &crate::PadTemplate) -> Self {
assert_initialized_main_thread!(); assert_initialized_main_thread!();
@ -1654,25 +1663,23 @@ impl<T: IsA<Pad> + IsA<glib::Object> + glib::object::IsClass> PadBuilder<T> {
} }
} }
let needs_specific_name = if templ.name().find('%').is_some() {
// Pad needs a specific name
true
} else {
pad.set_property("name", templ.name());
false
};
PadBuilder { PadBuilder {
pad, pad,
needs_specific_name, name: PadBuilderName::Undefined,
} }
} }
// rustdoc-stripper-ignore-next
/// Uses the `gst::Object` generated unique name.
pub fn generated_name(mut self) -> Self {
self.name = PadBuilderName::KeepGenerated;
self
}
// rustdoc-stripper-ignore-next // rustdoc-stripper-ignore-next
/// Sets the name of the Pad. /// Sets the name of the Pad.
pub fn name(mut self, name: impl glib::IntoGStr) -> Self { pub fn name(mut self, name: impl Into<String>) -> Self {
name.run_with_gstr(|name| self.pad.set_property("name", name)); self.name = PadBuilderName::UserDefined(name.into());
self.needs_specific_name = false;
self self
} }
@ -1682,7 +1689,7 @@ impl<T: IsA<Pad> + IsA<glib::Object> + glib::object::IsClass> PadBuilder<T> {
/// ///
/// This method is convenient when the `name` is provided as an `Option`. /// This method is convenient when the `name` is provided as an `Option`.
/// If the `name` is `None`, this has no effect. /// If the `name` is `None`, this has no effect.
pub fn maybe_name<N: glib::IntoGStr>(self, name: Option<N>) -> Self { pub fn maybe_name<N: Into<String>>(self, name: Option<N>) -> Self {
if let Some(name) = name { if let Some(name) = name {
self.name(name) self.name(name)
} else { } else {
@ -1695,7 +1702,7 @@ impl<T: IsA<Pad> + IsA<glib::Object> + glib::object::IsClass> PadBuilder<T> {
/// ///
/// This method is convenient when the `name` is provided as an `Option`. /// This method is convenient when the `name` is provided as an `Option`.
/// If the `name` is `None`, this has no effect. /// If the `name` is `None`, this has no effect.
pub fn name_if_some<N: glib::IntoGStr>(self, name: Option<N>) -> Self { pub fn name_if_some<N: Into<String>>(self, name: Option<N>) -> Self {
if let Some(name) = name { if let Some(name) = name {
self.name(name) self.name(name)
} else { } else {
@ -2047,18 +2054,132 @@ impl<T: IsA<Pad> + IsA<glib::Object> + glib::object::IsClass> PadBuilder<T> {
/// and no specific `name` was provided using [`PadBuilder::name`] /// and no specific `name` was provided using [`PadBuilder::name`]
/// or [`PadBuilder::maybe_name`], or for [`GhostPad`s](crate::GhostPad), /// or [`PadBuilder::maybe_name`], or for [`GhostPad`s](crate::GhostPad),
/// by defining a `target`. /// by defining a `target`.
///
/// Use [`generated_name()`](crate::PadBuilder::generated_name`) to keep the `gst::Object`
/// automatically generated unique name.
#[must_use = "Building the pad without using it has no effect"] #[must_use = "Building the pad without using it has no effect"]
#[track_caller] #[track_caller]
pub fn build(self) -> T { pub fn build(self) -> T {
if self.needs_specific_name { let Self { pad, name } = self;
panic!(concat!(
"Attempt to build a Pad from a wildcard-name template", let templ = pad.pad_template();
" or with a target Pad with an incompatible name.",
" Make sure to define a specific name using PadBuilder.", use PadBuilderName::*;
)); match (name, templ) {
(KeepGenerated, _) => (),
(Undefined, None) => (),
(Undefined, Some(templ)) => {
if templ.name().find('%').is_some() {
panic!(concat!(
"Attempt to build a Pad from a wildcard-name template",
" or with a target Pad with an incompatible name.",
" Make sure to define a specific name using PadBuilder",
" or opt-in to keep the automatically generated name.",
));
} else {
pad.set_property("name", templ.name());
}
}
(UserDefined(name), _) | (CandidateForWildcardTemplate(name), None) => {
pad.set_property("name", name);
}
(CandidateForWildcardTemplate(name), Some(templ)) => {
if templ.name().find('%').is_none() {
// Not a widlcard template
pad.set_property("name", templ.name());
} else {
let mut can_assign_name = true;
if templ.presence() == crate::PadPresence::Request {
// Check if the name is compatible with the name template.
use crate::CAT_RUST;
let mut name_parts = name.split('_');
for templ_part in templ.name_template().split('_') {
let Some(name_part) = name_parts.next() else {
crate::debug!(
CAT_RUST,
"Not using Pad name '{name}': not enough parts compared to template '{}'",
templ.name_template(),
);
can_assign_name = false;
break;
};
if let Some(conv_spec_start) = templ_part.find('%') {
if conv_spec_start > 0
&& !name_part.starts_with(&templ_part[..conv_spec_start])
{
crate::debug!(
CAT_RUST,
"Not using Pad name '{name}': mismatch template '{}' prefix",
templ.name_template(),
);
can_assign_name = false;
break;
}
let conv_spec_pos = conv_spec_start + 1;
match templ_part.get(conv_spec_pos..=conv_spec_pos) {
Some("s") => {
// *There can be only one* %s
break;
}
Some("u") => {
if name_part
.get(conv_spec_start..)
.map_or(true, |s| s.parse::<u32>().is_err())
{
crate::debug!(
CAT_RUST,
"Not using Pad name '{name}': can't parse '%u' from '{name_part}' (template '{}')",
templ.name_template(),
);
can_assign_name = false;
break;
}
}
Some("d") => {
if name_part
.get(conv_spec_start..)
.map_or(true, |s| s.parse::<i32>().is_err())
{
crate::debug!(
CAT_RUST,
"Not using target Pad name '{name}': can't parse '%i' from '{name_part}' (template '{}')",
templ.name_template(),
);
can_assign_name = false;
break;
}
}
other => {
unreachable!("Unexpected conversion specifier {other:?}")
}
}
} else if name_part != templ_part {
can_assign_name = false;
}
}
}
if can_assign_name {
pad.set_property("name", name);
} else {
panic!(concat!(
"Attempt to build a Pad from a wildcard-name template",
" with a target Pad with an incompatible name.",
" Make sure to define a specific name using PadBuilder",
" or opt-in to keep the automatically generated name.",
));
}
}
}
} }
self.pad pad
} }
} }
@ -2484,11 +2605,21 @@ mod tests {
let pad = crate::Pad::builder(crate::PadDirection::Unknown).build(); let pad = crate::Pad::builder(crate::PadDirection::Unknown).build();
assert!(pad.name().starts_with("pad")); assert!(pad.name().starts_with("pad"));
let pad = crate::Pad::builder(crate::PadDirection::Unknown)
.generated_name()
.build();
assert!(pad.name().starts_with("pad"));
let pad = crate::Pad::builder(crate::PadDirection::Unknown) let pad = crate::Pad::builder(crate::PadDirection::Unknown)
.maybe_name(None::<&str>) .maybe_name(None::<&str>)
.build(); .build();
assert!(pad.name().starts_with("pad")); assert!(pad.name().starts_with("pad"));
let pad = crate::Pad::builder(crate::PadDirection::Unknown)
.name_if_some(None::<&str>)
.build();
assert!(pad.name().starts_with("pad"));
let pad = crate::Pad::builder(crate::PadDirection::Sink) let pad = crate::Pad::builder(crate::PadDirection::Sink)
.name("sink_0") .name("sink_0")
.build(); .build();
@ -2509,6 +2640,11 @@ mod tests {
.build(); .build();
assert_eq!(pad.name(), "test"); assert_eq!(pad.name(), "test");
let pad = crate::Pad::builder(crate::PadDirection::Unknown)
.name_if_some(Some("test"))
.build();
assert_eq!(pad.name(), "test");
let caps = crate::Caps::new_any(); let caps = crate::Caps::new_any();
let templ = crate::PadTemplate::new( let templ = crate::PadTemplate::new(
"sink", "sink",
@ -2526,6 +2662,9 @@ mod tests {
.build(); .build();
assert!(pad.name().starts_with("audio_sink")); assert!(pad.name().starts_with("audio_sink"));
let pad = Pad::builder_from_template(&templ).generated_name().build();
assert!(pad.name().starts_with("pad"));
let templ = crate::PadTemplate::new( let templ = crate::PadTemplate::new(
"audio_%u", "audio_%u",
crate::PadDirection::Sink, crate::PadDirection::Sink,
@ -2536,6 +2675,9 @@ mod tests {
let pad = Pad::builder_from_template(&templ).name("audio_0").build(); let pad = Pad::builder_from_template(&templ).name("audio_0").build();
assert!(pad.name().starts_with("audio_0")); assert!(pad.name().starts_with("audio_0"));
let pad = Pad::builder_from_template(&templ).generated_name().build();
assert!(pad.name().starts_with("pad"));
} }
#[test] #[test]

View file

@ -3,6 +3,7 @@
use std::{cmp, fmt, ops, slice}; use std::{cmp, fmt, ops, slice};
use glib::{prelude::*, translate::*}; use glib::{prelude::*, translate::*};
use muldiv::MulDiv;
use num_rational::Rational32; use num_rational::Rational32;
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] #[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
@ -307,6 +308,33 @@ impl From<Fraction> for Rational32 {
} }
} }
pub trait MulDivExtFraction {
type Output;
fn mul_frac_floor(self, frac: Fraction) -> Option<Self::Output>;
fn mul_frac_round(self, frac: Fraction) -> Option<Self::Output>;
fn mul_frac_ceil(self, frac: Fraction) -> Option<Self::Output>;
}
impl<T> MulDivExtFraction for T
where
T: MulDiv<i32>,
{
type Output = <Self as MulDiv<i32>>::Output;
fn mul_frac_floor(self, frac: Fraction) -> Option<Self::Output> {
self.mul_div_floor(frac.numer(), frac.denom())
}
fn mul_frac_round(self, frac: Fraction) -> Option<Self::Output> {
self.mul_div_round(frac.numer(), frac.denom())
}
fn mul_frac_ceil(self, frac: Fraction) -> Option<Self::Output> {
self.mul_div_ceil(frac.numer(), frac.denom())
}
}
impl glib::types::StaticType for Fraction { impl glib::types::StaticType for Fraction {
#[inline] #[inline]
fn static_type() -> glib::types::Type { fn static_type() -> glib::types::Type {