tutorial: Add enum property to control output on progress bin

Fixes #84
This commit is contained in:
Tobias Morell 2019-12-19 13:40:23 +01:00 committed by Sebastian Dröge
parent e73e27cda4
commit aa40eae581
4 changed files with 190 additions and 2 deletions

View file

@ -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.

View file

@ -21,6 +21,7 @@ extern crate lazy_static;
mod identity;
mod progressbin;
mod progressbin_output_enum;
mod rgb2gray;
mod sinesrc;

View file

@ -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),

View file

@ -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
}
}