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 /tutorial | |
parent | 875c3efb916f2400df355f8517f1daeff6f7ae85 (diff) |
Update for glib/gstreamer bindings API changes
Diffstat (limited to 'tutorial')
-rw-r--r-- | tutorial/src/identity/imp.rs | 94 | ||||
-rw-r--r-- | tutorial/src/progressbin/imp.rs | 149 | ||||
-rw-r--r-- | tutorial/src/rgb2gray/imp.rs | 284 | ||||
-rw-r--r-- | tutorial/src/sinesrc/imp.rs | 260 |
4 files changed, 400 insertions, 387 deletions
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. |