diff options
-rw-r--r-- | gst-plugin-tutorial/README.md | 2 | ||||
-rw-r--r-- | gst-plugin-tutorial/src/lib.rs | 1 | ||||
-rw-r--r-- | gst-plugin-tutorial/src/progressbin.rs | 79 | ||||
-rw-r--r-- | gst-plugin-tutorial/src/progressbin_output_enum.rs | 110 |
4 files changed, 190 insertions, 2 deletions
diff --git a/gst-plugin-tutorial/README.md b/gst-plugin-tutorial/README.md index 62d70a6a..7592e26b 100644 --- a/gst-plugin-tutorial/README.md +++ b/gst-plugin-tutorial/README.md @@ -5,4 +5,4 @@ This tutorial is for the `gst-plugin-tutorial` plugin. This plugin provides 4 fe 1. [Part 1: `rgb2gray` - A Video Filter for converting RGB to grayscale](tutorial-1.md) 2. [Part 2: `sinesrc` - A raw audio sine wave source](tutorial-2.md) 3. Part 3: `identity` -4. Part 4: `progressBin` - Prints progress information +4. Part 4: `progressBin` - Prints progress information. Also showcases how to implement an enum-property on a plugin. diff --git a/gst-plugin-tutorial/src/lib.rs b/gst-plugin-tutorial/src/lib.rs index 5ec5e5e5..3d283bd0 100644 --- a/gst-plugin-tutorial/src/lib.rs +++ b/gst-plugin-tutorial/src/lib.rs @@ -21,6 +21,7 @@ extern crate lazy_static; mod identity; mod progressbin; +mod progressbin_output_enum; mod rgb2gray; mod sinesrc; diff --git a/gst-plugin-tutorial/src/progressbin.rs b/gst-plugin-tutorial/src/progressbin.rs index 431f721c..a393be46 100644 --- a/gst-plugin-tutorial/src/progressbin.rs +++ b/gst-plugin-tutorial/src/progressbin.rs @@ -6,6 +6,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use crate::progressbin_output_enum::ProgressBinOutput; use glib; use glib::prelude::*; use glib::subclass; @@ -13,14 +14,41 @@ use glib::subclass::prelude::*; use gst; use gst::prelude::*; use gst::subclass::prelude::*; +use std::sync::Mutex; + +const DEFAULT_OUTPUT_TYPE: ProgressBinOutput = ProgressBinOutput::Println; + +lazy_static! { + static ref CAT: gst::DebugCategory = gst::DebugCategory::new( + "progressbin", + gst::DebugColorFlags::empty(), + Some("Rust Progress Reporter"), + ); +} // Struct containing all the element data struct ProgressBin { progress: gst::Element, srcpad: gst::GhostPad, sinkpad: gst::GhostPad, + // We put the output_type property behind a mutex, as we want + // change it in the set_property function, which can be called + // from any thread. + 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 @@ -56,6 +84,7 @@ impl ObjectSubclass for ProgressBin { progress, srcpad, sinkpad, + output_type: Mutex::new(ProgressBinOutput::Println), } } @@ -101,6 +130,9 @@ impl ObjectSubclass for ProgressBin { ) .unwrap(); klass.add_pad_template(sink_pad_template); + + // Install all our properties + klass.install_properties(&PROPERTIES); } } @@ -109,6 +141,45 @@ impl ObjectImpl for ProgressBin { // This macro provides some boilerplate glib_object_impl!(); + // Called whenever a value of a property is changed. It can be called + // at any time from any thread. + fn set_property(&self, obj: &glib::Object, id: usize, value: &glib::Value) { + let prop = &PROPERTIES[id]; + let element = obj.downcast_ref::<gst_base::BaseTransform>().unwrap(); + + match *prop { + subclass::Property("output", ..) => { + let mut output_type = self.output_type.lock().unwrap(); + let new_output_type = value + .get_some::<ProgressBinOutput>() + .expect("type checked upstream"); + gst_info!( + CAT, + obj: element, + "Changing output from {:?} to {:?}", + output_type, + new_output_type + ); + *output_type = new_output_type; + } + _ => unimplemented!(), + } + } + + // Called whenever a value of a property is read. It can be called + // at any time from any thread. + fn get_property(&self, _obj: &glib::Object, id: usize) -> Result<glib::Value, ()> { + let prop = &PROPERTIES[id]; + + match *prop { + subclass::Property("output", ..) => { + let output_type = self.output_type.lock().unwrap(); + Ok(output_type.to_value()) + } + _ => unimplemented!(), + } + } + // Called right after construction of a new instance fn constructed(&self, obj: &glib::Object) { // Call the parent class' ::constructed() implementation first @@ -157,7 +228,13 @@ impl BinImpl for ProgressBin { { let s = msg.get_structure().unwrap(); if let Ok(percent) = s.get_some::<f64>("percent-double") { - println!("progress: {:5.1}%", percent); + let output_type = self.output_type.lock().unwrap(); + match *output_type { + ProgressBinOutput::Println => println!("progress: {:5.1}%", percent), + ProgressBinOutput::DebugCategory => { + gst_info!(CAT, "progress: {:5.1}%", percent); + } + }; } } _ => self.parent_handle_message(bin, msg), diff --git a/gst-plugin-tutorial/src/progressbin_output_enum.rs b/gst-plugin-tutorial/src/progressbin_output_enum.rs new file mode 100644 index 00000000..737b9cb0 --- /dev/null +++ b/gst-plugin-tutorial/src/progressbin_output_enum.rs @@ -0,0 +1,110 @@ +use glib::{gobject_sys, StaticType, Type}; + +// This enum may be used to control what type of output the progressbin should produce. +// It also serves the secondary purpose of illustrating how to add enum-type properties +// to a plugin written in rust. +#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)] +#[repr(u32)] +pub(crate) enum ProgressBinOutput { + Println = 0, + DebugCategory = 1, +} + +// This trait allows us to translate our custom enum type to a GLib type. +impl glib::translate::ToGlib for ProgressBinOutput { + // We use i32 as the underlying representation for the enum. + type GlibType = i32; + + fn to_glib(&self) -> Self::GlibType { + *self as Self::GlibType + } +} + +// This trait allows us to translate from a GLib type back to our custom enum type. +impl glib::translate::FromGlib<i32> for ProgressBinOutput { + fn from_glib(val: i32) -> Self { + match val { + 0 => ProgressBinOutput::Println, + 1 => ProgressBinOutput::DebugCategory, + _ => unreachable!(), + } + } +} + +// This trait registers our enum with the GLib type system. +impl StaticType for ProgressBinOutput { + fn static_type() -> Type { + progressbin_output_get_type() + } +} + +impl<'a> glib::value::FromValueOptional<'a> for ProgressBinOutput { + unsafe fn from_value_optional(value: &glib::Value) -> Option<Self> { + Some(glib::value::FromValue::from_value(value)) + } +} +impl<'a> glib::value::FromValue<'a> for ProgressBinOutput { + unsafe fn from_value(value: &glib::Value) -> Self { + use glib::translate::ToGlibPtr; + + glib::translate::from_glib(gobject_sys::g_value_get_enum(value.to_glib_none().0)) + } +} + +impl glib::value::SetValue for ProgressBinOutput { + unsafe fn set_value(value: &mut glib::Value, this: &Self) { + use glib::translate::{ToGlib, ToGlibPtrMut}; + + gobject_sys::g_value_set_enum(value.to_glib_none_mut().0, this.to_glib()) + } +} + +// On the first call this function will register the enum type with GObject, +// on all further calls it will simply return the registered type id. +fn progressbin_output_get_type() -> glib::Type { + use std::sync::Once; + // We only want to register the enum once, and hence we use a Once struct to ensure that. + static ONCE: Once = Once::new(); + static mut TYPE: glib::Type = glib::Type::Invalid; + + // The first time anyone calls this function, we will call ONCE to complete the registration + // process, otherwise we'll just skip it + ONCE.call_once(|| { + use std::ffi; + use std::ptr; + + // The descriptions in this array will be available to users of the plugin when using gst-inspect-1.0 + static mut VALUES: [gobject_sys::GEnumValue; 3] = [ + gobject_sys::GEnumValue { + value: ProgressBinOutput::Println as i32, + value_name: b"Println: Outputs the progress using a println! macro.\0".as_ptr() as *const _, + value_nick: b"println\0".as_ptr() as *const _, + }, + gobject_sys::GEnumValue { + value: ProgressBinOutput::DebugCategory as i32, + value_name: b"Debug Category: Outputs the progress as info logs under the element's debug category.\0".as_ptr() as *const _, + value_nick: b"debug-category\0".as_ptr() as *const _, + }, + // We use a struct with all null values to indicate the end of the array + gobject_sys::GEnumValue { + value: 0, + value_name: ptr::null(), + value_nick: ptr::null(), + }, + ]; + + // Here we call the bindings to register the enum. We store the result in TYPE, so that we + // may re-use it later + let name = ffi::CString::new("GstProgressBinOutput").unwrap(); + unsafe { + let type_ = gobject_sys::g_enum_register_static(name.as_ptr(), VALUES.as_ptr()); + TYPE = glib::translate::from_glib(type_); + } + }); + + // Check that TYPE is not invalid and return it if so + unsafe { + assert_ne!(TYPE, glib::Type::Invalid); + TYPE + } +} |