mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2025-01-26 10:58:11 +00:00
Port tutorials plugin to new subclassing API
This commit is contained in:
parent
485839a2a9
commit
4ac6863eed
4 changed files with 295 additions and 290 deletions
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
);
|
);
|
||||||
|
|
|
@ -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_)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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_)
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue