mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2024-11-25 04:51:26 +00:00
tutorial: Update for subclassing API changes
This commit is contained in:
parent
dbf108d9a4
commit
4829e31191
10 changed files with 352 additions and 213 deletions
|
@ -6,7 +6,6 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use glib::prelude::*;
|
||||
use glib::subclass;
|
||||
use glib::subclass::prelude::*;
|
||||
use gst::prelude::*;
|
||||
|
@ -14,11 +13,7 @@ use gst::subclass::prelude::*;
|
|||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
// Struct containing all the element data
|
||||
struct Identity {
|
||||
srcpad: gst::Pad,
|
||||
sinkpad: gst::Pad,
|
||||
}
|
||||
// This module contains the private implementation details of our element
|
||||
|
||||
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||
gst::DebugCategory::new(
|
||||
|
@ -28,6 +23,12 @@ static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
|||
)
|
||||
});
|
||||
|
||||
// Struct containing all the element data
|
||||
pub struct Identity {
|
||||
srcpad: gst::Pad,
|
||||
sinkpad: gst::Pad,
|
||||
}
|
||||
|
||||
impl Identity {
|
||||
// Called whenever a new buffer is passed to our sink pad. Here buffers should be processed and
|
||||
// whenever some output buffer is available have to push it out of the source pad.
|
||||
|
@ -38,7 +39,7 @@ impl Identity {
|
|||
fn sink_chain(
|
||||
&self,
|
||||
pad: &gst::Pad,
|
||||
_element: &gst::Element,
|
||||
_element: &super::Identity,
|
||||
buffer: gst::Buffer,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
gst_log!(CAT, obj: pad, "Handling buffer {:?}", buffer);
|
||||
|
@ -52,7 +53,7 @@ impl Identity {
|
|||
//
|
||||
// See the documentation of gst::Event and gst::EventRef to see what can be done with
|
||||
// events, and especially the gst::EventView type for inspecting events.
|
||||
fn sink_event(&self, pad: &gst::Pad, _element: &gst::Element, event: gst::Event) -> bool {
|
||||
fn sink_event(&self, pad: &gst::Pad, _element: &super::Identity, event: gst::Event) -> bool {
|
||||
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
||||
self.srcpad.push_event(event)
|
||||
}
|
||||
|
@ -69,7 +70,7 @@ impl Identity {
|
|||
fn sink_query(
|
||||
&self,
|
||||
pad: &gst::Pad,
|
||||
_element: &gst::Element,
|
||||
_element: &super::Identity,
|
||||
query: &mut gst::QueryRef,
|
||||
) -> bool {
|
||||
gst_log!(CAT, obj: pad, "Handling query {:?}", query);
|
||||
|
@ -84,7 +85,7 @@ impl Identity {
|
|||
//
|
||||
// See the documentation of gst::Event and gst::EventRef to see what can be done with
|
||||
// events, and especially the gst::EventView type for inspecting events.
|
||||
fn src_event(&self, pad: &gst::Pad, _element: &gst::Element, event: gst::Event) -> bool {
|
||||
fn src_event(&self, pad: &gst::Pad, _element: &super::Identity, event: gst::Event) -> bool {
|
||||
gst_log!(CAT, obj: pad, "Handling event {:?}", event);
|
||||
self.sinkpad.push_event(event)
|
||||
}
|
||||
|
@ -101,7 +102,7 @@ impl Identity {
|
|||
fn src_query(
|
||||
&self,
|
||||
pad: &gst::Pad,
|
||||
_element: &gst::Element,
|
||||
_element: &super::Identity,
|
||||
query: &mut gst::QueryRef,
|
||||
) -> bool {
|
||||
gst_log!(CAT, obj: pad, "Handling query {:?}", query);
|
||||
|
@ -114,6 +115,7 @@ impl Identity {
|
|||
// up the class data
|
||||
impl ObjectSubclass for Identity {
|
||||
const NAME: &'static str = "RsIdentity";
|
||||
type Type = super::Identity;
|
||||
type ParentType = gst::Element;
|
||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||
type Class = subclass::simple::ClassStruct<Self>;
|
||||
|
@ -123,7 +125,7 @@ impl ObjectSubclass for Identity {
|
|||
|
||||
// Called when a new instance is to be created. We need to return an instance
|
||||
// of our struct here and also get the class struct passed in case it's needed
|
||||
fn with_class(klass: &subclass::simple::ClassStruct<Self>) -> Self {
|
||||
fn with_class(klass: &Self::Class) -> Self {
|
||||
// Create our two pads from the templates that were registered with
|
||||
// the class and set all the functions on them.
|
||||
//
|
||||
|
@ -189,7 +191,7 @@ impl ObjectSubclass for Identity {
|
|||
//
|
||||
// Actual instances can create pads based on those pad templates
|
||||
// with a subset of the caps given here.
|
||||
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
||||
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
|
||||
|
@ -231,15 +233,14 @@ impl ObjectSubclass for Identity {
|
|||
// Implementation of glib::Object virtual methods
|
||||
impl ObjectImpl for Identity {
|
||||
// Called right after construction of a new instance
|
||||
fn constructed(&self, obj: &glib::Object) {
|
||||
fn constructed(&self, obj: &Self::Type) {
|
||||
// Call the parent class' ::constructed() implementation first
|
||||
self.parent_constructed(obj);
|
||||
|
||||
// Here we actually add the pads we created in Identity::new() to the
|
||||
// element so that GStreamer is aware of their existence.
|
||||
let element = obj.downcast_ref::<gst::Element>().unwrap();
|
||||
element.add_pad(&self.sinkpad).unwrap();
|
||||
element.add_pad(&self.srcpad).unwrap();
|
||||
obj.add_pad(&self.sinkpad).unwrap();
|
||||
obj.add_pad(&self.srcpad).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,7 +251,7 @@ impl ElementImpl for Identity {
|
|||
// the element again.
|
||||
fn change_state(
|
||||
&self,
|
||||
element: &gst::Element,
|
||||
element: &Self::Type,
|
||||
transition: gst::StateChange,
|
||||
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
||||
gst_trace!(CAT, obj: element, "Changing state {:?}", transition);
|
||||
|
@ -259,15 +260,3 @@ impl ElementImpl for Identity {
|
|||
self.parent_change_state(element, transition)
|
||||
}
|
||||
}
|
||||
|
||||
// Registers the type for our element, and then registers in GStreamer under
|
||||
// the name "rsidentity" for being able to instantiate it via e.g.
|
||||
// gst::ElementFactory::make().
|
||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
gst::Element::register(
|
||||
Some(plugin),
|
||||
"rsidentity",
|
||||
gst::Rank::None,
|
||||
Identity::get_type(),
|
||||
)
|
||||
}
|
33
tutorial/src/identity/mod.rs
Normal file
33
tutorial/src/identity/mod.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Copyright (C) 2020 Sebastian Dröge <sebastian@centricular.com>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use glib::prelude::*;
|
||||
|
||||
mod imp;
|
||||
|
||||
// The public Rust wrapper type for our element
|
||||
glib_wrapper! {
|
||||
pub struct Identity(ObjectSubclass<imp::Identity>) @extends gst::Element, gst::Object;
|
||||
}
|
||||
|
||||
// GStreamer elements need to be thread-safe. For the private implementation this is automatically
|
||||
// enforced but for the public wrapper type we need to specify this manually.
|
||||
unsafe impl Send for Identity {}
|
||||
unsafe impl Sync for Identity {}
|
||||
|
||||
// Registers the type for our element, and then registers in GStreamer under
|
||||
// the name "rsidentity" for being able to instantiate it via e.g.
|
||||
// gst::ElementFactory::make().
|
||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
gst::Element::register(
|
||||
Some(plugin),
|
||||
"rsidentity",
|
||||
gst::Rank::None,
|
||||
Identity::static_type(),
|
||||
)
|
||||
}
|
|
@ -15,24 +15,9 @@ use std::sync::Mutex;
|
|||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
// 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, GEnum)]
|
||||
#[repr(u32)]
|
||||
#[genum(type_name = "GstProgressBinOutput")]
|
||||
pub(crate) enum ProgressBinOutput {
|
||||
#[genum(
|
||||
name = "Println: Outputs the progress using a println! macro.",
|
||||
nick = "println"
|
||||
)]
|
||||
Println = 0,
|
||||
#[genum(
|
||||
name = "Debug Category: Outputs the progress as info logs under the element's debug category.",
|
||||
nick = "debug-category"
|
||||
)]
|
||||
DebugCategory = 1,
|
||||
}
|
||||
use super::ProgressBinOutput;
|
||||
|
||||
// This module contains the private implementation details of our element
|
||||
|
||||
const DEFAULT_OUTPUT_TYPE: ProgressBinOutput = ProgressBinOutput::Println;
|
||||
|
||||
|
@ -45,7 +30,7 @@ static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
|||
});
|
||||
|
||||
// Struct containing all the element data
|
||||
struct ProgressBin {
|
||||
pub struct ProgressBin {
|
||||
progress: gst::Element,
|
||||
srcpad: gst::GhostPad,
|
||||
sinkpad: gst::GhostPad,
|
||||
|
@ -72,6 +57,7 @@ static PROPERTIES: [subclass::Property; 1] = [subclass::Property("output", |name
|
|||
// up the class data
|
||||
impl ObjectSubclass for ProgressBin {
|
||||
const NAME: &'static str = "RsProgressBin";
|
||||
type Type = super::ProgressBin;
|
||||
type ParentType = gst::Bin;
|
||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||
type Class = subclass::simple::ClassStruct<Self>;
|
||||
|
@ -81,7 +67,7 @@ impl ObjectSubclass for ProgressBin {
|
|||
|
||||
// Called when a new instance is to be created. We need to return an instance
|
||||
// of our struct here and also get the class struct passed in case it's needed
|
||||
fn with_class(klass: &subclass::simple::ClassStruct<Self>) -> Self {
|
||||
fn with_class(klass: &Self::Class) -> Self {
|
||||
// Create our two ghostpads from the templates that were registered with
|
||||
// the class. We don't provide a target for them yet because we can only
|
||||
// do so after the progressreport element was added to the bin.
|
||||
|
@ -112,7 +98,7 @@ impl ObjectSubclass for ProgressBin {
|
|||
//
|
||||
// Actual instances can create pads based on those pad templates
|
||||
// with a subset of the caps given here.
|
||||
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
||||
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
|
||||
|
@ -158,9 +144,8 @@ impl ObjectSubclass for ProgressBin {
|
|||
impl ObjectImpl for ProgressBin {
|
||||
// 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) {
|
||||
fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) {
|
||||
let prop = &PROPERTIES[id];
|
||||
let element = obj.downcast_ref::<gst::Bin>().unwrap();
|
||||
|
||||
match *prop {
|
||||
subclass::Property("output", ..) => {
|
||||
|
@ -170,7 +155,7 @@ impl ObjectImpl for ProgressBin {
|
|||
.expect("type checked upstream");
|
||||
gst_info!(
|
||||
CAT,
|
||||
obj: element,
|
||||
obj: obj,
|
||||
"Changing output from {:?} to {:?}",
|
||||
output_type,
|
||||
new_output_type
|
||||
|
@ -183,7 +168,7 @@ 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: &glib::Object, id: usize) -> Result<glib::Value, ()> {
|
||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> Result<glib::Value, ()> {
|
||||
let prop = &PROPERTIES[id];
|
||||
|
||||
match *prop {
|
||||
|
@ -196,16 +181,15 @@ impl ObjectImpl for ProgressBin {
|
|||
}
|
||||
|
||||
// Called right after construction of a new instance
|
||||
fn constructed(&self, obj: &glib::Object) {
|
||||
fn constructed(&self, obj: &Self::Type) {
|
||||
// Call the parent class' ::constructed() implementation first
|
||||
self.parent_constructed(obj);
|
||||
|
||||
// Here we actually add the pads we created in ProgressBin::new() to the
|
||||
// element so that GStreamer is aware of their existence.
|
||||
let bin = obj.downcast_ref::<gst::Bin>().unwrap();
|
||||
|
||||
// Add the progressreport element to the bin.
|
||||
bin.add(&self.progress).unwrap();
|
||||
obj.add(&self.progress).unwrap();
|
||||
|
||||
// Then set the ghost pad targets to the corresponding pads of the progressreport element.
|
||||
self.sinkpad
|
||||
|
@ -216,8 +200,8 @@ impl ObjectImpl for ProgressBin {
|
|||
.unwrap();
|
||||
|
||||
// And finally add the two ghostpads to the bin.
|
||||
bin.add_pad(&self.sinkpad).unwrap();
|
||||
bin.add_pad(&self.srcpad).unwrap();
|
||||
obj.add_pad(&self.sinkpad).unwrap();
|
||||
obj.add_pad(&self.srcpad).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,7 +210,7 @@ impl ElementImpl for ProgressBin {}
|
|||
|
||||
// Implementation of gst::Bin virtual methods
|
||||
impl BinImpl for ProgressBin {
|
||||
fn handle_message(&self, bin: &gst::Bin, msg: gst::Message) {
|
||||
fn handle_message(&self, bin: &Self::Type, msg: gst::Message) {
|
||||
use gst::MessageView;
|
||||
|
||||
match msg.view() {
|
||||
|
@ -256,15 +240,3 @@ impl BinImpl for ProgressBin {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Registers the type for our element, and then registers in GStreamer under
|
||||
// the name "rsprogressbin" for being able to instantiate it via e.g.
|
||||
// gst::ElementFactory::make().
|
||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
gst::Element::register(
|
||||
Some(plugin),
|
||||
"rsprogressbin",
|
||||
gst::Rank::None,
|
||||
ProgressBin::get_type(),
|
||||
)
|
||||
}
|
52
tutorial/src/progressbin/mod.rs
Normal file
52
tutorial/src/progressbin/mod.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
// Copyright (C) 2020 Sebastian Dröge <sebastian@centricular.com>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use glib::prelude::*;
|
||||
|
||||
mod imp;
|
||||
|
||||
// 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, GEnum)]
|
||||
#[repr(u32)]
|
||||
#[genum(type_name = "GstProgressBinOutput")]
|
||||
pub enum ProgressBinOutput {
|
||||
#[genum(
|
||||
name = "Println: Outputs the progress using a println! macro.",
|
||||
nick = "println"
|
||||
)]
|
||||
Println = 0,
|
||||
#[genum(
|
||||
name = "Debug Category: Outputs the progress as info logs under the element's debug category.",
|
||||
nick = "debug-category"
|
||||
)]
|
||||
DebugCategory = 1,
|
||||
}
|
||||
|
||||
// The public Rust wrapper type for our element
|
||||
glib_wrapper! {
|
||||
pub struct ProgressBin(ObjectSubclass<imp::ProgressBin>) @extends gst::Bin, gst::Element, gst::Object;
|
||||
}
|
||||
|
||||
// GStreamer elements need to be thread-safe. For the private implementation this is automatically
|
||||
// enforced but for the public wrapper type we need to specify this manually.
|
||||
unsafe impl Send for ProgressBin {}
|
||||
unsafe impl Sync for ProgressBin {}
|
||||
|
||||
// Registers the type for our element, and then registers in GStreamer under
|
||||
// the name "rsprogressbin" for being able to instantiate it via e.g.
|
||||
// gst::ElementFactory::make().
|
||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
gst::Element::register(
|
||||
Some(plugin),
|
||||
"rsprogressbin",
|
||||
gst::Rank::None,
|
||||
ProgressBin::static_type(),
|
||||
)
|
||||
}
|
|
@ -17,6 +17,16 @@ use std::sync::Mutex;
|
|||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
// This module contains the private implementation details of our element
|
||||
//
|
||||
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||
gst::DebugCategory::new(
|
||||
"rsrgb2gray",
|
||||
gst::DebugColorFlags::empty(),
|
||||
Some("Rust RGB-GRAY converter"),
|
||||
)
|
||||
});
|
||||
|
||||
// Default values of properties
|
||||
const DEFAULT_INVERT: bool = false;
|
||||
const DEFAULT_SHIFT: u32 = 0;
|
||||
|
@ -68,19 +78,11 @@ struct State {
|
|||
}
|
||||
|
||||
// Struct containing all the element data
|
||||
struct Rgb2Gray {
|
||||
pub struct Rgb2Gray {
|
||||
settings: Mutex<Settings>,
|
||||
state: Mutex<Option<State>>,
|
||||
}
|
||||
|
||||
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||
gst::DebugCategory::new(
|
||||
"rsrgb2gray",
|
||||
gst::DebugColorFlags::empty(),
|
||||
Some("Rust RGB-GRAY converter"),
|
||||
)
|
||||
});
|
||||
|
||||
impl Rgb2Gray {
|
||||
// Converts one pixel of BGRx to a grayscale value, shifting and/or
|
||||
// inverting it as configured
|
||||
|
@ -113,6 +115,7 @@ impl Rgb2Gray {
|
|||
// up the class data
|
||||
impl ObjectSubclass for Rgb2Gray {
|
||||
const NAME: &'static str = "RsRgb2Gray";
|
||||
type Type = super::Rgb2Gray;
|
||||
type ParentType = gst_base::BaseTransform;
|
||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||
type Class = subclass::simple::ClassStruct<Self>;
|
||||
|
@ -139,7 +142,7 @@ impl ObjectSubclass for Rgb2Gray {
|
|||
// will automatically instantiate pads for them.
|
||||
//
|
||||
// Our element here can convert BGRx to BGRx or GRAY8, both being grayscale.
|
||||
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
||||
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
|
||||
|
@ -239,9 +242,8 @@ impl ObjectSubclass for Rgb2Gray {
|
|||
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: &glib::Object, id: usize, value: &glib::Value) {
|
||||
fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) {
|
||||
let prop = &PROPERTIES[id];
|
||||
let element = obj.downcast_ref::<gst_base::BaseTransform>().unwrap();
|
||||
|
||||
match *prop {
|
||||
subclass::Property("invert", ..) => {
|
||||
|
@ -249,7 +251,7 @@ impl ObjectImpl for Rgb2Gray {
|
|||
let invert = value.get_some().expect("type checked upstream");
|
||||
gst_info!(
|
||||
CAT,
|
||||
obj: element,
|
||||
obj: obj,
|
||||
"Changing invert from {} to {}",
|
||||
settings.invert,
|
||||
invert
|
||||
|
@ -261,7 +263,7 @@ impl ObjectImpl for Rgb2Gray {
|
|||
let shift = value.get_some().expect("type checked upstream");
|
||||
gst_info!(
|
||||
CAT,
|
||||
obj: element,
|
||||
obj: obj,
|
||||
"Changing shift from {} to {}",
|
||||
settings.shift,
|
||||
shift
|
||||
|
@ -274,7 +276,7 @@ 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: &glib::Object, id: usize) -> Result<glib::Value, ()> {
|
||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> Result<glib::Value, ()> {
|
||||
let prop = &PROPERTIES[id];
|
||||
|
||||
match *prop {
|
||||
|
@ -302,7 +304,7 @@ impl BaseTransformImpl for Rgb2Gray {
|
|||
// In our case that means that:
|
||||
fn transform_caps(
|
||||
&self,
|
||||
element: &gst_base::BaseTransform,
|
||||
element: &Self::Type,
|
||||
direction: gst::PadDirection,
|
||||
caps: &gst::Caps,
|
||||
filter: Option<&gst::Caps>,
|
||||
|
@ -360,7 +362,7 @@ impl BaseTransformImpl for Rgb2Gray {
|
|||
// Returns the size of one processing unit (i.e. a frame in our case) corresponding
|
||||
// to the given caps. This is used for allocating a big enough output buffer and
|
||||
// sanity checking the input buffer size, among other things.
|
||||
fn get_unit_size(&self, _element: &gst_base::BaseTransform, caps: &gst::Caps) -> Option<usize> {
|
||||
fn get_unit_size(&self, _element: &Self::Type, caps: &gst::Caps) -> Option<usize> {
|
||||
gst_video::VideoInfo::from_caps(caps)
|
||||
.map(|info| info.size())
|
||||
.ok()
|
||||
|
@ -374,7 +376,7 @@ impl BaseTransformImpl for Rgb2Gray {
|
|||
// the width, stride, etc when transforming buffers
|
||||
fn set_caps(
|
||||
&self,
|
||||
element: &gst_base::BaseTransform,
|
||||
element: &Self::Type,
|
||||
incaps: &gst::Caps,
|
||||
outcaps: &gst::Caps,
|
||||
) -> Result<(), gst::LoggableError> {
|
||||
|
@ -402,7 +404,7 @@ impl BaseTransformImpl for Rgb2Gray {
|
|||
|
||||
// Called when shutting down the element so we can release all stream-related state
|
||||
// There's also start(), which is called whenever starting the element again
|
||||
fn stop(&self, element: &gst_base::BaseTransform) -> Result<(), gst::ErrorMessage> {
|
||||
fn stop(&self, element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
||||
// Drop state
|
||||
let _ = self.state.lock().unwrap().take();
|
||||
|
||||
|
@ -414,7 +416,7 @@ impl BaseTransformImpl for Rgb2Gray {
|
|||
// Does the actual transformation of the input buffer to the output buffer
|
||||
fn transform(
|
||||
&self,
|
||||
element: &gst_base::BaseTransform,
|
||||
element: &Self::Type,
|
||||
inbuf: &gst::Buffer,
|
||||
outbuf: &mut gst::BufferRef,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
|
@ -562,15 +564,3 @@ impl BaseTransformImpl for Rgb2Gray {
|
|||
Ok(gst::FlowSuccess::Ok)
|
||||
}
|
||||
}
|
||||
|
||||
// Registers the type for our element, and then registers in GStreamer under
|
||||
// the name "rsrgb2gray" for being able to instantiate it via e.g.
|
||||
// gst::ElementFactory::make().
|
||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
gst::Element::register(
|
||||
Some(plugin),
|
||||
"rsrgb2gray",
|
||||
gst::Rank::None,
|
||||
Rgb2Gray::get_type(),
|
||||
)
|
||||
}
|
33
tutorial/src/rgb2gray/mod.rs
Normal file
33
tutorial/src/rgb2gray/mod.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Copyright (C) 2020 Sebastian Dröge <sebastian@centricular.com>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use glib::prelude::*;
|
||||
|
||||
mod imp;
|
||||
|
||||
// The public Rust wrapper type for our element
|
||||
glib_wrapper! {
|
||||
pub struct Rgb2Gray(ObjectSubclass<imp::Rgb2Gray>) @extends gst_base::BaseTransform, gst::Element, gst::Object;
|
||||
}
|
||||
|
||||
// GStreamer elements need to be thread-safe. For the private implementation this is automatically
|
||||
// enforced but for the public wrapper type we need to specify this manually.
|
||||
unsafe impl Send for Rgb2Gray {}
|
||||
unsafe impl Sync for Rgb2Gray {}
|
||||
|
||||
// Registers the type for our element, and then registers in GStreamer under
|
||||
// the name "rsrgb2gray" for being able to instantiate it via e.g.
|
||||
// gst::ElementFactory::make().
|
||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
gst::Element::register(
|
||||
Some(plugin),
|
||||
"rsrgb2gray",
|
||||
gst::Rank::None,
|
||||
Rgb2Gray::static_type(),
|
||||
)
|
||||
}
|
|
@ -24,6 +24,16 @@ use num_traits::float::Float;
|
|||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
// This module contains the private implementation details of our element
|
||||
|
||||
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||
gst::DebugCategory::new(
|
||||
"rssinesrc",
|
||||
gst::DebugColorFlags::empty(),
|
||||
Some("Rust Sine Wave Source"),
|
||||
)
|
||||
});
|
||||
|
||||
// Default values of properties
|
||||
const DEFAULT_SAMPLES_PER_BUFFER: u32 = 1024;
|
||||
const DEFAULT_FREQ: u32 = 440;
|
||||
|
@ -134,20 +144,12 @@ struct ClockWait {
|
|||
}
|
||||
|
||||
// Struct containing all the element data
|
||||
struct SineSrc {
|
||||
pub struct SineSrc {
|
||||
settings: Mutex<Settings>,
|
||||
state: Mutex<State>,
|
||||
clock_wait: Mutex<ClockWait>,
|
||||
}
|
||||
|
||||
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||
gst::DebugCategory::new(
|
||||
"rssinesrc",
|
||||
gst::DebugColorFlags::empty(),
|
||||
Some("Rust Sine Wave Source"),
|
||||
)
|
||||
});
|
||||
|
||||
impl SineSrc {
|
||||
fn process<F: Float + FromByteSlice>(
|
||||
data: &mut [u8],
|
||||
|
@ -198,6 +200,7 @@ impl SineSrc {
|
|||
// up the class data
|
||||
impl ObjectSubclass for SineSrc {
|
||||
const NAME: &'static str = "RsSineSrc";
|
||||
type Type = super::SineSrc;
|
||||
type ParentType = gst_base::PushSrc;
|
||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||
type Class = subclass::simple::ClassStruct<Self>;
|
||||
|
@ -228,7 +231,7 @@ impl ObjectSubclass for SineSrc {
|
|||
// will automatically instantiate pads for them.
|
||||
//
|
||||
// Our element here can output f32 and f64
|
||||
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
||||
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
|
||||
|
@ -281,22 +284,20 @@ impl ObjectSubclass for SineSrc {
|
|||
// Implementation of glib::Object virtual methods
|
||||
impl ObjectImpl for SineSrc {
|
||||
// Called right after construction of a new instance
|
||||
fn constructed(&self, obj: &glib::Object) {
|
||||
fn constructed(&self, obj: &Self::Type) {
|
||||
// Call the parent class' ::constructed() implementation first
|
||||
self.parent_constructed(obj);
|
||||
|
||||
// Initialize live-ness and notify the base class that
|
||||
// we'd like to operate in Time format
|
||||
let basesrc = obj.downcast_ref::<gst_base::BaseSrc>().unwrap();
|
||||
basesrc.set_live(DEFAULT_IS_LIVE);
|
||||
basesrc.set_format(gst::Format::Time);
|
||||
obj.set_live(DEFAULT_IS_LIVE);
|
||||
obj.set_format(gst::Format::Time);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) {
|
||||
let prop = &PROPERTIES[id];
|
||||
let basesrc = obj.downcast_ref::<gst_base::BaseSrc>().unwrap();
|
||||
|
||||
match *prop {
|
||||
subclass::Property("samples-per-buffer", ..) => {
|
||||
|
@ -304,7 +305,7 @@ impl ObjectImpl for SineSrc {
|
|||
let samples_per_buffer = value.get_some().expect("type checked upstream");
|
||||
gst_info!(
|
||||
CAT,
|
||||
obj: basesrc,
|
||||
obj: obj,
|
||||
"Changing samples-per-buffer from {} to {}",
|
||||
settings.samples_per_buffer,
|
||||
samples_per_buffer
|
||||
|
@ -312,14 +313,14 @@ impl ObjectImpl for SineSrc {
|
|||
settings.samples_per_buffer = samples_per_buffer;
|
||||
drop(settings);
|
||||
|
||||
let _ = basesrc.post_message(gst::message::Latency::builder().src(basesrc).build());
|
||||
let _ = obj.post_message(gst::message::Latency::builder().src(obj).build());
|
||||
}
|
||||
subclass::Property("freq", ..) => {
|
||||
let mut settings = self.settings.lock().unwrap();
|
||||
let freq = value.get_some().expect("type checked upstream");
|
||||
gst_info!(
|
||||
CAT,
|
||||
obj: basesrc,
|
||||
obj: obj,
|
||||
"Changing freq from {} to {}",
|
||||
settings.freq,
|
||||
freq
|
||||
|
@ -331,7 +332,7 @@ impl ObjectImpl for SineSrc {
|
|||
let volume = value.get_some().expect("type checked upstream");
|
||||
gst_info!(
|
||||
CAT,
|
||||
obj: basesrc,
|
||||
obj: obj,
|
||||
"Changing volume from {} to {}",
|
||||
settings.volume,
|
||||
volume
|
||||
|
@ -343,7 +344,7 @@ impl ObjectImpl for SineSrc {
|
|||
let mute = value.get_some().expect("type checked upstream");
|
||||
gst_info!(
|
||||
CAT,
|
||||
obj: basesrc,
|
||||
obj: obj,
|
||||
"Changing mute from {} to {}",
|
||||
settings.mute,
|
||||
mute
|
||||
|
@ -355,7 +356,7 @@ impl ObjectImpl for SineSrc {
|
|||
let is_live = value.get_some().expect("type checked upstream");
|
||||
gst_info!(
|
||||
CAT,
|
||||
obj: basesrc,
|
||||
obj: obj,
|
||||
"Changing is-live from {} to {}",
|
||||
settings.is_live,
|
||||
is_live
|
||||
|
@ -368,7 +369,7 @@ 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: &glib::Object, id: usize) -> Result<glib::Value, ()> {
|
||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> Result<glib::Value, ()> {
|
||||
let prop = &PROPERTIES[id];
|
||||
|
||||
match *prop {
|
||||
|
@ -404,14 +405,12 @@ impl ElementImpl for SineSrc {
|
|||
// the element again.
|
||||
fn change_state(
|
||||
&self,
|
||||
element: &gst::Element,
|
||||
element: &Self::Type,
|
||||
transition: gst::StateChange,
|
||||
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
||||
let basesrc = element.downcast_ref::<gst_base::BaseSrc>().unwrap();
|
||||
|
||||
// Configure live'ness once here just before starting the source
|
||||
if let gst::StateChange::ReadyToPaused = transition {
|
||||
basesrc.set_live(self.settings.lock().unwrap().is_live);
|
||||
element.set_live(self.settings.lock().unwrap().is_live);
|
||||
}
|
||||
|
||||
// Call the parent class' implementation of ::change_state()
|
||||
|
@ -427,11 +426,7 @@ impl BaseSrcImpl for SineSrc {
|
|||
//
|
||||
// We simply remember the resulting AudioInfo from the caps to be able to use this for knowing
|
||||
// the sample rate, etc. when creating buffers
|
||||
fn set_caps(
|
||||
&self,
|
||||
element: &gst_base::BaseSrc,
|
||||
caps: &gst::Caps,
|
||||
) -> Result<(), gst::LoggableError> {
|
||||
fn set_caps(&self, element: &Self::Type, caps: &gst::Caps) -> Result<(), gst::LoggableError> {
|
||||
use std::f64::consts::PI;
|
||||
|
||||
let info = gst_audio::AudioInfo::from_caps(caps).map_err(|_| {
|
||||
|
@ -481,7 +476,7 @@ impl BaseSrcImpl for SineSrc {
|
|||
}
|
||||
|
||||
// Called when starting, so we can initialize all stream-related state to its defaults
|
||||
fn start(&self, element: &gst_base::BaseSrc) -> Result<(), gst::ErrorMessage> {
|
||||
fn start(&self, element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
||||
// Reset state
|
||||
*self.state.lock().unwrap() = Default::default();
|
||||
self.unlock_stop(element)?;
|
||||
|
@ -492,7 +487,7 @@ impl BaseSrcImpl for SineSrc {
|
|||
}
|
||||
|
||||
// Called when shutting down the element so we can release all stream-related state
|
||||
fn stop(&self, element: &gst_base::BaseSrc) -> Result<(), gst::ErrorMessage> {
|
||||
fn stop(&self, element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
||||
// Reset state
|
||||
*self.state.lock().unwrap() = Default::default();
|
||||
self.unlock(element)?;
|
||||
|
@ -502,7 +497,7 @@ impl BaseSrcImpl for SineSrc {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn query(&self, element: &gst_base::BaseSrc, query: &mut gst::QueryRef) -> bool {
|
||||
fn query(&self, element: &Self::Type, query: &mut gst::QueryRef) -> bool {
|
||||
use gst::QueryView;
|
||||
|
||||
match query.view_mut() {
|
||||
|
@ -528,7 +523,7 @@ impl BaseSrcImpl for SineSrc {
|
|||
}
|
||||
}
|
||||
|
||||
fn fixate(&self, element: &gst_base::BaseSrc, mut caps: gst::Caps) -> gst::Caps {
|
||||
fn fixate(&self, element: &Self::Type, mut caps: gst::Caps) -> gst::Caps {
|
||||
// Fixate the caps. BaseSrc will do some fixation for us, but
|
||||
// as we allow any rate between 1 and MAX it would fixate to 1. 1Hz
|
||||
// is generally not a useful sample rate.
|
||||
|
@ -549,11 +544,11 @@ impl BaseSrcImpl for SineSrc {
|
|||
self.parent_fixate(element, caps)
|
||||
}
|
||||
|
||||
fn is_seekable(&self, _element: &gst_base::BaseSrc) -> bool {
|
||||
fn is_seekable(&self, _element: &Self::Type) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn do_seek(&self, element: &gst_base::BaseSrc, segment: &mut gst::Segment) -> bool {
|
||||
fn do_seek(&self, element: &Self::Type, segment: &mut gst::Segment) -> bool {
|
||||
// Handle seeking here. For Time and Default (sample offset) seeks we can
|
||||
// do something and have to update our sample offset and accumulator accordingly.
|
||||
//
|
||||
|
@ -659,7 +654,7 @@ impl BaseSrcImpl for SineSrc {
|
|||
}
|
||||
}
|
||||
|
||||
fn unlock(&self, element: &gst_base::BaseSrc) -> Result<(), gst::ErrorMessage> {
|
||||
fn unlock(&self, element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
||||
// This should unblock the create() function ASAP, so we
|
||||
// just unschedule the clock it here, if any.
|
||||
gst_debug!(CAT, obj: element, "Unlocking");
|
||||
|
@ -672,7 +667,7 @@ impl BaseSrcImpl for SineSrc {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn unlock_stop(&self, element: &gst_base::BaseSrc) -> Result<(), gst::ErrorMessage> {
|
||||
fn unlock_stop(&self, element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
||||
// This signals that unlocking is done, so we can reset
|
||||
// all values again.
|
||||
gst_debug!(CAT, obj: element, "Unlock stop");
|
||||
|
@ -685,7 +680,7 @@ impl BaseSrcImpl for SineSrc {
|
|||
|
||||
impl PushSrcImpl for SineSrc {
|
||||
// Creates the audio buffers
|
||||
fn create(&self, element: &gst_base::PushSrc) -> Result<gst::Buffer, gst::FlowError> {
|
||||
fn create(&self, element: &Self::Type) -> Result<gst::Buffer, gst::FlowError> {
|
||||
// Keep a local copy of the values of all our properties at this very moment. This
|
||||
// ensures that the mutex is never locked for long and the application wouldn't
|
||||
// have to block until this function returns when getting/setting property values
|
||||
|
@ -830,15 +825,3 @@ impl PushSrcImpl for SineSrc {
|
|||
Ok(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
// Registers the type for our element, and then registers in GStreamer under
|
||||
// the name "sinesrc" for being able to instantiate it via e.g.
|
||||
// gst::ElementFactory::make().
|
||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
gst::Element::register(
|
||||
Some(plugin),
|
||||
"rssinesrc",
|
||||
gst::Rank::None,
|
||||
SineSrc::get_type(),
|
||||
)
|
||||
}
|
33
tutorial/src/sinesrc/mod.rs
Normal file
33
tutorial/src/sinesrc/mod.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Copyright (C) 2020 Sebastian Dröge <sebastian@centricular.com>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use glib::prelude::*;
|
||||
|
||||
mod imp;
|
||||
|
||||
// The public Rust wrapper type for our element
|
||||
glib_wrapper! {
|
||||
pub struct SineSrc(ObjectSubclass<imp::SineSrc>) @extends gst_base::BaseSrc, gst::Element, gst::Object;
|
||||
}
|
||||
|
||||
// GStreamer elements need to be thread-safe. For the private implementation this is automatically
|
||||
// enforced but for the public wrapper type we need to specify this manually.
|
||||
unsafe impl Send for SineSrc {}
|
||||
unsafe impl Sync for SineSrc {}
|
||||
|
||||
// Registers the type for our element, and then registers in GStreamer under
|
||||
// the name "sinesrc" for being able to instantiate it via e.g.
|
||||
// gst::ElementFactory::make().
|
||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
gst::Element::register(
|
||||
Some(plugin),
|
||||
"rssinesrc",
|
||||
gst::Rank::None,
|
||||
SineSrc::static_type(),
|
||||
)
|
||||
}
|
|
@ -153,7 +153,7 @@ fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
|||
}
|
||||
```
|
||||
|
||||
With that our `src/lib.rs` is complete, and all following code is only in `src/rgb2gray.rs`. At the top of the new file we first need to add various `use-directives` to import various types and functions we’re going to use into the current module’s scope.
|
||||
With that our `src/lib.rs` is complete, and all following code is only in `src/rgb2gray/imp.rs`. At the top of the new file we first need to add various `use-directives` to import various types and functions we’re going to use into the current module’s scope.
|
||||
|
||||
```rust
|
||||
use glib;
|
||||
|
@ -178,12 +178,13 @@ GStreamer is based on the GLib object system ([GObject](https://developer.gnome.
|
|||
So, as a next step we need to register a new type for our RGB to Grayscale converter GStreamer element with the GObject type system, and then register that type with GStreamer to be able to create new instances of it. We do this with the following code
|
||||
|
||||
```rust
|
||||
struct Rgb2Gray{}
|
||||
pub struct Rgb2Gray{}
|
||||
|
||||
impl Rgb2Gray{}
|
||||
|
||||
impl ObjectSubclass for Rgb2Gray {
|
||||
const NAME: &'static str = "RsRgb2Gray";
|
||||
type Type = super::Rgb2Gray;
|
||||
type ParentType = gst_base::BaseTransform;
|
||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||
type Class = subclass::simple::ClassStruct<Self>;
|
||||
|
@ -200,29 +201,50 @@ impl ObjectSubclass for Rgb2Gray {
|
|||
}
|
||||
}
|
||||
|
||||
This defines a struct `Rgb2Gray` which is empty for now and an empty implementation of the struct which will later be used. The `ObjectSubclass` trait is implemented on the struct `Rgb2Gray` for providing static information about the type to the type system. By implementing `ObjectSubclass` we allow registering our struct with the GObject object system.
|
||||
|
||||
`ObjectSubclass` has an associated constant which contains the name of the type, some associated types, and functions for initializing/returning a new instance of our element (`new`) and for initializing the class metadata (`class_init`, more on that later). We simply let those functions proxy to associated functions on the `Rgb2Gray` struct that we’re going to define at a later time.
|
||||
|
||||
We also add the following code is in `src/rgb2gray/mod.rs`:
|
||||
|
||||
```rust
|
||||
use glib::prelude::*;
|
||||
|
||||
mod imp;
|
||||
|
||||
// The public Rust wrapper type for our element
|
||||
glib_wrapper! {
|
||||
pub struct Rgb2Gray(ObjectSubclass<imp::Rgb2Gray>) @extends gst_base::BaseTransform, gst::Element, gst::Object;
|
||||
}
|
||||
|
||||
// GStreamer elements need to be thread-safe. For the private implementation this is automatically
|
||||
// enforced but for the public wrapper type we need to specify this manually.
|
||||
unsafe impl Send for Rgb2Gray {}
|
||||
unsafe impl Sync for Rgb2Gray {}
|
||||
|
||||
// Registers the type for our element, and then registers in GStreamer under
|
||||
// the name "rsrgb2gray" for being able to instantiate it via e.g.
|
||||
// gst::ElementFactory::make().
|
||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
gst::Element::register(
|
||||
Some(plugin),
|
||||
"rsrgb2gray",
|
||||
gst::Rank::None,
|
||||
Rgb2Gray::get_type(),
|
||||
Rgb2Gray::static_type(),
|
||||
)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
This defines a struct `Rgb2Gray` which is empty for now and an empty implementation of the struct which will later be used. The `ObjectSubclass` trait is implemented on the struct `Rgb2Gray` for providing static information about the type to the type system. By implementing `ObjectSubclass` we allow registering our struct with the GObject object system.
|
||||
This defines a Rust wrapper type for our element subclass. This is similar to `gst::Element` and others and provides the public interface to our element.
|
||||
|
||||
`ObjectSubclass` has an associated constant which contains the name of the type, some associated types, and functions for initializing/returning a new instance of our element (`new`) and for initializing the class metadata (`class_init`, more on that later). We simply let those functions proxy to associated functions on the `Rgb2Gray` struct that we’re going to define at a later time.
|
||||
|
||||
In addition, we also define a `register` function (the one that is already called from our `plugin_init` function). When `register` function is called it registers the element factory with GStreamer based on the type ID, to be able to create new instances of it with the name “rsrgb2gray” (e.g. when using [`gst::ElementFactory::make`](https://slomo.pages.freedesktop.org/rustdocs/gstreamer/gstreamer/struct.ElementFactory.html#method.make)). The `get_type` function will register the type with the GObject type system on the first call and the next time it's called (or on all the following calls) it will return the type ID.
|
||||
In addition, we also define a `register` function (the one that is already called from our `plugin_init` function). When `register` function is called it registers the element factory with GStreamer based on the type ID, to be able to create new instances of it with the name “rsrgb2gray” (e.g. when using [`gst::ElementFactory::make`](https://slomo.pages.freedesktop.org/rustdocs/gstreamer/gstreamer/struct.ElementFactory.html#method.make)). The `static_type` function will register the type with the GObject type system on the first call and the next time it's called (or on all the following calls) it will return the type ID.
|
||||
|
||||
## Type Class & Instance Initialization
|
||||
|
||||
As a next step we implement the `new` funtion and `class_init` functions. In the first version, this struct is empty for now but we will later use it to store all state of our element.
|
||||
|
||||
```rust
|
||||
struct Rgb2Gray {
|
||||
pub struct Rgb2Gray {
|
||||
}
|
||||
|
||||
impl Rgb2Gray{}
|
||||
|
@ -235,7 +257,7 @@ impl ObjectSubclass for Rgb2Gray {
|
|||
}
|
||||
}
|
||||
|
||||
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
klass.set_metadata(
|
||||
"RGB-GRAY Converter",
|
||||
"Filter/Effect/Converter/Video",
|
||||
|
@ -267,17 +289,18 @@ impl BaseTransformImpl for Rgb2Gray {}
|
|||
|
||||
With all this defined, `gst-inspect-1.0` should be able to show some more information about our element already but will still complain that it’s not complete yet.
|
||||
|
||||
**Side note:** This is the basic code that should be in `rgb2gray.rs` to successfully build the plugin. You can fill up the code while going through the later part of the tutorial.
|
||||
**Side note:** This is the basic code that should be in `src/rgb2gray/imp.rs` and `src/rgb2gray/mod.rs` to successfully build the plugin. You can fill up the code while going through the later part of the tutorial.
|
||||
|
||||
```rust
|
||||
//all imports...
|
||||
// all imports...
|
||||
|
||||
struct Rgb2Gray {}
|
||||
pub struct Rgb2Gray {}
|
||||
|
||||
impl Rgb2Gray {}
|
||||
|
||||
impl ObjectSubclass for Rgb2Gray {
|
||||
const NAME: &'static str = "RsRgb2Gray";
|
||||
type Type = super::Rgb2Gray;
|
||||
type ParentType = gst_base::BaseTransform;
|
||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||
type Class = subclass::simple::ClassStruct<Self>;
|
||||
|
@ -290,13 +313,26 @@ impl ObjectImpl for Rgb2Gray {}
|
|||
impl ElementImpl for Rgb2Gray {}
|
||||
|
||||
impl BaseTransformImpl for Rgb2Gray {}
|
||||
```
|
||||
|
||||
```rust
|
||||
use glib::prelude::*;
|
||||
|
||||
mod imp;
|
||||
|
||||
glib_wrapper! {
|
||||
pub struct Rgb2Gray(ObjectSubclass<imp::Rgb2Gray>) @extends gst_base::BaseTransform, gst::Element, gst::Object;
|
||||
}
|
||||
|
||||
unsafe impl Send for Rgb2Gray {}
|
||||
unsafe impl Sync for Rgb2Gray {}
|
||||
|
||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
gst::Element::register(
|
||||
Some(plugin),
|
||||
"rsrgb2gray",
|
||||
gst::Rank::None,
|
||||
Rgb2Gray::get_type(),
|
||||
Rgb2Gray::static_type(),
|
||||
)
|
||||
}
|
||||
```
|
||||
|
@ -401,7 +437,7 @@ struct State {
|
|||
out_info: gst_video::VideoInfo,
|
||||
}
|
||||
|
||||
struct Rgb2Gray {
|
||||
pub struct Rgb2Gray {
|
||||
state: Mutex<Option<State>>
|
||||
}
|
||||
|
||||
|
@ -426,7 +462,7 @@ Whenever input/output caps are configured on our element, the `set_caps` virtual
|
|||
impl BaseTransformImpl for Rgb2Gray {
|
||||
fn set_caps(
|
||||
&self,
|
||||
element: &gst_base::BaseTransform,
|
||||
element: &Self::Type,
|
||||
incaps: &gst::Caps,
|
||||
outcaps: &gst::Caps,
|
||||
) -> Result<(), gst::LoggableError> {
|
||||
|
@ -452,7 +488,7 @@ impl BaseTransformImpl for Rgb2Gray {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn stop(&self, element: &gst_base::BaseTransform) -> Result<(), gst::ErrorMessage> {
|
||||
fn stop(&self, element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
||||
// Drop state
|
||||
let _ = self.state.lock().unwrap().take();
|
||||
|
||||
|
@ -469,7 +505,7 @@ Next we have to provide information to the `BaseTransform` base class about the
|
|||
|
||||
```rust
|
||||
impl BaseTransformImpl for Rgb2Gray {
|
||||
fn get_unit_size(&self, _element: &gst_base::BaseTransform, caps: &gst::Caps) -> Option<usize> {
|
||||
fn get_unit_size(&self, _element: &Self::Type, caps: &gst::Caps) -> Option<usize> {
|
||||
gst_video::VideoInfo::from_caps(caps).map(|info| info.size())
|
||||
}
|
||||
}
|
||||
|
@ -489,7 +525,7 @@ This has to be implemented in the `transform_caps` virtual method, and looks as
|
|||
impl BaseTransformImpl for Rgb2Gray {
|
||||
fn transform_caps(
|
||||
&self,
|
||||
element: &gst_base::BaseTransform,
|
||||
element: &Self::Type,
|
||||
direction: gst::PadDirection,
|
||||
caps: &gst::Caps,
|
||||
filter: Option<&gst::Caps>,
|
||||
|
@ -579,7 +615,7 @@ Afterwards we have to actually call this function on every pixel. For this the t
|
|||
impl BaseTransformImpl for Rgb2Gray {
|
||||
fn transform(
|
||||
&self,
|
||||
element: &gst_base::BaseTransform,
|
||||
element: &Self::Type,
|
||||
inbuf: &gst::Buffer,
|
||||
outbuf: &mut gst::BufferRef,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
|
@ -752,7 +788,7 @@ static PROPERTIES: [subclass::Property; 2] = [
|
|||
}),
|
||||
];
|
||||
|
||||
struct Rgb2Gray {
|
||||
pub struct Rgb2Gray {
|
||||
settings: Mutex<Settings>,
|
||||
state: Mutex<Option<State>>,
|
||||
}
|
||||
|
@ -777,7 +813,7 @@ In the next step we have to make use of these: we need to tell the GObject type
|
|||
|
||||
```rust
|
||||
impl ObjectSubclass for Rgb2Gray {
|
||||
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
[...]
|
||||
klass.install_properties(&PROPERTIES);
|
||||
[...]
|
||||
|
@ -787,9 +823,8 @@ impl ObjectSubclass for Rgb2Gray {
|
|||
impl ObjectImpl for Rgb2Gray {
|
||||
[...]
|
||||
|
||||
fn set_property(&self, obj: &glib::Object, id: usize, value: &glib::Value) {
|
||||
fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) {
|
||||
let prop = &PROPERTIES[id];
|
||||
let element = obj.downcast_ref::<gst_base::BaseTransform>().unwrap();
|
||||
|
||||
match *prop {
|
||||
subclass::Property("invert", ..) => {
|
||||
|
@ -797,7 +832,7 @@ impl ObjectImpl for Rgb2Gray {
|
|||
let invert = value.get_some().expect("type checked upstream");
|
||||
gst_info!(
|
||||
CAT,
|
||||
obj: element,
|
||||
obj: obj,
|
||||
"Changing invert from {} to {}",
|
||||
settings.invert,
|
||||
invert
|
||||
|
@ -809,7 +844,7 @@ impl ObjectImpl for Rgb2Gray {
|
|||
let shift = value.get_some().expect("type checked upstream");
|
||||
gst_info!(
|
||||
CAT,
|
||||
obj: element,
|
||||
obj: obj,
|
||||
"Changing shift from {} to {}",
|
||||
settings.shift,
|
||||
shift
|
||||
|
@ -820,7 +855,7 @@ impl ObjectImpl for Rgb2Gray {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_property(&self, _obj: &glib::Object, id: usize) -> Result<glib::Value, ()> {
|
||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> Result<glib::Value, ()> {
|
||||
let prop = &PROPERTIES[id];
|
||||
|
||||
match *prop {
|
||||
|
@ -871,7 +906,7 @@ impl Rgb2Gray {
|
|||
impl BaseTransformImpl for Rgb2Gray {
|
||||
fn transform(
|
||||
&self,
|
||||
element: &gst_base::BaseTransform,
|
||||
element: &Self::Type,
|
||||
inbuf: &gst::Buffer,
|
||||
outbuf: &mut gst::BufferRef,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
|
|
|
@ -19,6 +19,8 @@ Our sine wave element is going to produce raw audio, with a number of channels a
|
|||
|
||||
So let's get started with all the boilerplate. This time our element will be based on the [`PushSrc`](https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer-libs/html/GstPushSrc.html) base class instead of [`BaseTransform`](https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer-libs/html/GstBaseTransform.html). `PushSrc` is a subclass of the [`BaseSrc`](https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer-libs/html/GstBaseSrc.html) base class that only works in push mode, i.e. creates buffers as they arrive instead of allowing downstream elements to explicitly pull them.
|
||||
|
||||
In `src/sinesrc/imp.rs`:
|
||||
|
||||
```rust
|
||||
use glib;
|
||||
use gst;
|
||||
|
@ -134,7 +136,7 @@ impl Default for State {
|
|||
}
|
||||
|
||||
// Struct containing all the element data
|
||||
struct SineSrc {
|
||||
pub struct SineSrc {
|
||||
settings: Mutex<Settings>,
|
||||
state: Mutex<State>,
|
||||
}
|
||||
|
@ -149,6 +151,7 @@ static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
|||
|
||||
impl ObjectSubclass for SineSrc {
|
||||
const NAME: &'static str = "RsSineSrc";
|
||||
type Type = super::SineSrc;
|
||||
type ParentType = gst_base::PushSrc;
|
||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||
type Class = subclass::simple::ClassStruct<Self>;
|
||||
|
@ -179,7 +182,7 @@ impl ObjectSubclass for SineSrc {
|
|||
// will automatically instantiate pads for them.
|
||||
//
|
||||
// Our element here can output f32 and f64
|
||||
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
||||
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
|
||||
|
@ -231,22 +234,20 @@ impl ObjectSubclass for SineSrc {
|
|||
|
||||
impl ObjectImpl for SineSrc {
|
||||
// Called right after construction of a new instance
|
||||
fn constructed(&self, obj: &glib::Object) {
|
||||
fn constructed(&self, obj: &Self::Type) {
|
||||
// Call the parent class' ::constructed() implementation first
|
||||
self.parent_constructed(obj);
|
||||
|
||||
// Initialize live-ness and notify the base class that
|
||||
// we'd like to operate in Time format
|
||||
let basesrc = obj.downcast_ref::<gst_base::BaseSrc>().unwrap();
|
||||
basesrc.set_live(DEFAULT_IS_LIVE);
|
||||
basesrc.set_format(gst::Format::Time);
|
||||
obj.set_live(DEFAULT_IS_LIVE);
|
||||
obj.set_format(gst::Format::Time);
|
||||
}
|
||||
|
||||
// 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: u32, value: &glib::Value) {
|
||||
fn set_property(&self, obj: &Self::Type, id: u32, value: &glib::Value) {
|
||||
let prop = &PROPERTIES[id as usize];
|
||||
let element = obj.clone().downcast::<BaseSrc>().unwrap();
|
||||
|
||||
match *prop {
|
||||
Property::UInt("samples-per-buffer", ..) => {
|
||||
|
@ -254,7 +255,7 @@ impl ObjectImpl for SineSrc {
|
|||
let samples_per_buffer = value.get_some().expect("type checked upstream");
|
||||
gst_info!(
|
||||
CAT,
|
||||
obj: &element,
|
||||
obj: obj,
|
||||
"Changing samples-per-buffer from {} to {}",
|
||||
settings.samples_per_buffer,
|
||||
samples_per_buffer
|
||||
|
@ -263,14 +264,14 @@ impl ObjectImpl for SineSrc {
|
|||
drop(settings);
|
||||
|
||||
let _ =
|
||||
element.post_message(&gst::Message::new_latency().src(Some(&element)).build());
|
||||
obj.post_message(&gst::Message::new_latency().src(Some(obj)).build());
|
||||
}
|
||||
Property::UInt("freq", ..) => {
|
||||
let mut settings = self.settings.lock().unwrap();
|
||||
let freq = value.get_some().expect("type checked upstream");
|
||||
gst_info!(
|
||||
CAT,
|
||||
obj: &element,
|
||||
obj: obj,
|
||||
"Changing freq from {} to {}",
|
||||
settings.freq,
|
||||
freq
|
||||
|
@ -282,7 +283,7 @@ impl ObjectImpl for SineSrc {
|
|||
let volume = value.get_some().expect("type checked upstream");
|
||||
gst_info!(
|
||||
CAT,
|
||||
obj: &element,
|
||||
obj: obj,
|
||||
"Changing volume from {} to {}",
|
||||
settings.volume,
|
||||
volume
|
||||
|
@ -294,7 +295,7 @@ impl ObjectImpl for SineSrc {
|
|||
let mute = value.get_some().expect("type checked upstream");
|
||||
gst_info!(
|
||||
CAT,
|
||||
obj: &element,
|
||||
obj: obj,
|
||||
"Changing mute from {} to {}",
|
||||
settings.mute,
|
||||
mute
|
||||
|
@ -306,7 +307,7 @@ impl ObjectImpl for SineSrc {
|
|||
let is_live = value.get_some().expect("type checked upstream");
|
||||
gst_info!(
|
||||
CAT,
|
||||
obj: &element,
|
||||
obj: obj,
|
||||
"Changing is-live from {} to {}",
|
||||
settings.is_live,
|
||||
is_live
|
||||
|
@ -319,7 +320,7 @@ 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: &glib::Object, id: u32) -> Result<glib::Value, ()> {
|
||||
fn get_property(&self, _obj: &Self::Type, id: u32) -> Result<glib::Value, ()> {
|
||||
let prop = &PROPERTIES[id as usize];
|
||||
|
||||
match *prop {
|
||||
|
@ -353,7 +354,7 @@ impl ElementImpl for SineSrc { }
|
|||
|
||||
impl BaseSrcImpl for SineSrc {
|
||||
// Called when starting, so we can initialize all stream-related state to its defaults
|
||||
fn start(&self, element: &BaseSrc) -> bool {
|
||||
fn start(&self, element: &Self::Type) -> bool {
|
||||
// Reset state
|
||||
*self.state.lock().unwrap() = Default::default();
|
||||
|
||||
|
@ -363,7 +364,7 @@ impl BaseSrcImpl for SineSrc {
|
|||
}
|
||||
|
||||
// Called when shutting down the element so we can release all stream-related state
|
||||
fn stop(&self, element: &BaseSrc) -> bool {
|
||||
fn stop(&self, element: &Self::Type) -> bool {
|
||||
// Reset state
|
||||
*self.state.lock().unwrap() = Default::default();
|
||||
|
||||
|
@ -372,6 +373,24 @@ impl BaseSrcImpl for SineSrc {
|
|||
true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In `src/sinesrc/mod.rs`:
|
||||
|
||||
```rust
|
||||
use glib::prelude::*;
|
||||
|
||||
mod imp;
|
||||
|
||||
// The public Rust wrapper type for our element
|
||||
glib_wrapper! {
|
||||
pub struct SineSrc(ObjectSubclass<imp::SineSrc>) @extends gst_base::BaseSrc, gst::Element, gst::Object;
|
||||
}
|
||||
|
||||
// GStreamer elements need to be thread-safe. For the private implementation this is automatically
|
||||
// enforced but for the public wrapper type we need to specify this manually.
|
||||
unsafe impl Send for SineSrc {}
|
||||
unsafe impl Sync for SineSrc {}
|
||||
|
||||
// Registers the type for our element, and then registers in GStreamer under
|
||||
// the name "rssinesrc" for being able to instantiate it via e.g.
|
||||
|
@ -381,7 +400,7 @@ pub fn register(plugin: &gst::Plugin) {
|
|||
Some(plugin),
|
||||
"rssinesrc",
|
||||
gst::Rank::None,
|
||||
SineSrc::get_type(),
|
||||
SineSrc::static_type(),
|
||||
)
|
||||
}
|
||||
```
|
||||
|
@ -410,7 +429,7 @@ The first part that we have to implement, just like last time, is caps negotiati
|
|||
First of all, we need to get notified whenever the caps that our source is configured for are changing. This will happen once in the very beginning and then whenever the pipeline topology or state changes and new caps would be more optimal for the new situation. This notification happens via the `BaseTransform::set_caps` virtual method.
|
||||
|
||||
```rust
|
||||
fn set_caps(&self, element: &BaseSrc, caps: &gst::Caps) -> Result<(), gst::LoggableError> {
|
||||
fn set_caps(&self, element: &Self::Type, caps: &gst::Caps) -> Result<(), gst::LoggableError> {
|
||||
use std::f64::consts::PI;
|
||||
|
||||
let info = gst_audio::AudioInfo::from_caps(caps).map_err(|_| {
|
||||
|
@ -469,7 +488,7 @@ As a last step we post a new `LATENCY` message on the bus whenever the sample
|
|||
`BaseSrc` is by default already selecting possible caps for us, if there are multiple options. However these defaults might not be (and often are not) ideal and we should override the default behaviour slightly. This is done in the `BaseSrc::fixate` virtual method.
|
||||
|
||||
```rust
|
||||
fn fixate(&self, element: &BaseSrc, mut caps: gst::Caps) -> gst::Caps {
|
||||
fn fixate(&self, element: &Self::Type, mut caps: gst::Caps) -> gst::Caps {
|
||||
// Fixate the caps. BaseSrc will do some fixation for us, but
|
||||
// as we allow any rate between 1 and MAX it would fixate to 1. 1Hz
|
||||
// is generally not a useful sample rate.
|
||||
|
@ -561,7 +580,7 @@ Now that this is done, we need to implement the `PushSrc::create` virtual meth
|
|||
```rust
|
||||
fn create(
|
||||
&self,
|
||||
element: &PushSrc,
|
||||
element: &Self::Type,
|
||||
) -> Result<gst::Buffer, gst::FlowReturn> {
|
||||
// Keep a local copy of the values of all our properties at this very moment. This
|
||||
// ensures that the mutex is never locked for long and the application wouldn't
|
||||
|
@ -742,7 +761,7 @@ Now we also have to tell the base class that we're running in live mode now. Thi
|
|||
impl ElementImpl for SineSrc {
|
||||
fn change_state(
|
||||
&self,
|
||||
element: &BaseSrc,
|
||||
element: &Self::Type,
|
||||
transition: gst::StateChange,
|
||||
) -> gst::StateChangeReturn {
|
||||
// Configure live'ness once here just before starting the source
|
||||
|
@ -763,7 +782,7 @@ And as a last step, we also need to notify downstream elements about our [laten
|
|||
This querying is done with the `LATENCY` query, which we will have to handle in the `BaseSrc::query()` function.
|
||||
|
||||
```rust
|
||||
fn query(&self, element: &BaseSrc, query: &mut gst::QueryRef) -> bool {
|
||||
fn query(&self, element: &Self::Type, query: &mut gst::QueryRef) -> bool {
|
||||
use gst::QueryView;
|
||||
|
||||
match query.view_mut() {
|
||||
|
@ -819,7 +838,7 @@ struct SineSrc {
|
|||
|
||||
[...]
|
||||
|
||||
fn unlock(&self, element: &BaseSrc) -> bool {
|
||||
fn unlock(&self, element: &Self::Type) -> bool {
|
||||
// This should unblock the create() function ASAP, so we
|
||||
// just unschedule the clock it here, if any.
|
||||
gst_debug!(CAT, obj: element, "Unlocking");
|
||||
|
@ -838,7 +857,7 @@ We store the clock ID in our struct, together with a boolean to signal whether w
|
|||
Once everything is unlocked, we need to reset things again so that data flow can happen in the future. This is done in the `unlock_stop` virtual method.
|
||||
|
||||
```rust
|
||||
fn unlock_stop(&self, element: &BaseSrc) -> bool {
|
||||
fn unlock_stop(&self, element: &Self::Type) -> bool {
|
||||
// This signals that unlocking is done, so we can reset
|
||||
// all values again.
|
||||
gst_debug!(CAT, obj: element, "Unlock stop");
|
||||
|
@ -903,11 +922,11 @@ As a last feature we implement seeking on our source element. In our case that o
|
|||
Seeking is implemented in the `BaseSrc::do_seek` virtual method, and signalling whether we can actually seek in the `is_seekable` virtual method.
|
||||
|
||||
```rust
|
||||
fn is_seekable(&self, _element: &BaseSrc) -> bool {
|
||||
fn is_seekable(&self, _element: &Self::Type) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn do_seek(&self, element: &BaseSrc, segment: &mut gst::Segment) -> bool {
|
||||
fn do_seek(&self, element: &Self::Type, segment: &mut gst::Segment) -> bool {
|
||||
// Handle seeking here. For Time and Default (sample offset) seeks we can
|
||||
// do something and have to update our sample offset and accumulator accordingly.
|
||||
//
|
||||
|
|
Loading…
Reference in a new issue