Port tutorials plugin to new subclassing API

This commit is contained in:
Sebastian Dröge 2018-11-24 10:31:58 +01:00
parent 485839a2a9
commit 4ac6863eed
4 changed files with 295 additions and 290 deletions

View file

@ -6,11 +6,9 @@ repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
license = "MIT/Apache-2.0" license = "MIT/Apache-2.0"
[dependencies] [dependencies]
gobject-subclass = { git = "https://github.com/gtk-rs/gobject-subclass" } glib = { git = "https://github.com/gtk-rs/glib", features = ["subclassing"] }
gst-plugin = { path="../gst-plugin" } gstreamer = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["subclassing"] }
glib = { git = "https://github.com/gtk-rs/glib" } gstreamer-base = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["subclassing"] }
gstreamer = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gstreamer-base = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gstreamer-video = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" } gstreamer-video = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gstreamer-audio = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" } gstreamer-audio = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
byte-slice-cast = "0.2" byte-slice-cast = "0.2"

View file

@ -6,10 +6,8 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
extern crate glib;
extern crate gobject_subclass;
#[macro_use] #[macro_use]
extern crate gst_plugin; extern crate glib;
#[macro_use] #[macro_use]
extern crate gstreamer as gst; extern crate gstreamer as gst;
extern crate gstreamer_audio as gst_audio; extern crate gstreamer_audio as gst_audio;
@ -35,14 +33,14 @@ fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
// Plugin name, plugin description, plugin entry point function, version number of this plugin, // Plugin name, plugin description, plugin entry point function, version number of this plugin,
// license of the plugin, source package name, binary package name, origin where it comes from // license of the plugin, source package name, binary package name, origin where it comes from
// and the date/time of release. // and the date/time of release.
plugin_define!( gst_plugin_define!(
b"rstutorial\0", "rstutorial",
b"Rust Tutorial Plugin\0", "Rust Tutorial Plugin",
plugin_init, plugin_init,
b"1.0\0", "1.0",
b"MIT/X11\0", "MIT/X11",
b"rstutorial\0", "rstutorial",
b"rstutorial\0", "rstutorial",
b"https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs\0", "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs",
b"2017-12-30\0" "2017-12-30"
); );

View file

