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