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