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