From 25f85868d4ddd25c8587380948ebd5d9161707d3 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Mon, 24 Nov 2003 03:21:54 +0000 Subject: [PATCH] implement URI schemes Original commit message from CVS: implement URI schemes Elements can now register as a source or sink for a protocol and applications can use gst_element_make_from_uri () to get an element that handles a given URI. This patch provides: - removal of old broken URI handling scheme. - new URI handling using interfaces. - updates for registry to save handled URIs. - interface for URI handlers. - implementation of that in filesrc and filesink for the file:// URI - extension to pipeline parsing to allow specifying only a URI instead of element Does not include: - tests - inclusion in docs build --- docs/gst/tmpl/gstreamer-unused.sgml | 46 ++++ docs/gst/tmpl/gsturi.sgml | 51 ----- gst/elements/gstfakesink.c | 2 +- gst/elements/gstfilesink.c | 105 ++++++++- gst/elements/gstfilesink.h | 1 + gst/elements/gstfilesrc.c | 101 +++++++-- gst/elements/gstfilesrc.h | 1 + gst/gstelement.h | 12 +- gst/gstelementfactory.c | 62 +++++- gst/gstpluginfeature.c | 34 ++- gst/gstpluginfeature.h | 8 +- gst/gsturi.c | 423 +++++++++++++++++++++++------------- gst/gsturi.h | 78 ++++--- gst/parse/grammar.y | 104 +++++---- gst/parse/parse.l | 15 ++ gst/parse/types.h | 19 ++ gst/registries/gstxmlregistry.c | 92 ++++---- plugins/elements/gstfakesink.c | 2 +- plugins/elements/gstfilesink.c | 105 ++++++++- plugins/elements/gstfilesink.h | 1 + plugins/elements/gstfilesrc.c | 101 +++++++-- plugins/elements/gstfilesrc.h | 1 + tools/gst-inspect.c | 10 - tools/gst-xmlinspect.c | 10 - 24 files changed, 986 insertions(+), 398 deletions(-) diff --git a/docs/gst/tmpl/gstreamer-unused.sgml b/docs/gst/tmpl/gstreamer-unused.sgml index 7110bc5..0cad7ce 100644 --- a/docs/gst/tmpl/gstreamer-unused.sgml +++ b/docs/gst/tmpl/gstreamer-unused.sgml @@ -10248,6 +10248,15 @@ Destroy the scheduler @parent: @Returns: + + + + + +@handler: +@name: +@Returns: + @@ -10255,6 +10264,43 @@ Destroy the scheduler @handler: + + + + + +@name: +@Returns: + + + + + + +@uri: +@Returns: + + + + + + +@uri: +@name: +@Returns: + + + + + + +@name: +@uri: +@longdesc: +@element: +@property: +@Returns: + diff --git a/docs/gst/tmpl/gsturi.sgml b/docs/gst/tmpl/gsturi.sgml index 603da64..1488c3d 100644 --- a/docs/gst/tmpl/gsturi.sgml +++ b/docs/gst/tmpl/gsturi.sgml @@ -15,54 +15,3 @@ and the element property that can handle a given URI. - - - - - -@name: -@uri: -@longdesc: -@element: -@property: -@Returns: - - - - - - - -@name: -@Returns: - - - - - - - -@uri: -@Returns: - - - - - - - -@handler: -@name: -@Returns: - - - - - - - -@uri: -@name: -@Returns: - - diff --git a/gst/elements/gstfakesink.c b/gst/elements/gstfakesink.c index 4f23d8b..1e82e51 100644 --- a/gst/elements/gstfakesink.c +++ b/gst/elements/gstfakesink.c @@ -173,7 +173,7 @@ gst_fakesink_class_init (GstFakeSinkClass *klass) gst_fakesink_signals[SIGNAL_HANDOFF] = g_signal_new ("handoff", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstFakeSinkClass, handoff), NULL, NULL, - gst_marshal_VOID__POINTER_OBJECT, G_TYPE_NONE, 1, + gst_marshal_VOID__POINTER_OBJECT, G_TYPE_NONE, 2, GST_TYPE_BUFFER, GST_TYPE_PAD); gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_fakesink_set_property); diff --git a/gst/elements/gstfilesink.c b/gst/elements/gstfilesink.c index 5c4c81c..fef5d7e 100644 --- a/gst/elements/gstfilesink.c +++ b/gst/elements/gstfilesink.c @@ -69,6 +69,7 @@ GST_PAD_FORMATS_FUNCTION (gst_filesink_get_formats, static void gst_filesink_base_init (gpointer g_class); static void gst_filesink_class_init (GstFileSinkClass *klass); static void gst_filesink_init (GstFileSink *filesink); +static void gst_filesink_dispose (GObject *object); static void gst_filesink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); @@ -83,6 +84,8 @@ static gboolean gst_filesink_pad_query (GstPad *pad, GstQueryType type, GstFormat *format, gint64 *value); static void gst_filesink_chain (GstPad *pad,GstData *_data); +static void gst_filesink_uri_handler_init (gpointer g_iface, gpointer iface_data); + static GstElementStateReturn gst_filesink_change_state (GstElement *element); static GstElementClass *parent_class = NULL; @@ -105,7 +108,14 @@ gst_filesink_get_type (void) 0, (GInstanceInitFunc)gst_filesink_init, }; + static const GInterfaceInfo urihandler_info = { + gst_filesink_uri_handler_init, + NULL, + NULL + }; filesink_type = g_type_register_static (GST_TYPE_ELEMENT, "GstFileSink", &filesink_info, 0); + + g_type_add_interface_static (filesink_type, GST_TYPE_URI_HANDLER, &urihandler_info); GST_DEBUG_CATEGORY_INIT (gst_filesink_debug, "filesink", 0, "filesink element"); } @@ -138,8 +148,8 @@ gst_filesink_class_init (GstFileSinkClass *klass) gobject_class->set_property = gst_filesink_set_property; gobject_class->get_property = gst_filesink_get_property; + gobject_class->dispose = gst_filesink_dispose; } - static void gst_filesink_init (GstFileSink *filesink) { @@ -158,7 +168,38 @@ gst_filesink_init (GstFileSink *filesink) filesink->filename = NULL; filesink->file = NULL; } +static void +gst_filesink_dispose (GObject *object) +{ + GstFileSink *sink = GST_FILESINK (object); + G_OBJECT_CLASS (parent_class)->dispose (object); + + g_free (sink->uri); + sink->uri = NULL; + g_free (sink->filename); + sink->filename = NULL; +} +static gboolean +gst_filesink_set_location (GstFileSink *sink, const gchar *location) +{ + /* the element must be stopped or paused in order to do this */ + if (GST_STATE (sink) > GST_STATE_PAUSED) + return FALSE; + if (GST_STATE (sink) == GST_STATE_PAUSED && + GST_FLAG_IS_SET (sink, GST_FILESINK_OPEN)) + return FALSE; + + g_free (sink->filename); + g_free (sink->uri); + sink->filename = g_strdup (location); + sink->uri = gst_uri_construct ("file", location); + + if (GST_STATE (sink) == GST_STATE_PAUSED) + gst_filesink_open_file (sink); + + return TRUE; +} static void gst_filesink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { @@ -169,16 +210,7 @@ gst_filesink_set_property (GObject *object, guint prop_id, const GValue *value, switch (prop_id) { case ARG_LOCATION: - /* the element must be stopped or paused in order to do this */ - g_return_if_fail (GST_STATE (sink) <= GST_STATE_PAUSED); - if (GST_STATE (sink) == GST_STATE_PAUSED) - g_return_if_fail (!GST_FLAG_IS_SET (sink, GST_FILESINK_OPEN)); - - if (sink->filename) - g_free (sink->filename); - sink->filename = g_strdup (g_value_get_string (value)); - if (GST_STATE (sink) == GST_STATE_PAUSED) - gst_filesink_open_file (sink); + gst_filesink_set_location (sink, g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -435,3 +467,54 @@ gst_filesink_change_state (GstElement *element) return GST_STATE_SUCCESS; } + +/*** GSTURIHANDLER INTERFACE *************************************************/ + +static guint +gst_filesink_uri_get_type (void) +{ + return GST_URI_SINK; +} +static gchar ** +gst_filesink_uri_get_protocols(void) +{ + static gchar *protocols[] = {"file", NULL}; + return protocols; +} +static const gchar * +gst_filesink_uri_get_uri (GstURIHandler *handler) +{ + GstFileSink *sink = GST_FILESINK (handler); + + return sink->uri; +} +static gboolean +gst_filesink_uri_set_uri (GstURIHandler *handler, const gchar *uri) +{ + gchar *protocol, *location; + gboolean ret; + GstFileSink *sink = GST_FILESINK (handler); + + protocol = gst_uri_get_protocol (uri); + if (strcmp (protocol, "file") != 0) { + g_free (protocol); + return FALSE; + } + g_free (protocol); + location = gst_uri_get_location (uri); + ret = gst_filesink_set_location (sink, location); + g_free (location); + + return ret; +} + +static void +gst_filesink_uri_handler_init (gpointer g_iface, gpointer iface_data) +{ + GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface; + + iface->get_type = gst_filesink_uri_get_type; + iface->get_protocols = gst_filesink_uri_get_protocols; + iface->get_uri = gst_filesink_uri_get_uri; + iface->set_uri = gst_filesink_uri_set_uri; +} diff --git a/gst/elements/gstfilesink.h b/gst/elements/gstfilesink.h index b72551b..bd85a1d 100644 --- a/gst/elements/gstfilesink.h +++ b/gst/elements/gstfilesink.h @@ -53,6 +53,7 @@ struct _GstFileSink { GstElement element; gchar *filename; + gchar *uri; FILE *file; guint64 data_written; diff --git a/gst/elements/gstfilesrc.c b/gst/elements/gstfilesrc.c index 275d730..e1d4bff 100644 --- a/gst/elements/gstfilesrc.c +++ b/gst/elements/gstfilesrc.c @@ -136,6 +136,8 @@ static gboolean gst_filesrc_srcpad_query (GstPad *pad, GstQueryType type, static GstElementStateReturn gst_filesrc_change_state (GstElement *element); +static void gst_filesrc_uri_handler_init (gpointer g_iface, gpointer iface_data); + static GstElementClass *parent_class = NULL; /*static guint gst_filesrc_signals[LAST_SIGNAL] = { 0 };*/ @@ -157,8 +159,15 @@ gst_filesrc_get_type(void) 0, (GInstanceInitFunc)gst_filesrc_init, }; + static const GInterfaceInfo urihandler_info = { + gst_filesrc_uri_handler_init, + NULL, + NULL + }; filesrc_type = g_type_register_static (GST_TYPE_ELEMENT, "GstFileSrc", &filesrc_info, 0); - + + g_type_add_interface_static (filesrc_type, GST_TYPE_URI_HANDLER, &urihandler_info); + GST_DEBUG_CATEGORY_INIT (gst_filesrc_debug, "filesrc", 0, "filesrc element"); } return filesrc_type; @@ -263,8 +272,32 @@ gst_filesrc_dispose (GObject *object) g_mutex_free (src->map_regions_lock); if (src->filename) g_free (src->filename); + if (src->uri) + g_free (src->uri); } +static gboolean +gst_filesrc_set_location (GstFileSrc *src, const gchar *location) +{ + /* the element must be stopped in order to do this */ + if (GST_STATE (src) == GST_STATE_PLAYING) + return FALSE; + + if (src->filename) g_free (src->filename); + if (src->uri) g_free (src->uri); + /* clear the filename if we get a NULL (is that possible?) */ + if (location == NULL) { + src->filename = NULL; + src->uri = NULL; + } else { + src->filename = g_strdup (location); + src->uri = gst_uri_construct ("file", src->filename); + } + g_object_notify (G_OBJECT (src), "location"); + gst_uri_handler_new_uri (GST_URI_HANDLER (src), src->uri); + + return TRUE; +} static void gst_filesrc_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) @@ -278,19 +311,7 @@ gst_filesrc_set_property (GObject *object, guint prop_id, const GValue *value, G switch (prop_id) { case ARG_LOCATION: - /* the element must be stopped in order to do this */ - g_return_if_fail (GST_STATE (src) < GST_STATE_PLAYING); - - if (src->filename) g_free (src->filename); - /* clear the filename if we get a NULL (is that possible?) */ - if (g_value_get_string (value) == NULL) { - gst_element_set_state (GST_ELEMENT (object), GST_STATE_NULL); - src->filename = NULL; - /* otherwise set the new filename */ - } else { - src->filename = g_strdup (g_value_get_string (value)); - } - g_object_notify (G_OBJECT (src), "location"); + gst_filesrc_set_location (src, g_value_get_string (value)); break; case ARG_BLOCKSIZE: src->block_size = g_value_get_ulong (value); @@ -918,3 +939,55 @@ error: gst_event_unref (event); return FALSE; } + +/*** GSTURIHANDLER INTERFACE *************************************************/ + +static guint +gst_filesrc_uri_get_type (void) +{ + return GST_URI_SRC; +} +static gchar ** +gst_filesrc_uri_get_protocols(void) +{ + static gchar *protocols[] = {"file", NULL}; + return protocols; +} +static const gchar * +gst_filesrc_uri_get_uri (GstURIHandler *handler) +{ + GstFileSrc *src = GST_FILESRC (handler); + + return src->uri; +} +static gboolean +gst_filesrc_uri_set_uri (GstURIHandler *handler, const gchar *uri) +{ + gchar *protocol, *location; + gboolean ret; + GstFileSrc *src = GST_FILESRC (handler); + + protocol = gst_uri_get_protocol (uri); + if (strcmp (protocol, "file") != 0) { + g_free (protocol); + return FALSE; + } + g_free (protocol); + location = gst_uri_get_location (uri); + ret = gst_filesrc_set_location (src, location); + g_free (location); + + return ret; +} + +static void +gst_filesrc_uri_handler_init (gpointer g_iface, gpointer iface_data) +{ + GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface; + + iface->get_type = gst_filesrc_uri_get_type; + iface->get_protocols = gst_filesrc_uri_get_protocols; + iface->get_uri = gst_filesrc_uri_get_uri; + iface->set_uri = gst_filesrc_uri_set_uri; +} + diff --git a/gst/elements/gstfilesrc.h b/gst/elements/gstfilesrc.h index 23b6dde..1f584be 100644 --- a/gst/elements/gstfilesrc.h +++ b/gst/elements/gstfilesrc.h @@ -58,6 +58,7 @@ struct _GstFileSrc { guint pagesize; /* system page size*/ gchar *filename; /* filename */ + gchar *uri; /* caching the URI */ gint fd; /* open file descriptor*/ off_t filelen; /* what's the file length?*/ diff --git a/gst/gstelement.h b/gst/gstelement.h index f40547f..18622ee 100644 --- a/gst/gstelement.h +++ b/gst/gstelement.h @@ -398,7 +398,11 @@ struct _GstElementFactory { GList * padtemplates; guint numpadtemplates; - GList *interfaces; /* interfaces this element implements */ + /* URI interface stuff */ + guint uri_type; + gchar ** uri_protocols; + + GList * interfaces; /* interfaces this element implements */ GST_OBJECT_PADDING }; @@ -423,8 +427,10 @@ G_CONST_RETURN gchar * gst_element_factory_get_klass (GstElementFactory *factor G_CONST_RETURN gchar * gst_element_factory_get_description (GstElementFactory *factory); G_CONST_RETURN gchar * gst_element_factory_get_version (GstElementFactory *factory); G_CONST_RETURN gchar * gst_element_factory_get_author (GstElementFactory *factory); -guint gst_element_factory_get_num_pad_templates (GstElementFactory *factory); -G_CONST_RETURN GList * gst_element_factory_get_pad_templates (GstElementFactory *factory); +guint gst_element_factory_get_num_padtemplates (GstElementFactory *factory); +G_CONST_RETURN GList * gst_element_factory_get_padtemplates (GstElementFactory *factory); +guint gst_element_factory_get_uri_type (GstElementFactory *factory); +gchar ** gst_element_factory_get_uri_protocols (GstElementFactory *factory); GstElement* gst_element_factory_create (GstElementFactory *factory, const gchar *name); diff --git a/gst/gstelementfactory.c b/gst/gstelementfactory.c index 8e1e7ab..a3642fc 100644 --- a/gst/gstelementfactory.c +++ b/gst/gstelementfactory.c @@ -26,6 +26,7 @@ #include "gstelement.h" #include "gstregistrypool.h" #include "gstinfo.h" +#include "gsturi.h" GST_DEBUG_CATEGORY_STATIC (element_factory_debug); #define GST_CAT_DEFAULT element_factory_debug @@ -85,6 +86,9 @@ gst_element_factory_init (GstElementFactory *factory) factory->padtemplates = NULL; factory->numpadtemplates = 0; + factory->uri_type = GST_URI_UNKNOWN; + factory->uri_protocols = NULL; + factory->interfaces = NULL; } /** @@ -150,6 +154,11 @@ gst_element_factory_cleanup (GstElementFactory *factory) g_list_free (factory->padtemplates); factory->padtemplates = NULL; factory->numpadtemplates = 0; + factory->uri_type = GST_URI_UNKNOWN; + if (factory->uri_protocols) { + g_strfreev (factory->uri_protocols); + factory->uri_protocols = NULL; + } g_list_foreach (factory->interfaces, (GFunc) g_free, NULL); g_list_free (factory->interfaces); @@ -198,6 +207,20 @@ gst_element_register (GstPlugin *plugin, const gchar *name, guint rank, GType ty g_list_foreach (factory->padtemplates, (GFunc) g_object_ref, NULL); factory->numpadtemplates = klass->numpadtemplates; + /* special stuff for URI handling */ + if (g_type_is_a (type, GST_TYPE_URI_HANDLER)) { + GstURIHandlerInterface *iface = (GstURIHandlerInterface *) + g_type_interface_peek (klass, GST_TYPE_URI_HANDLER); + if (!iface || !iface->get_type || !iface->get_protocols) + goto error; + factory->uri_type = iface->get_type (); + if (!GST_URI_TYPE_IS_VALID (factory->uri_type)) + goto error; + factory->uri_protocols = g_strdupv (iface->get_protocols ()); + if (!factory->uri_protocols) + goto error; + } + interfaces = g_type_interfaces (type, &n_interfaces); for (i = 0; i < n_interfaces; i++) { __gst_element_factory_add_interface (factory, g_type_name (interfaces[i])); @@ -205,10 +228,13 @@ gst_element_register (GstPlugin *plugin, const gchar *name, guint rank, GType ty g_free (interfaces); gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE (factory), rank); - gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); return TRUE; + +error: + gst_element_factory_cleanup (factory); + return FALSE; } /** * gst_element_factory_create: @@ -422,7 +448,7 @@ __gst_element_factory_add_interface (GstElementFactory *elementfactory, const gc * gst_element_factory_get_pad_templates: * @factory: a #GstElementFactory * - * Gets the #Glist of pad templates for this factory. + * Gets the #GList of padtemplates for this factory. * * Returns: the padtemplates */ @@ -434,6 +460,38 @@ gst_element_factory_get_pad_templates (GstElementFactory *factory) return factory->padtemplates; } /** + * gst_element_factory_get_uri_type: + * @factory: a #GstElementFactory + * + * Gets the type of URIs the element supports or GST_URI_UNKNOWN if none. + * + * Returns: type of URIs this element supports + */ +guint +gst_element_factory_get_uri_type (GstElementFactory *factory) +{ + g_return_val_if_fail (GST_IS_ELEMENT_FACTORY (factory), GST_URI_UNKNOWN); + + return factory->uri_type; +} +/** + * gst_element_factory_get_uri_protocols: + * @factory: a #GstElementFactory + * + * Gets a NULL-terminated array of protocols this element supports or NULL, if + * no protocols are supported. You may not change the contents of the returned + * array as it is still ownt by the element factory. Use g_strdupv() if you want to. + * + * Returns: the supported protocols or NULL + */ +gchar ** +gst_element_factory_get_uri_protocols (GstElementFactory *factory) +{ + g_return_val_if_fail (GST_IS_ELEMENT_FACTORY (factory), NULL); + + return factory->uri_protocols; +} +/** * gst_element_factory_can_src_caps : * @factory: factory to query * @caps: the caps to check diff --git a/gst/gstpluginfeature.c b/gst/gstpluginfeature.c index 5c8afed..788222f 100644 --- a/gst/gstpluginfeature.c +++ b/gst/gstpluginfeature.c @@ -147,7 +147,7 @@ gst_plugin_feature_type_name_filter (GstPluginFeature *feature, * the most appropriate feature. */ void -gst_plugin_feature_set_rank (GstPluginFeature *feature, guint16 rank) +gst_plugin_feature_set_rank (GstPluginFeature *feature, guint rank) { g_return_if_fail (feature != NULL); g_return_if_fail (GST_IS_PLUGIN_FEATURE (feature)); @@ -155,7 +155,7 @@ gst_plugin_feature_set_rank (GstPluginFeature *feature, guint16 rank) feature->rank = rank; } /** - * gst_plugin_feature_set_rank: + * gst_plugin_feature_set_name: * @feature: a feature * @name: the name to set * @@ -175,4 +175,34 @@ gst_plugin_feature_set_name (GstPluginFeature *feature, const gchar *name) feature->name = g_strdup (name); } } +/** + * gst_plugin_feature_get rank: + * @feature: a feature + * + * Gets the rank of a plugin feature. + * + * Returns: The rank of the feature + */ +guint +gst_plugin_feature_get_rank (GstPluginFeature *feature) +{ + g_return_val_if_fail (GST_IS_PLUGIN_FEATURE (feature), GST_RANK_NONE); + + return feature->rank; +} +/** + * gst_plugin_feature_set_name: + * @feature: a feature + * + * Gets the name of a pluginfeature. + * + * Returns: the name + */ +G_CONST_RETURN gchar * +gst_plugin_feature_get_name (GstPluginFeature *feature) +{ + g_return_val_if_fail (GST_IS_PLUGIN_FEATURE (feature), NULL); + + return feature->name; +} diff --git a/gst/gstpluginfeature.h b/gst/gstpluginfeature.h index a543f66..760214c 100644 --- a/gst/gstpluginfeature.h +++ b/gst/gstpluginfeature.h @@ -44,10 +44,10 @@ typedef struct _GstPluginFeatureClass GstPluginFeatureClass; struct _GstPluginFeature { GObject object; + /*< private >*/ gchar *name; - gint rank; + guint rank; - /* --- private --- */ gpointer manager; GST_OBJECT_PADDING @@ -79,8 +79,10 @@ void gst_plugin_feature_unload_thyself (GstPluginFeature *feature); gboolean gst_plugin_feature_type_name_filter (GstPluginFeature *feature, GstTypeNameData *data); -void gst_plugin_feature_set_rank (GstPluginFeature *feature, guint16 rank); +void gst_plugin_feature_set_rank (GstPluginFeature *feature, guint rank); void gst_plugin_feature_set_name (GstPluginFeature *feature, const gchar *name); +guint gst_plugin_feature_get_rank (GstPluginFeature *feature); +G_CONST_RETURN gchar *gst_plugin_feature_get_name (GstPluginFeature *feature); G_END_DECLS diff --git a/gst/gsturi.c b/gst/gsturi.c index e198998..17f60d0 100644 --- a/gst/gsturi.c +++ b/gst/gsturi.c @@ -20,17 +20,22 @@ * Boston, MA 02111-1307, USA. */ -#include "gst_private.h" +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif #include "gsturi.h" -#include "gstregistrypool.h" #include "gstinfo.h" +#include "gstregistrypool.h" +#include "gstmarshal.h" + +#include +#include -static void gst_uri_handler_class_init (GstURIHandlerClass *klass); -static void gst_uri_handler_init (GstURIHandler *factory); +GST_DEBUG_CATEGORY_STATIC (gst_uri_handler_debug); +#define GST_CAT_DEFAULT gst_uri_handler_debug -static GstPluginFeatureClass *parent_class = NULL; -/* static guint gst_uri_handler_signals[LAST_SIGNAL] = { 0 }; */ +static void gst_uri_handler_base_init (gpointer g_class); GType gst_uri_handler_get_type (void) @@ -39,222 +44,346 @@ gst_uri_handler_get_type (void) if (!urihandler_type) { static const GTypeInfo urihandler_info = { - sizeof (GstURIHandlerClass), + sizeof (GstURIHandlerInterface), + gst_uri_handler_base_init, NULL, NULL, - (GClassInitFunc) gst_uri_handler_class_init, NULL, NULL, - sizeof(GstURIHandler), 0, - (GInstanceInitFunc) gst_uri_handler_init, + 0, + NULL, NULL }; - urihandler_type = g_type_register_static (GST_TYPE_PLUGIN_FEATURE, - "GstURIHandler", &urihandler_info, 0); + urihandler_type = g_type_register_static (G_TYPE_INTERFACE, + "GstURIHandler", &urihandler_info, 0); + + GST_DEBUG_CATEGORY_INIT (gst_uri_handler_debug, "GST_URI", GST_DEBUG_BOLD, "handling of URIs"); } return urihandler_type; } - static void -gst_uri_handler_class_init (GstURIHandlerClass *klass) +gst_uri_handler_base_init (gpointer g_class) { - GObjectClass *gobject_class; - GstObjectClass *gstobject_class; - GstPluginFeatureClass *gstpluginfeature_class; - - gobject_class = (GObjectClass*)klass; - gstobject_class = (GstObjectClass*)klass; - gstpluginfeature_class = (GstPluginFeatureClass*) klass; + static gboolean initialized = FALSE; - parent_class = g_type_class_ref (GST_TYPE_PLUGIN_FEATURE); + if (!initialized) { + g_signal_new ("new_uri", GST_TYPE_URI_HANDLER, G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GstURIHandlerInterface, new_uri), NULL, NULL, + gst_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); + initialized = TRUE; + } } + static void -gst_uri_handler_init (GstURIHandler *factory) +gst_uri_protocol_check_internal (const gchar *uri, gchar **endptr) { + gchar *check = (gchar *) uri; + + g_assert (uri != NULL); + g_assert (endptr != NULL); + + if (isalpha (*check)) { + check++; + while (isalnum (*check)) check++; + } + + *endptr = check; } +/** + * gst_uri_protocol_is_valid: + * @protocol: string to check + * + * Tests if the given string is a valid protocol identifier. Protocols + * must consist of alphanumeric characters and not start with a number. + * + * Returns: TRUE if the string is a valid protocol identifier + */ +gboolean +gst_uri_protocol_is_valid (const gchar *protocol) +{ + gchar *endptr; + + g_return_val_if_fail (protocol != NULL, FALSE); + + gst_uri_protocol_check_internal (protocol, &endptr); + return *endptr == '\0' && endptr != protocol; +} /** - * gst_uri_handler_new: - * @name: the name of the feature - * @uri: the uri to register - * @longdesc: a description for this uri - * @element: an element that can handle the uri - * @property: the property on the element to set the uri + * gst_uri_is_valid: + * @protocol: string to check * - * Creates a plugin feature to register an element that can - * handle the given uri on the given property. + * Tests if the given string is a valid URI identifier. URIs start with a valid + * protocol followed by "://" and a string identifying the location. * - * Returns: the new urihandler + * Returns: TRUE if the string is a valid URI */ -GstURIHandler* -gst_uri_handler_new (const gchar *name, - const gchar *uri, const gchar *longdesc, - const gchar *element, gchar *property) +gboolean +gst_uri_is_valid (const gchar *uri) { - GstURIHandler *factory; + gchar *endptr; + + g_return_val_if_fail (uri != NULL, FALSE); + + gst_uri_protocol_check_internal (uri, &endptr); - g_return_val_if_fail (name != NULL, NULL); + return (*endptr == ':' && + *(endptr + 1) == '/' && + *(endptr + 2) == '/'); +} +/** + * gst_uri_get_protocol: + * @uri: URI to get protocol from + * + * Extracts the protocol out of a given valid URI. The returned string must be + * freed using g_free(). + * + * Returns: The protocol for this URI. + */ +gchar * +gst_uri_get_protocol (const gchar *uri) +{ + gchar *colon; + g_return_val_if_fail (uri != NULL, NULL); - g_return_val_if_fail (element != NULL, NULL); - g_return_val_if_fail (property != NULL, NULL); - - factory = gst_uri_handler_find (name); - - if (!factory) { - factory = GST_URI_HANDLER (g_object_new (GST_TYPE_URI_HANDLER, NULL)); - } + g_return_val_if_fail (gst_uri_is_valid (uri), NULL); - GST_PLUGIN_FEATURE_NAME (factory) = g_strdup (name); - factory->uri = g_strdup (uri); - factory->longdesc = g_strdup (longdesc); - factory->element = g_strdup (element); - factory->property = g_strdup (property); + colon = strstr (uri, "://"); - return factory; + return g_strndup (uri, colon - uri); } - /** - * gst_uri_handler_find: - * @name: the name of the urihandler to find + * gst_uri_get_location: + * @uri: URI to get the location from * - * Return the URIHandler with the given name. + * Extracts the location out of a given valid URI. So the protocol and "://" + * are stripped from the URI. The returned string must be freed using + * g_free(). * - * Returns: a GstURIHandler with the given name; + * Returns: The location for this URI. */ -GstURIHandler* -gst_uri_handler_find (const gchar *name) +gchar * +gst_uri_get_location (const gchar *uri) { - GstPluginFeature *feature; - - g_return_val_if_fail (name != NULL, NULL); + gchar *colon; + + g_return_val_if_fail (uri != NULL, NULL); + g_return_val_if_fail (gst_uri_is_valid (uri), NULL); - feature = gst_registry_pool_find_feature (name, GST_TYPE_URI_HANDLER); - if (feature) - return GST_URI_HANDLER (feature); + colon = strstr (uri, "://"); - return NULL; + return g_strdup (colon + 3); } - -/* - * this is a straight copy from glib 2.2 - * remove this function when glib 2.2 is sufficiently widespread and - * then change to using the regular g_str_has_prefix +/** + * gst_uri_construct: + * @protocol: protocol for URI + * @location: location for URI + * + * Constructs a URI for a given valid protocol and location. + * + * Returns: a new string for this URI */ +gchar * +gst_uri_construct (const gchar *protocol, const gchar *location) +{ + g_return_val_if_fail (gst_uri_protocol_is_valid (protocol), NULL); + g_return_val_if_fail (location != NULL, NULL); + + return g_strdup_printf ("%s://%s", protocol, location); +} +typedef struct{ + GstURIType type; + gchar * protocol; +} SearchEntry; static gboolean -g_str_has_prefix_glib22 (gchar *haystack, gchar *needle) +search_by_entry (GstPluginFeature *feature, gpointer search_entry) { - if (haystack == NULL && needle == NULL) { - return TRUE; - } - if (haystack == NULL || needle == NULL) { + gchar **protocols; + GstElementFactory *factory; + SearchEntry *entry = (SearchEntry *) search_entry; + + if (!GST_IS_ELEMENT_FACTORY (feature)) return FALSE; - } - if (strncmp (haystack, needle, strlen (needle)) == 0) { - return TRUE; + factory = GST_ELEMENT_FACTORY (feature); + + if (gst_element_factory_get_uri_type (factory) != entry->type) + return FALSE; + + protocols = gst_element_factory_get_uri_protocols (factory); + /* must be set when uri type is valid */ + g_assert (protocols); + while (*protocols != NULL) { + if (strcmp (*protocols, entry->protocol) == 0) + return TRUE; + protocols++; } return FALSE; } +static gint +sort_by_rank (gconstpointer a, gconstpointer b) +{ + GstPluginFeature *first = GST_PLUGIN_FEATURE (a); + GstPluginFeature *second = GST_PLUGIN_FEATURE (b); + return gst_plugin_feature_get_rank (second) - gst_plugin_feature_get_rank (first); +} /** - * gst_uri_handler_uri_filter: - * @feature: the feature to inspect - * @uri: the name of the uri to match + * gst_element_make_from_uri: + * @type: wether to create a source or a sink + * @uri: URI to create element for + * @elementname: optional name of created element * - * Check if the given pluginfeature is a uri hanler and that - * it can handle the given uri. - * - * Returns: TRUE if the feature can handle the uri. + * Creates an element for handling the given URI. + * + * Returns: a new element or NULL if none could be created */ -gboolean -gst_uri_handler_uri_filter (GstPluginFeature *feature, const gchar *uri) +GstElement * +gst_element_make_from_uri (const GstURIType type, const gchar *uri, const gchar *elementname) { - if (G_OBJECT_TYPE (feature) == GST_TYPE_URI_HANDLER) { - GstURIHandler *handler = GST_URI_HANDLER (feature); + GList *possibilities, *walk; + SearchEntry entry; + GstElement *ret = NULL; + + g_return_val_if_fail (GST_URI_TYPE_IS_VALID (type), NULL); + g_return_val_if_fail (gst_uri_is_valid (uri), NULL); + + entry.type = type; + entry.protocol = gst_uri_get_protocol (uri); + possibilities = gst_registry_pool_feature_filter (search_by_entry, FALSE, &entry); + g_free (entry.protocol); + + if (!possibilities) { + GST_DEBUG ("No %s for URI '%s'", type == GST_URI_SINK ? "sink" : "source", uri); + return NULL; + } - if (g_str_has_prefix_glib22 ((gchar *) uri, handler->uri)) { - return TRUE; + possibilities = g_list_sort (possibilities, sort_by_rank); + walk = possibilities; + while (walk) { + if ((ret = gst_element_factory_create (GST_ELEMENT_FACTORY (walk->data), + elementname)) != NULL) { + GstURIHandler *handler = GST_URI_HANDLER (ret); + if (gst_uri_handler_set_uri (handler, uri)) + break; + g_object_unref (ret); + ret = NULL; } } - return FALSE; -} + g_list_free (possibilities); + GST_LOG_OBJECT (ret, "created %s for URL '%s'", type == GST_URI_SINK ? "sink" : "source", uri); + return ret; +} /** - * gst_uri_handler_find_by_uri: - * @uri: the uri to find a handler for + * gst_uri_handler_get_uri_type: + * @handler: Handler to query type of * - * Find a URIHandler for the given uri. + * Gets the type of a URI handler * - * Returns: a GstURIHandler that can handle the given uri. + * Returns: the type of the URI handler */ -GstURIHandler* -gst_uri_handler_find_by_uri (const gchar *uri) +guint +gst_uri_handler_get_uri_type (GstURIHandler *handler) { - GList *walk; - GstURIHandler *result = NULL; + GstURIHandlerInterface *iface; + guint ret; - g_return_val_if_fail (uri != NULL, NULL); + g_return_val_if_fail (GST_IS_URI_HANDLER (handler), GST_URI_UNKNOWN); - walk = gst_registry_pool_feature_filter ( - (GstPluginFeatureFilter) gst_uri_handler_uri_filter, TRUE, (gpointer) uri); + iface = GST_URI_HANDLER_GET_INTERFACE (handler); + g_return_val_if_fail (iface != NULL, GST_URI_UNKNOWN); + g_return_val_if_fail (iface->get_type != NULL, GST_URI_UNKNOWN); + ret = iface->get_type (); + g_return_val_if_fail (GST_URI_TYPE_IS_VALID (ret), GST_URI_UNKNOWN); - if (walk) { - result = GST_URI_HANDLER (walk->data); - } - g_list_free (walk); - - return result; + return ret; } - /** - * gst_uri_handler_create: - * @handler: the uri handler to use - * @name: the name of the element + * gst_uri_handler_get_protocols: + * @handler: Handler to get protocols for * - * Create an element with the given name from the given handler. + * Gets the list of supported protocols for this handler. This list may not be + * modified. * - * Returns: a new element associated with the handler. + * Returns: the supported protocols */ -GstElement* -gst_uri_handler_create (GstURIHandler *handler, const gchar *name) +gchar ** +gst_uri_handler_get_protocols (GstURIHandler *handler) { - GstElement *element = NULL; - - g_return_val_if_fail (handler != NULL, NULL); + GstURIHandlerInterface *iface; + gchar **ret; + + g_return_val_if_fail (GST_IS_URI_HANDLER (handler), NULL); - element = gst_element_factory_make (handler->element, name); + iface = GST_URI_HANDLER_GET_INTERFACE (handler); + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (iface->get_protocols != NULL, NULL); + ret = iface->get_protocols (); + g_return_val_if_fail (ret != NULL, NULL); - return element; + return ret; } - /** - * gst_uri_handler_make_by_uri: - * @uri: the uri - * @name: the name of the element + * gst_uri_handler_get_uri: + * @handler: handler to query URI of * - * Create an element with the given name that can handle the given - * uri. This function will also use set the uri on the element. + * Gets the currently handled URI of the handler or NULL, if none is set. * - * Returns: a new element that can handle the uri. + * Returns: the URI */ -GstElement* -gst_uri_handler_make_by_uri (const gchar *uri, const gchar *name) +G_CONST_RETURN gchar * +gst_uri_handler_get_uri (GstURIHandler *handler) { - GstElement *element = NULL; - GstURIHandler *handler; - - g_return_val_if_fail (uri != NULL, NULL); - - handler = gst_uri_handler_find_by_uri (uri); - if (handler) { - element = gst_uri_handler_create (handler, name); - if (element) { - g_object_set (G_OBJECT (element), handler->property, uri, NULL); - } - } - return element; + GstURIHandlerInterface *iface; + const gchar *ret; + + g_return_val_if_fail (GST_IS_URI_HANDLER (handler), NULL); + + iface = GST_URI_HANDLER_GET_INTERFACE (handler); + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (iface->get_uri != NULL, NULL); + ret = iface->get_uri (handler); + if (ret != NULL) + g_return_val_if_fail (gst_uri_is_valid (ret), NULL); + + return ret; } +/** + * gst_uri_handler_set_uri: + * @handler: handler to set URI of + * @uri: URI to set + * + * Tries to set the URI of the given handler and returns TRUE if it succeeded. + * + * Returns: TRUE, if the URI was set successfully + */ +gboolean +gst_uri_handler_set_uri (GstURIHandler *handler, const gchar *uri) +{ + GstURIHandlerInterface *iface; + + g_return_val_if_fail (GST_IS_URI_HANDLER (handler), FALSE); + g_return_val_if_fail (gst_uri_is_valid (uri), FALSE); + iface = GST_URI_HANDLER_GET_INTERFACE (handler); + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->set_uri != NULL, FALSE); + return iface->set_uri (handler, uri); +} +/** + * gst_uri_handler_new_uri: + * @handler: handler with a new URI + * @uri: new URI or NULL if it was unset + * + * Emits the new-uri event for a given handler, when that handler has a new URI. + * This function should only be called by URI handlers themselves. + */ +void +gst_uri_handler_new_uri (GstURIHandler *handler, const gchar *uri) +{ + g_return_if_fail (GST_IS_URI_HANDLER (handler)); - + g_signal_emit_by_name (handler, "new-uri", uri); +} diff --git a/gst/gsturi.h b/gst/gsturi.h index 2662b47..0d0a109 100644 --- a/gst/gsturi.h +++ b/gst/gsturi.h @@ -30,50 +30,76 @@ G_BEGIN_DECLS +typedef enum { + GST_URI_UNKNOWN, + GST_URI_SINK, + GST_URI_SRC +} GstURIType; + +#define GST_URI_TYPE_IS_VALID(type) ((type) == GST_URI_SRC || (type) == GST_URI_SINK) + /* uri handler functions */ #define GST_TYPE_URI_HANDLER (gst_uri_handler_get_type ()) #define GST_URI_HANDLER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_URI_HANDLER, GstURIHandler)) #define GST_IS_URI_HANDLER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_URI_HANDLER)) -#define GST_URI_HANDLER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_URI_HANDLER, GstURIHandlerClass)) -#define GST_IS_URI_HANDLER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_URI_HANDLER)) -#define GST_URI_HANDLER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_URI_HANDLER, GstURIHandlerClass)) +#define GST_URI_HANDLER_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GST_TYPE_URI_HANDLER, GstURIHandlerInterface)) +#define GST_URI_HANDLER_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), GST_TYPE_URI_HANDLER, GstURIHandler)) typedef struct _GstURIHandler GstURIHandler; -typedef struct _GstURIHandlerClass GstURIHandlerClass; +typedef struct _GstURIHandlerInterface GstURIHandlerInterface; -struct _GstURIHandler { - GstPluginFeature feature; +struct _GstURIHandlerInterface { + GTypeInterface parent; - /* --- public ---- */ - gchar *uri; /* The uri that is described */ - gchar *longdesc; /* description of the uri */ - gchar *element; /* The element that can handle this uri */ - gchar *property; /* The property on the element to set the uri */ + /* signals */ + void (* new_uri) (GstURIHandler * handler, + const gchar * uri); + /* idea for the future ? + gboolean (* require_password) (GstURIHandler * handler, + gchar ** username, + gchar ** password); + */ - GST_OBJECT_PADDING -}; + /* vtable */ + + /* querying capabilities */ + GstURIType (* get_type) (void); + gchar ** (* get_protocols) (void); -struct _GstURIHandlerClass { - GstPluginFeatureClass parent; + /* using the interface */ + G_CONST_RETURN gchar *(* get_uri) (GstURIHandler * handler); + gboolean (* set_uri) (GstURIHandler * handler, + const gchar * uri); + + /* we might want to add functions here to query features, someone with gnome-vfs knowledge go ahead */ GST_CLASS_PADDING }; -GType gst_uri_handler_get_type (void); +/* general URI functions */ -GstURIHandler* gst_uri_handler_new (const gchar *name, - const gchar *uri, const gchar *longdesc, - const gchar *element, gchar *property); +gboolean gst_uri_protocol_is_valid (const gchar * protocol); +gboolean gst_uri_is_valid (const gchar * uri); +gchar * gst_uri_get_protocol (const gchar * uri); +gchar * gst_uri_get_location (const gchar * uri); +gchar * gst_uri_construct (const gchar * protocol, + const gchar * location); -GstURIHandler* gst_uri_handler_find (const gchar *name); -GstURIHandler* gst_uri_handler_find_by_uri (const gchar *uri); +GstElement * gst_element_make_from_uri (const GstURIType type, + const gchar * uri, + const gchar * elementname); -GstElement* gst_uri_handler_create (GstURIHandler *handler, const gchar *name); -GstElement* gst_uri_handler_make_by_uri (const gchar *uri, const gchar *name); +/* accessing the interface */ +GType gst_uri_handler_get_type (void); -/* filters */ -gboolean gst_uri_handler_uri_filter (GstPluginFeature *feature, const gchar *uri); +guint gst_uri_handler_get_uri_type (GstURIHandler * handler); +gchar ** gst_uri_handler_get_protocols (GstURIHandler * handler); +G_CONST_RETURN gchar * gst_uri_handler_get_uri (GstURIHandler * handler); +gboolean gst_uri_handler_set_uri (GstURIHandler * handler, + const gchar * uri); +void gst_uri_handler_new_uri (GstURIHandler * handler, + const gchar * uri); G_END_DECLS -#endif /* __GST_URI_H */ +#endif /* __GST_URI_H__ */ diff --git a/gst/parse/grammar.y b/gst/parse/grammar.y index 61f3196..5de57fe 100644 --- a/gst/parse/grammar.y +++ b/gst/parse/grammar.y @@ -7,8 +7,10 @@ #include "../gst_private.h" +#include "../gstconfig.h" #include "../gstparse.h" #include "../gstinfo.h" +#include "../gsturi.h" #include "types.h" #define YYERROR_VERBOSE 1 @@ -97,7 +99,7 @@ typedef struct { } \ }G_STMT_END #define ERROR(type, ...) SET_ERROR (((graph_t *) graph)->error, (type), __VA_ARGS__ ) -#ifdef GST_DEBUG_ENABLED +#ifndef GST_DISABLE_GST_DEBUG # define YYDEBUG 1 /* bison 1.35 calls this macro with side effects, we need to make sure the side effects work - crappy bison @@ -122,7 +124,7 @@ typedef struct { } \ }G_STMT_END #define ERROR(type, args...) SET_ERROR (((graph_t *) graph)->error, (type), ## args ) -#ifdef GST_DEBUG_ENABLED +#ifndef GST_DISABLE_GST_DEBUG # define YYDEBUG 1 /* bison 1.35 calls this macro with side effects, we need to make sure the side effects work - crappy bison @@ -147,7 +149,7 @@ typedef struct { } \ }G_STMT_END #define ERROR(type, ...) SET_ERROR (((graph_t *) graph)->error, (type), "error while parsing") -#ifdef GST_DEBUG_ENABLED +#ifndef GST_DISABLE_GST_DEBUG # define YYDEBUG 1 #endif @@ -210,23 +212,6 @@ typedef struct { MAKE_LINK (link, NULL, _src, pads, NULL, NULL, NULL); \ }G_STMT_END -static inline void gst_parse_unescape (gchar *str) -{ - gchar *walk; - - g_return_if_fail (str != NULL); - - walk = str; - - while (*walk) { - if (*walk == '\\') - walk++; - *str = *walk; - str++; - walk++; - } - *str = '\0'; -} static void gst_parse_element_set (gchar *value, GstElement *element, graph_t *graph) { @@ -532,6 +517,7 @@ static int yyerror (const char *s); graph_t *g; } +%token PARSE_URL %token IDENTIFIER %left REF PADREF BINREF %token ASSIGNMENT @@ -542,7 +528,7 @@ static int yyerror (const char *s); %type reference %type linkpart link %type

