docs: update gst-launch-0.10 lines
[platform/upstream/gstreamer.git] / plugins / elements / gstdataurisrc.c
1 /* GStreamer
2  *
3  * Copyright (C) 2009 Igalia S.L
4  * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 /**
23  * SECTION:element-dataurisrc
24  *
25  * dataurisrc handles data: URIs, see <ulink url="http://tools.ietf.org/html/rfc2397">RFC 2397</ulink> for more information.
26  *
27  * <refsect2>
28  * <title>Example launch line</title>
29  * |[
30  * gst-launch-1.0 -v dataurisrc uri="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAfElEQVQ4je2MwQnAIAxFgziA4EnczIsO4MEROo/gzZWc4xdTbe1R6LGRR74heYS7iKElzfcMiRnt4hf8gk8EayB6luefue/HzlJfCA50XsNjYRxprZmenXNIKSGEsC+QUqK1hhgj521BzhnWWiilUGvdF5RS4L2HMQZCCJy8sHMm2TYdJAAAAABJRU5ErkJggg==" ! pngdec ! videoconvert ! freeze ! videoconvert ! autovideosink
31  * ]| This pipeline displays a small 16x16 PNG image from the data URI.
32  * </refsect2>
33  */
34
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
38
39 #include "gstdataurisrc.h"
40
41 #include <string.h>
42 #include <gst/base/gsttypefindhelper.h>
43
44 GST_DEBUG_CATEGORY (data_uri_src_debug);
45 #define GST_CAT_DEFAULT (data_uri_src_debug)
46
47 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
48     GST_PAD_SRC,
49     GST_PAD_ALWAYS,
50     GST_STATIC_CAPS_ANY);
51
52 enum
53 {
54   PROP_0,
55   PROP_URI,
56 };
57
58 static void gst_data_uri_src_finalize (GObject * object);
59 static void gst_data_uri_src_set_property (GObject * object,
60     guint prop_id, const GValue * value, GParamSpec * pspec);
61 static void gst_data_uri_src_get_property (GObject * object,
62     guint prop_id, GValue * value, GParamSpec * pspec);
63
64 static GstCaps *gst_data_uri_src_get_caps (GstBaseSrc * src, GstCaps * filter);
65 static gboolean gst_data_uri_src_get_size (GstBaseSrc * src, guint64 * size);
66 static gboolean gst_data_uri_src_is_seekable (GstBaseSrc * src);
67 static GstFlowReturn gst_data_uri_src_create (GstBaseSrc * src, guint64 offset,
68     guint size, GstBuffer ** buf);
69 static gboolean gst_data_uri_src_start (GstBaseSrc * src);
70
71 static void gst_data_uri_src_handler_init (gpointer g_iface,
72     gpointer iface_data);
73 static GstURIType gst_data_uri_src_get_uri_type (GType type);
74 static const gchar *const *gst_data_uri_src_get_protocols (GType type);
75 static gchar *gst_data_uri_src_get_uri (GstURIHandler * handler);
76 static gboolean gst_data_uri_src_set_uri (GstURIHandler * handler,
77     const gchar * uri, GError ** error);
78
79
80 #define gst_data_uri_src_parent_class parent_class
81 G_DEFINE_TYPE_WITH_CODE (GstDataURISrc, gst_data_uri_src, GST_TYPE_BASE_SRC,
82     G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER,
83         gst_data_uri_src_handler_init));
84
85 static void
86 gst_data_uri_src_class_init (GstDataURISrcClass * klass)
87 {
88   GObjectClass *gobject_class = (GObjectClass *) klass;
89   GstElementClass *element_class = (GstElementClass *) klass;
90   GstBaseSrcClass *basesrc_class = (GstBaseSrcClass *) klass;
91
92   gobject_class->finalize = gst_data_uri_src_finalize;
93   gobject_class->set_property = gst_data_uri_src_set_property;
94   gobject_class->get_property = gst_data_uri_src_get_property;
95
96   g_object_class_install_property (gobject_class, PROP_URI,
97       g_param_spec_string ("uri",
98           "URI",
99           "URI that should be used",
100           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
101
102   gst_element_class_add_pad_template (element_class,
103       gst_static_pad_template_get (&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
120 gst_data_uri_src_init (GstDataURISrc * src)
121 {
122 }
123
124 static void
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
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
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_set_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 *
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   if (gst_pad_has_current_caps (GST_BASE_SRC_PAD (basesrc)))
181     caps = gst_pad_get_current_caps (GST_BASE_SRC_PAD (basesrc));
182   else
183     caps = gst_caps_new_any ();
184   GST_OBJECT_UNLOCK (src);
185
186   return caps;
187 }
188
189 static gboolean
190 gst_data_uri_src_get_size (GstBaseSrc * basesrc, guint64 * size)
191 {
192   GstDataURISrc *src = GST_DATA_URI_SRC (basesrc);
193   gboolean ret;
194
195   GST_OBJECT_LOCK (src);
196   if (!src->buffer) {
197     ret = FALSE;
198     *size = -1;
199   } else {
200     ret = TRUE;
201     *size = gst_buffer_get_size (src->buffer);
202   }
203   GST_OBJECT_UNLOCK (src);
204
205   return ret;
206 }
207
208 static gboolean
209 gst_data_uri_src_is_seekable (GstBaseSrc * basesrc)
210 {
211   return TRUE;
212 }
213
214 static GstFlowReturn
215 gst_data_uri_src_create (GstBaseSrc * basesrc, guint64 offset, guint size,
216     GstBuffer ** buf)
217 {
218   GstDataURISrc *src = GST_DATA_URI_SRC (basesrc);
219   GstFlowReturn ret;
220
221   GST_OBJECT_LOCK (src);
222
223   if (!src->buffer)
224     goto no_buffer;
225
226   /* This is only correct because GstBaseSrc already clips size for us to be no
227    * larger than the max. available size if a segment at the end is requested */
228   if (offset + size > gst_buffer_get_size (src->buffer)) {
229     ret = GST_FLOW_EOS;
230   } else if (*buf != NULL) {
231     GstMapInfo src_info;
232     GstMapInfo dest_info;
233     gsize fill_size;
234
235     gst_buffer_map (src->buffer, &src_info, GST_MAP_READ);
236     gst_buffer_map (*buf, &dest_info, GST_MAP_WRITE);
237
238     fill_size = gst_buffer_fill (*buf, 0, src_info.data + offset, size);
239
240     gst_buffer_unmap (*buf, &dest_info);
241     gst_buffer_unmap (src->buffer, &src_info);
242     gst_buffer_set_size (*buf, fill_size);
243     ret = GST_FLOW_OK;
244   } else {
245     *buf =
246         gst_buffer_copy_region (src->buffer, GST_BUFFER_COPY_ALL, offset, size);
247     ret = GST_FLOW_OK;
248   }
249   GST_OBJECT_UNLOCK (src);
250
251   return ret;
252
253 /* ERRORS */
254 no_buffer:
255   {
256     GST_OBJECT_UNLOCK (src);
257     GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL), (NULL));
258     return GST_FLOW_NOT_NEGOTIATED;
259   }
260 }
261
262 static gboolean
263 gst_data_uri_src_start (GstBaseSrc * basesrc)
264 {
265   GstDataURISrc *src = GST_DATA_URI_SRC (basesrc);
266
267   GST_OBJECT_LOCK (src);
268
269   if (src->uri == NULL || *src->uri == '\0' || src->buffer == NULL)
270     goto no_uri;
271
272   GST_OBJECT_UNLOCK (src);
273
274   return TRUE;
275
276 /* ERRORS */
277 no_uri:
278   {
279     GST_OBJECT_UNLOCK (src);
280     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
281         ("No valid data URI specified, or the data URI could not be parsed."),
282         ("%s", src->uri));
283     return FALSE;
284   }
285 }
286
287 static GstURIType
288 gst_data_uri_src_get_uri_type (GType type)
289 {
290   return GST_URI_SRC;
291 }
292
293 static const gchar *const *
294 gst_data_uri_src_get_protocols (GType type)
295 {
296   static const gchar *protocols[] = { "data", 0 };
297
298   return protocols;
299 }
300
301 static gchar *
302 gst_data_uri_src_get_uri (GstURIHandler * handler)
303 {
304   GstDataURISrc *src = GST_DATA_URI_SRC (handler);
305   gchar *src_uri = NULL;
306
307   GST_OBJECT_LOCK (src);
308   src_uri = g_strdup (src->uri);
309   GST_OBJECT_UNLOCK (src);
310   return src_uri;
311 }
312
313 static gboolean
314 gst_data_uri_src_set_uri (GstURIHandler * handler, const gchar * uri,
315     GError ** error)
316 {
317   GstDataURISrc *src = GST_DATA_URI_SRC (handler);
318   gboolean ret = FALSE;
319   gchar *mimetype = NULL;
320   const gchar *parameters_start;
321   const gchar *data_start;
322   const gchar *orig_uri = uri;
323   GstCaps *caps;
324   GstBuffer *buffer;
325   gboolean base64 = FALSE;
326   gchar *charset = NULL;
327   gpointer bdata;
328   gsize bsize;
329
330   GST_OBJECT_LOCK (src);
331   if (GST_STATE (src) >= GST_STATE_PAUSED)
332     goto wrong_state;
333   GST_OBJECT_UNLOCK (src);
334
335   /* uri must be an URI as defined in RFC 2397
336    * data:[<mediatype>][;base64],<data>
337    */
338   if (strncmp ("data:", uri, 5) != 0)
339     goto invalid_uri;
340
341   uri += 5;
342
343   parameters_start = strchr (uri, ';');
344   data_start = strchr (uri, ',');
345   if (data_start == NULL)
346     goto invalid_uri;
347
348   if (data_start != uri && parameters_start != uri)
349     mimetype =
350         g_strndup (uri,
351         (parameters_start ? parameters_start : data_start) - uri);
352   else
353     mimetype = g_strdup ("text/plain");
354
355   GST_DEBUG_OBJECT (src, "Mimetype: %s", mimetype);
356
357   if (parameters_start != NULL) {
358     gchar **walk;
359     gchar *parameters =
360         g_strndup (parameters_start + 1, data_start - parameters_start - 1);
361     gchar **parameters_strv;
362
363     parameters_strv = g_strsplit (parameters, ";", -1);
364
365     GST_DEBUG_OBJECT (src, "Parameters: ");
366     walk = parameters_strv;
367     while (*walk) {
368       GST_DEBUG_OBJECT (src, "\t %s", *walk);
369       if (strcmp ("base64", *walk) == 0) {
370         base64 = TRUE;
371       } else if (strncmp ("charset=", *walk, 8) == 0) {
372         charset = g_strdup (*walk + 8);
373       }
374       walk++;
375     }
376     g_free (parameters);
377     g_strfreev (parameters_strv);
378   }
379
380   /* Skip comma */
381   data_start += 1;
382   if (base64) {
383     bdata = g_base64_decode (data_start, &bsize);
384   } else {
385     /* URI encoded, i.e. "percent" encoding */
386     bdata = g_uri_unescape_string (data_start, NULL);
387     if (bdata == NULL)
388       goto invalid_uri_encoded_data;
389     bsize = strlen (bdata) + 1;
390   }
391   /* Convert to UTF8 */
392   if (strcmp ("text/plain", mimetype) == 0 &&
393       charset && g_ascii_strcasecmp ("US-ASCII", charset) != 0
394       && g_ascii_strcasecmp ("UTF-8", charset) != 0) {
395     gsize read;
396     gsize written;
397     gpointer data;
398
399     data =
400         g_convert_with_fallback (bdata, -1, "UTF-8", charset, (char *) "*",
401         &read, &written, NULL);
402     g_free (bdata);
403
404     bdata = data;
405     bsize = written;
406   }
407   buffer = gst_buffer_new_wrapped (bdata, bsize);
408
409   caps = gst_type_find_helper_for_buffer (GST_OBJECT (src), buffer, NULL);
410   if (!caps)
411     caps = gst_caps_new_empty_simple (mimetype);
412   gst_base_src_set_caps (GST_BASE_SRC_CAST (src), caps);
413   gst_caps_unref (caps);
414
415   GST_OBJECT_LOCK (src);
416   gst_buffer_replace (&src->buffer, buffer);
417   gst_buffer_unref (buffer);
418   g_free (src->uri);
419   src->uri = g_strdup (orig_uri);
420   GST_OBJECT_UNLOCK (src);
421
422   ret = TRUE;
423
424 out:
425
426   g_free (mimetype);
427   g_free (charset);
428
429   return ret;
430
431 wrong_state:
432   {
433     GST_WARNING_OBJECT (src, "Can't set URI in %s state",
434         gst_element_state_get_name (GST_STATE (src)));
435     GST_OBJECT_UNLOCK (src);
436     g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_STATE,
437         "Changing the 'uri' property on dataurisrc while it is running "
438         "is not supported");
439     goto out;
440   }
441 invalid_uri:
442   {
443     GST_WARNING_OBJECT (src, "invalid URI '%s'", uri);
444     g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
445         "Invalid data URI");
446     goto out;
447   }
448 invalid_uri_encoded_data:
449   {
450     GST_WARNING_OBJECT (src, "Failed to parse data encoded in URI '%s'", uri);
451     g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
452         "Could not parse data encoded in data URI");
453     goto out;
454   }
455 }
456
457 static void
458 gst_data_uri_src_handler_init (gpointer g_iface, gpointer iface_data)
459 {
460   GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
461
462   iface->get_type = gst_data_uri_src_get_uri_type;
463   iface->get_protocols = gst_data_uri_src_get_protocols;
464   iface->get_uri = gst_data_uri_src_get_uri;
465   iface->set_uri = gst_data_uri_src_set_uri;
466 }
467
468 static gboolean
469 plugin_init (GstPlugin * plugin)
470 {
471   return gst_element_register (plugin, "dataurisrc",
472       GST_RANK_PRIMARY, GST_TYPE_DATA_URI_SRC);
473 }
474
475 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
476     GST_VERSION_MINOR,
477     dataurisrc,
478     "data: URI source",
479     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);