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>
4 *
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.
9 *
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.
14 *
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.
19 */
20
21 /**
22 * SECTION:element-dataurisrc
23 * @title: dataurisrc
24 *
25 * dataurisrc handles data: URIs, see [RFC 2397](http://tools.ietf.org/html/rfc2397) for more information.
26 *
27 * ## Example launch line
28 *
29 * |[
30 * gst-launch-1.0 -v dataurisrc uri="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAfElEQVQ4je2MwQnAIAxFgziA4EnczIsO4MEROo/gzZWc4xdTbe1R6LGRR74heYS7iKElzfcMiRnt4hf8gk8EayB6luefue/HzlJfCA50XsNjYRxprZmenXNIKSGEsC+QUqK1hhgj521BzhnWWiilUGvdF5RS4L2HMQZCCJy8sHMm2TYdJAAAAABJRU5ErkJggg==" ! pngdec ! videoconvert ! imagefreeze ! videoconvert ! autovideosink
31 * ]|
32 *
33 * This pipeline displays a small 16x16 PNG image from the data URI.
34 */
35
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39
40 #include "gstdataurisrc.h"
41
42 #include <string.h>
43 #include <gst/base/gsttypefindhelper.h>
44
45 GST_DEBUG_CATEGORY (data_uri_src_debug);
46 #define GST_CAT_DEFAULT (data_uri_src_debug)
47
48 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
49 GST_PAD_SRC,
50 GST_PAD_ALWAYS,
51 GST_STATIC_CAPS_ANY);
52
53 enum
54 {
55 PROP_0,
56 PROP_URI,
57 };
58
59 static void gst_data_uri_src_finalize (GObject * object);
60 static void gst_data_uri_src_set_property (GObject * object,
61 guint prop_id, const GValue * value, GParamSpec * pspec);
62 static void gst_data_uri_src_get_property (GObject * object,
63 guint prop_id, GValue * value, GParamSpec * pspec);
64
65 static GstCaps *gst_data_uri_src_get_caps (GstBaseSrc * src, GstCaps * filter);
66 static gboolean gst_data_uri_src_get_size (GstBaseSrc * src, guint64 * size);
67 static gboolean gst_data_uri_src_is_seekable (GstBaseSrc * src);
68 static GstFlowReturn gst_data_uri_src_create (GstBaseSrc * src, guint64 offset,
69 guint size, GstBuffer ** buf);
70 static gboolean gst_data_uri_src_start (GstBaseSrc * src);
71
72 static void gst_data_uri_src_handler_init (gpointer g_iface,
73 gpointer iface_data);
74 static GstURIType gst_data_uri_src_get_uri_type (GType type);
75 static const gchar *const *gst_data_uri_src_get_protocols (GType type);
76 static gchar *gst_data_uri_src_get_uri (GstURIHandler * handler);
77 static gboolean gst_data_uri_src_set_uri (GstURIHandler * handler,
78 const gchar * uri, GError ** error);
79
80
81 #define gst_data_uri_src_parent_class parent_class
82 G_DEFINE_TYPE_WITH_CODE (GstDataURISrc, gst_data_uri_src, GST_TYPE_BASE_SRC,
83 G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER,
84 gst_data_uri_src_handler_init));
85
86 static void
gst_data_uri_src_class_init(GstDataURISrcClass * klass)87 gst_data_uri_src_class_init (GstDataURISrcClass * klass)
88 {
89 GObjectClass *gobject_class = (GObjectClass *) klass;
90 GstElementClass *element_class = (GstElementClass *) klass;
91 GstBaseSrcClass *basesrc_class = (GstBaseSrcClass *) klass;
92
93 gobject_class->finalize = gst_data_uri_src_finalize;
94 gobject_class->set_property = gst_data_uri_src_set_property;
95 gobject_class->get_property = gst_data_uri_src_get_property;
96
97 g_object_class_install_property (gobject_class, PROP_URI,
98 g_param_spec_string ("uri",
99 "URI",
100 "URI that should be used",
101 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
102
103 gst_element_class_add_static_pad_template (element_class, &src_template);
104 gst_element_class_set_static_metadata (element_class,
105 "data: URI source element", "Source", "Handles data: uris",
106 "Philippe Normand <pnormand@igalia.com>, "
107 "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
108
109 GST_DEBUG_CATEGORY_INIT (data_uri_src_debug, "dataurisrc", 0,
110 "data: URI source");
111
112 basesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_data_uri_src_get_caps);
113 basesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_data_uri_src_get_size);
114 basesrc_class->is_seekable = GST_DEBUG_FUNCPTR (gst_data_uri_src_is_seekable);
115 basesrc_class->create = GST_DEBUG_FUNCPTR (gst_data_uri_src_create);
116 basesrc_class->start = GST_DEBUG_FUNCPTR (gst_data_uri_src_start);
117 }
118
119 static void
gst_data_uri_src_init(GstDataURISrc * src)120 gst_data_uri_src_init (GstDataURISrc * src)
121 {
122 }
123
124 static void
gst_data_uri_src_finalize(GObject * object)125 gst_data_uri_src_finalize (GObject * object)
126 {
127 GstDataURISrc *src = GST_DATA_URI_SRC (object);
128
129 g_free (src->uri);
130 src->uri = NULL;
131
132 if (src->buffer)
133 gst_buffer_unref (src->buffer);
134 src->buffer = NULL;
135
136 G_OBJECT_CLASS (parent_class)->finalize (object);
137 }
138
139 static void
gst_data_uri_src_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)140 gst_data_uri_src_set_property (GObject * object, guint prop_id,
141 const GValue * value, GParamSpec * pspec)
142 {
143 GstDataURISrc *src = GST_DATA_URI_SRC (object);
144
145 switch (prop_id) {
146 case PROP_URI:
147 gst_data_uri_src_set_uri (GST_URI_HANDLER (src),
148 g_value_get_string (value), NULL);
149 break;
150 default:
151 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
152 break;
153 }
154 }
155
156 static void
gst_data_uri_src_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)157 gst_data_uri_src_get_property (GObject * object,
158 guint prop_id, GValue * value, GParamSpec * pspec)
159 {
160 GstDataURISrc *src = GST_DATA_URI_SRC (object);
161
162 switch (prop_id) {
163 case PROP_URI:
164 g_value_take_string (value,
165 gst_data_uri_src_get_uri (GST_URI_HANDLER (src)));
166 break;
167 default:
168 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
169 break;
170 }
171 }
172
173 static GstCaps *
gst_data_uri_src_get_caps(GstBaseSrc * basesrc,GstCaps * filter)174 gst_data_uri_src_get_caps (GstBaseSrc * basesrc, GstCaps * filter)
175 {
176 GstDataURISrc *src = GST_DATA_URI_SRC (basesrc);
177 GstCaps *caps;
178
179 GST_OBJECT_LOCK (src);
180 caps = gst_pad_get_current_caps (GST_BASE_SRC_PAD (basesrc));
181 if (!caps)
182 caps = gst_caps_new_any ();
183 GST_OBJECT_UNLOCK (src);
184
185 return caps;
186 }
187
188 static gboolean
gst_data_uri_src_get_size(GstBaseSrc * basesrc,guint64 * size)189 gst_data_uri_src_get_size (GstBaseSrc * basesrc, guint64 * size)
190 {
191 GstDataURISrc *src = GST_DATA_URI_SRC (basesrc);
192 gboolean ret;
193
194 GST_OBJECT_LOCK (src);
195 if (!src->buffer) {
196 ret = FALSE;
197 *size = -1;
198 } else {
199 ret = TRUE;
200 *size = gst_buffer_get_size (src->buffer);
201 }
202 GST_OBJECT_UNLOCK (src);
203
204 return ret;
205 }
206
207 static gboolean
gst_data_uri_src_is_seekable(GstBaseSrc * basesrc)208 gst_data_uri_src_is_seekable (GstBaseSrc * basesrc)
209 {
210 return TRUE;
211 }
212
213 static GstFlowReturn
gst_data_uri_src_create(GstBaseSrc * basesrc,guint64 offset,guint size,GstBuffer ** buf)214 gst_data_uri_src_create (GstBaseSrc * basesrc, guint64 offset, guint size,
215 GstBuffer ** buf)
216 {
217 GstDataURISrc *src = GST_DATA_URI_SRC (basesrc);
218 GstFlowReturn ret;
219
220 GST_OBJECT_LOCK (src);
221
222 if (!src->buffer)
223 goto no_buffer;
224
225 /* This is only correct because GstBaseSrc already clips size for us to be no
226 * larger than the max. available size if a segment at the end is requested */
227 if (offset + size > gst_buffer_get_size (src->buffer)) {
228 ret = GST_FLOW_EOS;
229 } else if (*buf != NULL) {
230 GstMapInfo src_info;
231 GstMapInfo dest_info;
232 gsize fill_size;
233
234 gst_buffer_map (src->buffer, &src_info, GST_MAP_READ);
235 gst_buffer_map (*buf, &dest_info, GST_MAP_WRITE);
236
237 fill_size = gst_buffer_fill (*buf, 0, src_info.data + offset, size);
238
239 gst_buffer_unmap (*buf, &dest_info);
240 gst_buffer_unmap (src->buffer, &src_info);
241 gst_buffer_set_size (*buf, fill_size);
242 ret = GST_FLOW_OK;
243 } else {
244 *buf =
245 gst_buffer_copy_region (src->buffer, GST_BUFFER_COPY_ALL, offset, size);
246 ret = GST_FLOW_OK;
247 }
248 GST_OBJECT_UNLOCK (src);
249
250 return ret;
251
252 /* ERRORS */
253 no_buffer:
254 {
255 GST_OBJECT_UNLOCK (src);
256 GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL), (NULL));
257 return GST_FLOW_NOT_NEGOTIATED;
258 }
259 }
260
261 static gboolean
gst_data_uri_src_start(GstBaseSrc * basesrc)262 gst_data_uri_src_start (GstBaseSrc * basesrc)
263 {
264 GstDataURISrc *src = GST_DATA_URI_SRC (basesrc);
265
266 GST_OBJECT_LOCK (src);
267
268 if (src->uri == NULL || *src->uri == '\0' || src->buffer == NULL)
269 goto no_uri;
270
271 GST_OBJECT_UNLOCK (src);
272
273 return TRUE;
274
275 /* ERRORS */
276 no_uri:
277 {
278 GST_OBJECT_UNLOCK (src);
279 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
280 ("No valid data URI specified, or the data URI could not be parsed."),
281 ("%s", src->uri));
282 return FALSE;
283 }
284 }
285
286 static GstURIType
gst_data_uri_src_get_uri_type(GType type)287 gst_data_uri_src_get_uri_type (GType type)
288 {
289 return GST_URI_SRC;
290 }
291
292 static const gchar *const *
gst_data_uri_src_get_protocols(GType type)293 gst_data_uri_src_get_protocols (GType type)
294 {
295 static const gchar *protocols[] = { "data", 0 };
296
297 return protocols;
298 }
299
300 static gchar *
gst_data_uri_src_get_uri(GstURIHandler * handler)301 gst_data_uri_src_get_uri (GstURIHandler * handler)
302 {
303 GstDataURISrc *src = GST_DATA_URI_SRC (handler);
304 gchar *src_uri = NULL;
305
306 GST_OBJECT_LOCK (src);
307 src_uri = g_strdup (src->uri);
308 GST_OBJECT_UNLOCK (src);
309 return src_uri;
310 }
311
312 static gboolean
gst_data_uri_src_set_uri(GstURIHandler * handler,const gchar * uri,GError ** error)313 gst_data_uri_src_set_uri (GstURIHandler * handler, const gchar * uri,
314 GError ** error)
315 {
316 GstDataURISrc *src = GST_DATA_URI_SRC (handler);
317 gboolean ret = FALSE;
318 gchar *mimetype = NULL;
319 const gchar *parameters_start;
320 const gchar *data_start;
321 const gchar *orig_uri = uri;
322 GstCaps *caps;
323 GstBuffer *buffer;
324 gboolean base64 = FALSE;
325 gchar *charset = NULL;
326 gpointer bdata;
327 gsize bsize;
328
329 GST_OBJECT_LOCK (src);
330 if (GST_STATE (src) >= GST_STATE_PAUSED)
331 goto wrong_state;
332 GST_OBJECT_UNLOCK (src);
333
334 /* uri must be an URI as defined in RFC 2397
335 * data:[<mediatype>][;base64],<data>
336 */
337 if (strncmp ("data:", uri, 5) != 0)
338 goto invalid_uri;
339
340 uri += 5;
341
342 parameters_start = strchr (uri, ';');
343 data_start = strchr (uri, ',');
344 if (data_start == NULL)
345 goto invalid_uri;
346
347 if (data_start != uri && parameters_start != uri)
348 mimetype =
349 g_strndup (uri,
350 (parameters_start ? parameters_start : data_start) - uri);
351 else
352 mimetype = g_strdup ("text/plain");
353
354 GST_DEBUG_OBJECT (src, "Mimetype: %s", mimetype);
355
356 if (parameters_start != NULL) {
357 gchar **walk;
358 gchar *parameters =
359 g_strndup (parameters_start + 1, data_start - parameters_start - 1);
360 gchar **parameters_strv;
361
362 parameters_strv = g_strsplit (parameters, ";", -1);
363
364 GST_DEBUG_OBJECT (src, "Parameters: ");
365 walk = parameters_strv;
366 while (*walk) {
367 GST_DEBUG_OBJECT (src, "\t %s", *walk);
368 if (strcmp ("base64", *walk) == 0) {
369 base64 = TRUE;
370 } else if (strncmp ("charset=", *walk, 8) == 0) {
371 charset = g_strdup (*walk + 8);
372 }
373 walk++;
374 }
375 g_free (parameters);
376 g_strfreev (parameters_strv);
377 }
378
379 /* Skip comma */
380 data_start += 1;
381 if (base64) {
382 bdata = g_base64_decode (data_start, &bsize);
383 } else {
384 /* URI encoded, i.e. "percent" encoding */
385 bdata = g_uri_unescape_string (data_start, NULL);
386 if (bdata == NULL)
387 goto invalid_uri_encoded_data;
388 bsize = strlen (bdata) + 1;
389 }
390 /* Convert to UTF8 */
391 if (strcmp ("text/plain", mimetype) == 0 &&
392 charset && g_ascii_strcasecmp ("US-ASCII", charset) != 0
393 && g_ascii_strcasecmp ("UTF-8", charset) != 0) {
394 gsize read;
395 gsize written;
396 gpointer data;
397
398 data =
399 g_convert_with_fallback (bdata, -1, "UTF-8", charset, (char *) "*",
400 &read, &written, NULL);
401 g_free (bdata);
402
403 bdata = data;
404 bsize = written;
405 }
406 buffer = gst_buffer_new_wrapped (bdata, bsize);
407
408 caps = gst_type_find_helper_for_buffer (GST_OBJECT (src), buffer, NULL);
409 if (!caps)
410 caps = gst_caps_new_empty_simple (mimetype);
411 gst_base_src_set_caps (GST_BASE_SRC_CAST (src), caps);
412 gst_caps_unref (caps);
413
414 GST_OBJECT_LOCK (src);
415 gst_buffer_replace (&src->buffer, buffer);
416 gst_buffer_unref (buffer);
417 g_free (src->uri);
418 src->uri = g_strdup (orig_uri);
419 GST_OBJECT_UNLOCK (src);
420
421 ret = TRUE;
422
423 out:
424
425 g_free (mimetype);
426 g_free (charset);
427
428 return ret;
429
430 wrong_state:
431 {
432 GST_WARNING_OBJECT (src, "Can't set URI in %s state",
433 gst_element_state_get_name (GST_STATE (src)));
434 GST_OBJECT_UNLOCK (src);
435 g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_STATE,
436 "Changing the 'uri' property on dataurisrc while it is running "
437 "is not supported");
438 goto out;
439 }
440 invalid_uri:
441 {
442 GST_WARNING_OBJECT (src, "invalid URI '%s'", uri);
443 g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
444 "Invalid data URI");
445 goto out;
446 }
447 invalid_uri_encoded_data:
448 {
449 GST_WARNING_OBJECT (src, "Failed to parse data encoded in URI '%s'", uri);
450 g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
451 "Could not parse data encoded in data URI");
452 goto out;
453 }
454 }
455
456 static void
gst_data_uri_src_handler_init(gpointer g_iface,gpointer iface_data)457 gst_data_uri_src_handler_init (gpointer g_iface, gpointer iface_data)
458 {
459 GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
460
461 iface->get_type = gst_data_uri_src_get_uri_type;
462 iface->get_protocols = gst_data_uri_src_get_protocols;
463 iface->get_uri = gst_data_uri_src_get_uri;
464 iface->set_uri = gst_data_uri_src_set_uri;
465 }
466