diff options
author | Sebastian Dröge <sebastian@centricular.com> | 2021-01-21 21:21:29 +0300 |
---|---|---|
committer | Sebastian Dröge <sebastian@centricular.com> | 2021-01-25 15:43:05 +0300 |
commit | d4ce1a33f2257ea19f873711c78b7b57d5cb7e5f (patch) | |
tree | 7a4ff75e550640afd69786a2d8a41f07b6b6c593 | |
parent | 875c3efb916f2400df355f8517f1daeff6f7ae85 (diff) |
Update for glib/gstreamer bindings API changes
54 files changed, 5185 insertions, 4699 deletions
diff --git a/audio/audiofx/src/audioecho/imp.rs b/audio/audiofx/src/audioecho/imp.rs index 88204141e..19fa2deca 100644 --- a/audio/audiofx/src/audioecho/imp.rs +++ b/audio/audiofx/src/audioecho/imp.rs @@ -65,51 +65,6 @@ pub struct AudioEcho { state: Mutex<Option<State>>, } -static PROPERTIES: [subclass::Property; 4] = [ - subclass::Property("max-delay", |name| { - glib::ParamSpec::uint64(name, - "Maximum Delay", - "Maximum delay of the echo in nanoseconds (can't be changed in PLAYING or PAUSED state)", - 0, u64::MAX, - DEFAULT_MAX_DELAY, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("delay", |name| { - glib::ParamSpec::uint64( - name, - "Delay", - "Delay of the echo in nanoseconds", - 0, - u64::MAX, - DEFAULT_DELAY, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("intensity", |name| { - glib::ParamSpec::double( - name, - "Intensity", - "Intensity of the echo", - 0.0, - 1.0, - DEFAULT_INTENSITY, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("feedback", |name| { - glib::ParamSpec::double( - name, - "Feedback", - "Amount of feedback", - 0.0, - 1.0, - DEFAULT_FEEDBACK, - glib::ParamFlags::READWRITE, - ) - }), -]; - impl AudioEcho { fn process<F: Float + ToPrimitive + FromPrimitive>( data: &mut [F], @@ -134,6 +89,7 @@ impl ObjectSubclass for AudioEcho { const NAME: &'static str = "RsAudioEcho"; type Type = super::AudioEcho; type ParentType = gst_base::BaseTransform; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -145,78 +101,75 @@ impl ObjectSubclass for AudioEcho { state: Mutex::new(None), } } - - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Audio echo", - "Filter/Effect/Audio", - "Adds an echo or reverb effect to an audio stream", - "Sebastian Dröge <sebastian@centricular.com>", - ); - - let caps = gst::Caps::new_simple( - "audio/x-raw", - &[ - ( - "format", - &gst::List::new(&[ - &gst_audio::AUDIO_FORMAT_F32.to_str(), - &gst_audio::AUDIO_FORMAT_F64.to_str(), - ]), - ), - ("rate", &gst::IntRange::<i32>::new(0, i32::MAX)), - ("channels", &gst::IntRange::<i32>::new(0, i32::MAX)), - ("layout", &"interleaved"), - ], - ); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - klass.install_properties(&PROPERTIES); - - klass.configure( - gst_base::subclass::BaseTransformMode::AlwaysInPlace, - false, - false, - ); - } } impl ObjectImpl for AudioEcho { - fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::uint64("max-delay", + "Maximum Delay", + "Maximum delay of the echo in nanoseconds (can't be changed in PLAYING or PAUSED state)", + 0, u64::MAX, + DEFAULT_MAX_DELAY, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint64( + "delay", + "Delay", + "Delay of the echo in nanoseconds", + 0, + u64::MAX, + DEFAULT_DELAY, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::double( + "intensity", + "Intensity", + "Intensity of the echo", + 0.0, + 1.0, + DEFAULT_INTENSITY, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::double( + "feedback", + "Feedback", + "Amount of feedback", + 0.0, + 1.0, + DEFAULT_FEEDBACK, + glib::ParamFlags::READWRITE, + ), + ] + }); - match *prop { - subclass::Property("max-delay", ..) => { + PROPERTIES.as_ref() + } + + fn set_property( + &self, + _obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "max-delay" => { let mut settings = self.settings.lock().unwrap(); if self.state.lock().unwrap().is_none() { settings.max_delay = value.get_some().expect("type checked upstream"); } } - subclass::Property("delay", ..) => { + "delay" => { let mut settings = self.settings.lock().unwrap(); settings.delay = value.get_some().expect("type checked upstream"); } - subclass::Property("intensity", ..) => { + "intensity" => { let mut settings = self.settings.lock().unwrap(); settings.intensity = value.get_some().expect("type checked upstream"); } - subclass::Property("feedback", ..) => { + "feedback" => { let mut settings = self.settings.lock().unwrap(); settings.feedback = value.get_some().expect("type checked upstream"); } @@ -224,23 +177,21 @@ impl ObjectImpl for AudioEcho { } } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("max-delay", ..) => { + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "max-delay" => { let settings = self.settings.lock().unwrap(); settings.max_delay.to_value() } - subclass::Property("delay", ..) => { + "delay" => { let settings = self.settings.lock().unwrap(); settings.delay.to_value() } - subclass::Property("intensity", ..) => { + "intensity" => { let settings = self.settings.lock().unwrap(); settings.intensity.to_value() } - subclass::Property("feedback", ..) => { + "feedback" => { let settings = self.settings.lock().unwrap(); settings.feedback.to_value() } @@ -249,9 +200,65 @@ impl ObjectImpl for AudioEcho { } } -impl ElementImpl for AudioEcho {} +impl ElementImpl for AudioEcho { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Audio echo", + "Filter/Effect/Audio", + "Adds an echo or reverb effect to an audio stream", + "Sebastian Dröge <sebastian@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::new_simple( + "audio/x-raw", + &[ + ( + "format", + &gst::List::new(&[ + &gst_audio::AUDIO_FORMAT_F32.to_str(), + &gst_audio::AUDIO_FORMAT_F64.to_str(), + ]), + ), + ("rate", &gst::IntRange::<i32>::new(0, i32::MAX)), + ("channels", &gst::IntRange::<i32>::new(0, i32::MAX)), + ("layout", &"interleaved"), + ], + ); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } +} impl BaseTransformImpl for AudioEcho { + const MODE: gst_base::subclass::BaseTransformMode = + gst_base::subclass::BaseTransformMode::AlwaysInPlace; + const PASSTHROUGH_ON_SAME_CAPS: bool = false; + const TRANSFORM_IP_ON_PASSTHROUGH: bool = false; + fn transform_ip( &self, _element: &Self::Type, diff --git a/audio/audiofx/src/audioloudnorm/imp.rs b/audio/audiofx/src/audioloudnorm/imp.rs index 76e227e44..102894e55 100644 --- a/audio/audiofx/src/audioloudnorm/imp.rs +++ b/audio/audiofx/src/audioloudnorm/imp.rs @@ -210,53 +210,6 @@ pub struct AudioLoudNorm { state: Mutex<Option<State>>, } -static PROPERTIES: [subclass::Property; 4] = [ - subclass::Property("loudness-target", |name| { - glib::ParamSpec::double( - name, - "Loudness Target", - "Loudness target in LUFS", - -70.0, - -5.0, - DEFAULT_LOUDNESS_TARGET, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("loudness-range-target", |name| { - glib::ParamSpec::double( - name, - "Loudness Range Target", - "Loudness range target in LU", - 1.0, - 20.0, - DEFAULT_LOUDNESS_RANGE_TARGET, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("max-true-peak", |name| { - glib::ParamSpec::double( - name, - "Maximum True Peak", - "Maximum True Peak in dbTP", - -9.0, - 0.0, - DEFAULT_MAX_TRUE_PEAK, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("offset", |name| { - glib::ParamSpec::double( - name, - "Offset Gain", - "Offset Gain in LU", - -99.0, - 99.0, - DEFAULT_OFFSET, - glib::ParamFlags::READWRITE, - ) - }), -]; - // Gain analysis parameters const GAIN_LOOKAHEAD: usize = 3 * 192_000; // 3s const FRAME_SIZE: usize = 19_200; // 100ms @@ -1752,6 +1705,7 @@ impl ObjectSubclass for AudioLoudNorm { const NAME: &'static str = "RsAudioLoudNorm"; type Type = super::AudioLoudNorm; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -1796,47 +1750,54 @@ impl ObjectSubclass for AudioLoudNorm { state: Mutex::new(None), } } +} - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Audio loudness normalizer", - "Filter/Effect/Audio", - "Normalizes perceived loudness of an audio stream", - "Sebastian Dröge <sebastian@centricular.com>", - ); - - let caps = gst::Caps::new_simple( - "audio/x-raw", - &[ - ("format", &gst_audio::AUDIO_FORMAT_F64.to_str()), - ("rate", &192_000i32), - ("channels", &gst::IntRange::<i32>::new(1, std::i32::MAX)), - ("layout", &"interleaved"), - ], - ); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); +impl ObjectImpl for AudioLoudNorm { + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::double( + "loudness-target", + "Loudness Target", + "Loudness target in LUFS", + -70.0, + -5.0, + DEFAULT_LOUDNESS_TARGET, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::double( + "loudness-range-target", + "Loudness Range Target", + "Loudness range target in LU", + 1.0, + 20.0, + DEFAULT_LOUDNESS_RANGE_TARGET, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::double( + "max-true-peak", + "Maximum True Peak", + "Maximum True Peak in dbTP", + -9.0, + 0.0, + DEFAULT_MAX_TRUE_PEAK, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::double( + "offset", + "Offset Gain", + "Offset Gain in LU", + -99.0, + 99.0, + DEFAULT_OFFSET, + glib::ParamFlags::READWRITE, + ), + ] + }); - klass.install_properties(&PROPERTIES); + PROPERTIES.as_ref() } -} -impl ObjectImpl for AudioLoudNorm { fn constructed(&self, obj: &Self::Type) { self.parent_constructed(obj); @@ -1844,23 +1805,27 @@ impl ObjectImpl for AudioLoudNorm { obj.add_pad(&self.srcpad).unwrap(); } - fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("loudness-target", ..) => { + fn set_property( + &self, + _obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "loudness-target" => { let mut settings = self.settings.lock().unwrap(); settings.loudness_target = value.get_some().expect("type checked upstream"); } - subclass::Property("loudness-range-target", ..) => { + "loudness-range-target" => { let mut settings = self.settings.lock().unwrap(); settings.loudness_range_target = value.get_some().expect("type checked upstream"); } - subclass::Property("max-true-peak", ..) => { + "max-true-peak" => { let mut settings = self.settings.lock().unwrap(); settings.max_true_peak = value.get_some().expect("type checked upstream"); } - subclass::Property("offset", ..) => { + "offset" => { let mut settings = self.settings.lock().unwrap(); settings.offset = value.get_some().expect("type checked upstream"); } @@ -1868,23 +1833,21 @@ impl ObjectImpl for AudioLoudNorm { } } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("loudness-target", ..) => { + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "loudness-target" => { let settings = self.settings.lock().unwrap(); settings.loudness_target.to_value() } - subclass::Property("loudness-range-target", ..) => { + "loudness-range-target" => { let settings = self.settings.lock().unwrap(); settings.loudness_range_target.to_value() } - subclass::Property("max-true-peak", ..) => { + "max-true-peak" => { let settings = self.settings.lock().unwrap(); settings.max_true_peak.to_value() } - subclass::Property("offset", ..) => { + "offset" => { let settings = self.settings.lock().unwrap(); settings.offset.to_value() } @@ -1894,6 +1857,52 @@ impl ObjectImpl for AudioLoudNorm { } impl ElementImpl for AudioLoudNorm { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Audio loudness normalizer", + "Filter/Effect/Audio", + "Normalizes perceived loudness of an audio stream", + "Sebastian Dröge <sebastian@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::new_simple( + "audio/x-raw", + &[ + ("format", &gst_audio::AUDIO_FORMAT_F64.to_str()), + ("rate", &192_000i32), + ("channels", &gst::IntRange::<i32>::new(1, std::i32::MAX)), + ("layout", &"interleaved"), + ], + ); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + fn change_state( &self, element: &Self::Type, diff --git a/audio/audiofx/src/audiornnoise/imp.rs b/audio/audiofx/src/audiornnoise/imp.rs index 71a75f695..02a23a755 100644 --- a/audio/audiofx/src/audiornnoise/imp.rs +++ b/audio/audiofx/src/audiornnoise/imp.rs @@ -193,6 +193,7 @@ impl ObjectSubclass for AudioRNNoise { const NAME: &'static str = "AudioRNNoise"; type Type = super::AudioRNNoise; type ParentType = gst_base::BaseTransform; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -203,54 +204,64 @@ impl ObjectSubclass for AudioRNNoise { state: Mutex::new(None), } } - - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Audio denoise", - "Filter/Effect/Audio", - "Removes noise from an audio stream", - "Philippe Normand <philn@igalia.com>", - ); - - let caps = gst::Caps::new_simple( - "audio/x-raw", - &[ - ("format", &gst_audio::AUDIO_FORMAT_F32.to_str()), - ("rate", &48000), - ("channels", &gst::IntRange::<i32>::new(1, std::i32::MAX)), - ("layout", &"interleaved"), - ], - ); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - klass.configure( - gst_base::subclass::BaseTransformMode::NeverInPlace, - false, - false, - ); - } } impl ObjectImpl for AudioRNNoise {} -impl ElementImpl for AudioRNNoise {} + +impl ElementImpl for AudioRNNoise { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Audio denoise", + "Filter/Effect/Audio", + "Removes noise from an audio stream", + "Philippe Normand <philn@igalia.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::new_simple( + "audio/x-raw", + &[ + ("format", &gst_audio::AUDIO_FORMAT_F32.to_str()), + ("rate", &48000), + ("channels", &gst::IntRange::<i32>::new(1, std::i32::MAX)), + ("layout", &"interleaved"), + ], + ); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } +} impl BaseTransformImpl for AudioRNNoise { + const MODE: gst_base::subclass::BaseTransformMode = + gst_base::subclass::BaseTransformMode::NeverInPlace; + const PASSTHROUGH_ON_SAME_CAPS: bool = false; + const TRANSFORM_IP_ON_PASSTHROUGH: bool = false; + fn set_caps( &self, element: &Self::Type, diff --git a/audio/claxon/Cargo.toml b/audio/claxon/Cargo.toml index 531f49623..322471fa5 100644 --- a/audio/claxon/Cargo.toml +++ b/audio/claxon/Cargo.toml @@ -14,6 +14,7 @@ gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org claxon = { version = "0.4" } byte-slice-cast = "1.0" atomic_refcell = "0.1" +once_cell = "1" [dev-dependencies] gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" } diff --git a/audio/claxon/src/claxondec/imp.rs b/audio/claxon/src/claxondec/imp.rs index ce8d2761b..bbec688d9 100644 --- a/audio/claxon/src/claxondec/imp.rs +++ b/audio/claxon/src/claxondec/imp.rs @@ -19,13 +19,22 @@ use atomic_refcell::AtomicRefCell; use byte_slice_cast::*; +use once_cell::sync::Lazy; + +static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| { + gst::DebugCategory::new( + "claxondec", + gst::DebugColorFlags::empty(), + Some("Claxon FLAC decoder"), + ) +}); + struct State { streaminfo: Option<claxon::metadata::StreamInfo>, audio_info: Option<gst_audio::AudioInfo>, } pub struct ClaxonDec { - cat: gst::DebugCategory, state: AtomicRefCell<Option<State>>, } @@ -33,6 +42,7 @@ impl ObjectSubclass for ClaxonDec { const NAME: &'static str = "ClaxonDec"; type Type = super::ClaxonDec; type ParentType = gst_audio::AudioDecoder; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -40,64 +50,69 @@ impl ObjectSubclass for ClaxonDec { fn new() -> Self { Self { - cat: gst::DebugCategory::new( - "claxondec", - gst::DebugColorFlags::empty(), - Some("Claxon FLAC decoder"), - ), state: AtomicRefCell::new(None), } } +} - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Claxon FLAC decoder", - "Decoder/Audio", - "Claxon FLAC decoder", - "Ruben Gonzalez <rgonzalez@fluendo.com>", - ); +impl ObjectImpl for ClaxonDec {} - let sink_caps = gst::Caps::new_simple("audio/x-flac", &[("framed", &true)]); - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &sink_caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - let src_caps = gst::Caps::new_simple( - "audio/x-raw", - &[ - ( - "format", - &gst::List::new(&[ - &gst_audio::AudioFormat::S8.to_str(), - &gst_audio::AUDIO_FORMAT_S16.to_str(), - &gst_audio::AUDIO_FORMAT_S2432.to_str(), - &gst_audio::AUDIO_FORMAT_S32.to_str(), - ]), - ), - ("rate", &gst::IntRange::<i32>::new(1, 655_350)), - ("channels", &gst::IntRange::<i32>::new(1, 8)), - ("layout", &"interleaved"), - ], - ); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &src_caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); +impl ElementImpl for ClaxonDec { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Claxon FLAC decoder", + "Decoder/Audio", + "Claxon FLAC decoder", + "Ruben Gonzalez <rgonzalez@fluendo.com>", + ) + }); + + Some(&*ELEMENT_METADATA) } -} -impl ObjectImpl for ClaxonDec {} + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let sink_caps = gst::Caps::new_simple("audio/x-flac", &[("framed", &true)]); + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &sink_caps, + ) + .unwrap(); + + let src_caps = gst::Caps::new_simple( + "audio/x-raw", + &[ + ( + "format", + &gst::List::new(&[ + &gst_audio::AudioFormat::S8.to_str(), + &gst_audio::AUDIO_FORMAT_S16.to_str(), + &gst_audio::AUDIO_FORMAT_S2432.to_str(), + &gst_audio::AUDIO_FORMAT_S32.to_str(), + ]), + ), + ("rate", &gst::IntRange::<i32>::new(1, 655_350)), + ("channels", &gst::IntRange::<i32>::new(1, 8)), + ("layout", &"interleaved"), + ], + ); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &src_caps, + ) + .unwrap(); + + vec![sink_pad_template, src_pad_template] + }); -impl ElementImpl for ClaxonDec {} + PAD_TEMPLATES.as_ref() + } +} impl AudioDecoderImpl for ClaxonDec { fn stop(&self, _element: &Self::Type) -> Result<(), gst::ErrorMessage> { @@ -116,7 +131,7 @@ impl AudioDecoderImpl for ClaxonDec { } fn set_format(&self, element: &Self::Type, caps: &gst::Caps) -> Result<(), gst::LoggableError> { - gst_debug!(self.cat, obj: element, "Setting format {:?}", caps); + gst_debug!(CAT, obj: element, "Setting format {:?}", caps); let mut streaminfo: Option<claxon::metadata::StreamInfo> = None; let mut audio_info: Option<gst_audio::AudioInfo> = None; @@ -127,18 +142,18 @@ impl AudioDecoderImpl for ClaxonDec { if streamheaders.len() < 2 { gst_debug!( - self.cat, + CAT, obj: element, "Not enough streamheaders, trying in-band" ); } else { let ident_buf = streamheaders[0].get::<gst::Buffer>(); if let Ok(Some(ident_buf)) = ident_buf { - gst_debug!(self.cat, obj: element, "Got streamheader buffers"); + gst_debug!(CAT, obj: element, "Got streamheader buffers"); let inmap = ident_buf.map_readable().unwrap(); if inmap[0..7] != [0x7f, b'F', b'L', b'A', b'C', 0x01, 0x00] { - gst_debug!(self.cat, obj: element, "Unknown streamheader format"); + gst_debug!(CAT, obj: element, "Unknown streamheader format"); } else if let Ok(tstreaminfo) = get_claxon_streaminfo(&inmap[13..]) { if let Ok(taudio_info) = get_gstaudioinfo(tstreaminfo) { // To speed up negotiation @@ -146,7 +161,7 @@ impl AudioDecoderImpl for ClaxonDec { || element.negotiate().is_err() { gst_debug!( - self.cat, + CAT, obj: element, "Error to negotiate output from based on in-caps streaminfo" ); @@ -175,7 +190,7 @@ impl AudioDecoderImpl for ClaxonDec { element: &Self::Type, inbuf: Option<&gst::Buffer>, ) -> Result<gst::FlowSuccess, gst::FlowError> { - gst_debug!(self.cat, obj: element, "Handling buffer {:?}", inbuf); + gst_debug!(CAT, obj: element, "Handling buffer {:?}", inbuf); let inbuf = match inbuf { None => return Ok(gst::FlowSuccess::Ok), @@ -183,7 +198,7 @@ impl AudioDecoderImpl for ClaxonDec { }; let inmap = inbuf.map_readable().map_err(|_| { - gst_error!(self.cat, obj: element, "Failed to buffer readable"); + gst_error!(CAT, obj: element, "Failed to buffer readable"); gst::FlowError::Error })?; @@ -191,17 +206,17 @@ impl AudioDecoderImpl for ClaxonDec { let state = state_guard.as_mut().ok_or(gst::FlowError::NotNegotiated)?; if inmap.as_slice() == b"fLaC" { - gst_debug!(self.cat, obj: element, "fLaC buffer received"); + gst_debug!(CAT, obj: element, "fLaC buffer received"); } else if inmap[0] & 0x7F == 0x00 { - gst_debug!(self.cat, obj: element, "Streaminfo header buffer received"); + gst_debug!(CAT, obj: element, "Streaminfo header buffer received"); return self.handle_streaminfo_header(element, state, inmap.as_ref()); } else if inmap[0] == 0b1111_1111 && inmap[1] & 0b1111_1100 == 0b1111_1000 { - gst_debug!(self.cat, obj: element, "Data buffer received"); + gst_debug!(CAT, obj: element, "Data buffer received"); return self.handle_data(element, state, inmap.as_ref()); } else { // info about other headers in flacparse and https://xiph.org/flac/format.html gst_debug!( - self.cat, + CAT, obj: element, "Other header buffer received {:?}", inmap[0] & 0x7F @@ -230,7 +245,7 @@ impl ClaxonDec { })?; gst_debug!( - self.cat, + CAT, obj: element, "Successfully parsed headers: {:?}", audio_info diff --git a/audio/csound/src/filter/imp.rs b/audio/csound/src/filter/imp.rs index 6fdda3aff..9967587e4 100644 --- a/audio/csound/src/filter/imp.rs +++ b/audio/csound/src/filter/imp.rs @@ -80,49 +80,6 @@ pub struct CsoundFilter { compiled: AtomicBool, } -static PROPERTIES: [subclass::Property; 4] = [ - subclass::Property("loop", |name| { - glib::ParamSpec::boolean( - name, - "Loop", - "loop over the score (can be changed in PLAYING or PAUSED state)", - DEFAULT_LOOP, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("location", |name| { - glib::ParamSpec::string( - name, - "Location", - "Location of the csd file to be used by csound. - Use either location or CSD-text but not both at the same time, if so and error would be triggered", - None, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("csd-text", |name| { - glib::ParamSpec::string( - name, - "CSD-text", - "The content of a csd file passed as a String. - Use either location or csd-text but not both at the same time, if so and error would be triggered", - None, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("score_offset", |name| { - glib::ParamSpec::double( - name, - "Score Offset", - "Score offset in seconds to start the performance", - 0.0, - f64::MAX, - SCORE_OFFSET_DEFAULT, - glib::ParamFlags::READWRITE, - ) - }), -]; - impl State { // Considering an input of size: input_size and the user's ksmps, // calculates the equivalent output_size @@ -361,6 +318,7 @@ impl ObjectSubclass for CsoundFilter { const NAME: &'static str = "CsoundFilter"; type Type = super::CsoundFilter; type ParentType = gst_base::BaseTransform; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -383,61 +341,63 @@ impl ObjectSubclass for CsoundFilter { compiled: AtomicBool::new(false), } } +} - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Audio filter", - "Filter/Effect/Audio", - "Implement an audio filter/effects using Csound", - "Natanael Mojica <neithanmo@gmail.com>", - ); +impl ObjectImpl for CsoundFilter { + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::boolean( + "loop", + "Loop", + "loop over the score (can be changed in PLAYING or PAUSED state)", + DEFAULT_LOOP, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::string( + "location", + "Location", + "Location of the csd file to be used by csound. + Use either location or CSD-text but not both at the same time, if so and error would be triggered", + None, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::string( + "csd-text", + "CSD-text", + "The content of a csd file passed as a String. + Use either location or csd-text but not both at the same time, if so and error would be triggered", + None, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::double( + "score-offset", + "Score Offset", + "Score offset in seconds to start the performance", + 0.0, + f64::MAX, + SCORE_OFFSET_DEFAULT, + glib::ParamFlags::READWRITE, + ), + ] + }); - let caps = gst::Caps::new_simple( - "audio/x-raw", - &[ - ("format", &gst_audio::AUDIO_FORMAT_F64.to_str()), - ("rate", &gst::IntRange::<i32>::new(1, i32::MAX)), - ("channels", &gst::IntRange::<i32>::new(1, i32::MAX)), - ("layout", &"interleaved"), - ], - ); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - klass.install_properties(&PROPERTIES); - - klass.configure( - gst_base::subclass::BaseTransformMode::NeverInPlace, - false, - false, - ); + PROPERTIES.as_ref() } -} -impl ObjectImpl for CsoundFilter { - fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; - match *prop { - subclass::Property("loop", ..) => { + fn set_property( + &self, + _obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "loop" => { let mut settings = self.settings.lock().unwrap(); settings.loop_ = value.get_some().expect("type checked upstream"); } - subclass::Property("location", ..) => { + "location" => { let mut settings = self.settings.lock().unwrap(); if self.state.lock().unwrap().is_none() { settings.location = match value.get::<String>() { @@ -446,7 +406,7 @@ impl ObjectImpl for CsoundFilter { }; } } - subclass::Property("csd-text", ..) => { + "csd-text" => { let mut settings = self.settings.lock().unwrap(); if self.state.lock().unwrap().is_none() { settings.csd_text = match value.get::<String>() { @@ -455,7 +415,7 @@ impl ObjectImpl for CsoundFilter { }; } } - subclass::Property("score_offset", ..) => { + "score_offset" => { let mut settings = self.settings.lock().unwrap(); settings.offset = value.get_some().expect("type checked upstream"); } @@ -463,23 +423,21 @@ impl ObjectImpl for CsoundFilter { } } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("loop", ..) => { + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "loop" => { let settings = self.settings.lock().unwrap(); settings.loop_.to_value() } - subclass::Property("location", ..) => { + "location" => { let settings = self.settings.lock().unwrap(); settings.location.to_value() } - subclass::Property("csd-text", ..) => { + "csd-text" => { let settings = self.settings.lock().unwrap(); settings.csd_text.to_value() } - subclass::Property("score_offset", ..) => { + "score_offset" => { let settings = self.settings.lock().unwrap(); settings.offset.to_value() } @@ -488,9 +446,60 @@ impl ObjectImpl for CsoundFilter { } } -impl ElementImpl for CsoundFilter {} +impl ElementImpl for CsoundFilter { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Audio filter", + "Filter/Effect/Audio", + "Implement an audio filter/effects using Csound", + "Natanael Mojica <neithanmo@gmail.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::new_simple( + "audio/x-raw", + &[ + ("format", &gst_audio::AUDIO_FORMAT_F64.to_str()), + ("rate", &gst::IntRange::<i32>::new(1, i32::MAX)), + ("channels", &gst::IntRange::<i32>::new(1, i32::MAX)), + ("layout", &"interleaved"), + ], + ); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } +} impl BaseTransformImpl for CsoundFilter { + const MODE: gst_base::subclass::BaseTransformMode = + gst_base::subclass::BaseTransformMode::NeverInPlace; + const PASSTHROUGH_ON_SAME_CAPS: bool = false; + const TRANSFORM_IP_ON_PASSTHROUGH: bool = false; + fn start(&self, _element: &Self::Type) -> std::result::Result<(), gst::ErrorMessage> { self.compile_score()?; diff --git a/audio/lewton/src/lewtondec/imp.rs b/audio/lewton/src/lewtondec/imp.rs index 3573a55e7..c08167752 100644 --- a/audio/lewton/src/lewtondec/imp.rs +++ b/audio/lewton/src/lewtondec/imp.rs @@ -47,6 +47,7 @@ impl ObjectSubclass for LewtonDec { const NAME: &'static str = "LewtonDec"; type Type = super::LewtonDec; type ParentType = gst_audio::AudioDecoder; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -57,48 +58,58 @@ impl ObjectSubclass for LewtonDec { state: AtomicRefCell::new(None), } } +} - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "lewton Vorbis decoder", - "Decoder/Audio", - "lewton Vorbis decoder", - "Sebastian Dröge <sebastian@centricular.com>", - ); +impl ObjectImpl for LewtonDec {} - let sink_caps = gst::Caps::new_simple("audio/x-vorbis", &[]); - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &sink_caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - let src_caps = gst::Caps::new_simple( - "audio/x-raw", - &[ - ("format", &gst_audio::AUDIO_FORMAT_F32.to_str()), - ("rate", &gst::IntRange::<i32>::new(1, std::i32::MAX)), - ("channels", &gst::IntRange::<i32>::new(1, 255)), - ("layout", &"interleaved"), - ], - ); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &src_caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); +impl ElementImpl for LewtonDec { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "lewton Vorbis decoder", + "Decoder/Audio", + "lewton Vorbis decoder", + "Sebastian Dröge <sebastian@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) } -} -impl ObjectImpl for LewtonDec {} + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let sink_caps = gst::Caps::new_simple("audio/x-vorbis", &[]); + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &sink_caps, + ) + .unwrap(); + + let src_caps = gst::Caps::new_simple( + "audio/x-raw", + &[ + ("format", &gst_audio::AUDIO_FORMAT_F32.to_str()), + ("rate", &gst::IntRange::<i32>::new(1, std::i32::MAX)), + ("channels", &gst::IntRange::<i32>::new(1, 255)), + ("layout", &"interleaved"), + ], + ); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &src_caps, + ) + .unwrap(); + + vec![sink_pad_template, src_pad_template] + }); -impl ElementImpl for LewtonDec {} + PAD_TEMPLATES.as_ref() + } +} impl AudioDecoderImpl for LewtonDec { fn stop(&self, _element: &Self::Type) -> Result<(), gst::ErrorMessage> { diff --git a/generic/file/src/filesink/imp.rs b/generic/file/src/filesink/imp.rs index 2c301db8c..13d9015ac 100644 --- a/generic/file/src/filesink/imp.rs +++ b/generic/file/src/filesink/imp.rs @@ -38,16 +38,6 @@ impl Default for Settings { } } -static PROPERTIES: [subclass::Property; 1] = [subclass::Property("location", |name| { - glib::ParamSpec::string( - name, - "File Location", - "Location of the file to write", - None, - glib::ParamFlags::READWRITE, - ) -})]; - enum State { Stopped, Started { file: File, position: u64 }, @@ -120,6 +110,7 @@ impl ObjectSubclass for FileSink { const NAME: &'static str = "RsFileSink"; type Type = super::FileSink; type ParentType = gst_base::BaseSink; + type Interfaces = (gst::URIHandler,); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -131,38 +122,32 @@ impl ObjectSubclass for FileSink { state: Mutex::new(Default::default()), } } - - fn type_init(type_: &mut subclass::InitializingType<Self>) { - type_.add_interface::<gst::URIHandler>(); - } - - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "File Sink", - "Sink/File", - "Write stream to a file", - "François Laignel <fengalin@free.fr>, Luis de Bethencourt <luisbg@osg.samsung.com>", - ); - - let caps = gst::Caps::new_any(); - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - klass.install_properties(&PROPERTIES); - } } impl ObjectImpl for FileSink { - fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; - match *prop { - subclass::Property("location", ..) => { + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![glib::ParamSpec::string( + "location", + "File Location", + "Location of the file to write", + None, + glib::ParamFlags::READWRITE, + )] + }); + + PROPERTIES.as_ref() + } + + fn set_property( + &self, + obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "location" => { let res = match value.get::<String>() { Ok(Some(location)) => FileLocation::try_from_path_str(location) .and_then(|file_location| self.set_location(obj, Some(file_location))), @@ -178,10 +163,9 @@ impl ObjectImpl for FileSink { }; } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - match *prop { - subclass::Property("location", ..) => { + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "location" => { let settings = self.settings.lock().unwrap(); let location = settings .location @@ -195,7 +179,37 @@ impl ObjectImpl for FileSink { } } -impl ElementImpl for FileSink {} +impl ElementImpl for FileSink { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "File Sink", + "Sink/File", + "Write stream to a file", + "François Laignel <fengalin@free.fr>, Luis de Bethencourt <luisbg@osg.samsung.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::new_any(); + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } +} impl BaseSinkImpl for FileSink { fn start(&self, element: &Self::Type) -> Result<(), gst::ErrorMessage> { @@ -286,6 +300,12 @@ impl BaseSinkImpl for FileSink { } impl URIHandlerImpl for FileSink { + const URI_TYPE: gst::URIType = gst::URIType::Sink; + + fn get_protocols() -> &'static [&'static str] { + &["file"] + } + fn get_uri(&self, _element: &Self::Type) -> Option<String> { let settings = self.settings.lock().unwrap(); @@ -308,12 +328,4 @@ impl URIHandlerImpl for FileSink { Ok(()) } } - - fn get_uri_type() -> gst::URIType { - gst::URIType::Sink - } - - fn get_protocols() -> Vec<String> { - vec!["file".to_string()] - } } diff --git a/generic/file/src/filesrc/imp.rs b/generic/file/src/filesrc/imp.rs index baae92fac..1d037c371 100644 --- a/generic/file/src/filesrc/imp.rs +++ b/generic/file/src/filesrc/imp.rs @@ -38,16 +38,6 @@ impl Default for Settings { } } -static PROPERTIES: [subclass::Property; 1] = [subclass::Property("location", |name| { - glib::ParamSpec::string( - name, - "File Location", - "Location of the file to read from", - None, - glib::ParamFlags::READWRITE, - ) -})]; - enum State { Stopped, Started { file: File, position: u64 }, @@ -134,6 +124,7 @@ impl ObjectSubclass for FileSrc { const NAME: &'static str = "RsFileSrc"; type Type = super::FileSrc; type ParentType = gst_base::BaseSrc; + type Interfaces = (gst::URIHandler,); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -145,38 +136,32 @@ impl ObjectSubclass for FileSrc { state: Mutex::new(Default::default()), } } - - fn type_init(type_: &mut subclass::InitializingType<Self>) { - type_.add_interface::<gst::URIHandler>(); - } - - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "File Source", - "Source/File", - "Read stream from a file", - "François Laignel <fengalin@free.fr>, Sebastian Dröge <sebastian@centricular.com>", - ); - - let caps = gst::Caps::new_any(); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - klass.install_properties(&PROPERTIES); - } } impl ObjectImpl for FileSrc { - fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; - match *prop { - subclass::Property("location", ..) => { + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![glib::ParamSpec::string( + "location", + "File Location", + "Location of the file to read from", + None, + glib::ParamFlags::READWRITE, + )] + }); + + PROPERTIES.as_ref() + } + + fn set_property( + &self, + obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "location" => { let res = match value.get::<String>() { Ok(Some(location)) => FileLocation::try_from_path_str(location) .and_then(|file_location| self.set_location(obj, Some(file_location))), @@ -192,10 +177,9 @@ impl ObjectImpl for FileSrc { }; } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - match *prop { - subclass::Property("location", ..) => { + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "location" => { let settings = self.settings.lock().unwrap(); let location = settings .location @@ -215,7 +199,37 @@ impl ObjectImpl for FileSrc { } } -impl ElementImpl for FileSrc {} +impl ElementImpl for FileSrc { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "File Source", + "Source/File", + "Read stream from a file", + "François Laignel <fengalin@free.fr>, Sebastian Dröge <sebastian@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::new_any(); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } +} impl BaseSrcImpl for FileSrc { fn is_seekable(&self, _src: &Self::Type) -> bool { @@ -339,6 +353,12 @@ impl BaseSrcImpl for FileSrc { } impl URIHandlerImpl for FileSrc { + const URI_TYPE: gst::URIType = gst::URIType::Src; + + fn get_protocols() -> &'static [&'static str] { + &["file"] + } + fn get_uri(&self, _element: &Self::Type) -> Option<String> { let settings = self.settings.lock().unwrap(); @@ -361,12 +381,4 @@ impl URIHandlerImpl for FileSrc { Ok(()) } } - - fn get_uri_type() -> gst::URIType { - gst::URIType::Src - } - - fn get_protocols() -> Vec<String> { - vec!["file".to_string()] - } } diff --git a/generic/sodium/src/decrypter/imp.rs b/generic/sodium/src/decrypter/imp.rs index d1b73aab4..9aa80b863 100644 --- a/generic/sodium/src/decrypter/imp.rs +++ b/generic/sodium/src/decrypter/imp.rs @@ -41,27 +41,6 @@ static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| { ) }); -static PROPERTIES: [subclass::Property; 2] = [ - subclass::Property("receiver-key", |name| { - glib::ParamSpec::boxed( - name, - "Receiver Key", - "The private key of the Reeiver", - glib::Bytes::static_type(), - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("sender-key", |name| { - glib::ParamSpec::boxed( - name, - "Sender Key", - "The public key of the Sender", - glib::Bytes::static_type(), - glib::ParamFlags::WRITABLE, - ) - }), -]; - #[derive(Debug, Clone, Default)] struct Props { receiver_key: Option<glib::Bytes>, @@ -570,6 +549,7 @@ impl ObjectSubclass for Decrypter { const NAME: &'static str = "RsSodiumDecryptor"; type Type = super::Decrypter; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -621,38 +601,32 @@ impl ObjectSubclass for Decrypter { state, } } - - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Decrypter", - "Generic", - "libsodium-based file decrypter", - "Jordan Petridis <jordan@centricular.com>", - ); - - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &gst::Caps::new_any(), - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - let sink_caps = gst::Caps::builder("application/x-sodium-encrypted").build(); - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &sink_caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - klass.install_properties(&PROPERTIES); - } } impl ObjectImpl for Decrypter { + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::boxed( + "receiver-key", + "Receiver Key", + "The private key of the Reeiver", + glib::Bytes::static_type(), + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boxed( + "sender-key", + "Sender Key", + "The public key of the Sender", + glib::Bytes::static_type(), + glib::ParamFlags::WRITABLE, + ), + ] + }); + + PROPERTIES.as_ref() + } + fn constructed(&self, obj: &Self::Type) { self.parent_constructed(obj); @@ -660,16 +634,20 @@ impl ObjectImpl for Decrypter { obj.add_pad(&self.srcpad).unwrap(); } - fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("sender-key", ..) => { + fn set_property( + &self, + _obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "sender-key" => { let mut props = self.props.lock().unwrap(); props.sender_key = value.get().expect("type checked upstream"); } - subclass::Property("receiver-key", ..) => { + "receiver-key" => { let mut props = self.props.lock().unwrap(); props.receiver_key = value.get().expect("type checked upstream"); } @@ -678,11 +656,9 @@ impl ObjectImpl for Decrypter { } } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("receiver-key", ..) => { + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "receiver-key" => { let props = self.props.lock().unwrap(); props.receiver_key.to_value() } @@ -693,6 +669,44 @@ impl ObjectImpl for Decrypter { } impl ElementImpl for Decrypter { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Decrypter", + "Generic", + "libsodium-based file decrypter", + "Jordan Petridis <jordan@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &gst::Caps::new_any(), + ) + .unwrap(); + + let sink_caps = gst::Caps::builder("application/x-sodium-encrypted").build(); + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &sink_caps, + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + fn change_state( &self, element: &Self::Type, diff --git a/generic/sodium/src/encrypter/imp.rs b/generic/sodium/src/encrypter/imp.rs index 28d921e33..3827669e5 100644 --- a/generic/sodium/src/encrypter/imp.rs +++ b/generic/sodium/src/encrypter/imp.rs @@ -44,38 +44,6 @@ static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| { ) }); -static PROPERTIES: [subclass::Property; 3] = [ - subclass::Property("receiver-key", |name| { - glib::ParamSpec::boxed( - name, - "Receiver Key", - "The public key of the Receiver", - glib::Bytes::static_type(), - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("sender-key", |name| { - glib::ParamSpec::boxed( - name, - "Sender Key", - "The private key of the Sender", - glib::Bytes::static_type(), - glib::ParamFlags::WRITABLE, - ) - }), - subclass::Property("block-size", |name| { - glib::ParamSpec::uint( - name, - "Block Size", - "The block-size of the chunks", - 1024, - std::u32::MAX, - 32768, - glib::ParamFlags::READWRITE, - ) - }), -]; - #[derive(Debug, Clone)] struct Props { receiver_key: Option<glib::Bytes>, @@ -392,6 +360,7 @@ impl ObjectSubclass for Encrypter { const NAME: &'static str = "RsSodiumEncrypter"; type Type = super::Encrypter; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -444,38 +413,41 @@ impl ObjectSubclass for Encrypter { state, } } - - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Encrypter", - "Generic", - "libsodium-based file encrypter", - "Jordan Petridis <jordan@centricular.com>", - ); - - let src_caps = gst::Caps::builder("application/x-sodium-encrypted").build(); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &src_caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &gst::Caps::new_any(), - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - klass.install_properties(&PROPERTIES); - } } impl ObjectImpl for Encrypter { + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::boxed( + "receiver-key", + "Receiver Key", + "The public key of the Receiver", + glib::Bytes::static_type(), + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boxed( + "sender-key", + "Sender Key", + "The private key of the Sender", + glib::Bytes::static_type(), + glib::ParamFlags::WRITABLE, + ), + glib::ParamSpec::uint( + "block-size", + "Block Size", + "The block-size of the chunks", + 1024, + std::u32::MAX, + 32768, + glib::ParamFlags::READWRITE, + ), + ] + }); + + PROPERTIES.as_ref() + } + fn constructed(&self, obj: &Self::Type) { self.parent_constructed(obj); @@ -483,21 +455,25 @@ impl ObjectImpl for Encrypter { obj.add_pad(&self.srcpad).unwrap(); } - fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("sender-key", ..) => { + fn set_property( + &self, + _obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "sender-key" => { let mut props = self.props.lock().unwrap(); props.sender_key = value.get().expect("type checked upstream"); } - subclass::Property("receiver-key", ..) => { + "receiver-key" => { let mut props = self.props.lock().unwrap(); props.receiver_key = value.get().expect("type checked upstream"); } - subclass::Property("block-size", ..) => { + "block-size" => { let mut props = self.props.lock().unwrap(); props.block_size = value.get_some().expect("type checked upstream"); } @@ -506,16 +482,14 @@ impl ObjectImpl for Encrypter { } } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("receiver-key", ..) => { + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "receiver-key" => { let props = self.props.lock().unwrap(); props.receiver_key.to_value() } - subclass::Property("block-size", ..) => { + "block-size" => { let props = self.props.lock().unwrap(); props.block_size.to_value() } @@ -526,6 +500,44 @@ impl ObjectImpl for Encrypter { } impl ElementImpl for Encrypter { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Encrypter", + "Generic", + "libsodium-based file encrypter", + "Jordan Petridis <jordan@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let src_caps = gst::Caps::builder("application/x-sodium-encrypted").build(); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &src_caps, + ) + .unwrap(); + + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &gst::Caps::new_any(), + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + fn change_state( &self, element: &Self::Type, diff --git a/generic/threadshare/src/appsrc/imp.rs b/generic/threadshare/src/appsrc/imp.rs index 908b99a53..e34ef3150 100644 --- a/generic/threadshare/src/appsrc/imp.rs +++ b/generic/threadshare/src/appsrc/imp.rs @@ -66,58 +66,6 @@ impl Default for Settings { } } -static PROPERTIES: [subclass::Property; 5] = [ - subclass::Property("context", |name| { - glib::ParamSpec::string( - name, - "Context", - "Context name to share threads with", - Some(DEFAULT_CONTEXT), - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("context-wait", |name| { - glib::ParamSpec::uint( - name, - "Context Wait", - "Throttle poll loop to run at most once every this many ms", - 0, - 1000, - DEFAULT_CONTEXT_WAIT, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("max-buffers", |name| { - glib::ParamSpec::uint( - name, - "Max Buffers", - "Maximum number of buffers to queue up", - 1, - u32::MAX, - DEFAULT_MAX_BUFFERS, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("caps", |name| { - glib::ParamSpec::boxed( - name, - "Caps", - "Caps to use", - gst::Caps::static_type(), - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("do-timestamp", |name| { - glib::ParamSpec::boolean( - name, - "Do Timestamp", - "Timestamp buffers with the current running time on arrival", - DEFAULT_DO_TIMESTAMP, - glib::ParamFlags::READWRITE, - ) - }), -]; - static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| { gst::DebugCategory::new( "ts-appsrc", @@ -560,68 +508,12 @@ impl ObjectSubclass for AppSrc { const NAME: &'static str = "RsTsAppSrc"; type Type = super::AppSrc; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; glib::object_subclass!(); - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Thread-sharing app source", - "Source/Generic", - "Thread-sharing app source", - "Sebastian Dröge <sebastian@centricular.com>", - ); - - let caps = gst::Caps::new_any(); - - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - klass.install_properties(&PROPERTIES); - - klass.add_signal_with_class_handler( - "push-buffer", - glib::SignalFlags::RUN_LAST | glib::SignalFlags::ACTION, - &[gst::Buffer::static_type()], - bool::static_type(), - |_, args| { - let element = args[0] - .get::<super::AppSrc>() - .expect("signal arg") - .expect("missing signal arg"); - let buffer = args[1] - .get::<gst::Buffer>() - .expect("signal arg") - .expect("missing signal arg"); - let appsrc = Self::from_instance(&element); - - Some(appsrc.push_buffer(&element, buffer).to_value()) - }, - ); - - klass.add_signal_with_class_handler( - "end-of-stream", - glib::SignalFlags::RUN_LAST | glib::SignalFlags::ACTION, - &[], - bool::static_type(), - |_, args| { - let element = args[0] - .get::<super::AppSrc>() - .expect("signal arg") - .expect("missing signal arg"); - let appsrc = Self::from_instance(&element); - Some(appsrc.end_of_stream(&element).to_value()) - }, - ); - } - fn with_class(klass: &Self::Class) -> Self { let src_pad_handler = AppSrcPadHandler::default(); @@ -639,43 +531,134 @@ impl ObjectSubclass for AppSrc { } impl ObjectImpl for AppSrc { - fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::string( + "context", + "Context", + "Context name to share threads with", + Some(DEFAULT_CONTEXT), + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "context-wait", + "Context Wait", + "Throttle poll loop to run at most once every this many ms", + 0, + 1000, + DEFAULT_CONTEXT_WAIT, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "max-buffers", + "Max Buffers", + "Maximum number of buffers to queue up", + 1, + u32::MAX, + DEFAULT_MAX_BUFFERS, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boxed( + "caps", + "Caps", + "Caps to use", + gst::Caps::static_type(), + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boolean( + "do-timestamp", + "Do Timestamp", + "Timestamp buffers with the current running time on arrival", + DEFAULT_DO_TIMESTAMP, + glib::ParamFlags::READWRITE, + ), + ] + }); + + PROPERTIES.as_ref() + } + fn signals() -> &'static [glib::subclass::Signal] { + static SIGNALS: Lazy<Vec<glib::subclass::Signal>> = Lazy::new(|| { + vec![ + glib::subclass::Signal::builder( + "push-buffer", + &[gst::Buffer::static_type()], + bool::static_type(), + ) + .action() + .class_handler(|_, args| { + let element = args[0] + .get::<super::AppSrc>() + .expect("signal arg") + .expect("missing signal arg"); + let buffer = args[1] + .get::<gst::Buffer>() + .expect("signal arg") + .expect("missing signal arg"); + let appsrc = AppSrc::from_instance(&element); + + Some(appsrc.push_buffer(&element, buffer).to_value()) + }) + .build(), + glib::subclass::Signal::builder("end-of-stream", &[], bool::static_type()) + .action() + .class_handler(|_, args| { + let element = args[0] + .get::<super::AppSrc>() + .expect("signal arg") + .expect("missing signal arg"); + let appsrc = AppSrc::from_instance(&element); + + Some(appsrc.end_of_stream(&element).to_value()) + }) + .build(), + ] + }); + + SIGNALS.as_ref() + } + + fn set_property( + &self, + _obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { let mut settings = self.settings.lock().unwrap(); - match *prop { - subclass::Property("context", ..) => { + match pspec.get_name() { + "context" => { settings.context = value .get() .expect("type checked upstream") .unwrap_or_else(|| "".into()); } - subclass::Property("context-wait", ..) => { + "context-wait" => { settings.context_wait = value.get_some().expect("type checked upstream"); } - subclass::Property("caps", ..) => { + "caps" => { settings.caps = value.get().expect("type checked upstream"); } - subclass::Property("max-buffers", ..) => { + "max-buffers" => { settings.max_buffers = value.get_some().expect("type checked upstream"); } - subclass::Property("do-timestamp", ..) => { + "do-timestamp" => { settings.do_timestamp = value.get_some().expect("type checked upstream"); } _ => unimplemented!(), } } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { let settings = self.settings.lock().unwrap(); - match *prop { - subclass::Property("context", ..) => settings.context.to_value(), - subclass::Property("context-wait", ..) => settings.context_wait.to_value(), - subclass::Property("caps", ..) => settings.caps.to_value(), - subclass::Property("max-buffers", ..) => settings.max_buffers.to_value(), - subclass::Property("do-timestamp", ..) => settings.do_timestamp.to_value(), + match pspec.get_name() { + "context" => settings.context.to_value(), + "context-wait" => settings.context_wait.to_value(), + "caps" => settings.caps.to_value(), + "max-buffers" => settings.max_buffers.to_value(), + "do-timestamp" => settings.do_timestamp.to_value(), _ => unimplemented!(), } } @@ -690,6 +673,36 @@ impl ObjectImpl for AppSrc { } impl ElementImpl for AppSrc { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Thread-sharing app source", + "Source/Generic", + "Thread-sharing app source", + "Sebastian Dröge <sebastian@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::new_any(); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + fn change_state( &self, element: &Self::Type, diff --git a/generic/threadshare/src/inputselector/imp.rs b/generic/threadshare/src/inputselector/imp.rs index 3b03add30..36c736495 100644 --- a/generic/threadshare/src/inputselector/imp.rs +++ b/generic/threadshare/src/inputselector/imp.rs @@ -55,38 +55,6 @@ impl Default for Settings { } } -static PROPERTIES: [subclass::Property; 3] = [ - subclass::Property("context", |name| { - glib::ParamSpec::string( - name, - "Context", - "Context name to share threads with", - Some(DEFAULT_CONTEXT), - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("context-wait", |name| { - glib::ParamSpec::uint( - name, - "Context Wait", - "Throttle poll loop to run at most once every this many ms", - 0, - 1000, - DEFAULT_CONTEXT_WAIT, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("active-pad", |name| { - glib::ParamSpec::object( - name, - "Active Pad", - "Currently active pad", - gst::Pad::static_type(), - glib::ParamFlags::READWRITE, - ) - }), -]; - #[derive(Debug)] struct InputSelectorPadSinkHandlerInner { segment: Option<gst::Segment>, @@ -429,42 +397,12 @@ impl ObjectSubclass for InputSelector { const NAME: &'static str = "RsTsInputSelector"; type Type = super::InputSelector; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; glib::object_subclass!(); - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Thread-sharing input selector", - "Generic", - "Simple input selector element", - "Mathieu Duponchelle <mathieu@centricular.com>", - ); - - let caps = gst::Caps::new_any(); - - let sink_pad_template = gst::PadTemplate::new( - "sink_%u", - gst::PadDirection::Sink, - gst::PadPresence::Request, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - klass.install_properties(&PROPERTIES); - } - fn with_class(klass: &Self::Class) -> Self { Self { src_pad: PadSrc::new( @@ -479,22 +417,58 @@ impl ObjectSubclass for InputSelector { } impl ObjectImpl for InputSelector { - fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::string( + "context", + "Context", + "Context name to share threads with", + Some(DEFAULT_CONTEXT), + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "context-wait", + "Context Wait", + "Throttle poll loop to run at most once every this many ms", + 0, + 1000, + DEFAULT_CONTEXT_WAIT, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::object( + "active-pad", + "Active Pad", + "Currently active pad", + gst::Pad::static_type(), + glib::ParamFlags::READWRITE, + ), + ] + }); + + PROPERTIES.as_ref() + } - match *prop { - subclass::Property("context", ..) => { + fn set_property( + &self, + _obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "context" => { let mut settings = self.settings.lock().unwrap(); settings.context = value .get() .expect("type checked upstream") .unwrap_or_else(|| "".into()); } - subclass::Property("context-wait", ..) => { + "context-wait" => { let mut settings = self.settings.lock().unwrap(); settings.context_wait = value.get_some().expect("type checked upstream"); } - subclass::Property("active-pad", ..) => { + "active-pad" => { let pad = value.get::<gst::Pad>().expect("type checked upstream"); let mut state = self.state.lock().unwrap(); let pads = self.pads.lock().unwrap(); @@ -526,19 +500,17 @@ impl ObjectImpl for InputSelector { } } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("context", ..) => { + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "context" => { let settings = self.settings.lock().unwrap(); settings.context.to_value() } - subclass::Property("context-wait", ..) => { + "context-wait" => { let settings = self.settings.lock().unwrap(); settings.context_wait.to_value() } - subclass::Property("active-pad", ..) => { + "active-pad" => { let state = self.state.lock().unwrap(); let active_pad = state.active_sinkpad.clone(); active_pad.to_value() @@ -556,6 +528,44 @@ impl ObjectImpl for InputSelector { } impl ElementImpl for InputSelector { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Thread-sharing input selector", + "Generic", + "Simple input selector element", + "Mathieu Duponchelle <mathieu@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::new_any(); + let sink_pad_template = gst::PadTemplate::new( + "sink_%u", + gst::PadDirection::Sink, + gst::PadPresence::Request, + &caps, + ) + .unwrap(); + + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![sink_pad_template, src_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + fn change_state( &self, element: &Self::Type, diff --git a/generic/threadshare/src/jitterbuffer/imp.rs b/generic/threadshare/src/jitterbuffer/imp.rs index 430f8fd35..19e376900 100644 --- a/generic/threadshare/src/jitterbuffer/imp.rs +++ b/generic/threadshare/src/jitterbuffer/imp.rs @@ -72,80 +72,6 @@ impl Default for Settings { } } -static PROPERTIES: [subclass::Property; 7] = [ - subclass::Property("latency", |name| { - glib::ParamSpec::uint( - name, - "Buffer latency in ms", - "Amount of ms to buffer", - 0, - std::u32::MAX, - DEFAULT_LATENCY_MS, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("do-lost", |name| { - glib::ParamSpec::boolean( - name, - "Do Lost", - "Send an event downstream when a packet is lost", - DEFAULT_DO_LOST, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("max-dropout-time", |name| { - glib::ParamSpec::uint( - name, - "Max dropout time", - "The maximum time (milliseconds) of missing packets tolerated.", - 0, - std::u32::MAX, - DEFAULT_MAX_DROPOUT_TIME, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("max-misorder-time", |name| { - glib::ParamSpec::uint( - name, - "Max misorder time", - "The maximum time (milliseconds) of misordered packets tolerated.", - 0, - std::u32::MAX, - DEFAULT_MAX_MISORDER_TIME, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("stats", |name| { - glib::ParamSpec::boxed( - name, - "Statistics", - "Various statistics", - gst::Structure::static_type(), - glib::ParamFlags::READABLE, - ) - }), - subclass::Property("context", |name| { - glib::ParamSpec::string( - name, - "Context", - "Context name to share threads with", - Some(DEFAULT_CONTEXT), - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("context-wait", |name| { - glib::ParamSpec::uint( - name, - "Context Wait", - "Throttle poll loop to run at most once every this many ms", - 0, - 1000, - DEFAULT_CONTEXT_WAIT, - glib::ParamFlags::READWRITE, - ) - }), -]; - #[derive(Eq)] struct GapPacket { buffer: gst::Buffer, @@ -1413,63 +1339,12 @@ impl ObjectSubclass for JitterBuffer { const NAME: &'static str = "RsTsJitterBuffer"; type Type = super::JitterBuffer; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; glib::object_subclass!(); - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Thread-sharing jitterbuffer", - "Generic", - "Simple jitterbuffer", - "Mathieu Duponchelle <mathieu@centricular.com>", - ); - - let caps = gst::Caps::new_any(); - - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - klass.add_signal( - "request-pt-map", - glib::SignalFlags::RUN_LAST, - &[u32::static_type()], - gst::Caps::static_type(), - ); - - klass.add_signal_with_class_handler( - "clear-pt-map", - glib::SignalFlags::RUN_LAST | glib::SignalFlags::ACTION, - &[], - glib::types::Type::Unit, - |_, args| { - let element = args[0] - .get::<super::JitterBuffer>() - .expect("signal arg") - .expect("missing signal arg"); - let jb = Self::from_instance(&element); - jb.clear_pt_map(&element); - None - }, - ); - - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - klass.install_properties(&PROPERTIES); - } - fn with_class(klass: &Self::Class) -> Self { let sink_pad_handler = SinkHandler::default(); let src_pad_handler = SrcHandler::default(); @@ -1493,11 +1368,102 @@ impl ObjectSubclass for JitterBuffer { } impl ObjectImpl for JitterBuffer { - fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::string( + "context", + "Context", + "Context name to share threads with", + Some(DEFAULT_CONTEXT), + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "context-wait", + "Context Wait", + "Throttle poll loop to run at most once every this many ms", + 0, + 1000, + DEFAULT_CONTEXT_WAIT, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "latency", + "Buffer latency in ms", + "Amount of ms to buffer", + 0, + std::u32::MAX, + DEFAULT_LATENCY_MS, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boolean( + "do-lost", + "Do Lost", + "Send an event downstream when a packet is lost", + DEFAULT_DO_LOST, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "max-dropout-time", + "Max dropout time", + "The maximum time (milliseconds) of missing packets tolerated.", + 0, + std::u32::MAX, + DEFAULT_MAX_DROPOUT_TIME, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "max-misorder-time", + "Max misorder time", + "The maximum time (milliseconds) of misordered packets tolerated.", + 0, + std::u32::MAX, + DEFAULT_MAX_MISORDER_TIME, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boxed( + "stats", + "Statistics", + "Various statistics", + gst::Structure::static_type(), + glib::ParamFlags::READABLE, + ), + ] + }); + + PROPERTIES.as_ref() + } + + fn signals() -> &'static [glib::subclass::Signal] { + static SIGNALS: Lazy<Vec<glib::subclass::Signal>> = Lazy::new(|| { + vec![ + glib::subclass::Signal::builder("clear-pt-map", &[], glib::types::Type::Unit) + .action() + .class_handler(|_, args| { + let element = args[0] + .get::<super::JitterBuffer>() + .expect("signal arg") + .expect("missing signal arg"); + let jb = JitterBuffer::from_instance(&element); + jb.clear_pt_map(&element); + None + }) + .build(), + ] + }); + + SIGNALS.as_ref() + } - match *prop { - subclass::Property("latency", ..) => { + fn set_property( + &self, + obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "latency" => { let latency_ms = { let mut settings = self.settings.lock().unwrap(); settings.latency_ms = value.get_some().expect("type checked upstream"); @@ -1509,26 +1475,26 @@ impl ObjectImpl for JitterBuffer { let _ = obj.post_message(gst::message::Latency::builder().src(obj).build()); } - subclass::Property("do-lost", ..) => { + "do-lost" => { let mut settings = self.settings.lock().unwrap(); settings.do_lost = value.get_some().expect("type checked upstream"); } - subclass::Property("max-dropout-time", ..) => { + "max-dropout-time" => { let mut settings = self.settings.lock().unwrap(); settings.max_dropout_time = value.get_some().expect("type checked upstream"); } - subclass::Property("max-misorder-time", ..) => { + "max-misorder-time" => { let mut settings = self.settings.lock().unwrap(); settings.max_misorder_time = value.get_some().expect("type checked upstream"); } - subclass::Property("context", ..) => { + "context" => { let mut settings = self.settings.lock().unwrap(); settings.context = value .get() .expect("type checked upstream") .unwrap_or_else(|| "".into()); } - subclass::Property("context-wait", ..) => { + "context-wait" => { let mut settings = self.settings.lock().unwrap(); settings.context_wait = value.get_some().expect("type checked upstream"); } @@ -1536,27 +1502,25 @@ impl ObjectImpl for JitterBuffer { } } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("latency", ..) => { + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "latency" => { let settings = self.settings.lock().unwrap(); settings.latency_ms.to_value() } - subclass::Property("do-lost", ..) => { + "do-lost" => { let settings = self.settings.lock().unwrap(); settings.do_lost.to_value() } - subclass::Property("max-dropout-time", ..) => { + "max-dropout-time" => { let settings = self.settings.lock().unwrap(); settings.max_dropout_time.to_value() } - subclass::Property("max-misorder-time", ..) => { + "max-misorder-time" => { let settings = self.settings.lock().unwrap(); settings.max_misorder_time.to_value() } - subclass::Property("stats", ..) => { + "stats" => { let state = self.state.lock().unwrap(); let s = gst::Structure::new( "application/x-rtp-jitterbuffer-stats", @@ -1568,11 +1532,11 @@ impl ObjectImpl for JitterBuffer { ); s.to_value() } - subclass::Property("context", ..) => { + "context" => { let settings = self.settings.lock().unwrap(); settings.context.to_value() } - subclass::Property("context-wait", ..) => { + "context-wait" => { let settings = self.settings.lock().unwrap(); settings.context_wait.to_value() } @@ -1590,6 +1554,45 @@ impl ObjectImpl for JitterBuffer { } impl ElementImpl for JitterBuffer { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Thread-sharing jitterbuffer", + "Generic", + "Simple jitterbuffer", + "Mathieu Duponchelle <mathieu@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::new_any(); + + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![sink_pad_template, src_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + fn change_state( &self, element: &Self::Type, diff --git a/generic/threadshare/src/proxy/imp.rs b/generic/threadshare/src/proxy/imp.rs index 52d408242..bc13654d4 100644 --- a/generic/threadshare/src/proxy/imp.rs +++ b/generic/threadshare/src/proxy/imp.rs @@ -93,81 +93,6 @@ impl Default for SettingsSrc { } } -static PROPERTIES_SRC: [subclass::Property; 6] = [ - subclass::Property("max-size-buffers", |name| { - glib::ParamSpec::uint( - name, - "Max Size Buffers", - "Maximum number of buffers to queue (0=unlimited)", - 0, - u32::MAX, - DEFAULT_MAX_SIZE_BUFFERS, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("max-size-bytes", |name| { - glib::ParamSpec::uint( - name, - "Max Size Bytes", - "Maximum number of bytes to queue (0=unlimited)", - 0, - u32::MAX, - DEFAULT_MAX_SIZE_BYTES, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("max-size-time", |name| { - glib::ParamSpec::uint64( - name, - "Max Size Time", - "Maximum number of nanoseconds to queue (0=unlimited)", - 0, - u64::MAX - 1, - DEFAULT_MAX_SIZE_TIME, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("context", |name| { - glib::ParamSpec::string( - name, - "Context", - "Context name to share threads with", - Some(DEFAULT_CONTEXT), - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("context-wait", |name| { - glib::ParamSpec::uint( - name, - "Context Wait", - "Throttle poll loop to run at most once every this many ms", - 0, - 1000, - DEFAULT_CONTEXT_WAIT, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("proxy-context", |name| { - glib::ParamSpec::string( - name, - "Proxy Context", - "Context name of the proxy to share with", - Some(DEFAULT_PROXY_CONTEXT), - glib::ParamFlags::READWRITE, - ) - }), -]; - -static PROPERTIES_SINK: [subclass::Property; 1] = [subclass::Property("proxy-context", |name| { - glib::ParamSpec::string( - name, - "Proxy Context", - "Context name of the proxy to share with", - Some(DEFAULT_PROXY_CONTEXT), - glib::ParamFlags::READWRITE, - ) -})]; - // TODO: Refactor into a Sender and Receiver instead of the have_ booleans #[derive(Debug, Default)] @@ -650,34 +575,13 @@ impl ProxySink { impl ObjectSubclass for ProxySink { const NAME: &'static str = "RsTsProxySink"; type Type = super::ProxySink; + type Interfaces = (); type ParentType = gst::Element; type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; glib::object_subclass!(); - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Thread-sharing proxy sink", - "Sink/Generic", - "Thread-sharing proxy sink", - "Sebastian Dröge <sebastian@centricular.com>", - ); - - let caps = gst::Caps::new_any(); - - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - klass.install_properties(&PROPERTIES_SINK); - } - fn with_class(klass: &Self::Class) -> Self { Self { sink_pad: PadSink::new( @@ -691,12 +595,30 @@ impl ObjectSubclass for ProxySink { } impl ObjectImpl for ProxySink { - fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES_SINK[id]; + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![glib::ParamSpec::string( + "proxy-context", + "Proxy Context", + "Context name of the proxy to share with", + Some(DEFAULT_PROXY_CONTEXT), + glib::ParamFlags::READWRITE, + )] + }); + + PROPERTIES.as_ref() + } + fn set_property( + &self, + _obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { let mut settings = self.settings.lock().unwrap(); - match *prop { - subclass::Property("proxy-context", ..) => { + match pspec.get_name() { + "proxy-context" => { settings.proxy_context = value .get() .expect("type checked upstream") @@ -706,12 +628,10 @@ impl ObjectImpl for ProxySink { } } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES_SINK[id]; - + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { let settings = self.settings.lock().unwrap(); - match *prop { - subclass::Property("proxy-context", ..) => settings.proxy_context.to_value(), + match pspec.get_name() { + "proxy-context" => settings.proxy_context.to_value(), _ => unimplemented!(), } } @@ -726,6 +646,37 @@ impl ObjectImpl for ProxySink { } impl ElementImpl for ProxySink { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Thread-sharing proxy sink", + "Sink/Generic", + "Thread-sharing proxy sink", + "Sebastian Dröge <sebastian@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::new_any(); + + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + fn change_state( &self, element: &Self::Type, @@ -1166,33 +1117,12 @@ impl ObjectSubclass for ProxySrc { const NAME: &'static str = "RsTsProxySrc"; type Type = super::ProxySrc; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; glib::object_subclass!(); - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Thread-sharing proxy source", - "Source/Generic", - "Thread-sharing proxy source", - "Sebastian Dröge <sebastian@centricular.com>", - ); - - let caps = gst::Caps::new_any(); - - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - klass.install_properties(&PROPERTIES_SRC); - } - fn new() -> Self { unreachable!() } @@ -1212,30 +1142,93 @@ impl ObjectSubclass for ProxySrc { } impl ObjectImpl for ProxySrc { - fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES_SRC[id]; + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::string( + "context", + "Context", + "Context name to share threads with", + Some(DEFAULT_CONTEXT), + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "context-wait", + "Context Wait", + "Throttle poll loop to run at most once every this many ms", + 0, + 1000, + DEFAULT_CONTEXT_WAIT, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::string( + "proxy-context", + "Proxy Context", + "Context name of the proxy to share with", + Some(DEFAULT_PROXY_CONTEXT), + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "max-size-buffers", + "Max Size Buffers", + "Maximum number of buffers to queue (0=unlimited)", + 0, + u32::MAX, + DEFAULT_MAX_SIZE_BUFFERS, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "max-size-bytes", + "Max Size Bytes", + "Maximum number of bytes to queue (0=unlimited)", + 0, + u32::MAX, + DEFAULT_MAX_SIZE_BYTES, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint64( + "max-size-time", + "Max Size Time", + "Maximum number of nanoseconds to queue (0=unlimited)", + 0, + u64::MAX - 1, + DEFAULT_MAX_SIZE_TIME, + glib::ParamFlags::READWRITE, + ), + ] + }); + + PROPERTIES.as_ref() + } + fn set_property( + &self, + _obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { let mut settings = self.settings.lock().unwrap(); - match *prop { - subclass::Property("max-size-buffers", ..) => { + match pspec.get_name() { + "max-size-buffers" => { settings.max_size_buffers = value.get_some().expect("type checked upstream"); } - subclass::Property("max-size-bytes", ..) => { + "max-size-bytes" => { settings.max_size_bytes = value.get_some().expect("type checked upstream"); } - subclass::Property("max-size-time", ..) => { + "max-size-time" => { settings.max_size_time = value.get_some().expect("type checked upstream"); } - subclass::Property("context", ..) => { + "context" => { settings.context = value .get() .expect("type checked upstream") .unwrap_or_else(|| "".into()); } - subclass::Property("context-wait", ..) => { + "context-wait" => { settings.context_wait = value.get_some().expect("type checked upstream"); } - subclass::Property("proxy-context", ..) => { + "proxy-context" => { settings.proxy_context = value .get() .expect("type checked upstream") @@ -1245,17 +1238,15 @@ impl ObjectImpl for ProxySrc { } } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES_SRC[id]; - + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { let settings = self.settings.lock().unwrap(); - match *prop { - subclass::Property("max-size-buffers", ..) => settings.max_size_buffers.to_value(), - subclass::Property("max-size-bytes", ..) => settings.max_size_bytes.to_value(), - subclass::Property("max-size-time", ..) => settings.max_size_time.to_value(), - subclass::Property("context", ..) => settings.context.to_value(), - subclass::Property("context-wait", ..) => settings.context_wait.to_value(), - subclass::Property("proxy-context", ..) => settings.proxy_context.to_value(), + match pspec.get_name() { + "max-size-buffers" => settings.max_size_buffers.to_value(), + "max-size-bytes" => settings.max_size_bytes.to_value(), + "max-size-time" => settings.max_size_time.to_value(), + "context" => settings.context.to_value(), + "context-wait" => settings.context_wait.to_value(), + "proxy-context" => settings.proxy_context.to_value(), _ => unimplemented!(), } } @@ -1270,6 +1261,37 @@ impl ObjectImpl for ProxySrc { } impl ElementImpl for ProxySrc { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Thread-sharing proxy source", + "Source/Generic", + "Thread-sharing proxy source", + "Sebastian Dröge <sebastian@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::new_any(); + + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + fn change_state( &self, element: &Self::Type, diff --git a/generic/threadshare/src/queue/imp.rs b/generic/threadshare/src/queue/imp.rs index 8f0d98c05..dd257cadc 100644 --- a/generic/threadshare/src/queue/imp.rs +++ b/generic/threadshare/src/queue/imp.rs @@ -65,62 +65,6 @@ impl Default for Settings { } } -static PROPERTIES: [subclass::Property; 5] = [ - subclass::Property("max-size-buffers", |name| { - glib::ParamSpec::uint( - name, - "Max Size Buffers", - "Maximum number of buffers to queue (0=unlimited)", - 0, - u32::MAX, - DEFAULT_MAX_SIZE_BUFFERS, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("max-size-bytes", |name| { - glib::ParamSpec::uint( - name, - "Max Size Bytes", - "Maximum number of bytes to queue (0=unlimited)", - 0, - u32::MAX, - DEFAULT_MAX_SIZE_BYTES, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("max-size-time", |name| { - glib::ParamSpec::uint64( - name, - "Max Size Time", - "Maximum number of nanoseconds to queue (0=unlimited)", - 0, - u64::MAX - 1, - DEFAULT_MAX_SIZE_TIME, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("context", |name| { - glib::ParamSpec::string( - name, - "Context", - "Context name to share threads with", - Some(DEFAULT_CONTEXT), - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("context-wait", |name| { - glib::ParamSpec::uint( - name, - "Context Wait", - "Throttle poll loop to run at most once every this many ms", - 0, - 1000, - DEFAULT_CONTEXT_WAIT, - glib::ParamFlags::READWRITE, - ) - }), -]; - #[derive(Debug)] struct PendingQueue { more_queue_space_sender: Option<oneshot::Sender<()>>, @@ -752,42 +696,12 @@ impl ObjectSubclass for Queue { const NAME: &'static str = "RsTsQueue"; type Type = super::Queue; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; glib::object_subclass!(); - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Thread-sharing queue", - "Generic", - "Simple data queue", - "Sebastian Dröge <sebastian@centricular.com>", - ); - - let caps = gst::Caps::new_any(); - - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - klass.install_properties(&PROPERTIES); - } - fn with_class(klass: &Self::Class) -> Self { Self { sink_pad: PadSink::new( @@ -808,43 +722,97 @@ impl ObjectSubclass for Queue { } impl ObjectImpl for Queue { - fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::string( + "context", + "Context", + "Context name to share threads with", + Some(DEFAULT_CONTEXT), + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "context-wait", + "Context Wait", + "Throttle poll loop to run at most once every this many ms", + 0, + 1000, + DEFAULT_CONTEXT_WAIT, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "max-size-buffers", + "Max Size Buffers", + "Maximum number of buffers to queue (0=unlimited)", + 0, + u32::MAX, + DEFAULT_MAX_SIZE_BUFFERS, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "max-size-bytes", + "Max Size Bytes", + "Maximum number of bytes to queue (0=unlimited)", + 0, + u32::MAX, + DEFAULT_MAX_SIZE_BYTES, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint64( + "max-size-time", + "Max Size Time", + "Maximum number of nanoseconds to queue (0=unlimited)", + 0, + u64::MAX - 1, + DEFAULT_MAX_SIZE_TIME, + glib::ParamFlags::READWRITE, + ), + ] + }); + + PROPERTIES.as_ref() + } + fn set_property( + &self, + _obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { let mut settings = self.settings.lock().unwrap(); - match *prop { - subclass::Property("max-size-buffers", ..) => { + match pspec.get_name() { + "max-size-buffers" => { settings.max_size_buffers = value.get_some().expect("type checked upstream"); } - subclass::Property("max-size-bytes", ..) => { + "max-size-bytes" => { settings.max_size_bytes = value.get_some().expect("type checked upstream"); } - subclass::Property("max-size-time", ..) => { + "max-size-time" => { settings.max_size_time = value.get_some().expect("type checked upstream"); } - subclass::Property("context", ..) => { + "context" => { settings.context = value .get() .expect("type checked upstream") .unwrap_or_else(|| "".into()); } - subclass::Property("context-wait", ..) => { + "context-wait" => { settings.context_wait = value.get_some().expect("type checked upstream"); } _ => unimplemented!(), } } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { let settings = self.settings.lock().unwrap(); - match *prop { - subclass::Property("max-size-buffers", ..) => settings.max_size_buffers.to_value(), - subclass::Property("max-size-bytes", ..) => settings.max_size_bytes.to_value(), - subclass::Property("max-size-time", ..) => settings.max_size_time.to_value(), - subclass::Property("context", ..) => settings.context.to_value(), - subclass::Property("context-wait", ..) => settings.context_wait.to_value(), + match pspec.get_name() { + "max-size-buffers" => settings.max_size_buffers.to_value(), + "max-size-bytes" => settings.max_size_bytes.to_value(), + "max-size-time" => settings.max_size_time.to_value(), + "context" => settings.context.to_value(), + "context-wait" => settings.context_wait.to_value(), _ => unimplemented!(), } } @@ -858,6 +826,45 @@ impl ObjectImpl for Queue { } impl ElementImpl for Queue { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Thread-sharing queue", + "Generic", + "Simple data queue", + "Sebastian Dröge <sebastian@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::new_any(); + + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![sink_pad_template, src_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + fn change_state( &self, element: &Self::Type, diff --git a/generic/threadshare/src/tcpclientsrc/imp.rs b/generic/threadshare/src/tcpclientsrc/imp.rs index afd23423b..4b584b07e 100644 --- a/generic/threadshare/src/tcpclientsrc/imp.rs +++ b/generic/threadshare/src/tcpclientsrc/imp.rs @@ -75,69 +75,6 @@ impl Default for Settings { } } -static PROPERTIES: [subclass::Property; 6] = [ - subclass::Property("host", |name| { - glib::ParamSpec::string( - name, - "Host", - "The host IP address to receive packets from", - DEFAULT_HOST, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("port", |name| { - glib::ParamSpec::int( - name, - "Port", - "Port to receive packets from", - 0, - u16::MAX as i32, - DEFAULT_PORT, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("caps", |name| { - glib::ParamSpec::boxed( - name, - "Caps", - "Caps to use", - gst::Caps::static_type(), - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("blocksize", |name| { - glib::ParamSpec::uint( - name, - "Blocksize", - "Size in bytes to read per buffer (-1 = default)", - 0, - u32::MAX, - DEFAULT_BLOCKSIZE, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("context", |name| { - glib::ParamSpec::string( - name, - "Context", - "Context name to share threads with", - Some(DEFAULT_CONTEXT), - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("context-wait", |name| { - glib::ParamSpec::uint( - name, - "Context Wait", - "Throttle poll loop to run at most once every this many ms", - 0, - 1000, - DEFAULT_CONTEXT_WAIT, - glib::ParamFlags::READWRITE, - ) - }), -]; - struct TcpClientReader(tokio::net::TcpStream); impl TcpClientReader { @@ -612,32 +549,12 @@ impl ObjectSubclass for TcpClientSrc { const NAME: &'static str = "RsTsTcpClientSrc"; type Type = super::TcpClientSrc; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; glib::object_subclass!(); - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Thread-sharing TCP client source", - "Source/Network", - "Receives data over the network via TCP", - "Sebastian Dröge <sebastian@centricular.com>, LEE Dongjun <redongjun@gmail.com>", - ); - - let caps = gst::Caps::new_any(); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - klass.install_properties(&PROPERTIES); - } - fn with_class(klass: &Self::Class) -> Self { let src_pad_handler = TcpClientSrcPadHandler::default(); @@ -654,47 +571,106 @@ impl ObjectSubclass for TcpClientSrc { } impl ObjectImpl for TcpClientSrc { - fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::string( + "context", + "Context", + "Context name to share threads with", + Some(DEFAULT_CONTEXT), + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "context-wait", + "Context Wait", + "Throttle poll loop to run at most once every this many ms", + 0, + 1000, + DEFAULT_CONTEXT_WAIT, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::string( + "host", + "Host", + "The host IP address to receive packets from", + DEFAULT_HOST, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::int( + "port", + "Port", + "Port to receive packets from", + 0, + u16::MAX as i32, + DEFAULT_PORT, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boxed( + "caps", + "Caps", + "Caps to use", + gst::Caps::static_type(), + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "blocksize", + "Blocksize", + "Size in bytes to read per buffer (-1 = default)", + 0, + u32::MAX, + DEFAULT_BLOCKSIZE, + glib::ParamFlags::READWRITE, + ), + ] + }); + + PROPERTIES.as_ref() + } + fn set_property( + &self, + _obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { let mut settings = self.settings.lock().unwrap(); - match *prop { - subclass::Property("host", ..) => { + match pspec.get_name() { + "host" => { settings.host = value.get().expect("type checked upstream"); } - subclass::Property("port", ..) => { + "port" => { settings.port = value.get_some().expect("type checked upstream"); } - subclass::Property("caps", ..) => { + "caps" => { settings.caps = value.get().expect("type checked upstream"); } - subclass::Property("blocksize", ..) => { + "blocksize" => { settings.blocksize = value.get_some().expect("type checked upstream"); } - subclass::Property("context", ..) => { + "context" => { settings.context = value .get() .expect("type checked upstream") .unwrap_or_else(|| "".into()); } - subclass::Property("context-wait", ..) => { + "context-wait" => { settings.context_wait = value.get_some().expect("type checked upstream"); } _ => unimplemented!(), } } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { let settings = self.settings.lock().unwrap(); - match *prop { - subclass::Property("host", ..) => settings.host.to_value(), - subclass::Property("port", ..) => settings.port.to_value(), - subclass::Property("caps", ..) => settings.caps.to_value(), - subclass::Property("blocksize", ..) => settings.blocksize.to_value(), - subclass::Property("context", ..) => settings.context.to_value(), - subclass::Property("context-wait", ..) => settings.context_wait.to_value(), + match pspec.get_name() { + "host" => settings.host.to_value(), + "port" => settings.port.to_value(), + "caps" => settings.caps.to_value(), + "blocksize" => settings.blocksize.to_value(), + "context" => settings.context.to_value(), + "context-wait" => settings.context_wait.to_value(), _ => unimplemented!(), } } @@ -709,6 +685,36 @@ impl ObjectImpl for TcpClientSrc { } impl ElementImpl for TcpClientSrc { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Thread-sharing TCP client source", + "Source/Network", + "Receives data over the network via TCP", + "Sebastian Dröge <sebastian@centricular.com>, LEE Dongjun <redongjun@gmail.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::new_any(); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + fn change_state( &self, element: &Self::Type, diff --git a/generic/threadshare/src/udpsink/imp.rs b/generic/threadshare/src/udpsink/imp.rs index bd991bbc1..8b44588c1 100644 --- a/generic/threadshare/src/udpsink/imp.rs +++ b/generic/threadshare/src/udpsink/imp.rs @@ -118,174 +118,6 @@ static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| { ) }); -static PROPERTIES: [subclass::Property; 17] = [ - subclass::Property("sync", |name| { - glib::ParamSpec::boolean( - name, - "Sync", - "Sync on the clock", - DEFAULT_SYNC, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("bind-address", |name| { - glib::ParamSpec::string( - name, - "Bind Address", - "Address to bind the socket to", - Some(DEFAULT_BIND_ADDRESS), - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("bind-port", |name| { - glib::ParamSpec::int( - name, - "Bind Port", - "Port to bind the socket to", - 0, - u16::MAX as i32, - DEFAULT_BIND_PORT, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("bind-address-v6", |name| { - glib::ParamSpec::string( - name, - "Bind Address V6", - "Address to bind the V6 socket to", - Some(DEFAULT_BIND_ADDRESS_V6), - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("bind-port-v6", |name| { - glib::ParamSpec::int( - name, - "Bind Port", - "Port to bind the V6 socket to", - 0, - u16::MAX as i32, - DEFAULT_BIND_PORT_V6, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("socket", |name| { - glib::ParamSpec::object( - name, - "Socket", - "Socket to use for UDP transmission. (None == allocate)", - gio::Socket::static_type(), - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("used-socket", |name| { - glib::ParamSpec::object( - name, - "Used Socket", - "Socket currently in use for UDP transmission. (None = no socket)", - gio::Socket::static_type(), - glib::ParamFlags::READABLE, - ) - }), - subclass::Property("socket-v6", |name| { - glib::ParamSpec::object( - name, - "Socket V6", - "IPV6 Socket to use for UDP transmission. (None == allocate)", - gio::Socket::static_type(), - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("used-socket-v6", |name| { - glib::ParamSpec::object( - name, - "Used Socket V6", - "V6 Socket currently in use for UDP transmission. (None = no socket)", - gio::Socket::static_type(), - glib::ParamFlags::READABLE, - ) - }), - subclass::Property("auto-multicast", |name| { - glib::ParamSpec::boolean( - name, - "Auto multicast", - "Automatically join/leave the multicast groups, FALSE means user has to do it himself", - DEFAULT_AUTO_MULTICAST, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("loop", |name| { - glib::ParamSpec::boolean( - name, - "Loop", - "Set the multicast loop parameter.", - DEFAULT_LOOP, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("ttl", |name| { - glib::ParamSpec::uint( - name, - "Time To Live", - "Used for setting the unicast TTL parameter", - 0, - u8::MAX as u32, - DEFAULT_TTL, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("ttl-mc", |name| { - glib::ParamSpec::uint( - name, - "Time To Live Multicast", - "Used for setting the multicast TTL parameter", - 0, - u8::MAX as u32, - DEFAULT_TTL_MC, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("qos-dscp", |name| { - glib::ParamSpec::int( - name, - "QoS DSCP", - "Quality of Service, differentiated services code point (-1 default)", - -1, - 63, - DEFAULT_QOS_DSCP, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("clients", |name| { - glib::ParamSpec::string( - name, - "Clients", - "A comma separated list of host:port pairs with destinations", - Some(DEFAULT_CLIENTS), - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("context", |name| { - glib::ParamSpec::string( - name, - "Context", - "Context name to share threads with", - Some(DEFAULT_CONTEXT), - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("context-wait", |name| { - glib::ParamSpec::uint( - name, - "Context Wait", - "Throttle poll loop to run at most once every this many ms", - 0, - 1000, - DEFAULT_CONTEXT_WAIT, - glib::ParamFlags::READWRITE, - ) - }), -]; - #[derive(Debug)] enum TaskItem { Buffer(gst::Buffer), @@ -1127,107 +959,12 @@ impl ObjectSubclass for UdpSink { const NAME: &'static str = "RsTsUdpSink"; type Type = super::UdpSink; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; glib::object_subclass!(); - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Thread-sharing UDP sink", - "Sink/Network", - "Thread-sharing UDP sink", - "Mathieu <mathieu@centricular.com>", - ); - - let caps = gst::Caps::new_any(); - - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - klass.add_signal_with_class_handler( - "add", - glib::SignalFlags::RUN_LAST | glib::SignalFlags::ACTION, - &[String::static_type(), i32::static_type()], - glib::types::Type::Unit, - |_, args| { - let element = args[0] - .get::<super::UdpSink>() - .expect("signal arg") - .expect("missing signal arg"); - let host = args[1] - .get::<String>() - .expect("signal arg") - .expect("missing signal arg"); - let port = args[2] - .get::<i32>() - .expect("signal arg") - .expect("missing signal arg"); - - if let Ok(addr) = try_into_socket_addr(&element, &host, port) { - let udpsink = Self::from_instance(&element); - udpsink.add_client(addr); - } - - None - }, - ); - - klass.add_signal_with_class_handler( - "remove", - glib::SignalFlags::RUN_LAST | glib::SignalFlags::ACTION, - &[String::static_type(), i32::static_type()], - glib::types::Type::Unit, - |_, args| { - let element = args[0] - .get::<super::UdpSink>() - .expect("signal arg") - .expect("missing signal arg"); - let host = args[1] - .get::<String>() - .expect("signal arg") - .expect("missing signal arg"); - let port = args[2] - .get::<i32>() - .expect("signal arg") - .expect("missing signal arg"); - - let udpsink = Self::from_instance(&element); - - if let Ok(addr) = try_into_socket_addr(&element, &host, port) { - udpsink.remove_client(addr); - } - - None - }, - ); - - klass.add_signal_with_class_handler( - "clear", - glib::SignalFlags::RUN_LAST | glib::SignalFlags::ACTION, - &[], - glib::types::Type::Unit, - |_, args| { - let element = args[0] - .get::<super::UdpSink>() - .expect("signal arg") - .expect("missing signal arg"); - - let udpsink = Self::from_instance(&element); - udpsink.clear_clients(std::iter::empty()); - - None - }, - ); - - klass.install_properties(&PROPERTIES); - } - fn with_class(klass: &Self::Class) -> Self { let settings = Arc::new(StdMutex::new(Settings::default())); let sink_pad_handler = UdpSinkPadHandler::new(Arc::clone(&settings)); @@ -1245,66 +982,289 @@ impl ObjectSubclass for UdpSink { } impl ObjectImpl for UdpSink { - fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::string( + "context", + "Context", + "Context name to share threads with", + Some(DEFAULT_CONTEXT), + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "context-wait", + "Context Wait", + "Throttle poll loop to run at most once every this many ms", + 0, + 1000, + DEFAULT_CONTEXT_WAIT, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boolean( + "sync", + "Sync", + "Sync on the clock", + DEFAULT_SYNC, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::string( + "bind-address", + "Bind Address", + "Address to bind the socket to", + Some(DEFAULT_BIND_ADDRESS), + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::int( + "bind-port", + "Bind Port", + "Port to bind the socket to", + 0, + u16::MAX as i32, + DEFAULT_BIND_PORT, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::string( + "bind-address-v6", + "Bind Address V6", + "Address to bind the V6 socket to", + Some(DEFAULT_BIND_ADDRESS_V6), + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::int( + "bind-port-v6", + "Bind Port", + "Port to bind the V6 socket to", + 0, + u16::MAX as i32, + DEFAULT_BIND_PORT_V6, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::object( + "socket", + "Socket", + "Socket to use for UDP transmission. (None == allocate)", + gio::Socket::static_type(), + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::object( + "used-socket", + "Used Socket", + "Socket currently in use for UDP transmission. (None = no socket)", + gio::Socket::static_type(), + glib::ParamFlags::READABLE, + ), + glib::ParamSpec::object( + "socket-v6", + "Socket V6", + "IPV6 Socket to use for UDP transmission. (None == allocate)", + gio::Socket::static_type(), + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::object( + "used-socket-v6", + "Used Socket V6", + "V6 Socket currently in use for UDP transmission. (None = no socket)", + gio::Socket::static_type(), + glib::ParamFlags::READABLE, + ), + glib::ParamSpec::boolean( + "auto-multicast", + "Auto multicast", + "Automatically join/leave the multicast groups, FALSE means user has to do it himself", + DEFAULT_AUTO_MULTICAST, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boolean( + "loop", + "Loop", + "Set the multicast loop parameter.", + DEFAULT_LOOP, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "ttl", + "Time To Live", + "Used for setting the unicast TTL parameter", + 0, + u8::MAX as u32, + DEFAULT_TTL, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "ttl-mc", + "Time To Live Multicast", + "Used for setting the multicast TTL parameter", + 0, + u8::MAX as u32, + DEFAULT_TTL_MC, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::int( + "qos-dscp", + "QoS DSCP", + "Quality of Service, differentiated services code point (-1 default)", + -1, + 63, + DEFAULT_QOS_DSCP, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::string( + "clients", + "Clients", + "A comma separated list of host:port pairs with destinations", + Some(DEFAULT_CLIENTS), + glib::ParamFlags::READWRITE, + ), + ] + }); + + PROPERTIES.as_ref() + } + + fn signals() -> &'static [glib::subclass::Signal] { + static SIGNALS: Lazy<Vec<glib::subclass::Signal>> = Lazy::new(|| { + vec![ + glib::subclass::Signal::builder( + "add", + &[String::static_type(), i32::static_type()], + glib::types::Type::Unit, + ) + .action() + .class_handler(|_, args| { + let element = args[0] + .get::<super::UdpSink>() + .expect("signal arg") + .expect("missing signal arg"); + let host = args[1] + .get::<String>() + .expect("signal arg") + .expect("missing signal arg"); + let port = args[2] + .get::<i32>() + .expect("signal arg") + .expect("missing signal arg"); + + if let Ok(addr) = try_into_socket_addr(&element, &host, port) { + let udpsink = UdpSink::from_instance(&element); + udpsink.add_client(addr); + } + + None + }) + .build(), + glib::subclass::Signal::builder( + "remove", + &[String::static_type(), i32::static_type()], + glib::types::Type::Unit, + ) + .action() + .class_handler(|_, args| { + let element = args[0] + .get::<super::UdpSink>() + .expect("signal arg") + .expect("missing signal arg"); + let host = args[1] + .get::<String>() + .expect("signal arg") + .expect("missing signal arg"); + let port = args[2] + .get::<i32>() + .expect("signal arg") + .expect("missing signal arg"); + + if let Ok(addr) = try_into_socket_addr(&element, &host, port) { + let udpsink = UdpSink::from_instance(&element); + udpsink.remove_client(addr); + } + + None + }) + .build(), + glib::subclass::Signal::builder("clear", &[], glib::types::Type::Unit) + .action() + .class_handler(|_, args| { + let element = args[0] + .get::<super::UdpSink>() + .expect("signal arg") + .expect("missing signal arg"); + + let udpsink = UdpSink::from_instance(&element); + udpsink.clear_clients(std::iter::empty()); + + None + }) + .build(), + ] + }); + SIGNALS.as_ref() + } + + fn set_property( + &self, + obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { let mut settings = self.settings.lock().unwrap(); - match *prop { - subclass::Property("sync", ..) => { + match pspec.get_name() { + "sync" => { settings.sync = value.get_some().expect("type checked upstream"); } - subclass::Property("bind-address", ..) => { + "bind-address" => { settings.bind_address = value .get() .expect("type checked upstream") .unwrap_or_else(|| "".into()); } - subclass::Property("bind-port", ..) => { + "bind-port" => { settings.bind_port = value.get_some().expect("type checked upstream"); } - subclass::Property("bind-address-v6", ..) => { + "bind-address-v6" => { settings.bind_address_v6 = value .get() .expect("type checked upstream") .unwrap_or_else(|| "".into()); } - subclass::Property("bind-port-v6", ..) => { + "bind-port-v6" => { settings.bind_port_v6 = value.get_some().expect("type checked upstream"); } - subclass::Property("socket", ..) => { + "socket" => { settings.socket = value .get::<gio::Socket>() .expect("type checked upstream") .map(|socket| GioSocketWrapper::new(&socket)); } - subclass::Property("used-socket", ..) => { + "used-socket" => { unreachable!(); } - subclass::Property("socket-v6", ..) => { + "socket-v6" => { settings.socket_v6 = value .get::<gio::Socket>() .expect("type checked upstream") .map(|socket| GioSocketWrapper::new(&socket)); } - subclass::Property("used-socket-v6", ..) => { + "used-socket-v6" => { unreachable!(); } - subclass::Property("auto-multicast", ..) => { + "auto-multicast" => { settings.auto_multicast = value.get_some().expect("type checked upstream"); } - subclass::Property("loop", ..) => { + "loop" => { settings.multicast_loop = value.get_some().expect("type checked upstream"); } - subclass::Property("ttl", ..) => { + "ttl" => { settings.ttl = value.get_some().expect("type checked upstream"); } - subclass::Property("ttl-mc", ..) => { + "ttl-mc" => { settings.ttl_mc = value.get_some().expect("type checked upstream"); } - subclass::Property("qos-dscp", ..) => { + "qos-dscp" => { settings.qos_dscp = value.get_some().expect("type checked upstream"); } - subclass::Property("clients", ..) => { + "clients" => { let clients: String = value .get() .expect("type checked upstream") @@ -1329,55 +1289,53 @@ impl ObjectImpl for UdpSink { self.clear_clients(clients_iter); } - subclass::Property("context", ..) => { + "context" => { settings.context = value .get() .expect("type checked upstream") .unwrap_or_else(|| "".into()); } - subclass::Property("context-wait", ..) => { + "context-wait" => { settings.context_wait = value.get_some().expect("type checked upstream"); } _ => unimplemented!(), } } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { let settings = self.settings.lock().unwrap(); - match *prop { - subclass::Property("sync", ..) => settings.sync.to_value(), - subclass::Property("bind-address", ..) => settings.bind_address.to_value(), - subclass::Property("bind-port", ..) => settings.bind_port.to_value(), - subclass::Property("bind-address-v6", ..) => settings.bind_address_v6.to_value(), - subclass::Property("bind-port-v6", ..) => settings.bind_port_v6.to_value(), - subclass::Property("socket", ..) => settings + match pspec.get_name() { + "sync" => settings.sync.to_value(), + "bind-address" => settings.bind_address.to_value(), + "bind-port" => settings.bind_port.to_value(), + "bind-address-v6" => settings.bind_address_v6.to_value(), + "bind-port-v6" => settings.bind_port_v6.to_value(), + "socket" => settings .socket .as_ref() .map(GioSocketWrapper::as_socket) .to_value(), - subclass::Property("used-socket", ..) => settings + "used-socket" => settings .used_socket .as_ref() .map(GioSocketWrapper::as_socket) .to_value(), - subclass::Property("socket-v6", ..) => settings + "socket-v6" => settings .socket_v6 .as_ref() .map(GioSocketWrapper::as_socket) .to_value(), - subclass::Property("used-socket-v6", ..) => settings + "used-socket-v6" => settings .used_socket_v6 .as_ref() .map(GioSocketWrapper::as_socket) .to_value(), - subclass::Property("auto-multicast", ..) => settings.sync.to_value(), - subclass::Property("loop", ..) => settings.multicast_loop.to_value(), - subclass::Property("ttl", ..) => settings.ttl.to_value(), - subclass::Property("ttl-mc", ..) => settings.ttl_mc.to_value(), - subclass::Property("qos-dscp", ..) => settings.qos_dscp.to_value(), - subclass::Property("clients", ..) => { + "auto-multicast" => settings.sync.to_value(), + "loop" => settings.multicast_loop.to_value(), + "ttl" => settings.ttl.to_value(), + "ttl-mc" => settings.ttl_mc.to_value(), + "qos-dscp" => settings.qos_dscp.to_value(), + "clients" => { drop(settings); let clients: Vec<String> = self @@ -1389,8 +1347,8 @@ impl ObjectImpl for UdpSink { clients.join(",").to_value() } - subclass::Property("context", ..) => settings.context.to_value(), - subclass::Property("context-wait", ..) => settings.context_wait.to_value(), + "context" => settings.context.to_value(), + "context-wait" => settings.context_wait.to_value(), _ => unimplemented!(), } } @@ -1405,6 +1363,37 @@ impl ObjectImpl for UdpSink { } impl ElementImpl for UdpSink { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Thread-sharing UDP sink", + "Sink/Network", + "Thread-sharing UDP sink", + "Mathieu <mathieu@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::new_any(); + + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + fn change_state( &self, element: &Self::Type, diff --git a/generic/threadshare/src/udpsrc/imp.rs b/generic/threadshare/src/udpsrc/imp.rs index 1d42561c9..b8d92e143 100644 --- a/generic/threadshare/src/udpsrc/imp.rs +++ b/generic/threadshare/src/udpsrc/imp.rs @@ -84,105 +84,6 @@ impl Default for Settings { } } -static PROPERTIES: [subclass::Property; 10] = [ - subclass::Property("address", |name| { - glib::ParamSpec::string( - name, - "Address", - "Address/multicast group to listen on", - DEFAULT_ADDRESS, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("port", |name| { - glib::ParamSpec::int( - name, - "Port", - "Port to listen on", - 0, - u16::MAX as i32, - DEFAULT_PORT, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("reuse", |name| { - glib::ParamSpec::boolean( - name, - "Reuse", - "Allow reuse of the port", - DEFAULT_REUSE, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("caps", |name| { - glib::ParamSpec::boxed( - name, - "Caps", - "Caps to use", - gst::Caps::static_type(), - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("mtu", |name| { - glib::ParamSpec::uint( - name, - "MTU", - "Maximum expected packet size. This directly defines the allocation size of the receive buffer pool", - 0, - i32::MAX as u32, - DEFAULT_MTU, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("socket", |name| { - glib::ParamSpec::object( - name, - "Socket", - "Socket to use for UDP reception. (None == allocate)", - gio::Socket::static_type(), - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("used-socket", |name| { - glib::ParamSpec::object( - name, - "Used Socket", - "Socket currently in use for UDP reception. (None = no socket)", - gio::Socket::static_type(), - glib::ParamFlags::READABLE, - ) - }), - subclass::Property("context", |name| { - glib::ParamSpec::string( - name, - "Context", - "Context name to share threads with", - Some(DEFAULT_CONTEXT), - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("context-wait", |name| { - glib::ParamSpec::uint( - name, - "Context Wait", - "Throttle poll loop to run at most once every this many ms", - 0, - 1000, - DEFAULT_CONTEXT_WAIT, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("retrieve-sender-address", |name| { - glib::ParamSpec::boolean( - name, - "Retrieve sender address", - "Whether to retrieve the sender address and add it to buffers as meta. Disabling this might result in minor performance improvements in certain scenarios", - DEFAULT_RETRIEVE_SENDER_ADDRESS, - glib::ParamFlags::READWRITE, - ) - }), -]; - #[derive(Debug)] struct UdpReader(tokio::net::UdpSocket); @@ -789,48 +690,12 @@ impl ObjectSubclass for UdpSrc { const NAME: &'static str = "RsTsUdpSrc"; type Type = super::UdpSrc; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; glib::object_subclass!(); - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Thread-sharing UDP source", - "Source/Network", - "Receives data over the network via UDP", - "Sebastian Dröge <sebastian@centricular.com>", - ); - - let caps = gst::Caps::new_any(); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - #[cfg(not(windows))] - { - klass.install_properties(&PROPERTIES); - } - #[cfg(windows)] - { - let properties = PROPERTIES - .iter() - .filter(|p| match *p { - subclass::Property("socket", ..) | subclass::Property("used-socket", ..) => { - false - } - _ => true, - }) - .collect::<Vec<_>>(); - klass.install_properties(properties.as_slice()); - } - } - fn with_class(klass: &Self::Class) -> Self { let src_pad_handler = UdpSrcPadHandler::default(); @@ -847,76 +712,167 @@ impl ObjectSubclass for UdpSrc { } impl ObjectImpl for UdpSrc { - fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + let mut properties = vec![ + glib::ParamSpec::string( + "context", + "Context", + "Context name to share threads with", + Some(DEFAULT_CONTEXT), + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "context-wait", + "Context Wait", + "Throttle poll loop to run at most once every this many ms", + 0, + 1000, + DEFAULT_CONTEXT_WAIT, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::string( + "address", + "Address", + "Address/multicast group to listen on", + DEFAULT_ADDRESS, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::int( + "port", + "Port", + "Port to listen on", + 0, + u16::MAX as i32, + DEFAULT_PORT, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boolean( + "reuse", + "Reuse", + "Allow reuse of the port", + DEFAULT_REUSE, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boxed( + "caps", + "Caps", + "Caps to use", + gst::Caps::static_type(), + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "mtu", + "MTU", + "Maximum expected packet size. This directly defines the allocation size of the receive buffer pool", + 0, + i32::MAX as u32, + DEFAULT_MTU, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boolean( + "retrieve-sender-address", + "Retrieve sender address", + "Whether to retrieve the sender address and add it to buffers as meta. Disabling this might result in minor performance improvements in certain scenarios", + DEFAULT_RETRIEVE_SENDER_ADDRESS, + glib::ParamFlags::READWRITE, + ), + ]; + + #[cfg(not(windows))] + { + properties.push(glib::ParamSpec::object( + "socket", + "Socket", + "Socket to use for UDP reception. (None == allocate)", + gio::Socket::static_type(), + glib::ParamFlags::READWRITE, + )); + properties.push(glib::ParamSpec::object( + "used-socket", + "Used Socket", + "Socket currently in use for UDP reception. (None = no socket)", + gio::Socket::static_type(), + glib::ParamFlags::READABLE, + )); + } + properties + }); + + PROPERTIES.as_ref() + } + + fn set_property( + &self, + _obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { let mut settings = self.settings.lock().unwrap(); - match *prop { - subclass::Property("address", ..) => { + match pspec.get_name() { + "address" => { settings.address = value.get().expect("type checked upstream"); } - subclass::Property("port", ..) => { + "port" => { settings.port = value.get_some().expect("type checked upstream"); } - subclass::Property("reuse", ..) => { + "reuse" => { settings.reuse = value.get_some().expect("type checked upstream"); } - subclass::Property("caps", ..) => { + "caps" => { settings.caps = value.get().expect("type checked upstream"); } - subclass::Property("mtu", ..) => { + "mtu" => { settings.mtu = value.get_some().expect("type checked upstream"); } - subclass::Property("socket", ..) => { + "socket" => { settings.socket = value .get::<gio::Socket>() .expect("type checked upstream") .map(|socket| GioSocketWrapper::new(&socket)); } - subclass::Property("used-socket", ..) => { + "used-socket" => { unreachable!(); } - subclass::Property("context", ..) => { + "context" => { settings.context = value .get() .expect("type checked upstream") .unwrap_or_else(|| "".into()); } - subclass::Property("context-wait", ..) => { + "context-wait" => { settings.context_wait = value.get_some().expect("type checked upstream"); } - subclass::Property("retrieve-sender-address", ..) => { + "retrieve-sender-address" => { settings.retrieve_sender_address = value.get_some().expect("type checked upstream"); } _ => unimplemented!(), } } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { let settings = self.settings.lock().unwrap(); - match *prop { - subclass::Property("address", ..) => settings.address.to_value(), - subclass::Property("port", ..) => settings.port.to_value(), - subclass::Property("reuse", ..) => settings.reuse.to_value(), - subclass::Property("caps", ..) => settings.caps.to_value(), - subclass::Property("mtu", ..) => settings.mtu.to_value(), - subclass::Property("socket", ..) => settings + match pspec.get_name() { + "address" => settings.address.to_value(), + "port" => settings.port.to_value(), + "reuse" => settings.reuse.to_value(), + "caps" => settings.caps.to_value(), + "mtu" => settings.mtu.to_value(), + "socket" => settings .socket .as_ref() .map(GioSocketWrapper::as_socket) .to_value(), - subclass::Property("used-socket", ..) => settings + "used-socket" => settings .used_socket .as_ref() .map(GioSocketWrapper::as_socket) .to_value(), - subclass::Property("context", ..) => settings.context.to_value(), - subclass::Property("context-wait", ..) => settings.context_wait.to_value(), - subclass::Property("retrieve-sender-address", ..) => { - settings.retrieve_sender_address.to_value() - } + "context" => settings.context.to_value(), + "context-wait" => settings.context_wait.to_value(), + "retrieve-sender-address" => settings.retrieve_sender_address.to_value(), _ => unimplemented!(), } } @@ -931,6 +887,36 @@ impl ObjectImpl for UdpSrc { } impl ElementImpl for UdpSrc { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Thread-sharing UDP source", + "Source/Network", + "Receives data over the network via UDP", + "Sebastian Dröge <sebastian@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::new_any(); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + fn change_state( &self, element: &Self::Type, diff --git a/generic/threadshare/tests/pad.rs b/generic/threadshare/tests/pad.rs index ee8bc2457..e7d8bb960 100644 --- a/generic/threadshare/tests/pad.rs +++ b/generic/threadshare/tests/pad.rs @@ -69,17 +69,6 @@ struct ItemSender { mod imp_src { use super::*; - static SRC_PROPERTIES: [glib::subclass::Property; 1] = - [glib::subclass::Property("context", |name| { - glib::ParamSpec::string( - name, - "Context", - "Context name to share threads with", - Some(DEFAULT_CONTEXT), - glib::ParamFlags::READWRITE, - ) - })]; - #[derive(Clone, Debug, Default)] struct Settings { context: String, @@ -316,32 +305,12 @@ mod imp_src { const NAME: &'static str = "TsElementSrcTest"; type Type = super::ElementSrcTest; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = glib::subclass::simple::ClassStruct<Self>; glib::object_subclass!(); - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Thread-sharing Test Src Element", - "Generic", - "Src Element for Pad Src Test", - "François Laignel <fengalin@free.fr>", - ); - - let caps = gst::Caps::new_any(); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - klass.install_properties(&SRC_PROPERTIES); - } - fn with_class(klass: &Self::Class) -> Self { ElementSrcTest { src_pad: PadSrc::new( @@ -356,11 +325,29 @@ mod imp_src { } impl ObjectImpl for ElementSrcTest { - fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &SRC_PROPERTIES[id]; + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![glib::ParamSpec::string( + "context", + "Context", + "Context name to share threads with", + Some(DEFAULT_CONTEXT), + glib::ParamFlags::WRITABLE, + )] + }); + + PROPERTIES.as_ref() + } - match *prop { - glib::subclass::Property("context", ..) => { + fn set_property( + &self, + _obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "context" => { let context = value .get() .expect("type checked upstream") @@ -380,6 +367,36 @@ mod imp_src { } impl ElementImpl for ElementSrcTest { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Thread-sharing Test Src Element", + "Generic", + "Src Element for Pad Src Test", + "François Laignel <fengalin@free.fr>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::new_any(); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + fn change_state( &self, element: &Self::Type, @@ -452,17 +469,6 @@ unsafe impl Sync for ElementSrcTest {} mod imp_sink { use super::*; - static SINK_PROPERTIES: [glib::subclass::Property; 1] = - [glib::subclass::Property("sender", |name| { - glib::ParamSpec::boxed( - name, - "Sender", - "Channel sender to forward the incoming items to", - ItemSender::get_type(), - glib::ParamFlags::WRITABLE, - ) - })]; - #[derive(Clone, Debug, Default)] struct PadSinkTestHandler; @@ -634,32 +640,12 @@ mod imp_sink { const NAME: &'static str = "TsElementSinkTest"; type Type = super::ElementSinkTest; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = glib::subclass::simple::ClassStruct<Self>; glib::object_subclass!(); - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Thread-sharing Test Sink Element", - "Generic", - "Sink Element for Pad Test", - "François Laignel <fengalin@free.fr>", - ); - - let caps = gst::Caps::new_any(); - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - klass.install_properties(&SINK_PROPERTIES); - } - fn with_class(klass: &Self::Class) -> Self { ElementSinkTest { sink_pad: PadSink::new( @@ -673,11 +659,29 @@ mod imp_sink { } impl ObjectImpl for ElementSinkTest { - fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &SINK_PROPERTIES[id]; + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![glib::ParamSpec::boxed( + "sender", + "Sender", + "Channel sender to forward the incoming items to", + ItemSender::get_type(), + glib::ParamFlags::WRITABLE, + )] + }); + + PROPERTIES.as_ref() + } - match *prop { - glib::subclass::Property("sender", ..) => { + fn set_property( + &self, + _obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "sender" => { let ItemSender { sender } = value .get::<&ItemSender>() .expect("type checked upstream") @@ -697,6 +701,36 @@ mod imp_sink { } impl ElementImpl for ElementSinkTest { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Thread-sharing Test Sink Element", + "Generic", + "Sink Element for Pad Test", + "François Laignel <fengalin@free.fr>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::new_any(); + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + fn change_state( &self, element: &Self::Type, diff --git a/net/reqwest/src/reqwesthttpsrc/imp.rs b/net/reqwest/src/reqwesthttpsrc/imp.rs index 8c0f223d0..429d86c33 100644 --- a/net/reqwest/src/reqwesthttpsrc/imp.rs +++ b/net/reqwest/src/reqwesthttpsrc/imp.rs @@ -69,110 +69,6 @@ impl Default for Settings { } } -static PROPERTIES: [subclass::Property; 11] = [ - subclass::Property("location", |name| { - glib::ParamSpec::string( - name, - "Location", - "URL to read from", - None, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("user-agent", |name| { - glib::ParamSpec::string( - name, - "User-Agent", - "Value of the User-Agent HTTP request header field", - DEFAULT_USER_AGENT.into(), - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("is-live", |name| { - glib::ParamSpec::boolean( - name, - "Is Live", - "Act like a live source", - DEFAULT_IS_LIVE, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("user-id", |name| { - glib::ParamSpec::string( - name, - "User-id", - "HTTP location URI user id for authentication", - None, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("user-pw", |name| { - glib::ParamSpec::string( - name, - "User-pw", - "HTTP location URI user password for authentication", - None, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("timeout", |name| { - glib::ParamSpec::uint( - name, - "Timeout", - "Value in seconds to timeout a blocking I/O (0 = No timeout).", - 0, - 3600, - DEFAULT_TIMEOUT, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("compress", |name| { - glib::ParamSpec::boolean( - name, - "Compress", - "Allow compressed content encodings", - DEFAULT_COMPRESS, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("extra-headers", |name| { - glib::ParamSpec::boxed( - name, - "Extra Headers", - "Extra headers to append to the HTTP request", - gst::Structure::static_type(), - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("cookies", |name| { - glib::ParamSpec::boxed( - name, - "Cookies", - "HTTP request cookies", - Vec::<String>::static_type(), - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("iradio-mode", |name| { - glib::ParamSpec::boolean( - name, - "I-Radio Mode", - "Enable internet radio mode (ask server to send shoutcast/icecast metadata interleaved with the actual stream data", - DEFAULT_IRADIO_MODE, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("keep-alive", |name| { - glib::ParamSpec::boolean( - name, - "Keep Alive", - "Use HTTP persistent connections", - DEFAULT_KEEP_ALIVE, - glib::ParamFlags::READWRITE, - ) - }), -]; - const REQWEST_CLIENT_CONTEXT: &str = "gst.reqwest.client"; #[derive(Clone, Debug, glib::GBoxed)] @@ -681,10 +577,103 @@ impl ReqwestHttpSrc { } impl ObjectImpl for ReqwestHttpSrc { - fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; - match *prop { - subclass::Property("location", ..) => { + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::string( + "location", + "Location", + "URL to read from", + None, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::string( + "user-agent", + "User-Agent", + "Value of the User-Agent HTTP request header field", + DEFAULT_USER_AGENT.into(), + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boolean( + "is-live", + "Is Live", + "Act like a live source", + DEFAULT_IS_LIVE, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::string( + "user-id", + "User-id", + "HTTP location URI user id for authentication", + None, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::string( + "user-pw", + "User-pw", + "HTTP location URI user password for authentication", + None, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "timeout", + "Timeout", + "Value in seconds to timeout a blocking I/O (0 = No timeout).", + 0, + 3600, + DEFAULT_TIMEOUT, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boolean( + "compress", + "Compress", + "Allow compressed content encodings", + DEFAULT_COMPRESS, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boxed( + "extra-headers", + "Extra Headers", + "Extra headers to append to the HTTP request", + gst::Structure::static_type(), + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boxed( + "cookies", + "Cookies", + "HTTP request cookies", + Vec::<String>::static_type(), + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boolean( + "iradio-mode", + "I-Radio Mode", + "Enable internet radio mode (ask server to send shoutcast/icecast metadata interleaved with the actual stream data", + DEFAULT_IRADIO_MODE, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boolean( + "keep-alive", + "Keep Alive", + "Use HTTP persistent connections", + DEFAULT_KEEP_ALIVE, + glib::ParamFlags::READWRITE, + ), + ] + }); + + PROPERTIES.as_ref() + } + + fn set_property( + &self, + obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "location" => { let location = value.get::<&str>().expect("type checked upstream"); if let Err(err) = self.set_location(obj, location) { gst_error!( @@ -695,7 +684,7 @@ impl ObjectImpl for ReqwestHttpSrc { ); } } - subclass::Property("user-agent", ..) => { + "user-agent" => { let mut settings = self.settings.lock().unwrap(); let user_agent = value .get() @@ -703,46 +692,46 @@ impl ObjectImpl for ReqwestHttpSrc { .unwrap_or_else(|| DEFAULT_USER_AGENT.into()); settings.user_agent = user_agent; } - subclass::Property("is-live", ..) => { + "is-live" => { let is_live = value.get_some().expect("type checked upstream"); obj.set_live(is_live); } - subclass::Property("user-id", ..) => { + "user-id" => { let mut settings = self.settings.lock().unwrap(); let user_id = value.get().expect("type checked upstream"); settings.user_id = user_id; } - subclass::Property("user-pw", ..) => { + "user-pw" => { let mut settings = self.settings.lock().unwrap(); let user_pw = value.get().expect("type checked upstream"); settings.user_pw = user_pw; } - subclass::Property("timeout", ..) => { + "timeout" => { let mut settings = self.settings.lock().unwrap(); let timeout = value.get_some().expect("type checked upstream"); settings.timeout = timeout; } - subclass::Property("compress", ..) => { + "compress" => { let mut settings = self.settings.lock().unwrap(); let compress = value.get_some().expect("type checked upstream"); settings.compress = compress; } - subclass::Property("extra-headers", ..) => { + "extra-headers" => { let mut settings = self.settings.lock().unwrap(); let extra_headers = value.get().expect("type checked upstream"); settings.extra_headers = extra_headers; } - subclass::Property("cookies", ..) => { + "cookies" => { let mut settings = self.settings.lock().unwrap(); let cookies = value.get().expect("type checked upstream"); settings.cookies = cookies.unwrap_or_else(Vec::new); } - subclass::Property("iradio-mode", ..) => { + "iradio-mode" => { let mut settings = self.settings.lock().unwrap(); let iradio_mode = value.get_some().expect("type checked upstream"); settings.iradio_mode = iradio_mode; } - subclass::Property("keep-alive", ..) => { + "keep-alive" => { let mut settings = self.settings.lock().unwrap(); let keep_alive = value.get_some().expect("type checked upstream"); settings.keep_alive = keep_alive; @@ -751,49 +740,48 @@ impl ObjectImpl for ReqwestHttpSrc { }; } - fn get_property(&self, obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - match *prop { - subclass::Property("location", ..) => { + fn get_property(&self, obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "location" => { let settings = self.settings.lock().unwrap(); let location = settings.location.as_ref().map(Url::to_string); location.to_value() } - subclass::Property("user-agent", ..) => { + "user-agent" => { let settings = self.settings.lock().unwrap(); settings.user_agent.to_value() } - subclass::Property("is-live", ..) => obj.is_live().to_value(), - subclass::Property("user-id", ..) => { + "is-live" => obj.is_live().to_value(), + "user-id" => { let settings = self.settings.lock().unwrap(); settings.user_id.to_value() } - subclass::Property("user-pw", ..) => { + "user-pw" => { let settings = self.settings.lock().unwrap(); settings.user_pw.to_value() } - subclass::Property("timeout", ..) => { + "timeout" => { let settings = self.settings.lock().unwrap(); settings.timeout.to_value() } - subclass::Property("compress", ..) => { + "compress" => { let settings = self.settings.lock().unwrap(); settings.compress.to_value() } - subclass::Property("extra-headers", ..) => { + "extra-headers" => { let settings = self.settings.lock().unwrap(); settings.extra_headers.to_value() } - subclass::Property("cookies", ..) => { + "cookies" => { let settings = self.settings.lock().unwrap(); settings.cookies.to_value() } - subclass::Property("iradio-mode", ..) => { + "iradio-mode" => { let settings = self.settings.lock().unwrap(); settings.iradio_mode.to_value() } - subclass::Property("keep-alive", ..) => { + "keep-alive" => { let settings = self.settings.lock().unwrap(); settings.keep_alive.to_value() } @@ -809,6 +797,36 @@ impl ObjectImpl for ReqwestHttpSrc { } impl ElementImpl for ReqwestHttpSrc { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "HTTP Source", + "Source/Network/HTTP", + "Read stream from an HTTP/HTTPS location", + "Sebastian Dröge <sebastian@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::new_any(); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + fn set_context(&self, element: &Self::Type, context: &gst::Context) { if context.get_context_type() == REQWEST_CLIENT_CONTEXT { let mut external_client = self.external_client.lock().unwrap(); @@ -1077,6 +1095,12 @@ impl PushSrcImpl for ReqwestHttpSrc { } impl URIHandlerImpl for ReqwestHttpSrc { + const URI_TYPE: gst::URIType = gst::URIType::Src; + + fn get_protocols() -> &'static [&'static str] { + &["http", "https"] + } + fn get_uri(&self, _element: &Self::Type) -> Option<String> { let settings = self.settings.lock().unwrap(); @@ -1086,20 +1110,13 @@ impl URIHandlerImpl for ReqwestHttpSrc { fn set_uri(&self, element: &Self::Type, uri: &str) -> Result<(), glib::Error> { self.set_location(&element, Some(uri)) } - - fn get_uri_type() -> gst::URIType { - gst::URIType::Src - } - - fn get_protocols() -> Vec<String> { - vec!["http".to_string(), "https".to_string()] - } } impl ObjectSubclass for ReqwestHttpSrc { const NAME: &'static str = "ReqwestHttpSrc"; type Type = super::ReqwestHttpSrc; type ParentType = gst_base::PushSrc; + type Interfaces = (gst::URIHandler,); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -1114,29 +1131,4 @@ impl ObjectSubclass for ReqwestHttpSrc { canceller: Mutex::new(None), } } - - fn type_init(type_: &mut subclass::InitializingType<Self>) { - type_.add_interface::<gst::URIHandler>(); - } - - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "HTTP Source", - "Source/Network/HTTP", - "Read stream from an HTTP/HTTPS location", - "Sebastian Dröge <sebastian@centricular.com>", - ); - - let caps = gst::Caps::new_any(); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - klass.install_properties(&PROPERTIES); - } } diff --git a/net/rusoto/src/aws_transcriber/imp.rs b/net/rusoto/src/aws_transcriber/imp.rs index e62120fed..3e5a9ae80 100644 --- a/net/rusoto/src/aws_transcriber/imp.rs +++ b/net/rusoto/src/aws_transcriber/imp.rs @@ -116,40 +116,6 @@ const DEFAULT_LATENCY_MS: u32 = 8000; const DEFAULT_USE_PARTIAL_RESULTS: bool = true; const GRANULARITY_MS: u32 = 100; -static PROPERTIES: [subclass::Property; 3] = [ - subclass::Property("language-code", |name| { - glib::ParamSpec::string( - name, - "Language Code", - "The Language of the Stream, see \ - <https://docs.aws.amazon.com/transcribe/latest/dg/how-streaming-transcription.html> \ - for an up to date list of allowed languages", - Some("en-US"), - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("use-partial-results", |name| { - glib::ParamSpec::boolean( - name, - "Latency", - "Whether partial results from AWS should be used", - DEFAULT_USE_PARTIAL_RESULTS, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("latency", |name| { - glib::ParamSpec::uint( - name, - "Latency", - "Amount of milliseconds to allow AWS transcribe", - 2 * GRANULARITY_MS, - std::u32::MAX, - DEFAULT_LATENCY_MS, - glib::ParamFlags::READWRITE, - ) - }), -]; - #[derive(Debug, Clone)] struct Settings { latency_ms: u32, @@ -1010,6 +976,7 @@ impl ObjectSubclass for Transcriber { const NAME: &'static str = "RsAwsTranscriber"; type Type = super::Transcriber; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -1063,45 +1030,43 @@ impl ObjectSubclass for Transcriber { ws_sink: AtomicRefCell::new(None), } } +} - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Transcriber", - "Audio/Text/Filter", - "Speech to Text filter, using AWS transcribe", - "Jordan Petridis <jordan@centricular.com>, Mathieu Duponchelle <mathieu@centricular.com>", - ); +impl ObjectImpl for Transcriber { + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::string( + "language-code", + "Language Code", + "The Language of the Stream, see \ + <https://docs.aws.amazon.com/transcribe/latest/dg/how-streaming-transcription.html> \ + for an up to date list of allowed languages", + Some("en-US"), + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boolean( + "use-partial-results", + "Latency", + "Whether partial results from AWS should be used", + DEFAULT_USE_PARTIAL_RESULTS, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "latency", + "Latency", + "Amount of milliseconds to allow AWS transcribe", + 2 * GRANULARITY_MS, + std::u32::MAX, + DEFAULT_LATENCY_MS, + glib::ParamFlags::READWRITE, + ), + ] + }); - let src_caps = gst::Caps::builder("text/x-raw") - .field("format", &"utf8") - .build(); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &src_caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - let sink_caps = gst::Caps::builder("audio/x-raw") - .field("format", &"S16LE") - .field("rate", &gst::IntRange::<i32>::new(8000, 48000)) - .field("channels", &1) - .build(); - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &sink_caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - klass.install_properties(&PROPERTIES); + PROPERTIES.as_ref() } -} -impl ObjectImpl for Transcriber { fn constructed(&self, obj: &Self::Type) { self.parent_constructed(obj); @@ -1110,19 +1075,23 @@ impl ObjectImpl for Transcriber { obj.set_element_flags(gst::ElementFlags::PROVIDE_CLOCK | gst::ElementFlags::REQUIRE_CLOCK); } - fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("language_code", ..) => { + fn set_property( + &self, + _obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "language_code" => { let mut settings = self.settings.lock().unwrap(); settings.language_code = value.get().expect("type checked upstream"); } - subclass::Property("latency", ..) => { + "latency" => { let mut settings = self.settings.lock().unwrap(); settings.latency_ms = value.get_some().expect("type checked upstream"); } - subclass::Property("use-partial-results", ..) => { + "use-partial-results" => { let mut settings = self.settings.lock().unwrap(); settings.use_partial_results = value.get_some().expect("type checked upstream"); } @@ -1130,19 +1099,17 @@ impl ObjectImpl for Transcriber { } } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("language-code", ..) => { + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "language-code" => { let settings = self.settings.lock().unwrap(); settings.language_code.to_value() } - subclass::Property("latency", ..) => { + "latency" => { let settings = self.settings.lock().unwrap(); settings.latency_ms.to_value() } - subclass::Property("use-partial-results", ..) => { + "use-partial-results" => { let settings = self.settings.lock().unwrap(); settings.use_partial_results.to_value() } @@ -1152,6 +1119,51 @@ impl ObjectImpl for Transcriber { } impl ElementImpl for Transcriber { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Transcriber", + "Audio/Text/Filter", + "Speech to Text filter, using AWS transcribe", + "Jordan Petridis <jordan@centricular.com>, Mathieu Duponchelle <mathieu@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let src_caps = gst::Caps::builder("text/x-raw") + .field("format", &"utf8") + .build(); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &src_caps, + ) + .unwrap(); + + let sink_caps = gst::Caps::builder("audio/x-raw") + .field("format", &"S16LE") + .field("rate", &gst::IntRange::<i32>::new(8000, 48000)) + .field("channels", &1) + .build(); + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &sink_caps, + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + fn change_state( &self, element: &Self::Type, diff --git a/net/rusoto/src/s3sink/imp.rs b/net/rusoto/src/s3sink/imp.rs index 93dfee28b..0896419e4 100644 --- a/net/rusoto/src/s3sink/imp.rs +++ b/net/rusoto/src/s3sink/imp.rs @@ -116,47 +116,6 @@ impl Default for Settings { } } -static PROPERTIES: [subclass::Property; 4] = [ - subclass::Property("bucket", |name| { - glib::ParamSpec::string( - name, - "S3 Bucket", - "The bucket of the file to write", - None, - glib::ParamFlags::READWRITE, /* + GST_PARAM_MUTABLE_READY) */ - ) - }), - subclass::Property("key", |name| { - glib::ParamSpec::string( - name, - "S3 Key", - "The key of the file to write", - None, - glib::ParamFlags::READWRITE, /* + GST_PARAM_MUTABLE_READY) */ - ) - }), - subclass::Property("region", |name| { - glib::ParamSpec::string( - name, - "AWS Region", - "An AWS region (e.g. eu-west-2).", - None, - glib::ParamFlags::READWRITE, /* + GST_PARAM_MUTABLE_READY) */ - ) - }), - subclass::Property("part-size", |name| { - glib::ParamSpec::uint64( - name, - "Part size", - "A size (in bytes) of an individual part used for multipart upload.", - 5 * 1024 * 1024, // 5 MB - 5 * 1024 * 1024 * 1024, // 5 GB - DEFAULT_BUFFER_SIZE, - glib::ParamFlags::READWRITE, /* + GST_PARAM_MUTABLE_READY) */ - ) - }), -]; - impl S3Sink { fn flush_current_buffer( &self, @@ -386,6 +345,7 @@ impl ObjectSubclass for S3Sink { const NAME: &'static str = "RusotoS3Sink"; type Type = super::S3Sink; type ParentType = gst_base::BaseSink; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -398,42 +358,65 @@ impl ObjectSubclass for S3Sink { canceller: Mutex::new(None), } } +} - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Amazon S3 sink", - "Source/Network", - "Writes an object to Amazon S3", - "Marcin Kolny <mkolny@amazon.com>", - ); +impl ObjectImpl for S3Sink { + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::string( + "bucket", + "S3 Bucket", + "The bucket of the file to write", + None, + glib::ParamFlags::READWRITE, /* + GST_PARAM_MUTABLE_READY) */ + ), + glib::ParamSpec::string( + "key", + "S3 Key", + "The key of the file to write", + None, + glib::ParamFlags::READWRITE, /* + GST_PARAM_MUTABLE_READY) */ + ), + glib::ParamSpec::string( + "region", + "AWS Region", + "An AWS region (e.g. eu-west-2).", + None, + glib::ParamFlags::READWRITE, /* + GST_PARAM_MUTABLE_READY) */ + ), + glib::ParamSpec::uint64( + "part-size", + "Part size", + "A size (in bytes) of an individual part used for multipart upload.", + 5 * 1024 * 1024, // 5 MB + 5 * 1024 * 1024 * 1024, // 5 GB + DEFAULT_BUFFER_SIZE, + glib::ParamFlags::READWRITE, /* + GST_PARAM_MUTABLE_READY) */ + ), + ] + }); - let caps = gst::Caps::new_any(); - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - klass.install_properties(&PROPERTIES); + PROPERTIES.as_ref() } -} -impl ObjectImpl for S3Sink { - fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id as usize]; + fn set_property( + &self, + _obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { let mut settings = self.settings.lock().unwrap(); - match *prop { - subclass::Property("bucket", ..) => { + match pspec.get_name() { + "bucket" => { settings.bucket = value.get::<String>().expect("type checked upstream"); } - subclass::Property("key", ..) => { + "key" => { settings.key = value.get::<String>().expect("type checked upstream"); } - subclass::Property("region", ..) => { + "region" => { settings.region = Region::from_str( &value .get::<String>() @@ -442,28 +425,57 @@ impl ObjectImpl for S3Sink { ) .unwrap(); } - subclass::Property("part-size", ..) => { + "part-size" => { settings.buffer_size = value.get_some::<u64>().expect("type checked upstream"); } _ => unimplemented!(), } } - fn get_property(&self, _: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id as usize]; + fn get_property(&self, _: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { let settings = self.settings.lock().unwrap(); - match *prop { - subclass::Property("key", ..) => settings.key.to_value(), - subclass::Property("bucket", ..) => settings.bucket.to_value(), - subclass::Property("region", ..) => settings.region.name().to_value(), - subclass::Property("part-size", ..) => settings.buffer_size.to_value(), + match pspec.get_name() { + "key" => settings.key.to_value(), + "bucket" => settings.bucket.to_value(), + "region" => settings.region.name().to_value(), + "part-size" => settings.buffer_size.to_value(), _ => unimplemented!(), } } } -impl ElementImpl for S3Sink {} +impl ElementImpl for S3Sink { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Amazon S3 sink", + "Source/Network", + "Writes an object to Amazon S3", + "Marcin Kolny <mkolny@amazon.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::new_any(); + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } +} impl BaseSinkImpl for S3Sink { fn start(&self, _element: &Self::Type) -> Result<(), gst::ErrorMessage> { diff --git a/net/rusoto/src/s3src/imp.rs b/net/rusoto/src/s3src/imp.rs index 01248af08..6bf1615a6 100644 --- a/net/rusoto/src/s3src/imp.rs +++ b/net/rusoto/src/s3src/imp.rs @@ -51,16 +51,6 @@ static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| { ) }); -static PROPERTIES: [subclass::Property; 1] = [subclass::Property("uri", |name| { - glib::ParamSpec::string( - name, - "URI", - "The S3 object URI", - None, - glib::ParamFlags::READWRITE, /* + GST_PARAM_MUTABLE_READY) */ - ) -})]; - impl S3Src { fn cancel(&self) { let mut canceller = self.canceller.lock().unwrap(); @@ -210,6 +200,7 @@ impl ObjectSubclass for S3Src { const NAME: &'static str = "RusotoS3Src"; type Type = super::S3Src; type ParentType = gst_base::BaseSrc; + type Interfaces = (gst::URIHandler,); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -222,50 +213,41 @@ impl ObjectSubclass for S3Src { canceller: Mutex::new(None), } } - - fn type_init(typ: &mut subclass::InitializingType<Self>) { - typ.add_interface::<gst::URIHandler>(); - } - - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Amazon S3 source", - "Source/Network", - "Reads an object from Amazon S3", - "Arun Raghavan <arun@arunraghavan.net>", - ); - - let caps = gst::Caps::new_any(); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - klass.install_properties(&PROPERTIES); - } } impl ObjectImpl for S3Src { - fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id as usize]; + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![glib::ParamSpec::string( + "uri", + "URI", + "The S3 object URI", + None, + glib::ParamFlags::READWRITE | gst::PARAM_FLAG_MUTABLE_READY, + )] + }); + + PROPERTIES.as_ref() + } - match *prop { - subclass::Property("uri", ..) => { + fn set_property( + &self, + obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "uri" => { let _ = self.set_uri(obj, value.get().expect("type checked upstream")); } _ => unimplemented!(), } } - fn get_property(&self, _: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id as usize]; - - match *prop { - subclass::Property("uri", ..) => { + fn get_property(&self, _: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "uri" => { let url = match *self.url.lock().unwrap() { Some(ref url) => url.to_string(), None => "".to_string(), @@ -287,10 +269,44 @@ impl ObjectImpl for S3Src { } impl ElementImpl for S3Src { - // No overrides + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Amazon S3 source", + "Source/Network", + "Reads an object from Amazon S3", + "Arun Raghavan <arun@arunraghavan.net>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::new_any(); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } } impl URIHandlerImpl for S3Src { + const URI_TYPE: gst::URIType = gst::URIType::Src; + + fn get_protocols() -> &'static [&'static str] { + &["s3"] + } + fn get_uri(&self, _: &Self::Type) -> Option<String> { self.url.lock().unwrap().as_ref().map(|s| s.to_string()) } @@ -298,14 +314,6 @@ impl URIHandlerImpl for S3Src { fn set_uri(&self, element: &Self::Type, uri: &str) -> Result<(), glib::Error> { self.set_uri(element, Some(uri)) } - - fn get_uri_type() -> gst::URIType { - gst::URIType::Src - } - - fn get_protocols() -> Vec<String> { - vec!["s3".to_string()] - } } impl BaseSrcImpl for S3Src { diff --git a/text/json/src/jsongstenc/imp.rs b/text/json/src/jsongstenc/imp.rs index 07e555e14..5844eb930 100644 --- a/text/json/src/jsongstenc/imp.rs +++ b/text/json/src/jsongstenc/imp.rs @@ -205,6 +205,7 @@ impl ObjectSubclass for JsonGstEnc { const NAME: &'static str = "RsJsonGstEnc"; type Type = super::JsonGstEnc; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -238,36 +239,6 @@ impl ObjectSubclass for JsonGstEnc { state: Mutex::new(State::default()), } } - - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "GStreamer buffers to JSON", - "Encoder/JSON", - "Wraps buffers containing any valid top-level JSON structures \ - into higher level JSON objects, and outputs those as ndjson", - "Mathieu Duponchelle <mathieu@centricular.com>", - ); - - let caps = gst::Caps::builder("application/x-json").build(); - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - let caps = gst::Caps::builder("application/x-json").build(); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - } } impl ObjectImpl for JsonGstEnc { @@ -280,6 +251,46 @@ impl ObjectImpl for JsonGstEnc { } impl ElementImpl for JsonGstEnc { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "GStreamer buffers to JSON", + "Encoder/JSON", + "Wraps buffers containing any valid top-level JSON structures \ + into higher level JSON objects, and outputs those as ndjson", + "Mathieu Duponchelle <mathieu@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::builder("application/x-json").build(); + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + let caps = gst::Caps::builder("application/x-json").build(); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + fn change_state( &self, element: &Self::Type, diff --git a/text/json/src/jsongstparse/imp.rs b/text/json/src/jsongstparse/imp.rs index bb4d7fb99..8b51edc32 100644 --- a/text/json/src/jsongstparse/imp.rs +++ b/text/json/src/jsongstparse/imp.rs @@ -867,6 +867,7 @@ impl ObjectSubclass for JsonGstParse { const NAME: &'static str = "RsJsonGstParse"; type Type = super::JsonGstParse; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -934,35 +935,6 @@ impl ObjectSubclass for JsonGstParse { state: Mutex::new(State::default()), } } - - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "JSON GStreamer parser", - "Parser/JSON", - "Parses ndjson as output by jsongstenc", - "Mathieu Duponchelle <mathieu@centricular.com>", - ); - - let caps = gst::Caps::builder("application/x-json").build(); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - let caps = gst::Caps::new_any(); - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - } } impl ObjectImpl for JsonGstParse { @@ -975,6 +947,45 @@ impl ObjectImpl for JsonGstParse { } impl ElementImpl for JsonGstParse { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "JSON GStreamer parser", + "Parser/JSON", + "Parses ndjson as output by jsongstenc", + "Mathieu Duponchelle <mathieu@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::builder("application/x-json").build(); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + let caps = gst::Caps::new_any(); + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + fn change_state( &self, element: &Self::Type, diff --git a/text/wrap/src/gsttextwrap/imp.rs b/text/wrap/src/gsttextwrap/imp.rs index e0401212e..86b623577 100644 --- a/text/wrap/src/gsttextwrap/imp.rs +++ b/text/wrap/src/gsttextwrap/imp.rs @@ -45,52 +45,6 @@ const DEFAULT_COLUMNS: u32 = 32; /* CEA 608 max columns */ const DEFAULT_LINES: u32 = 0; const DEFAULT_ACCUMULATE: i64 = -1; -static PROPERTIES: [subclass::Property; 4] = [ - subclass::Property("dictionary", |name| { - glib::ParamSpec::string( - name, - "Dictionary", - "Path to a dictionary to load at runtime to perform hyphenation, see \ - <https://docs.rs/crate/hyphenation/0.7.1> for more information", - None, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("columns", |name| { - glib::ParamSpec::uint( - name, - "Columns", - "Maximum number of columns for any given line", - 1, - std::u32::MAX, - DEFAULT_COLUMNS, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("lines", |name| { - glib::ParamSpec::uint( - name, - "Lines", - "Split input buffer into output buffers with max lines (0=do not split)", - 0, - std::u32::MAX, - DEFAULT_LINES, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("accumulate-time", |name| { - glib::ParamSpec::int64( - name, - "accumulate-time", - "Cut-off time for input text accumulation (-1=do not accumulate)", - -1, - std::i64::MAX, - DEFAULT_ACCUMULATE, - glib::ParamFlags::READWRITE, - ) - }), -]; - #[derive(Debug, Clone)] struct Settings { dictionary: Option<String>, @@ -409,6 +363,7 @@ impl ObjectSubclass for TextWrap { const NAME: &'static str = "RsTextWrap"; type Type = super::TextWrap; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -449,41 +404,53 @@ impl ObjectSubclass for TextWrap { state, } } - - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Text Wrapper", - "Text/Filter", - "Breaks text into fixed-size lines, with optional hyphenationz", - "Mathieu Duponchelle <mathieu@centricular.com>", - ); - - let caps = gst::Caps::builder("text/x-raw") - .field("format", &"utf8") - .build(); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - klass.install_properties(&PROPERTIES); - } } impl ObjectImpl for TextWrap { + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::string( + "dictionary", + "Dictionary", + "Path to a dictionary to load at runtime to perform hyphenation, see \ + <https://docs.rs/crate/hyphenation/0.7.1> for more information", + None, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "columns", + "Columns", + "Maximum number of columns for any given line", + 1, + std::u32::MAX, + DEFAULT_COLUMNS, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "lines", + "Lines", + "Split input buffer into output buffers with max lines (0=do not split)", + 0, + std::u32::MAX, + DEFAULT_LINES, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::int64( + "accumulate-time", + "accumulate-time", + "Cut-off time for input text accumulation (-1=do not accumulate)", + -1, + std::i64::MAX, + DEFAULT_ACCUMULATE, + glib::ParamFlags::READWRITE, + ), + ] + }); + + PROPERTIES.as_ref() + } + fn constructed(&self, obj: &Self::Type) { self.parent_constructed(obj); @@ -491,27 +458,31 @@ impl ObjectImpl for TextWrap { obj.add_pad(&self.srcpad).unwrap(); } - fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("dictionary", ..) => { + fn set_property( + &self, + _obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "dictionary" => { let mut settings = self.settings.lock().unwrap(); let mut state = self.state.lock().unwrap(); settings.dictionary = value.get().expect("type checked upstream"); state.options = None; } - subclass::Property("columns", ..) => { + "columns" => { let mut settings = self.settings.lock().unwrap(); let mut state = self.state.lock().unwrap(); settings.columns = value.get_some().expect("type checked upstream"); state.options = None; } - subclass::Property("lines", ..) => { + "lines" => { let mut settings = self.settings.lock().unwrap(); settings.lines = value.get_some().expect("type checked upstream"); } - subclass::Property("accumulate-time", ..) => { + "accumulate-time" => { let mut settings = self.settings.lock().unwrap(); settings.accumulate_time = match value.get_some().expect("type checked upstream") { -1i64 => gst::CLOCK_TIME_NONE, @@ -522,23 +493,21 @@ impl ObjectImpl for TextWrap { } } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("dictionary", ..) => { + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "dictionary" => { let settings = self.settings.lock().unwrap(); settings.dictionary.to_value() } - subclass::Property("columns", ..) => { + "columns" => { let settings = self.settings.lock().unwrap(); settings.columns.to_value() } - subclass::Property("lines", ..) => { + "lines" => { let settings = self.settings.lock().unwrap(); settings.lines.to_value() } - subclass::Property("accumulate-time", ..) => { + "accumulate-time" => { let settings = self.settings.lock().unwrap(); match settings.accumulate_time.0 { Some(time) => (time as i64).to_value(), @@ -551,6 +520,46 @@ impl ObjectImpl for TextWrap { } impl ElementImpl for TextWrap { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Text Wrapper", + "Text/Filter", + "Breaks text into fixed-size lines, with optional hyphenation", + "Mathieu Duponchelle <mathieu@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::builder("text/x-raw") + .field("format", &"utf8") + .build(); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + fn change_state( &self, element: &Self::Type, diff --git a/tutorial/src/identity/imp.rs b/tutorial/src/identity/imp.rs index d94ec448d..7a256e60c 100644 --- a/tutorial/src/identity/imp.rs +++ b/tutorial/src/identity/imp.rs @@ -118,6 +118,7 @@ impl ObjectSubclass for Identity { const NAME: &'static str = "RsIdentity"; type Type = super::Identity; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -185,50 +186,6 @@ impl ObjectSubclass for Identity { // into the debug logs Self { srcpad, sinkpad } } - - // Called exactly once when registering the type. Used for - // setting up metadata for all instances, e.g. the name and - // classification and the pad templates with their caps. - // - // Actual instances can create pads based on those pad templates - // with a subset of the caps given here. - fn class_init(klass: &mut Self::Class) { - // Set the element specific metadata. This information is what - // is visible from gst-inspect-1.0 and can also be programatically - // retrieved from the gst::Registry after initial registration - // without having to load the plugin in memory. - klass.set_metadata( - "Identity", - "Generic", - "Does nothing with the data", - "Sebastian Dröge <sebastian@centricular.com>", - ); - - // Create and add pad templates for our sink and source pad. These - // are later used for actually creating the pads and beforehand - // already provide information to GStreamer about all possible - // pads that could exist for this type. - - // Our element can accept any possible caps on both pads - let caps = gst::Caps::new_any(); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - } } // Implementation of glib::Object virtual methods @@ -247,6 +204,55 @@ impl ObjectImpl for Identity { // Implementation of gst::Element virtual methods impl ElementImpl for Identity { + // Set the element specific metadata. This information is what + // is visible from gst-inspect-1.0 and can also be programatically + // retrieved from the gst::Registry after initial registration + // without having to load the plugin in memory. + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Identity", + "Generic", + "Does nothing with the data", + "Sebastian Dröge <sebastian@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + // Create and add pad templates for our sink and source pad. These + // are later used for actually creating the pads and beforehand + // already provide information to GStreamer about all possible + // pads that could exist for this type. + // + // Actual instances can create pads based on those pad templates + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + // Our element can accept any possible caps on both pads + let caps = gst::Caps::new_any(); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + // Called whenever the state of the element should be changed. This allows for // starting up the element, allocating/deallocating resources or shutting down // the element again. diff --git a/tutorial/src/progressbin/imp.rs b/tutorial/src/progressbin/imp.rs index 9a1e5c7cf..7410bc2b3 100644 --- a/tutorial/src/progressbin/imp.rs +++ b/tutorial/src/progressbin/imp.rs @@ -41,18 +41,6 @@ pub struct ProgressBin { output_type: Mutex<ProgressBinOutput>, } -// Metadata for the element's properties -static PROPERTIES: [subclass::Property; 1] = [subclass::Property("output", |name| { - glib::ParamSpec::enum_( - name, - "Output", - "Defines the output type of the progressbin", - ProgressBinOutput::static_type(), - DEFAULT_OUTPUT_TYPE as i32, - glib::ParamFlags::READWRITE, - ) -})]; - // This trait registers our type with the GObject object system and // provides the entry points for creating a new instance and setting // up the class data @@ -60,6 +48,7 @@ impl ObjectSubclass for ProgressBin { const NAME: &'static str = "RsProgressBin"; type Type = super::ProgressBin; type ParentType = gst::Bin; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -92,64 +81,37 @@ impl ObjectSubclass for ProgressBin { output_type: Mutex::new(ProgressBinOutput::Println), } } - - // Called exactly once when registering the type. Used for - // setting up metadata for all instances, e.g. the name and - // classification and the pad templates with their caps. - // - // Actual instances can create pads based on those pad templates - // with a subset of the caps given here. - fn class_init(klass: &mut Self::Class) { - // Set the element specific metadata. This information is what - // is visible from gst-inspect-1.0 and can also be programatically - // retrieved from the gst::Registry after initial registration - // without having to load the plugin in memory. - klass.set_metadata( - "ProgressBin", - "Generic", - "Prints progress information to stdout", - "Sebastian Dröge <sebastian@centricular.com>", - ); - - // Create and add pad templates for our sink and source pad. These - // are later used for actually creating the pads and beforehand - // already provide information to GStreamer about all possible - // pads that could exist for this type. - - // Our element can accept any possible caps on both pads - let caps = gst::Caps::new_any(); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - // Install all our properties - klass.install_properties(&PROPERTIES); - } } // Implementation of glib::Object virtual methods impl ObjectImpl for ProgressBin { + // Metadata for the element's properties + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![glib::ParamSpec::enum_( + "output", + "Output", + "Defines the output type of the progressbin", + ProgressBinOutput::static_type(), + DEFAULT_OUTPUT_TYPE as i32, + glib::ParamFlags::READWRITE, + )] + }); + + PROPERTIES.as_ref() + } + // Called whenever a value of a property is changed. It can be called // at any time from any thread. - fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("output", ..) => { + fn set_property( + &self, + obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "output" => { let mut output_type = self.output_type.lock().unwrap(); let new_output_type = value .get_some::<ProgressBinOutput>() @@ -169,11 +131,9 @@ impl ObjectImpl for ProgressBin { // Called whenever a value of a property is read. It can be called // at any time from any thread. - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("output", ..) => { + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "output" => { let output_type = self.output_type.lock().unwrap(); output_type.to_value() } @@ -207,7 +167,56 @@ impl ObjectImpl for ProgressBin { } // Implementation of gst::Element virtual methods -impl ElementImpl for ProgressBin {} +impl ElementImpl for ProgressBin { + // Set the element specific metadata. This information is what + // is visible from gst-inspect-1.0 and can also be programatically + // retrieved from the gst::Registry after initial registration + // without having to load the plugin in memory. + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "ProgressBin", + "Generic", + "Prints progress information to stdout", + "Sebastian Dröge <sebastian@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + // Create and add pad templates for our sink and source pad. These + // are later used for actually creating the pads and beforehand + // already provide information to GStreamer about all possible + // pads that could exist for this type. + // + // Actual instances can create pads based on those pad templates + // with a subset of the caps given here. + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + // Our element can accept any possible caps on both pads + let caps = gst::Caps::new_any(); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } +} // Implementation of gst::Bin virtual methods impl BinImpl for ProgressBin { diff --git a/tutorial/src/rgb2gray/imp.rs b/tutorial/src/rgb2gray/imp.rs index f330e467b..019e48d33 100644 --- a/tutorial/src/rgb2gray/imp.rs +++ b/tutorial/src/rgb2gray/imp.rs @@ -48,30 +48,6 @@ impl Default for Settings { } } -// Metadata for the properties -static PROPERTIES: [subclass::Property; 2] = [ - subclass::Property("invert", |name| { - glib::ParamSpec::boolean( - name, - "Invert", - "Invert grayscale output", - DEFAULT_INVERT, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("shift", |name| { - glib::ParamSpec::uint( - name, - "Shift", - "Shift grayscale output (wrapping around)", - 0, - 255, - DEFAULT_SHIFT, - glib::ParamFlags::READWRITE, - ) - }), -]; - // Stream-specific state, i.e. video format configuration struct State { in_info: gst_video::VideoInfo, @@ -118,6 +94,7 @@ impl ObjectSubclass for Rgb2Gray { const NAME: &'static str = "RsRgb2Gray"; type Type = super::Rgb2Gray; type ParentType = gst_base::BaseTransform; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -132,122 +109,47 @@ impl ObjectSubclass for Rgb2Gray { state: Mutex::new(None), } } +} - // Called exactly once when registering the type. Used for - // setting up metadata for all instances, e.g. the name and - // classification and the pad templates with their caps. - // - // Actual instances can create pads based on those pad templates - // with a subset of the caps given here. In case of basetransform, - // a "src" and "sink" pad template are required here and the base class - // will automatically instantiate pads for them. - // - // Our element here can convert BGRx to BGRx or GRAY8, both being grayscale. - fn class_init(klass: &mut Self::Class) { - // Set the element specific metadata. This information is what - // is visible from gst-inspect-1.0 and can also be programatically - // retrieved from the gst::Registry after initial registration - // without having to load the plugin in memory. - klass.set_metadata( - "RGB-GRAY Converter", - "Filter/Effect/Converter/Video", - "Converts RGB to GRAY or grayscale RGB", - "Sebastian Dröge <sebastian@centricular.com>", - ); - - // Create and add pad templates for our sink and source pad. These - // are later used for actually creating the pads and beforehand - // already provide information to GStreamer about all possible - // pads that could exist for this type. - - // On the src pad, we can produce BGRx and GRAY8 of any - // width/height and with any framerate - let caps = gst::Caps::new_simple( - "video/x-raw", - &[ - ( - "format", - &gst::List::new(&[ - &gst_video::VideoFormat::Bgrx.to_str(), - &gst_video::VideoFormat::Gray8.to_str(), - ]), - ), - ("width", &gst::IntRange::<i32>::new(0, i32::MAX)), - ("height", &gst::IntRange::<i32>::new(0, i32::MAX)), - ( - "framerate", - &gst::FractionRange::new( - gst::Fraction::new(0, 1), - gst::Fraction::new(i32::MAX, 1), - ), +// Implementation of glib::Object virtual methods +impl ObjectImpl for Rgb2Gray { + fn properties() -> &'static [glib::ParamSpec] { + // Metadata for the properties + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::boolean( + "invert", + "Invert", + "Invert grayscale output", + DEFAULT_INVERT, + glib::ParamFlags::READWRITE, ), - ], - ); - // The src pad template must be named "src" for basetransform - // and specific a pad that is always there - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - // On the sink pad, we can accept BGRx of any - // width/height and with any framerate - let caps = gst::Caps::new_simple( - "video/x-raw", - &[ - ("format", &gst_video::VideoFormat::Bgrx.to_str()), - ("width", &gst::IntRange::<i32>::new(0, i32::MAX)), - ("height", &gst::IntRange::<i32>::new(0, i32::MAX)), - ( - "framerate", - &gst::FractionRange::new( - gst::Fraction::new(0, 1), - gst::Fraction::new(i32::MAX, 1), - ), + glib::ParamSpec::uint( + "shift", + "Shift", + "Shift grayscale output (wrapping around)", + 0, + 255, + DEFAULT_SHIFT, + glib::ParamFlags::READWRITE, ), - ], - ); - // The sink pad template must be named "sink" for basetransform - // and specific a pad that is always there - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - // Install all our properties - klass.install_properties(&PROPERTIES); - - // Configure basetransform so that we are never running in-place, - // don't passthrough on same caps and also never call transform_ip - // in passthrough mode (which does not matter for us here). - // - // We could work in-place for BGRx->BGRx but don't do here for simplicity - // for now. - klass.configure( - gst_base::subclass::BaseTransformMode::NeverInPlace, - false, - false, - ); + ] + }); + + PROPERTIES.as_ref() } -} -// Implementation of glib::Object virtual methods -impl ObjectImpl for Rgb2Gray { // Called whenever a value of a property is changed. It can be called // at any time from any thread. - fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("invert", ..) => { + fn set_property( + &self, + obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "invert" => { let mut settings = self.settings.lock().unwrap(); let invert = value.get_some().expect("type checked upstream"); gst_info!( @@ -259,7 +161,7 @@ impl ObjectImpl for Rgb2Gray { ); settings.invert = invert; } - subclass::Property("shift", ..) => { + "shift" => { let mut settings = self.settings.lock().unwrap(); let shift = value.get_some().expect("type checked upstream"); gst_info!( @@ -277,15 +179,13 @@ impl ObjectImpl for Rgb2Gray { // Called whenever a value of a property is read. It can be called // at any time from any thread. - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("invert", ..) => { + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "invert" => { let settings = self.settings.lock().unwrap(); settings.invert.to_value() } - subclass::Property("shift", ..) => { + "shift" => { let settings = self.settings.lock().unwrap(); settings.shift.to_value() } @@ -295,10 +195,112 @@ impl ObjectImpl for Rgb2Gray { } // Implementation of gst::Element virtual methods -impl ElementImpl for Rgb2Gray {} +impl ElementImpl for Rgb2Gray { + // Set the element specific metadata. This information is what + // is visible from gst-inspect-1.0 and can also be programatically + // retrieved from the gst::Registry after initial registration + // without having to load the plugin in memory. + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "RGB-GRAY Converter", + "Filter/Effect/Converter/Video", + "Converts RGB to GRAY or grayscale RGB", + "Sebastian Dröge <sebastian@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + // Create and add pad templates for our sink and source pad. These + // are later used for actually creating the pads and beforehand + // already provide information to GStreamer about all possible + // pads that could exist for this type. + // + // Our element here can convert BGRx to BGRx or GRAY8, both being grayscale. + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + // On the src pad, we can produce BGRx and GRAY8 of any + // width/height and with any framerate + let caps = gst::Caps::new_simple( + "video/x-raw", + &[ + ( + "format", + &gst::List::new(&[ + &gst_video::VideoFormat::Bgrx.to_str(), + &gst_video::VideoFormat::Gray8.to_str(), + ]), + ), + ("width", &gst::IntRange::<i32>::new(0, i32::MAX)), + ("height", &gst::IntRange::<i32>::new(0, i32::MAX)), + ( + "framerate", + &gst::FractionRange::new( + gst::Fraction::new(0, 1), + gst::Fraction::new(i32::MAX, 1), + ), + ), + ], + ); + // The src pad template must be named "src" for basetransform + // and specific a pad that is always there + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + // On the sink pad, we can accept BGRx of any + // width/height and with any framerate + let caps = gst::Caps::new_simple( + "video/x-raw", + &[ + ("format", &gst_video::VideoFormat::Bgrx.to_str()), + ("width", &gst::IntRange::<i32>::new(0, i32::MAX)), + ("height", &gst::IntRange::<i32>::new(0, i32::MAX)), + ( + "framerate", + &gst::FractionRange::new( + gst::Fraction::new(0, 1), + gst::Fraction::new(i32::MAX, 1), + ), + ), + ], + ); + // The sink pad template must be named "sink" for basetransform + // and specific a pad that is always there + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } +} // Implementation of gst_base::BaseTransform virtual methods impl BaseTransformImpl for Rgb2Gray { + // Configure basetransform so that we are never running in-place, + // don't passthrough on same caps and also never call transform_ip + // in passthrough mode (which does not matter for us here). + // + // We could work in-place for BGRx->BGRx but don't do here for simplicity + // for now. + const MODE: gst_base::subclass::BaseTransformMode = + gst_base::subclass::BaseTransformMode::NeverInPlace; + const PASSTHROUGH_ON_SAME_CAPS: bool = false; + const TRANSFORM_IP_ON_PASSTHROUGH: bool = false; + // Called for converting caps from one pad to another to account for any // changes in the media format this element is performing. // diff --git a/tutorial/src/sinesrc/imp.rs b/tutorial/src/sinesrc/imp.rs index 951dc4b4e..558aaf58a 100644 --- a/tutorial/src/sinesrc/imp.rs +++ b/tutorial/src/sinesrc/imp.rs @@ -64,61 +64,6 @@ impl Default for Settings { } } -// Metadata for the properties -static PROPERTIES: [subclass::Property; 5] = [ - subclass::Property("samples-per-buffer", |name| { - glib::ParamSpec::uint( - name, - "Samples Per Buffer", - "Number of samples per output buffer", - 1, - u32::MAX, - DEFAULT_SAMPLES_PER_BUFFER, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("freq", |name| { - glib::ParamSpec::uint( - name, - "Frequency", - "Frequency", - 1, - u32::MAX, - DEFAULT_FREQ, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("volume", |name| { - glib::ParamSpec::double( - name, - "Volume", - "Output volume", - 0.0, - 10.0, - DEFAULT_VOLUME, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("mute", |name| { - glib::ParamSpec::boolean( - name, - "Mute", - "Mute", - DEFAULT_MUTE, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("is-live", |name| { - glib::ParamSpec::boolean( - name, - "Is Live", - "(Pseudo) live output", - DEFAULT_IS_LIVE, - glib::ParamFlags::READWRITE, - ) - }), -]; - // Stream-specific state, i.e. audio format configuration // and sample offset struct State { @@ -203,6 +148,7 @@ impl ObjectSubclass for SineSrc { const NAME: &'static str = "RsSineSrc"; type Type = super::SineSrc; type ParentType = gst_base::PushSrc; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -221,69 +167,61 @@ impl ObjectSubclass for SineSrc { }), } } - - // Called exactly once when registering the type. Used for - // setting up metadata for all instances, e.g. the name and - // classification and the pad templates with their caps. - // - // Actual instances can create pads based on those pad templates - // with a subset of the caps given here. In case of basesrc, - // a "src" and "sink" pad template are required here and the base class - // will automatically instantiate pads for them. - // - // Our element here can output f32 and f64 - fn class_init(klass: &mut Self::Class) { - // Set the element specific metadata. This information is what - // is visible from gst-inspect-1.0 and can also be programatically - // retrieved from the gst::Registry after initial registration - // without having to load the plugin in memory. - klass.set_metadata( - "Sine Wave Source", - "Source/Audio", - "Creates a sine wave", - "Sebastian Dröge <sebastian@centricular.com>", - ); - - // Create and add pad templates for our sink and source pad. These - // are later used for actually creating the pads and beforehand - // already provide information to GStreamer about all possible - // pads that could exist for this type. - - // On the src pad, we can produce F32/F64 with any sample rate - // and any number of channels - let caps = gst::Caps::new_simple( - "audio/x-raw", - &[ - ( - "format", - &gst::List::new(&[ - &gst_audio::AUDIO_FORMAT_F32.to_str(), - &gst_audio::AUDIO_FORMAT_F64.to_str(), - ]), - ), - ("layout", &"interleaved"), - ("rate", &gst::IntRange::<i32>::new(1, i32::MAX)), - ("channels", &gst::IntRange::<i32>::new(1, i32::MAX)), - ], - ); - // The src pad template must be named "src" for basesrc - // and specific a pad that is always there - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - // Install all our properties - klass.install_properties(&PROPERTIES); - } } // Implementation of glib::Object virtual methods impl ObjectImpl for SineSrc { + // Metadata for the properties + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::uint( + "samples-per-buffer", + "Samples Per Buffer", + "Number of samples per output buffer", + 1, + u32::MAX, + DEFAULT_SAMPLES_PER_BUFFER, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "freq", + "Frequency", + "Frequency", + 1, + u32::MAX, + DEFAULT_FREQ, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::double( + "volume", + "Volume", + "Output volume", + 0.0, + 10.0, + DEFAULT_VOLUME, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boolean( + "mute", + "Mute", + "Mute", + DEFAULT_MUTE, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boolean( + "is-live", + "Is Live", + "(Pseudo) live output", + DEFAULT_IS_LIVE, + glib::ParamFlags::READWRITE, + ), + ] + }); + + PROPERTIES.as_ref() + } + // Called right after construction of a new instance fn constructed(&self, obj: &Self::Type) { // Call the parent class' ::constructed() implementation first @@ -297,11 +235,15 @@ impl ObjectImpl for SineSrc { // Called whenever a value of a property is changed. It can be called // at any time from any thread. - fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("samples-per-buffer", ..) => { + fn set_property( + &self, + obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "samples-per-buffer" => { let mut settings = self.settings.lock().unwrap(); let samples_per_buffer = value.get_some().expect("type checked upstream"); gst_info!( @@ -316,7 +258,7 @@ impl ObjectImpl for SineSrc { let _ = obj.post_message(gst::message::Latency::builder().src(obj).build()); } - subclass::Property("freq", ..) => { + "freq" => { let mut settings = self.settings.lock().unwrap(); let freq = value.get_some().expect("type checked upstream"); gst_info!( @@ -328,7 +270,7 @@ impl ObjectImpl for SineSrc { ); settings.freq = freq; } - subclass::Property("volume", ..) => { + "volume" => { let mut settings = self.settings.lock().unwrap(); let volume = value.get_some().expect("type checked upstream"); gst_info!( @@ -340,7 +282,7 @@ impl ObjectImpl for SineSrc { ); settings.volume = volume; } - subclass::Property("mute", ..) => { + "mute" => { let mut settings = self.settings.lock().unwrap(); let mute = value.get_some().expect("type checked upstream"); gst_info!( @@ -352,7 +294,7 @@ impl ObjectImpl for SineSrc { ); settings.mute = mute; } - subclass::Property("is-live", ..) => { + "is-live" => { let mut settings = self.settings.lock().unwrap(); let is_live = value.get_some().expect("type checked upstream"); gst_info!( @@ -370,27 +312,25 @@ impl ObjectImpl for SineSrc { // Called whenever a value of a property is read. It can be called // at any time from any thread. - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("samples-per-buffer", ..) => { + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "samples-per-buffer" => { let settings = self.settings.lock().unwrap(); settings.samples_per_buffer.to_value() } - subclass::Property("freq", ..) => { + "freq" => { let settings = self.settings.lock().unwrap(); settings.freq.to_value() } - subclass::Property("volume", ..) => { + "volume" => { let settings = self.settings.lock().unwrap(); settings.volume.to_value() } - subclass::Property("mute", ..) => { + "mute" => { let settings = self.settings.lock().unwrap(); settings.mute.to_value() } - subclass::Property("is-live", ..) => { + "is-live" => { let settings = self.settings.lock().unwrap(); settings.is_live.to_value() } @@ -401,6 +341,62 @@ impl ObjectImpl for SineSrc { // Implementation of gst::Element virtual methods impl ElementImpl for SineSrc { + // Set the element specific metadata. This information is what + // is visible from gst-inspect-1.0 and can also be programatically + // retrieved from the gst::Registry after initial registration + // without having to load the plugin in memory. + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Sine Wave Source", + "Source/Audio", + "Creates a sine wave", + "Sebastian Dröge <sebastian@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + // Create and add pad templates for our sink and source pad. These + // are later used for actually creating the pads and beforehand + // already provide information to GStreamer about all possible + // pads that could exist for this type. + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + // On the src pad, we can produce F32/F64 with any sample rate + // and any number of channels + let caps = gst::Caps::new_simple( + "audio/x-raw", + &[ + ( + "format", + &gst::List::new(&[ + &gst_audio::AUDIO_FORMAT_F32.to_str(), + &gst_audio::AUDIO_FORMAT_F64.to_str(), + ]), + ), + ("layout", &"interleaved"), + ("rate", &gst::IntRange::<i32>::new(1, i32::MAX)), + ("channels", &gst::IntRange::<i32>::new(1, i32::MAX)), + ], + ); + // The src pad template must be named "src" for basesrc + // and specific a pad that is always there + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + // Called whenever the state of the element should be changed. This allows for // starting up the element, allocating/deallocating resources or shutting down // the element again. diff --git a/utils/fallbackswitch/src/fallbacksrc/custom_source/imp.rs b/utils/fallbackswitch/src/fallbacksrc/custom_source/imp.rs index edca3ed73..895390248 100644 --- a/utils/fallbackswitch/src/fallbacksrc/custom_source/imp.rs +++ b/utils/fallbackswitch/src/fallbacksrc/custom_source/imp.rs @@ -35,16 +35,6 @@ static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| { ) }); -static PROPERTIES: [subclass::Property; 1] = [subclass::Property("source", |name| { - glib::ParamSpec::object( - name, - "Source", - "Source", - gst::Element::static_type(), - glib::ParamFlags::WRITABLE | glib::ParamFlags::CONSTRUCT_ONLY, - ) -})]; - struct Stream { source_pad: gst::Pad, ghost_pad: gst::GhostPad, @@ -67,6 +57,7 @@ impl ObjectSubclass for CustomSource { const NAME: &'static str = "FallbackSrcCustomSource"; type Type = super::CustomSource; type ParentType = gst::Bin; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -82,35 +73,32 @@ impl ObjectSubclass for CustomSource { }), } } - - fn class_init(klass: &mut Self::Class) { - let src_pad_template = gst::PadTemplate::new( - "audio_%u", - gst::PadDirection::Src, - gst::PadPresence::Sometimes, - &gst::Caps::new_any(), - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - let src_pad_template = gst::PadTemplate::new( - "video_%u", - gst::PadDirection::Src, - gst::PadPresence::Sometimes, - &gst::Caps::new_any(), - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - klass.install_properties(&PROPERTIES); - } } impl ObjectImpl for CustomSource { - fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![glib::ParamSpec::object( + "source", + "Source", + "Source", + gst::Element::static_type(), + glib::ParamFlags::WRITABLE | glib::ParamFlags::CONSTRUCT_ONLY, + )] + }); + + PROPERTIES.as_ref() + } - match *prop { - subclass::Property("source", ..) => { + fn set_property( + &self, + obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "source" => { let source = value.get::<gst::Element>().unwrap().unwrap(); self.source.set(source.clone()).unwrap(); obj.add(&source).unwrap(); @@ -129,6 +117,30 @@ impl ObjectImpl for CustomSource { } impl ElementImpl for CustomSource { + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let audio_src_pad_template = gst::PadTemplate::new( + "audio_%u", + gst::PadDirection::Src, + gst::PadPresence::Sometimes, + &gst::Caps::new_any(), + ) + .unwrap(); + + let video_src_pad_template = gst::PadTemplate::new( + "video_%u", + gst::PadDirection::Src, + gst::PadPresence::Sometimes, + &gst::Caps::new_any(), + ) + .unwrap(); + + vec![audio_src_pad_template, video_src_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + #[allow(clippy::single_match)] fn change_state( &self, diff --git a/utils/fallbackswitch/src/fallbacksrc/imp.rs b/utils/fallbackswitch/src/fallbacksrc/imp.rs index 89eccc497..f4ceb1ebb 100644 --- a/utils/fallbackswitch/src/fallbacksrc/imp.rs +++ b/utils/fallbackswitch/src/fallbacksrc/imp.rs @@ -177,137 +177,11 @@ pub struct FallbackSrc { state: Mutex<Option<State>>, } -static PROPERTIES: [subclass::Property; 13] = [ - subclass::Property("enable-audio", |name| { - glib::ParamSpec::boolean( - name, - "Enable Audio", - "Enable the audio stream, this will output silence if there's no audio in the configured URI", - true, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("enable-video", |name| { - glib::ParamSpec::boolean( - name, - "Enable Video", - "Enable the video stream, this will output black or the fallback video if there's no video in the configured URI", - true, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("uri", |name| { - glib::ParamSpec::string(name, "URI", "URI to use", None, glib::ParamFlags::READWRITE) - }), - subclass::Property("source", |name| { - glib::ParamSpec::object( - name, - "Source", - "Source to use instead of the URI", - gst::Element::static_type(), - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("fallback-uri", |name| { - glib::ParamSpec::string( - name, - "Fallback URI", - "Fallback URI to use for video in case the main stream doesn't work", - None, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("timeout", |name| { - glib::ParamSpec::uint64( - name, - "Timeout", - "Timeout for switching to the fallback URI", - 0, - std::u64::MAX, - 5 * gst::SECOND_VAL, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("restart-timeout", |name| { - glib::ParamSpec::uint64( - name, - "Timeout", - "Timeout for restarting an active source", - 0, - std::u64::MAX, - 5 * gst::SECOND_VAL, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("retry-timeout", |name| { - glib::ParamSpec::uint64( - name, - "Retry Timeout", - "Timeout for stopping after repeated failure", - 0, - std::u64::MAX, - 60 * gst::SECOND_VAL, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("restart-on-eos", |name| { - glib::ParamSpec::boolean( - name, - "Restart on EOS", - "Restart source on EOS", - false, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("status", |name| { - glib::ParamSpec::enum_( - name, - "Status", - "Current source status", - Status::static_type(), - Status::Stopped as i32, - glib::ParamFlags::READABLE, - ) - }), - subclass::Property("min-latency", |name| { - glib::ParamSpec::uint64( - name, - "Minimum Latency", - "When the main source has a higher latency than the fallback source \ - this allows to configure a minimum latency that would be configured \ - if initially the fallback is enabled", - 0, - std::u64::MAX, - 0, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("buffer-duration", |name| { - glib::ParamSpec::int64( - name, - "Buffer Duration", - "Buffer duration when buffering streams (-1 default value)", - -1, - std::i64::MAX, - -1, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("statistics", |name| { - glib::ParamSpec::boxed( - name, - "Statistics", - "Various statistics", - gst::Structure::static_type(), - glib::ParamFlags::READABLE, - ) - }), -]; - impl ObjectSubclass for FallbackSrc { const NAME: &'static str = "FallbackSrc"; type Type = super::FallbackSrc; type ParentType = gst::Bin; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -319,58 +193,125 @@ impl ObjectSubclass for FallbackSrc { state: Mutex::new(None), } } - - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Fallback Source", - "Generic/Source", - "Live source with uridecodebin3 or custom source, and fallback image stream", - "Sebastian Dröge <sebastian@centricular.com>", - ); - - let src_pad_template = gst::PadTemplate::new( - "audio", - gst::PadDirection::Src, - gst::PadPresence::Sometimes, - &gst::Caps::new_any(), - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - let src_pad_template = gst::PadTemplate::new( - "video", - gst::PadDirection::Src, - gst::PadPresence::Sometimes, - &gst::Caps::new_any(), - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - klass.install_properties(&PROPERTIES); - - klass.add_signal_with_class_handler_and_accumulator( - "update-uri", - glib::SignalFlags::RUN_LAST | glib::SignalFlags::ACTION, - &[String::static_type()], - String::static_type(), - |_token, args| { - // Simplify return the input by default - Some(args[1].clone()) - }, - |_hint, ret, value| { - *ret = value.clone(); - false - }, - ); - } } impl ObjectImpl for FallbackSrc { - fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::boolean( + "enable-audio", + "Enable Audio", + "Enable the audio stream, this will output silence if there's no audio in the configured URI", + true, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boolean( + "enable-video", + "Enable Video", + "Enable the video stream, this will output black or the fallback video if there's no video in the configured URI", + true, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::string("uri", "URI", "URI to use", None, glib::ParamFlags::READWRITE), + glib::ParamSpec::object( + "source", + "Source", + "Source to use instead of the URI", + gst::Element::static_type(), + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::string( + "fallback-uri", + "Fallback URI", + "Fallback URI to use for video in case the main stream doesn't work", + None, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint64( + "timeout", + "Timeout", + "Timeout for switching to the fallback URI", + 0, + std::u64::MAX, + 5 * gst::SECOND_VAL, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint64( + "restart-timeout", + "Timeout", + "Timeout for restarting an active source", + 0, + std::u64::MAX, + 5 * gst::SECOND_VAL, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint64( + "retry-timeout", + "Retry Timeout", + "Timeout for stopping after repeated failure", + 0, + std::u64::MAX, + 60 * gst::SECOND_VAL, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boolean( + "restart-on-eos", + "Restart on EOS", + "Restart source on EOS", + false, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::enum_( + "status", + "Status", + "Current source status", + Status::static_type(), + Status::Stopped as i32, + glib::ParamFlags::READABLE, + ), + glib::ParamSpec::uint64( + "min-latency", + "Minimum Latency", + "When the main source has a higher latency than the fallback source \ + this allows to configure a minimum latency that would be configured \ + if initially the fallback is enabled", + 0, + std::u64::MAX, + 0, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::int64( + "buffer-duration", + "Buffer Duration", + "Buffer duration when buffering streams (-1 default value)", + -1, + std::i64::MAX, + -1, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boxed( + "statistics", + "Statistics", + "Various statistics", + gst::Structure::static_type(), + glib::ParamFlags::READABLE, + ), + ] + }); - match *prop { - subclass::Property("enable-audio", ..) => { + PROPERTIES.as_ref() + } + + fn set_property( + &self, + obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "enable-audio" => { let mut settings = self.settings.lock().unwrap(); let new_value = value.get_some().expect("type checked upstream"); gst_info!( @@ -382,7 +323,7 @@ impl ObjectImpl for FallbackSrc { ); settings.enable_audio = new_value; } - subclass::Property("enable-video", ..) => { + "enable-video" => { let mut settings = self.settings.lock().unwrap(); let new_value = value.get_some().expect("type checked upstream"); gst_info!( @@ -394,7 +335,7 @@ impl ObjectImpl for FallbackSrc { ); settings.enable_video = new_value; } - subclass::Property("uri", ..) => { + "uri" => { let mut settings = self.settings.lock().unwrap(); let new_value = value.get().expect("type checked upstream"); gst_info!( @@ -406,7 +347,7 @@ impl ObjectImpl for FallbackSrc { ); settings.uri = new_value; } - subclass::Property("source", ..) => { + "source" => { let mut settings = self.settings.lock().unwrap(); let new_value = value.get().expect("type checked upstream"); gst_info!( @@ -418,7 +359,7 @@ impl ObjectImpl for FallbackSrc { ); settings.source = new_value; } - subclass::Property("fallback-uri", ..) => { + "fallback-uri" => { let mut settings = self.settings.lock().unwrap(); let new_value = value.get().expect("type checked upstream"); gst_info!( @@ -430,7 +371,7 @@ impl ObjectImpl for FallbackSrc { ); settings.fallback_uri = new_value; } - subclass::Property("timeout", ..) => { + "timeout" => { let mut settings = self.settings.lock().unwrap(); let new_value = value.get_some().expect("type checked upstream"); gst_info!( @@ -442,7 +383,7 @@ impl ObjectImpl for FallbackSrc { ); settings.timeout = new_value; } - subclass::Property("restart-timeout", ..) => { + "restart-timeout" => { let mut settings = self.settings.lock().unwrap(); let new_value = value.get_some().expect("type checked upstream"); gst_info!( @@ -454,7 +395,7 @@ impl ObjectImpl for FallbackSrc { ); settings.restart_timeout = new_value; } - subclass::Property("retry-timeout", ..) => { + "retry-timeout" => { let mut settings = self.settings.lock().unwrap(); let new_value = value.get_some().expect("type checked upstream"); gst_info!( @@ -466,7 +407,7 @@ impl ObjectImpl for FallbackSrc { ); settings.retry_timeout = new_value; } - subclass::Property("restart-on-eos", ..) => { + "restart-on-eos" => { let mut settings = self.settings.lock().unwrap(); let new_value = value.get_some().expect("type checked upstream"); gst_info!( @@ -478,7 +419,7 @@ impl ObjectImpl for FallbackSrc { ); settings.restart_on_eos = new_value; } - subclass::Property("min-latency", ..) => { + "min-latency" => { let mut settings = self.settings.lock().unwrap(); let new_value = value.get_some().expect("type checked upstream"); gst_info!( @@ -490,7 +431,7 @@ impl ObjectImpl for FallbackSrc { ); settings.min_latency = new_value; } - subclass::Property("buffer-duration", ..) => { + "buffer-duration" => { let mut settings = self.settings.lock().unwrap(); let new_value = value.get_some().expect("type checked upstream"); gst_info!( @@ -509,47 +450,45 @@ impl ObjectImpl for FallbackSrc { // Called whenever a value of a property is read. It can be called // at any time from any thread. #[allow(clippy::blocks_in_if_conditions)] - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("enable-audio", ..) => { + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "enable-audio" => { let settings = self.settings.lock().unwrap(); settings.enable_audio.to_value() } - subclass::Property("enable-video", ..) => { + "enable-video" => { let settings = self.settings.lock().unwrap(); settings.enable_video.to_value() } - subclass::Property("uri", ..) => { + "uri" => { let settings = self.settings.lock().unwrap(); settings.uri.to_value() } - subclass::Property("source", ..) => { + "source" => { let settings = self.settings.lock().unwrap(); settings.source.to_value() } - subclass::Property("fallback-uri", ..) => { + "fallback-uri" => { let settings = self.settings.lock().unwrap(); settings.fallback_uri.to_value() } - subclass::Property("timeout", ..) => { + "timeout" => { let settings = self.settings.lock().unwrap(); settings.timeout.to_value() } - subclass::Property("restart-timeout", ..) => { + "restart-timeout" => { let settings = self.settings.lock().unwrap(); settings.restart_timeout.to_value() } - subclass::Property("retry-timeout", ..) => { + "retry-timeout" => { let settings = self.settings.lock().unwrap(); settings.retry_timeout.to_value() } - subclass::Property("restart-on-eos", ..) => { + "restart-on-eos" => { let settings = self.settings.lock().unwrap(); settings.restart_on_eos.to_value() } - subclass::Property("status", ..) => { + "status" => { let state_guard = self.state.lock().unwrap(); // If we have no state then we'r stopped @@ -601,19 +540,42 @@ impl ObjectImpl for FallbackSrc { // Otherwise we're running now Status::Running.to_value() } - subclass::Property("min-latency", ..) => { + "min-latency" => { let settings = self.settings.lock().unwrap(); settings.min_latency.to_value() } - subclass::Property("buffer-duration", ..) => { + "buffer-duration" => { let settings = self.settings.lock().unwrap(); settings.buffer_duration.to_value() } - subclass::Property("statistics", ..) => self.get_stats().to_value(), + "statistics" => self.get_stats().to_value(), _ => unimplemented!(), } } + fn signals() -> &'static [glib::subclass::Signal] { + static SIGNALS: Lazy<Vec<glib::subclass::Signal>> = Lazy::new(|| { + vec![glib::subclass::Signal::builder( + "update-uri", + &[String::static_type()], + String::static_type(), + ) + .action() + .class_handler(|_token, args| { + // Simply return the input by default + Some(args[1].clone()) + }) + .accumulator(|_hint, ret, value| { + // First signal handler wins + *ret = value.clone(); + false + }) + .build()] + }); + + SIGNALS.as_ref() + } + fn constructed(&self, obj: &Self::Type) { self.parent_constructed(obj); @@ -624,6 +586,43 @@ impl ObjectImpl for FallbackSrc { } impl ElementImpl for FallbackSrc { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Fallback Source", + "Generic/Source", + "Live source with uridecodebin3 or custom source, and fallback image stream", + "Sebastian Dröge <sebastian@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let audio_src_pad_template = gst::PadTemplate::new( + "audio", + gst::PadDirection::Src, + gst::PadPresence::Sometimes, + &gst::Caps::new_any(), + ) + .unwrap(); + + let video_src_pad_template = gst::PadTemplate::new( + "video", + gst::PadDirection::Src, + gst::PadPresence::Sometimes, + &gst::Caps::new_any(), + ) + .unwrap(); + + vec![audio_src_pad_template, video_src_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + #[allow(clippy::single_match)] fn change_state( &self, diff --git a/utils/fallbackswitch/src/fallbacksrc/video_fallback/imp.rs b/utils/fallbackswitch/src/fallbacksrc/video_fallback/imp.rs index 02892a35f..20f2964ed 100644 --- a/utils/fallbackswitch/src/fallbacksrc/video_fallback/imp.rs +++ b/utils/fallbackswitch/src/fallbacksrc/video_fallback/imp.rs @@ -35,29 +35,6 @@ static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| { ) }); -static PROPERTIES: [subclass::Property; 2] = [ - subclass::Property("uri", |name| { - glib::ParamSpec::string( - name, - "URI", - "URI to use for video in case the main stream doesn't work", - None, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("min-latency", |name| { - glib::ParamSpec::uint64( - name, - "Minimum Latency", - "Minimum Latency", - 0, - std::u64::MAX, - 0, - glib::ParamFlags::READWRITE, - ) - }), -]; - #[derive(Debug, Clone)] struct Settings { uri: Option<String>, @@ -89,6 +66,7 @@ impl ObjectSubclass for VideoFallbackSource { const NAME: &'static str = "FallbackSrcVideoFallbackSource"; type Type = super::VideoFallbackSource; type ParentType = gst::Bin; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -105,26 +83,43 @@ impl ObjectSubclass for VideoFallbackSource { settings: Mutex::new(Settings::default()), } } - - fn class_init(klass: &mut Self::Class) { - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &gst::Caps::new_any(), - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - klass.install_properties(&PROPERTIES); - } } impl ObjectImpl for VideoFallbackSource { - fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::string( + "uri", + "URI", + "URI to use for video in case the main stream doesn't work", + None, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint64( + "min-latency", + "Minimum Latency", + "Minimum Latency", + 0, + std::u64::MAX, + 0, + glib::ParamFlags::READWRITE, + ), + ] + }); + + PROPERTIES.as_ref() + } - match *prop { - subclass::Property("uri", ..) => { + fn set_property( + &self, + obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "uri" => { let mut settings = self.settings.lock().unwrap(); let new_value = value.get().expect("type checked upstream"); gst_info!( @@ -136,7 +131,7 @@ impl ObjectImpl for VideoFallbackSource { ); settings.uri = new_value; } - subclass::Property("min-latency", ..) => { + "min-latency" => { let mut settings = self.settings.lock().unwrap(); let new_value = value.get_some().expect("type checked upstream"); gst_info!( @@ -152,15 +147,13 @@ impl ObjectImpl for VideoFallbackSource { } } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("uri", ..) => { + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "uri" => { let settings = self.settings.lock().unwrap(); settings.uri.to_value() } - subclass::Property("min-latency", ..) => { + "min-latency" => { let settings = self.settings.lock().unwrap(); settings.min_latency.to_value() } @@ -178,6 +171,22 @@ impl ObjectImpl for VideoFallbackSource { } impl ElementImpl for VideoFallbackSource { + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &gst::Caps::new_any(), + ) + .unwrap(); + + vec![src_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + #[allow(clippy::single_match)] fn change_state( &self, diff --git a/utils/fallbackswitch/src/fallbackswitch/imp.rs b/utils/fallbackswitch/src/fallbackswitch/imp.rs index 93a125708..39350b1a0 100644 --- a/utils/fallbackswitch/src/fallbackswitch/imp.rs +++ b/utils/fallbackswitch/src/fallbackswitch/imp.rs @@ -123,58 +123,6 @@ impl Default for Settings { } } -static PROPERTIES: [subclass::Property; 5] = [ - subclass::Property("timeout", |name| { - glib::ParamSpec::uint64( - name, - "Timeout", - "Timeout in nanoseconds", - 0, - std::u64::MAX, - DEFAULT_TIMEOUT, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("active-pad", |name| { - glib::ParamSpec::object( - name, - "Active Pad", - "Currently active pad. Writes are ignored if auto-switch=true", - gst::Pad::static_type(), - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("auto-switch", |name| { - glib::ParamSpec::boolean( - name, - "Automatically switch pads", - "Automatically switch pads (If true, prefer primary sink, otherwise manual selection via the active-pad property)", - DEFAULT_AUTO_SWITCH, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("primary-health", |name| { - glib::ParamSpec::enum_( - name, - "Primary stream state", - "Reports the health of the primary stream on the sink pad", - StreamHealth::static_type(), - DEFAULT_STREAM_HEALTH as i32, - glib::ParamFlags::READABLE, - ) - }), - subclass::Property("fallback-health", |name| { - glib::ParamSpec::enum_( - name, - "Fallback stream state", - "Reports the health of the fallback stream on the fallback_sink pad", - StreamHealth::static_type(), - DEFAULT_STREAM_HEALTH as i32, - glib::ParamFlags::READABLE, - ) - }), -]; - impl OutputState { fn get_health( &self, @@ -697,6 +645,7 @@ impl ObjectSubclass for FallbackSwitch { const NAME: &'static str = "FallbackSwitch"; type Type = super::FallbackSwitch; type ParentType = gst_base::Aggregator; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -717,62 +666,72 @@ impl ObjectSubclass for FallbackSwitch { settings: Mutex::new(Settings::default()), } } +} - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Fallback Switch", - "Generic", - "Allows switching to a fallback input after a given timeout", - "Sebastian Dröge <sebastian@centricular.com>", - ); - - let caps = gst::Caps::new_any(); - let src_pad_template = gst::PadTemplate::with_gtype( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - gst_base::AggregatorPad::static_type(), - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - let sink_pad_template = gst::PadTemplate::with_gtype( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - gst_base::AggregatorPad::static_type(), - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - let fallbacksink_pad_template = gst::PadTemplate::with_gtype( - "fallback_sink", - gst::PadDirection::Sink, - gst::PadPresence::Request, - &caps, - gst_base::AggregatorPad::static_type(), - ) - .unwrap(); - klass.add_pad_template(fallbacksink_pad_template); +impl ObjectImpl for FallbackSwitch { + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::uint64( + "timeout", + "Timeout", + "Timeout in nanoseconds", + 0, + std::u64::MAX, + DEFAULT_TIMEOUT, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::object( + "active-pad", + "Active Pad", + "Currently active pad. Writes are ignored if auto-switch=true", + gst::Pad::static_type(), + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boolean( + "auto-switch", + "Automatically switch pads", + "Automatically switch pads (If true, prefer primary sink, otherwise manual selection via the active-pad property)", + DEFAULT_AUTO_SWITCH, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::enum_( + "primary-health", + "Primary stream state", + "Reports the health of the primary stream on the sink pad", + StreamHealth::static_type(), + DEFAULT_STREAM_HEALTH as i32, + glib::ParamFlags::READABLE, + ), + glib::ParamSpec::enum_( + "fallback-health", + "Fallback stream state", + "Reports the health of the fallback stream on the fallback_sink pad", + StreamHealth::static_type(), + DEFAULT_STREAM_HEALTH as i32, + glib::ParamFlags::READABLE, + ), + ] + }); - klass.install_properties(&PROPERTIES); + PROPERTIES.as_ref() } -} -impl ObjectImpl for FallbackSwitch { fn constructed(&self, obj: &Self::Type) { self.parent_constructed(obj); obj.add_pad(&self.primary_sinkpad).unwrap(); } - fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("timeout", ..) => { + fn set_property( + &self, + obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "timeout" => { let mut settings = self.settings.lock().unwrap(); let timeout = value.get_some().expect("type checked upstream"); gst_info!( @@ -785,7 +744,7 @@ impl ObjectImpl for FallbackSwitch { settings.timeout = timeout; drop(settings); } - subclass::Property("active-pad", ..) => { + "active-pad" => { let settings = self.settings.lock().unwrap(); if settings.auto_switch { gst_warning!( @@ -804,7 +763,7 @@ impl ObjectImpl for FallbackSwitch { } drop(settings); } - subclass::Property("auto-switch", ..) => { + "auto-switch" => { let mut settings = self.settings.lock().unwrap(); settings.auto_switch = value.get_some().expect("type checked upstream"); } @@ -812,27 +771,25 @@ impl ObjectImpl for FallbackSwitch { } } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("timeout", ..) => { + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "timeout" => { let settings = self.settings.lock().unwrap(); settings.timeout.to_value() } - subclass::Property("active-pad", ..) => { + "active-pad" => { let active_pad = self.active_sinkpad.lock().unwrap().clone(); active_pad.to_value() } - subclass::Property("auto-switch", ..) => { + "auto-switch" => { let settings = self.settings.lock().unwrap(); settings.auto_switch.to_value() } - subclass::Property("primary-health", ..) => { + "primary-health" => { let state = self.output_state.lock().unwrap(); state.primary.stream_health.to_value() } - subclass::Property("fallback-health", ..) => { + "fallback-health" => { let state = self.output_state.lock().unwrap(); state.fallback.stream_health.to_value() } @@ -842,6 +799,59 @@ impl ObjectImpl for FallbackSwitch { } impl ElementImpl for FallbackSwitch { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Fallback Switch", + "Generic", + "Allows switching to a fallback input after a given timeout", + "Sebastian Dröge <sebastian@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::new_any(); + let src_pad_template = gst::PadTemplate::with_gtype( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + gst_base::AggregatorPad::static_type(), + ) + .unwrap(); + + let sink_pad_template = gst::PadTemplate::with_gtype( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + gst_base::AggregatorPad::static_type(), + ) + .unwrap(); + + let fallbacksink_pad_template = gst::PadTemplate::with_gtype( + "fallback_sink", + gst::PadDirection::Sink, + gst::PadPresence::Request, + &caps, + gst_base::AggregatorPad::static_type(), + ) + .unwrap(); + + vec![ + src_pad_template, + sink_pad_template, + fallbacksink_pad_template, + ] + }); + + PAD_TEMPLATES.as_ref() + } + fn request_new_pad( &self, element: &Self::Type, diff --git a/utils/togglerecord/src/togglerecord/imp.rs b/utils/togglerecord/src/togglerecord/imp.rs index e61406489..37246dbef 100644 --- a/utils/togglerecord/src/togglerecord/imp.rs +++ b/utils/togglerecord/src/togglerecord/imp.rs @@ -47,27 +47,6 @@ impl Default for Settings { } } -static PROPERTIES: [subclass::Property; 2] = [ - subclass::Property("record", |name| { - glib::ParamSpec::boolean( - name, - "Record", - "Enable/disable recording", - DEFAULT_RECORD, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("recording", |name| { - glib::ParamSpec::boolean( - name, - "Recording", - "Whether recording is currently taking place", - DEFAULT_RECORD, - glib::ParamFlags::READABLE, - ) - }), -]; - #[derive(Clone)] struct Stream { sinkpad: gst::Pad, @@ -1642,6 +1621,7 @@ impl ObjectSubclass for ToggleRecord { const NAME: &'static str = "RsToggleRecord"; type Type = super::ToggleRecord; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -1720,62 +1700,41 @@ impl ObjectSubclass for ToggleRecord { pads: Mutex::new(pads), } } - - fn class_init(klass: &mut Self::Class) { - klass.install_properties(&PROPERTIES); - - klass.set_metadata( - "Toggle Record", - "Generic", - "Valve that ensures multiple streams start/end at the same time", - "Sebastian Dröge <sebastian@centricular.com>", - ); - - let caps = gst::Caps::new_any(); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - let src_pad_template = gst::PadTemplate::new( - "src_%u", - gst::PadDirection::Src, - gst::PadPresence::Sometimes, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - let sink_pad_template = gst::PadTemplate::new( - "sink_%u", - gst::PadDirection::Sink, - gst::PadPresence::Request, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - } } impl ObjectImpl for ToggleRecord { - fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::boolean( + "record", + "Record", + "Enable/disable recording", + DEFAULT_RECORD, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boolean( + "recording", + "Recording", + "Whether recording is currently taking place", + DEFAULT_RECORD, + glib::ParamFlags::READABLE, + ), + ] + }); + + PROPERTIES.as_ref() + } - match *prop { - subclass::Property("record", ..) => { + fn set_property( + &self, + obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "record" => { let mut settings = self.settings.lock(); let record = value.get_some().expect("type checked upstream"); gst_debug!( @@ -1792,15 +1751,13 @@ impl ObjectImpl for ToggleRecord { } } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("record", ..) => { + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "record" => { let settings = self.settings.lock(); settings.record.to_value() } - subclass::Property("recording", ..) => { + "recording" => { let rec_state = self.state.lock(); (rec_state.recording_state == RecordingState::Recording).to_value() } @@ -1817,6 +1774,65 @@ impl ObjectImpl for ToggleRecord { } impl ElementImpl for ToggleRecord { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Toggle Record", + "Generic", + "Valve that ensures multiple streams start/end at the same time", + "Sebastian Dröge <sebastian@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::new_any(); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + let secondary_src_pad_template = gst::PadTemplate::new( + "src_%u", + gst::PadDirection::Src, + gst::PadPresence::Sometimes, + &caps, + ) + .unwrap(); + + let secondary_sink_pad_template = gst::PadTemplate::new( + "sink_%u", + gst::PadDirection::Sink, + gst::PadPresence::Request, + &caps, + ) + .unwrap(); + + vec![ + src_pad_template, + sink_pad_template, + secondary_src_pad_template, + secondary_sink_pad_template, + ] + }); + + PAD_TEMPLATES.as_ref() + } + fn change_state( &self, element: &Self::Type, diff --git a/video/cdg/src/cdgdec/imp.rs b/video/cdg/src/cdgdec/imp.rs index 64cbbbf5e..6893478ce 100644 --- a/video/cdg/src/cdgdec/imp.rs +++ b/video/cdg/src/cdgdec/imp.rs @@ -32,6 +32,7 @@ impl ObjectSubclass for CdgDec { const NAME: &'static str = "CdgDec"; type Type = super::CdgDec; type ParentType = gst_video::VideoDecoder; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -43,48 +44,58 @@ impl ObjectSubclass for CdgDec { output_info: Mutex::new(None), } } - - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "CDG decoder", - "Decoder/Video", - "CDG decoder", - "Guillaume Desmottes <guillaume.desmottes@collabora.com>", - ); - - let sink_caps = gst::Caps::new_simple("video/x-cdg", &[("parsed", &true)]); - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &sink_caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - let src_caps = gst::Caps::new_simple( - "video/x-raw", - &[ - ("format", &gst_video::VideoFormat::Rgba.to_str()), - ("width", &(CDG_WIDTH as i32)), - ("height", &(CDG_HEIGHT as i32)), - ("framerate", &gst::Fraction::new(0, 1)), - ], - ); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &src_caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - } } impl ObjectImpl for CdgDec {} -impl ElementImpl for CdgDec {} +impl ElementImpl for CdgDec { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "CDG decoder", + "Decoder/Video", + "CDG decoder", + "Guillaume Desmottes <guillaume.desmottes@collabora.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let sink_caps = gst::Caps::new_simple("video/x-cdg", &[("parsed", &true)]); + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &sink_caps, + ) + .unwrap(); + + let src_caps = gst::Caps::new_simple( + "video/x-raw", + &[ + ("format", &gst_video::VideoFormat::Rgba.to_str()), + ("width", &(CDG_WIDTH as i32)), + ("height", &(CDG_HEIGHT as i32)), + ("framerate", &gst::Fraction::new(0, 1)), + ], + ); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &src_caps, + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } +} impl VideoDecoderImpl for CdgDec { fn start(&self, element: &Self::Type) -> Result<(), gst::ErrorMessage> { diff --git a/video/cdg/src/cdgparse/imp.rs b/video/cdg/src/cdgparse/imp.rs index 697575b59..756dee536 100644 --- a/video/cdg/src/cdgparse/imp.rs +++ b/video/cdg/src/cdgparse/imp.rs @@ -39,6 +39,7 @@ impl ObjectSubclass for CdgParse { const NAME: &'static str = "CdgParse"; type Type = super::CdgParse; type ParentType = gst_base::BaseParse; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -47,48 +48,58 @@ impl ObjectSubclass for CdgParse { fn new() -> Self { Self } - - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "CDG parser", - "Codec/Parser/Video", - "CDG parser", - "Guillaume Desmottes <guillaume.desmottes@collabora.com>", - ); - - let sink_caps = gst::Caps::new_simple("video/x-cdg", &[]); - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &sink_caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - let src_caps = gst::Caps::new_simple( - "video/x-cdg", - &[ - ("width", &(CDG_WIDTH as i32)), - ("height", &(CDG_HEIGHT as i32)), - ("framerate", &gst::Fraction::new(0, 1)), - ("parsed", &true), - ], - ); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &src_caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - } } impl ObjectImpl for CdgParse {} -impl ElementImpl for CdgParse {} +impl ElementImpl for CdgParse { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "CDG parser", + "Codec/Parser/Video", + "CDG parser", + "Guillaume Desmottes <guillaume.desmottes@collabora.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let sink_caps = gst::Caps::new_simple("video/x-cdg", &[]); + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &sink_caps, + ) + .unwrap(); + + let src_caps = gst::Caps::new_simple( + "video/x-cdg", + &[ + ("width", &(CDG_WIDTH as i32)), + ("height", &(CDG_HEIGHT as i32)), + ("framerate", &gst::Fraction::new(0, 1)), + ("parsed", &true), + ], + ); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &src_caps, + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } +} fn bytes_to_time(bytes: Bytes) -> gst::ClockTime { match bytes { diff --git a/video/closedcaption/src/ccdetect/imp.rs b/video/closedcaption/src/ccdetect/imp.rs index ae736de70..6506a1b2a 100644 --- a/video/closedcaption/src/ccdetect/imp.rs +++ b/video/closedcaption/src/ccdetect/imp.rs @@ -77,38 +77,6 @@ pub struct CCDetect { state: Mutex<Option<State>>, } -static PROPERTIES: [subclass::Property; 3] = [ - subclass::Property("window", |name| { - glib::ParamSpec::uint64( - name, - "Window", - "Window of time (in ns) to determine if captions exist in the stream", - 0, - u64::MAX, - DEFAULT_WINDOW, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("cc608", |name| { - glib::ParamSpec::boolean( - name, - "cc608", - "Whether CEA608 captions (CC1/CC3) have been detected", - DEFAULT_CC608, - glib::ParamFlags::READABLE, - ) - }), - subclass::Property("cc708", |name| { - glib::ParamSpec::boolean( - name, - "cc608", - "Whether CEA708 captions (cc_data) have been detected", - DEFAULT_CC708, - glib::ParamFlags::READABLE, - ) - }), -]; - #[derive(Debug, Clone, Copy)] struct CCPacketContents { cc608: bool, @@ -387,6 +355,7 @@ impl ObjectSubclass for CCDetect { const NAME: &'static str = "CCDetect"; type Type = super::CCDetect; type ParentType = gst_base::BaseTransform; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -398,58 +367,50 @@ impl ObjectSubclass for CCDetect { state: Mutex::new(None), } } - - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Closed Caption Detect", - "Filter/Video/ClosedCaption/Detect", - "Detect if valid closed captions are present in a stream", - "Matthew Waters <matthew@centricular.com>", - ); - - let mut caps = gst::Caps::new_empty(); - { - let caps = caps.get_mut().unwrap(); - let s = gst::Structure::builder("closedcaption/x-cea-708") - .field("format", &gst::List::new(&[&"cc_data", &"cdp"])) - .build(); - caps.append_structure(s); - } - - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - klass.install_properties(&PROPERTIES); - - klass.configure( - gst_base::subclass::BaseTransformMode::AlwaysInPlace, - true, - true, - ); - } } impl ObjectImpl for CCDetect { - fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::uint64( + "window", + "Window", + "Window of time (in ns) to determine if captions exist in the stream", + 0, + u64::MAX, + DEFAULT_WINDOW, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boolean( + "cc608", + "cc608", + "Whether CEA608 captions (CC1/CC3) have been detected", + DEFAULT_CC608, + glib::ParamFlags::READABLE, + ), + glib::ParamSpec::boolean( + "cc708", + "cc608", + "Whether CEA708 captions (cc_data) have been detected", + DEFAULT_CC708, + glib::ParamFlags::READABLE, + ), + ] + }); + + PROPERTIES.as_ref() + } - match *prop { - subclass::Property("window", ..) => { + fn set_property( + &self, + _obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "window" => { let mut settings = self.settings.lock().unwrap(); settings.window = value.get_some().expect("type checked upstream"); } @@ -457,19 +418,17 @@ impl ObjectImpl for CCDetect { } } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("window", ..) => { + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "window" => { let settings = self.settings.lock().unwrap(); settings.window.to_value() } - subclass::Property("cc608", ..) => { + "cc608" => { let settings = self.settings.lock().unwrap(); settings.cc608.to_value() } - subclass::Property("cc708", ..) => { + "cc708" => { let settings = self.settings.lock().unwrap(); settings.cc708.to_value() } @@ -478,9 +437,60 @@ impl ObjectImpl for CCDetect { } } -impl ElementImpl for CCDetect {} +impl ElementImpl for CCDetect { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Closed Caption Detect", + "Filter/Video/ClosedCaption/Detect", + "Detect if valid closed captions are present in a stream", + "Matthew Waters <matthew@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let mut caps = gst::Caps::new_empty(); + { + let caps = caps.get_mut().unwrap(); + let s = gst::Structure::builder("closedcaption/x-cea-708") + .field("format", &gst::List::new(&[&"cc_data", &"cdp"])) + .build(); + caps.append_structure(s); + } + + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } +} impl BaseTransformImpl for CCDetect { + const MODE: gst_base::subclass::BaseTransformMode = + gst_base::subclass::BaseTransformMode::AlwaysInPlace; + const TRANSFORM_IP_ON_PASSTHROUGH: bool = true; + const PASSTHROUGH_ON_SAME_CAPS: bool = true; + fn transform_ip_passthrough( &self, element: &Self::Type, diff --git a/video/closedcaption/src/cea608overlay/imp.rs b/video/closedcaption/src/cea608overlay/imp.rs index e40016735..17530446b 100644 --- a/video/closedcaption/src/cea608overlay/imp.rs +++ b/video/closedcaption/src/cea608overlay/imp.rs @@ -388,6 +388,7 @@ impl ObjectSubclass for Cea608Overlay { const NAME: &'static str = "RsCea608Overlay"; type Type = super::Cea608Overlay; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -424,38 +425,6 @@ impl ObjectSubclass for Cea608Overlay { state: Mutex::new(State::default()), } } - - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Cea 608 overlay", - "Video/Overlay/Subtitle", - "Renders CEA 608 closed caption meta over raw video frames", - "Mathieu Duponchelle <mathieu@centricular.com>", - ); - - let caps = gst_video::VideoFormat::iter_raw() - .into_video_caps() - .unwrap() - .build(); - - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - } } impl ObjectImpl for Cea608Overlay { @@ -468,6 +437,48 @@ impl ObjectImpl for Cea608Overlay { } impl ElementImpl for Cea608Overlay { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Cea 608 overlay", + "Video/Overlay/Subtitle", + "Renders CEA 608 closed caption meta over raw video frames", + "Mathieu Duponchelle <mathieu@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst_video::VideoFormat::iter_raw() + .into_video_caps() + .unwrap() + .build(); + + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + fn change_state( &self, element: &Self::Type, diff --git a/video/closedcaption/src/cea608tott/imp.rs b/video/closedcaption/src/cea608tott/imp.rs index b32745e46..cbd4e35cd 100644 --- a/video/closedcaption/src/cea608tott/imp.rs +++ b/video/closedcaption/src/cea608tott/imp.rs @@ -375,6 +375,7 @@ impl ObjectSubclass for Cea608ToTt { const NAME: &'static str = "Cea608ToTt"; type Type = super::Cea608ToTt; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -411,56 +412,6 @@ impl ObjectSubclass for Cea608ToTt { state: AtomicRefCell::new(State::default()), } } - - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "CEA-608 to TT", - "Generic", - "Converts CEA-608 Closed Captions to SRT/VTT timed text", - "Sebastian Dröge <sebastian@centricular.com>", - ); - - let mut caps = gst::Caps::new_empty(); - { - let caps = caps.get_mut().unwrap(); - - // WebVTT - let s = gst::Structure::builder("application/x-subtitle-vtt").build(); - caps.append_structure(s); - - // SRT - let s = gst::Structure::builder("application/x-subtitle").build(); - caps.append_structure(s); - - // Raw timed text - let s = gst::Structure::builder("text/x-raw") - .field("format", &"utf8") - .build(); - caps.append_structure(s); - } - - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - let caps = gst::Caps::builder("closedcaption/x-cea-608") - .field("format", &"raw") - .build(); - - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - } } impl ObjectImpl for Cea608ToTt { @@ -473,6 +424,66 @@ impl ObjectImpl for Cea608ToTt { } impl ElementImpl for Cea608ToTt { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "CEA-608 to TT", + "Generic", + "Converts CEA-608 Closed Captions to SRT/VTT timed text", + "Sebastian Dröge <sebastian@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let mut caps = gst::Caps::new_empty(); + { + let caps = caps.get_mut().unwrap(); + + // WebVTT + let s = gst::Structure::builder("application/x-subtitle-vtt").build(); + caps.append_structure(s); + + // SRT + let s = gst::Structure::builder("application/x-subtitle").build(); + caps.append_structure(s); + + // Raw timed text + let s = gst::Structure::builder("text/x-raw") + .field("format", &"utf8") + .build(); + caps.append_structure(s); + } + + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + let caps = gst::Caps::builder("closedcaption/x-cea-608") + .field("format", &"raw") + .build(); + + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + fn change_state( &self, element: &Self::Type, diff --git a/video/closedcaption/src/mcc_enc/imp.rs b/video/closedcaption/src/mcc_enc/imp.rs index dd63bba59..e76eebb74 100644 --- a/video/closedcaption/src/mcc_enc/imp.rs +++ b/video/closedcaption/src/mcc_enc/imp.rs @@ -69,27 +69,6 @@ impl Default for Settings { } } -static PROPERTIES: [subclass::Property; 2] = [ - subclass::Property("uuid", |name| { - glib::ParamSpec::string( - name, - "UUID", - "UUID for the output file", - None, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("creation-date", |name| { - glib::ParamSpec::boxed( - name, - "Creation Date", - "Creation date for the output file", - glib::DateTime::static_type(), - glib::ParamFlags::READWRITE, - ) - }), -]; - pub struct MccEnc { srcpad: gst::Pad, sinkpad: gst::Pad, @@ -470,6 +449,7 @@ impl ObjectSubclass for MccEnc { const NAME: &'static str = "RsMccEnc"; type Type = super::MccEnc; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -519,74 +499,45 @@ impl ObjectSubclass for MccEnc { settings: Mutex::new(Settings::default()), } } - - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Mcc Encoder", - "Encoder/ClosedCaption", - "Encodes MCC Closed Caption Files", - "Sebastian Dröge <sebastian@centricular.com>", - ); - - let mut caps = gst::Caps::new_empty(); - { - let caps = caps.get_mut().unwrap(); - - let framerates = gst::List::new(&[ - &gst::Fraction::new(24, 1), - &gst::Fraction::new(25, 1), - &gst::Fraction::new(30000, 1001), - &gst::Fraction::new(30, 1), - &gst::Fraction::new(50, 1), - &gst::Fraction::new(60000, 1001), - &gst::Fraction::new(60, 1), - ]); - - let s = gst::Structure::builder("closedcaption/x-cea-708") - .field("format", &"cdp") - .field("framerate", &framerates) - .build(); - caps.append_structure(s); - - let s = gst::Structure::builder("closedcaption/x-cea-608") - .field("format", &"s334-1a") - .field("framerate", &framerates) - .build(); - caps.append_structure(s); - } - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - let caps = gst::Caps::builder("application/x-mcc").build(); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - klass.install_properties(&PROPERTIES); - } } impl ObjectImpl for MccEnc { - fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::string( + "uuid", + "UUID", + "UUID for the output file", + None, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::boxed( + "creation-date", + "Creation Date", + "Creation date for the output file", + glib::DateTime::static_type(), + glib::ParamFlags::READWRITE, + ), + ] + }); + + PROPERTIES.as_ref() + } - match *prop { - subclass::Property("uuid", ..) => { + fn set_property( + &self, + _obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "uuid" => { let mut settings = self.settings.lock().unwrap(); settings.uuid = value.get().expect("type checked upstream"); } - subclass::Property("creation-date", ..) => { + "creation-date" => { let mut settings = self.settings.lock().unwrap(); settings.creation_date = value.get().expect("type checked upstream"); } @@ -594,15 +545,13 @@ impl ObjectImpl for MccEnc { } } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("uuid", ..) => { + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "uuid" => { let settings = self.settings.lock().unwrap(); settings.uuid.to_value() } - subclass::Property("creation-date", ..) => { + "creation-date" => { let settings = self.settings.lock().unwrap(); settings.creation_date.to_value() } @@ -619,6 +568,70 @@ impl ObjectImpl for MccEnc { } impl ElementImpl for MccEnc { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Mcc Encoder", + "Encoder/ClosedCaption", + "Encodes MCC Closed Caption Files", + "Sebastian Dröge <sebastian@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let mut caps = gst::Caps::new_empty(); + { + let caps = caps.get_mut().unwrap(); + + let framerates = gst::List::new(&[ + &gst::Fraction::new(24, 1), + &gst::Fraction::new(25, 1), + &gst::Fraction::new(30000, 1001), + &gst::Fraction::new(30, 1), + &gst::Fraction::new(50, 1), + &gst::Fraction::new(60000, 1001), + &gst::Fraction::new(60, 1), + ]); + + let s = gst::Structure::builder("closedcaption/x-cea-708") + .field("format", &"cdp") + .field("framerate", &framerates) + .build(); + caps.append_structure(s); + + let s = gst::Structure::builder("closedcaption/x-cea-608") + .field("format", &"s334-1a") + .field("framerate", &framerates) + .build(); + caps.append_structure(s); + } + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + let caps = gst::Caps::builder("application/x-mcc").build(); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + fn change_state( &self, element: &Self::Type, diff --git a/video/closedcaption/src/mcc_parse/imp.rs b/video/closedcaption/src/mcc_parse/imp.rs index 43ddfff06..2c35b33bf 100644 --- a/video/closedcaption/src/mcc_parse/imp.rs +++ b/video/closedcaption/src/mcc_parse/imp.rs @@ -1122,6 +1122,7 @@ impl ObjectSubclass for MccParse { const NAME: &'static str = "RsMccParse"; type Type = super::MccParse; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -1184,56 +1185,6 @@ impl ObjectSubclass for MccParse { state: Mutex::new(State::default()), } } - - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Mcc Parse", - "Parser/ClosedCaption", - "Parses MCC Closed Caption Files", - "Sebastian Dröge <sebastian@centricular.com>", - ); - - let mut caps = gst::Caps::new_empty(); - { - let caps = caps.get_mut().unwrap(); - let framerate = gst::FractionRange::new( - gst::Fraction::new(1, std::i32::MAX), - gst::Fraction::new(std::i32::MAX, 1), - ); - - let s = gst::Structure::builder("closedcaption/x-cea-708") - .field("format", &"cdp") - .field("framerate", &framerate) - .build(); - caps.append_structure(s); - - let s = gst::Structure::builder("closedcaption/x-cea-608") - .field("format", &"s334-1a") - .field("framerate", &framerate) - .build(); - caps.append_structure(s); - } - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - let caps = gst::Caps::builder("application/x-mcc") - .field("version", &gst::List::new(&[&1i32, &2i32])) - .build(); - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - } } impl ObjectImpl for MccParse { @@ -1246,6 +1197,66 @@ impl ObjectImpl for MccParse { } impl ElementImpl for MccParse { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Mcc Parse", + "Parser/ClosedCaption", + "Parses MCC Closed Caption Files", + "Sebastian Dröge <sebastian@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let mut caps = gst::Caps::new_empty(); + { + let caps = caps.get_mut().unwrap(); + let framerate = gst::FractionRange::new( + gst::Fraction::new(1, std::i32::MAX), + gst::Fraction::new(std::i32::MAX, 1), + ); + + let s = gst::Structure::builder("closedcaption/x-cea-708") + .field("format", &"cdp") + .field("framerate", &framerate) + .build(); + caps.append_structure(s); + + let s = gst::Structure::builder("closedcaption/x-cea-608") + .field("format", &"s334-1a") + .field("framerate", &framerate) + .build(); + caps.append_structure(s); + } + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + let caps = gst::Caps::builder("application/x-mcc") + .field("version", &gst::List::new(&[&1i32, &2i32])) + .build(); + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + fn change_state( &self, element: &Self::Type, diff --git a/video/closedcaption/src/scc_enc/imp.rs b/video/closedcaption/src/scc_enc/imp.rs index bf45bd0c2..caca4d0d5 100644 --- a/video/closedcaption/src/scc_enc/imp.rs +++ b/video/closedcaption/src/scc_enc/imp.rs @@ -337,6 +337,7 @@ impl ObjectSubclass for SccEnc { const NAME: &'static str = "RsSccEnc"; type Type = super::SccEnc; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -385,40 +386,6 @@ impl ObjectSubclass for SccEnc { state: Mutex::new(State::default()), } } - - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Scc Encoder", - "Encoder/ClosedCaption", - "Encodes SCC Closed Caption Files", - "Sebastian Dröge <sebastian@centricular.com>, Jordan Petridis <jordan@centricular.com>", - ); - - let framerates = - gst::List::new(&[&gst::Fraction::new(30000, 1001), &gst::Fraction::new(30, 1)]); - let caps = gst::Caps::builder("closedcaption/x-cea-608") - .field("format", &"raw") - .field("framerate", &framerates) - .build(); - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - let caps = gst::Caps::builder("application/x-scc").build(); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - } } impl ObjectImpl for SccEnc { @@ -431,6 +398,50 @@ impl ObjectImpl for SccEnc { } impl ElementImpl for SccEnc { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Scc Encoder", + "Encoder/ClosedCaption", + "Encodes SCC Closed Caption Files", + "Sebastian Dröge <sebastian@centricular.com>, Jordan Petridis <jordan@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let framerates = + gst::List::new(&[&gst::Fraction::new(30000, 1001), &gst::Fraction::new(30, 1)]); + let caps = gst::Caps::builder("closedcaption/x-cea-608") + .field("format", &"raw") + .field("framerate", &framerates) + .build(); + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + let caps = gst::Caps::builder("application/x-scc").build(); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + fn change_state( &self, element: &Self::Type, diff --git a/video/closedcaption/src/scc_parse/imp.rs b/video/closedcaption/src/scc_parse/imp.rs index b22562a80..8f5a2d576 100644 --- a/video/closedcaption/src/scc_parse/imp.rs +++ b/video/closedcaption/src/scc_parse/imp.rs @@ -1002,6 +1002,7 @@ impl ObjectSubclass for SccParse { const NAME: &'static str = "RsSccParse"; type Type = super::SccParse; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -1064,41 +1065,6 @@ impl ObjectSubclass for SccParse { state: Mutex::new(State::default()), } } - - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Scc Parse", - "Parser/ClosedCaption", - "Parses SCC Closed Caption Files", - "Sebastian Dröge <sebastian@centricular.com>, Jordan Petridis <jordan@centricular.com>", - ); - - let caps = gst::Caps::builder("closedcaption/x-cea-608") - .field("format", &"raw") - .field( - "framerate", - &gst::List::new(&[&gst::Fraction::new(30000, 1001), &gst::Fraction::new(30, 1)]), - ) - .build(); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - let caps = gst::Caps::builder("application/x-scc").build(); - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - } } impl ObjectImpl for SccParse { @@ -1111,6 +1077,54 @@ impl ObjectImpl for SccParse { } impl ElementImpl for SccParse { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Scc Parse", + "Parser/ClosedCaption", + "Parses SCC Closed Caption Files", + "Sebastian Dröge <sebastian@centricular.com>, Jordan Petridis <jordan@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::builder("closedcaption/x-cea-608") + .field("format", &"raw") + .field( + "framerate", + &gst::List::new(&[ + &gst::Fraction::new(30000, 1001), + &gst::Fraction::new(30, 1), + ]), + ) + .build(); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + let caps = gst::Caps::builder("application/x-scc").build(); + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + fn change_state( &self, element: &Self::Type, diff --git a/video/closedcaption/src/tttocea608/imp.rs b/video/closedcaption/src/tttocea608/imp.rs index ebb9aa731..cab504e4e 100644 --- a/video/closedcaption/src/tttocea608/imp.rs +++ b/video/closedcaption/src/tttocea608/imp.rs @@ -95,17 +95,6 @@ const DEFAULT_FPS_D: i32 = 1; const DEFAULT_MODE: Cea608Mode = Cea608Mode::RollUp2; -static PROPERTIES: [subclass::Property; 1] = [subclass::Property("mode", |name| { - glib::ParamSpec::enum_( - name, - "Mode", - "Which mode to operate in", - Cea608Mode::static_type(), - DEFAULT_MODE as i32, - glib::ParamFlags::READWRITE, - ) -})]; - #[derive(Debug, Clone)] struct Settings { mode: Cea608Mode, @@ -958,6 +947,7 @@ impl ObjectSubclass for TtToCea608 { const NAME: &'static str = "TtToCea608"; type Type = super::TtToCea608; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -995,61 +985,24 @@ impl ObjectSubclass for TtToCea608 { settings: Mutex::new(Settings::default()), } } - - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "TT to CEA-608", - "Generic", - "Converts timed text to CEA-608 Closed Captions", - "Mathieu Duponchelle <mathieu@centricular.com>", - ); - - let mut caps = gst::Caps::new_empty(); - { - let caps = caps.get_mut().unwrap(); - - let s = gst::Structure::new_empty("text/x-raw"); - caps.append_structure(s); - - let s = gst::Structure::builder("application/x-json") - .field("format", &"cea608") - .build(); - caps.append_structure(s); - } - - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - let framerate = gst::FractionRange::new( - gst::Fraction::new(1, std::i32::MAX), - gst::Fraction::new(std::i32::MAX, 1), - ); - - let caps = gst::Caps::builder("closedcaption/x-cea-608") - .field("format", &"raw") - .field("framerate", &framerate) - .build(); - - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - klass.install_properties(&PROPERTIES); - } } impl ObjectImpl for TtToCea608 { + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![glib::ParamSpec::enum_( + "mode", + "Mode", + "Which mode to operate in", + Cea608Mode::static_type(), + DEFAULT_MODE as i32, + glib::ParamFlags::READWRITE, + )] + }); + + PROPERTIES.as_ref() + } + fn constructed(&self, obj: &Self::Type) { self.parent_constructed(obj); @@ -1057,11 +1010,15 @@ impl ObjectImpl for TtToCea608 { obj.add_pad(&self.srcpad).unwrap(); } - fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("mode", ..) => { + fn set_property( + &self, + _obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "mode" => { let mut settings = self.settings.lock().unwrap(); settings.mode = value .get_some::<Cea608Mode>() @@ -1071,11 +1028,9 @@ impl ObjectImpl for TtToCea608 { } } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("mode", ..) => { + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "mode" => { let settings = self.settings.lock().unwrap(); settings.mode.to_value() } @@ -1085,6 +1040,66 @@ impl ObjectImpl for TtToCea608 { } impl ElementImpl for TtToCea608 { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "TT to CEA-608", + "Generic", + "Converts timed text to CEA-608 Closed Captions", + "Mathieu Duponchelle <mathieu@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let mut caps = gst::Caps::new_empty(); + { + let caps = caps.get_mut().unwrap(); + + let s = gst::Structure::new_empty("text/x-raw"); + caps.append_structure(s); + + let s = gst::Structure::builder("application/x-json") + .field("format", &"cea608") + .build(); + caps.append_structure(s); + } + + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + let framerate = gst::FractionRange::new( + gst::Fraction::new(1, std::i32::MAX), + gst::Fraction::new(std::i32::MAX, 1), + ); + + let caps = gst::Caps::builder("closedcaption/x-cea-608") + .field("format", &"raw") + .field("framerate", &framerate) + .build(); + + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } + fn change_state( &self, element: &Self::Type, diff --git a/video/closedcaption/src/tttojson/imp.rs b/video/closedcaption/src/tttojson/imp.rs index c31664cd1..659df5bb4 100644 --- a/video/closedcaption/src/tttojson/imp.rs +++ b/video/closedcaption/src/tttojson/imp.rs @@ -37,17 +37,6 @@ static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| { const DEFAULT_MODE: Cea608Mode = Cea608Mode::RollUp2; -static PROPERTIES: [subclass::Property; 1] = [subclass::Property("mode", |name| { - glib::ParamSpec::enum_( - name, - "Mode", - "Which mode to operate in", - Cea608Mode::static_type(), - DEFAULT_MODE as i32, - glib::ParamFlags::READWRITE, - ) -})]; - #[derive(Debug, Clone)] struct Settings { mode: Cea608Mode, @@ -156,12 +145,54 @@ impl TtToJson { } } -impl ElementImpl for TtToJson {} +impl ElementImpl for TtToJson { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Timed text to JSON encoder", + "Encoder/ClosedCaption", + "Encodes Timed Text to JSON", + "Mathieu Duponchelle <mathieu@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::builder("text/x-raw") + .field("format", &"utf8") + .build(); + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + let caps = gst::Caps::builder("application/x-json").build(); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } +} impl ObjectSubclass for TtToJson { const NAME: &'static str = "RsTtToJson"; type Type = super::TtToJson; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -195,42 +226,24 @@ impl ObjectSubclass for TtToJson { settings: Mutex::new(Settings::default()), } } - - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Timed text to JSON encoder", - "Encoder/ClosedCaption", - "Encodes Timed Text to JSON", - "Mathieu Duponchelle <mathieu@centricular.com>", - ); - - let caps = gst::Caps::builder("text/x-raw") - .field("format", &"utf8") - .build(); - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - let caps = gst::Caps::builder("application/x-json").build(); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - klass.install_properties(&PROPERTIES); - } } impl ObjectImpl for TtToJson { + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![glib::ParamSpec::enum_( + "mode", + "Mode", + "Which mode to operate in", + Cea608Mode::static_type(), + DEFAULT_MODE as i32, + glib::ParamFlags::READWRITE, + )] + }); + + PROPERTIES.as_ref() + } + fn constructed(&self, obj: &Self::Type) { self.parent_constructed(obj); @@ -238,11 +251,15 @@ impl ObjectImpl for TtToJson { obj.add_pad(&self.srcpad).unwrap(); } - fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("mode", ..) => { + fn set_property( + &self, + _obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "mode" => { let mut settings = self.settings.lock().unwrap(); settings.mode = value .get_some::<Cea608Mode>() @@ -252,11 +269,9 @@ impl ObjectImpl for TtToJson { } } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("mode", ..) => { + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "mode" => { let settings = self.settings.lock().unwrap(); settings.mode.to_value() } diff --git a/video/dav1d/src/dav1ddec/imp.rs b/video/dav1d/src/dav1ddec/imp.rs index fb1a258d0..c17501194 100644 --- a/video/dav1d/src/dav1ddec/imp.rs +++ b/video/dav1d/src/dav1ddec/imp.rs @@ -349,6 +349,7 @@ impl ObjectSubclass for Dav1dDec { const NAME: &'static str = "RsDav1dDec"; type Type = super::Dav1dDec; type ParentType = gst_video::VideoDecoder; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -364,54 +365,64 @@ impl ObjectSubclass for Dav1dDec { }), } } +} - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "Dav1d AV1 Decoder", - "Codec/Decoder/Video", - "Decode AV1 video streams with dav1d", - "Philippe Normand <philn@igalia.com>", - ); +impl ObjectImpl for Dav1dDec {} - let sink_caps = gst::Caps::new_simple("video/x-av1", &[]); - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &sink_caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - let src_caps = gst::Caps::new_simple( - "video/x-raw", - &[ - ("format", &gst::List::from_owned(video_output_formats())), - ("width", &gst::IntRange::<i32>::new(1, i32::MAX)), - ("height", &gst::IntRange::<i32>::new(1, i32::MAX)), - ( - "framerate", - &gst::FractionRange::new( - gst::Fraction::new(0, 1), - gst::Fraction::new(i32::MAX, 1), - ), - ), - ], - ); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &src_caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); +impl ElementImpl for Dav1dDec { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "Dav1d AV1 Decoder", + "Codec/Decoder/Video", + "Decode AV1 video streams with dav1d", + "Philippe Normand <philn@igalia.com>", + ) + }); + + Some(&*ELEMENT_METADATA) } -} -impl ObjectImpl for Dav1dDec {} + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let sink_caps = gst::Caps::new_simple("video/x-av1", &[]); + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &sink_caps, + ) + .unwrap(); -impl ElementImpl for Dav1dDec {} + let src_caps = gst::Caps::new_simple( + "video/x-raw", + &[ + ("format", &gst::List::from_owned(video_output_formats())), + ("width", &gst::IntRange::<i32>::new(1, i32::MAX)), + ("height", &gst::IntRange::<i32>::new(1, i32::MAX)), + ( + "framerate", + &gst::FractionRange::new( + gst::Fraction::new(0, 1), + gst::Fraction::new(i32::MAX, 1), + ), + ), + ], + ); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &src_caps, + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } +} impl VideoDecoderImpl for Dav1dDec { fn start(&self, element: &Self::Type) -> Result<(), gst::ErrorMessage> { diff --git a/video/flavors/src/flvdemux/imp.rs b/video/flavors/src/flvdemux/imp.rs index 540f6c7ea..6f4b8d553 100644 --- a/video/flavors/src/flvdemux/imp.rs +++ b/video/flavors/src/flvdemux/imp.rs @@ -124,6 +124,7 @@ impl ObjectSubclass for FlvDemux { const NAME: &'static str = "RsFlvDemux"; type Type = super::FlvDemux; type ParentType = gst::Element; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -176,100 +177,6 @@ impl ObjectSubclass for FlvDemux { flow_combiner: Mutex::new(gst_base::UniqueFlowCombiner::new()), } } - - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "FLV Demuxer", - "Codec/Demuxer", - "Demuxes FLV Streams", - "Sebastian Dröge <sebastian@centricular.com>", - ); - - let mut caps = gst::Caps::new_empty(); - { - let caps = caps.get_mut().unwrap(); - - caps.append( - gst::Caps::builder("audio/mpeg") - .field("mpegversion", &1i32) - .build(), - ); - caps.append( - gst::Caps::builder("audio/x-raw") - .field("layout", &"interleaved") - .field("format", &gst::List::new(&[&"U8", &"S16LE"])) - .build(), - ); - caps.append( - gst::Caps::builder("audio/x-adpcm") - .field("layout", &"swf") - .build(), - ); - caps.append(gst::Caps::builder("audio/x-nellymoser").build()); - caps.append(gst::Caps::builder("audio/x-alaw").build()); - caps.append(gst::Caps::builder("audio/x-mulaw").build()); - caps.append( - gst::Caps::builder("audio/mpeg") - .field("mpegversion", &4i32) - .field("framed", &true) - .field("stream-format", &"raw") - .build(), - ); - caps.append(gst::Caps::builder("audio/x-speex").build()); - } - let audiosrc_pad_template = gst::PadTemplate::new( - "audio", - gst::PadDirection::Src, - gst::PadPresence::Sometimes, - &caps, - ) - .unwrap(); - klass.add_pad_template(audiosrc_pad_template); - - let mut caps = gst::Caps::new_empty(); - { - let caps = caps.get_mut().unwrap(); - - caps.append( - gst::Caps::builder("video/x-flash-video") - .field("flvversion", &1i32) - .build(), - ); - caps.append(gst::Caps::builder("video/x-flash-screen").build()); - caps.append(gst::Caps::builder("video/x-vp6-flash").build()); - caps.append(gst::Caps::builder("video/x-vp6-flash-alpha").build()); - caps.append(gst::Caps::builder("video/x-flash-screen2").build()); - caps.append( - gst::Caps::builder("video/x-h264") - .field("stream-format", &"avc") - .build(), - ); - caps.append(gst::Caps::builder("video/x-h263").build()); - caps.append( - gst::Caps::builder("video/mpeg") - .field("mpegversion", &4i32) - .build(), - ); - } - let videosrc_pad_template = gst::PadTemplate::new( - "video", - gst::PadDirection::Src, - gst::PadPresence::Sometimes, - &caps, - ) - .unwrap(); - klass.add_pad_template(videosrc_pad_template); - - let caps = gst::Caps::builder("video/x-flv").build(); - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - } } impl ObjectImpl for FlvDemux { @@ -280,7 +187,114 @@ impl ObjectImpl for FlvDemux { } } -impl ElementImpl for FlvDemux {} +impl ElementImpl for FlvDemux { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "FLV Demuxer", + "Codec/Demuxer", + "Demuxes FLV Streams", + "Sebastian Dröge <sebastian@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let mut caps = gst::Caps::new_empty(); + { + let caps = caps.get_mut().unwrap(); + + caps.append( + gst::Caps::builder("audio/mpeg") + .field("mpegversion", &1i32) + .build(), + ); + caps.append( + gst::Caps::builder("audio/x-raw") + .field("layout", &"interleaved") + .field("format", &gst::List::new(&[&"U8", &"S16LE"])) + .build(), + ); + caps.append( + gst::Caps::builder("audio/x-adpcm") + .field("layout", &"swf") + .build(), + ); + caps.append(gst::Caps::builder("audio/x-nellymoser").build()); + caps.append(gst::Caps::builder("audio/x-alaw").build()); + caps.append(gst::Caps::builder("audio/x-mulaw").build()); + caps.append( + gst::Caps::builder("audio/mpeg") + .field("mpegversion", &4i32) + .field("framed", &true) + .field("stream-format", &"raw") + .build(), + ); + caps.append(gst::Caps::builder("audio/x-speex").build()); + } + let audiosrc_pad_template = gst::PadTemplate::new( + "audio", + gst::PadDirection::Src, + gst::PadPresence::Sometimes, + &caps, + ) + .unwrap(); + + let mut caps = gst::Caps::new_empty(); + { + let caps = caps.get_mut().unwrap(); + + caps.append( + gst::Caps::builder("video/x-flash-video") + .field("flvversion", &1i32) + .build(), + ); + caps.append(gst::Caps::builder("video/x-flash-screen").build()); + caps.append(gst::Caps::builder("video/x-vp6-flash").build()); + caps.append(gst::Caps::builder("video/x-vp6-flash-alpha").build()); + caps.append(gst::Caps::builder("video/x-flash-screen2").build()); + caps.append( + gst::Caps::builder("video/x-h264") + .field("stream-format", &"avc") + .build(), + ); + caps.append(gst::Caps::builder("video/x-h263").build()); + caps.append( + gst::Caps::builder("video/mpeg") + .field("mpegversion", &4i32) + .build(), + ); + } + let videosrc_pad_template = gst::PadTemplate::new( + "video", + gst::PadDirection::Src, + gst::PadPresence::Sometimes, + &caps, + ) + .unwrap(); + + let caps = gst::Caps::builder("video/x-flv").build(); + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![ + audiosrc_pad_template, + videosrc_pad_template, + sink_pad_template, + ] + }); + + PAD_TEMPLATES.as_ref() + } +} impl FlvDemux { fn sink_activate( diff --git a/video/gif/src/gifenc/imp.rs b/video/gif/src/gifenc/imp.rs index effcd020a..c97a6ee34 100644 --- a/video/gif/src/gifenc/imp.rs +++ b/video/gif/src/gifenc/imp.rs @@ -81,18 +81,6 @@ impl Default for Settings { } } -static PROPERTIES: [subclass::Property; 1] = [subclass::Property("repeat", |name| { - glib::ParamSpec::int( - name, - "Repeat", - "Repeat (-1 to loop forever, 0 .. n finite repetitions)", - -1, - std::u16::MAX as i32, - DEFAULT_REPEAT, - glib::ParamFlags::READWRITE, - ) -})]; - struct State { video_info: gst_video::VideoInfo, cache: Arc<CacheBuffer>, @@ -146,6 +134,7 @@ impl ObjectSubclass for GifEnc { const NAME: &'static str = "GifEnc"; type Type = super::GifEnc; type ParentType = gst_video::VideoEncoder; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -157,69 +146,34 @@ impl ObjectSubclass for GifEnc { settings: Mutex::new(Default::default()), } } - - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "GIF encoder", - "Encoder/Video", - "GIF encoder", - "Markus Ebner <info@ebner-markus.de>", - ); - - let sink_caps = gst::Caps::new_simple( - "video/x-raw", - &[ - ( - "format", - &gst::List::new(&[ - &gst_video::VideoFormat::Rgb.to_str(), - &gst_video::VideoFormat::Rgba.to_str(), - ]), - ), - ("width", &gst::IntRange::<i32>::new(1, std::u16::MAX as i32)), - ( - "height", - &gst::IntRange::<i32>::new(1, std::u16::MAX as i32), - ), - ( - "framerate", - &gst::FractionRange::new( - gst::Fraction::new(1, 1), - // frame-delay timing in gif is a multiple of 10ms -> max 100fps - gst::Fraction::new(100, 1), - ), - ), - ], - ); - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &sink_caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - let src_caps = gst::Caps::new_simple("image/gif", &[]); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &src_caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - klass.install_properties(&PROPERTIES); - } } impl ObjectImpl for GifEnc { - fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![glib::ParamSpec::int( + "repeat", + "Repeat", + "Repeat (-1 to loop forever, 0 .. n finite repetitions)", + -1, + std::u16::MAX as i32, + DEFAULT_REPEAT, + glib::ParamFlags::READWRITE, + )] + }); + + PROPERTIES.as_ref() + } - match *prop { - subclass::Property("repeat", ..) => { + fn set_property( + &self, + _obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "repeat" => { let mut settings = self.settings.lock().unwrap(); settings.repeat = value.get_some().expect("type checked upstream"); } @@ -227,11 +181,9 @@ impl ObjectImpl for GifEnc { } } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("repeat", ..) => { + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "repeat" => { let settings = self.settings.lock().unwrap(); settings.repeat.to_value() } @@ -240,7 +192,70 @@ impl ObjectImpl for GifEnc { } } -impl ElementImpl for GifEnc {} +impl ElementImpl for GifEnc { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "GIF encoder", + "Encoder/Video", + "GIF encoder", + "Markus Ebner <info@ebner-markus.de>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let sink_caps = gst::Caps::new_simple( + "video/x-raw", + &[ + ( + "format", + &gst::List::new(&[ + &gst_video::VideoFormat::Rgb.to_str(), + &gst_video::VideoFormat::Rgba.to_str(), + ]), + ), + ("width", &gst::IntRange::<i32>::new(1, std::u16::MAX as i32)), + ( + "height", + &gst::IntRange::<i32>::new(1, std::u16::MAX as i32), + ), + ( + "framerate", + &gst::FractionRange::new( + gst::Fraction::new(1, 1), + // frame-delay timing in gif is a multiple of 10ms -> max 100fps + gst::Fraction::new(100, 1), + ), + ), + ], + ); + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &sink_caps, + ) + .unwrap(); + + let src_caps = gst::Caps::new_simple("image/gif", &[]); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &src_caps, + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } +} impl VideoEncoderImpl for GifEnc { fn stop(&self, _element: &Self::Type) -> Result<(), gst::ErrorMessage> { diff --git a/video/hsv/src/hsvdetector/imp.rs b/video/hsv/src/hsvdetector/imp.rs index a82400fa0..03e047411 100644 --- a/video/hsv/src/hsvdetector/imp.rs +++ b/video/hsv/src/hsvdetector/imp.rs @@ -55,76 +55,6 @@ impl Default for Settings { } } -// Metadata for the properties -static PROPERTIES: [subclass::Property; 6] = [ - subclass::Property("hue-ref", |name| { - glib::ParamSpec::float( - name, - "Hue reference", - "Hue reference in degrees", - f32::MIN, - f32::MAX, - DEFAULT_HUE_REF, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("hue-var", |name| { - glib::ParamSpec::float( - name, - "Hue variation", - "Allowed hue variation from the reference hue angle, in degrees", - 0.0, - 180.0, - DEFAULT_HUE_VAR, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("saturation-ref", |name| { - glib::ParamSpec::float( - name, - "Saturation reference", - "Reference saturation value", - 0.0, - 1.0, - DEFAULT_SATURATION_REF, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("saturation-var", |name| { - glib::ParamSpec::float( - name, - "Saturation variation", - "Allowed saturation variation from the reference value", - 0.0, - 1.0, - DEFAULT_SATURATION_VAR, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("value-ref", |name| { - glib::ParamSpec::float( - name, - "Value reference", - "Reference value value", - 0.0, - 1.0, - DEFAULT_VALUE_REF, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("value-var", |name| { - glib::ParamSpec::float( - name, - "Value variation", - "Allowed value variation from the reference value", - 0.0, - 1.0, - DEFAULT_VALUE_VAR, - glib::ParamFlags::READWRITE, - ) - }), -]; - // Stream-specific state, i.e. video format configuration struct State { in_info: gst_video::VideoInfo, @@ -149,6 +79,7 @@ impl ObjectSubclass for HsvDetector { const NAME: &'static str = "HsvDetector"; type Type = super::HsvDetector; type ParentType = gst_base::BaseTransform; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -162,90 +93,81 @@ impl ObjectSubclass for HsvDetector { state: AtomicRefCell::new(None), } } +} - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "HSV detector", - "Filter/Effect/Converter/Video", - "Works within the HSV colorspace to mark positive pixels", - "Julien Bardagi <julien.bardagi@gmail.com>", - ); - - // src pad capabilities - let caps = gst::Caps::new_simple( - "video/x-raw", - &[ - ( - "format", - &gst::List::new(&[&gst_video::VideoFormat::Rgba.to_str()]), +impl ObjectImpl for HsvDetector { + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::float( + "hue-ref", + "Hue reference", + "Hue reference in degrees", + f32::MIN, + f32::MAX, + DEFAULT_HUE_REF, + glib::ParamFlags::READWRITE, ), - ("width", &gst::IntRange::<i32>::new(0, i32::MAX)), - ("height", &gst::IntRange::<i32>::new(0, i32::MAX)), - ( - "framerate", - &gst::FractionRange::new( - gst::Fraction::new(0, 1), - gst::Fraction::new(i32::MAX, 1), - ), + glib::ParamSpec::float( + "hue-var", + "Hue variation", + "Allowed hue variation from the reference hue angle, in degrees", + 0.0, + 180.0, + DEFAULT_HUE_VAR, + glib::ParamFlags::READWRITE, ), - ], - ); - - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - // sink pad capabilities - let caps = gst::Caps::new_simple( - "video/x-raw", - &[ - ( - "format", - &gst::List::new(&[&gst_video::VideoFormat::Rgbx.to_str()]), + glib::ParamSpec::float( + "saturation-ref", + "Saturation reference", + "Reference saturation value", + 0.0, + 1.0, + DEFAULT_SATURATION_REF, + glib::ParamFlags::READWRITE, ), - ("width", &gst::IntRange::<i32>::new(0, i32::MAX)), - ("height", &gst::IntRange::<i32>::new(0, i32::MAX)), - ( - "framerate", - &gst::FractionRange::new( - gst::Fraction::new(0, 1), - gst::Fraction::new(i32::MAX, 1), - ), + glib::ParamSpec::float( + "saturation-var", + "Saturation variation", + "Allowed saturation variation from the reference value", + 0.0, + 1.0, + DEFAULT_SATURATION_VAR, + glib::ParamFlags::READWRITE, ), - ], - ); + glib::ParamSpec::float( + "value-ref", + "Value reference", + "Reference value value", + 0.0, + 1.0, + DEFAULT_VALUE_REF, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::float( + "value-var", + "Value variation", + "Allowed value variation from the reference value", + 0.0, + 1.0, + DEFAULT_VALUE_VAR, + glib::ParamFlags::READWRITE, + ), + ] + }); - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - // Install all our properties - klass.install_properties(&PROPERTIES); - - klass.configure( - gst_base::subclass::BaseTransformMode::NeverInPlace, - false, - false, - ); + PROPERTIES.as_ref() } -} - -impl ObjectImpl for HsvDetector { - fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; - match *prop { - subclass::Property("hue-ref", ..) => { + fn set_property( + &self, + obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "hue-ref" => { let mut settings = self.settings.lock().unwrap(); let hue_ref = value.get_some().expect("type checked upstream"); gst_info!( @@ -257,7 +179,7 @@ impl ObjectImpl for HsvDetector { ); settings.hue_ref = hue_ref; } - subclass::Property("hue-var", ..) => { + "hue-var" => { let mut settings = self.settings.lock().unwrap(); let hue_var = value.get_some().expect("type checked upstream"); gst_info!( @@ -269,7 +191,7 @@ impl ObjectImpl for HsvDetector { ); settings.hue_var = hue_var; } - subclass::Property("saturation-ref", ..) => { + "saturation-ref" => { let mut settings = self.settings.lock().unwrap(); let saturation_ref = value.get_some().expect("type checked upstream"); gst_info!( @@ -281,7 +203,7 @@ impl ObjectImpl for HsvDetector { ); settings.saturation_ref = saturation_ref; } - subclass::Property("saturation-var", ..) => { + "saturation-var" => { let mut settings = self.settings.lock().unwrap(); let saturation_var = value.get_some().expect("type checked upstream"); gst_info!( @@ -293,7 +215,7 @@ impl ObjectImpl for HsvDetector { ); settings.saturation_var = saturation_var; } - subclass::Property("value-ref", ..) => { + "value-ref" => { let mut settings = self.settings.lock().unwrap(); let value_ref = value.get_some().expect("type checked upstream"); gst_info!( @@ -305,7 +227,7 @@ impl ObjectImpl for HsvDetector { ); settings.value_ref = value_ref; } - subclass::Property("value-var", ..) => { + "value-var" => { let mut settings = self.settings.lock().unwrap(); let value_var = value.get_some().expect("type checked upstream"); gst_info!( @@ -323,31 +245,29 @@ impl ObjectImpl for HsvDetector { // Called whenever a value of a property is read. It can be called // at any time from any thread. - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("hue-ref", ..) => { + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "hue-ref" => { let settings = self.settings.lock().unwrap(); settings.hue_ref.to_value() } - subclass::Property("hue-var", ..) => { + "hue-var" => { let settings = self.settings.lock().unwrap(); settings.hue_var.to_value() } - subclass::Property("saturation-ref", ..) => { + "saturation-ref" => { let settings = self.settings.lock().unwrap(); settings.saturation_ref.to_value() } - subclass::Property("saturation-var", ..) => { + "saturation-var" => { let settings = self.settings.lock().unwrap(); settings.saturation_var.to_value() } - subclass::Property("value-ref", ..) => { + "value-ref" => { let settings = self.settings.lock().unwrap(); settings.value_ref.to_value() } - subclass::Property("value-var", ..) => { + "value-var" => { let settings = self.settings.lock().unwrap(); settings.value_var.to_value() } @@ -356,9 +276,90 @@ impl ObjectImpl for HsvDetector { } } -impl ElementImpl for HsvDetector {} +impl ElementImpl for HsvDetector { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "HSV detector", + "Filter/Effect/Converter/Video", + "Works within the HSV colorspace to mark positive pixels", + "Julien Bardagi <julien.bardagi@gmail.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let caps = gst::Caps::new_simple( + "video/x-raw", + &[ + ( + "format", + &gst::List::new(&[&gst_video::VideoFormat::Rgba.to_str()]), + ), + ("width", &gst::IntRange::<i32>::new(0, i32::MAX)), + ("height", &gst::IntRange::<i32>::new(0, i32::MAX)), + ( + "framerate", + &gst::FractionRange::new( + gst::Fraction::new(0, 1), + gst::Fraction::new(i32::MAX, 1), + ), + ), + ], + ); + + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + // sink pad capabilities + let caps = gst::Caps::new_simple( + "video/x-raw", + &[ + ( + "format", + &gst::List::new(&[&gst_video::VideoFormat::Rgbx.to_str()]), + ), + ("width", &gst::IntRange::<i32>::new(0, i32::MAX)), + ("height", &gst::IntRange::<i32>::new(0, i32::MAX)), + ( + "framerate", + &gst::FractionRange::new( + gst::Fraction::new(0, 1), + gst::Fraction::new(i32::MAX, 1), + ), + ), + ], + ); + + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } +} impl BaseTransformImpl for HsvDetector { + const MODE: gst_base::subclass::BaseTransformMode = + gst_base::subclass::BaseTransformMode::NeverInPlace; + const PASSTHROUGH_ON_SAME_CAPS: bool = false; + const TRANSFORM_IP_ON_PASSTHROUGH: bool = false; + fn transform_caps( &self, element: &Self::Type, diff --git a/video/hsv/src/hsvfilter/imp.rs b/video/hsv/src/hsvfilter/imp.rs index 3329f3cbc..bbbb2180a 100644 --- a/video/hsv/src/hsvfilter/imp.rs +++ b/video/hsv/src/hsvfilter/imp.rs @@ -51,65 +51,6 @@ impl Default for Settings { } } -// Metadata for the properties -static PROPERTIES: [subclass::Property; 5] = [ - subclass::Property("hue-shift", |name| { - glib::ParamSpec::float( - name, - "Hue shift", - "Hue shifting in degrees", - f32::MIN, - f32::MAX, - DEFAULT_HUE_SHIFT, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("saturation-mul", |name| { - glib::ParamSpec::float( - name, - "Saturation multiplier", - "Saturation multiplier to apply to the saturation value (before offset)", - f32::MIN, - f32::MAX, - DEFAULT_SATURATION_MUL, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("saturation-off", |name| { - glib::ParamSpec::float( - name, - "Saturation offset", - "Saturation offset to add to the saturation value (after multiplier)", - f32::MIN, - f32::MAX, - DEFAULT_SATURATION_OFF, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("value-mul", |name| { - glib::ParamSpec::float( - name, - "Value multiplier", - "Value multiplier to apply to the value (before offset)", - f32::MIN, - f32::MAX, - DEFAULT_VALUE_MUL, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("value-off", |name| { - glib::ParamSpec::float( - name, - "Value offset", - "Value offset to add to the value (after multiplier)", - f32::MIN, - f32::MAX, - DEFAULT_VALUE_OFF, - glib::ParamFlags::READWRITE, - ) - }), -]; - // Stream-specific state, i.e. video format configuration struct State { info: gst_video::VideoInfo, @@ -133,6 +74,7 @@ impl ObjectSubclass for HsvFilter { const NAME: &'static str = "HsvFilter"; type Type = super::HsvFilter; type ParentType = gst_base::BaseTransform; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -146,70 +88,72 @@ impl ObjectSubclass for HsvFilter { state: AtomicRefCell::new(None), } } +} - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "HSV filter", - "Filter/Effect/Converter/Video", - "Works within the HSV colorspace to apply tranformations to incoming frames", - "Julien Bardagi <julien.bardagi@gmail.com>", - ); - - // src pad capabilities - let caps = gst::Caps::new_simple( - "video/x-raw", - &[ - ( - "format", - &gst::List::new(&[&gst_video::VideoFormat::Rgbx.to_str()]), +impl ObjectImpl for HsvFilter { + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::float( + "hue-shift", + "Hue shift", + "Hue shifting in degrees", + f32::MIN, + f32::MAX, + DEFAULT_HUE_SHIFT, + glib::ParamFlags::READWRITE, ), - ("width", &gst::IntRange::<i32>::new(0, i32::MAX)), - ("height", &gst::IntRange::<i32>::new(0, i32::MAX)), - ( - "framerate", - &gst::FractionRange::new( - gst::Fraction::new(0, 1), - gst::Fraction::new(i32::MAX, 1), - ), + glib::ParamSpec::float( + "saturation-mul", + "Saturation multiplier", + "Saturation multiplier to apply to the saturation value (before offset)", + f32::MIN, + f32::MAX, + DEFAULT_SATURATION_MUL, + glib::ParamFlags::READWRITE, ), - ], - ); + glib::ParamSpec::float( + "saturation-off", + "Saturation offset", + "Saturation offset to add to the saturation value (after multiplier)", + f32::MIN, + f32::MAX, + DEFAULT_SATURATION_OFF, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::float( + "value-mul", + "Value multiplier", + "Value multiplier to apply to the value (before offset)", + f32::MIN, + f32::MAX, + DEFAULT_VALUE_MUL, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::float( + "value-off", + "Value offset", + "Value offset to add to the value (after multiplier)", + f32::MIN, + f32::MAX, + DEFAULT_VALUE_OFF, + glib::ParamFlags::READWRITE, + ), + ] + }); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - // Install all our properties - klass.install_properties(&PROPERTIES); - - klass.configure( - gst_base::subclass::BaseTransformMode::AlwaysInPlace, - false, - false, - ); + PROPERTIES.as_ref() } -} -impl ObjectImpl for HsvFilter { - fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("hue-shift", ..) => { + fn set_property( + &self, + obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "hue-shift" => { let mut settings = self.settings.lock().unwrap(); let hue_shift = value.get_some().expect("type checked upstream"); gst_info!( @@ -221,7 +165,7 @@ impl ObjectImpl for HsvFilter { ); settings.hue_shift = hue_shift; } - subclass::Property("saturation-mul", ..) => { + "saturation-mul" => { let mut settings = self.settings.lock().unwrap(); let saturation_mul = value.get_some().expect("type checked upstream"); gst_info!( @@ -233,7 +177,7 @@ impl ObjectImpl for HsvFilter { ); settings.saturation_mul = saturation_mul; } - subclass::Property("saturation-off", ..) => { + "saturation-off" => { let mut settings = self.settings.lock().unwrap(); let saturation_off = value.get_some().expect("type checked upstream"); gst_info!( @@ -245,7 +189,7 @@ impl ObjectImpl for HsvFilter { ); settings.saturation_off = saturation_off; } - subclass::Property("value-mul", ..) => { + "value-mul" => { let mut settings = self.settings.lock().unwrap(); let value_mul = value.get_some().expect("type checked upstream"); gst_info!( @@ -257,7 +201,7 @@ impl ObjectImpl for HsvFilter { ); settings.value_mul = value_mul; } - subclass::Property("value-off", ..) => { + "value-off" => { let mut settings = self.settings.lock().unwrap(); let value_off = value.get_some().expect("type checked upstream"); gst_info!( @@ -275,27 +219,25 @@ impl ObjectImpl for HsvFilter { // Called whenever a value of a property is read. It can be called // at any time from any thread. - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("hue-shift", ..) => { + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "hue-shift" => { let settings = self.settings.lock().unwrap(); settings.hue_shift.to_value() } - subclass::Property("saturation-mul", ..) => { + "saturation-mul" => { let settings = self.settings.lock().unwrap(); settings.saturation_mul.to_value() } - subclass::Property("saturation-off", ..) => { + "saturation-off" => { let settings = self.settings.lock().unwrap(); settings.saturation_off.to_value() } - subclass::Property("value-mul", ..) => { + "value-mul" => { let settings = self.settings.lock().unwrap(); settings.value_mul.to_value() } - subclass::Property("value-off", ..) => { + "value-off" => { let settings = self.settings.lock().unwrap(); settings.value_off.to_value() } @@ -304,9 +246,71 @@ impl ObjectImpl for HsvFilter { } } -impl ElementImpl for HsvFilter {} +impl ElementImpl for HsvFilter { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "HSV filter", + "Filter/Effect/Converter/Video", + "Works within the HSV colorspace to apply tranformations to incoming frames", + "Julien Bardagi <julien.bardagi@gmail.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + // src pad capabilities + let caps = gst::Caps::new_simple( + "video/x-raw", + &[ + ( + "format", + &gst::List::new(&[&gst_video::VideoFormat::Rgbx.to_str()]), + ), + ("width", &gst::IntRange::<i32>::new(0, i32::MAX)), + ("height", &gst::IntRange::<i32>::new(0, i32::MAX)), + ( + "framerate", + &gst::FractionRange::new( + gst::Fraction::new(0, 1), + gst::Fraction::new(i32::MAX, 1), + ), + ), + ], + ); + + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &caps, + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } +} impl BaseTransformImpl for HsvFilter { + const MODE: gst_base::subclass::BaseTransformMode = + gst_base::subclass::BaseTransformMode::AlwaysInPlace; + const PASSTHROUGH_ON_SAME_CAPS: bool = false; + const TRANSFORM_IP_ON_PASSTHROUGH: bool = false; + fn get_unit_size(&self, _element: &Self::Type, caps: &gst::Caps) -> Option<usize> { gst_video::VideoInfo::from_caps(caps) .map(|info| info.size()) diff --git a/video/rav1e/src/rav1enc/imp.rs b/video/rav1e/src/rav1enc/imp.rs index be43ee4d4..2974a662f 100644 --- a/video/rav1e/src/rav1enc/imp.rs +++ b/video/rav1e/src/rav1enc/imp.rs @@ -60,117 +60,6 @@ impl Default for Settings { } } -static PROPERTIES: [subclass::Property; 10] = [ - subclass::Property("speed-preset", |name| { - glib::ParamSpec::uint( - name, - "Speed Preset", - "Speed preset (10 fastest, 0 slowest)", - 0, - 10, - DEFAULT_SPEED_PRESET, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("low-latency", |name| { - glib::ParamSpec::boolean( - name, - "Low Latency", - "Low Latency", - DEFAULT_LOW_LATENCY, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("min-key-frame-interval", |name| { - glib::ParamSpec::uint64( - name, - "Min Key Frame Interval", - "Min Key Frame Interval", - 0, - std::u64::MAX, - DEFAULT_MIN_KEY_FRAME_INTERVAL, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("max-key-frame-interval", |name| { - glib::ParamSpec::uint64( - name, - "Max Key Frame Interval", - "Max Key Frame Interval", - 0, - std::u64::MAX, - DEFAULT_MAX_KEY_FRAME_INTERVAL, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("bitrate", |name| { - glib::ParamSpec::int( - name, - "Bitrate", - "Bitrate", - 0, - std::i32::MAX, - DEFAULT_BITRATE, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("quantizer", |name| { - glib::ParamSpec::uint( - name, - "Quantizer", - "Quantizer", - 0, - std::u32::MAX, - DEFAULT_QUANTIZER as u32, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("tile-cols", |name| { - glib::ParamSpec::uint( - name, - "Tile Cols", - "Tile Cols", - 0, - std::u32::MAX, - DEFAULT_TILE_COLS as u32, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("tile-rows", |name| { - glib::ParamSpec::uint( - name, - "Tile Rows", - "Tile Rows", - 0, - std::u32::MAX, - DEFAULT_TILE_ROWS as u32, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("tiles", |name| { - glib::ParamSpec::uint( - name, - "Tiles", - "Tiles", - 0, - std::u32::MAX, - DEFAULT_TILES as u32, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("threads", |name| { - glib::ParamSpec::uint( - name, - "Threads", - "Threads", - 0, - std::u32::MAX, - DEFAULT_THREADS as u32, - glib::ParamFlags::READWRITE, - ) - }), -]; - enum Context { Eight(rav1e::Context<u8>), Sixteen(rav1e::Context<u16>), @@ -319,6 +208,7 @@ impl ObjectSubclass for Rav1Enc { const NAME: &'static str = "Rav1Enc"; type Type = super::Rav1Enc; type ParentType = gst_video::VideoEncoder; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -330,112 +220,154 @@ impl ObjectSubclass for Rav1Enc { settings: Mutex::new(Default::default()), } } +} - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "rav1e AV1 encoder", - "Encoder/Video", - "rav1e AV1 encoder", - "Sebastian Dröge <sebastian@centricular.com>", - ); - - let sink_caps = gst::Caps::new_simple( - "video/x-raw", - &[ - ( - "format", - &gst::List::new(&[ - &gst_video::VideoFormat::I420.to_str(), - &gst_video::VideoFormat::Y42b.to_str(), - &gst_video::VideoFormat::Y444.to_str(), - &gst_video::VideoFormat::I42010le.to_str(), - &gst_video::VideoFormat::I42210le.to_str(), - &gst_video::VideoFormat::Y44410le.to_str(), - &gst_video::VideoFormat::I42012le.to_str(), - &gst_video::VideoFormat::I42212le.to_str(), - &gst_video::VideoFormat::Y44412le.to_str(), - // &gst_video::VideoFormat::Gray8.to_str(), - ]), +impl ObjectImpl for Rav1Enc { + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::uint( + "speed-preset", + "Speed Preset", + "Speed preset (10 fastest, 0 slowest)", + 0, + 10, + DEFAULT_SPEED_PRESET, + glib::ParamFlags::READWRITE, ), - ("width", &gst::IntRange::<i32>::new(1, std::i32::MAX)), - ("height", &gst::IntRange::<i32>::new(1, std::i32::MAX)), - ( - "framerate", - &gst::FractionRange::new( - gst::Fraction::new(0, 1), - gst::Fraction::new(std::i32::MAX, 1), - ), + glib::ParamSpec::boolean( + "low-latency", + "Low Latency", + "Low Latency", + DEFAULT_LOW_LATENCY, + glib::ParamFlags::READWRITE, ), - ], - ); - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &sink_caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - let src_caps = gst::Caps::new_simple("video/x-av1", &[]); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &src_caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - - klass.install_properties(&PROPERTIES); - } -} + glib::ParamSpec::uint64( + "min-key-frame-interval", + "Min Key Frame Interval", + "Min Key Frame Interval", + 0, + std::u64::MAX, + DEFAULT_MIN_KEY_FRAME_INTERVAL, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint64( + "max-key-frame-interval", + "Max Key Frame Interval", + "Max Key Frame Interval", + 0, + std::u64::MAX, + DEFAULT_MAX_KEY_FRAME_INTERVAL, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::int( + "bitrate", + "Bitrate", + "Bitrate", + 0, + std::i32::MAX, + DEFAULT_BITRATE, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "quantizer", + "Quantizer", + "Quantizer", + 0, + std::u32::MAX, + DEFAULT_QUANTIZER as u32, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "tile-cols", + "Tile Cols", + "Tile Cols", + 0, + std::u32::MAX, + DEFAULT_TILE_COLS as u32, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "tile-rows", + "Tile Rows", + "Tile Rows", + 0, + std::u32::MAX, + DEFAULT_TILE_ROWS as u32, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "tiles", + "Tiles", + "Tiles", + 0, + std::u32::MAX, + DEFAULT_TILES as u32, + glib::ParamFlags::READWRITE, + ), + glib::ParamSpec::uint( + "threads", + "Threads", + "Threads", + 0, + std::u32::MAX, + DEFAULT_THREADS as u32, + glib::ParamFlags::READWRITE, + ), + ] + }); -impl ObjectImpl for Rav1Enc { - fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; + PROPERTIES.as_ref() + } - match *prop { - subclass::Property("speed-preset", ..) => { + fn set_property( + &self, + _obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "speed-preset" => { let mut settings = self.settings.lock().unwrap(); settings.speed_preset = value.get_some().expect("type checked upstream"); } - subclass::Property("low-latency", ..) => { + "low-latency" => { let mut settings = self.settings.lock().unwrap(); settings.low_latency = value.get_some().expect("type checked upstream"); } - subclass::Property("min-key-frame-interval", ..) => { + "min-key-frame-interval" => { let mut settings = self.settings.lock().unwrap(); settings.min_key_frame_interval = value.get_some().expect("type checked upstream"); } - subclass::Property("max-key-frame-interval", ..) => { + "max-key-frame-interval" => { let mut settings = self.settings.lock().unwrap(); settings.max_key_frame_interval = value.get_some().expect("type checked upstream"); } - subclass::Property("bitrate", ..) => { + "bitrate" => { let mut settings = self.settings.lock().unwrap(); settings.bitrate = value.get_some().expect("type checked upstream"); } - subclass::Property("quantizer", ..) => { + "quantizer" => { let mut settings = self.settings.lock().unwrap(); settings.quantizer = value.get_some::<u32>().expect("type checked upstream") as usize; } - subclass::Property("tile-cols", ..) => { + "tile-cols" => { let mut settings = self.settings.lock().unwrap(); settings.tile_cols = value.get_some::<u32>().expect("type checked upstream") as usize; } - subclass::Property("tile-rows", ..) => { + "tile-rows" => { let mut settings = self.settings.lock().unwrap(); settings.tile_rows = value.get_some::<u32>().expect("type checked upstream") as usize; } - subclass::Property("tiles", ..) => { + "tiles" => { let mut settings = self.settings.lock().unwrap(); settings.tiles = value.get_some::<u32>().expect("type checked upstream") as usize; } - subclass::Property("threads", ..) => { + "threads" => { let mut settings = self.settings.lock().unwrap(); settings.threads = value.get_some::<u32>().expect("type checked upstream") as usize; } @@ -443,47 +375,45 @@ impl ObjectImpl for Rav1Enc { } } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("speed-preset", ..) => { + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "speed-preset" => { let settings = self.settings.lock().unwrap(); settings.speed_preset.to_value() } - subclass::Property("low-latency", ..) => { + "low-latency" => { let settings = self.settings.lock().unwrap(); settings.low_latency.to_value() } - subclass::Property("min-key-frame-interval", ..) => { + "min-key-frame-interval" => { let settings = self.settings.lock().unwrap(); settings.min_key_frame_interval.to_value() } - subclass::Property("max-key-frame-interval", ..) => { + "max-key-frame-interval" => { let settings = self.settings.lock().unwrap(); settings.max_key_frame_interval.to_value() } - subclass::Property("bitrate", ..) => { + "bitrate" => { let settings = self.settings.lock().unwrap(); settings.bitrate.to_value() } - subclass::Property("quantizer", ..) => { + "quantizer" => { let settings = self.settings.lock().unwrap(); (settings.quantizer as u32).to_value() } - subclass::Property("tile-cols", ..) => { + "tile-cols" => { let settings = self.settings.lock().unwrap(); (settings.tile_cols as u32).to_value() } - subclass::Property("tile-rows", ..) => { + "tile-rows" => { let settings = self.settings.lock().unwrap(); (settings.tile_rows as u32).to_value() } - subclass::Property("tiles", ..) => { + "tiles" => { let settings = self.settings.lock().unwrap(); (settings.tiles as u32).to_value() } - subclass::Property("threads", ..) => { + "threads" => { let settings = self.settings.lock().unwrap(); (settings.threads as u32).to_value() } @@ -492,7 +422,74 @@ impl ObjectImpl for Rav1Enc { } } -impl ElementImpl for Rav1Enc {} +impl ElementImpl for Rav1Enc { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "rav1e AV1 encoder", + "Encoder/Video", + "rav1e AV1 encoder", + "Sebastian Dröge <sebastian@centricular.com>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let sink_caps = gst::Caps::new_simple( + "video/x-raw", + &[ + ( + "format", + &gst::List::new(&[ + &gst_video::VideoFormat::I420.to_str(), + &gst_video::VideoFormat::Y42b.to_str(), + &gst_video::VideoFormat::Y444.to_str(), + &gst_video::VideoFormat::I42010le.to_str(), + &gst_video::VideoFormat::I42210le.to_str(), + &gst_video::VideoFormat::Y44410le.to_str(), + &gst_video::VideoFormat::I42012le.to_str(), + &gst_video::VideoFormat::I42212le.to_str(), + &gst_video::VideoFormat::Y44412le.to_str(), + // &gst_video::VideoFormat::Gray8.to_str(), + ]), + ), + ("width", &gst::IntRange::<i32>::new(1, std::i32::MAX)), + ("height", &gst::IntRange::<i32>::new(1, std::i32::MAX)), + ( + "framerate", + &gst::FractionRange::new( + gst::Fraction::new(0, 1), + gst::Fraction::new(std::i32::MAX, 1), + ), + ), + ], + ); + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &sink_caps, + ) + .unwrap(); + + let src_caps = gst::Caps::new_simple("video/x-av1", &[]); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &src_caps, + ) + .unwrap(); + + vec![src_pad_template, sink_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } +} impl VideoEncoderImpl for Rav1Enc { fn stop(&self, _element: &Self::Type) -> Result<(), gst::ErrorMessage> { diff --git a/video/rspng/src/pngenc/imp.rs b/video/rspng/src/pngenc/imp.rs index 068564f1a..42e5854a5 100644 --- a/video/rspng/src/pngenc/imp.rs +++ b/video/rspng/src/pngenc/imp.rs @@ -100,29 +100,6 @@ impl Default for Settings { } } -static PROPERTIES: [subclass::Property; 2] = [ - subclass::Property("compression-level", |name| { - glib::ParamSpec::enum_( - name, - "Compression level", - "Selects the compression algorithm to use", - CompressionLevel::static_type(), - DEFAULT_COMPRESSION_LEVEL as i32, - glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("filter", |name| { - glib::ParamSpec::enum_( - name, - "Filter", - "Selects the filter type to applied", - FilterType::static_type(), - DEFAULT_FILTER_TYPE as i32, - glib::ParamFlags::READWRITE, - ) - }), -]; - struct State { video_info: gst_video::VideoInfo, cache: Arc<CacheBuffer>, @@ -192,6 +169,7 @@ impl ObjectSubclass for PngEncoder { const NAME: &'static str = "PngEncoder"; type Type = super::PngEncoder; type ParentType = gst_video::VideoEncoder; + type Interfaces = (); type Instance = gst::subclass::ElementInstanceStruct<Self>; type Class = subclass::simple::ClassStruct<Self>; @@ -203,72 +181,49 @@ impl ObjectSubclass for PngEncoder { settings: Mutex::new(Default::default()), } } +} - fn class_init(klass: &mut Self::Class) { - klass.set_metadata( - "PNG encoder", - "Encoder/Video", - "PNG encoder", - "Natanael Mojica <neithanmo@gmail>", - ); - - let sink_caps = gst::Caps::new_simple( - "video/x-raw", - &[ - ( - "format", - &gst::List::new(&[ - &gst_video::VideoFormat::Gray8.to_str(), - &gst_video::VideoFormat::Gray16Be.to_str(), - &gst_video::VideoFormat::Rgb.to_str(), - &gst_video::VideoFormat::Rgba.to_str(), - ]), +impl ObjectImpl for PngEncoder { + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { + vec![ + glib::ParamSpec::enum_( + "compression-level", + "Compression level", + "Selects the compression algorithm to use", + CompressionLevel::static_type(), + DEFAULT_COMPRESSION_LEVEL as i32, + glib::ParamFlags::READWRITE, ), - ("width", &gst::IntRange::<i32>::new(1, std::i32::MAX)), - ("height", &gst::IntRange::<i32>::new(1, std::i32::MAX)), - ( - "framerate", - &gst::FractionRange::new( - gst::Fraction::new(1, 1), - gst::Fraction::new(std::i32::MAX, 1), - ), + glib::ParamSpec::enum_( + "filter", + "Filter", + "Selects the filter type to applied", + FilterType::static_type(), + DEFAULT_FILTER_TYPE as i32, + glib::ParamFlags::READWRITE, ), - ], - ); - let sink_pad_template = gst::PadTemplate::new( - "sink", - gst::PadDirection::Sink, - gst::PadPresence::Always, - &sink_caps, - ) - .unwrap(); - klass.add_pad_template(sink_pad_template); - - let src_caps = gst::Caps::new_simple("image/png", &[]); - let src_pad_template = gst::PadTemplate::new( - "src", - gst::PadDirection::Src, - gst::PadPresence::Always, - &src_caps, - ) - .unwrap(); - klass.add_pad_template(src_pad_template); - klass.install_properties(&PROPERTIES); - } -} + ] + }); -impl ObjectImpl for PngEncoder { - fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; + PROPERTIES.as_ref() + } - match *prop { - subclass::Property("compression-level", ..) => { + fn set_property( + &self, + _obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.get_name() { + "compression-level" => { let mut settings = self.settings.lock(); settings.compression = value .get_some::<CompressionLevel>() .expect("type checked upstream"); } - subclass::Property("filter", ..) => { + "filter" => { let mut settings = self.settings.lock(); settings.filter = value .get_some::<FilterType>() @@ -278,15 +233,13 @@ impl ObjectImpl for PngEncoder { } } - fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("compression-level", ..) => { + fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.get_name() { + "compression-level" => { let settings = self.settings.lock(); settings.compression.to_value() } - subclass::Property("filter", ..) => { + "filter" => { let settings = self.settings.lock(); settings.filter.to_value() } @@ -295,7 +248,68 @@ impl ObjectImpl for PngEncoder { } } -impl ElementImpl for PngEncoder {} +impl ElementImpl for PngEncoder { + fn metadata() -> Option<&'static gst::subclass::ElementMetadata> { + static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| { + gst::subclass::ElementMetadata::new( + "PNG encoder", + "Encoder/Video", + "PNG encoder", + "Natanael Mojica <neithanmo@gmail>", + ) + }); + + Some(&*ELEMENT_METADATA) + } + + fn pad_templates() -> &'static [gst::PadTemplate] { + static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| { + let sink_caps = gst::Caps::new_simple( + "video/x-raw", + &[ + ( + "format", + &gst::List::new(&[ + &gst_video::VideoFormat::Gray8.to_str(), + &gst_video::VideoFormat::Gray16Be.to_str(), + &gst_video::VideoFormat::Rgb.to_str(), + &gst_video::VideoFormat::Rgba.to_str(), + ]), + ), + ("width", &gst::IntRange::<i32>::new(1, std::i32::MAX)), + ("height", &gst::IntRange::<i32>::new(1, std::i32::MAX)), + ( + "framerate", + &gst::FractionRange::new( + gst::Fraction::new(1, 1), + gst::Fraction::new(std::i32::MAX, 1), + ), + ), + ], + ); + let sink_pad_template = gst::PadTemplate::new( + "sink", + gst::PadDirection::Sink, + gst::PadPresence::Always, + &sink_caps, + ) + .unwrap(); + + let src_caps = gst::Caps::new_simple("image/png", &[]); + let src_pad_template = gst::PadTemplate::new( + "src", + gst::PadDirection::Src, + gst::PadPresence::Always, + &src_caps, + ) + .unwrap(); + + vec![sink_pad_template, src_pad_template] + }); + + PAD_TEMPLATES.as_ref() + } +} impl VideoEncoderImpl for PngEncoder { fn stop(&self, _element: &Self::Type) -> Result<(), gst::ErrorMessage> { |