d63e43728dc8460e3852a05480e0b394bace4ca2
[platform/upstream/gstreamer.git] / plugins / elements / gstdataurisrc.c
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  *
24  * dataurisrc handles data: URIs, see <ulink url="http://tools.ietf.org/html/rfc2397">RFC 2397</ulink> for more information.
25  *
26  * <refsect2>
27  * <title>Example launch line</title>
28  * |[
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.
31  * </refsect2>
32  */
33
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
37
38 #include "gstdataurisrc.h"
39
40 #include <string.h>
41 #include <gst/base/gsttypefindhelper.h>
42
43 GST_DEBUG_CATEGORY (data_uri_src_debug);
44 #define GST_CAT_DEFAULT (data_uri_src_debug)
45
46 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
47     GST_PAD_SRC,
48     GST_PAD_ALWAYS,
49     GST_STATIC_CAPS_ANY);
50
51 enum
52 {
53   PROP_0,
54   PROP_URI,
55 };
56
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);
62
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);
69
70 static void gst_data_uri_src_handler_init (gpointer g_iface,
71     gpointer iface_data);
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);
77
78
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));
83
84 static void
85 gst_data_uri_src_class_init (GstDataURISrcClass * klass)
86 {
87   GObjectClass *gobject_class = (GObjectClass *) klass;
88   GstElementClass *element_class = (GstElementClass *) klass;
89   GstBaseSrcClass *basesrc_class = (GstBaseSrcClass *) klass;
90
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;
94
95   g_object_class_install_property (gobject_class, PROP_URI,
96       g_param_spec_string ("uri",
97           "URI",
98           "URI that should be used",
99           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
100
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>");
106
107   GST_DEBUG_CATEGORY_INIT (data_uri_src_debug, "dataurisrc", 0,
108       "data: URI source");
109
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);
115 }
116
117 static void
118 gst_data_uri_src_init (GstDataURISrc * src)
119 {
120 }
121
122 static void
123 gst_data_uri_src_finalize (GObject * object)
124 {
125   GstDataURISrc *src = GST_DATA_URI_SRC (object);
126
127   g_free (src->uri);
128   src->uri = NULL;
129
130   if (src->buffer)
131     gst_buffer_unref (src->buffer);
132   src->buffer = NULL;
133
134   G_OBJECT_CLASS (parent_class)->finalize (object);
135 }
136
137 static void
138 gst_data_uri_src_set_property (GObject * object, guint prop_id,
139     const GValue * value, GParamSpec * pspec)
140 {
141   GstDataURISrc *src = GST_DATA_URI_SRC (object);
142
143   switch (prop_id) {
144     case PROP_URI:
145       gst_data_uri_src_set_uri (GST_URI_HANDLER (src),
146           g_value_get_string (value), NULL);
147       break;
148     default:
149       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
150       break;
151   }
152 }
153
154 static void
155 gst_data_uri_src_get_property (GObject * object,
156     guint prop_id, GValue * value, GParamSpec * pspec)
157 {
158   GstDataURISrc *src = GST_DATA_URI_SRC (object);
159
160   switch (prop_id) {
161     case PROP_URI:
162       g_value_take_string (value,
163           gst_data_uri_src_get_uri (GST_URI_HANDLER (src)));
164       break;
165     default:
166       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
167       break;
168   }
169 }
170
171 static GstCaps *
172 gst_data_uri_src_get_caps (GstBaseSrc * basesrc, GstCaps * filter)
173 {
174   GstDataURISrc *src = GST_DATA_URI_SRC (basesrc);
175   GstCaps *caps;
176
177   GST_OBJECT_LOCK (src);
178   caps = gst_pad_get_current_caps (GST_BASE_SRC_PAD (basesrc));
179   if (!caps)
180     caps = gst_caps_new_any ();
181   GST_OBJECT_UNLOCK (src);
182
183   return caps;
184 }
185
186 static gboolean
187 gst_data_uri_src_get_size (GstBaseSrc * basesrc, guint64 * size)
188 {
189   GstDataURISrc *src = GST_DATA_URI_SRC (basesrc);
190   gboolean ret;
191
192   GST_OBJECT_LOCK (src);
193   if (!src->buffer) {
194     ret = FALSE;
195     *size = -1;
196   } else {
197     ret = TRUE;
198     *size = gst_buffer_get_size (src->buffer);
199   }
200   GST_OBJECT_UNLOCK (src);
201
202   return ret;
203 }
204
205 static gboolean
206 gst_data_uri_src_is_seekable (GstBaseSrc * basesrc)
207 {
208   return TRUE;
209 }
210
211 static GstFlowReturn
212 gst_data_uri_src_create (GstBaseSrc * basesrc, guint64 offset, guint size,
213     GstBuffer ** buf)
214 {
215   GstDataURISrc *src = GST_DATA_URI_SRC (basesrc);
216   GstFlowReturn ret;
217
218   GST_OBJECT_LOCK (src);
219
220   if (!src->buffer)
221     goto no_buffer;
222
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)) {
226     ret = GST_FLOW_EOS;
227   } else if (*buf != NULL) {
228     GstMapInfo src_info;
229     GstMapInfo dest_info;
230     gsize fill_size;
231
232     gst_buffer_map (src->buffer, &src_info, GST_MAP_READ);
233     gst_buffer_map (*buf, &dest_info, GST_MAP_WRITE);
234
235     fill_size = gst_buffer_fill (*buf, 0, src_info.data + offset, size);
236
237     gst_buffer_unmap (*buf, &dest_info);
238     gst_buffer_unmap (src->buffer, &src_info);
239     gst_buffer_set_size (*buf, fill_size);
240     ret = GST_FLOW_OK;
241   } else {
242     *buf =
243         gst_buffer_copy_region (src->buffer, GST_BUFFER_COPY_ALL, offset, size);
244     ret = GST_FLOW_OK;
245   }
246   GST_OBJECT_UNLOCK (src);
247
248   return ret;
249
250 /* ERRORS */
251 no_buffer:
252   {
253     GST_OBJECT_UNLOCK (src);
254     GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL), (NULL));
255     return GST_FLOW_NOT_NEGOTIATED;
256   }
257 }
258
259 static gboolean
260 gst_data_uri_src_start (GstBaseSrc * basesrc)
261 {
262   GstDataURISrc *src = GST_DATA_URI_SRC (basesrc);
263
264   GST_OBJECT_LOCK (src);
265
266   if (src->uri == NULL || *src->uri == '\0' || src->buffer == NULL)
267     goto no_uri;
268
269   GST_OBJECT_UNLOCK (src);
270
271   return TRUE;
272
273 /* ERRORS */
274 no_uri:
275   {
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."),
279         ("%s", src->uri));
280     return FALSE;
281   }
282 }
283
284 static GstURIType
285 gst_data_uri_src_get_uri_type (GType type)
286 {
287   return GST_URI_SRC;
288 }
289
290 static const gchar *const *
291 gst_data_uri_src_get_protocols (GType type)
292 {
293   static const gchar *protocols[] = { "data", 0 };
294
295   return protocols;
296 }
297
298 static gchar *
299 gst_data_uri_src_get_uri (GstURIHandler * handler)
300 {
301   GstDataURISrc *src = GST_DATA_URI_SRC (handler);
302   gchar *src_uri = NULL;
303
304   GST_OBJECT_LOCK (src);
305   src_uri = g_strdup (src->uri);
306   GST_OBJECT_UNLOCK (src);
307   return src_uri;
308 }
309
310 static gboolean
311 gst_data_uri_src_set_uri (GstURIHandler * handler, const gchar * uri,
312     GError ** error)
313 {
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;
320   GstCaps *caps;
321   GstBuffer *buffer;
322   gboolean base64 = FALSE;
323   gchar *charset = NULL;
324   gpointer bdata;
325   gsize bsize;
326
327   GST_OBJECT_LOCK (src);
328   if (GST_STATE (src) >= GST_STATE_PAUSED)
329     goto wrong_state;
330   GST_OBJECT_UNLOCK (src);
331
332   /* uri must be an URI as defined in RFC 2397
333    * data:[<mediatype>][;base64],<data>
334    */
335   if (strncmp ("data:", uri, 5) != 0)
336     goto invalid_uri;
337
338   uri += 5;
339
340   parameters_start = strchr (uri, ';');
341   data_start = strchr (uri, ',');
342   if (data_start == NULL)
343     goto invalid_uri;
344
345   if (data_start != uri && parameters_start != uri)
346     mimetype =
347         g_strndup (uri,
348         (parameters_start ? parameters_start : data_start) - uri);
349   else
350     mimetype = g_strdup ("text/plain");
351
352   GST_DEBUG_OBJECT (src, "Mimetype: %s", mimetype);
353
354   if (parameters_start != NULL) {
355     gchar **walk;
356     gchar *parameters =
357         g_strndup (parameters_start + 1, data_start - parameters_start - 1);
358     gchar **parameters_strv;
359
360     parameters_strv = g_strsplit (parameters, ";", -1);
361
362     GST_DEBUG_OBJECT (src, "Parameters: ");
363     walk = parameters_strv;
364     while (*walk) {
365       GST_DEBUG_OBJECT (src, "\t %s", *walk);
366       if (strcmp ("base64", *walk) == 0) {
367         base64 = TRUE;
368       } else if (strncmp ("charset=", *walk, 8) == 0) {
369         charset = g_strdup (*walk + 8);
370       }
371       walk++;
372     }
373     g_free (parameters);
374     g_strfreev (parameters_strv);
375   }
376
377   /* Skip comma */
378   data_start += 1;
379   if (base64) {
380     bdata = g_base64_decode (data_start, &bsize);
381   } else {
382     /* URI encoded, i.e. "percent" encoding */
383     bdata = g_uri_unescape_string (data_start, NULL);
384     if (bdata == NULL)
385       goto invalid_uri_encoded_data;
386     bsize = strlen (bdata) + 1;
387   }
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) {
392     gsize read;
393     gsize written;
394     gpointer data;
395
396     data =
397         g_convert_with_fallback (bdata, -1, "UTF-8", charset, (char *) "*",
398         &read, &written, NULL);
399     g_free (bdata);
400
401     bdata = data;
402     bsize = written;
403   }
404   buffer = gst_buffer_new_wrapped (bdata, bsize);
405
406   caps = gst_type_find_helper_for_buffer (GST_OBJECT (src), buffer, NULL);
407   if (!caps)
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);
411
412   GST_OBJECT_LOCK (src);
413   gst_buffer_replace (&src->buffer, buffer);
414   gst_buffer_unref (buffer);
415   g_free (src->uri);
416   src->uri = g_strdup (orig_uri);
417   GST_OBJECT_UNLOCK (src);
418
419   ret = TRUE;
420
421 out:
422
423   g_free (mimetype);
424   g_free (charset);
425
426   return ret;
427
428 wrong_state:
429   {
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 "
435         "is not supported");
436     goto out;
437   }
438 invalid_uri:
439   {
440     GST_WARNING_OBJECT (src, "invalid URI '%s'", uri);
441     g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
442         "Invalid data URI");
443     goto out;
444   }
445 invalid_uri_encoded_data:
446   {
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");
450     goto out;
451   }
452 }
453
454 static void
455 gst_data_uri_src_handler_init (gpointer g_iface, gpointer iface_data)
456 {
457   GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
458
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;
463 }