@ -1,4 +1,4 @@
// Copyright (C) 2017 Sebastian Dröge <sebastian@centricular.com> // Copyright (C) 2017,2018 Sebastian Dröge <sebastian@centricular.com>
// //
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
@ -7,14 +7,15 @@
// except according to those terms. // except according to those terms.
use glib; use glib;
use glib::subclass;
use glib::subclass::prelude::*;
use gst; use gst;
use gst::prelude::*; use gst::prelude::*;
use gst::subclass::prelude::*;
use gst_base;
use gst_base::subclass::prelude::*;
use gst_video; use gst_video;
use gobject_subclass::object::*;
use gst_plugin::base_transform::*;
use gst_plugin::element::*;
use std::i32; use std::i32;
use std::sync::Mutex; use std::sync::Mutex;
@ -39,22 +40,27 @@ impl Default for Settings {
} }
// Metadata for the properties // Metadata for the properties
static PROPERTIES: [Property; 2] = [ static PROPERTIES: [subclass::Property; 2] = [
Property::Boolean( subclass::Property("invert", || {
glib::ParamSpec::boolean(
"invert", "invert",
"Invert", "Invert",
"Invert grayscale output", "Invert grayscale output",
DEFAULT_INVERT, DEFAULT_INVERT,
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::UInt( }),
subclass::Property("shift", || {
glib::ParamSpec::uint(
"shift", "shift",
"Shift", "Shift",
"Shift grayscale output (wrapping around)", "Shift grayscale output (wrapping around)",
(0, 255), 0,
255,
DEFAULT_SHIFT, DEFAULT_SHIFT,
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
}),
]; ];
// Stream-specific state, i.e. video format configuration // Stream-specific state, i.e. video format configuration
@ -71,9 +77,43 @@ struct Rgb2Gray {
} }
impl Rgb2Gray { impl Rgb2Gray {
// Converts one pixel of BGRx to a grayscale value, shifting and/or
// inverting it as configured
#[inline]
fn bgrx_to_gray(in_p: &[u8], shift: u8, invert: bool) -> u8 {
// See https://en.wikipedia.org/wiki/YUV#SDTV_with_BT.601
const R_Y: u32 = 19595; // 0.299 * 65536
const G_Y: u32 = 38470; // 0.587 * 65536
const B_Y: u32 = 7471; // 0.114 * 65536
assert_eq!(in_p.len(), 4);
let b = u32::from(in_p[0]);
let g = u32::from(in_p[1]);
let r = u32::from(in_p[2]);
let gray = ((r * R_Y) + (g * G_Y) + (b * B_Y)) / 65536;
let gray = (gray as u8).wrapping_add(shift);
if invert {
255 - gray
} else {
gray
}
}
}
impl ObjectSubclass for Rgb2Gray {
const NAME: &'static str = "RsRgb2Gray";
type ParentType = gst_base::BaseTransform;
type Instance = gst::subclass::ElementInstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;
glib_object_subclass!();
// Called when a new instance is to be created // Called when a new instance is to be created
fn new(_transform: &BaseTransform) -> Box<BaseTransformImpl<BaseTransform>> { fn new() -> Self {
Box::new(Self { Self {
cat: gst::DebugCategory::new( cat: gst::DebugCategory::new(
"rsrgb2gray", "rsrgb2gray",
gst::DebugColorFlags::empty(), gst::DebugColorFlags::empty(),
@ -81,7 +121,7 @@ impl Rgb2Gray {
), ),
settings: Mutex::new(Default::default()), settings: Mutex::new(Default::default()),
state: Mutex::new(None), state: Mutex::new(None),
}) }
} }
// Called exactly once when registering the type. Used for // Called exactly once when registering the type. Used for
@ -94,7 +134,7 @@ impl Rgb2Gray {
// will automatically instantiate pads for them. // will automatically instantiate pads for them.
// //
// Our element here can convert BGRx to BGRx or GRAY8, both being grayscale. // Our element here can convert BGRx to BGRx or GRAY8, both being grayscale.
fn class_init(klass: &mut BaseTransformClass) { fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
klass.set_metadata( klass.set_metadata(
"RGB-GRAY Converter", "RGB-GRAY Converter",
"Filter/Effect/Converter/Video", "Filter/Effect/Converter/Video",
@ -171,45 +211,26 @@ impl Rgb2Gray {
// //
// We could work in-place for BGRx->BGRx but don't do here for simplicity // We could work in-place for BGRx->BGRx but don't do here for simplicity
// for now. // for now.
klass.configure(BaseTransformMode::NeverInPlace, false, false); klass.configure(
} gst_base::subclass::BaseTransformMode::NeverInPlace,
false,
// Converts one pixel of BGRx to a grayscale value, shifting and/or false,
// inverting it as configured );
#[inline]
fn bgrx_to_gray(in_p: &[u8], shift: u8, invert: bool) -> u8 {
// See https://en.wikipedia.org/wiki/YUV#SDTV_with_BT.601
const R_Y: u32 = 19595; // 0.299 * 65536
const G_Y: u32 = 38470; // 0.587 * 65536
const B_Y: u32 = 7471; // 0.114 * 65536
assert_eq!(in_p.len(), 4);
let b = u32::from(in_p[0]);
let g = u32::from(in_p[1]);
let r = u32::from(in_p[2]);
let gray = ((r * R_Y) + (g * G_Y) + (b * B_Y)) / 65536;
let gray = (gray as u8).wrapping_add(shift);
if invert {
255 - gray
} else {
gray
}
} }
} }
// Virtual methods of GObject itself // Virtual methods of GObject itself
impl ObjectImpl<BaseTransform> for Rgb2Gray { impl ObjectImpl for Rgb2Gray {
glib_object_impl!();
// Called whenever a value of a property is changed. It can be called // Called whenever a value of a property is changed. It can be called
// at any time from any thread. // at any time from any thread.
fn set_property(&self, obj: &glib::Object, id: u32, value: &glib::Value) { fn set_property(&self, obj: &glib::Object, id: usize, value: &glib::Value) {
let prop = &PROPERTIES[id as usize]; let prop = &PROPERTIES[id];
let element = obj.downcast_ref::<BaseTransform>().unwrap(); let element = obj.downcast_ref::<gst_base::BaseTransform>().unwrap();
match *prop { match *prop {
Property::Boolean("invert", ..) => { subclass::Property("invert", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
let invert = value.get().unwrap(); let invert = value.get().unwrap();
gst_info!( gst_info!(
@ -221,7 +242,7 @@ impl ObjectImpl<BaseTransform> for Rgb2Gray {
); );
settings.invert = invert; settings.invert = invert;
} }
Property::UInt("shift", ..) => { subclass::Property("shift", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
let shift = value.get().unwrap(); let shift = value.get().unwrap();
gst_info!( gst_info!(
@ -239,15 +260,15 @@ impl ObjectImpl<BaseTransform> for Rgb2Gray {
// Called whenever a value of a property is read. It can be called // Called whenever a value of a property is read. It can be called
// at any time from any thread. // at any time from any thread.
fn get_property(&self, _obj: &glib::Object, id: u32) -> Result<glib::Value, ()> { fn get_property(&self, _obj: &glib::Object, id: usize) -> Result<glib::Value, ()> {
let prop = &PROPERTIES[id as usize]; let prop = &PROPERTIES[id];
match *prop { match *prop {
Property::Boolean("invert", ..) => { subclass::Property("invert", ..) => {
let settings = self.settings.lock().unwrap(); let settings = self.settings.lock().unwrap();
Ok(settings.invert.to_value()) Ok(settings.invert.to_value())
} }
Property::UInt("shift", ..) => { subclass::Property("shift", ..) => {
let settings = self.settings.lock().unwrap(); let settings = self.settings.lock().unwrap();
Ok(settings.shift.to_value()) Ok(settings.shift.to_value())
} }
@ -257,17 +278,17 @@ impl ObjectImpl<BaseTransform> for Rgb2Gray {
} }
// Virtual methods of gst::Element. We override none // Virtual methods of gst::Element. We override none
impl ElementImpl<BaseTransform> for Rgb2Gray {} impl ElementImpl for Rgb2Gray {}
// Virtual methods of gst_base::BaseTransform // Virtual methods of gst_base::BaseTransform
impl BaseTransformImpl<BaseTransform> for Rgb2Gray { impl BaseTransformImpl for Rgb2Gray {
// Called for converting caps from one pad to another to account for any // Called for converting caps from one pad to another to account for any
// changes in the media format this element is performing. // changes in the media format this element is performing.
// //
// In our case that means that: // In our case that means that:
fn transform_caps( fn transform_caps(
&self, &self,
element: &BaseTransform, element: &gst_base::BaseTransform,
direction: gst::PadDirection, direction: gst::PadDirection,
caps: &gst::Caps, caps: &gst::Caps,
filter: Option<&gst::Caps>, filter: Option<&gst::Caps>,
@ -325,7 +346,7 @@ impl BaseTransformImpl<BaseTransform> for Rgb2Gray {
// Returns the size of one processing unit (i.e. a frame in our case) corresponding // 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 // to the given caps. This is used for allocating a big enough output buffer and
// sanity checking the input buffer size, among other things. // sanity checking the input buffer size, among other things.
fn get_unit_size(&self, _element: &BaseTransform, caps: &gst::Caps) -> Option<usize> { fn get_unit_size(&self, _element: &gst_base::BaseTransform, caps: &gst::Caps) -> Option<usize> {
gst_video::VideoInfo::from_caps(caps).map(|info| info.size()) gst_video::VideoInfo::from_caps(caps).map(|info| info.size())
} }
@ -335,7 +356,12 @@ impl BaseTransformImpl<BaseTransform> for Rgb2Gray {
// //
// We simply remember the resulting VideoInfo from the caps to be able to use this for knowing // We simply remember the resulting VideoInfo from the caps to be able to use this for knowing
// the width, stride, etc when transforming buffers // the width, stride, etc when transforming buffers
fn set_caps(&self, element: &BaseTransform, incaps: &gst::Caps, outcaps: &gst::Caps) -> bool { fn set_caps(
&self,
element: &gst_base::BaseTransform,
incaps: &gst::Caps,
outcaps: &gst::Caps,
) -> bool {
let in_info = match gst_video::VideoInfo::from_caps(incaps) { let in_info = match gst_video::VideoInfo::from_caps(incaps) {
None => return false, None => return false,
Some(info) => info, Some(info) => info,
@ -360,7 +386,7 @@ impl BaseTransformImpl<BaseTransform> for Rgb2Gray {
// Called when shutting down the element so we can release all stream-related state // 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 // There's also start(), which is called whenever starting the element again
fn stop(&self, element: &BaseTransform) -> bool { fn stop(&self, element: &gst_base::BaseTransform) -> bool {
// Drop state // Drop state
let _ = self.state.lock().unwrap().take(); let _ = self.state.lock().unwrap().take();
@ -372,7 +398,7 @@ impl BaseTransformImpl<BaseTransform> for Rgb2Gray {
// Does the actual transformation of the input buffer to the output buffer // Does the actual transformation of the input buffer to the output buffer
fn transform( fn transform(
&self, &self,
element: &BaseTransform, element: &gst_base::BaseTransform,
inbuf: &gst::Buffer, inbuf: &gst::Buffer,
outbuf: &mut gst::BufferRef, outbuf: &mut gst::BufferRef,
) -> gst::FlowReturn { ) -> gst::FlowReturn {
@ -529,34 +555,9 @@ impl BaseTransformImpl<BaseTransform> for Rgb2Gray {
} }
} }
// This zero-sized struct is containing the static metadata of our element. It is only necessary to
// be able to implement traits on it, but e.g. a plugin that registers multiple elements with the
// same code would use this struct to store information about the concrete element. An example of
// this would be a plugin that wraps around a library that has multiple decoders with the same API,
// but wants (as it should) a separate element registered for each decoder.
struct Rgb2GrayStatic;
// The basic trait for registering the type: This returns a name for the type and registers the
// instance and class initializations functions with the type system, thus hooking everything
// together.
impl ImplTypeStatic<BaseTransform> for Rgb2GrayStatic {
fn get_name(&self) -> &str {
"Rgb2Gray"
}
fn new(&self, element: &BaseTransform) -> Box<BaseTransformImpl<BaseTransform>> {
Rgb2Gray::new(element)
}
fn class_init(&self, klass: &mut BaseTransformClass) {
Rgb2Gray::class_init(klass);
}
}
// Registers the type for our element, and then registers in GStreamer under // Registers the type for our element, and then registers in GStreamer under
// the name "rsrgb2gray" for being able to instantiate it via e.g. // the name "rsrgb2gray" for being able to instantiate it via e.g.
// gst::ElementFactory::make(). // gst::ElementFactory::make().
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> { pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
let type_ = register_type(Rgb2GrayStatic); gst::Element::register(plugin, "rsrgb2gray", 0, Rgb2Gray::get_type())
gst::Element::register(plugin, "rsrgb2gray", 0, type_)
} }

View file

@ -7,17 +7,18 @@
// except according to those terms. // except according to those terms.
use glib; use glib;
use glib::subclass;
use glib::subclass::prelude::*;
use gst; use gst;
use gst::prelude::*; use gst::prelude::*;
use gst::subclass::prelude::*;
use gst_audio; use gst_audio;
use gst_base;
use gst_base::prelude::*; use gst_base::prelude::*;
use gst_base::subclass::prelude::*;
use byte_slice_cast::*; use byte_slice_cast::*;
use gobject_subclass::object::*;
use gst_plugin::base_src::*;
use gst_plugin::element::*;
use std::ops::Rem; use std::ops::Rem;
use std::sync::Mutex; use std::sync::Mutex;
use std::{i32, u32}; use std::{i32, u32};
@ -55,45 +56,58 @@ impl Default for Settings {
} }
// Metadata for the properties // Metadata for the properties
static PROPERTIES: [Property; 5] = [ static PROPERTIES: [subclass::Property; 5] = [
Property::UInt( subclass::Property("samples-per-buffer", || {
glib::ParamSpec::uint(
"samples-per-buffer", "samples-per-buffer",
"Samples Per Buffer", "Samples Per Buffer",
"Number of samples per output buffer", "Number of samples per output buffer",
(1, u32::MAX), 1,
u32::MAX,
DEFAULT_SAMPLES_PER_BUFFER, DEFAULT_SAMPLES_PER_BUFFER,
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::UInt( }),
subclass::Property("freq", || {
glib::ParamSpec::uint(
"freq", "freq",
"Frequency", "Frequency",
"Frequency", "Frequency",
(1, u32::MAX), 1,
u32::MAX,
DEFAULT_FREQ, DEFAULT_FREQ,
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::Double( }),
subclass::Property("volume", || {
glib::ParamSpec::double(
"volume", "volume",
"Volume", "Volume",
"Output volume", "Output volume",
(0.0, 10.0), 0.0,
10.0,
DEFAULT_VOLUME, DEFAULT_VOLUME,
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::Boolean( }),
subclass::Property("mute", || {
glib::ParamSpec::boolean(
"mute", "mute",
"Mute", "Mute",
"Mute", "Mute",
DEFAULT_MUTE, DEFAULT_MUTE,
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::Boolean( }),
subclass::Property("is-live", || {
glib::ParamSpec::boolean(
"is-live", "is-live",
"Is Live", "Is Live",
"(Pseudo) live output", "(Pseudo) live output",
DEFAULT_IS_LIVE, DEFAULT_IS_LIVE,
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
}),
]; ];
// Stream-specific state, i.e. audio format configuration // Stream-specific state, i.e. audio format configuration
@ -130,77 +144,6 @@ struct SineSrc {
} }
impl SineSrc { impl SineSrc {
// Called when a new instance is to be created
fn new(element: &BaseSrc) -> Box<BaseSrcImpl<BaseSrc>> {
// Initialize live-ness and notify the base class that
// we'd like to operate in Time format
element.set_live(DEFAULT_IS_LIVE);
element.set_format(gst::Format::Time);
Box::new(Self {
cat: gst::DebugCategory::new(
"rssinesrc",
gst::DebugColorFlags::empty(),
"Rust Sine Wave Source",
),
settings: Mutex::new(Default::default()),
state: Mutex::new(Default::default()),
clock_wait: Mutex::new(ClockWait {
clock_id: None,
flushing: true,
}),
})
}
// 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 BaseSrcClass) {
klass.set_metadata(
"Sine Wave Source",
"Source/Audio",
"Creates a sine wave",
"Sebastian Dröge <sebastian@centricular.com>",
);
// 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_string(),
&gst_audio::AUDIO_FORMAT_F64.to_string(),
]),
),
("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,
);
klass.add_pad_template(src_pad_template);
// Install all our properties
klass.install_properties(&PROPERTIES);
}
fn process<F: Float + FromByteSlice>( fn process<F: Float + FromByteSlice>(
data: &mut [u8], data: &mut [u8],
accumulator_ref: &mut f64, accumulator_ref: &mut f64,
@ -245,21 +188,109 @@ impl SineSrc {
} }
} }
impl ObjectSubclass for SineSrc {
const NAME: &'static str = "RsSineSrc";
type ParentType = gst_base::BaseSrc;
type Instance = gst::subclass::ElementInstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;
glib_object_subclass!();
// Called when a new instance is to be created
fn new() -> Self {
Self {
cat: gst::DebugCategory::new(
"rssinesrc",
gst::DebugColorFlags::empty(),
"Rust Sine Wave Source",
),
settings: Mutex::new(Default::default()),
state: Mutex::new(Default::default()),
clock_wait: Mutex::new(ClockWait {
clock_id: None,
flushing: true,
}),
}
}
// 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 subclass::simple::ClassStruct<Self>) {
klass.set_metadata(
"Sine Wave Source",
"Source/Audio",
"Creates a sine wave",
"Sebastian Dröge <sebastian@centricular.com>",
);
// 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_string(),
&gst_audio::AUDIO_FORMAT_F64.to_string(),
]),
),
("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,
);
klass.add_pad_template(src_pad_template);
// Install all our properties
klass.install_properties(&PROPERTIES);
}
}
// Virtual methods of GObject itself // Virtual methods of GObject itself
impl ObjectImpl<BaseSrc> for SineSrc { impl ObjectImpl for SineSrc {
glib_object_impl!();
// Called right after construction of each object
fn constructed(&self, obj: &glib::Object) {
self.parent_constructed(obj);
let basesrc = obj.downcast_ref::<gst_base::BaseSrc>().unwrap();
// Initialize live-ness and notify the base class that
// we'd like to operate in Time format
basesrc.set_live(DEFAULT_IS_LIVE);
basesrc.set_format(gst::Format::Time);
}
// Called whenever a value of a property is changed. It can be called // Called whenever a value of a property is changed. It can be called
// at any time from any thread. // at any time from any thread.
fn set_property(&self, obj: &glib::Object, id: u32, value: &glib::Value) { fn set_property(&self, obj: &glib::Object, id: usize, value: &glib::Value) {
let prop = &PROPERTIES[id as usize]; let prop = &PROPERTIES[id];
let element = obj.downcast_ref::<BaseSrc>().unwrap(); let basesrc = obj.downcast_ref::<gst_base::BaseSrc>().unwrap();
match *prop { match *prop {
Property::UInt("samples-per-buffer", ..) => { subclass::Property("samples-per-buffer", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
let samples_per_buffer = value.get().unwrap(); let samples_per_buffer = value.get().unwrap();
gst_info!( gst_info!(
self.cat, self.cat,
obj: element, obj: basesrc,
"Changing samples-per-buffer from {} to {}", "Changing samples-per-buffer from {} to {}",
settings.samples_per_buffer, settings.samples_per_buffer,
samples_per_buffer samples_per_buffer
@ -268,50 +299,50 @@ impl ObjectImpl<BaseSrc> for SineSrc {
drop(settings); drop(settings);
let _ = let _ =
element.post_message(&gst::Message::new_latency().src(Some(element)).build()); basesrc.post_message(&gst::Message::new_latency().src(Some(basesrc)).build());
} }
Property::UInt("freq", ..) => { subclass::Property("freq", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
let freq = value.get().unwrap(); let freq = value.get().unwrap();
gst_info!( gst_info!(
self.cat, self.cat,
obj: element, obj: basesrc,
"Changing freq from {} to {}", "Changing freq from {} to {}",
settings.freq, settings.freq,
freq freq
); );
settings.freq = freq; settings.freq = freq;
} }
Property::Double("volume", ..) => { subclass::Property("volume", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
let volume = value.get().unwrap(); let volume = value.get().unwrap();
gst_info!( gst_info!(
self.cat, self.cat,
obj: element, obj: basesrc,
"Changing volume from {} to {}", "Changing volume from {} to {}",
settings.volume, settings.volume,
volume volume
); );
settings.volume = volume; settings.volume = volume;
} }
Property::Boolean("mute", ..) => { subclass::Property("mute", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
let mute = value.get().unwrap(); let mute = value.get().unwrap();
gst_info!( gst_info!(
self.cat, self.cat,
obj: element, obj: basesrc,
"Changing mute from {} to {}", "Changing mute from {} to {}",
settings.mute, settings.mute,
mute mute
); );
settings.mute = mute; settings.mute = mute;
} }
Property::Boolean("is-live", ..) => { subclass::Property("is-live", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
let is_live = value.get().unwrap(); let is_live = value.get().unwrap();
gst_info!( gst_info!(
self.cat, self.cat,
obj: element, obj: basesrc,
"Changing is-live from {} to {}", "Changing is-live from {} to {}",
settings.is_live, settings.is_live,
is_live is_live
@ -324,27 +355,27 @@ impl ObjectImpl<BaseSrc> for SineSrc {
// Called whenever a value of a property is read. It can be called // Called whenever a value of a property is read. It can be called
// at any time from any thread. // at any time from any thread.
fn get_property(&self, _obj: &glib::Object, id: u32) -> Result<glib::Value, ()> { fn get_property(&self, _obj: &glib::Object, id: usize) -> Result<glib::Value, ()> {
let prop = &PROPERTIES[id as usize]; let prop = &PROPERTIES[id];
match *prop { match *prop {
Property::UInt("samples-per-buffer", ..) => { subclass::Property("samples-per-buffer", ..) => {
let settings = self.settings.lock().unwrap(); let settings = self.settings.lock().unwrap();
Ok(settings.samples_per_buffer.to_value()) Ok(settings.samples_per_buffer.to_value())
} }
Property::UInt("freq", ..) => { subclass::Property("freq", ..) => {
let settings = self.settings.lock().unwrap(); let settings = self.settings.lock().unwrap();
Ok(settings.freq.to_value()) Ok(settings.freq.to_value())
} }
Property::Double("volume", ..) => { subclass::Property("volume", ..) => {
let settings = self.settings.lock().unwrap(); let settings = self.settings.lock().unwrap();
Ok(settings.volume.to_value()) Ok(settings.volume.to_value())
} }
Property::Boolean("mute", ..) => { subclass::Property("mute", ..) => {
let settings = self.settings.lock().unwrap(); let settings = self.settings.lock().unwrap();
Ok(settings.mute.to_value()) Ok(settings.mute.to_value())
} }
Property::Boolean("is-live", ..) => { subclass::Property("is-live", ..) => {
let settings = self.settings.lock().unwrap(); let settings = self.settings.lock().unwrap();
Ok(settings.is_live.to_value()) Ok(settings.is_live.to_value())
} }
@ -354,33 +385,35 @@ impl ObjectImpl<BaseSrc> for SineSrc {
} }
// Virtual methods of gst::Element. We override none // Virtual methods of gst::Element. We override none
impl ElementImpl<BaseSrc> for SineSrc { impl ElementImpl for SineSrc {
fn change_state( fn change_state(
&self, &self,
element: &BaseSrc, element: &gst::Element,
transition: gst::StateChange, transition: gst::StateChange,
) -> gst::StateChangeReturn { ) -> gst::StateChangeReturn {
let basesrc = element.downcast_ref::<gst_base::BaseSrc>().unwrap();
// Configure live'ness once here just before starting the source // Configure live'ness once here just before starting the source
match transition { match transition {
gst::StateChange::ReadyToPaused => { gst::StateChange::ReadyToPaused => {
element.set_live(self.settings.lock().unwrap().is_live); basesrc.set_live(self.settings.lock().unwrap().is_live);
} }
_ => (), _ => (),
} }
element.parent_change_state(transition) self.parent_change_state(element, transition)
} }
} }
// Virtual methods of gst_base::BaseSrc // Virtual methods of gst_base::BaseSrc
impl BaseSrcImpl<BaseSrc> for SineSrc { impl BaseSrcImpl for SineSrc {
// Called whenever the input/output caps are changing, i.e. in the very beginning before data // Called whenever the input/output caps are changing, i.e. in the very beginning before data
// flow happens and whenever the situation in the pipeline is changing. All buffers after this // flow happens and whenever the situation in the pipeline is changing. All buffers after this
// call have the caps given here. // call have the caps given here.
// //
// We simply remember the resulting AudioInfo from the caps to be able to use this for knowing // We simply remember the resulting AudioInfo from the caps to be able to use this for knowing
// the sample rate, etc. when creating buffers // the sample rate, etc. when creating buffers
fn set_caps(&self, element: &BaseSrc, caps: &gst::CapsRef) -> bool { fn set_caps(&self, element: &gst_base::BaseSrc, caps: &gst::CapsRef) -> bool {
use std::f64::consts::PI; use std::f64::consts::PI;
let info = match gst_audio::AudioInfo::from_caps(caps) { let info = match gst_audio::AudioInfo::from_caps(caps) {
@ -431,7 +464,7 @@ impl BaseSrcImpl<BaseSrc> for SineSrc {
} }
// Called when starting, so we can initialize all stream-related state to its defaults // Called when starting, so we can initialize all stream-related state to its defaults
fn start(&self, element: &BaseSrc) -> bool { fn start(&self, element: &gst_base::BaseSrc) -> bool {
// Reset state // Reset state
*self.state.lock().unwrap() = Default::default(); *self.state.lock().unwrap() = Default::default();
self.unlock_stop(element); self.unlock_stop(element);
@ -442,7 +475,7 @@ impl BaseSrcImpl<BaseSrc> for SineSrc {
} }
// Called when shutting down the element so we can release all stream-related state // Called when shutting down the element so we can release all stream-related state
fn stop(&self, element: &BaseSrc) -> bool { fn stop(&self, element: &gst_base::BaseSrc) -> bool {
// Reset state // Reset state
*self.state.lock().unwrap() = Default::default(); *self.state.lock().unwrap() = Default::default();
self.unlock(element); self.unlock(element);
@ -452,7 +485,7 @@ impl BaseSrcImpl<BaseSrc> for SineSrc {
true true
} }
fn query(&self, element: &BaseSrc, query: &mut gst::QueryRef) -> bool { fn query(&self, element: &gst_base::BaseSrc, query: &mut gst::QueryRef) -> bool {
use gst::QueryView; use gst::QueryView;
match query.view_mut() { match query.view_mut() {
@ -483,13 +516,13 @@ impl BaseSrcImpl<BaseSrc> for SineSrc {
} }
_ => (), _ => (),
} }
BaseSrcBase::parent_query(element, query) BaseSrcImpl::parent_query(self, element, query)
} }
// Creates the audio buffers // Creates the audio buffers
fn create( fn create(
&self, &self,
element: &BaseSrc, element: &gst_base::BaseSrc,
_offset: u64, _offset: u64,
_length: u32, _length: u32,
) -> Result<gst::Buffer, gst::FlowError> { ) -> Result<gst::Buffer, gst::FlowError> {
@ -643,7 +676,7 @@ impl BaseSrcImpl<BaseSrc> for SineSrc {
Ok(buffer) Ok(buffer)
} }
fn fixate(&self, element: &BaseSrc, caps: gst::Caps) -> gst::Caps { fn fixate(&self, element: &gst_base::BaseSrc, caps: gst::Caps) -> gst::Caps {
// Fixate the caps. BaseSrc will do some fixation for us, but // 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 // as we allow any rate between 1 and MAX it would fixate to 1. 1Hz
// is generally not a useful sample rate. // is generally not a useful sample rate.
@ -661,14 +694,14 @@ impl BaseSrcImpl<BaseSrc> for SineSrc {
// Let BaseSrc fixate anything else for us. We could've alternatively have // Let BaseSrc fixate anything else for us. We could've alternatively have
// called Caps::fixate() here // called Caps::fixate() here
element.parent_fixate(caps) self.parent_fixate(element, caps)
} }
fn is_seekable(&self, _element: &BaseSrc) -> bool { fn is_seekable(&self, _element: &gst_base::BaseSrc) -> bool {
true true
} }
fn do_seek(&self, element: &BaseSrc, segment: &mut gst::Segment) -> bool { fn do_seek(&self, element: &gst_base::BaseSrc, segment: &mut gst::Segment) -> bool {
// Handle seeking here. For Time and Default (sample offset) seeks we can // Handle seeking here. For Time and Default (sample offset) seeks we can
// do something and have to update our sample offset and accumulator accordingly. // do something and have to update our sample offset and accumulator accordingly.
// //
@ -774,7 +807,7 @@ impl BaseSrcImpl<BaseSrc> for SineSrc {
} }
} }
fn unlock(&self, element: &BaseSrc) -> bool { fn unlock(&self, element: &gst_base::BaseSrc) -> bool {
// This should unblock the create() function ASAP, so we // This should unblock the create() function ASAP, so we
// just unschedule the clock it here, if any. // just unschedule the clock it here, if any.
gst_debug!(self.cat, obj: element, "Unlocking"); gst_debug!(self.cat, obj: element, "Unlocking");
@ -787,7 +820,7 @@ impl BaseSrcImpl<BaseSrc> for SineSrc {
true true
} }
fn unlock_stop(&self, element: &BaseSrc) -> bool { fn unlock_stop(&self, element: &gst_base::BaseSrc) -> bool {
// This signals that unlocking is done, so we can reset // This signals that unlocking is done, so we can reset
// all values again. // all values again.
gst_debug!(self.cat, obj: element, "Unlock stop"); gst_debug!(self.cat, obj: element, "Unlock stop");
@ -798,34 +831,9 @@ impl BaseSrcImpl<BaseSrc> for SineSrc {
} }
} }
// This zero-sized struct is containing the static metadata of our element. It is only necessary to
// be able to implement traits on it, but e.g. a plugin that registers multiple elements with the
// same code would use this struct to store information about the concrete element. An example of
// this would be a plugin that wraps around a library that has multiple decoders with the same API,
// but wants (as it should) a separate element registered for each decoder.
struct SineSrcStatic;
// The basic trait for registering the type: This returns a name for the type and registers the
// instance and class initializations functions with the type system, thus hooking everything
// together.
impl ImplTypeStatic<BaseSrc> for SineSrcStatic {
fn get_name(&self) -> &str {
"SineSrc"
}
fn new(&self, element: &BaseSrc) -> Box<BaseSrcImpl<BaseSrc>> {
SineSrc::new(element)
}
fn class_init(&self, klass: &mut BaseSrcClass) {
SineSrc::class_init(klass);
}
}
// Registers the type for our element, and then registers in GStreamer under // Registers the type for our element, and then registers in GStreamer under
// the name "sinesrc" for being able to instantiate it via e.g. // the name "sinesrc" for being able to instantiate it via e.g.
// gst::ElementFactory::make(). // gst::ElementFactory::make().
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> { pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
let type_ = register_type(SineSrcStatic); gst::Element::register(plugin, "rssinesrc", 0, SineSrc::get_type())
gst::Element::register(plugin, "rssinesrc", 0, type_)
} }