From 4d16d25b47406da1f476e70d5b0bfab6ec1c0ee0 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 25 Jan 2010 11:12:47 +0100 Subject: [PATCH] dataurisrc: Add data: URI source element This is slightly based on the WebKit data: URI source but supports more parts of RFC 2397. Fixes bug #596885. --- plugins/elements/gstdataurisrc.c | 427 +++++++++++++++++++++++++++++++++++++++ plugins/elements/gstdataurisrc.h | 61 ++++++ 2 files changed, 488 insertions(+) create mode 100644 plugins/elements/gstdataurisrc.c create mode 100644 plugins/elements/gstdataurisrc.h diff --git a/plugins/elements/gstdataurisrc.c b/plugins/elements/gstdataurisrc.c new file mode 100644 index 0000000..aaebe87 --- /dev/null +++ b/plugins/elements/gstdataurisrc.c @@ -0,0 +1,427 @@ +/* GStreamer + * + * Copyright (C) 2009 Igalia S.L + * Copyright (C) 2009 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstdataurisrc.h" + +#include +#include + +GST_DEBUG_CATEGORY (data_uri_src_debug); +#define GST_CAT_DEFAULT (data_uri_src_debug) + +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +enum +{ + PROP_0, + PROP_URI, +}; + +static void gst_data_uri_src_finalize (GObject * object); +static void gst_data_uri_src_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_data_uri_src_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + +static GstCaps *gst_data_uri_src_get_caps (GstBaseSrc * src); +static gboolean gst_data_uri_src_get_size (GstBaseSrc * src, guint64 * size); +static gboolean gst_data_uri_src_is_seekable (GstBaseSrc * src); +static GstFlowReturn gst_data_uri_src_create (GstBaseSrc * src, guint64 offset, + guint size, GstBuffer ** buf); +static gboolean gst_data_uri_src_check_get_range (GstBaseSrc * src); + +static void gst_data_uri_src_handler_init (gpointer g_iface, + gpointer iface_data); +static GstURIType gst_data_uri_src_get_uri_type (void); +static gchar **gst_data_uri_src_get_protocols (void); +static const gchar *gst_data_uri_src_get_uri (GstURIHandler * handler); +static gboolean gst_data_uri_src_set_uri (GstURIHandler * handler, + const gchar * uri); + +static void +_do_init (GType gtype) +{ + static const GInterfaceInfo urihandler_info = { + gst_data_uri_src_handler_init, + 0, 0 + }; + + GST_DEBUG_CATEGORY_INIT (data_uri_src_debug, "dataurisrc", 0, + "data: URI source"); + g_type_add_interface_static (gtype, GST_TYPE_URI_HANDLER, &urihandler_info); +} + +GST_BOILERPLATE_FULL (GstDataURISrc, gst_data_uri_src, GstBaseSrc, + GST_TYPE_BASE_SRC, _do_init); + +static void +gst_data_uri_src_base_init (gpointer klass) +{ + GstElementClass *element_class = (GstElementClass *) (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_template)); + gst_element_class_set_details_simple (element_class, + "data: URI source element", "Source", "Handles data: uris", + "Philippe Normand , " + "Sebastian Dröge "); + +} + +static void +gst_data_uri_src_class_init (GstDataURISrcClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstBaseSrcClass *basesrc_class = (GstBaseSrcClass *) klass; + + gobject_class->finalize = gst_data_uri_src_finalize; + gobject_class->set_property = gst_data_uri_src_set_property; + gobject_class->get_property = gst_data_uri_src_get_property; + + g_object_class_install_property (gobject_class, PROP_URI, + g_param_spec_string ("uri", + "URI", + "URI that should be used", + NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + basesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_data_uri_src_get_caps); + basesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_data_uri_src_get_size); + basesrc_class->is_seekable = GST_DEBUG_FUNCPTR (gst_data_uri_src_is_seekable); + basesrc_class->create = GST_DEBUG_FUNCPTR (gst_data_uri_src_create); + basesrc_class->check_get_range = + GST_DEBUG_FUNCPTR (gst_data_uri_src_check_get_range); +} + +static void +gst_data_uri_src_init (GstDataURISrc * src, GstDataURISrcClass * g_class) +{ + gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_BYTES); +} + +static void +gst_data_uri_src_finalize (GObject * object) +{ + GstDataURISrc *src = GST_DATA_URI_SRC (object); + + g_free (src->uri); + src->uri = NULL; + + if (src->buffer) + gst_buffer_unref (src->buffer); + src->buffer = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_data_uri_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstDataURISrc *src = GST_DATA_URI_SRC (object); + + switch (prop_id) { + case PROP_URI: + gst_data_uri_src_set_uri (GST_URI_HANDLER (src), + g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_data_uri_src_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstDataURISrc *src = GST_DATA_URI_SRC (object); + + switch (prop_id) { + case PROP_URI: + g_value_set_string (value, + gst_data_uri_src_get_uri (GST_URI_HANDLER (src))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstCaps * +gst_data_uri_src_get_caps (GstBaseSrc * basesrc) +{ + GstDataURISrc *src = GST_DATA_URI_SRC (basesrc); + GstCaps *caps; + + GST_OBJECT_LOCK (src); + if (!src->buffer || !GST_BUFFER_CAPS (src->buffer)) + caps = gst_caps_new_empty (); + else + caps = gst_buffer_get_caps (src->buffer); + GST_OBJECT_UNLOCK (src); + + return caps; +} + +static gboolean +gst_data_uri_src_get_size (GstBaseSrc * basesrc, guint64 * size) +{ + GstDataURISrc *src = GST_DATA_URI_SRC (basesrc); + gboolean ret; + + GST_OBJECT_LOCK (src); + if (!src->buffer) { + ret = FALSE; + *size = -1; + } else { + ret = TRUE; + *size = GST_BUFFER_SIZE (src->buffer); + } + GST_OBJECT_UNLOCK (src); + + return ret; +} + +static gboolean +gst_data_uri_src_is_seekable (GstBaseSrc * basesrc) +{ + return TRUE; +} + +static GstFlowReturn +gst_data_uri_src_create (GstBaseSrc * basesrc, guint64 offset, guint size, + GstBuffer ** buf) +{ + GstDataURISrc *src = GST_DATA_URI_SRC (basesrc); + GstFlowReturn ret; + + GST_OBJECT_LOCK (src); + if (!src->buffer) { + ret = GST_FLOW_NOT_NEGOTIATED; + GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL), (NULL)); + } else if (offset + size > GST_BUFFER_SIZE (src->buffer)) { + ret = GST_FLOW_UNEXPECTED; + } else { + ret = GST_FLOW_OK; + *buf = gst_buffer_create_sub (src->buffer, offset, size); + gst_buffer_set_caps (*buf, GST_BUFFER_CAPS (src->buffer)); + } + GST_OBJECT_UNLOCK (src); + + return ret; +} + +static gboolean +gst_data_uri_src_check_get_range (GstBaseSrc * basesrc) +{ + return TRUE; +} + +static void +gst_data_uri_src_handler_init (gpointer g_iface, gpointer iface_data) +{ + GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface; + + iface->get_type = gst_data_uri_src_get_uri_type; + iface->get_protocols = gst_data_uri_src_get_protocols; + iface->get_uri = gst_data_uri_src_get_uri; + iface->set_uri = gst_data_uri_src_set_uri; +} + +static GstURIType +gst_data_uri_src_get_uri_type (void) +{ + return GST_URI_SRC; +} + +static gchar ** +gst_data_uri_src_get_protocols (void) +{ + static gchar *protocols[] = { "data", 0 }; + + return protocols; +} + +static const gchar * +gst_data_uri_src_get_uri (GstURIHandler * handler) +{ + GstDataURISrc *src = GST_DATA_URI_SRC (handler); + + return src->uri; +} + +static gboolean +gst_data_uri_src_set_uri (GstURIHandler * handler, const gchar * uri) +{ + GstDataURISrc *src = GST_DATA_URI_SRC (handler); + gboolean ret = FALSE; + gchar *mimetype = NULL; + const gchar *parameters_start; + const gchar *data_start; + GstCaps *caps; + gboolean base64 = FALSE; + gchar *charset = NULL; + + GST_OBJECT_LOCK (src); + if (GST_STATE (src) >= GST_STATE_PAUSED) + goto wrong_state; + + /* uri must be an URI as defined in RFC 2397 + * data:[][;base64], + */ + if (strncmp ("data:", uri, 5) != 0) + goto invalid_uri; + + uri += 5; + + parameters_start = strchr (uri, ';'); + data_start = strchr (uri, ','); + if (data_start == NULL) + goto invalid_uri; + + if (data_start != uri && parameters_start != uri) + mimetype = + g_strndup (uri, + (parameters_start ? parameters_start : data_start) - uri); + else + mimetype = g_strdup ("text/plain"); + + GST_DEBUG_OBJECT (src, "Mimetype: %s", mimetype); + + if (parameters_start != NULL) { + gchar **walk; + gchar *parameters = + g_strndup (parameters_start + 1, data_start - parameters_start - 1); + gchar **parameters_strv; + + parameters_strv = g_strsplit (parameters, ";", -1); + + GST_DEBUG_OBJECT (src, "Parameters: "); + walk = parameters_strv; + while (*walk) { + GST_DEBUG_OBJECT (src, "\t %s", *walk); + if (strcmp ("base64", *walk) == 0) { + base64 = TRUE; + } else if (strncmp ("charset=", *walk, 8) == 0) { + charset = g_strdup (*walk + 8); + } + walk++; + } + g_free (parameters); + g_strfreev (parameters_strv); + } + + /* Skip comma */ + data_start += 1; + if (base64) { + gsize bsize; + + src->buffer = gst_buffer_new (); + GST_BUFFER_DATA (src->buffer) = + (guint8 *) g_base64_decode (data_start, &bsize); + GST_BUFFER_MALLOCDATA (src->buffer) = GST_BUFFER_DATA (src->buffer); + GST_BUFFER_SIZE (src->buffer) = bsize; + } else { + gchar *data; + + /* URI encoded, i.e. "percent" encoding */ + data = g_uri_unescape_string (data_start, NULL); + if (data == NULL) + goto invalid_uri_encoded_data; + + src->buffer = gst_buffer_new (); + GST_BUFFER_DATA (src->buffer) = (guint8 *) data; + GST_BUFFER_MALLOCDATA (src->buffer) = GST_BUFFER_DATA (src->buffer); + GST_BUFFER_SIZE (src->buffer) = strlen (data) + 1; + } + + /* Convert to UTF8 */ + if (strcmp ("text/plain", mimetype) == 0 && + charset && strcasecmp ("US-ASCII", charset) != 0 + && strcasecmp ("UTF-8", charset) != 0) { + gsize read; + gsize written; + gchar *old_data = (gchar *) GST_BUFFER_DATA (src->buffer); + gchar *data; + + data = + g_convert_with_fallback (old_data, -1, "UTF-8", charset, "*", &read, + &written, NULL); + g_free (old_data); + GST_BUFFER_DATA (src->buffer) = GST_BUFFER_MALLOCDATA (src->buffer) = + (guint8 *) data; + GST_BUFFER_SIZE (src->buffer) = written; + } + + caps = gst_type_find_helper_for_buffer (GST_OBJECT (src), src->buffer, NULL); + if (!caps) + caps = gst_caps_new_simple (mimetype, NULL); + gst_buffer_set_caps (src->buffer, caps); + gst_caps_unref (caps); + + ret = TRUE; + GST_OBJECT_UNLOCK (src); +out: + g_free (mimetype); + g_free (charset); + + + return ret; + +invalid_uri: + { + GST_OBJECT_UNLOCK (src); + GST_ELEMENT_ERROR (src, STREAM, FORMAT, (NULL), (NULL)); + } + goto out; +wrong_state: + { + GST_OBJECT_UNLOCK (src); + GST_ELEMENT_ERROR (src, CORE, FAILED, (NULL), (NULL)); + } + goto out; +invalid_uri_encoded_data: + { + GST_OBJECT_UNLOCK (src); + GST_ELEMENT_ERROR (src, STREAM, FORMAT, (NULL), (NULL)); + } + goto out; +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "dataurisrc", + GST_RANK_PRIMARY, GST_TYPE_DATA_URI_SRC); +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "dataurisrc", + "data: URI source", + plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); diff --git a/plugins/elements/gstdataurisrc.h b/plugins/elements/gstdataurisrc.h new file mode 100644 index 0000000..95ad962 --- /dev/null +++ b/plugins/elements/gstdataurisrc.h @@ -0,0 +1,61 @@ +/* GStreamer + * + * Copyright (C) 2009 Igalia S.L + * Copyright (C) 2009 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_DATA_URI_SRC__ +#define __GST_DATA_URI_SRC__ + +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_DATA_URI_SRC \ + (gst_data_uri_src_get_type()) +#define GST_DATA_URI_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DATA_URI_SRC,GstDataURISrc)) +#define GST_DATA_URI_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DATA_URI_SRC,GstDataURISrcClass)) +#define GST_IS_DATA_URI_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DATA_URI_SRC)) +#define GST_IS_DATA_URI_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DATA_URI_SRC)) +typedef struct _GstDataURISrc GstDataURISrc; +typedef struct _GstDataURISrcClass GstDataURISrcClass; + +struct _GstDataURISrc +{ + GstBaseSrc parent; + + /* */ + gchar *uri; + GstBuffer *buffer; +}; + +struct _GstDataURISrcClass +{ + GstBaseSrcClass parent_class; +}; + +GType gst_data_uri_src_get_type (void); + +G_END_DECLS + +#endif /* __GST_DATA_URI_SRC__ */ -- 2.7.4