// Copyright (C) 2018 Sebastian Dröge // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. // // SPDX-License-Identifier: MIT OR Apache-2.0 use gst::glib; use gst::prelude::*; use gst::subclass::prelude::*; use once_cell::sync::Lazy; // This module contains the private implementation details of our element static CAT: Lazy = Lazy::new(|| { gst::DebugCategory::new( "rsidentity", gst::DebugColorFlags::empty(), Some("Identity Element"), ) }); // Struct containing all the element data pub struct Identity { srcpad: gst::Pad, sinkpad: gst::Pad, } impl Identity { // Called whenever a new buffer is passed to our sink pad. Here buffers should be processed and // whenever some output buffer is available have to push it out of the source pad. // Here we just pass through all buffers directly // // See the documentation of gst::Buffer and gst::BufferRef to see what can be done with // buffers. fn sink_chain( &self, pad: &gst::Pad, buffer: gst::Buffer, ) -> Result { gst::log!(CAT, obj: pad, "Handling buffer {:?}", buffer); self.srcpad.push(buffer) } // Called whenever an event arrives on the sink pad. It has to be handled accordingly and in // most cases has to be either passed to Pad::event_default() on this pad for default handling, // or Pad::push_event() on all pads with the opposite direction for direct forwarding. // Here we just pass through all events directly to the source pad. // // See the documentation of gst::Event and gst::EventRef to see what can be done with // events, and especially the gst::EventView type for inspecting events. fn sink_event(&self, pad: &gst::Pad, event: gst::Event) -> bool { gst::log!(CAT, obj: pad, "Handling event {:?}", event); self.srcpad.push_event(event) } // Called whenever a query is sent to the sink pad. It has to be answered if the element can // handle it, potentially by forwarding the query first to the peer pads of the pads with the // opposite direction, or false has to be returned. Default handling can be achieved with // Pad::query_default() on this pad and forwarding with Pad::peer_query() on the pads with the // opposite direction. // Here we just forward all queries directly to the source pad's peers. // // See the documentation of gst::Query and gst::QueryRef to see what can be done with // queries, and especially the gst::QueryView type for inspecting and modifying queries. fn sink_query(&self, pad: &gst::Pad, query: &mut gst::QueryRef) -> bool { gst::log!(CAT, obj: pad, "Handling query {:?}", query); self.srcpad.peer_query(query) } // Called whenever an event arrives on the source pad. It has to be handled accordingly and in // most cases has to be either passed to Pad::event_default() on the same pad for default // handling, or Pad::push_event() on all pads with the opposite direction for direct // forwarding. // Here we just pass through all events directly to the sink pad. // // See the documentation of gst::Event and gst::EventRef to see what can be done with // events, and especially the gst::EventView type for inspecting events. fn src_event(&self, pad: &gst::Pad, event: gst::Event) -> bool { gst::log!(CAT, obj: pad, "Handling event {:?}", event); self.sinkpad.push_event(event) } // Called whenever a query is sent to the source pad. It has to be answered if the element can // handle it, potentially by forwarding the query first to the peer pads of the pads with the // opposite direction, or false has to be returned. Default handling can be achieved with // Pad::query_default() on this pad and forwarding with Pad::peer_query() on the pads with the // opposite direction. // Here we just forward all queries directly to the sink pad's peers. // // See the documentation of gst::Query and gst::QueryRef to see what can be done with // queries, and especially the gst::QueryView type for inspecting and modifying queries. fn src_query(&self, pad: &gst::Pad, query: &mut gst::QueryRef) -> bool { gst::log!(CAT, obj: pad, "Handling query {:?}", query); self.sinkpad.peer_query(query) } } // This trait registers our type with the GObject object system and // provides the entry points for creating a new instance and setting // up the class data #[glib::object_subclass] impl ObjectSubclass for Identity { const NAME: &'static str = "GstRsIdentity"; type Type = super::Identity; type ParentType = gst::Element; // Called when a new instance is to be created. We need to return an instance // of our struct here and also get the class struct passed in case it's needed fn with_class(klass: &Self::Class) -> Self { // Create our two pads from the templates that were registered with // the class and set all the functions on them. // // Each function is wrapped in catch_panic_pad_function(), which will // - Catch panics from the pad functions and instead of aborting the process // it will simply convert them into an error message and poison the element // instance // - Extract our Identity struct from the object instance and pass it to us // // Details about what each function is good for is next to each function definition let templ = klass.pad_template("sink").unwrap(); let sinkpad = gst::Pad::builder_with_template(&templ, Some("sink")) .chain_function(|pad, parent, buffer| { Identity::catch_panic_pad_function( parent, || Err(gst::FlowError::Error), |identity| identity.sink_chain(pad, buffer), ) }) .event_function(|pad, parent, event| { Identity::catch_panic_pad_function( parent, || false, |identity| identity.sink_event(pad, event), ) }) .query_function(|pad, parent, query| { Identity::catch_panic_pad_function( parent, || false, |identity| identity.sink_query(pad, query), ) }) .build(); let templ = klass.pad_template("src").unwrap(); let srcpad = gst::Pad::builder_with_template(&templ, Some("src")) .event_function(|pad, parent, event| { Identity::catch_panic_pad_function( parent, || false, |identity| identity.src_event(pad, event), ) }) .query_function(|pad, parent, query| { Identity::catch_panic_pad_function( parent, || false, |identity| identity.src_query(pad, query), ) }) .build(); // Return an instance of our struct and also include our debug category here. // The debug category will be used later whenever we need to put something // into the debug logs Self { srcpad, sinkpad } } } // Implementation of glib::Object virtual methods impl ObjectImpl for Identity { // Called right after construction of a new instance fn constructed(&self) { // Call the parent class' ::constructed() implementation first self.parent_constructed(); // Here we actually add the pads we created in Identity::new() to the // element so that GStreamer is aware of their existence. let obj = self.instance(); obj.add_pad(&self.sinkpad).unwrap(); obj.add_pad(&self.srcpad).unwrap(); } } impl GstObjectImpl for Identity {} // Implementation of gst::Element virtual methods impl ElementImpl for Identity { // Set the element specific metadata. This information is what // is visible from gst-inspect-1.0 and can also be programmatically // 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 = Lazy::new(|| { gst::subclass::ElementMetadata::new( "Identity", "Generic", "Does nothing with the data", "Sebastian Dröge ", ) }); Some(&*ELEMENT_METADATA) } // 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. // // Actual instances can create pads based on those pad templates fn pad_templates() -> &'static [gst::PadTemplate] { static PAD_TEMPLATES: Lazy> = Lazy::new(|| { // Our element can accept any possible caps on both pads let caps = gst::Caps::new_any(); let src_pad_template = gst::PadTemplate::new( "src", gst::PadDirection::Src, gst::PadPresence::Always, &caps, ) .unwrap(); let sink_pad_template = gst::PadTemplate::new( "sink", gst::PadDirection::Sink, gst::PadPresence::Always, &caps, ) .unwrap(); vec![src_pad_template, sink_pad_template] }); PAD_TEMPLATES.as_ref() } // Called whenever the state of the element should be changed. This allows for // starting up the element, allocating/deallocating resources or shutting down // the element again. fn change_state( &self, transition: gst::StateChange, ) -> Result { gst::trace!(CAT, imp: self, "Changing state {:?}", transition); // Call the parent class' implementation of ::change_state() self.parent_change_state(transition) } }