From aeefcefe09c1a602a54c6a859abfbed26735a4b5 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Sat, 14 May 2016 14:31:39 +0100 Subject: [PATCH] Add skeleton for RsFileSink with the new structure Doesn't write to a file yet, just prints out a message when a buffer is received. All the skeleton is there to implement Sink children. --- build.rs | 2 +- src/lib.rs | 32 ++++++ src/plugin.c | 6 +- src/rsfilesink.c | 159 ---------------------------- src/rsfilesink.h | 38 ------- src/rsfilesink.rs | 97 ++++++++++++++++- src/rssink.c | 264 ++++++++++++++++++++++++++++++++++++++++++++++ src/rssink.h | 31 ++++++ src/rssink.rs | 63 +++++++++++ 9 files changed, 487 insertions(+), 205 deletions(-) delete mode 100644 src/rsfilesink.c delete mode 100644 src/rsfilesink.h create mode 100644 src/rssink.c create mode 100644 src/rssink.h create mode 100644 src/rssink.rs diff --git a/build.rs b/build.rs index 0d24d045..16bb0f5a 100644 --- a/build.rs +++ b/build.rs @@ -6,7 +6,7 @@ fn main() { let gstbase = pkg_config::probe_library("gstreamer-base-1.0").unwrap(); let includes = [gstreamer.include_paths, gstbase.include_paths]; - let files = ["src/plugin.c", "src/rssource.c", "src/rsfilesink.c"]; + let files = ["src/plugin.c", "src/rssource.c", "src/rssink.c"]; let mut config = gcc::Config::new(); config.include("src"); diff --git a/src/lib.rs b/src/lib.rs index e48bdca8..f4e59908 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ extern crate hyper; #[macro_use] pub mod utils; pub mod rssource; +pub mod rssink; pub mod rsfilesrc; pub mod rshttpsrc; pub mod rsfilesink; @@ -15,6 +16,8 @@ use utils::*; use rssource::Source; use rsfilesrc::FileSrc; use rshttpsrc::HttpSrc; +use rssink::Sink; +use rsfilesink::FileSink; use std::os::raw::c_void; use libc::{c_char}; @@ -33,6 +36,18 @@ extern "C" { push_only: GBoolean) -> GBoolean; } +extern "C" { + fn gst_rs_sink_register(plugin: *const c_void, + name: *const c_char, + long_name: *const c_char, + description: *const c_char, + classification: *const c_char, + author: *const c_char, + rank: i32, + create_instance: extern fn() -> *mut Box, + protocols: *const c_char) -> GBoolean; +} + #[no_mangle] pub extern "C" fn sources_register(plugin: *const c_void) -> GBoolean { @@ -62,3 +77,20 @@ pub extern "C" fn sources_register(plugin: *const c_void) -> GBoolean { return GBoolean::True; } + +#[no_mangle] +pub extern "C" fn sinks_register(plugin: *const c_void) -> GBoolean { + + unsafe { + gst_rs_sink_register(plugin, + CString::new("rsfilesink").unwrap().as_ptr(), + CString::new("File Sink").unwrap().as_ptr(), + CString::new("Writes to local files").unwrap().as_ptr(), + CString::new("Sink/File").unwrap().as_ptr(), + CString::new("Luis de Bethencourt ").unwrap().as_ptr(), + 256 + 100, + FileSink::new_ptr, + CString::new("file").unwrap().as_ptr()); + } + return GBoolean::True; +} diff --git a/src/plugin.c b/src/plugin.c index fe6fecaf..23f832df 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -1,7 +1,7 @@ #include #include "rssource.h" -#include "rsfilesink.h" +#include "rssink.h" static gboolean plugin_init (GstPlugin * plugin) @@ -9,10 +9,8 @@ plugin_init (GstPlugin * plugin) if (!gst_rs_source_plugin_init (plugin)) return FALSE; - if (!gst_element_register (plugin, "rsfilesink", GST_RANK_NONE, - GST_TYPE_RSFILE_SINK)) { + if (!gst_rs_sink_plugin_init (plugin)) return FALSE; - } return TRUE; } diff --git a/src/rsfilesink.c b/src/rsfilesink.c deleted file mode 100644 index c7301508..00000000 --- a/src/rsfilesink.c +++ /dev/null @@ -1,159 +0,0 @@ -#include "rsfilesink.h" - -GST_DEBUG_CATEGORY_STATIC (gst_rsfile_sink_debug); -#define GST_CAT_DEFAULT gst_rsfile_sink_debug - -static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS_ANY); - -enum -{ - PROP_0, - PROP_LOCATION -}; - -static void gst_rsfile_sink_finalize (GObject * object); - -static void gst_rsfile_sink_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_rsfile_sink_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); - -static gboolean gst_rsfile_sink_start (GstBaseSink * basesink); -static gboolean gst_rsfile_sink_stop (GstBaseSink * basesink); -static gboolean gst_rsfile_sink_query (GstBaseSink * basesink, GstQuery * query); -static GstFlowReturn gst_rsfile_sink_render (GstBaseSink * sink, GstBuffer * buffer); -static gboolean gst_rsfile_sink_event (GstBaseSink * sink, GstEvent * event); - -#define _do_init \ - GST_DEBUG_CATEGORY_INIT (gst_rsfile_sink_debug, "rsfilesink", 0, "rsfilesink element"); -#define gst_rsfile_sink_parent_class parent_class -G_DEFINE_TYPE_WITH_CODE (GstRsFileSink, gst_rsfile_sink, GST_TYPE_BASE_SINK, _do_init); - -static void -gst_rsfile_sink_class_init (GstRsFileSinkClass * klass) -{ - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - GstBaseSinkClass *gstbasesink_class; - - gobject_class = G_OBJECT_CLASS (klass); - gstelement_class = GST_ELEMENT_CLASS (klass); - gstbasesink_class = GST_BASE_SINK_CLASS (klass); - - gobject_class->set_property = gst_rsfile_sink_set_property; - gobject_class->get_property = gst_rsfile_sink_get_property; - - g_object_class_install_property (gobject_class, PROP_LOCATION, - g_param_spec_string ("location", "File Location", - "Location of the file to read", NULL, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | - GST_PARAM_MUTABLE_READY)); - - gobject_class->finalize = gst_rsfile_sink_finalize; - - gst_element_class_set_static_metadata (gstelement_class, - "File Sink", - "Sink/Rsfile", - "Write to a file", - "Luis de Bethencourt "); - gst_element_class_add_static_pad_template (gstelement_class, &sink_template); - - gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_rsfile_sink_start); - gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_rsfile_sink_stop); - gstbasesink_class->query = GST_DEBUG_FUNCPTR (gst_rsfile_sink_query); - gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_rsfile_sink_render); - gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_rsfile_sink_event); -} - -static void -gst_rsfile_sink_init (GstRsFileSink * src) -{ - gst_base_sink_set_blocksize (GST_BASE_SINK (src), 4096); -} - -static void -gst_rsfile_sink_finalize (GObject * object) -{ - GstRsFileSink *src = GST_RSFILE_SINK (object); - - g_free (src->location); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -gst_rsfile_sink_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstRsFileSink *src = GST_RSFILE_SINK (object); - - switch (prop_id) { - case PROP_LOCATION: - if (src->location) - g_free (src->location); - src->location = g_value_dup_string (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_rsfile_sink_get_property (GObject * object, guint prop_id, GValue * value, - GParamSpec * pspec) -{ - GstRsFileSink *src = GST_RSFILE_SINK (object); - - switch (prop_id) { - case PROP_LOCATION: - g_value_set_string (value, src->location); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -extern void bar(void); - -static GstFlowReturn -gst_rsfile_sink_render (GstBaseSink * sink, GstBuffer * buffer) -{ - bar(); -} - -static gboolean -gst_rsfile_sink_query (GstBaseSink * basesink, GstQuery * query) -{ - GstRsFileSink *sink = GST_RSFILE_SINK (basesink); - - return TRUE; -} - -static gboolean -gst_rsfile_sink_event (GstBaseSink * sink, GstEvent * event) -{ - return TRUE; -} - -/* open the rsfile, necessary to go to READY state */ -static gboolean -gst_rsfile_sink_start (GstBaseSink * basesink) -{ - GstRsFileSink *src = GST_RSFILE_SINK (basesink); - - return TRUE; -} - -/* unmap and close the rsfile */ -static gboolean -gst_rsfile_sink_stop (GstBaseSink * basesink) -{ - GstRsFileSink *src = GST_RSFILE_SINK (basesink); - - return TRUE; -} diff --git a/src/rsfilesink.h b/src/rsfilesink.h deleted file mode 100644 index 1ea88b78..00000000 --- a/src/rsfilesink.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef __GST_RSFILE_SINK_H__ -#define __GST_RSFILE_SINK_H__ - -#include -#include - -G_BEGIN_DECLS - -#define GST_TYPE_RSFILE_SINK \ - (gst_rsfile_sink_get_type()) -#define GST_RSFILE_SINK(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RSFILE_SINK,GstRsFileSink)) -#define GST_RSFILE_SINK_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RSFILE_SINK,GstRsFileSinkClass)) -#define GST_IS_RSFILE_SINK(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RSFILE_SINK)) -#define GST_IS_RSFILE_SINK_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RSFILE_SINK)) -#define GST_RSFILE_SINK_CAST(obj) ((GstRsFileSink*) obj) - -typedef struct _GstRsFileSink GstRsFileSink; -typedef struct _GstRsFileSinkClass GstRsFileSinkClass; - -struct _GstRsFileSink { - GstBaseSink element; - - gchar *location; -}; - -struct _GstRsFileSinkClass { - GstBaseSinkClass parent_class; -}; - -G_GNUC_INTERNAL GType gst_rsfile_sink_get_type (void); - -G_END_DECLS - -#endif /* __GST_RSFILE_SINK_H__ */ diff --git a/src/rsfilesink.rs b/src/rsfilesink.rs index c3033d09..314ca44e 100644 --- a/src/rsfilesink.rs +++ b/src/rsfilesink.rs @@ -1,4 +1,95 @@ -#[no_mangle] -pub extern fn bar() { - print!("bar\n"); +use std::u64; +use std::fs::File; +use std::path::PathBuf; +use url::Url; + +use std::io::Write; + +use utils::*; +use rssink::*; + +#[derive(Debug)] +pub struct FileSink { + location: Option, + file: Option, + position: u64, +} + +impl FileSink { + fn new() -> FileSink { + FileSink { location: None, file: None, position: 0 } + } + + fn new_source() -> Box { + Box::new(FileSink::new()) + } + pub extern "C" fn new_ptr() -> *mut Box { + let instance = Box::new(FileSink::new_source()); + return Box::into_raw(instance); + } +} + +impl Sink for FileSink { + fn set_uri(&mut self, uri_str: &Option) -> bool { + match *uri_str { + None => { + self.location = None; + return true; + }, + Some(ref uri_str) => { + let uri_parsed = Url::parse(uri_str.as_str()); + match uri_parsed { + Ok(u) => { + match u.to_file_path().ok() { + Some(p) => { + self.location = Some(p); + return true; + }, + None => { + self.location = None; + println_err!("Unsupported file URI '{}'", uri_str); + return false; + } + } + }, + Err(err) => { + self.location = None; + println_err!("Failed to parse URI '{}': {}", uri_str, err); + return false; + } + } + } + } + } + + fn get_uri(&self) -> Option { + match self.location { + None => None, + Some(ref location) => Url::from_file_path(&location).map(|u| u.into_string()).ok() + } + } + + fn start(&mut self) -> bool { + self.file = None; + self.position = 0; + + match self.location { + None => return false, + Some(ref location) => { + return true; + }, + } + } + + fn stop(&mut self) -> bool { + self.file = None; + self.position = 0; + + true + } + + fn render(&mut self) -> Result { + println!("Got a buffer!"); + Err(GstFlowReturn::Ok) + } } diff --git a/src/rssink.c b/src/rssink.c new file mode 100644 index 00000000..9348d4c2 --- /dev/null +++ b/src/rssink.c @@ -0,0 +1,264 @@ +#include "rssink.h" + +#include +#include + +typedef struct { + gchar *name; + gchar *long_name; + gchar *description; + gchar *classification; + gchar *author; + void * (*create_instance) (void); + gchar **protocols; +} ElementData; +static GHashTable *sinks; + +/* Declarations for Rust code */ +extern gboolean sinks_register (void *plugin); +extern GstFlowReturn sink_render (void * filesink); +extern gboolean sink_set_uri (void * filesink, const char *uri); +extern char * sink_get_uri (void * filesink); +extern gboolean sink_start (void * filesink); +extern gboolean sink_stop (void * filesink); + +GST_DEBUG_CATEGORY_STATIC (gst_rs_sink_debug); +#define GST_CAT_DEFAULT gst_rs_sink_debug + +static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +enum +{ + PROP_0, + PROP_URI +}; + +static void gst_rs_sink_uri_handler_init (gpointer g_iface, + gpointer iface_data); + +static void gst_rs_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_rs_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gboolean gst_rs_sink_start (GstBaseSink * basesink); +static gboolean gst_rs_sink_stop (GstBaseSink * basesink); + +static GstFlowReturn gst_rs_sink_render (GstBaseSink * sink, GstBuffer * buffer); + +static GObjectClass *parent_class; + +static void +gst_rs_sink_class_init (GstRsSinkClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBaseSinkClass *gstbasesink_class; + ElementData * data = g_hash_table_lookup (sinks, GSIZE_TO_POINTER (G_TYPE_FROM_CLASS (klass))); + g_assert (data != NULL); + + gobject_class = G_OBJECT_CLASS (klass); + gstelement_class = GST_ELEMENT_CLASS (klass); + gstbasesink_class = GST_BASE_SINK_CLASS (klass); + + gobject_class->set_property = gst_rs_sink_set_property; + gobject_class->get_property = gst_rs_sink_get_property; + + g_object_class_install_property (gobject_class, PROP_URI, + g_param_spec_string ("uri", "URI", + "URI to read from", NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY)); + + gst_element_class_set_static_metadata (gstelement_class, + data->long_name, + data->classification, + data->description, + data->author); + gst_element_class_add_static_pad_template (gstelement_class, &sink_template); + + gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_rs_sink_start); + gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_rs_sink_stop); + gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_rs_sink_render); +} + +static void +gst_rs_sink_init (GstRsSink * sink, GstRsSinkClass * klass) +{ + ElementData * data = g_hash_table_lookup (sinks, GSIZE_TO_POINTER (G_TYPE_FROM_CLASS (klass))); + g_assert (data != NULL); + + gst_base_sink_set_sync (GST_BASE_SINK (sink), FALSE); + + sink->instance = data->create_instance (); +} + +static void +gst_rs_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstRsSink *sink = GST_RS_SINK (object); + + switch (prop_id) { + case PROP_URI: + sink_set_uri (sink->instance, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rs_sink_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstRsSink *sink = GST_RS_SINK (object); + + switch (prop_id) { + case PROP_URI: + g_value_take_string (value, sink_get_uri (sink->instance)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstFlowReturn +gst_rs_sink_render (GstBaseSink * basesink, GstBuffer * buffer) +{ + GstRsSink *sink = GST_RS_SINK (basesink); + GstFlowReturn ret; + + ret = sink_render (sink->instance); + + return ret; +} + +/* open the rs, necessary to go to READY state */ +static gboolean +gst_rs_sink_start (GstBaseSink * basesink) +{ + GstRsSink *sink = GST_RS_SINK (basesink); + + return sink_start (sink->instance); +} + +/* unmap and close the rs */ +static gboolean +gst_rs_sink_stop (GstBaseSink * basesink) +{ + GstRsSink *sink = GST_RS_SINK (basesink); + + return sink_stop (sink->instance); +} + +static GstURIType +gst_rs_sink_uri_get_type (GType type) +{ + return GST_URI_SINK; +} + +static const gchar *const * +gst_rs_sink_uri_get_protocols (GType type) +{ + ElementData * data = g_hash_table_lookup (sinks, GSIZE_TO_POINTER (type)); + g_assert (data != NULL); + + return (const gchar * const *) data->protocols; +} + +static gchar * +gst_rs_sink_uri_get_uri (GstURIHandler * handler) +{ + GstRsSink *sink = GST_RS_SINK (handler); + + return sink_get_uri (sink->instance); +} + +static gboolean +gst_rs_sink_uri_set_uri (GstURIHandler * handler, const gchar * uri, + GError ** err) +{ + GstRsSink *sink = GST_RS_SINK (handler); + + if (!sink_set_uri (sink->instance, uri)) { + g_set_error (err, GST_URI_ERROR, GST_URI_ERROR_BAD_URI, + "Can't handle URI '%s'", uri); + return FALSE; + } + + return TRUE; +} + +static void +gst_rs_sink_uri_handler_init (gpointer g_iface, gpointer iface_data) +{ + GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface; + + iface->get_type = gst_rs_sink_uri_get_type; + iface->get_protocols = gst_rs_sink_uri_get_protocols; + iface->get_uri = gst_rs_sink_uri_get_uri; + iface->set_uri = gst_rs_sink_uri_set_uri; +} + +gboolean +gst_rs_sink_plugin_init (GstPlugin * plugin) +{ + sinks = g_hash_table_new (g_direct_hash, g_direct_equal); + GST_DEBUG_CATEGORY_INIT (gst_rs_sink_debug, "rssink", 0, "rssink element"); + + parent_class = g_type_class_ref (GST_TYPE_BASE_SINK); + + return sinks_register (plugin); +} + +gboolean +gst_rs_sink_register (GstPlugin * plugin, const gchar *name, const gchar * long_name, const gchar * description, const gchar * classification, const gchar * author, GstRank rank, void * (*create_instance) (void), const gchar *protocols) +{ + GTypeInfo type_info = { + sizeof (GstRsSinkClass), + NULL, + NULL, + (GClassInitFunc) gst_rs_sink_class_init, + NULL, + NULL, + sizeof (GstRsSink), + 0, + (GInstanceInitFunc) gst_rs_sink_init + }; + GInterfaceInfo iface_info = { + gst_rs_sink_uri_handler_init, + NULL, + NULL + }; + GType type; + gchar *type_name; + ElementData *data; + + data = g_new0 (ElementData, 1); + data->name = g_strdup (name); + data->long_name = g_strdup (long_name); + data->description = g_strdup (description); + data->classification = g_strdup (classification); + data->author = g_strdup (author); + data->create_instance = create_instance; + data->protocols = g_strsplit (protocols, ":", -1); + + type_name = g_strconcat ("RsSink-", name, NULL); + type = g_type_register_static (GST_TYPE_BASE_SINK, type_name, &type_info, 0); + g_free (type_name); + + g_type_add_interface_static (type, GST_TYPE_URI_HANDLER, &iface_info); + + g_hash_table_insert (sinks, GSIZE_TO_POINTER (type), data); + + if (!gst_element_register (plugin, name, rank, type)) + return FALSE; + + return TRUE; +} diff --git a/src/rssink.h b/src/rssink.h new file mode 100644 index 00000000..3d5edd8b --- /dev/null +++ b/src/rssink.h @@ -0,0 +1,31 @@ +#ifndef __GST_RS_SINK_H__ +#define __GST_RS_SINK_H__ + +#include +#include + +G_BEGIN_DECLS + +#define GST_RS_SINK(obj) \ + ((GstRsSink *)obj) +#define GST_RS_SINK_CLASS(klass) \ + ((GstRsSinkKlass *)klass) + +typedef struct _GstRsSink GstRsSink; +typedef struct _GstRsSinkClass GstRsSinkClass; + +struct _GstRsSink { + GstBaseSink element; + + gpointer instance; +}; + +struct _GstRsSinkClass { + GstBaseSinkClass parent_class; +}; + +G_GNUC_INTERNAL gboolean gst_rs_sink_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RS_SINK_H__ */ diff --git a/src/rssink.rs b/src/rssink.rs new file mode 100644 index 00000000..d3d0a4b5 --- /dev/null +++ b/src/rssink.rs @@ -0,0 +1,63 @@ +use libc::{c_char}; +use std::ffi::{CStr, CString}; +use std::ptr; + +use utils::*; + +pub trait Sink { + fn set_uri(&mut self, uri_str: &Option) -> bool; + fn get_uri(&self) -> Option; + fn start(&mut self) -> bool; + fn stop(&mut self) -> bool; + fn render(&mut self) -> Result; +} + +#[no_mangle] +pub extern "C" fn sink_set_uri(ptr: *mut Box, uri_ptr: *const c_char) -> GBoolean{ + let source: &mut Box = unsafe { &mut *ptr }; + + if uri_ptr.is_null() { + GBoolean::from_bool(source.set_uri(&None)) + } else { + let uri = unsafe { CStr::from_ptr(uri_ptr) }; + GBoolean::from_bool(source.set_uri(&Some(String::from(uri.to_str().unwrap())))) + } +} + +#[no_mangle] +pub extern "C" fn sink_get_uri(ptr: *mut Box) -> *mut c_char { + let source: &mut Box = unsafe { &mut *ptr }; + + match source.get_uri() { + Some(ref uri) => + CString::new(uri.clone().into_bytes()).unwrap().into_raw(), + None => + ptr::null_mut() + } +} + +#[no_mangle] +pub extern "C" fn sink_render(ptr: *mut Box) -> GstFlowReturn { + let source: &mut Box = unsafe { &mut *ptr }; + + match source.render() { + Ok(data) => { + GstFlowReturn::Ok + }, + Err(ret) => ret, + } +} + +#[no_mangle] +pub extern "C" fn sink_start(ptr: *mut Box) -> GBoolean { + let source: &mut Box = unsafe { &mut *ptr }; + + GBoolean::from_bool(source.start()) +} + +#[no_mangle] +pub extern "C" fn sink_stop(ptr: *mut Box) -> GBoolean { + let source: &mut Box = unsafe { &mut *ptr }; + + GBoolean::from_bool(source.stop()) +}