1 /* GStreamer data:// uri source element
2 * Copyright (C) 2009 Igalia S.L
3 * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
22 * SECTION:element-dataurisrc
24 * dataurisrc handles data: URIs, see <ulink url="http://tools.ietf.org/html/rfc2397">RFC 2397</ulink> for more information.
27 * <title>Example launch line</title>
29 * gst-launch-1.0 -v dataurisrc uri="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAfElEQVQ4je2MwQnAIAxFgziA4EnczIsO4MEROo/gzZWc4xdTbe1R6LGRR74heYS7iKElzfcMiRnt4hf8gk8EayB6luefue/HzlJfCA50XsNjYRxprZmenXNIKSGEsC+QUqK1hhgj521BzhnWWiilUGvdF5RS4L2HMQZCCJy8sHMm2TYdJAAAAABJRU5ErkJggg==" ! pngdec ! videoconvert ! imagefreeze ! videoconvert ! autovideosink
30 * ]| This pipeline displays a small 16x16 PNG image from the data URI.
38 #include "gstdataurisrc.h"
41 #include <gst/base/gsttypefindhelper.h>
43 GST_DEBUG_CATEGORY (data_uri_src_debug);
44 #define GST_CAT_DEFAULT (data_uri_src_debug)
46 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
57 static void gst_data_uri_src_finalize (GObject * object);
58 static void gst_data_uri_src_set_property (GObject * object,
59 guint prop_id, const GValue * value, GParamSpec * pspec);
60 static void gst_data_uri_src_get_property (GObject * object,
61 guint prop_id, GValue * value, GParamSpec * pspec);
63 static GstCaps *gst_data_uri_src_get_caps (GstBaseSrc * src, GstCaps * filter);
64 static gboolean gst_data_uri_src_get_size (GstBaseSrc * src, guint64 * size);
65 static gboolean gst_data_uri_src_is_seekable (GstBaseSrc * src);
66 static GstFlowReturn gst_data_uri_src_create (GstBaseSrc * src, guint64 offset,
67 guint size, GstBuffer ** buf);
68 static gboolean gst_data_uri_src_start (GstBaseSrc * src);
70 static void gst_data_uri_src_handler_init (gpointer g_iface,
72 static GstURIType gst_data_uri_src_get_uri_type (GType type);
73 static const gchar *const *gst_data_uri_src_get_protocols (GType type);
74 static gchar *gst_data_uri_src_get_uri (GstURIHandler * handler);
75 static gboolean gst_data_uri_src_set_uri (GstURIHandler * handler,
76 const gchar * uri, GError ** error);
79 #define gst_data_uri_src_parent_class parent_class
80 G_DEFINE_TYPE_WITH_CODE (GstDataURISrc, gst_data_uri_src, GST_TYPE_BASE_SRC,
81 G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER,
82 gst_data_uri_src_handler_init));
85 gst_data_uri_src_class_init (GstDataURISrcClass * klass)
87 GObjectClass *gobject_class = (GObjectClass *) klass;
88 GstElementClass *element_class = (GstElementClass *) klass;
89 GstBaseSrcClass *basesrc_class = (GstBaseSrcClass *) klass;
91 gobject_class->finalize = gst_data_uri_src_finalize;
92 gobject_class->set_property = gst_data_uri_src_set_property;
93 gobject_class->get_property = gst_data_uri_src_get_property;
95 g_object_class_install_property (gobject_class, PROP_URI,
96 g_param_spec_string ("uri",
98 "URI that should be used",
99 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
101 gst_element_class_add_static_pad_template (element_class, &src_template);
102 gst_element_class_set_static_metadata (element_class,
103 "data: URI source element", "Source", "Handles data: uris",
104 "Philippe Normand <pnormand@igalia.com>, "
105 "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
107 GST_DEBUG_CATEGORY_INIT (data_uri_src_debug, "dataurisrc", 0,
110 basesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_data_uri_src_get_caps);
111 basesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_data_uri_src_get_size);
112 basesrc_class->is_seekable = GST_DEBUG_FUNCPTR (gst_data_uri_src_is_seekable);
113 basesrc_class->create = GST_DEBUG_FUNCPTR (gst_data_uri_src_create);
114 basesrc_class->start = GST_DEBUG_FUNCPTR (gst_data_uri_src_start);
118 gst_data_uri_src_init (GstDataURISrc * src)
123 gst_data_uri_src_finalize (GObject * object)
125 GstDataURISrc *src = GST_DATA_URI_SRC (object);
131 gst_buffer_unref (src->buffer);
134 G_OBJECT_CLASS (parent_class)->finalize (object);
138 gst_data_uri_src_set_property (GObject * object, guint prop_id,
139 const GValue * value, GParamSpec * pspec)
141 GstDataURISrc *src = GST_DATA_URI_SRC (object);
145 gst_data_uri_src_set_uri (GST_URI_HANDLER (src),
146 g_value_get_string (value), NULL);
149 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
155 gst_data_uri_src_get_property (GObject * object,
156 guint prop_id, GValue * value, GParamSpec * pspec)
158 GstDataURISrc *src = GST_DATA_URI_SRC (object);
162 g_value_take_string (value,
163 gst_data_uri_src_get_uri (GST_URI_HANDLER (src)));
166 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
172 gst_data_uri_src_get_caps (GstBaseSrc * basesrc, GstCaps * filter)
174 GstDataURISrc *src = GST_DATA_URI_SRC (basesrc);
177 GST_OBJECT_LOCK (src);
178 caps = gst_pad_get_current_caps (GST_BASE_SRC_PAD (basesrc));
180 caps = gst_caps_new_any ();
181 GST_OBJECT_UNLOCK (src);
187 gst_data_uri_src_get_size (GstBaseSrc * basesrc, guint64 * size)
189 GstDataURISrc *src = GST_DATA_URI_SRC (basesrc);
192 GST_OBJECT_LOCK (src);
198 *size = gst_buffer_get_size (src->buffer);
200 GST_OBJECT_UNLOCK (src);
206 gst_data_uri_src_is_seekable (GstBaseSrc * basesrc)
212 gst_data_uri_src_create (GstBaseSrc * basesrc, guint64 offset, guint size,
215 GstDataURISrc *src = GST_DATA_URI_SRC (basesrc);
218 GST_OBJECT_LOCK (src);
223 /* This is only correct because GstBaseSrc already clips size for us to be no
224 * larger than the max. available size if a segment at the end is requested */
225 if (offset + size > gst_buffer_get_size (src->buffer)) {
227 } else if (*buf != NULL) {
229 GstMapInfo dest_info;
232 gst_buffer_map (src->buffer, &src_info, GST_MAP_READ);
233 gst_buffer_map (*buf, &dest_info, GST_MAP_WRITE);
235 fill_size = gst_buffer_fill (*buf, 0, src_info.data + offset, size);
237 gst_buffer_unmap (*buf, &dest_info);
238 gst_buffer_unmap (src->buffer, &src_info);
239 gst_buffer_set_size (*buf, fill_size);
243 gst_buffer_copy_region (src->buffer, GST_BUFFER_COPY_ALL, offset, size);
246 GST_OBJECT_UNLOCK (src);
253 GST_OBJECT_UNLOCK (src);
254 GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL), (NULL));
255 return GST_FLOW_NOT_NEGOTIATED;
260 gst_data_uri_src_start (GstBaseSrc * basesrc)
262 GstDataURISrc *src = GST_DATA_URI_SRC (basesrc);
264 GST_OBJECT_LOCK (src);
266 if (src->uri == NULL || *src->uri == '\0' || src->buffer == NULL)
269 GST_OBJECT_UNLOCK (src);
276 GST_OBJECT_UNLOCK (src);
277 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
278 ("No valid data URI specified, or the data URI could not be parsed."),
285 gst_data_uri_src_get_uri_type (GType type)
290 static const gchar *const *
291 gst_data_uri_src_get_protocols (GType type)
293 static const gchar *protocols[] = { "data", 0 };
299 gst_data_uri_src_get_uri (GstURIHandler * handler)
301 GstDataURISrc *src = GST_DATA_URI_SRC (handler);
302 gchar *src_uri = NULL;
304 GST_OBJECT_LOCK (src);
305 src_uri = g_strdup (src->uri);
306 GST_OBJECT_UNLOCK (src);
311 gst_data_uri_src_set_uri (GstURIHandler * handler, const gchar * uri,
314 GstDataURISrc *src = GST_DATA_URI_SRC (handler);
315 gboolean ret = FALSE;
316 gchar *mimetype = NULL;
317 const gchar *parameters_start;
318 const gchar *data_start;
319 const gchar *orig_uri = uri;
322 gboolean base64 = FALSE;
323 gchar *charset = NULL;
327 GST_OBJECT_LOCK (src);
328 if (GST_STATE (src) >= GST_STATE_PAUSED)
330 GST_OBJECT_UNLOCK (src);
332 /* uri must be an URI as defined in RFC 2397
333 * data:[<mediatype>][;base64],<data>
335 if (strncmp ("data:", uri, 5) != 0)
340 parameters_start = strchr (uri, ';');
341 data_start = strchr (uri, ',');
342 if (data_start == NULL)
345 if (data_start != uri && parameters_start != uri)
348 (parameters_start ? parameters_start : data_start) - uri);
350 mimetype = g_strdup ("text/plain");
352 GST_DEBUG_OBJECT (src, "Mimetype: %s", mimetype);
354 if (parameters_start != NULL) {
357 g_strndup (parameters_start + 1, data_start - parameters_start - 1);
358 gchar **parameters_strv;
360 parameters_strv = g_strsplit (parameters, ";", -1);
362 GST_DEBUG_OBJECT (src, "Parameters: ");
363 walk = parameters_strv;
365 GST_DEBUG_OBJECT (src, "\t %s", *walk);
366 if (strcmp ("base64", *walk) == 0) {
368 } else if (strncmp ("charset=", *walk, 8) == 0) {
369 charset = g_strdup (*walk + 8);
374 g_strfreev (parameters_strv);
380 bdata = g_base64_decode (data_start, &bsize);
382 /* URI encoded, i.e. "percent" encoding */
383 bdata = g_uri_unescape_string (data_start, NULL);
385 goto invalid_uri_encoded_data;
386 bsize = strlen (bdata) + 1;
388 /* Convert to UTF8 */
389 if (strcmp ("text/plain", mimetype) == 0 &&
390 charset && g_ascii_strcasecmp ("US-ASCII", charset) != 0
391 && g_ascii_strcasecmp ("UTF-8", charset) != 0) {
397 g_convert_with_fallback (bdata, -1, "UTF-8", charset, (char *) "*",
398 &read, &written, NULL);
404 buffer = gst_buffer_new_wrapped (bdata, bsize);
406 caps = gst_type_find_helper_for_buffer (GST_OBJECT (src), buffer, NULL);
408 caps = gst_caps_new_empty_simple (mimetype);
409 gst_base_src_set_caps (GST_BASE_SRC_CAST (src), caps);
410 gst_caps_unref (caps);
412 GST_OBJECT_LOCK (src);
413 gst_buffer_replace (&src->buffer, buffer);
414 gst_buffer_unref (buffer);
416 src->uri = g_strdup (orig_uri);
417 GST_OBJECT_UNLOCK (src);
430 GST_WARNING_OBJECT (src, "Can't set URI in %s state",
431 gst_element_state_get_name (GST_STATE (src)));
432 GST_OBJECT_UNLOCK (src);
433 g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_STATE,
434 "Changing the 'uri' property on dataurisrc while it is running "
440 GST_WARNING_OBJECT (src, "invalid URI '%s'", uri);
441 g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
445 invalid_uri_encoded_data:
447 GST_WARNING_OBJECT (src, "Failed to parse data encoded in URI '%s'", uri);
448 g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
449 "Could not parse data encoded in data URI");
455 gst_data_uri_src_handler_init (gpointer g_iface, gpointer iface_data)
457 GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
459 iface->get_type = gst_data_uri_src_get_uri_type;
460 iface->get_protocols = gst_data_uri_src_get_protocols;
461 iface->get_uri = gst_data_uri_src_get_uri;
462 iface->set_uri = gst_data_uri_src_set_uri;