gstreamer: Provide class metadata, pad templates and basetransform configuration via trait methods

This is closer to how this works in Python and also how properties and
signals work now in the glib bindings.

class_init() only has to be implemented for more special uses now.
This commit is contained in:
Sebastian Dröge 2020-10-20 23:55:20 +03:00
parent beee75dabe
commit e11b12df7c
8 changed files with 254 additions and 226 deletions

View file

@ -97,6 +97,7 @@ mod media_factory {
const NAME: &'static str = "RsRTSPMediaFactory";
type Type = super::Factory;
type ParentType = gst_rtsp_server::RTSPMediaFactory;
type Interfaces = ();
type Instance = gst::subclass::ElementInstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;
@ -190,6 +191,7 @@ mod media {
const NAME: &'static str = "RsRTSPMedia";
type Type = super::Media;
type ParentType = gst_rtsp_server::RTSPMedia;
type Interfaces = ();
type Instance = gst::subclass::ElementInstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;
@ -258,6 +260,7 @@ mod server {
const NAME: &'static str = "RsRTSPServer";
type Type = super::Server;
type ParentType = gst_rtsp_server::RTSPServer;
type Interfaces = ();
type Instance = gst::subclass::ElementInstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;
@ -334,6 +337,7 @@ mod client {
const NAME: &'static str = "RsRTSPClient";
type Type = super::Client;
type ParentType = gst_rtsp_server::RTSPClient;
type Interfaces = ();
type Instance = gst::subclass::ElementInstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;

View file

@ -59,6 +59,7 @@ mod fir_filter {
const NAME: &'static str = "RsFirFilter";
type Type = super::FirFilter;
type ParentType = gst_base::BaseTransform;
type Interfaces = ();
type Instance = gst::subclass::ElementInstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;
@ -73,89 +74,85 @@ mod fir_filter {
history: Mutex::new(VecDeque::new()),
}
}
// 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 basetransform,
// a "src" and "sink" pad template are required here and the base class
// will automatically instantiate pads for them.
//
// Our element here can only handle F32 mono audio.
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
// without having to load the plugin in memory.
klass.set_metadata(
"FIR Filter",
"Filter/Effect/Audio",
"A FIR audio filter",
"Sebastian Dröge <sebastian@centricular.com>",
);
// Create and add pad templates for our sink and source pad. These
// are later used for actually creating the pads and beforehand
// already provide information to GStreamer about all possible
// pads that could exist for this type.
// On both of pads we can only handle F32 mono at any sample rate.
let caps = gst::Caps::new_simple(
"audio/x-raw",
&[
("format", &gst_audio::AUDIO_FORMAT_F32.to_str()),
("rate", &gst::IntRange::<i32>::new(1, i32::MAX)),
("channels", &1i32),
("layout", &"interleaved"),
],
);
// The src pad template must be named "src" for basetransform
// and specific a pad that is always there
let src_pad_template = gst::PadTemplate::new(
"src",
gst::PadDirection::Src,
gst::PadPresence::Always,
&caps,
)
.unwrap();
klass.add_pad_template(src_pad_template);
// The sink pad template must be named "sink" for basetransform
// and specific a pad that is always there
let sink_pad_template = gst::PadTemplate::new(
"sink",
gst::PadDirection::Sink,
gst::PadPresence::Always,
&caps,
)
.unwrap();
klass.add_pad_template(sink_pad_template);
// Configure basetransform so that we are always running in-place,
// don't passthrough on same caps and also never call transform_ip
// in passthrough mode (which does not matter for us here).
//
// The way how our processing is implemented, in-place transformation
// is simpler.
klass.configure(
gst_base::subclass::BaseTransformMode::AlwaysInPlace,
false,
false,
);
}
}
// Implementation of glib::Object virtual methods
impl ObjectImpl for FirFilter {}
// Implementation of gst::Element virtual methods
impl ElementImpl for FirFilter {}
impl ElementImpl for FirFilter {
// 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 without having to load the plugin in memory.
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
gst::subclass::ElementMetadata::new(
"FIR Filter",
"Filter/Effect/Audio",
"A FIR audio filter",
"Sebastian Dröge <sebastian@centricular.com>",
)
});
Some(&*ELEMENT_METADATA)
}
fn pad_templates() -> &'static [gst::PadTemplate] {
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
// Create pad templates for our sink and source pad. These are later used for
// actually creating the pads and beforehand already provide information to
// GStreamer about all possible pads that could exist for this type.
// On both of pads we can only handle F32 mono at any sample rate.
let caps = gst::Caps::new_simple(
"audio/x-raw",
&[
("format", &gst_audio::AUDIO_FORMAT_F32.to_str()),
("rate", &gst::IntRange::<i32>::new(1, i32::MAX)),
("channels", &1i32),
("layout", &"interleaved"),
],
);
vec![
// The src pad template must be named "src" for basetransform
// and specific a pad that is always there
gst::PadTemplate::new(
"src",
gst::PadDirection::Src,
gst::PadPresence::Always,
&caps,
)
.unwrap(),
// The sink pad template must be named "sink" for basetransform
// and specific a pad that is always there
gst::PadTemplate::new(
"sink",
gst::PadDirection::Sink,
gst::PadPresence::Always,
&caps,
)
.unwrap(),
]
});
PAD_TEMPLATES.as_ref()
}
}
// Implementation of gst_base::BaseTransform virtual methods
impl BaseTransformImpl for FirFilter {
// Configure basetransform so that we are always running in-place,
// don't passthrough on same caps and also never call transform_ip
// in passthrough mode (which does not matter for us here).
//
// The way how our processing is implemented, in-place transformation
// is simpler.
const MODE: gst_base::subclass::BaseTransformMode =
gst_base::subclass::BaseTransformMode::AlwaysInPlace;
const PASSTHROUGH_ON_SAME_CAPS: bool = false;
const TRANSFORM_IP_ON_PASSTHROUGH: bool = false;
// 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.

View file

@ -13,7 +13,18 @@ use std::ptr;
use crate::BaseTransform;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum BaseTransformMode {
AlwaysInPlace,
NeverInPlace,
Both,
}
pub trait BaseTransformImpl: BaseTransformImplExt + ElementImpl {
const MODE: BaseTransformMode;
const PASSTHROUGH_ON_SAME_CAPS: bool;
const TRANSFORM_IP_ON_PASSTHROUGH: bool;
fn start(&self, element: &Self::Type) -> Result<(), gst::ErrorMessage> {
self.parent_start(element)
}
@ -823,13 +834,6 @@ impl<T: BaseTransformImpl> BaseTransformImplExt for T {
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum BaseTransformMode {
AlwaysInPlace,
NeverInPlace,
Both,
}
unsafe impl<T: BaseTransformImpl> IsSubclassable<T> for BaseTransform
where
<T as ObjectSubclass>::Instance: PanicPoison,
@ -854,50 +858,27 @@ where
klass.before_transform = Some(base_transform_before_transform::<T>);
klass.submit_input_buffer = Some(base_transform_submit_input_buffer::<T>);
klass.generate_output = Some(base_transform_generate_output::<T>);
}
}
pub unsafe trait BaseTransformClassSubclassExt: Sized + 'static {
fn configure<T: BaseTransformImpl>(
&mut self,
mode: BaseTransformMode,
passthrough_on_same_caps: bool,
transform_ip_on_passthrough: bool,
) where
Self: ClassStruct<Type = T>,
<T as ObjectSubclass>::Instance: PanicPoison,
{
unsafe {
let klass = &mut *(self as *mut Self as *mut ffi::GstBaseTransformClass);
klass.passthrough_on_same_caps = T::PASSTHROUGH_ON_SAME_CAPS.to_glib();
klass.transform_ip_on_passthrough = T::TRANSFORM_IP_ON_PASSTHROUGH.to_glib();
klass.passthrough_on_same_caps = passthrough_on_same_caps.to_glib();
klass.transform_ip_on_passthrough = transform_ip_on_passthrough.to_glib();
match mode {
BaseTransformMode::AlwaysInPlace => {
klass.transform = None;
klass.transform_ip = Some(base_transform_transform_ip::<T>);
}
BaseTransformMode::NeverInPlace => {
klass.transform = Some(base_transform_transform::<T>);
klass.transform_ip = None;
}
BaseTransformMode::Both => {
klass.transform = Some(base_transform_transform::<T>);
klass.transform_ip = Some(base_transform_transform_ip::<T>);
}
match T::MODE {
BaseTransformMode::AlwaysInPlace => {
klass.transform = None;
klass.transform_ip = Some(base_transform_transform_ip::<T>);
}
BaseTransformMode::NeverInPlace => {
klass.transform = Some(base_transform_transform::<T>);
klass.transform_ip = None;
}
BaseTransformMode::Both => {
klass.transform = Some(base_transform_transform::<T>);
klass.transform_ip = Some(base_transform_transform_ip::<T>);
}
}
}
}
unsafe impl<T: ClassStruct> BaseTransformClassSubclassExt for T
where
T::Type: BaseTransformImpl,
<T::Type as ObjectSubclass>::Instance: PanicPoison,
{
}
#[derive(Debug)]
pub enum GenerateOutputSuccess {
Buffer(gst::Buffer),

View file

@ -34,8 +34,6 @@ pub mod prelude {
pub use super::base_parse::{BaseParseImpl, BaseParseImplExt};
pub use super::base_sink::{BaseSinkImpl, BaseSinkImplExt};
pub use super::base_src::{BaseSrcImpl, BaseSrcImplExt};
pub use super::base_transform::{
BaseTransformClassSubclassExt, BaseTransformImpl, BaseTransformImplExt,
};
pub use super::base_transform::{BaseTransformImpl, BaseTransformImplExt};
pub use super::push_src::{PushSrcImpl, PushSrcImplExt};
}

View file

@ -8,7 +8,20 @@ use crate::Device;
use crate::DeviceProvider;
use crate::LoggableError;
#[derive(Debug, Clone)]
pub struct DeviceProviderMetadata {
long_name: String,
classification: String,
description: String,
author: String,
additional: Vec<(String, String)>,
}
pub trait DeviceProviderImpl: DeviceProviderImplExt + ObjectImpl + Send + Sync {
fn metadata() -> Option<&'static DeviceProviderMetadata> {
None
}
fn probe(&self, device_provider: &Self::Type) -> Vec<Device> {
self.parent_probe(device_provider)
}
@ -78,38 +91,6 @@ impl<T: DeviceProviderImpl> DeviceProviderImplExt for T {
}
}
pub unsafe trait DeviceProviderClassSubclassExt: Sized + 'static {
fn set_metadata(
&mut self,
long_name: &str,
classification: &str,
description: &str,
author: &str,
) {
unsafe {
ffi::gst_device_provider_class_set_metadata(
self as *mut Self as *mut ffi::GstDeviceProviderClass,
long_name.to_glib_none().0,
classification.to_glib_none().0,
description.to_glib_none().0,
author.to_glib_none().0,
);
}
}
fn add_metadata(&mut self, key: &str, value: &str) {
unsafe {
ffi::gst_device_provider_class_add_metadata(
self as *mut Self as *mut ffi::GstDeviceProviderClass,
key.to_glib_none().0,
value.to_glib_none().0,
);
}
}
}
unsafe impl DeviceProviderClassSubclassExt for glib::Class<DeviceProvider> {}
unsafe impl<T: DeviceProviderImpl> IsSubclassable<T> for DeviceProvider {
fn override_vfuncs(klass: &mut glib::Class<Self>) {
<glib::Object as IsSubclassable<T>>::override_vfuncs(klass);
@ -117,6 +98,26 @@ unsafe impl<T: DeviceProviderImpl> IsSubclassable<T> for DeviceProvider {
klass.probe = Some(device_provider_probe::<T>);
klass.start = Some(device_provider_start::<T>);
klass.stop = Some(device_provider_stop::<T>);
unsafe {
if let Some(metadata) = T::metadata() {
ffi::gst_device_provider_class_set_metadata(
klass,
metadata.long_name.to_glib_none().0,
metadata.classification.to_glib_none().0,
metadata.description.to_glib_none().0,
metadata.author.to_glib_none().0,
);
for (key, value) in &metadata.additional {
ffi::gst_device_provider_class_add_metadata(
klass,
key.to_glib_none().0,
value.to_glib_none().0,
);
}
}
}
}
}

View file

@ -14,7 +14,56 @@ use crate::StateChangeError;
use crate::StateChangeReturn;
use crate::StateChangeSuccess;
#[derive(Debug, Clone)]
pub struct ElementMetadata {
long_name: String,
classification: String,
description: String,
author: String,
additional: Vec<(String, String)>,
}
impl ElementMetadata {
pub fn new(long_name: &str, classification: &str, description: &str, author: &str) -> Self {
Self {
long_name: long_name.into(),
classification: classification.into(),
description: description.into(),
author: author.into(),
additional: vec![],
}
}
pub fn with_additional(
long_name: &str,
classification: &str,
description: &str,
author: &str,
additional: &[(&str, &str)],
) -> Self {
Self {
long_name: long_name.into(),
classification: classification.into(),
description: description.into(),
author: author.into(),
additional: additional
.iter()
.copied()
.map(|(key, value)| (key.into(), value.into()))
.collect(),
}
}
}
pub trait ElementImpl: ElementImplExt + ObjectImpl + Send + Sync {
fn metadata() -> Option<&'static ElementMetadata> {
None
}
fn pad_templates() -> &'static [PadTemplate] {
&[]
}
fn change_state(
&self,
element: &Self::Type,
@ -302,53 +351,13 @@ where
}
}
pub unsafe trait ElementClassSubclassExt: Sized + 'static {
fn add_pad_template(&mut self, pad_template: PadTemplate) {
unsafe {
ffi::gst_element_class_add_pad_template(
self as *mut Self as *mut ffi::GstElementClass,
pad_template.to_glib_none().0,
);
}
}
fn set_metadata(
&mut self,
long_name: &str,
classification: &str,
description: &str,
author: &str,
) {
unsafe {
ffi::gst_element_class_set_metadata(
self as *mut Self as *mut ffi::GstElementClass,
long_name.to_glib_none().0,
classification.to_glib_none().0,
description.to_glib_none().0,
author.to_glib_none().0,
);
}
}
fn add_metadata(&mut self, key: &str, value: &str) {
unsafe {
ffi::gst_element_class_add_metadata(
self as *mut Self as *mut ffi::GstElementClass,
key.to_glib_none().0,
value.to_glib_none().0,
);
}
}
}
unsafe impl ElementClassSubclassExt for glib::Class<Element> {}
unsafe impl<T: ElementImpl> IsSubclassable<T> for Element
where
<T as ObjectSubclass>::Instance: PanicPoison,
{
fn override_vfuncs(klass: &mut glib::Class<Self>) {
<glib::Object as IsSubclassable<T>>::override_vfuncs(klass);
let klass = klass.as_mut();
klass.change_state = Some(element_change_state::<T>);
klass.request_new_pad = Some(element_request_new_pad::<T>);
@ -359,6 +368,30 @@ where
klass.set_clock = Some(element_set_clock::<T>);
klass.provide_clock = Some(element_provide_clock::<T>);
klass.post_message = Some(element_post_message::<T>);
unsafe {
for pad_template in T::pad_templates() {
ffi::gst_element_class_add_pad_template(klass, pad_template.to_glib_none().0);
}
if let Some(metadata) = T::metadata() {
ffi::gst_element_class_set_metadata(
klass,
metadata.long_name.to_glib_none().0,
metadata.classification.to_glib_none().0,
metadata.description.to_glib_none().0,
metadata.author.to_glib_none().0,
);
for (key, value) in &metadata.additional {
ffi::gst_element_class_add_metadata(
klass,
key.to_glib_none().0,
value.to_glib_none().0,
);
}
}
}
}
}
@ -623,6 +656,7 @@ mod tests {
const NAME: &'static str = "TestElement";
type Type = super::TestElement;
type ParentType = Element;
type Interfaces = ();
type Instance = crate::subclass::ElementInstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;
@ -679,34 +713,6 @@ mod tests {
sinkpad,
}
}
fn class_init(klass: &mut Self::Class) {
klass.set_metadata(
"Test Element",
"Generic",
"Does nothing",
"Sebastian Dröge <sebastian@centricular.com>",
);
let caps = crate::Caps::new_any();
let src_pad_template = crate::PadTemplate::new(
"src",
crate::PadDirection::Src,
crate::PadPresence::Always,
&caps,
)
.unwrap();
klass.add_pad_template(src_pad_template);
let sink_pad_template = crate::PadTemplate::new(
"sink",
crate::PadDirection::Sink,
crate::PadPresence::Always,
&caps,
)
.unwrap();
klass.add_pad_template(sink_pad_template);
}
}
impl ObjectImpl for TestElement {
@ -719,6 +725,45 @@ mod tests {
}
impl ElementImpl for TestElement {
fn metadata() -> Option<&'static ElementMetadata> {
use once_cell::sync::Lazy;
static ELEMENT_METADATA: Lazy<ElementMetadata> = Lazy::new(|| {
ElementMetadata::new(
"Test Element",
"Generic",
"Does nothing",
"Sebastian Dröge <sebastian@centricular.com>",
)
});
Some(&*ELEMENT_METADATA)
}
fn pad_templates() -> &'static [PadTemplate] {
use once_cell::sync::Lazy;
static PAD_TEMPLATES: Lazy<Vec<PadTemplate>> = Lazy::new(|| {
let caps = crate::Caps::new_any();
vec![
PadTemplate::new(
"src",
crate::PadDirection::Src,
crate::PadPresence::Always,
&caps,
)
.unwrap(),
PadTemplate::new(
"sink",
crate::PadDirection::Sink,
crate::PadPresence::Always,
&caps,
)
.unwrap(),
]
});
PAD_TEMPLATES.as_ref()
}
fn change_state(
&self,
element: &Self::Type,

View file

@ -32,6 +32,9 @@ mod preset;
mod tag_setter;
mod uri_handler;
pub use self::device_provider::DeviceProviderMetadata;
pub use self::element::ElementMetadata;
pub use self::error::FlowError;
pub use self::plugin::{MAJOR_VERSION, MINOR_VERSION};
@ -43,10 +46,8 @@ pub mod prelude {
pub use super::child_proxy::ChildProxyImpl;
pub use super::clock::{ClockImpl, ClockImplExt};
pub use super::device::{DeviceImpl, DeviceImplExt};
pub use super::device_provider::{
DeviceProviderClassSubclassExt, DeviceProviderImpl, DeviceProviderImplExt,
};
pub use super::element::{ElementClassSubclassExt, ElementImpl, ElementImplExt};
pub use super::device_provider::{DeviceProviderImpl, DeviceProviderImplExt};
pub use super::element::{ElementImpl, ElementImplExt};
pub use super::ghost_pad::GhostPadImpl;
pub use super::pad::{PadImpl, PadImplExt};
pub use super::pipeline::PipelineImpl;

View file

@ -104,6 +104,7 @@ mod tests {
const NAME: &'static str = "TestPad";
type Type = super::TestPad;
type ParentType = Pad;
type Interfaces = ();
type Instance = subclass::simple::InstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;