linklist -%type element +%type element %type

padlist pads assignments %left '{' '}' '(' ')' @@ -566,12 +552,11 @@ element: IDENTIFIER { $$ = gst_element_factory_make ($1, NULL); $$ = $1; } ; - assignments: /* NOP */ { $$ = NULL; } | assignments ASSIGNMENT { $$ = g_slist_prepend ($1, $2); } ; -bin: '{' assignments chain '}' { GST_BIN_MAKE ($$, "thread", $3, $2); } - | '(' assignments chain ')' { GST_BIN_MAKE ($$, "bin", $3, $2); } +bin: '{' assignments chain '}' { GST_BIN_MAKE ($$, "thread", $3, $2); } + | '(' assignments chain ')' { GST_BIN_MAKE ($$, "bin", $3, $2); } | BINREF assignments chain ')' { GST_BIN_MAKE ($$, $1, $3, $2); gst_parse_strfree ($1); } @@ -670,21 +655,6 @@ chain: element { $$ = gst_parse_chain_new (); gst_parse_chain_free ($2); $$ = $1; } - | link chain { if ($2->front) { - if (!$2->front->src_name) { - ERROR (GST_PARSE_ERROR_LINK, "link without source element"); - gst_parse_free_link ($2->front); - } else { - ((graph_t *) graph)->links = g_slist_prepend (((graph_t *) graph)->links, $2->front); - } - } - if (!$1->sink_name) { - $1->sink = $2->first; - } - $2->front = $1; - $$ = $2; - } - | chain linklist { GSList *walk; if ($1->back) { $2 = g_slist_prepend ($2, $1->back); @@ -716,8 +686,60 @@ chain: element { $$ = gst_parse_chain_new (); $$ = $1; } | chain error { $$ = $1; } + | link chain { if ($2->front) { + if (!$2->front->src_name) { + ERROR (GST_PARSE_ERROR_LINK, "link without source element"); + gst_parse_free_link ($2->front); + } else { + ((graph_t *) graph)->links = g_slist_prepend (((graph_t *) graph)->links, $2->front); + } + } + if (!$1->sink_name) { + $1->sink = $2->first; + } + $2->front = $1; + $$ = $2; + } + | PARSE_URL chain { $$ = $2; + if ($$->front) { + GstElement *element = + gst_element_make_from_uri (GST_URI_SRC, $1, NULL); + if (!element) { + ERROR (GST_PARSE_ERROR_NO_SUCH_ELEMENT, + "No source element for URI \"%s\"", $1); + } else { + $$->front->src = element; + ((graph_t *) graph)->links = g_slist_prepend ( + ((graph_t *) graph)->links, $$->front); + $$->front = NULL; + $$->elements = g_slist_prepend ($$->elements, element); + } + } else { + ERROR (GST_PARSE_ERROR_LINK, + "No element to link URI \"%s\" to", $1); + } + g_free ($1); + } + | link PARSE_URL { GstElement *element = + gst_element_make_from_uri (GST_URI_SINK, $2, NULL); + if (!element) { + ERROR (GST_PARSE_ERROR_NO_SUCH_ELEMENT, + "No sink element for URI \"%s\"", $2); + YYERROR; + } else if ($1->sink_name || $1->sink_pads) { + ERROR (GST_PARSE_ERROR_LINK, + "could not link sink element for URI \"%s\"", $2); + YYERROR; + } else { + $$ = gst_parse_chain_new (); + $$->first = $$->last = element; + $$->front = $1; + $$->front->sink = element; + $$->elements = g_slist_prepend (NULL, element); + } + g_free ($2); + } ; - graph: /* NOP */ { ERROR (GST_PARSE_ERROR_EMPTY, "Empty pipeline not allowed"); $$ = (graph_t *) graph; } @@ -785,7 +807,7 @@ _gst_parse_launch (const gchar *str, GError **error) dstr = g_strdup (str); _gst_parse_yy_scan_string (dstr); -#ifdef GST_DEBUG_ENABLED +#ifndef GST_DISABLE_GST_DEBUG yydebug = 1; #endif diff --git a/gst/parse/parse.l b/gst/parse/parse.l index 021d13b..95fb0c5 100644 --- a/gst/parse/parse.l +++ b/gst/parse/parse.l @@ -7,6 +7,7 @@ #include "types.h" #include "../gstinfo.h" +#include "../gsturi.h" #include "grammar.tab.h" #ifdef G_HAVE_ISO_VARARGS @@ -29,6 +30,9 @@ _string {_char}+|("\""([^\"]|"\\\"")*"\"")|("'"([^']|"\\\"")*"'") _comma [[:space:]]*","[[:space:]]* _assign [[:space:]]*"="[[:space:]]* +_protocol [[:alpha:]][[:alnum:]+-\.]* +_url {_protocol}"://"{_string}|["."{_identifier}]?"/"{_string} + /* we must do this here, because nearly everything matches a {_string} */ _assignment {_identifier}{_assign}{_string} @@ -106,6 +110,17 @@ _link ("!"[[:space:]]*{_caps}([[:space:]]*";"[[:space:]]*{_caps})*[[:space:]]*"! BEGIN (INITIAL); return LINK; } +{_url} { + PRINT ("URL: %s\n", yytext); + if (gst_uri_is_valid (yytext)) { + lvalp->s = g_strdup (yytext); + } else { + lvalp->s = gst_uri_construct ("file", yytext); + } + gst_parse_unescape (lvalp->s); + BEGIN (INITIAL); + return PARSE_URL; +} {_operator} { PRINT ("OPERATOR: [%s]\n", yytext); return *yytext; } diff --git a/gst/parse/types.h b/gst/parse/types.h index ffc7643..3fa3588 100644 --- a/gst/parse/types.h +++ b/gst/parse/types.h @@ -66,4 +66,23 @@ void __gst_parse_chain_free (chain_t *data); # define gst_parse_chain_free g_free #endif /* __GST_PARSE_TRACE */ +static inline void +gst_parse_unescape (gchar *str) +{ + gchar *walk; + + g_return_if_fail (str != NULL); + + walk = str; + + while (*walk) { + if (*walk == '\\') + walk++; + *str = *walk; + str++; + walk++; + } + *str = '\0'; +} + #endif /* __GST_PARSE_TYPES_H__ */ diff --git a/gst/registries/gstxmlregistry.c b/gst/registries/gstxmlregistry.c index 76f2679..dcf10b3 100644 --- a/gst/registries/gstxmlregistry.c +++ b/gst/registries/gstxmlregistry.c @@ -680,6 +680,26 @@ gst_xml_registry_parse_plugin (GMarkupParseContext *context, const gchar *tag, c return TRUE; } +static void +add_to_char_array (gchar ***array, gchar *value) +{ + gchar **new; + gchar **old = *array; + gint i = 0; + + /* expensive, but cycles are cheap... */ + if (old) + while (old[i]) i++; + new = g_new0 (gchar *, i + 2); + new[i] = value; + while (i > 0) { + i--; + new[i] = old[i]; + } + g_free (old); + *array = new; +} + static gboolean gst_xml_registry_parse_element_factory (GMarkupParseContext *context, const gchar *tag, const gchar *text, gsize text_len, GstXMLRegistry *registry, GError **error) @@ -715,6 +735,14 @@ gst_xml_registry_parse_element_factory (GMarkupParseContext *context, const gcha if (ret == text + text_len) { gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE (factory), rank); } + } else if (!strcmp (tag, "uri_type")) { + if (strncasecmp (text, "sink", 4) == 0) { + factory->uri_type = GST_URI_SINK; + } else if (strncasecmp (text, "source", 5) == 0) { + factory->uri_type = GST_URI_SRC; + } + } else if (!strcmp (tag, "uri_protocol")) { + add_to_char_array (&factory->uri_protocols, g_strndup (text, text_len)); } else if (!strcmp(tag, "interface")) { gchar *tmp = g_strndup (text, text_len); @@ -747,21 +775,7 @@ gst_xml_registry_parse_type_find_factory (GMarkupParseContext *context, const gc factory->caps = g_strndup (text, text_len); }*/ else if (!strcmp(tag, "extension")) { - gchar **new; - gchar **old = factory->extensions; - gint i = 0; - - /* expensive, but cycles are cheap... */ - if (old) - while (old[i]) i++; - new = g_new0 (gchar *, i + 2); - new[i] = g_strndup (text, text_len); - while (i > 0) { - i--; - new[i] = old[i]; - } - g_free (old); - factory->extensions = new; + add_to_char_array (&factory->extensions, g_strndup (text, text_len)); } return TRUE; @@ -813,30 +827,6 @@ gst_xml_registry_parse_index_factory (GMarkupParseContext *context, const gchar } static gboolean -gst_xml_registry_parse_uri_handler (GMarkupParseContext *context, const gchar *tag, const gchar *text, - gsize text_len, GstXMLRegistry *registry, GError **error) -{ - GstURIHandler *handler = GST_URI_HANDLER (registry->current_feature); - - if (!strcmp (tag, "name")) { - registry->current_feature->name = g_strndup (text, text_len); - } - else if (!strcmp (tag, "uri")) { - handler->uri = g_strndup (text, text_len); - } - else if (!strcmp (tag, "longdesc")) { - handler->longdesc = g_strndup (text, text_len); - } - else if (!strcmp (tag, "element")) { - handler->element = g_strndup (text, text_len); - } - else if (!strcmp (tag, "property")) { - handler->property = g_strndup (text, text_len); - } - return TRUE; -} - -static gboolean gst_xml_registry_parse_padtemplate (GMarkupParseContext *context, const gchar *tag, const gchar *text, gsize text_len, GstXMLRegistry *registry, GError **error) { @@ -953,9 +943,6 @@ gst_xml_registry_start_element (GMarkupParseContext *context, else if (GST_IS_INDEX_FACTORY (feature)) { xmlregistry->parser = gst_xml_registry_parse_index_factory; } - else if (GST_IS_URI_HANDLER (feature)) { - xmlregistry->parser = gst_xml_registry_parse_uri_handler; - } else { g_warning ("unkown feature type"); } @@ -1495,6 +1482,18 @@ gst_xml_registry_save_feature (GstXMLRegistry *xmlregistry, GstPluginFeature *fe PUT_ESCAPED ("interface", (gchar *) walk->data); walk = g_list_next (walk); } + + if (GST_URI_TYPE_IS_VALID (factory->uri_type)) { + gchar **protocol; + + PUT_ESCAPED ("uri_type", factory->uri_type == GST_URI_SINK ? "sink" : "source"); + g_assert (factory->uri_protocols); + protocol = factory->uri_protocols; + while (*protocol) { + PUT_ESCAPED ("uri_protocol", *protocol); + protocol++; + } + } } else if (GST_IS_TYPE_FIND_FACTORY (feature)) { GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (feature); @@ -1521,18 +1520,9 @@ gst_xml_registry_save_feature (GstXMLRegistry *xmlregistry, GstPluginFeature *fe else if (GST_IS_INDEX_FACTORY (feature)) { PUT_ESCAPED ("longdesc", GST_INDEX_FACTORY (feature)->longdesc); } - else if (GST_IS_URI_HANDLER (feature)) { - GstURIHandler *handler = GST_URI_HANDLER (feature); - - PUT_ESCAPED ("uri", handler->uri); - PUT_ESCAPED ("longdesc", handler->longdesc); - PUT_ESCAPED ("element", handler->element); - PUT_ESCAPED ("property", handler->property); - } return TRUE; } - static gboolean gst_xml_registry_save_plugin (GstXMLRegistry *xmlregistry, GstPlugin *plugin) { diff --git a/plugins/elements/gstfakesink.c b/plugins/elements/gstfakesink.c index 4f23d8b..1e82e51 100644 --- a/plugins/elements/gstfakesink.c +++ b/plugins/elements/gstfakesink.c @@ -173,7 +173,7 @@ gst_fakesink_class_init (GstFakeSinkClass *klass) gst_fakesink_signals[SIGNAL_HANDOFF] = g_signal_new ("handoff", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstFakeSinkClass, handoff), NULL, NULL, - gst_marshal_VOID__POINTER_OBJECT, G_TYPE_NONE, 1, + gst_marshal_VOID__POINTER_OBJECT, G_TYPE_NONE, 2, GST_TYPE_BUFFER, GST_TYPE_PAD); gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_fakesink_set_property); diff --git a/plugins/elements/gstfilesink.c b/plugins/elements/gstfilesink.c index 5c4c81c..fef5d7e 100644 --- a/plugins/elements/gstfilesink.c +++ b/plugins/elements/gstfilesink.c @@ -69,6 +69,7 @@ GST_PAD_FORMATS_FUNCTION (gst_filesink_get_formats, static void gst_filesink_base_init (gpointer g_class); static void gst_filesink_class_init (GstFileSinkClass *klass); static void gst_filesink_init (GstFileSink *filesink); +static void gst_filesink_dispose (GObject *object); static void gst_filesink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); @@ -83,6 +84,8 @@ static gboolean gst_filesink_pad_query (GstPad *pad, GstQueryType type, GstFormat *format, gint64 *value); static void gst_filesink_chain (GstPad *pad,GstData *_data); +static void gst_filesink_uri_handler_init (gpointer g_iface, gpointer iface_data); + static GstElementStateReturn gst_filesink_change_state (GstElement *element); static GstElementClass *parent_class = NULL; @@ -105,7 +108,14 @@ gst_filesink_get_type (void) 0, (GInstanceInitFunc)gst_filesink_init, }; + static const GInterfaceInfo urihandler_info = { + gst_filesink_uri_handler_init, + NULL, + NULL + }; filesink_type = g_type_register_static (GST_TYPE_ELEMENT, "GstFileSink", &filesink_info, 0); + + g_type_add_interface_static (filesink_type, GST_TYPE_URI_HANDLER, &urihandler_info); GST_DEBUG_CATEGORY_INIT (gst_filesink_debug, "filesink", 0, "filesink element"); } @@ -138,8 +148,8 @@ gst_filesink_class_init (GstFileSinkClass *klass) gobject_class->set_property = gst_filesink_set_property; gobject_class->get_property = gst_filesink_get_property; + gobject_class->dispose = gst_filesink_dispose; } - static void gst_filesink_init (GstFileSink *filesink) { @@ -158,7 +168,38 @@ gst_filesink_init (GstFileSink *filesink) filesink->filename = NULL; filesink->file = NULL; } +static void +gst_filesink_dispose (GObject *object) +{ + GstFileSink *sink = GST_FILESINK (object); + G_OBJECT_CLASS (parent_class)->dispose (object); + + g_free (sink->uri); + sink->uri = NULL; + g_free (sink->filename); + sink->filename = NULL; +} +static gboolean +gst_filesink_set_location (GstFileSink *sink, const gchar *location) +{ + /* the element must be stopped or paused in order to do this */ + if (GST_STATE (sink) > GST_STATE_PAUSED) + return FALSE; + if (GST_STATE (sink) == GST_STATE_PAUSED && + GST_FLAG_IS_SET (sink, GST_FILESINK_OPEN)) + return FALSE; + + g_free (sink->filename); + g_free (sink->uri); + sink->filename = g_strdup (location); + sink->uri = gst_uri_construct ("file", location); + + if (GST_STATE (sink) == GST_STATE_PAUSED) + gst_filesink_open_file (sink); + + return TRUE; +} static void gst_filesink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { @@ -169,16 +210,7 @@ gst_filesink_set_property (GObject *object, guint prop_id, const GValue *value, switch (prop_id) { case ARG_LOCATION: - /* the element must be stopped or paused in order to do this */ - g_return_if_fail (GST_STATE (sink) <= GST_STATE_PAUSED); - if (GST_STATE (sink) == GST_STATE_PAUSED) - g_return_if_fail (!GST_FLAG_IS_SET (sink, GST_FILESINK_OPEN)); - - if (sink->filename) - g_free (sink->filename); - sink->filename = g_strdup (g_value_get_string (value)); - if (GST_STATE (sink) == GST_STATE_PAUSED) - gst_filesink_open_file (sink); + gst_filesink_set_location (sink, g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -435,3 +467,54 @@ gst_filesink_change_state (GstElement *element) return GST_STATE_SUCCESS; } + +/*** GSTURIHANDLER INTERFACE *************************************************/ + +static guint +gst_filesink_uri_get_type (void) +{ + return GST_URI_SINK; +} +static gchar ** +gst_filesink_uri_get_protocols(void) +{ + static gchar *protocols[] = {"file", NULL}; + return protocols; +} +static const gchar * +gst_filesink_uri_get_uri (GstURIHandler *handler) +{ + GstFileSink *sink = GST_FILESINK (handler); + + return sink->uri; +} +static gboolean +gst_filesink_uri_set_uri (GstURIHandler *handler, const gchar *uri) +{ + gchar *protocol, *location; + gboolean ret; + GstFileSink *sink = GST_FILESINK (handler); + + protocol = gst_uri_get_protocol (uri); + if (strcmp (protocol, "file") != 0) { + g_free (protocol); + return FALSE; + } + g_free (protocol); + location = gst_uri_get_location (uri); + ret = gst_filesink_set_location (sink, location); + g_free (location); + + return ret; +} + +static void +gst_filesink_uri_handler_init (gpointer g_iface, gpointer iface_data) +{ + GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface; + + iface->get_type = gst_filesink_uri_get_type; + iface->get_protocols = gst_filesink_uri_get_protocols; + iface->get_uri = gst_filesink_uri_get_uri; + iface->set_uri = gst_filesink_uri_set_uri; +} diff --git a/plugins/elements/gstfilesink.h b/plugins/elements/gstfilesink.h index b72551b..bd85a1d 100644 --- a/plugins/elements/gstfilesink.h +++ b/plugins/elements/gstfilesink.h @@ -53,6 +53,7 @@ struct _GstFileSink { GstElement element; gchar *filename; + gchar *uri; FILE *file; guint64 data_written; diff --git a/plugins/elements/gstfilesrc.c b/plugins/elements/gstfilesrc.c index 275d730..e1d4bff 100644 --- a/plugins/elements/gstfilesrc.c +++ b/plugins/elements/gstfilesrc.c @@ -136,6 +136,8 @@ static gboolean gst_filesrc_srcpad_query (GstPad *pad, GstQueryType type, static GstElementStateReturn gst_filesrc_change_state (GstElement *element); +static void gst_filesrc_uri_handler_init (gpointer g_iface, gpointer iface_data); + static GstElementClass *parent_class = NULL; /*static guint gst_filesrc_signals[LAST_SIGNAL] = { 0 };*/ @@ -157,8 +159,15 @@ gst_filesrc_get_type(void) 0, (GInstanceInitFunc)gst_filesrc_init, }; + static const GInterfaceInfo urihandler_info = { + gst_filesrc_uri_handler_init, + NULL, + NULL + }; filesrc_type = g_type_register_static (GST_TYPE_ELEMENT, "GstFileSrc", &filesrc_info, 0); - + + g_type_add_interface_static (filesrc_type, GST_TYPE_URI_HANDLER, &urihandler_info); + GST_DEBUG_CATEGORY_INIT (gst_filesrc_debug, "filesrc", 0, "filesrc element"); } return filesrc_type; @@ -263,8 +272,32 @@ gst_filesrc_dispose (GObject *object) g_mutex_free (src->map_regions_lock); if (src->filename) g_free (src->filename); + if (src->uri) + g_free (src->uri); } +static gboolean +gst_filesrc_set_location (GstFileSrc *src, const gchar *location) +{ + /* the element must be stopped in order to do this */ + if (GST_STATE (src) == GST_STATE_PLAYING) + return FALSE; + + if (src->filename) g_free (src->filename); + if (src->uri) g_free (src->uri); + /* clear the filename if we get a NULL (is that possible?) */ + if (location == NULL) { + src->filename = NULL; + src->uri = NULL; + } else { + src->filename = g_strdup (location); + src->uri = gst_uri_construct ("file", src->filename); + } + g_object_notify (G_OBJECT (src), "location"); + gst_uri_handler_new_uri (GST_URI_HANDLER (src), src->uri); + + return TRUE; +} static void gst_filesrc_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) @@ -278,19 +311,7 @@ gst_filesrc_set_property (GObject *object, guint prop_id, const GValue *value, G switch (prop_id) { case ARG_LOCATION: - /* the element must be stopped in order to do this */ - g_return_if_fail (GST_STATE (src) < GST_STATE_PLAYING); - - if (src->filename) g_free (src->filename); - /* clear the filename if we get a NULL (is that possible?) */ - if (g_value_get_string (value) == NULL) { - gst_element_set_state (GST_ELEMENT (object), GST_STATE_NULL); - src->filename = NULL; - /* otherwise set the new filename */ - } else { - src->filename = g_strdup (g_value_get_string (value)); - } - g_object_notify (G_OBJECT (src), "location"); + gst_filesrc_set_location (src, g_value_get_string (value)); break; case ARG_BLOCKSIZE: src->block_size = g_value_get_ulong (value); @@ -918,3 +939,55 @@ error: gst_event_unref (event); return FALSE; } + +/*** GSTURIHANDLER INTERFACE *************************************************/ + +static guint +gst_filesrc_uri_get_type (void) +{ + return GST_URI_SRC; +} +static gchar ** +gst_filesrc_uri_get_protocols(void) +{ + static gchar *protocols[] = {"file", NULL}; + return protocols; +} +static const gchar * +gst_filesrc_uri_get_uri (GstURIHandler *handler) +{ + GstFileSrc *src = GST_FILESRC (handler); + + return src->uri; +} +static gboolean +gst_filesrc_uri_set_uri (GstURIHandler *handler, const gchar *uri) +{ + gchar *protocol, *location; + gboolean ret; + GstFileSrc *src = GST_FILESRC (handler); + + protocol = gst_uri_get_protocol (uri); + if (strcmp (protocol, "file") != 0) { + g_free (protocol); + return FALSE; + } + g_free (protocol); + location = gst_uri_get_location (uri); + ret = gst_filesrc_set_location (src, location); + g_free (location); + + return ret; +} + +static void +gst_filesrc_uri_handler_init (gpointer g_iface, gpointer iface_data) +{ + GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface; + + iface->get_type = gst_filesrc_uri_get_type; + iface->get_protocols = gst_filesrc_uri_get_protocols; + iface->get_uri = gst_filesrc_uri_get_uri; + iface->set_uri = gst_filesrc_uri_set_uri; +} + diff --git a/plugins/elements/gstfilesrc.h b/plugins/elements/gstfilesrc.h index 23b6dde..1f584be 100644 --- a/plugins/elements/gstfilesrc.h +++ b/plugins/elements/gstfilesrc.h @@ -58,6 +58,7 @@ struct _GstFileSrc { guint pagesize; /* system page size*/ gchar *filename; /* filename */ + gchar *uri; /* caching the URI */ gint fd; /* open file descriptor*/ off_t filelen; /* what's the file length?*/ diff --git a/tools/gst-inspect.c b/tools/gst-inspect.c index 366ea8a..3192922 100644 --- a/tools/gst-inspect.c +++ b/tools/gst-inspect.c @@ -874,16 +874,6 @@ print_element_list (void) g_print ("%s: %s: %s\n", plugin->desc.name, GST_PLUGIN_FEATURE_NAME (factory), factory->longdesc); } -#ifndef GST_DISABLE_URI - else if (GST_IS_URI_HANDLER (feature)) { - GstURIHandler *handler; - - handler = GST_URI_HANDLER (feature); - g_print ("%s: %s: \"%s\" (%s) element \"%s\" property \"%s\"\n", - plugin->desc.name, GST_PLUGIN_FEATURE_NAME (handler), handler->uri, - handler->longdesc, handler->element, handler->property); - } -#endif else { g_print ("%s: %s (%s)\n", plugin->desc.name, GST_PLUGIN_FEATURE_NAME (feature), diff --git a/tools/gst-xmlinspect.c b/tools/gst-xmlinspect.c index 89d1c03..3467e58 100644 --- a/tools/gst-xmlinspect.c +++ b/tools/gst-xmlinspect.c @@ -867,16 +867,6 @@ print_element_list (void) g_print ("%s: %s: %s\n", plugin->desc.name, GST_PLUGIN_FEATURE_NAME (factory), factory->longdesc); } -#ifndef GST_DISABLE_URI - else if (GST_IS_URI_HANDLER (feature)) { - GstURIHandler *handler; - - handler = GST_URI_HANDLER (feature); - g_print ("%s: %s: \"%s\" (%s) element \"%s\" property \"%s\"\n", - plugin->desc.name, GST_PLUGIN_FEATURE_NAME (handler), handler->uri, - handler->longdesc, handler->element, handler->property); - } -#endif else { g_print ("%s: %s (%s)\n", plugin->desc.name, GST_PLUGIN_FEATURE_NAME (feature), -- 2.7.4