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