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