Add GST Plugin

This commit is contained in:
Wanjohi
2023-11-25 14:41:43 +03:00
parent 54f12305d9
commit 65fc63b325
8 changed files with 979 additions and 0 deletions

23
src/README.md Normal file
View File

@@ -0,0 +1,23 @@
To be used as follows:
```rust
// Create the parsebin element
let parsebin = gst::ElementFactory::make("parsebin", None).expect("Failed to create parsebin element");
// Create the h264parse element
let h264parse = gst::ElementFactory::make("h264parse", None).expect("Failed to create h264parse element");
// Create the mp4mux element for fmp4
let mp4mux = gst::ElementFactory::make("mp4mux", None).expect("Failed to create mp4mux element");
mp4mux.set_property("fragment-duration", &2000u32).unwrap(); // Fragment duration in milliseconds
mp4mux.set_property_from_str("streamable", "true"); // For streaming output
// Add elements to the pipeline
pipeline.add_many(&[&waylandsrc, &parsebin, &h264parse, &mp4mux, &moqsink])?;
// Link the elements together
waylandsrc.link(&parsebin)?;
parsebin.link(&h264parse)?;
h264parse.link(&mp4mux)?;
mp4mux.link(&moqsink)?;
```

22
src/lib.rs Normal file
View File

@@ -0,0 +1,22 @@
#![allow(clippy::non_send_fields_in_send_ty, unused_doc_comments)]
use gst::glib;
mod warpsink;
fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
warpsink::register(plugin)?;
Ok(())
}
gst::plugin_define!(
warp,
env!("CARGO_PKG_DESCRIPTION"),
plugin_init,
concat!(env!("CARGO_PKG_VERSION"), "-", env!("COMMIT_ID")),
"MIT/X11",
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_REPOSITORY"),
env!("BUILD_REL_DATE")
);

151
src/warpsink/imp.rs Normal file
View File

@@ -0,0 +1,151 @@
use gst::glib;
use gst::prelude::*;
use gst::subclass::prelude::*;
use gst_base::subclass::prelude::*;
use gst_base::BaseSink;
use once_cell::sync::Lazy;
use std::sync::Mutex;
// Define the plugin's instance structure with a URL property.
pub struct MoqSink {
sink: gst_base::BaseSink,
url: Mutex<Option<String>>, // Store the URL property value
// Add additional fields for moq_transport as needed.
}
// Implement the GObject subclass for our sink.
#[glib::object_subclass]
impl ObjectSubclass for MoqSink {
const NAME: &'static str = "MoqSink";
type Type = super::MoqSink;
type ParentType = BaseSink;
}
// Implement the GstObject trait for our sink to set up the metadata.
impl GstObjectImpl for MoqSink {}
// Implement the Element trait for our sink. This is where we can add pads and set up
// the element's functionality.
impl ElementImpl for MoqSink {
// Define metadata for the element.
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
gst::subclass::ElementMetadata::new(
"MoQ Sink",
"Sink/Network",
"Send data over QUIC using the MoQ protocol",
"Wanjohi Ryan <elviswanjohi47@gmail.com>",
)
});
Some(&*ELEMENT_METADATA)
}
}
// Implement the properties and their behavior for the sink.
impl ObjectImpl for MoqSink {
fn properties() -> &'static [glib::ParamSpec] {
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
vec![
glib::ParamSpecString::new(
"url",
"URL",
"URL to send data to",
None, // Default value
glib::ParamFlags::READWRITE,
),
]
});
PROPERTIES.as_ref()
}
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
match pspec.name() {
"url" => {
let mut url = self.url.lock().unwrap();
*url = value.get().expect("Type checked upstream");
}
_ => unimplemented!(),
}
}
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
match pspec.name() {
"url" => {
let url = self.url.lock().unwrap();
url.as_ref().to_value()
}
_ => unimplemented!(),
}
}
}
// Implement the BaseSink trait for our sink. This is where most of the sink logic is implemented.
impl BaseSinkImpl for MoqSink {
fn start(&self, element: &Self::Type) -> Result<(), gst::ErrorMessage> {
// Initialize moq_transport here.
Ok(())
}
fn stop(&self, element: &Self::Type) -> Result<(), gst::ErrorMessage> {
// Clean up moq_transport here.
Ok(())
}
fn render(&self, element: &Self::Type, buffer: &gst::Buffer) -> Result<gst::FlowSuccess, gst::FlowError> {
// Send buffer data over QUIC using moq_transport.
// Extract data from the buffer
let data = buffer.map_readable().map_err(|_| gst::FlowError::Error)?;
// Assuming that the upstream element is producing MP4 atoms, we need to distinguish
// between 'moof' and 'mdat' atoms. We will also assume that each buffer contains a
// complete atom for simplicity. In a real-world scenario, you might need to handle
// partial atoms and reassemble them here.
//
// The first 4 bytes of the buffer contain the size of the atom, and the next 4 bytes
// contain the atom type. For 'moof' and 'mdat', we would send them as separate messages
// or bundle them together, depending on the protocol requirements.
// Check if the buffer is large enough to contain the size and type of the atom.
if data.len() < 8 {
gst::element_error!(
element,
gst::CoreError::Failed,
("Buffer is too small to contain an MP4 atom")
);
return Err(gst::FlowError::Error);
}
// Read atom size and type
let size = u32::from_be_bytes(data[0..4].try_into().unwrap()) as usize;
let atom_type = &data[4..8];
// Ensure the buffer contains the complete atom
if size > data.len() {
gst::element_error!(
element,
gst::CoreError::Failed,
("Buffer does not contain the complete MP4 atom")
);
return Err(gst::FlowError::Error);
}
// Handle 'moof' and 'mdat' atoms
match atom_type {
b"moof" => {
// Handle 'moof' atom
// Send the 'moof' atom over the moq_transport protocol
// self.moq_transport.send_moof(data.as_slice());
},
b"mdat" => {
// Handle 'mdat' atom
// Send the 'mdat' atom over the moq_transport protocol
// self.moq_transport.send_mdat(data.as_slice());
},
_ => {
// Handle other atoms or ignore them
}
}
Ok(gst::FlowSuccess::Ok)
}
}

17
src/warpsink/mod.rs Normal file
View File

@@ -0,0 +1,17 @@
use gst::glib;
use gst::prelude::*;
mod imp;
glib::wrapper! {
pub struct MoqSink(ObjectSubclass<imp::MoqSink>) @extends gst_base::BaseSink, gst::Element, gst::Object;
}
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
gst::Element::register(
Some(plugin),
"moqsink",
gst::Rank::None,
MoqSink::static_type(),
)
}