1 /* GStreamer |
|
2 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> |
|
3 * 2000 Wim Taymans <wtay@chello.be> |
|
4 * 2001 Bastien Nocera <hadess@hadess.net> |
|
5 * 2002 Kristian Rietveld <kris@gtk.org> |
|
6 * 2002,2003 Colin Walters <walters@gnu.org> |
|
7 * |
|
8 * gnomevfssrc.c: |
|
9 * |
|
10 * This library is free software; you can redistribute it and/or |
|
11 * modify it under the terms of the GNU Library General Public |
|
12 * License as published by the Free Software Foundation; either |
|
13 * version 2 of the License, or (at your option) any later version. |
|
14 * |
|
15 * This library is distributed in the hope that it will be useful, |
|
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
18 * Library General Public License for more details. |
|
19 * |
|
20 * You should have received a copy of the GNU Library General Public |
|
21 * License along with this library; if not, write to the |
|
22 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
|
23 * Boston, MA 02111-1307, USA. |
|
24 */ |
|
25 |
|
26 /** |
|
27 * SECTION:element-gnomevfssrc |
|
28 * @short_description: Read from any GnomeVFS-supported location |
|
29 * @see_also: #GstFileSrc, #GstGnomeVFSSink |
|
30 * |
|
31 * <refsect2> |
|
32 * <para> |
|
33 * This plugin reads data from a local or remote location specified |
|
34 * by an URI. This location can be specified using any protocol supported by |
|
35 * the GnomeVFS library. Common protocols are 'file', 'http', 'ftp', or 'smb'. |
|
36 * </para> |
|
37 * <para> |
|
38 * In case the element-gnomevfssrc::iradio-mode property is set and the |
|
39 * location is a http resource, gnomevfssrc will send special icecast http |
|
40 * headers to the server to request additional icecast metainformation. If |
|
41 * the server is not an icecast server, it will display the same behaviour |
|
42 * as if the element-gnomevfssrc::iradio-mode property was not set. However, |
|
43 * if the server is in fact an icecast server, gnomevfssrc will output |
|
44 * data with a media type of application/x-icy, in which case you will |
|
45 * need to use the #ICYDemux element as follow-up element to extract |
|
46 * the icecast meta data and to determine the underlying media type. |
|
47 * </para> |
|
48 * <para> |
|
49 * Example pipeline: |
|
50 * <programlisting> |
|
51 * gst-launch -v gnomevfssrc location=file:///home/joe/foo.xyz ! fakesink |
|
52 * </programlisting> |
|
53 * The above pipeline will simply read a local file and do nothing with the |
|
54 * data read. Instead of gnomevfssrc, we could just as well have used the |
|
55 * filesrc element here. |
|
56 * </para> |
|
57 * <para> |
|
58 * Another example pipeline: |
|
59 * <programlisting> |
|
60 * gst-launch -v gnomevfssrc location=smb://othercomputer/foo.xyz ! filesink location=/home/joe/foo.xyz |
|
61 * </programlisting> |
|
62 * The above pipeline will copy a file from a remote host to the local file |
|
63 * system using the Samba protocol. |
|
64 * </para> |
|
65 * <para> |
|
66 * Yet another example pipeline: |
|
67 * <programlisting> |
|
68 * gst-launch -v gnomevfssrc location=http://music.foobar.com/demo.mp3 ! mad ! audioconvert ! audioresample ! alsasink |
|
69 * </programlisting> |
|
70 * The above pipeline will read and decode and play an mp3 file from a |
|
71 * web server using the http protocol. |
|
72 * </para> |
|
73 * </refsect2> |
|
74 * |
|
75 */ |
|
76 |
|
77 |
|
78 #define BROKEN_SIG 1 |
|
79 /*#undef BROKEN_SIG */ |
|
80 |
|
81 #ifdef HAVE_CONFIG_H |
|
82 #include "config.h" |
|
83 #endif |
|
84 |
|
85 #include "gst/gst-i18n-plugin.h" |
|
86 |
|
87 #include "gstgnomevfssrc.h" |
|
88 |
|
89 #include <stdlib.h> |
|
90 #include <sys/types.h> |
|
91 #include <sys/socket.h> |
|
92 #include <sys/time.h> |
|
93 #include <netinet/in.h> |
|
94 #include <arpa/inet.h> |
|
95 #include <netdb.h> |
|
96 #include <sys/stat.h> |
|
97 #include <fcntl.h> |
|
98 #include <unistd.h> |
|
99 #include <sys/mman.h> |
|
100 #include <errno.h> |
|
101 #include <string.h> |
|
102 |
|
103 #include <gst/gst.h> |
|
104 #include <gst/tag/tag.h> |
|
105 |
|
106 /* gnome-vfs.h doesn't include the following header, which we need: */ |
|
107 #include <libgnomevfs/gnome-vfs-standard-callbacks.h> |
|
108 |
|
109 GST_DEBUG_CATEGORY_STATIC (gnomevfssrc_debug); |
|
110 #define GST_CAT_DEFAULT gnomevfssrc_debug |
|
111 |
|
112 static const GstElementDetails gst_gnome_vfs_src_details = |
|
113 GST_ELEMENT_DETAILS ("GnomeVFS Source", |
|
114 "Source/File", |
|
115 "Read from any GnomeVFS-supported file", |
|
116 "Bastien Nocera <hadess@hadess.net>\n" |
|
117 "Ronald S. Bultje <rbultje@ronald.bitfreak.net>"); |
|
118 |
|
119 static GStaticMutex count_lock = G_STATIC_MUTEX_INIT; |
|
120 static gint ref_count = 0; |
|
121 static gboolean vfs_owner = FALSE; |
|
122 |
|
123 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", |
|
124 GST_PAD_SRC, |
|
125 GST_PAD_ALWAYS, |
|
126 GST_STATIC_CAPS_ANY); |
|
127 |
|
128 enum |
|
129 { |
|
130 ARG_0, |
|
131 ARG_HANDLE, |
|
132 ARG_LOCATION, |
|
133 ARG_IRADIO_MODE, |
|
134 ARG_IRADIO_NAME, |
|
135 ARG_IRADIO_GENRE, |
|
136 ARG_IRADIO_URL, |
|
137 ARG_IRADIO_TITLE |
|
138 }; |
|
139 |
|
140 static void gst_gnome_vfs_src_base_init (gpointer g_class); |
|
141 static void gst_gnome_vfs_src_class_init (GstGnomeVFSSrcClass * klass); |
|
142 static void gst_gnome_vfs_src_init (GstGnomeVFSSrc * gnomevfssrc); |
|
143 static void gst_gnome_vfs_src_finalize (GObject * object); |
|
144 static void gst_gnome_vfs_src_uri_handler_init (gpointer g_iface, |
|
145 gpointer iface_data); |
|
146 |
|
147 static void gst_gnome_vfs_src_set_property (GObject * object, guint prop_id, |
|
148 const GValue * value, GParamSpec * pspec); |
|
149 static void gst_gnome_vfs_src_get_property (GObject * object, guint prop_id, |
|
150 GValue * value, GParamSpec * pspec); |
|
151 |
|
152 static gboolean gst_gnome_vfs_src_stop (GstBaseSrc * src); |
|
153 static gboolean gst_gnome_vfs_src_start (GstBaseSrc * src); |
|
154 static gboolean gst_gnome_vfs_src_is_seekable (GstBaseSrc * src); |
|
155 static gboolean gst_gnome_vfs_src_check_get_range (GstBaseSrc * src); |
|
156 static gboolean gst_gnome_vfs_src_get_size (GstBaseSrc * src, guint64 * size); |
|
157 static GstFlowReturn gst_gnome_vfs_src_create (GstBaseSrc * basesrc, |
|
158 guint64 offset, guint size, GstBuffer ** buffer); |
|
159 |
|
160 static GstElementClass *parent_class = NULL; |
|
161 |
|
162 GType |
|
163 gst_gnome_vfs_src_get_type (void) |
|
164 { |
|
165 static GType gnomevfssrc_type = 0; |
|
166 |
|
167 if (!gnomevfssrc_type) { |
|
168 static const GTypeInfo gnomevfssrc_info = { |
|
169 sizeof (GstGnomeVFSSrcClass), |
|
170 gst_gnome_vfs_src_base_init, |
|
171 NULL, |
|
172 (GClassInitFunc) gst_gnome_vfs_src_class_init, |
|
173 NULL, |
|
174 NULL, |
|
175 sizeof (GstGnomeVFSSrc), |
|
176 0, |
|
177 (GInstanceInitFunc) gst_gnome_vfs_src_init, |
|
178 }; |
|
179 static const GInterfaceInfo urihandler_info = { |
|
180 gst_gnome_vfs_src_uri_handler_init, |
|
181 NULL, |
|
182 NULL |
|
183 }; |
|
184 |
|
185 gnomevfssrc_type = |
|
186 g_type_register_static (GST_TYPE_BASE_SRC, |
|
187 "GstGnomeVFSSrc", &gnomevfssrc_info, 0); |
|
188 g_type_add_interface_static (gnomevfssrc_type, GST_TYPE_URI_HANDLER, |
|
189 &urihandler_info); |
|
190 } |
|
191 return gnomevfssrc_type; |
|
192 } |
|
193 |
|
194 static void |
|
195 gst_gnome_vfs_src_base_init (gpointer g_class) |
|
196 { |
|
197 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); |
|
198 |
|
199 gst_element_class_add_pad_template (element_class, |
|
200 gst_static_pad_template_get (&srctemplate)); |
|
201 gst_element_class_set_details (element_class, &gst_gnome_vfs_src_details); |
|
202 |
|
203 GST_DEBUG_CATEGORY_INIT (gnomevfssrc_debug, "gnomevfssrc", 0, |
|
204 "Gnome-VFS Source"); |
|
205 } |
|
206 |
|
207 static void |
|
208 gst_gnome_vfs_src_class_init (GstGnomeVFSSrcClass * klass) |
|
209 { |
|
210 GObjectClass *gobject_class; |
|
211 GstElementClass *gstelement_class; |
|
212 GstBaseSrcClass *gstbasesrc_class; |
|
213 |
|
214 gobject_class = G_OBJECT_CLASS (klass); |
|
215 gstelement_class = GST_ELEMENT_CLASS (klass); |
|
216 gstbasesrc_class = GST_BASE_SRC_CLASS (klass); |
|
217 |
|
218 parent_class = g_type_class_peek_parent (klass); |
|
219 |
|
220 gobject_class->finalize = gst_gnome_vfs_src_finalize; |
|
221 gobject_class->set_property = gst_gnome_vfs_src_set_property; |
|
222 gobject_class->get_property = gst_gnome_vfs_src_get_property; |
|
223 |
|
224 /* properties */ |
|
225 gst_element_class_install_std_props (GST_ELEMENT_CLASS (klass), |
|
226 "location", ARG_LOCATION, G_PARAM_READWRITE, NULL); |
|
227 g_object_class_install_property (gobject_class, |
|
228 ARG_HANDLE, |
|
229 g_param_spec_boxed ("handle", |
|
230 "GnomeVFSHandle", "Handle for GnomeVFS", |
|
231 GST_TYPE_GNOME_VFS_HANDLE, G_PARAM_READWRITE)); |
|
232 |
|
233 /* icecast stuff */ |
|
234 g_object_class_install_property (gobject_class, |
|
235 ARG_IRADIO_MODE, |
|
236 g_param_spec_boolean ("iradio-mode", |
|
237 "iradio-mode", |
|
238 "Enable internet radio mode (extraction of shoutcast/icecast metadata)", |
|
239 FALSE, G_PARAM_READWRITE)); |
|
240 g_object_class_install_property (gobject_class, |
|
241 ARG_IRADIO_NAME, |
|
242 g_param_spec_string ("iradio-name", |
|
243 "iradio-name", "Name of the stream", NULL, G_PARAM_READABLE)); |
|
244 g_object_class_install_property (gobject_class, |
|
245 ARG_IRADIO_GENRE, |
|
246 g_param_spec_string ("iradio-genre", |
|
247 "iradio-genre", "Genre of the stream", NULL, G_PARAM_READABLE)); |
|
248 g_object_class_install_property (gobject_class, |
|
249 ARG_IRADIO_URL, |
|
250 g_param_spec_string ("iradio-url", |
|
251 "iradio-url", |
|
252 "Homepage URL for radio stream", NULL, G_PARAM_READABLE)); |
|
253 g_object_class_install_property (gobject_class, |
|
254 ARG_IRADIO_TITLE, |
|
255 g_param_spec_string ("iradio-title", |
|
256 "iradio-title", |
|
257 "Name of currently playing song", NULL, G_PARAM_READABLE)); |
|
258 |
|
259 gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_start); |
|
260 gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_stop); |
|
261 gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_get_size); |
|
262 gstbasesrc_class->is_seekable = |
|
263 GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_is_seekable); |
|
264 gstbasesrc_class->check_get_range = |
|
265 GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_check_get_range); |
|
266 gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_create); |
|
267 } |
|
268 |
|
269 static void |
|
270 gst_gnome_vfs_src_init (GstGnomeVFSSrc * gnomevfssrc) |
|
271 { |
|
272 gnomevfssrc->uri = NULL; |
|
273 gnomevfssrc->uri_name = NULL; |
|
274 gnomevfssrc->handle = NULL; |
|
275 gnomevfssrc->curoffset = 0; |
|
276 gnomevfssrc->seekable = FALSE; |
|
277 |
|
278 gnomevfssrc->icy_caps = NULL; |
|
279 gnomevfssrc->iradio_mode = FALSE; |
|
280 gnomevfssrc->http_callbacks_pushed = FALSE; |
|
281 gnomevfssrc->iradio_name = NULL; |
|
282 gnomevfssrc->iradio_genre = NULL; |
|
283 gnomevfssrc->iradio_url = NULL; |
|
284 gnomevfssrc->iradio_title = NULL; |
|
285 |
|
286 g_static_mutex_lock (&count_lock); |
|
287 if (ref_count == 0) { |
|
288 /* gnome vfs engine init */ |
|
289 if (gnome_vfs_initialized () == FALSE) { |
|
290 gnome_vfs_init (); |
|
291 vfs_owner = TRUE; |
|
292 } |
|
293 } |
|
294 ref_count++; |
|
295 g_static_mutex_unlock (&count_lock); |
|
296 } |
|
297 |
|
298 static void |
|
299 gst_gnome_vfs_src_finalize (GObject * object) |
|
300 { |
|
301 GstGnomeVFSSrc *src = GST_GNOME_VFS_SRC (object); |
|
302 |
|
303 g_static_mutex_lock (&count_lock); |
|
304 ref_count--; |
|
305 if (ref_count == 0 && vfs_owner) { |
|
306 if (gnome_vfs_initialized () == TRUE) { |
|
307 gnome_vfs_shutdown (); |
|
308 } |
|
309 } |
|
310 g_static_mutex_unlock (&count_lock); |
|
311 |
|
312 if (src->uri) { |
|
313 gnome_vfs_uri_unref (src->uri); |
|
314 src->uri = NULL; |
|
315 } |
|
316 |
|
317 g_free (src->uri_name); |
|
318 src->uri_name = NULL; |
|
319 |
|
320 g_free (src->iradio_name); |
|
321 src->iradio_name = NULL; |
|
322 |
|
323 g_free (src->iradio_genre); |
|
324 src->iradio_genre = NULL; |
|
325 |
|
326 g_free (src->iradio_url); |
|
327 src->iradio_url = NULL; |
|
328 |
|
329 g_free (src->iradio_title); |
|
330 src->iradio_title = NULL; |
|
331 |
|
332 if (src->icy_caps) { |
|
333 gst_caps_unref (src->icy_caps); |
|
334 src->icy_caps = NULL; |
|
335 } |
|
336 |
|
337 G_OBJECT_CLASS (parent_class)->finalize (object); |
|
338 } |
|
339 |
|
340 /* |
|
341 * URI interface support. |
|
342 */ |
|
343 |
|
344 static GstURIType |
|
345 gst_gnome_vfs_src_uri_get_type (void) |
|
346 { |
|
347 return GST_URI_SRC; |
|
348 } |
|
349 |
|
350 static gchar ** |
|
351 gst_gnome_vfs_src_uri_get_protocols (void) |
|
352 { |
|
353 static gchar **protocols = NULL; |
|
354 |
|
355 if (!protocols) |
|
356 protocols = gst_gnomevfs_get_supported_uris (); |
|
357 |
|
358 return protocols; |
|
359 } |
|
360 |
|
361 static const gchar * |
|
362 gst_gnome_vfs_src_uri_get_uri (GstURIHandler * handler) |
|
363 { |
|
364 GstGnomeVFSSrc *src = GST_GNOME_VFS_SRC (handler); |
|
365 |
|
366 return src->uri_name; |
|
367 } |
|
368 |
|
369 static gboolean |
|
370 gst_gnome_vfs_src_uri_set_uri (GstURIHandler * handler, const gchar * uri) |
|
371 { |
|
372 GstGnomeVFSSrc *src = GST_GNOME_VFS_SRC (handler); |
|
373 |
|
374 if (GST_STATE (src) == GST_STATE_PLAYING || |
|
375 GST_STATE (src) == GST_STATE_PAUSED) |
|
376 return FALSE; |
|
377 |
|
378 g_object_set (G_OBJECT (src), "location", uri, NULL); |
|
379 |
|
380 return TRUE; |
|
381 } |
|
382 |
|
383 static void |
|
384 gst_gnome_vfs_src_uri_handler_init (gpointer g_iface, gpointer iface_data) |
|
385 { |
|
386 GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface; |
|
387 |
|
388 iface->get_type = gst_gnome_vfs_src_uri_get_type; |
|
389 iface->get_protocols = gst_gnome_vfs_src_uri_get_protocols; |
|
390 iface->get_uri = gst_gnome_vfs_src_uri_get_uri; |
|
391 iface->set_uri = gst_gnome_vfs_src_uri_set_uri; |
|
392 } |
|
393 |
|
394 static void |
|
395 gst_gnome_vfs_src_set_property (GObject * object, guint prop_id, |
|
396 const GValue * value, GParamSpec * pspec) |
|
397 { |
|
398 GstGnomeVFSSrc *src; |
|
399 |
|
400 src = GST_GNOME_VFS_SRC (object); |
|
401 |
|
402 switch (prop_id) { |
|
403 case ARG_LOCATION:{ |
|
404 const gchar *new_location; |
|
405 |
|
406 /* the element must be stopped or paused in order to do this */ |
|
407 if (GST_STATE (src) == GST_STATE_PLAYING || |
|
408 GST_STATE (src) == GST_STATE_PAUSED) |
|
409 break; |
|
410 |
|
411 if (src->uri) { |
|
412 gnome_vfs_uri_unref (src->uri); |
|
413 src->uri = NULL; |
|
414 } |
|
415 if (src->uri_name) { |
|
416 g_free (src->uri_name); |
|
417 src->uri_name = NULL; |
|
418 } |
|
419 |
|
420 new_location = g_value_get_string (value); |
|
421 if (new_location) { |
|
422 src->uri_name = gst_gnome_vfs_location_to_uri_string (new_location); |
|
423 src->uri = gnome_vfs_uri_new (src->uri_name); |
|
424 } |
|
425 break; |
|
426 } |
|
427 case ARG_HANDLE: |
|
428 if (GST_STATE (src) == GST_STATE_NULL || |
|
429 GST_STATE (src) == GST_STATE_READY) { |
|
430 if (src->uri) { |
|
431 gnome_vfs_uri_unref (src->uri); |
|
432 src->uri = NULL; |
|
433 } |
|
434 if (src->uri_name) { |
|
435 g_free (src->uri_name); |
|
436 src->uri_name = NULL; |
|
437 } |
|
438 src->handle = g_value_get_boxed (value); |
|
439 } |
|
440 break; |
|
441 case ARG_IRADIO_MODE: |
|
442 src->iradio_mode = g_value_get_boolean (value); |
|
443 break; |
|
444 default: |
|
445 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
|
446 break; |
|
447 } |
|
448 } |
|
449 |
|
450 static void |
|
451 gst_gnome_vfs_src_get_property (GObject * object, guint prop_id, GValue * value, |
|
452 GParamSpec * pspec) |
|
453 { |
|
454 GstGnomeVFSSrc *src; |
|
455 |
|
456 src = GST_GNOME_VFS_SRC (object); |
|
457 |
|
458 switch (prop_id) { |
|
459 case ARG_LOCATION: |
|
460 g_value_set_string (value, src->uri_name); |
|
461 break; |
|
462 case ARG_HANDLE: |
|
463 g_value_set_boxed (value, src->handle); |
|
464 break; |
|
465 case ARG_IRADIO_MODE: |
|
466 g_value_set_boolean (value, src->iradio_mode); |
|
467 break; |
|
468 case ARG_IRADIO_NAME: |
|
469 g_value_set_string (value, src->iradio_name); |
|
470 break; |
|
471 case ARG_IRADIO_GENRE: |
|
472 g_value_set_string (value, src->iradio_genre); |
|
473 break; |
|
474 case ARG_IRADIO_URL: |
|
475 g_value_set_string (value, src->iradio_url); |
|
476 break; |
|
477 case ARG_IRADIO_TITLE: |
|
478 g_value_set_string (value, src->iradio_title); |
|
479 break; |
|
480 default: |
|
481 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
|
482 break; |
|
483 } |
|
484 } |
|
485 |
|
486 static char * |
|
487 gst_gnome_vfs_src_unicodify (const char *str) |
|
488 { |
|
489 const gchar *env_vars[] = { "GST_ICY_TAG_ENCODING", |
|
490 "GST_TAG_ENCODING", NULL |
|
491 }; |
|
492 |
|
493 return gst_tag_freeform_string_to_utf8 (str, -1, env_vars); |
|
494 } |
|
495 |
|
496 static void |
|
497 gst_gnome_vfs_src_send_additional_headers_callback (gconstpointer in, |
|
498 gsize in_size, gpointer out, gsize out_size, gpointer callback_data) |
|
499 { |
|
500 GstGnomeVFSSrc *src = GST_GNOME_VFS_SRC (callback_data); |
|
501 GnomeVFSModuleCallbackAdditionalHeadersOut *out_args = |
|
502 (GnomeVFSModuleCallbackAdditionalHeadersOut *) out; |
|
503 |
|
504 if (!src->iradio_mode) |
|
505 return; |
|
506 GST_DEBUG_OBJECT (src, "sending headers\n"); |
|
507 |
|
508 out_args->headers = g_list_append (out_args->headers, |
|
509 g_strdup ("icy-metadata:1\r\n")); |
|
510 } |
|
511 |
|
512 static void |
|
513 gst_gnome_vfs_src_received_headers_callback (gconstpointer in, |
|
514 gsize in_size, gpointer out, gsize out_size, gpointer callback_data) |
|
515 { |
|
516 GList *i; |
|
517 gint icy_metaint; |
|
518 GstGnomeVFSSrc *src = GST_GNOME_VFS_SRC (callback_data); |
|
519 GnomeVFSModuleCallbackReceivedHeadersIn *in_args = |
|
520 (GnomeVFSModuleCallbackReceivedHeadersIn *) in; |
|
521 |
|
522 /* This is only used for internet radio stuff right now */ |
|
523 if (!src->iradio_mode) |
|
524 return; |
|
525 |
|
526 for (i = in_args->headers; i; i = i->next) { |
|
527 char *data = (char *) i->data; |
|
528 char *key = data; |
|
529 char *value = strchr (data, ':'); |
|
530 |
|
531 if (!value) |
|
532 continue; |
|
533 |
|
534 value++; |
|
535 g_strstrip (value); |
|
536 if (!strlen (value)) |
|
537 continue; |
|
538 |
|
539 /* Icecast stuff */ |
|
540 if (strncmp (data, "icy-metaint:", 12) == 0) { /* ugh */ |
|
541 if (sscanf (data + 12, "%d", &icy_metaint) == 1) { |
|
542 if (icy_metaint > 0) |
|
543 src->icy_caps = gst_caps_new_simple ("application/x-icy", |
|
544 "metadata-interval", G_TYPE_INT, icy_metaint, NULL); |
|
545 } |
|
546 continue; |
|
547 } |
|
548 |
|
549 if (!strncmp (data, "icy-", 4)) |
|
550 key = data + 4; |
|
551 else |
|
552 continue; |
|
553 |
|
554 GST_DEBUG_OBJECT (src, "key: %s", key); |
|
555 if (!strncmp (key, "name", 4)) { |
|
556 g_free (src->iradio_name); |
|
557 src->iradio_name = gst_gnome_vfs_src_unicodify (value); |
|
558 if (src->iradio_name) |
|
559 g_object_notify (G_OBJECT (src), "iradio-name"); |
|
560 } else if (!strncmp (key, "genre", 5)) { |
|
561 g_free (src->iradio_genre); |
|
562 src->iradio_genre = gst_gnome_vfs_src_unicodify (value); |
|
563 if (src->iradio_genre) |
|
564 g_object_notify (G_OBJECT (src), "iradio-genre"); |
|
565 } else if (!strncmp (key, "url", 3)) { |
|
566 g_free (src->iradio_url); |
|
567 src->iradio_url = gst_gnome_vfs_src_unicodify (value); |
|
568 if (src->iradio_url) |
|
569 g_object_notify (G_OBJECT (src), "iradio-url"); |
|
570 } |
|
571 } |
|
572 } |
|
573 |
|
574 static void |
|
575 gst_gnome_vfs_src_push_callbacks (GstGnomeVFSSrc * src) |
|
576 { |
|
577 if (src->http_callbacks_pushed) |
|
578 return; |
|
579 |
|
580 GST_DEBUG_OBJECT (src, "pushing callbacks"); |
|
581 gnome_vfs_module_callback_push |
|
582 (GNOME_VFS_MODULE_CALLBACK_HTTP_SEND_ADDITIONAL_HEADERS, |
|
583 gst_gnome_vfs_src_send_additional_headers_callback, src, NULL); |
|
584 gnome_vfs_module_callback_push |
|
585 (GNOME_VFS_MODULE_CALLBACK_HTTP_RECEIVED_HEADERS, |
|
586 gst_gnome_vfs_src_received_headers_callback, src, NULL); |
|
587 |
|
588 src->http_callbacks_pushed = TRUE; |
|
589 } |
|
590 |
|
591 static void |
|
592 gst_gnome_vfs_src_pop_callbacks (GstGnomeVFSSrc * src) |
|
593 { |
|
594 if (!src->http_callbacks_pushed) |
|
595 return; |
|
596 |
|
597 GST_DEBUG_OBJECT (src, "popping callbacks"); |
|
598 gnome_vfs_module_callback_pop |
|
599 (GNOME_VFS_MODULE_CALLBACK_HTTP_SEND_ADDITIONAL_HEADERS); |
|
600 gnome_vfs_module_callback_pop |
|
601 (GNOME_VFS_MODULE_CALLBACK_HTTP_RECEIVED_HEADERS); |
|
602 |
|
603 src->http_callbacks_pushed = FALSE; |
|
604 } |
|
605 |
|
606 /* |
|
607 * Read a new buffer from src->reqoffset, takes care of events |
|
608 * and seeking and such. |
|
609 */ |
|
610 static GstFlowReturn |
|
611 gst_gnome_vfs_src_create (GstBaseSrc * basesrc, guint64 offset, guint size, |
|
612 GstBuffer ** buffer) |
|
613 { |
|
614 GnomeVFSResult res; |
|
615 GstBuffer *buf; |
|
616 GnomeVFSFileSize readbytes; |
|
617 guint8 *data; |
|
618 GstGnomeVFSSrc *src; |
|
619 |
|
620 src = GST_GNOME_VFS_SRC (basesrc); |
|
621 |
|
622 GST_DEBUG ("now at %llu, reading from %lld, size %u", src->curoffset, offset, |
|
623 size); |
|
624 |
|
625 /* seek if required */ |
|
626 if (G_UNLIKELY (src->curoffset != offset)) { |
|
627 GST_DEBUG ("need to seek"); |
|
628 if (src->seekable) { |
|
629 GST_DEBUG ("seeking to %lld", offset); |
|
630 res = gnome_vfs_seek (src->handle, GNOME_VFS_SEEK_START, offset); |
|
631 if (res != GNOME_VFS_OK) |
|
632 goto seek_failed; |
|
633 src->curoffset = offset; |
|
634 } else { |
|
635 goto cannot_seek; |
|
636 } |
|
637 } |
|
638 |
|
639 buf = gst_buffer_new_and_alloc (size); |
|
640 |
|
641 if (src->icy_caps) |
|
642 gst_buffer_set_caps (buf, src->icy_caps); |
|
643 |
|
644 data = GST_BUFFER_DATA (buf); |
|
645 GST_BUFFER_OFFSET (buf) = src->curoffset; |
|
646 |
|
647 res = gnome_vfs_read (src->handle, data, size, &readbytes); |
|
648 |
|
649 if (G_UNLIKELY (res == GNOME_VFS_ERROR_EOF || (res == GNOME_VFS_OK |
|
650 && readbytes == 0))) |
|
651 goto eos; |
|
652 |
|
653 GST_BUFFER_SIZE (buf) = readbytes; |
|
654 |
|
655 if (G_UNLIKELY (res != GNOME_VFS_OK)) |
|
656 goto read_failed; |
|
657 |
|
658 src->curoffset += readbytes; |
|
659 |
|
660 /* we're done, return the buffer */ |
|
661 *buffer = buf; |
|
662 |
|
663 return GST_FLOW_OK; |
|
664 |
|
665 seek_failed: |
|
666 { |
|
667 GST_ELEMENT_ERROR (src, RESOURCE, SEEK, (NULL), |
|
668 ("Failed to seek to requested position %" G_GINT64_FORMAT ": %s", |
|
669 offset, gnome_vfs_result_to_string (res))); |
|
670 return GST_FLOW_ERROR; |
|
671 } |
|
672 cannot_seek: |
|
673 { |
|
674 GST_ELEMENT_ERROR (src, RESOURCE, SEEK, (NULL), |
|
675 ("Requested seek from %" G_GINT64_FORMAT " to %" G_GINT64_FORMAT |
|
676 " on non-seekable stream", src->curoffset, offset)); |
|
677 return GST_FLOW_ERROR; |
|
678 } |
|
679 read_failed: |
|
680 { |
|
681 gst_buffer_unref (buf); |
|
682 GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), |
|
683 ("Failed to read data: %s", gnome_vfs_result_to_string (res))); |
|
684 return GST_FLOW_ERROR; |
|
685 } |
|
686 eos: |
|
687 { |
|
688 gst_buffer_unref (buf); |
|
689 GST_DEBUG_OBJECT (src, "Reading data gave EOS"); |
|
690 return GST_FLOW_UNEXPECTED; |
|
691 } |
|
692 } |
|
693 |
|
694 static gboolean |
|
695 gst_gnome_vfs_src_is_seekable (GstBaseSrc * basesrc) |
|
696 { |
|
697 GstGnomeVFSSrc *src; |
|
698 |
|
699 src = GST_GNOME_VFS_SRC (basesrc); |
|
700 |
|
701 return src->seekable; |
|
702 } |
|
703 |
|
704 static gboolean |
|
705 gst_gnome_vfs_src_check_get_range (GstBaseSrc * basesrc) |
|
706 { |
|
707 GstGnomeVFSSrc *src; |
|
708 const gchar *protocol; |
|
709 |
|
710 src = GST_GNOME_VFS_SRC (basesrc); |
|
711 |
|
712 if (src->uri == NULL) { |
|
713 GST_WARNING_OBJECT (src, "no URI set yet"); |
|
714 return FALSE; |
|
715 } |
|
716 |
|
717 if (gnome_vfs_uri_is_local (src->uri)) { |
|
718 GST_LOG_OBJECT (src, "local URI (%s), assuming random access is possible", |
|
719 GST_STR_NULL (src->uri_name)); |
|
720 return TRUE; |
|
721 } |
|
722 |
|
723 /* blacklist certain protocols we know won't work getrange-based */ |
|
724 protocol = gnome_vfs_uri_get_scheme (src->uri); |
|
725 if (protocol == NULL) |
|
726 goto undecided; |
|
727 |
|
728 if (strcmp (protocol, "http") == 0 || strcmp (protocol, "https") == 0) { |
|
729 GST_LOG_OBJECT (src, "blacklisted protocol '%s', no random access possible" |
|
730 " (URI=%s)", protocol, GST_STR_NULL (src->uri_name)); |
|
731 return FALSE; |
|
732 } |
|
733 |
|
734 /* fall through to undecided */ |
|
735 |
|
736 undecided: |
|
737 { |
|
738 /* don't know what to do, let the basesrc class decide for us */ |
|
739 GST_LOG_OBJECT (src, "undecided about URI '%s', let base class handle it", |
|
740 GST_STR_NULL (src->uri_name)); |
|
741 |
|
742 if (GST_BASE_SRC_CLASS (parent_class)->check_get_range) |
|
743 return GST_BASE_SRC_CLASS (parent_class)->check_get_range (basesrc); |
|
744 |
|
745 return FALSE; |
|
746 } |
|
747 } |
|
748 |
|
749 static gboolean |
|
750 gst_gnome_vfs_src_get_size (GstBaseSrc * basesrc, guint64 * size) |
|
751 { |
|
752 GstGnomeVFSSrc *src; |
|
753 GnomeVFSFileInfo *info; |
|
754 GnomeVFSFileInfoOptions options; |
|
755 GnomeVFSResult res; |
|
756 |
|
757 src = GST_GNOME_VFS_SRC (basesrc); |
|
758 |
|
759 *size = -1; |
|
760 info = gnome_vfs_file_info_new (); |
|
761 options = GNOME_VFS_FILE_INFO_DEFAULT | GNOME_VFS_FILE_INFO_FOLLOW_LINKS; |
|
762 res = gnome_vfs_get_file_info_from_handle (src->handle, info, options); |
|
763 if (res == GNOME_VFS_OK) { |
|
764 if ((info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE) != 0) { |
|
765 *size = info->size; |
|
766 GST_DEBUG_OBJECT (src, "from handle: %" G_GUINT64_FORMAT " bytes", *size); |
|
767 } else if (src->own_handle && gnome_vfs_uri_is_local (src->uri)) { |
|
768 GST_DEBUG_OBJECT (src, |
|
769 "file size not known, file local, trying fallback"); |
|
770 res = gnome_vfs_get_file_info_uri (src->uri, info, options); |
|
771 if (res == GNOME_VFS_OK && |
|
772 (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE) != 0) { |
|
773 *size = info->size; |
|
774 GST_DEBUG_OBJECT (src, "from uri: %" G_GUINT64_FORMAT " bytes", *size); |
|
775 } |
|
776 } |
|
777 } else { |
|
778 GST_WARNING_OBJECT (src, "getting info failed: %s", |
|
779 gnome_vfs_result_to_string (res)); |
|
780 } |
|
781 gnome_vfs_file_info_unref (info); |
|
782 |
|
783 if (*size == (GnomeVFSFileSize) - 1) |
|
784 return FALSE; |
|
785 |
|
786 GST_DEBUG_OBJECT (src, "return size %" G_GUINT64_FORMAT, *size); |
|
787 |
|
788 return TRUE; |
|
789 } |
|
790 |
|
791 /* open the file, do stuff necessary to go to PAUSED state */ |
|
792 static gboolean |
|
793 gst_gnome_vfs_src_start (GstBaseSrc * basesrc) |
|
794 { |
|
795 GnomeVFSResult res; |
|
796 GstGnomeVFSSrc *src; |
|
797 |
|
798 src = GST_GNOME_VFS_SRC (basesrc); |
|
799 |
|
800 gst_gnome_vfs_src_push_callbacks (src); |
|
801 |
|
802 if (src->uri != NULL) { |
|
803 GnomeVFSOpenMode mode = GNOME_VFS_OPEN_READ; |
|
804 |
|
805 /* this can block... */ |
|
806 res = gnome_vfs_open_uri (&src->handle, src->uri, mode); |
|
807 if (res != GNOME_VFS_OK) |
|
808 goto open_failed; |
|
809 src->own_handle = TRUE; |
|
810 } else if (!src->handle) { |
|
811 goto no_filename; |
|
812 } else { |
|
813 src->own_handle = FALSE; |
|
814 } |
|
815 |
|
816 if (gnome_vfs_seek (src->handle, GNOME_VFS_SEEK_CURRENT, 0) == GNOME_VFS_OK) { |
|
817 src->seekable = TRUE; |
|
818 } else { |
|
819 src->seekable = FALSE; |
|
820 } |
|
821 |
|
822 return TRUE; |
|
823 |
|
824 /* ERRORS */ |
|
825 open_failed: |
|
826 { |
|
827 gchar *filename = gnome_vfs_uri_to_string (src->uri, |
|
828 GNOME_VFS_URI_HIDE_PASSWORD); |
|
829 |
|
830 gst_gnome_vfs_src_pop_callbacks (src); |
|
831 |
|
832 if (res == GNOME_VFS_ERROR_NOT_FOUND || |
|
833 res == GNOME_VFS_ERROR_HOST_NOT_FOUND || |
|
834 res == GNOME_VFS_ERROR_SERVICE_NOT_AVAILABLE) { |
|
835 GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL), |
|
836 ("Could not open vfs file \"%s\" for reading: %s (%d)", |
|
837 filename, gnome_vfs_result_to_string (res), res)); |
|
838 } else { |
|
839 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), |
|
840 ("Could not open vfs file \"%s\" for reading: %s (%d)", |
|
841 filename, gnome_vfs_result_to_string (res), res)); |
|
842 } |
|
843 g_free (filename); |
|
844 return FALSE; |
|
845 } |
|
846 no_filename: |
|
847 { |
|
848 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), ("No filename given")); |
|
849 return FALSE; |
|
850 } |
|
851 } |
|
852 |
|
853 static gboolean |
|
854 gst_gnome_vfs_src_stop (GstBaseSrc * basesrc) |
|
855 { |
|
856 GstGnomeVFSSrc *src; |
|
857 |
|
858 src = GST_GNOME_VFS_SRC (basesrc); |
|
859 |
|
860 gst_gnome_vfs_src_pop_callbacks (src); |
|
861 |
|
862 if (src->own_handle) { |
|
863 GnomeVFSResult res; |
|
864 |
|
865 res = gnome_vfs_close (src->handle); |
|
866 if (res != GNOME_VFS_OK) { |
|
867 GST_ELEMENT_ERROR (src, RESOURCE, CLOSE, (NULL), |
|
868 ("Could not close vfs handle: %s", gnome_vfs_result_to_string (res))); |
|
869 } |
|
870 src->handle = NULL; |
|
871 } |
|
872 src->curoffset = 0; |
|
873 |
|
874 if (src->icy_caps) { |
|
875 gst_caps_unref (src->icy_caps); |
|
876 src->icy_caps = NULL; |
|
877 } |
|
878 |
|
879 return TRUE; |
|
880 } |
|