• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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