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