element: Enforce that elements created by gst_element_factory_create/make() are floating
[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  * @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
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
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_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 *
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
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
208 gst_data_uri_src_is_seekable (GstBaseSrc * basesrc)
209 {
210   return TRUE;
211 }
212
213 static GstFlowReturn
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
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
287 gst_data_uri_src_get_uri_type (GType type)
288 {
289   return GST_URI_SRC;
290 }
291
292 static const gchar *const *
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 *
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
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
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 }