Port threadshare plugin to new subclassing API

This commit is contained in:
Sebastian Dröge 2018-12-06 13:03:04 +02:00
parent 4d87c11293
commit e64a9b4a1a
7 changed files with 1088 additions and 990 deletions

View file

@ -12,10 +12,8 @@ gio-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys" } gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys" }
glib = { git = "https://github.com/gtk-rs/glib", features = ["subclassing"] } glib = { git = "https://github.com/gtk-rs/glib", features = ["subclassing"] }
gio = { git = "https://github.com/gtk-rs/gio" } gio = { git = "https://github.com/gtk-rs/gio" }
gstreamer = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" } gstreamer = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["subclassing"] }
gstreamer-check = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" } gstreamer-check = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gobject-subclass = { git = "https://github.com/gtk-rs/gobject-subclass" }
gst-plugin = { path = "../gst-plugin" }
tokio = "0.1" tokio = "0.1"
tokio-reactor = "0.1" tokio-reactor = "0.1"
tokio-executor = "0.1" tokio-executor = "0.1"

View file

@ -17,11 +17,11 @@
use glib; use glib;
use glib::prelude::*; use glib::prelude::*;
use glib::subclass;
use glib::subclass::prelude::*;
use gst; use gst;
use gst::prelude::*; use gst::prelude::*;
use gst::subclass::prelude::*;
use gobject_subclass::object::*;
use gst_plugin::element::*;
use std::sync::Mutex; use std::sync::Mutex;
use std::u32; use std::u32;
@ -63,44 +63,56 @@ impl Default for Settings {
} }
} }
static PROPERTIES: [Property; 5] = [ static PROPERTIES: [subclass::Property; 5] = [
Property::String( subclass::Property("context", || {
glib::ParamSpec::string(
"context", "context",
"Context", "Context",
"Context name to share threads with", "Context name to share threads with",
Some(DEFAULT_CONTEXT), Some(DEFAULT_CONTEXT),
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::UInt( }),
subclass::Property("context-wait", || {
glib::ParamSpec::uint(
"context-wait", "context-wait",
"Context Wait", "Context Wait",
"Throttle poll loop to run at most once every this many ms", "Throttle poll loop to run at most once every this many ms",
(0, 1000), 0,
1000,
DEFAULT_CONTEXT_WAIT, DEFAULT_CONTEXT_WAIT,
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::UInt( }),
subclass::Property("max-buffers", || {
glib::ParamSpec::uint(
"max-buffers", "max-buffers",
"Max Buffers", "Max Buffers",
"Maximum number of buffers to queue up", "Maximum number of buffers to queue up",
(1, u32::MAX), 1,
u32::MAX,
DEFAULT_MAX_BUFFERS, DEFAULT_MAX_BUFFERS,
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::Boxed( }),
subclass::Property("caps", || {
glib::ParamSpec::boxed(
"caps", "caps",
"Caps", "Caps",
"Caps to use", "Caps to use",
gst::Caps::static_type, gst::Caps::static_type(),
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::Boolean( }),
subclass::Property("do-timestamp", || {
glib::ParamSpec::boolean(
"do-timestamp", "do-timestamp",
"Do Timestamp", "Do Timestamp",
"Timestamp buffers with the current running time on arrival", "Timestamp buffers with the current running time on arrival",
DEFAULT_DO_TIMESTAMP, DEFAULT_DO_TIMESTAMP,
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
}),
]; ];
struct State { struct State {
@ -133,88 +145,6 @@ struct AppSrc {
} }
impl AppSrc { impl AppSrc {
fn class_init(klass: &mut ElementClass) {
klass.set_metadata(
"Thread-sharing app source",
"Source/Generic",
"Thread-sharing app source",
"Sebastian Dröge <sebastian@centricular.com>",
);
let caps = gst::Caps::new_any();
let src_pad_template = gst::PadTemplate::new(
"src",
gst::PadDirection::Src,
gst::PadPresence::Always,
&caps,
);
klass.add_pad_template(src_pad_template);
klass.install_properties(&PROPERTIES);
klass.add_action_signal(
"push-buffer",
&[gst::Buffer::static_type()],
bool::static_type(),
|args| {
let element = args[0]
.get::<gst::Element>()
.unwrap()
.downcast::<Element>()
.unwrap();
let buffer = args[1].get::<gst::Buffer>().unwrap();
let appsrc = element.get_impl().downcast_ref::<AppSrc>().unwrap();
Some(appsrc.push_buffer(&element, buffer).to_value())
},
);
klass.add_action_signal("end-of-stream", &[], bool::static_type(), |args| {
let element = args[0]
.get::<gst::Element>()
.unwrap()
.downcast::<Element>()
.unwrap();
let appsrc = element.get_impl().downcast_ref::<AppSrc>().unwrap();
Some(appsrc.end_of_stream(&element).to_value())
});
}
fn init(element: &Element) -> Box<ElementImpl<Element>> {
let templ = element.get_pad_template("src").unwrap();
let src_pad = gst::Pad::new_from_template(&templ, "src");
src_pad.set_event_function(|pad, parent, event| {
AppSrc::catch_panic_pad_function(
parent,
|| false,
|queue, element| queue.src_event(pad, element, event),
)
});
src_pad.set_query_function(|pad, parent, query| {
AppSrc::catch_panic_pad_function(
parent,
|| false,
|queue, element| queue.src_query(pad, element, query),
)
});
element.add_pad(&src_pad).unwrap();
::set_element_flags(element, gst::ElementFlags::SOURCE);
Box::new(Self {
cat: gst::DebugCategory::new(
"ts-appsrc",
gst::DebugColorFlags::empty(),
"Thread-sharing app source",
),
src_pad: src_pad,
state: Mutex::new(State::default()),
settings: Mutex::new(Settings::default()),
})
}
fn create_io_context_event(state: &State) -> Option<gst::Event> { fn create_io_context_event(state: &State) -> Option<gst::Event> {
if let (&Some(ref pending_future_id), &Some(ref io_context)) = if let (&Some(ref pending_future_id), &Some(ref io_context)) =
(&state.pending_future_id, &state.io_context) (&state.pending_future_id, &state.io_context)
@ -232,7 +162,7 @@ impl AppSrc {
} }
} }
fn src_event(&self, pad: &gst::Pad, element: &Element, event: gst::Event) -> bool { fn src_event(&self, pad: &gst::Pad, element: &gst::Element, event: gst::Event) -> bool {
use gst::EventView; use gst::EventView;
gst_log!(self.cat, obj: pad, "Handling event {:?}", event); gst_log!(self.cat, obj: pad, "Handling event {:?}", event);
@ -265,7 +195,12 @@ impl AppSrc {
ret ret
} }
fn src_query(&self, pad: &gst::Pad, _element: &Element, query: &mut gst::QueryRef) -> bool { fn src_query(
&self,
pad: &gst::Pad,
_element: &gst::Element,
query: &mut gst::QueryRef,
) -> bool {
use gst::QueryView; use gst::QueryView;
gst_log!(self.cat, obj: pad, "Handling query {:?}", query); gst_log!(self.cat, obj: pad, "Handling query {:?}", query);
@ -306,7 +241,7 @@ impl AppSrc {
ret ret
} }
fn push_buffer(&self, element: &Element, mut buffer: gst::Buffer) -> bool { fn push_buffer(&self, element: &gst::Element, mut buffer: gst::Buffer) -> bool {
let settings = self.settings.lock().unwrap().clone(); let settings = self.settings.lock().unwrap().clone();
if settings.do_timestamp { if settings.do_timestamp {
@ -337,7 +272,7 @@ impl AppSrc {
} }
} }
fn end_of_stream(&self, element: &Element) -> bool { fn end_of_stream(&self, element: &gst::Element) -> bool {
let mut state = self.state.lock().unwrap(); let mut state = self.state.lock().unwrap();
if let Some(ref mut channel) = state.channel { if let Some(ref mut channel) = state.channel {
match channel.try_send(Either::Right(gst::Event::new_eos().build())) { match channel.try_send(Either::Right(gst::Event::new_eos().build())) {
@ -354,7 +289,7 @@ impl AppSrc {
fn push_item( fn push_item(
&self, &self,
element: &Element, element: &gst::Element,
item: Either<gst::Buffer, gst::Event>, item: Either<gst::Buffer, gst::Event>,
) -> future::Either< ) -> future::Either<
Box<Future<Item = (), Error = ()> + Send + 'static>, Box<Future<Item = (), Error = ()> + Send + 'static>,
@ -449,7 +384,7 @@ impl AppSrc {
} }
} }
fn prepare(&self, element: &Element) -> Result<(), gst::ErrorMessage> { fn prepare(&self, element: &gst::Element) -> Result<(), gst::ErrorMessage> {
gst_debug!(self.cat, obj: element, "Preparing"); gst_debug!(self.cat, obj: element, "Preparing");
let settings = self.settings.lock().unwrap().clone(); let settings = self.settings.lock().unwrap().clone();
@ -480,7 +415,7 @@ impl AppSrc {
Ok(()) Ok(())
} }
fn unprepare(&self, element: &Element) -> Result<(), ()> { fn unprepare(&self, element: &gst::Element) -> Result<(), ()> {
gst_debug!(self.cat, obj: element, "Unpreparing"); gst_debug!(self.cat, obj: element, "Unpreparing");
let mut state = self.state.lock().unwrap(); let mut state = self.state.lock().unwrap();
@ -498,7 +433,7 @@ impl AppSrc {
Ok(()) Ok(())
} }
fn start(&self, element: &Element) -> Result<(), ()> { fn start(&self, element: &gst::Element) -> Result<(), ()> {
gst_debug!(self.cat, obj: element, "Starting"); gst_debug!(self.cat, obj: element, "Starting");
let settings = self.settings.lock().unwrap().clone(); let settings = self.settings.lock().unwrap().clone();
let mut state = self.state.lock().unwrap(); let mut state = self.state.lock().unwrap();
@ -515,7 +450,7 @@ impl AppSrc {
let element_clone = element.clone(); let element_clone = element.clone();
let future = channel_receiver.for_each(move |item| { let future = channel_receiver.for_each(move |item| {
let appsrc = element_clone.get_impl().downcast_ref::<AppSrc>().unwrap(); let appsrc = Self::from_instance(&element_clone);
appsrc.push_item(&element_clone, item) appsrc.push_item(&element_clone, item)
}); });
io_context.spawn(future); io_context.spawn(future);
@ -526,7 +461,7 @@ impl AppSrc {
Ok(()) Ok(())
} }
fn stop(&self, element: &Element) -> Result<(), ()> { fn stop(&self, element: &gst::Element) -> Result<(), ()> {
gst_debug!(self.cat, obj: element, "Stopping"); gst_debug!(self.cat, obj: element, "Stopping");
let mut state = self.state.lock().unwrap(); let mut state = self.state.lock().unwrap();
@ -539,28 +474,114 @@ impl AppSrc {
} }
} }
impl ObjectImpl<Element> for AppSrc { impl ObjectSubclass for AppSrc {
fn set_property(&self, _obj: &glib::Object, id: u32, value: &glib::Value) { const NAME: &'static str = "RsTsAppSrc";
let prop = &PROPERTIES[id as usize]; type ParentType = gst::Element;
type Instance = gst::subclass::ElementInstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;
glib_object_subclass!();
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
klass.set_metadata(
"Thread-sharing app source",
"Source/Generic",
"Thread-sharing app source",
"Sebastian Dröge <sebastian@centricular.com>",
);
let caps = gst::Caps::new_any();
let src_pad_template = gst::PadTemplate::new(
"src",
gst::PadDirection::Src,
gst::PadPresence::Always,
&caps,
);
klass.add_pad_template(src_pad_template);
klass.install_properties(&PROPERTIES);
klass.add_action_signal(
"push-buffer",
&[gst::Buffer::static_type()],
bool::static_type(),
|args| {
let element = args[0].get::<gst::Element>().unwrap();
let buffer = args[1].get::<gst::Buffer>().unwrap();
let appsrc = Self::from_instance(&element);
Some(appsrc.push_buffer(&element, buffer).to_value())
},
);
klass.add_action_signal("end-of-stream", &[], bool::static_type(), |args| {
let element = args[0].get::<gst::Element>().unwrap();
let appsrc = Self::from_instance(&element);
Some(appsrc.end_of_stream(&element).to_value())
});
}
fn new() -> Self {
unreachable!()
}
fn new_with_class(klass: &subclass::simple::ClassStruct<Self>) -> Self {
let templ = klass.get_pad_template("src").unwrap();
let src_pad = gst::Pad::new_from_template(&templ, "src");
src_pad.set_event_function(|pad, parent, event| {
AppSrc::catch_panic_pad_function(
parent,
|| false,
|queue, element| queue.src_event(pad, element, event),
)
});
src_pad.set_query_function(|pad, parent, query| {
AppSrc::catch_panic_pad_function(
parent,
|| false,
|queue, element| queue.src_query(pad, element, query),
)
});
Self {
cat: gst::DebugCategory::new(
"ts-appsrc",
gst::DebugColorFlags::empty(),
"Thread-sharing app source",
),
src_pad: src_pad,
state: Mutex::new(State::default()),
settings: Mutex::new(Settings::default()),
}
}
}
impl ObjectImpl for AppSrc {
glib_object_impl!();
fn set_property(&self, _obj: &glib::Object, id: usize, value: &glib::Value) {
let prop = &PROPERTIES[id];
match *prop { match *prop {
Property::String("context", ..) => { subclass::Property("context", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.context = value.get().unwrap_or_else(|| "".into()); settings.context = value.get().unwrap_or_else(|| "".into());
} }
Property::UInt("context-wait", ..) => { subclass::Property("context-wait", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.context_wait = value.get().unwrap(); settings.context_wait = value.get().unwrap();
} }
Property::Boxed("caps", ..) => { subclass::Property("caps", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.caps = value.get(); settings.caps = value.get();
} }
Property::UInt("max-buffers", ..) => { subclass::Property("max-buffers", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.max_buffers = value.get().unwrap(); settings.max_buffers = value.get().unwrap();
} }
Property::Boolean("do-timestamp", ..) => { subclass::Property("do-timestamp", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.do_timestamp = value.get().unwrap(); settings.do_timestamp = value.get().unwrap();
} }
@ -568,39 +589,48 @@ impl ObjectImpl<Element> for AppSrc {
} }
} }
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::String("context", ..) => { subclass::Property("context", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.context.to_value()) Ok(settings.context.to_value())
} }
Property::UInt("context-wait", ..) => { subclass::Property("context-wait", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.context_wait.to_value()) Ok(settings.context_wait.to_value())
} }
Property::Boxed("caps", ..) => { subclass::Property("caps", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.caps.to_value()) Ok(settings.caps.to_value())
} }
Property::UInt("max-buffers", ..) => { subclass::Property("max-buffers", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.max_buffers.to_value()) Ok(settings.max_buffers.to_value())
} }
Property::Boolean("do-timestamp", ..) => { subclass::Property("do-timestamp", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.do_timestamp.to_value()) Ok(settings.do_timestamp.to_value())
} }
_ => unimplemented!(), _ => unimplemented!(),
} }
} }
fn constructed(&self, obj: &glib::Object) {
self.parent_constructed(obj);
let element = obj.downcast_ref::<gst::Element>().unwrap();
element.add_pad(&self.src_pad).unwrap();
::set_element_flags(element, gst::ElementFlags::SOURCE);
}
} }
impl ElementImpl<Element> for AppSrc { impl ElementImpl for AppSrc {
fn change_state( fn change_state(
&self, &self,
element: &Element, element: &gst::Element,
transition: gst::StateChange, transition: gst::StateChange,
) -> gst::StateChangeReturn { ) -> gst::StateChangeReturn {
gst_trace!(self.cat, obj: element, "Changing state {:?}", transition); gst_trace!(self.cat, obj: element, "Changing state {:?}", transition);
@ -624,7 +654,7 @@ impl ElementImpl<Element> for AppSrc {
_ => (), _ => (),
} }
let mut ret = element.parent_change_state(transition); let mut ret = self.parent_change_state(element, transition);
if ret == gst::StateChangeReturn::Failure { if ret == gst::StateChangeReturn::Failure {
return ret; return ret;
} }
@ -648,23 +678,6 @@ impl ElementImpl<Element> for AppSrc {
} }
} }
struct AppSrcStatic;
impl ImplTypeStatic<Element> for AppSrcStatic {
fn get_name(&self) -> &str {
"AppSrc"
}
fn new(&self, element: &Element) -> Box<ElementImpl<Element>> {
AppSrc::init(element)
}
fn class_init(&self, klass: &mut ElementClass) {
AppSrc::class_init(klass);
}
}
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> { pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
let type_ = register_type(AppSrcStatic); gst::Element::register(plugin, "ts-appsrc", 0, AppSrc::get_type())
gst::Element::register(plugin, "ts-appsrc", 0, type_)
} }

View file

@ -27,9 +27,6 @@ extern crate gstreamer_sys as gst_ffi;
extern crate gio; extern crate gio;
#[macro_use] #[macro_use]
extern crate glib; extern crate glib;
extern crate gobject_subclass;
#[macro_use]
extern crate gst_plugin;
#[macro_use] #[macro_use]
extern crate gstreamer as gst; extern crate gstreamer as gst;
@ -73,16 +70,16 @@ fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
Ok(()) Ok(())
} }
plugin_define!( gst_plugin_define!(
b"threadshare\0", "threadshare",
b"Threadshare Plugin\0", "Threadshare Plugin",
plugin_init, plugin_init,
b"0.1.0\0", "0.1.0",
b"LGPL\0", "LGPL",
b"threadshare\0", "threadshare",
b"threadshare\0", "threadshare",
b"https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs\0", "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs",
b"2018-03-01\0" "2018-03-01"
); );
pub fn set_element_flags<T: glib::IsA<gst::Object> + glib::IsA<gst::Element>>( pub fn set_element_flags<T: glib::IsA<gst::Object> + glib::IsA<gst::Element>>(

View file

@ -17,11 +17,11 @@
use glib; use glib;
use glib::prelude::*; use glib::prelude::*;
use glib::subclass;
use glib::subclass::prelude::*;
use gst; use gst;
use gst::prelude::*; use gst::prelude::*;
use gst::subclass::prelude::*;
use gobject_subclass::object::*;
use gst_plugin::element::*;
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::VecDeque; use std::collections::VecDeque;
@ -87,62 +87,80 @@ impl Default for SettingsSrc {
} }
} }
static PROPERTIES_SRC: [Property; 6] = [ static PROPERTIES_SRC: [subclass::Property; 6] = [
Property::UInt( subclass::Property("max-size-buffers", || {
glib::ParamSpec::uint(
"max-size-buffers", "max-size-buffers",
"Max Size Buffers", "Max Size Buffers",
"Maximum number of buffers to queue (0=unlimited)", "Maximum number of buffers to queue (0=unlimited)",
(0, u32::MAX), 0,
u32::MAX,
DEFAULT_MAX_SIZE_BUFFERS, DEFAULT_MAX_SIZE_BUFFERS,
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::UInt( }),
subclass::Property("max-size-bytes", || {
glib::ParamSpec::uint(
"max-size-bytes", "max-size-bytes",
"Max Size Bytes", "Max Size Bytes",
"Maximum number of bytes to queue (0=unlimited)", "Maximum number of bytes to queue (0=unlimited)",
(0, u32::MAX), 0,
u32::MAX,
DEFAULT_MAX_SIZE_BYTES, DEFAULT_MAX_SIZE_BYTES,
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::UInt64( }),
subclass::Property("max-size-time", || {
glib::ParamSpec::uint64(
"max-size-time", "max-size-time",
"Max Size Time", "Max Size Time",
"Maximum number of nanoseconds to queue (0=unlimited)", "Maximum number of nanoseconds to queue (0=unlimited)",
(0, u64::MAX - 1), 0,
u64::MAX - 1,
DEFAULT_MAX_SIZE_TIME, DEFAULT_MAX_SIZE_TIME,
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::String( }),
subclass::Property("context", || {
glib::ParamSpec::string(
"context", "context",
"Context", "Context",
"Context name to share threads with", "Context name to share threads with",
Some(DEFAULT_CONTEXT), Some(DEFAULT_CONTEXT),
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::UInt( }),
subclass::Property("context-wait", || {
glib::ParamSpec::uint(
"context-wait", "context-wait",
"Context Wait", "Context Wait",
"Throttle poll loop to run at most once every this many ms", "Throttle poll loop to run at most once every this many ms",
(0, 1000), 0,
1000,
DEFAULT_CONTEXT_WAIT, DEFAULT_CONTEXT_WAIT,
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::String( }),
subclass::Property("proxy-context", || {
glib::ParamSpec::string(
"proxy-context", "proxy-context",
"Proxy Context", "Proxy Context",
"Context name of the proxy to share with", "Context name of the proxy to share with",
Some(DEFAULT_PROXY_CONTEXT), Some(DEFAULT_PROXY_CONTEXT),
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
}),
]; ];
static PROPERTIES_SINK: [Property; 1] = [Property::String( static PROPERTIES_SINK: [subclass::Property; 1] = [subclass::Property("proxy-context", || {
glib::ParamSpec::string(
"proxy-context", "proxy-context",
"Proxy Context", "Proxy Context",
"Context name of the proxy to share with", "Context name of the proxy to share with",
Some(DEFAULT_PROXY_CONTEXT), Some(DEFAULT_PROXY_CONTEXT),
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
)]; )
})];
// TODO: Refactor into a Sender and Receiver instead of the have_ booleans // TODO: Refactor into a Sender and Receiver instead of the have_ booleans
@ -273,80 +291,10 @@ struct ProxySink {
} }
impl ProxySink { impl ProxySink {
fn class_init(klass: &mut ElementClass) {
klass.set_metadata(
"Thread-sharing proxy sink",
"Sink/Generic",
"Thread-sharing proxy sink",
"Sebastian Dröge <sebastian@centricular.com>",
);
let caps = gst::Caps::new_any();
let sink_pad_template = gst::PadTemplate::new(
"sink",
gst::PadDirection::Sink,
gst::PadPresence::Always,
&caps,
);
klass.add_pad_template(sink_pad_template);
klass.install_properties(&PROPERTIES_SINK);
}
fn init(element: &Element) -> Box<ElementImpl<Element>> {
let templ = element.get_pad_template("sink").unwrap();
let sink_pad = gst::Pad::new_from_template(&templ, "sink");
sink_pad.set_chain_function(|pad, parent, buffer| {
ProxySink::catch_panic_pad_function(
parent,
|| gst::FlowReturn::Error,
|queue, element| queue.sink_chain(pad, element, buffer),
)
});
sink_pad.set_chain_list_function(|pad, parent, list| {
ProxySink::catch_panic_pad_function(
parent,
|| gst::FlowReturn::Error,
|queue, element| queue.sink_chain_list(pad, element, list),
)
});
sink_pad.set_event_function(|pad, parent, event| {
ProxySink::catch_panic_pad_function(
parent,
|| false,
|queue, element| queue.sink_event(pad, element, event),
)
});
sink_pad.set_query_function(|pad, parent, query| {
ProxySink::catch_panic_pad_function(
parent,
|| false,
|queue, element| queue.sink_query(pad, element, query),
)
});
element.add_pad(&sink_pad).unwrap();
::set_element_flags(element, gst::ElementFlags::SINK);
Box::new(Self {
cat: gst::DebugCategory::new(
"ts-proxysink",
gst::DebugColorFlags::empty(),
"Thread-sharing proxy sink",
),
sink_pad: sink_pad,
state: Mutex::new(StateSink::default()),
settings: Mutex::new(SettingsSink::default()),
})
}
fn enqueue_item( fn enqueue_item(
&self, &self,
_pad: &gst::Pad, _pad: &gst::Pad,
element: &Element, element: &gst::Element,
item: DataQueueItem, item: DataQueueItem,
) -> gst::FlowReturn { ) -> gst::FlowReturn {
let wait_future = { let wait_future = {
@ -426,10 +374,7 @@ impl ProxySink {
let element_clone = element.clone(); let element_clone = element.clone();
let future = future::poll_fn(move || { let future = future::poll_fn(move || {
let sink = element_clone let sink = Self::from_instance(&element_clone);
.get_impl()
.downcast_ref::<ProxySink>()
.unwrap();
let state = sink.state.lock().unwrap(); let state = sink.state.lock().unwrap();
gst_log!( gst_log!(
@ -549,7 +494,7 @@ impl ProxySink {
fn sink_chain( fn sink_chain(
&self, &self,
pad: &gst::Pad, pad: &gst::Pad,
element: &Element, element: &gst::Element,
buffer: gst::Buffer, buffer: gst::Buffer,
) -> gst::FlowReturn { ) -> gst::FlowReturn {
gst_log!(self.cat, obj: pad, "Handling buffer {:?}", buffer); gst_log!(self.cat, obj: pad, "Handling buffer {:?}", buffer);
@ -559,14 +504,14 @@ impl ProxySink {
fn sink_chain_list( fn sink_chain_list(
&self, &self,
pad: &gst::Pad, pad: &gst::Pad,
element: &Element, element: &gst::Element,
list: gst::BufferList, list: gst::BufferList,
) -> gst::FlowReturn { ) -> gst::FlowReturn {
gst_log!(self.cat, obj: pad, "Handling buffer list {:?}", list); gst_log!(self.cat, obj: pad, "Handling buffer list {:?}", list);
self.enqueue_item(pad, element, DataQueueItem::BufferList(list)) self.enqueue_item(pad, element, DataQueueItem::BufferList(list))
} }
fn sink_event(&self, pad: &gst::Pad, element: &Element, event: gst::Event) -> bool { fn sink_event(&self, pad: &gst::Pad, element: &gst::Element, event: gst::Event) -> bool {
use gst::EventView; use gst::EventView;
gst_log!(self.cat, obj: pad, "Handling event {:?}", event); gst_log!(self.cat, obj: pad, "Handling event {:?}", event);
@ -612,13 +557,18 @@ impl ProxySink {
true true
} }
fn sink_query(&self, pad: &gst::Pad, element: &Element, query: &mut gst::QueryRef) -> bool { fn sink_query(
&self,
pad: &gst::Pad,
element: &gst::Element,
query: &mut gst::QueryRef,
) -> bool {
gst_log!(self.cat, obj: pad, "Handling query {:?}", query); gst_log!(self.cat, obj: pad, "Handling query {:?}", query);
pad.query_default(element, query) pad.query_default(element, query)
} }
fn prepare(&self, element: &Element) -> Result<(), gst::ErrorMessage> { fn prepare(&self, element: &gst::Element) -> Result<(), gst::ErrorMessage> {
gst_debug!(self.cat, obj: element, "Preparing"); gst_debug!(self.cat, obj: element, "Preparing");
let settings = self.settings.lock().unwrap().clone(); let settings = self.settings.lock().unwrap().clone();
@ -640,7 +590,7 @@ impl ProxySink {
Ok(()) Ok(())
} }
fn unprepare(&self, element: &Element) -> Result<(), ()> { fn unprepare(&self, element: &gst::Element) -> Result<(), ()> {
gst_debug!(self.cat, obj: element, "Unpreparing"); gst_debug!(self.cat, obj: element, "Unpreparing");
let mut state = self.state.lock().unwrap(); let mut state = self.state.lock().unwrap();
@ -650,7 +600,7 @@ impl ProxySink {
Ok(()) Ok(())
} }
fn start(&self, element: &Element) -> Result<(), ()> { fn start(&self, element: &gst::Element) -> Result<(), ()> {
gst_debug!(self.cat, obj: element, "Starting"); gst_debug!(self.cat, obj: element, "Starting");
let state = self.state.lock().unwrap(); let state = self.state.lock().unwrap();
@ -662,7 +612,7 @@ impl ProxySink {
Ok(()) Ok(())
} }
fn stop(&self, element: &Element) -> Result<(), ()> { fn stop(&self, element: &gst::Element) -> Result<(), ()> {
gst_debug!(self.cat, obj: element, "Stopping"); gst_debug!(self.cat, obj: element, "Stopping");
let mut state = self.state.lock().unwrap(); let mut state = self.state.lock().unwrap();
@ -682,12 +632,93 @@ impl ProxySink {
} }
} }
impl ObjectImpl<Element> for ProxySink { impl ObjectSubclass for ProxySink {
fn set_property(&self, _obj: &glib::Object, id: u32, value: &glib::Value) { const NAME: &'static str = "RsTsProxySink";
let prop = &PROPERTIES_SINK[id as usize]; type ParentType = gst::Element;
type Instance = gst::subclass::ElementInstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;
glib_object_subclass!();
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
klass.set_metadata(
"Thread-sharing proxy sink",
"Sink/Generic",
"Thread-sharing proxy sink",
"Sebastian Dröge <sebastian@centricular.com>",
);
let caps = gst::Caps::new_any();
let sink_pad_template = gst::PadTemplate::new(
"sink",
gst::PadDirection::Sink,
gst::PadPresence::Always,
&caps,
);
klass.add_pad_template(sink_pad_template);
klass.install_properties(&PROPERTIES_SINK);
}
fn new() -> Self {
unreachable!()
}
fn new_with_class(klass: &subclass::simple::ClassStruct<Self>) -> Self {
let templ = klass.get_pad_template("sink").unwrap();
let sink_pad = gst::Pad::new_from_template(&templ, "sink");
sink_pad.set_chain_function(|pad, parent, buffer| {
ProxySink::catch_panic_pad_function(
parent,
|| gst::FlowReturn::Error,
|queue, element| queue.sink_chain(pad, element, buffer),
)
});
sink_pad.set_chain_list_function(|pad, parent, list| {
ProxySink::catch_panic_pad_function(
parent,
|| gst::FlowReturn::Error,
|queue, element| queue.sink_chain_list(pad, element, list),
)
});
sink_pad.set_event_function(|pad, parent, event| {
ProxySink::catch_panic_pad_function(
parent,
|| false,
|queue, element| queue.sink_event(pad, element, event),
)
});
sink_pad.set_query_function(|pad, parent, query| {
ProxySink::catch_panic_pad_function(
parent,
|| false,
|queue, element| queue.sink_query(pad, element, query),
)
});
Self {
cat: gst::DebugCategory::new(
"ts-proxysink",
gst::DebugColorFlags::empty(),
"Thread-sharing proxy sink",
),
sink_pad: sink_pad,
state: Mutex::new(StateSink::default()),
settings: Mutex::new(SettingsSink::default()),
}
}
}
impl ObjectImpl for ProxySink {
glib_object_impl!();
fn set_property(&self, _obj: &glib::Object, id: usize, value: &glib::Value) {
let prop = &PROPERTIES_SINK[id];
match *prop { match *prop {
Property::String("proxy-context", ..) => { subclass::Property("proxy-context", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.proxy_context = value.get().unwrap_or_else(|| "".into()); settings.proxy_context = value.get().unwrap_or_else(|| "".into());
} }
@ -695,23 +726,32 @@ impl ObjectImpl<Element> for ProxySink {
} }
} }
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_SINK[id as usize]; let prop = &PROPERTIES_SINK[id];
match *prop { match *prop {
Property::String("proxy-context", ..) => { subclass::Property("proxy-context", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.proxy_context.to_value()) Ok(settings.proxy_context.to_value())
} }
_ => unimplemented!(), _ => unimplemented!(),
} }
} }
fn constructed(&self, obj: &glib::Object) {
self.parent_constructed(obj);
let element = obj.downcast_ref::<gst::Element>().unwrap();
element.add_pad(&self.sink_pad).unwrap();
::set_element_flags(element, gst::ElementFlags::SINK);
}
} }
impl ElementImpl<Element> for ProxySink { impl ElementImpl for ProxySink {
fn change_state( fn change_state(
&self, &self,
element: &Element, element: &gst::Element,
transition: gst::StateChange, transition: gst::StateChange,
) -> gst::StateChangeReturn { ) -> gst::StateChangeReturn {
gst_trace!(self.cat, obj: element, "Changing state {:?}", transition); gst_trace!(self.cat, obj: element, "Changing state {:?}", transition);
@ -735,7 +775,7 @@ impl ElementImpl<Element> for ProxySink {
_ => (), _ => (),
} }
let ret = element.parent_change_state(transition); let ret = self.parent_change_state(element, transition);
if ret == gst::StateChangeReturn::Failure { if ret == gst::StateChangeReturn::Failure {
return ret; return ret;
} }
@ -760,71 +800,6 @@ struct ProxySrc {
} }
impl ProxySrc { impl ProxySrc {
fn class_init(klass: &mut ElementClass) {
klass.set_metadata(
"Thread-sharing proxy source",
"Source/Generic",
"Thread-sharing proxy source",
"Sebastian Dröge <sebastian@centricular.com>",
);
let caps = gst::Caps::new_any();
let src_pad_template = gst::PadTemplate::new(
"src",
gst::PadDirection::Src,
gst::PadPresence::Always,
&caps,
);
klass.add_pad_template(src_pad_template);
klass.install_properties(&PROPERTIES_SRC);
}
fn init(element: &Element) -> Box<ElementImpl<Element>> {
let templ = element.get_pad_template("src").unwrap();
let src_pad = gst::Pad::new_from_template(&templ, "src");
src_pad.set_event_function(|pad, parent, event| {
ProxySrc::catch_panic_pad_function(
parent,
|| false,
|queue, element| queue.src_event(pad, element, event),
)
});
src_pad.set_query_function(|pad, parent, query| {
ProxySrc::catch_panic_pad_function(
parent,
|| false,
|queue, element| queue.src_query(pad, element, query),
)
});
element.add_pad(&src_pad).unwrap();
::set_element_flags(element, gst::ElementFlags::SOURCE);
Box::new(Self {
cat: gst::DebugCategory::new(
"ts-proxysrc",
gst::DebugColorFlags::empty(),
"Thread-sharing proxy source",
),
src_pad: src_pad,
state: Mutex::new(StateSrc::default()),
settings: Mutex::new(SettingsSrc::default()),
})
}
fn catch_panic_pad_function<T, F: FnOnce(&Self, &Element) -> T, G: FnOnce() -> T>(
parent: &Option<gst::Object>,
fallback: G,
f: F,
) -> T {
let element = parent.as_ref().unwrap().downcast_ref::<Element>().unwrap();
let src = element.get_impl().downcast_ref::<ProxySrc>().unwrap();
element.catch_panic(fallback, |element| f(src, element))
}
fn create_io_context_event(state: &StateSrc) -> Option<gst::Event> { fn create_io_context_event(state: &StateSrc) -> Option<gst::Event> {
if let (&Some(ref pending_future_id), &Some(ref io_context)) = if let (&Some(ref pending_future_id), &Some(ref io_context)) =
(&state.pending_future_id, &state.io_context) (&state.pending_future_id, &state.io_context)
@ -842,7 +817,7 @@ impl ProxySrc {
} }
} }
fn src_event(&self, pad: &gst::Pad, element: &Element, event: gst::Event) -> bool { fn src_event(&self, pad: &gst::Pad, element: &gst::Element, event: gst::Event) -> bool {
use gst::EventView; use gst::EventView;
gst_log!(self.cat, obj: pad, "Handling event {:?}", event); gst_log!(self.cat, obj: pad, "Handling event {:?}", event);
@ -875,7 +850,12 @@ impl ProxySrc {
ret ret
} }
fn src_query(&self, pad: &gst::Pad, _element: &Element, query: &mut gst::QueryRef) -> bool { fn src_query(
&self,
pad: &gst::Pad,
_element: &gst::Element,
query: &mut gst::QueryRef,
) -> bool {
use gst::QueryView; use gst::QueryView;
gst_log!(self.cat, obj: pad, "Handling query {:?}", query); gst_log!(self.cat, obj: pad, "Handling query {:?}", query);
@ -917,7 +897,7 @@ impl ProxySrc {
fn push_item( fn push_item(
&self, &self,
element: &Element, element: &gst::Element,
item: DataQueueItem, item: DataQueueItem,
) -> future::Either< ) -> future::Either<
Box<Future<Item = (), Error = gst::FlowError> + Send + 'static>, Box<Future<Item = (), Error = gst::FlowError> + Send + 'static>,
@ -1045,7 +1025,7 @@ impl ProxySrc {
} }
} }
fn prepare(&self, element: &Element) -> Result<(), gst::ErrorMessage> { fn prepare(&self, element: &gst::Element) -> Result<(), gst::ErrorMessage> {
gst_debug!(self.cat, obj: element, "Preparing"); gst_debug!(self.cat, obj: element, "Preparing");
let settings = self.settings.lock().unwrap().clone(); let settings = self.settings.lock().unwrap().clone();
@ -1095,14 +1075,11 @@ impl ProxySrc {
.schedule( .schedule(
&io_context, &io_context,
move |item| { move |item| {
let src = element_clone.get_impl().downcast_ref::<ProxySrc>().unwrap(); let src = Self::from_instance(&element_clone);
src.push_item(&element_clone, item) src.push_item(&element_clone, item)
}, },
move |err| { move |err| {
let src = element_clone2 let src = Self::from_instance(&element_clone2);
.get_impl()
.downcast_ref::<ProxySrc>()
.unwrap();
gst_error!(src.cat, obj: &element_clone2, "Got error {}", err); gst_error!(src.cat, obj: &element_clone2, "Got error {}", err);
match err { match err {
gst::FlowError::CustomError => (), gst::FlowError::CustomError => (),
@ -1143,7 +1120,7 @@ impl ProxySrc {
Ok(()) Ok(())
} }
fn unprepare(&self, element: &Element) -> Result<(), ()> { fn unprepare(&self, element: &gst::Element) -> Result<(), ()> {
gst_debug!(self.cat, obj: element, "Unpreparing"); gst_debug!(self.cat, obj: element, "Unpreparing");
// FIXME: The IO Context has to be alive longer than the queue, // FIXME: The IO Context has to be alive longer than the queue,
@ -1181,7 +1158,7 @@ impl ProxySrc {
Ok(()) Ok(())
} }
fn start(&self, element: &Element) -> Result<(), ()> { fn start(&self, element: &gst::Element) -> Result<(), ()> {
gst_debug!(self.cat, obj: element, "Starting"); gst_debug!(self.cat, obj: element, "Starting");
let state = self.state.lock().unwrap(); let state = self.state.lock().unwrap();
let queue = state.queue.as_ref().unwrap().0.lock().unwrap(); let queue = state.queue.as_ref().unwrap().0.lock().unwrap();
@ -1195,7 +1172,7 @@ impl ProxySrc {
Ok(()) Ok(())
} }
fn stop(&self, element: &Element) -> Result<(), ()> { fn stop(&self, element: &gst::Element) -> Result<(), ()> {
gst_debug!(self.cat, obj: element, "Stopping"); gst_debug!(self.cat, obj: element, "Stopping");
let state = self.state.lock().unwrap(); let state = self.state.lock().unwrap();
let mut queue = state.queue.as_ref().unwrap().0.lock().unwrap(); let mut queue = state.queue.as_ref().unwrap().0.lock().unwrap();
@ -1212,32 +1189,99 @@ impl ProxySrc {
} }
} }
impl ObjectImpl<Element> for ProxySrc { impl ObjectSubclass for ProxySrc {
fn set_property(&self, _obj: &glib::Object, id: u32, value: &glib::Value) { const NAME: &'static str = "RsTsProxySrc";
let prop = &PROPERTIES_SRC[id as usize]; type ParentType = gst::Element;
type Instance = gst::subclass::ElementInstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;
glib_object_subclass!();
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
klass.set_metadata(
"Thread-sharing proxy source",
"Source/Generic",
"Thread-sharing proxy source",
"Sebastian Dröge <sebastian@centricular.com>",
);
let caps = gst::Caps::new_any();
let src_pad_template = gst::PadTemplate::new(
"src",
gst::PadDirection::Src,
gst::PadPresence::Always,
&caps,
);
klass.add_pad_template(src_pad_template);
klass.install_properties(&PROPERTIES_SRC);
}
fn new() -> Self {
unreachable!()
}
fn new_with_class(klass: &subclass::simple::ClassStruct<Self>) -> Self {
let templ = klass.get_pad_template("src").unwrap();
let src_pad = gst::Pad::new_from_template(&templ, "src");
src_pad.set_event_function(|pad, parent, event| {
ProxySrc::catch_panic_pad_function(
parent,
|| false,
|queue, element| queue.src_event(pad, element, event),
)
});
src_pad.set_query_function(|pad, parent, query| {
ProxySrc::catch_panic_pad_function(
parent,
|| false,
|queue, element| queue.src_query(pad, element, query),
)
});
Self {
cat: gst::DebugCategory::new(
"ts-proxysrc",
gst::DebugColorFlags::empty(),
"Thread-sharing proxy source",
),
src_pad: src_pad,
state: Mutex::new(StateSrc::default()),
settings: Mutex::new(SettingsSrc::default()),
}
}
}
impl ObjectImpl for ProxySrc {
glib_object_impl!();
fn set_property(&self, _obj: &glib::Object, id: usize, value: &glib::Value) {
let prop = &PROPERTIES_SRC[id];
match *prop { match *prop {
Property::UInt("max-size-buffers", ..) => { subclass::Property("max-size-buffers", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.max_size_buffers = value.get().unwrap(); settings.max_size_buffers = value.get().unwrap();
} }
Property::UInt("max-size-bytes", ..) => { subclass::Property("max-size-bytes", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.max_size_bytes = value.get().unwrap(); settings.max_size_bytes = value.get().unwrap();
} }
Property::UInt64("max-size-time", ..) => { subclass::Property("max-size-time", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.max_size_time = value.get().unwrap(); settings.max_size_time = value.get().unwrap();
} }
Property::String("context", ..) => { subclass::Property("context", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.context = value.get().unwrap_or_else(|| "".into()); settings.context = value.get().unwrap_or_else(|| "".into());
} }
Property::UInt("context-wait", ..) => { subclass::Property("context-wait", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.context_wait = value.get().unwrap(); settings.context_wait = value.get().unwrap();
} }
Property::String("proxy-context", ..) => { subclass::Property("proxy-context", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.proxy_context = value.get().unwrap_or_else(|| "".into()); settings.proxy_context = value.get().unwrap_or_else(|| "".into());
} }
@ -1245,43 +1289,52 @@ impl ObjectImpl<Element> for ProxySrc {
} }
} }
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_SRC[id as usize]; let prop = &PROPERTIES_SRC[id];
match *prop { match *prop {
Property::UInt("max-size-buffers", ..) => { subclass::Property("max-size-buffers", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.max_size_buffers.to_value()) Ok(settings.max_size_buffers.to_value())
} }
Property::UInt("max-size-bytes", ..) => { subclass::Property("max-size-bytes", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.max_size_bytes.to_value()) Ok(settings.max_size_bytes.to_value())
} }
Property::UInt64("max-size-time", ..) => { subclass::Property("max-size-time", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.max_size_time.to_value()) Ok(settings.max_size_time.to_value())
} }
Property::String("context", ..) => { subclass::Property("context", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.context.to_value()) Ok(settings.context.to_value())
} }
Property::UInt("context-wait", ..) => { subclass::Property("context-wait", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.context_wait.to_value()) Ok(settings.context_wait.to_value())
} }
Property::String("proxy-context", ..) => { subclass::Property("proxy-context", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.proxy_context.to_value()) Ok(settings.proxy_context.to_value())
} }
_ => unimplemented!(), _ => unimplemented!(),
} }
} }
fn constructed(&self, obj: &glib::Object) {
self.parent_constructed(obj);
let element = obj.downcast_ref::<gst::Element>().unwrap();
element.add_pad(&self.src_pad).unwrap();
::set_element_flags(element, gst::ElementFlags::SOURCE);
}
} }
impl ElementImpl<Element> for ProxySrc { impl ElementImpl for ProxySrc {
fn change_state( fn change_state(
&self, &self,
element: &Element, element: &gst::Element,
transition: gst::StateChange, transition: gst::StateChange,
) -> gst::StateChangeReturn { ) -> gst::StateChangeReturn {
gst_trace!(self.cat, obj: element, "Changing state {:?}", transition); gst_trace!(self.cat, obj: element, "Changing state {:?}", transition);
@ -1305,7 +1358,7 @@ impl ElementImpl<Element> for ProxySrc {
_ => (), _ => (),
} }
let mut ret = element.parent_change_state(transition); let mut ret = self.parent_change_state(element, transition);
if ret == gst::StateChangeReturn::Failure { if ret == gst::StateChangeReturn::Failure {
return ret; return ret;
} }
@ -1325,42 +1378,7 @@ impl ElementImpl<Element> for ProxySrc {
} }
} }
struct ProxySinkStatic;
impl ImplTypeStatic<Element> for ProxySinkStatic {
fn get_name(&self) -> &str {
"ProxySink"
}
fn new(&self, element: &Element) -> Box<ElementImpl<Element>> {
ProxySink::init(element)
}
fn class_init(&self, klass: &mut ElementClass) {
ProxySink::class_init(klass);
}
}
struct ProxySrcStatic;
impl ImplTypeStatic<Element> for ProxySrcStatic {
fn get_name(&self) -> &str {
"ProxySrc"
}
fn new(&self, element: &Element) -> Box<ElementImpl<Element>> {
ProxySrc::init(element)
}
fn class_init(&self, klass: &mut ElementClass) {
ProxySrc::class_init(klass);
}
}
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> { pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
let type_ = register_type(ProxySinkStatic); gst::Element::register(plugin, "ts-proxysink", 0, ProxySink::get_type())?;
gst::Element::register(plugin, "ts-proxysink", 0, type_)?; gst::Element::register(plugin, "ts-proxysrc", 0, ProxySrc::get_type())
let type_ = register_type(ProxySrcStatic);
gst::Element::register(plugin, "ts-proxysrc", 0, type_)
} }

View file

@ -17,11 +17,11 @@
use glib; use glib;
use glib::prelude::*; use glib::prelude::*;
use glib::subclass;
use glib::subclass::prelude::*;
use gst; use gst;
use gst::prelude::*; use gst::prelude::*;
use gst::subclass::prelude::*;
use gobject_subclass::object::*;
use gst_plugin::element::*;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::sync::Mutex; use std::sync::Mutex;
@ -64,46 +64,60 @@ impl Default for Settings {
} }
} }
static PROPERTIES: [Property; 5] = [ static PROPERTIES: [subclass::Property; 5] = [
Property::UInt( subclass::Property("max-size-buffers", || {
glib::ParamSpec::uint(
"max-size-buffers", "max-size-buffers",
"Max Size Buffers", "Max Size Buffers",
"Maximum number of buffers to queue (0=unlimited)", "Maximum number of buffers to queue (0=unlimited)",
(0, u32::MAX), 0,
u32::MAX,
DEFAULT_MAX_SIZE_BUFFERS, DEFAULT_MAX_SIZE_BUFFERS,
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::UInt( }),
subclass::Property("max-size-bytes", || {
glib::ParamSpec::uint(
"max-size-bytes", "max-size-bytes",
"Max Size Bytes", "Max Size Bytes",
"Maximum number of bytes to queue (0=unlimited)", "Maximum number of bytes to queue (0=unlimited)",
(0, u32::MAX), 0,
u32::MAX,
DEFAULT_MAX_SIZE_BYTES, DEFAULT_MAX_SIZE_BYTES,
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::UInt64( }),
subclass::Property("max-size-time", || {
glib::ParamSpec::uint64(
"max-size-time", "max-size-time",
"Max Size Time", "Max Size Time",
"Maximum number of nanoseconds to queue (0=unlimited)", "Maximum number of nanoseconds to queue (0=unlimited)",
(0, u64::MAX - 1), 0,
u64::MAX - 1,
DEFAULT_MAX_SIZE_TIME, DEFAULT_MAX_SIZE_TIME,
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::String( }),
subclass::Property("context", || {
glib::ParamSpec::string(
"context", "context",
"Context", "Context",
"Context name to share threads with", "Context name to share threads with",
Some(DEFAULT_CONTEXT), Some(DEFAULT_CONTEXT),
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::UInt( }),
subclass::Property("context-wait", || {
glib::ParamSpec::uint(
"context-wait", "context-wait",
"Context Wait", "Context Wait",
"Throttle poll loop to run at most once every this many ms", "Throttle poll loop to run at most once every this many ms",
(0, 1000), 0,
1000,
DEFAULT_CONTEXT_WAIT, DEFAULT_CONTEXT_WAIT,
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
}),
]; ];
struct State { struct State {
@ -141,100 +155,6 @@ struct Queue {
} }
impl Queue { impl Queue {
fn class_init(klass: &mut ElementClass) {
klass.set_metadata(
"Thread-sharing queue",
"Generic",
"Simple data queue",
"Sebastian Dröge <sebastian@centricular.com>",
);
let caps = gst::Caps::new_any();
let sink_pad_template = gst::PadTemplate::new(
"sink",
gst::PadDirection::Sink,
gst::PadPresence::Always,
&caps,
);
klass.add_pad_template(sink_pad_template);
let src_pad_template = gst::PadTemplate::new(
"src",
gst::PadDirection::Src,
gst::PadPresence::Always,
&caps,
);
klass.add_pad_template(src_pad_template);
klass.install_properties(&PROPERTIES);
}
fn init(element: &Element) -> Box<ElementImpl<Element>> {
let templ = element.get_pad_template("sink").unwrap();
let sink_pad = gst::Pad::new_from_template(&templ, "sink");
let templ = element.get_pad_template("src").unwrap();
let src_pad = gst::Pad::new_from_template(&templ, "src");
sink_pad.set_chain_function(|pad, parent, buffer| {
Queue::catch_panic_pad_function(
parent,
|| gst::FlowReturn::Error,
|queue, element| queue.sink_chain(pad, element, buffer),
)
});
sink_pad.set_chain_list_function(|pad, parent, list| {
Queue::catch_panic_pad_function(
parent,
|| gst::FlowReturn::Error,
|queue, element| queue.sink_chain_list(pad, element, list),
)
});
sink_pad.set_event_function(|pad, parent, event| {
Queue::catch_panic_pad_function(
parent,
|| false,
|queue, element| queue.sink_event(pad, element, event),
)
});
sink_pad.set_query_function(|pad, parent, query| {
Queue::catch_panic_pad_function(
parent,
|| false,
|queue, element| queue.sink_query(pad, element, query),
)
});
src_pad.set_event_function(|pad, parent, event| {
Queue::catch_panic_pad_function(
parent,
|| false,
|queue, element| queue.src_event(pad, element, event),
)
});
src_pad.set_query_function(|pad, parent, query| {
Queue::catch_panic_pad_function(
parent,
|| false,
|queue, element| queue.src_query(pad, element, query),
)
});
element.add_pad(&sink_pad).unwrap();
element.add_pad(&src_pad).unwrap();
Box::new(Self {
cat: gst::DebugCategory::new(
"ts-queue",
gst::DebugColorFlags::empty(),
"Thread-sharing queue",
),
sink_pad: sink_pad,
src_pad: src_pad,
state: Mutex::new(State::default()),
settings: Mutex::new(Settings::default()),
})
}
fn create_io_context_event(state: &State) -> Option<gst::Event> { fn create_io_context_event(state: &State) -> Option<gst::Event> {
if let (&Some(ref pending_future_id), &Some(ref io_context)) = if let (&Some(ref pending_future_id), &Some(ref io_context)) =
(&state.pending_future_id, &state.io_context) (&state.pending_future_id, &state.io_context)
@ -255,7 +175,7 @@ impl Queue {
fn enqueue_item( fn enqueue_item(
&self, &self,
_pad: &gst::Pad, _pad: &gst::Pad,
element: &Element, element: &gst::Element,
item: DataQueueItem, item: DataQueueItem,
) -> gst::FlowReturn { ) -> gst::FlowReturn {
let wait_future = { let wait_future = {
@ -325,7 +245,7 @@ impl Queue {
let element_clone = element.clone(); let element_clone = element.clone();
let future = future::poll_fn(move || { let future = future::poll_fn(move || {
let queue = element_clone.get_impl().downcast_ref::<Queue>().unwrap(); let queue = Self::from_instance(&element_clone);
let mut state = queue.state.lock().unwrap(); let mut state = queue.state.lock().unwrap();
let State { let State {
@ -430,7 +350,7 @@ impl Queue {
fn sink_chain( fn sink_chain(
&self, &self,
pad: &gst::Pad, pad: &gst::Pad,
element: &Element, element: &gst::Element,
buffer: gst::Buffer, buffer: gst::Buffer,
) -> gst::FlowReturn { ) -> gst::FlowReturn {
gst_log!(self.cat, obj: pad, "Handling buffer {:?}", buffer); gst_log!(self.cat, obj: pad, "Handling buffer {:?}", buffer);
@ -440,14 +360,14 @@ impl Queue {
fn sink_chain_list( fn sink_chain_list(
&self, &self,
pad: &gst::Pad, pad: &gst::Pad,
element: &Element, element: &gst::Element,
list: gst::BufferList, list: gst::BufferList,
) -> gst::FlowReturn { ) -> gst::FlowReturn {
gst_log!(self.cat, obj: pad, "Handling buffer list {:?}", list); gst_log!(self.cat, obj: pad, "Handling buffer list {:?}", list);
self.enqueue_item(pad, element, DataQueueItem::BufferList(list)) self.enqueue_item(pad, element, DataQueueItem::BufferList(list))
} }
fn sink_event(&self, pad: &gst::Pad, element: &Element, mut event: gst::Event) -> bool { fn sink_event(&self, pad: &gst::Pad, element: &gst::Element, mut event: gst::Event) -> bool {
use gst::EventView; use gst::EventView;
gst_log!(self.cat, obj: pad, "Handling event {:?}", event); gst_log!(self.cat, obj: pad, "Handling event {:?}", event);
@ -505,7 +425,12 @@ impl Queue {
} }
} }
fn sink_query(&self, pad: &gst::Pad, _element: &Element, query: &mut gst::QueryRef) -> bool { fn sink_query(
&self,
pad: &gst::Pad,
_element: &gst::Element,
query: &mut gst::QueryRef,
) -> bool {
gst_log!(self.cat, obj: pad, "Handling query {:?}", query); gst_log!(self.cat, obj: pad, "Handling query {:?}", query);
if query.is_serialized() { if query.is_serialized() {
@ -518,7 +443,7 @@ impl Queue {
} }
} }
fn src_event(&self, pad: &gst::Pad, element: &Element, event: gst::Event) -> bool { fn src_event(&self, pad: &gst::Pad, element: &gst::Element, event: gst::Event) -> bool {
use gst::EventView; use gst::EventView;
gst_log!(self.cat, obj: pad, "Handling event {:?}", event); gst_log!(self.cat, obj: pad, "Handling event {:?}", event);
@ -542,7 +467,12 @@ impl Queue {
self.sink_pad.push_event(event) self.sink_pad.push_event(event)
} }
fn src_query(&self, pad: &gst::Pad, _element: &Element, query: &mut gst::QueryRef) -> bool { fn src_query(
&self,
pad: &gst::Pad,
_element: &gst::Element,
query: &mut gst::QueryRef,
) -> bool {
use gst::QueryView; use gst::QueryView;
gst_log!(self.cat, obj: pad, "Handling query {:?}", query); gst_log!(self.cat, obj: pad, "Handling query {:?}", query);
@ -578,7 +508,7 @@ impl Queue {
fn push_item( fn push_item(
&self, &self,
element: &Element, element: &gst::Element,
item: DataQueueItem, item: DataQueueItem,
) -> future::Either< ) -> future::Either<
Box<Future<Item = (), Error = gst::FlowError> + Send + 'static>, Box<Future<Item = (), Error = gst::FlowError> + Send + 'static>,
@ -679,7 +609,7 @@ impl Queue {
} }
} }
fn prepare(&self, element: &Element) -> Result<(), gst::ErrorMessage> { fn prepare(&self, element: &gst::Element) -> Result<(), gst::ErrorMessage> {
gst_debug!(self.cat, obj: element, "Preparing"); gst_debug!(self.cat, obj: element, "Preparing");
let settings = self.settings.lock().unwrap().clone(); let settings = self.settings.lock().unwrap().clone();
@ -719,11 +649,11 @@ impl Queue {
.schedule( .schedule(
&io_context, &io_context,
move |item| { move |item| {
let queue = element_clone.get_impl().downcast_ref::<Queue>().unwrap(); let queue = Self::from_instance(&element_clone);
queue.push_item(&element_clone, item) queue.push_item(&element_clone, item)
}, },
move |err| { move |err| {
let queue = element_clone2.get_impl().downcast_ref::<Queue>().unwrap(); let queue = Self::from_instance(&element_clone2);
gst_error!(queue.cat, obj: &element_clone2, "Got error {}", err); gst_error!(queue.cat, obj: &element_clone2, "Got error {}", err);
match err { match err {
gst::FlowError::CustomError => (), gst::FlowError::CustomError => (),
@ -762,7 +692,7 @@ impl Queue {
Ok(()) Ok(())
} }
fn unprepare(&self, element: &Element) -> Result<(), ()> { fn unprepare(&self, element: &gst::Element) -> Result<(), ()> {
gst_debug!(self.cat, obj: element, "Unpreparing"); gst_debug!(self.cat, obj: element, "Unpreparing");
// FIXME: The IO Context has to be alive longer than the queue, // FIXME: The IO Context has to be alive longer than the queue,
@ -792,7 +722,7 @@ impl Queue {
Ok(()) Ok(())
} }
fn start(&self, element: &Element) -> Result<(), ()> { fn start(&self, element: &gst::Element) -> Result<(), ()> {
gst_debug!(self.cat, obj: element, "Starting"); gst_debug!(self.cat, obj: element, "Starting");
let mut state = self.state.lock().unwrap(); let mut state = self.state.lock().unwrap();
@ -806,7 +736,7 @@ impl Queue {
Ok(()) Ok(())
} }
fn stop(&self, element: &Element) -> Result<(), ()> { fn stop(&self, element: &gst::Element) -> Result<(), ()> {
gst_debug!(self.cat, obj: element, "Stopping"); gst_debug!(self.cat, obj: element, "Stopping");
let mut state = self.state.lock().unwrap(); let mut state = self.state.lock().unwrap();
@ -826,28 +756,135 @@ impl Queue {
} }
} }
impl ObjectImpl<Element> for Queue { impl ObjectSubclass for Queue {
fn set_property(&self, _obj: &glib::Object, id: u32, value: &glib::Value) { const NAME: &'static str = "RsTsQueue";
let prop = &PROPERTIES[id as usize]; type ParentType = gst::Element;
type Instance = gst::subclass::ElementInstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;
glib_object_subclass!();
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
klass.set_metadata(
"Thread-sharing queue",
"Generic",
"Simple data queue",
"Sebastian Dröge <sebastian@centricular.com>",
);
let caps = gst::Caps::new_any();
let sink_pad_template = gst::PadTemplate::new(
"sink",
gst::PadDirection::Sink,
gst::PadPresence::Always,
&caps,
);
klass.add_pad_template(sink_pad_template);
let src_pad_template = gst::PadTemplate::new(
"src",
gst::PadDirection::Src,
gst::PadPresence::Always,
&caps,
);
klass.add_pad_template(src_pad_template);
klass.install_properties(&PROPERTIES);
}
fn new() -> Self {
unreachable!()
}
fn new_with_class(klass: &subclass::simple::ClassStruct<Self>) -> Self {
let templ = klass.get_pad_template("sink").unwrap();
let sink_pad = gst::Pad::new_from_template(&templ, "sink");
let templ = klass.get_pad_template("src").unwrap();
let src_pad = gst::Pad::new_from_template(&templ, "src");
sink_pad.set_chain_function(|pad, parent, buffer| {
Queue::catch_panic_pad_function(
parent,
|| gst::FlowReturn::Error,
|queue, element| queue.sink_chain(pad, element, buffer),
)
});
sink_pad.set_chain_list_function(|pad, parent, list| {
Queue::catch_panic_pad_function(
parent,
|| gst::FlowReturn::Error,
|queue, element| queue.sink_chain_list(pad, element, list),
)
});
sink_pad.set_event_function(|pad, parent, event| {
Queue::catch_panic_pad_function(
parent,
|| false,
|queue, element| queue.sink_event(pad, element, event),
)
});
sink_pad.set_query_function(|pad, parent, query| {
Queue::catch_panic_pad_function(
parent,
|| false,
|queue, element| queue.sink_query(pad, element, query),
)
});
src_pad.set_event_function(|pad, parent, event| {
Queue::catch_panic_pad_function(
parent,
|| false,
|queue, element| queue.src_event(pad, element, event),
)
});
src_pad.set_query_function(|pad, parent, query| {
Queue::catch_panic_pad_function(
parent,
|| false,
|queue, element| queue.src_query(pad, element, query),
)
});
Self {
cat: gst::DebugCategory::new(
"ts-queue",
gst::DebugColorFlags::empty(),
"Thread-sharing queue",
),
sink_pad: sink_pad,
src_pad: src_pad,
state: Mutex::new(State::default()),
settings: Mutex::new(Settings::default()),
}
}
}
impl ObjectImpl for Queue {
glib_object_impl!();
fn set_property(&self, _obj: &glib::Object, id: usize, value: &glib::Value) {
let prop = &PROPERTIES[id];
match *prop { match *prop {
Property::UInt("max-size-buffers", ..) => { subclass::Property("max-size-buffers", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.max_size_buffers = value.get().unwrap(); settings.max_size_buffers = value.get().unwrap();
} }
Property::UInt("max-size-bytes", ..) => { subclass::Property("max-size-bytes", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.max_size_bytes = value.get().unwrap(); settings.max_size_bytes = value.get().unwrap();
} }
Property::UInt64("max-size-time", ..) => { subclass::Property("max-size-time", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.max_size_time = value.get().unwrap(); settings.max_size_time = value.get().unwrap();
} }
Property::String("context", ..) => { subclass::Property("context", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.context = value.get().unwrap_or_else(|| "".into()); settings.context = value.get().unwrap_or_else(|| "".into());
} }
Property::UInt("context-wait", ..) => { subclass::Property("context-wait", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.context_wait = value.get().unwrap(); settings.context_wait = value.get().unwrap();
} }
@ -855,39 +892,47 @@ impl ObjectImpl<Element> for Queue {
} }
} }
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("max-size-buffers", ..) => { subclass::Property("max-size-buffers", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.max_size_buffers.to_value()) Ok(settings.max_size_buffers.to_value())
} }
Property::UInt("max-size-bytes", ..) => { subclass::Property("max-size-bytes", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.max_size_bytes.to_value()) Ok(settings.max_size_bytes.to_value())
} }
Property::UInt64("max-size-time", ..) => { subclass::Property("max-size-time", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.max_size_time.to_value()) Ok(settings.max_size_time.to_value())
} }
Property::String("context", ..) => { subclass::Property("context", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.context.to_value()) Ok(settings.context.to_value())
} }
Property::UInt("context-wait", ..) => { subclass::Property("context-wait", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.context_wait.to_value()) Ok(settings.context_wait.to_value())
} }
_ => unimplemented!(), _ => unimplemented!(),
} }
} }
fn constructed(&self, obj: &glib::Object) {
self.parent_constructed(obj);
let element = obj.downcast_ref::<gst::Element>().unwrap();
element.add_pad(&self.sink_pad).unwrap();
element.add_pad(&self.src_pad).unwrap();
}
} }
impl ElementImpl<Element> for Queue { impl ElementImpl for Queue {
fn change_state( fn change_state(
&self, &self,
element: &Element, element: &gst::Element,
transition: gst::StateChange, transition: gst::StateChange,
) -> gst::StateChangeReturn { ) -> gst::StateChangeReturn {
gst_trace!(self.cat, obj: element, "Changing state {:?}", transition); gst_trace!(self.cat, obj: element, "Changing state {:?}", transition);
@ -911,7 +956,7 @@ impl ElementImpl<Element> for Queue {
_ => (), _ => (),
} }
let ret = element.parent_change_state(transition); let ret = self.parent_change_state(element, transition);
if ret == gst::StateChangeReturn::Failure { if ret == gst::StateChangeReturn::Failure {
return ret; return ret;
} }
@ -928,24 +973,6 @@ impl ElementImpl<Element> for Queue {
} }
} }
struct QueueStatic;
impl ImplTypeStatic<Element> for QueueStatic {
fn get_name(&self) -> &str {
"Queue"
}
fn new(&self, element: &Element) -> Box<ElementImpl<Element>> {
Queue::init(element)
}
fn class_init(&self, klass: &mut ElementClass) {
Queue::class_init(klass);
}
}
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> { pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
let queue_static = QueueStatic; gst::Element::register(plugin, "ts-queue", 0, Queue::get_type())
let type_ = register_type(queue_static);
gst::Element::register(plugin, "ts-queue", 0, type_)
} }

View file

@ -18,11 +18,11 @@
use glib; use glib;
use glib::prelude::*; use glib::prelude::*;
use glib::subclass;
use glib::subclass::prelude::*;
use gst; use gst;
use gst::prelude::*; use gst::prelude::*;
use gst::subclass::prelude::*;
use gobject_subclass::object::*;
use gst_plugin::element::*;
use std::io; use std::io;
use std::sync::Mutex; use std::sync::Mutex;
@ -71,52 +71,67 @@ impl Default for Settings {
} }
} }
static PROPERTIES: [Property; 6] = [ static PROPERTIES: [subclass::Property; 6] = [
Property::String( subclass::Property("address", || {
glib::ParamSpec::string(
"address", "address",
"Address", "Address",
"Address to receive packets from", "Address to receive packets from",
DEFAULT_ADDRESS, DEFAULT_ADDRESS,
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::UInt( }),
subclass::Property("port", || {
glib::ParamSpec::uint(
"port", "port",
"Port", "Port",
"Port to receive packets from", "Port to receive packets from",
(0, u16::MAX as u32), 0,
u16::MAX as u32,
DEFAULT_PORT, DEFAULT_PORT,
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::Boxed( }),
subclass::Property("caps", || {
glib::ParamSpec::boxed(
"caps", "caps",
"Caps", "Caps",
"Caps to use", "Caps to use",
gst::Caps::static_type, gst::Caps::static_type(),
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::UInt( }),
subclass::Property("chunk-size", || {
glib::ParamSpec::uint(
"chunk-size", "chunk-size",
"Chunk Size", "Chunk Size",
"Chunk Size", "Chunk Size",
(0, u16::MAX as u32), 0,
u16::MAX as u32,
DEFAULT_CHUNK_SIZE, DEFAULT_CHUNK_SIZE,
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::String( }),
subclass::Property("context", || {
glib::ParamSpec::string(
"context", "context",
"Context", "Context",
"Context name to share threads with", "Context name to share threads with",
Some(DEFAULT_CONTEXT), Some(DEFAULT_CONTEXT),
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::UInt( }),
subclass::Property("context-wait", || {
glib::ParamSpec::uint(
"context-wait", "context-wait",
"Context Wait", "Context Wait",
"Throttle poll loop to run at most once every this many ms", "Throttle poll loop to run at most once every this many ms",
(0, 1000), 0,
1000,
DEFAULT_CONTEXT_WAIT, DEFAULT_CONTEXT_WAIT,
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
}),
]; ];
pub struct TcpClientReader { pub struct TcpClientReader {
@ -185,61 +200,7 @@ struct TcpClientSrc {
} }
impl TcpClientSrc { impl TcpClientSrc {
fn class_init(klass: &mut ElementClass) { fn src_event(&self, pad: &gst::Pad, element: &gst::Element, event: gst::Event) -> bool {
klass.set_metadata(
"Thread-sharing TCP client source",
"Source/Network",
"Receives data over the network via TCP",
"Sebastian Dröge <sebastian@centricular.com>, LEE Dongjun <redongjun@gmail.com>",
);
let caps = gst::Caps::new_any();
let src_pad_template = gst::PadTemplate::new(
"src",
gst::PadDirection::Src,
gst::PadPresence::Always,
&caps,
);
klass.add_pad_template(src_pad_template);
klass.install_properties(&PROPERTIES);
}
fn init(element: &Element) -> Box<ElementImpl<Element>> {
let templ = element.get_pad_template("src").unwrap();
let src_pad = gst::Pad::new_from_template(&templ, "src");
src_pad.set_event_function(|pad, parent, event| {
TcpClientSrc::catch_panic_pad_function(
parent,
|| false,
|tcpclientsrc, element| tcpclientsrc.src_event(pad, element, event),
)
});
src_pad.set_query_function(|pad, parent, query| {
TcpClientSrc::catch_panic_pad_function(
parent,
|| false,
|tcpclientsrc, element| tcpclientsrc.src_query(pad, element, query),
)
});
element.add_pad(&src_pad).unwrap();
::set_element_flags(element, gst::ElementFlags::SOURCE);
Box::new(Self {
cat: gst::DebugCategory::new(
"ts-tcpclientsrc",
gst::DebugColorFlags::empty(),
"Thread-sharing TCP Client source",
),
src_pad: src_pad,
state: Mutex::new(State::default()),
settings: Mutex::new(Settings::default()),
})
}
fn src_event(&self, pad: &gst::Pad, element: &Element, event: gst::Event) -> bool {
use gst::EventView; use gst::EventView;
gst_log!(self.cat, obj: pad, "Handling event {:?}", event); gst_log!(self.cat, obj: pad, "Handling event {:?}", event);
@ -271,7 +232,12 @@ impl TcpClientSrc {
ret ret
} }
fn src_query(&self, pad: &gst::Pad, _element: &Element, query: &mut gst::QueryRef) -> bool { fn src_query(
&self,
pad: &gst::Pad,
_element: &gst::Element,
query: &mut gst::QueryRef,
) -> bool {
use gst::QueryView; use gst::QueryView;
gst_log!(self.cat, obj: pad, "Handling query {:?}", query); gst_log!(self.cat, obj: pad, "Handling query {:?}", query);
@ -331,7 +297,7 @@ impl TcpClientSrc {
fn push_buffer( fn push_buffer(
&self, &self,
element: &Element, element: &gst::Element,
buffer: gst::Buffer, buffer: gst::Buffer,
) -> future::Either< ) -> future::Either<
Box<Future<Item = (), Error = gst::FlowError> + Send + 'static>, Box<Future<Item = (), Error = gst::FlowError> + Send + 'static>,
@ -431,7 +397,7 @@ impl TcpClientSrc {
} }
} }
fn prepare(&self, element: &Element) -> Result<(), gst::ErrorMessage> { fn prepare(&self, element: &gst::Element) -> Result<(), gst::ErrorMessage> {
use std::net::{IpAddr, SocketAddr}; use std::net::{IpAddr, SocketAddr};
gst_debug!(self.cat, obj: element, "Preparing"); gst_debug!(self.cat, obj: element, "Preparing");
@ -493,17 +459,11 @@ impl TcpClientSrc {
.schedule( .schedule(
&io_context, &io_context,
move |buffer| { move |buffer| {
let tcpclientsrc = element_clone let tcpclientsrc = Self::from_instance(&element_clone);
.get_impl()
.downcast_ref::<TcpClientSrc>()
.unwrap();
tcpclientsrc.push_buffer(&element_clone, buffer) tcpclientsrc.push_buffer(&element_clone, buffer)
}, },
move |err| { move |err| {
let tcpclientsrc = element_clone2 let tcpclientsrc = Self::from_instance(&element_clone2);
.get_impl()
.downcast_ref::<TcpClientSrc>()
.unwrap();
gst_error!(tcpclientsrc.cat, obj: &element_clone2, "Got error {}", err); gst_error!(tcpclientsrc.cat, obj: &element_clone2, "Got error {}", err);
match err { match err {
Either::Left(gst::FlowError::CustomError) => (), Either::Left(gst::FlowError::CustomError) => (),
@ -547,7 +507,7 @@ impl TcpClientSrc {
Ok(()) Ok(())
} }
fn unprepare(&self, element: &Element) -> Result<(), ()> { fn unprepare(&self, element: &gst::Element) -> Result<(), ()> {
gst_debug!(self.cat, obj: element, "Unpreparing"); gst_debug!(self.cat, obj: element, "Unpreparing");
// FIXME: The IO Context has to be alive longer than the queue, // FIXME: The IO Context has to be alive longer than the queue,
@ -576,7 +536,7 @@ impl TcpClientSrc {
Ok(()) Ok(())
} }
fn start(&self, element: &Element) -> Result<(), ()> { fn start(&self, element: &gst::Element) -> Result<(), ()> {
gst_debug!(self.cat, obj: element, "Starting"); gst_debug!(self.cat, obj: element, "Starting");
let state = self.state.lock().unwrap(); let state = self.state.lock().unwrap();
@ -589,7 +549,7 @@ impl TcpClientSrc {
Ok(()) Ok(())
} }
fn stop(&self, element: &Element) -> Result<(), ()> { fn stop(&self, element: &gst::Element) -> Result<(), ()> {
gst_debug!(self.cat, obj: element, "Stopping"); gst_debug!(self.cat, obj: element, "Stopping");
let mut state = self.state.lock().unwrap(); let mut state = self.state.lock().unwrap();
@ -604,32 +564,98 @@ impl TcpClientSrc {
} }
} }
impl ObjectImpl<Element> for TcpClientSrc { impl ObjectSubclass for TcpClientSrc {
fn set_property(&self, _obj: &glib::Object, id: u32, value: &glib::Value) { const NAME: &'static str = "RsTsTcpClientSrc";
let prop = &PROPERTIES[id as usize]; type ParentType = gst::Element;
type Instance = gst::subclass::ElementInstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;
glib_object_subclass!();
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
klass.set_metadata(
"Thread-sharing TCP client source",
"Source/Network",
"Receives data over the network via TCP",
"Sebastian Dröge <sebastian@centricular.com>, LEE Dongjun <redongjun@gmail.com>",
);
let caps = gst::Caps::new_any();
let src_pad_template = gst::PadTemplate::new(
"src",
gst::PadDirection::Src,
gst::PadPresence::Always,
&caps,
);
klass.add_pad_template(src_pad_template);
klass.install_properties(&PROPERTIES);
}
fn new() -> Self {
unreachable!()
}
fn new_with_class(klass: &subclass::simple::ClassStruct<Self>) -> Self {
let templ = klass.get_pad_template("src").unwrap();
let src_pad = gst::Pad::new_from_template(&templ, "src");
src_pad.set_event_function(|pad, parent, event| {
TcpClientSrc::catch_panic_pad_function(
parent,
|| false,
|tcpclientsrc, element| tcpclientsrc.src_event(pad, element, event),
)
});
src_pad.set_query_function(|pad, parent, query| {
TcpClientSrc::catch_panic_pad_function(
parent,
|| false,
|tcpclientsrc, element| tcpclientsrc.src_query(pad, element, query),
)
});
Self {
cat: gst::DebugCategory::new(
"ts-tcpclientsrc",
gst::DebugColorFlags::empty(),
"Thread-sharing TCP Client source",
),
src_pad: src_pad,
state: Mutex::new(State::default()),
settings: Mutex::new(Settings::default()),
}
}
}
impl ObjectImpl for TcpClientSrc {
glib_object_impl!();
fn set_property(&self, _obj: &glib::Object, id: usize, value: &glib::Value) {
let prop = &PROPERTIES[id];
match *prop { match *prop {
Property::String("address", ..) => { subclass::Property("address", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.address = value.get(); settings.address = value.get();
} }
Property::UInt("port", ..) => { subclass::Property("port", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.port = value.get().unwrap(); settings.port = value.get().unwrap();
} }
Property::Boxed("caps", ..) => { subclass::Property("caps", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.caps = value.get(); settings.caps = value.get();
} }
Property::UInt("chunk-size", ..) => { subclass::Property("chunk-size", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.chunk_size = value.get().unwrap(); settings.chunk_size = value.get().unwrap();
} }
Property::String("context", ..) => { subclass::Property("context", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.context = value.get().unwrap_or_else(|| "".into()); settings.context = value.get().unwrap_or_else(|| "".into());
} }
Property::UInt("context-wait", ..) => { subclass::Property("context-wait", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.context_wait = value.get().unwrap(); settings.context_wait = value.get().unwrap();
} }
@ -637,43 +663,52 @@ impl ObjectImpl<Element> for TcpClientSrc {
} }
} }
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::String("address", ..) => { subclass::Property("address", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.address.to_value()) Ok(settings.address.to_value())
} }
Property::UInt("port", ..) => { subclass::Property("port", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.port.to_value()) Ok(settings.port.to_value())
} }
Property::Boxed("caps", ..) => { subclass::Property("caps", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.caps.to_value()) Ok(settings.caps.to_value())
} }
Property::UInt("chunk-size", ..) => { subclass::Property("chunk-size", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.chunk_size.to_value()) Ok(settings.chunk_size.to_value())
} }
Property::String("context", ..) => { subclass::Property("context", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.context.to_value()) Ok(settings.context.to_value())
} }
Property::UInt("context-wait", ..) => { subclass::Property("context-wait", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.context_wait.to_value()) Ok(settings.context_wait.to_value())
} }
_ => unimplemented!(), _ => unimplemented!(),
} }
} }
fn constructed(&self, obj: &glib::Object) {
self.parent_constructed(obj);
let element = obj.downcast_ref::<gst::Element>().unwrap();
element.add_pad(&self.src_pad).unwrap();
::set_element_flags(element, gst::ElementFlags::SOURCE);
}
} }
impl ElementImpl<Element> for TcpClientSrc { impl ElementImpl for TcpClientSrc {
fn change_state( fn change_state(
&self, &self,
element: &Element, element: &gst::Element,
transition: gst::StateChange, transition: gst::StateChange,
) -> gst::StateChangeReturn { ) -> gst::StateChangeReturn {
gst_trace!(self.cat, obj: element, "Changing state {:?}", transition); gst_trace!(self.cat, obj: element, "Changing state {:?}", transition);
@ -699,7 +734,7 @@ impl ElementImpl<Element> for TcpClientSrc {
_ => (), _ => (),
} }
let mut ret = element.parent_change_state(transition); let mut ret = self.parent_change_state(element, transition);
if ret == gst::StateChangeReturn::Failure { if ret == gst::StateChangeReturn::Failure {
return ret; return ret;
} }
@ -719,24 +754,6 @@ impl ElementImpl<Element> for TcpClientSrc {
} }
} }
struct TcpClientSrcStatic;
impl ImplTypeStatic<Element> for TcpClientSrcStatic {
fn get_name(&self) -> &str {
"TcpClientSrc"
}
fn new(&self, element: &Element) -> Box<ElementImpl<Element>> {
TcpClientSrc::init(element)
}
fn class_init(&self, klass: &mut ElementClass) {
TcpClientSrc::class_init(klass);
}
}
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> { pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
let tcpclientsrc_static = TcpClientSrcStatic; gst::Element::register(plugin, "ts-tcpclientsrc", 0, TcpClientSrc::get_type())
let type_ = register_type(tcpclientsrc_static);
gst::Element::register(plugin, "ts-tcpclientsrc", 0, type_)
} }

View file

@ -17,17 +17,17 @@
use glib; use glib;
use glib::prelude::*; use glib::prelude::*;
use glib::subclass;
use glib::subclass::prelude::*;
use gst; use gst;
use gst::prelude::*; use gst::prelude::*;
use gst::subclass::prelude::*;
use gio; use gio;
use gio_ffi; use gio_ffi;
use gobject_ffi; use gobject_ffi;
use gobject_subclass::object::*;
use gst_plugin::element::*;
use std::io; use std::io;
use std::sync::Mutex; use std::sync::Mutex;
use std::u16; use std::u16;
@ -179,73 +179,94 @@ impl Default for Settings {
} }
} }
static PROPERTIES: [Property; 9] = [ static PROPERTIES: [subclass::Property; 9] = [
Property::String( subclass::Property("address", || {
glib::ParamSpec::string(
"address", "address",
"Address", "Address",
"Address/multicast group to listen on", "Address/multicast group to listen on",
DEFAULT_ADDRESS, DEFAULT_ADDRESS,
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::UInt( }),
subclass::Property("port", || {
glib::ParamSpec::uint(
"port", "port",
"Port", "Port",
"Port to listen on", "Port to listen on",
(0, u16::MAX as u32), 0,
u16::MAX as u32,
DEFAULT_PORT, DEFAULT_PORT,
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::Boolean( }),
subclass::Property("reuse", || {
glib::ParamSpec::boolean(
"reuse", "reuse",
"Reuse", "Reuse",
"Allow reuse of the port", "Allow reuse of the port",
DEFAULT_REUSE, DEFAULT_REUSE,
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::Boxed( }),
subclass::Property("caps", || {
glib::ParamSpec::boxed(
"caps", "caps",
"Caps", "Caps",
"Caps to use", "Caps to use",
gst::Caps::static_type, gst::Caps::static_type(),
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::UInt( }),
subclass::Property("mtu", || {
glib::ParamSpec::uint(
"mtu", "mtu",
"MTU", "MTU",
"MTU", "MTU",
(0, u16::MAX as u32), 0,
u16::MAX as u32,
DEFAULT_MTU, DEFAULT_MTU,
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::Object( }),
subclass::Property("socket", || {
glib::ParamSpec::object(
"socket", "socket",
"Socket", "Socket",
"Socket to use for UDP reception. (None == allocate)", "Socket to use for UDP reception. (None == allocate)",
gio::Socket::static_type, gio::Socket::static_type(),
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::Object( }),
subclass::Property("used-socket", || {
glib::ParamSpec::object(
"used-socket", "used-socket",
"Used Socket", "Used Socket",
"Socket currently in use for UDP reception. (None = no socket)", "Socket currently in use for UDP reception. (None = no socket)",
gio::Socket::static_type, gio::Socket::static_type(),
PropertyMutability::Readable, glib::ParamFlags::READABLE,
), )
Property::String( }),
subclass::Property("context", || {
glib::ParamSpec::string(
"context", "context",
"Context", "Context",
"Context name to share threads with", "Context name to share threads with",
Some(DEFAULT_CONTEXT), Some(DEFAULT_CONTEXT),
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
Property::UInt( }),
subclass::Property("context-wait", || {
glib::ParamSpec::uint(
"context-wait", "context-wait",
"Context Wait", "Context Wait",
"Throttle poll loop to run at most once every this many ms", "Throttle poll loop to run at most once every this many ms",
(0, 1000), 0,
1000,
DEFAULT_CONTEXT_WAIT, DEFAULT_CONTEXT_WAIT,
PropertyMutability::ReadWrite, glib::ParamFlags::READWRITE,
), )
}),
]; ];
pub struct UdpReader { pub struct UdpReader {
@ -296,61 +317,7 @@ struct UdpSrc {
} }
impl UdpSrc { impl UdpSrc {
fn class_init(klass: &mut ElementClass) { fn src_event(&self, pad: &gst::Pad, element: &gst::Element, event: gst::Event) -> bool {
klass.set_metadata(
"Thread-sharing UDP source",
"Source/Network",
"Receives data over the network via UDP",
"Sebastian Dröge <sebastian@centricular.com>",
);
let caps = gst::Caps::new_any();
let src_pad_template = gst::PadTemplate::new(
"src",
gst::PadDirection::Src,
gst::PadPresence::Always,
&caps,
);
klass.add_pad_template(src_pad_template);
klass.install_properties(&PROPERTIES);
}
fn init(element: &Element) -> Box<ElementImpl<Element>> {
let templ = element.get_pad_template("src").unwrap();
let src_pad = gst::Pad::new_from_template(&templ, "src");
src_pad.set_event_function(|pad, parent, event| {
UdpSrc::catch_panic_pad_function(
parent,
|| false,
|udpsrc, element| udpsrc.src_event(pad, element, event),
)
});
src_pad.set_query_function(|pad, parent, query| {
UdpSrc::catch_panic_pad_function(
parent,
|| false,
|udpsrc, element| udpsrc.src_query(pad, element, query),
)
});
element.add_pad(&src_pad).unwrap();
::set_element_flags(element, gst::ElementFlags::SOURCE);
Box::new(Self {
cat: gst::DebugCategory::new(
"ts-udpsrc",
gst::DebugColorFlags::empty(),
"Thread-sharing UDP source",
),
src_pad: src_pad,
state: Mutex::new(State::default()),
settings: Mutex::new(Settings::default()),
})
}
fn src_event(&self, pad: &gst::Pad, element: &Element, event: gst::Event) -> bool {
use gst::EventView; use gst::EventView;
gst_log!(self.cat, obj: pad, "Handling event {:?}", event); gst_log!(self.cat, obj: pad, "Handling event {:?}", event);
@ -382,7 +349,12 @@ impl UdpSrc {
ret ret
} }
fn src_query(&self, pad: &gst::Pad, _element: &Element, query: &mut gst::QueryRef) -> bool { fn src_query(
&self,
pad: &gst::Pad,
_element: &gst::Element,
query: &mut gst::QueryRef,
) -> bool {
use gst::QueryView; use gst::QueryView;
gst_log!(self.cat, obj: pad, "Handling query {:?}", query); gst_log!(self.cat, obj: pad, "Handling query {:?}", query);
@ -442,7 +414,7 @@ impl UdpSrc {
fn push_buffer( fn push_buffer(
&self, &self,
element: &Element, element: &gst::Element,
buffer: gst::Buffer, buffer: gst::Buffer,
) -> future::Either< ) -> future::Either<
Box<Future<Item = (), Error = gst::FlowError> + Send + 'static>, Box<Future<Item = (), Error = gst::FlowError> + Send + 'static>,
@ -537,7 +509,7 @@ impl UdpSrc {
} }
} }
fn prepare(&self, element: &Element) -> Result<(), gst::ErrorMessage> { fn prepare(&self, element: &gst::Element) -> Result<(), gst::ErrorMessage> {
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
gst_debug!(self.cat, obj: element, "Preparing"); gst_debug!(self.cat, obj: element, "Preparing");
@ -768,11 +740,11 @@ impl UdpSrc {
.schedule( .schedule(
&io_context, &io_context,
move |buffer| { move |buffer| {
let udpsrc = element_clone.get_impl().downcast_ref::<UdpSrc>().unwrap(); let udpsrc = Self::from_instance(&element_clone);
udpsrc.push_buffer(&element_clone, buffer) udpsrc.push_buffer(&element_clone, buffer)
}, },
move |err| { move |err| {
let udpsrc = element_clone2.get_impl().downcast_ref::<UdpSrc>().unwrap(); let udpsrc = Self::from_instance(&element_clone2);
gst_error!(udpsrc.cat, obj: &element_clone2, "Got error {}", err); gst_error!(udpsrc.cat, obj: &element_clone2, "Got error {}", err);
match err { match err {
Either::Left(gst::FlowError::CustomError) => (), Either::Left(gst::FlowError::CustomError) => (),
@ -819,7 +791,7 @@ impl UdpSrc {
Ok(()) Ok(())
} }
fn unprepare(&self, element: &Element) -> Result<(), ()> { fn unprepare(&self, element: &gst::Element) -> Result<(), ()> {
gst_debug!(self.cat, obj: element, "Unpreparing"); gst_debug!(self.cat, obj: element, "Unpreparing");
self.settings.lock().unwrap().used_socket = None; self.settings.lock().unwrap().used_socket = None;
@ -850,7 +822,7 @@ impl UdpSrc {
Ok(()) Ok(())
} }
fn start(&self, element: &Element) -> Result<(), ()> { fn start(&self, element: &gst::Element) -> Result<(), ()> {
gst_debug!(self.cat, obj: element, "Starting"); gst_debug!(self.cat, obj: element, "Starting");
let state = self.state.lock().unwrap(); let state = self.state.lock().unwrap();
@ -863,7 +835,7 @@ impl UdpSrc {
Ok(()) Ok(())
} }
fn stop(&self, element: &Element) -> Result<(), ()> { fn stop(&self, element: &gst::Element) -> Result<(), ()> {
gst_debug!(self.cat, obj: element, "Stopping"); gst_debug!(self.cat, obj: element, "Stopping");
let mut state = self.state.lock().unwrap(); let mut state = self.state.lock().unwrap();
@ -878,45 +850,111 @@ impl UdpSrc {
} }
} }
impl ObjectImpl<Element> for UdpSrc { impl ObjectSubclass for UdpSrc {
fn set_property(&self, _obj: &glib::Object, id: u32, value: &glib::Value) { const NAME: &'static str = "RsTsUdpSrc";
let prop = &PROPERTIES[id as usize]; type ParentType = gst::Element;
type Instance = gst::subclass::ElementInstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;
glib_object_subclass!();
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
klass.set_metadata(
"Thread-sharing UDP source",
"Source/Network",
"Receives data over the network via UDP",
"Sebastian Dröge <sebastian@centricular.com>",
);
let caps = gst::Caps::new_any();
let src_pad_template = gst::PadTemplate::new(
"src",
gst::PadDirection::Src,
gst::PadPresence::Always,
&caps,
);
klass.add_pad_template(src_pad_template);
klass.install_properties(&PROPERTIES);
}
fn new() -> Self {
unreachable!()
}
fn new_with_class(klass: &subclass::simple::ClassStruct<Self>) -> Self {
let templ = klass.get_pad_template("src").unwrap();
let src_pad = gst::Pad::new_from_template(&templ, "src");
src_pad.set_event_function(|pad, parent, event| {
UdpSrc::catch_panic_pad_function(
parent,
|| false,
|udpsrc, element| udpsrc.src_event(pad, element, event),
)
});
src_pad.set_query_function(|pad, parent, query| {
UdpSrc::catch_panic_pad_function(
parent,
|| false,
|udpsrc, element| udpsrc.src_query(pad, element, query),
)
});
Self {
cat: gst::DebugCategory::new(
"ts-udpsrc",
gst::DebugColorFlags::empty(),
"Thread-sharing UDP source",
),
src_pad: src_pad,
state: Mutex::new(State::default()),
settings: Mutex::new(Settings::default()),
}
}
}
impl ObjectImpl for UdpSrc {
glib_object_impl!();
fn set_property(&self, _obj: &glib::Object, id: usize, value: &glib::Value) {
let prop = &PROPERTIES[id];
match *prop { match *prop {
Property::String("address", ..) => { subclass::Property("address", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.address = value.get(); settings.address = value.get();
} }
Property::UInt("port", ..) => { subclass::Property("port", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.port = value.get().unwrap(); settings.port = value.get().unwrap();
} }
Property::Boolean("reuse", ..) => { subclass::Property("reuse", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.reuse = value.get().unwrap(); settings.reuse = value.get().unwrap();
} }
Property::Boxed("caps", ..) => { subclass::Property("caps", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.caps = value.get(); settings.caps = value.get();
} }
Property::UInt("mtu", ..) => { subclass::Property("mtu", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.mtu = value.get().unwrap(); settings.mtu = value.get().unwrap();
} }
Property::Object("socket", ..) => { subclass::Property("socket", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.socket = value settings.socket = value
.get::<gio::Socket>() .get::<gio::Socket>()
.map(|socket| GioSocketWrapper::new(&socket)); .map(|socket| GioSocketWrapper::new(&socket));
} }
Property::Object("used-socket", ..) => { subclass::Property("used-socket", ..) => {
unreachable!(); unreachable!();
} }
Property::String("context", ..) => { subclass::Property("context", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.context = value.get().unwrap_or_else(|| "".into()); settings.context = value.get().unwrap_or_else(|| "".into());
} }
Property::UInt("context-wait", ..) => { subclass::Property("context-wait", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
settings.context_wait = value.get().unwrap(); settings.context_wait = value.get().unwrap();
} }
@ -924,31 +962,31 @@ impl ObjectImpl<Element> for UdpSrc {
} }
} }
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::String("address", ..) => { subclass::Property("address", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.address.to_value()) Ok(settings.address.to_value())
} }
Property::UInt("port", ..) => { subclass::Property("port", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.port.to_value()) Ok(settings.port.to_value())
} }
Property::Boolean("reuse", ..) => { subclass::Property("reuse", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.reuse.to_value()) Ok(settings.reuse.to_value())
} }
Property::Boxed("caps", ..) => { subclass::Property("caps", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.caps.to_value()) Ok(settings.caps.to_value())
} }
Property::UInt("mtu", ..) => { subclass::Property("mtu", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.mtu.to_value()) Ok(settings.mtu.to_value())
} }
Property::Object("socket", ..) => { subclass::Property("socket", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings Ok(settings
.socket .socket
@ -956,7 +994,7 @@ impl ObjectImpl<Element> for UdpSrc {
.map(GioSocketWrapper::as_socket) .map(GioSocketWrapper::as_socket)
.to_value()) .to_value())
} }
Property::Object("used-socket", ..) => { subclass::Property("used-socket", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings Ok(settings
.used_socket .used_socket
@ -964,23 +1002,31 @@ impl ObjectImpl<Element> for UdpSrc {
.map(GioSocketWrapper::as_socket) .map(GioSocketWrapper::as_socket)
.to_value()) .to_value())
} }
Property::String("context", ..) => { subclass::Property("context", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.context.to_value()) Ok(settings.context.to_value())
} }
Property::UInt("context-wait", ..) => { subclass::Property("context-wait", ..) => {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
Ok(settings.context_wait.to_value()) Ok(settings.context_wait.to_value())
} }
_ => unimplemented!(), _ => unimplemented!(),
} }
} }
fn constructed(&self, obj: &glib::Object) {
self.parent_constructed(obj);
let element = obj.downcast_ref::<gst::Element>().unwrap();
element.add_pad(&self.src_pad).unwrap();
::set_element_flags(element, gst::ElementFlags::SOURCE);
}
} }
impl ElementImpl<Element> for UdpSrc { impl ElementImpl for UdpSrc {
fn change_state( fn change_state(
&self, &self,
element: &Element, element: &gst::Element,
transition: gst::StateChange, transition: gst::StateChange,
) -> gst::StateChangeReturn { ) -> gst::StateChangeReturn {
gst_trace!(self.cat, obj: element, "Changing state {:?}", transition); gst_trace!(self.cat, obj: element, "Changing state {:?}", transition);
@ -1004,7 +1050,7 @@ impl ElementImpl<Element> for UdpSrc {
_ => (), _ => (),
} }
let mut ret = element.parent_change_state(transition); let mut ret = self.parent_change_state(element, transition);
if ret == gst::StateChangeReturn::Failure { if ret == gst::StateChangeReturn::Failure {
return ret; return ret;
} }
@ -1028,24 +1074,6 @@ impl ElementImpl<Element> for UdpSrc {
} }
} }
struct UdpSrcStatic;
impl ImplTypeStatic<Element> for UdpSrcStatic {
fn get_name(&self) -> &str {
"UdpSrc"
}
fn new(&self, element: &Element) -> Box<ElementImpl<Element>> {
UdpSrc::init(element)
}
fn class_init(&self, klass: &mut ElementClass) {
UdpSrc::class_init(klass);
}
}
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> { pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
let udpsrc_static = UdpSrcStatic; gst::Element::register(plugin, "ts-udpsrc", 0, UdpSrc::get_type())
let type_ = register_type(udpsrc_static);
gst::Element::register(plugin, "ts-udpsrc", 0, type_)
} }