ext/gnomevfs/gstgnomevfssrc.c: Treat GNOME_VFS_RESULT_EOF as EOS, not as error (...
[platform/upstream/gstreamer.git] / ext / gnomevfs / gstgnomevfssrc.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *                    2001 Bastien Nocera <hadess@hadess.net>
5  *                    2002 Kristian Rietveld <kris@gtk.org>
6  *                    2002,2003 Colin Walters <walters@gnu.org>
7  *
8  * gnomevfssrc.c:
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public
21  * License along with this library; if not, write to the
22  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23  * Boston, MA 02111-1307, USA.
24  */
25
26 /**
27  * SECTION:element-gnomevfssrc
28  * @short_description: Read from any GnomeVFS-supported location
29  * @see_also: #GstFileSrc, #GstGnomeVFSSink
30  *
31  * <refsect2>
32  * <para>
33  * This plugin reads data from a local or remote location specified
34  * by an URI. This location can be specified using any protocol supported by
35  * the GnomeVFS library. Common protocols are 'file', 'http', 'ftp', or 'smb'.
36  * </para>
37  * <para>
38  * Example pipeline:
39  * <programlisting>
40  * gst-launch -v gnomevfssrc location=file:///home/joe/foo.xyz ! fakesink
41  * </programlisting>
42  * The above pipeline will simply read a local file and do nothing with the
43  * data read. Instead of gnomevfssrc, we could just as well have used the
44  * filesrc element here.
45  * </para>
46  * <para>
47  * Another example pipeline:
48  * <programlisting>
49  * gst-launch -v gnomevfssrc location=smb://othercomputer/foo.xyz ! filesink location=/home/joe/foo.xyz
50  * </programlisting>
51  * The above pipeline will copy a file from a remote host to the local file
52  * system using the Samba protocol.
53  * </para>
54  * <para>
55  * Yet another example pipeline:
56  * <programlisting>
57  * gst-launch -v gnomevfssrc location=http://music.foobar.com/demo.mp3 ! mad ! audioconvert ! audioresample ! alsasink
58  * </programlisting>
59  * The above pipeline will read and decode and play an mp3 file from a
60  * web server using the http protocol.
61  * </para>
62  * </refsect2>
63  *
64  */
65
66
67 #define BROKEN_SIG 1
68 /*#undef BROKEN_SIG */
69
70 #ifdef HAVE_CONFIG_H
71 #include "config.h"
72 #endif
73
74 #include "gst/gst-i18n-plugin.h"
75
76 #include "gstgnomevfssrc.h"
77
78 #include <stdlib.h>
79 #include <sys/types.h>
80 #include <sys/socket.h>
81 #include <sys/time.h>
82 #include <netinet/in.h>
83 #include <arpa/inet.h>
84 #include <netdb.h>
85 #include <sys/stat.h>
86 #include <fcntl.h>
87 #include <unistd.h>
88 #include <sys/mman.h>
89 #include <errno.h>
90 #include <string.h>
91
92 #include <gst/gst.h>
93 /* gnome-vfs.h doesn't include the following header, which we need: */
94 #include <libgnomevfs/gnome-vfs-standard-callbacks.h>
95
96 GST_DEBUG_CATEGORY_STATIC (gnomevfssrc_debug);
97 #define GST_CAT_DEFAULT gnomevfssrc_debug
98
99
100 static GStaticMutex count_lock = G_STATIC_MUTEX_INIT;
101 static gint ref_count = 0;
102 static gboolean vfs_owner = FALSE;
103
104
105 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
106     GST_PAD_SRC,
107     GST_PAD_ALWAYS,
108     GST_STATIC_CAPS_ANY);
109
110 enum
111 {
112   ARG_0,
113   ARG_HANDLE,
114   ARG_LOCATION,
115   ARG_IRADIO_MODE,
116   ARG_IRADIO_NAME,
117   ARG_IRADIO_GENRE,
118   ARG_IRADIO_URL,
119   ARG_IRADIO_TITLE
120 };
121
122 static void gst_gnome_vfs_src_base_init (gpointer g_class);
123 static void gst_gnome_vfs_src_class_init (GstGnomeVFSSrcClass * klass);
124 static void gst_gnome_vfs_src_init (GstGnomeVFSSrc * gnomevfssrc);
125 static void gst_gnome_vfs_src_finalize (GObject * object);
126 static void gst_gnome_vfs_src_uri_handler_init (gpointer g_iface,
127     gpointer iface_data);
128
129 static void gst_gnome_vfs_src_set_property (GObject * object, guint prop_id,
130     const GValue * value, GParamSpec * pspec);
131 static void gst_gnome_vfs_src_get_property (GObject * object, guint prop_id,
132     GValue * value, GParamSpec * pspec);
133
134 static gboolean gst_gnome_vfs_src_stop (GstBaseSrc * src);
135 static gboolean gst_gnome_vfs_src_start (GstBaseSrc * src);
136 static gboolean gst_gnome_vfs_src_is_seekable (GstBaseSrc * src);
137 static gboolean gst_gnome_vfs_src_get_size (GstBaseSrc * src, guint64 * size);
138 static GstFlowReturn gst_gnome_vfs_src_create (GstBaseSrc * basesrc,
139     guint64 offset, guint size, GstBuffer ** buffer);
140
141 static int audiocast_init (GstGnomeVFSSrc * src);
142 static int audiocast_register_listener (gint * port, gint * fd);
143 static void audiocast_do_notifications (GstGnomeVFSSrc * src);
144 static gpointer audiocast_thread_run (GstGnomeVFSSrc * src);
145 static void audiocast_thread_kill (GstGnomeVFSSrc * src);
146
147 static GstElementClass *parent_class = NULL;
148
149 GType
150 gst_gnome_vfs_src_get_type (void)
151 {
152   static GType gnomevfssrc_type = 0;
153
154   if (!gnomevfssrc_type) {
155     static const GTypeInfo gnomevfssrc_info = {
156       sizeof (GstGnomeVFSSrcClass),
157       gst_gnome_vfs_src_base_init,
158       NULL,
159       (GClassInitFunc) gst_gnome_vfs_src_class_init,
160       NULL,
161       NULL,
162       sizeof (GstGnomeVFSSrc),
163       0,
164       (GInstanceInitFunc) gst_gnome_vfs_src_init,
165     };
166     static const GInterfaceInfo urihandler_info = {
167       gst_gnome_vfs_src_uri_handler_init,
168       NULL,
169       NULL
170     };
171
172     gnomevfssrc_type =
173         g_type_register_static (GST_TYPE_BASE_SRC,
174         "GstGnomeVFSSrc", &gnomevfssrc_info, 0);
175     g_type_add_interface_static (gnomevfssrc_type, GST_TYPE_URI_HANDLER,
176         &urihandler_info);
177   }
178   return gnomevfssrc_type;
179 }
180
181 static void
182 gst_gnome_vfs_src_base_init (gpointer g_class)
183 {
184   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
185   static GstElementDetails gst_gnome_vfs_src_details =
186       GST_ELEMENT_DETAILS ("GnomeVFS Source",
187       "Source/File",
188       "Read from any GnomeVFS-supported file",
189       "Bastien Nocera <hadess@hadess.net>\n"
190       "Ronald S. Bultje <rbultje@ronald.bitfreak.net>");
191
192   gst_element_class_add_pad_template (element_class,
193       gst_static_pad_template_get (&srctemplate));
194   gst_element_class_set_details (element_class, &gst_gnome_vfs_src_details);
195
196   GST_DEBUG_CATEGORY_INIT (gnomevfssrc_debug, "gnomevfssrc", 0,
197       "Gnome-VFS Source");
198 }
199
200 static void
201 gst_gnome_vfs_src_class_init (GstGnomeVFSSrcClass * klass)
202 {
203   GObjectClass *gobject_class;
204   GstElementClass *gstelement_class;
205   GstBaseSrcClass *gstbasesrc_class;
206
207   gobject_class = G_OBJECT_CLASS (klass);
208   gstelement_class = GST_ELEMENT_CLASS (klass);
209   gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
210
211   parent_class = g_type_class_peek_parent (klass);
212
213   gobject_class->finalize = gst_gnome_vfs_src_finalize;
214   gobject_class->set_property = gst_gnome_vfs_src_set_property;
215   gobject_class->get_property = gst_gnome_vfs_src_get_property;
216
217   /* properties */
218   gst_element_class_install_std_props (GST_ELEMENT_CLASS (klass),
219       "location", ARG_LOCATION, G_PARAM_READWRITE, NULL);
220   g_object_class_install_property (gobject_class,
221       ARG_HANDLE,
222       g_param_spec_boxed ("handle",
223           "GnomeVFSHandle", "Handle for GnomeVFS",
224           GST_TYPE_GNOME_VFS_HANDLE, G_PARAM_READWRITE));
225
226   /* icecast stuff */
227   g_object_class_install_property (gobject_class,
228       ARG_IRADIO_MODE,
229       g_param_spec_boolean ("iradio-mode",
230           "iradio-mode",
231           "Enable internet radio mode (extraction of icecast/audiocast metadata)",
232           FALSE, G_PARAM_READWRITE));
233   g_object_class_install_property (gobject_class,
234       ARG_IRADIO_NAME,
235       g_param_spec_string ("iradio-name",
236           "iradio-name", "Name of the stream", NULL, G_PARAM_READABLE));
237   g_object_class_install_property (gobject_class,
238       ARG_IRADIO_GENRE,
239       g_param_spec_string ("iradio-genre",
240           "iradio-genre", "Genre of the stream", NULL, G_PARAM_READABLE));
241   g_object_class_install_property (gobject_class,
242       ARG_IRADIO_URL,
243       g_param_spec_string ("iradio-url",
244           "iradio-url",
245           "Homepage URL for radio stream", NULL, G_PARAM_READABLE));
246   g_object_class_install_property (gobject_class,
247       ARG_IRADIO_TITLE,
248       g_param_spec_string ("iradio-title",
249           "iradio-title",
250           "Name of currently playing song", NULL, G_PARAM_READABLE));
251
252   gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_start);
253   gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_stop);
254   gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_get_size);
255   gstbasesrc_class->is_seekable =
256       GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_is_seekable);
257   gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_gnome_vfs_src_create);
258 }
259
260 static void
261 gst_gnome_vfs_src_init (GstGnomeVFSSrc * gnomevfssrc)
262 {
263   gnomevfssrc->uri = NULL;
264   gnomevfssrc->uri_name = NULL;
265   gnomevfssrc->handle = NULL;
266   gnomevfssrc->curoffset = 0;
267   gnomevfssrc->seekable = FALSE;
268
269   gnomevfssrc->icy_metaint = 0;
270   gnomevfssrc->iradio_mode = FALSE;
271   gnomevfssrc->http_callbacks_pushed = FALSE;
272   gnomevfssrc->icy_count = 0;
273   gnomevfssrc->iradio_name = NULL;
274   gnomevfssrc->iradio_genre = NULL;
275   gnomevfssrc->iradio_url = NULL;
276   gnomevfssrc->iradio_title = NULL;
277
278   gnomevfssrc->audiocast_udpdata_mutex = g_mutex_new ();
279   gnomevfssrc->audiocast_queue_mutex = g_mutex_new ();
280   gnomevfssrc->audiocast_notify_queue = NULL;
281   gnomevfssrc->audiocast_thread = NULL;
282
283   g_static_mutex_lock (&count_lock);
284   if (ref_count == 0) {
285     /* gnome vfs engine init */
286     if (gnome_vfs_initialized () == FALSE) {
287       gnome_vfs_init ();
288       vfs_owner = TRUE;
289     }
290   }
291   ref_count++;
292   g_static_mutex_unlock (&count_lock);
293 }
294
295 static void
296 gst_gnome_vfs_src_finalize (GObject * object)
297 {
298   GstGnomeVFSSrc *src = GST_GNOME_VFS_SRC (object);
299
300   g_static_mutex_lock (&count_lock);
301   ref_count--;
302   if (ref_count == 0 && vfs_owner) {
303     if (gnome_vfs_initialized () == TRUE) {
304       gnome_vfs_shutdown ();
305     }
306   }
307   g_static_mutex_unlock (&count_lock);
308
309   if (src->uri) {
310     gnome_vfs_uri_unref (src->uri);
311     src->uri = NULL;
312   }
313
314   if (src->uri_name) {
315     g_free (src->uri_name);
316     src->uri_name = NULL;
317   }
318
319   g_mutex_free (src->audiocast_udpdata_mutex);
320   g_mutex_free (src->audiocast_queue_mutex);
321
322   G_OBJECT_CLASS (parent_class)->finalize (object);
323 }
324
325 /*
326  * URI interface support.
327  */
328
329 static guint
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   static gchar **protocols = NULL;
339
340   if (!protocols)
341     protocols = gst_gnomevfs_get_supported_uris ();
342
343   return protocols;
344 }
345
346 static const gchar *
347 gst_gnome_vfs_src_uri_get_uri (GstURIHandler * handler)
348 {
349   GstGnomeVFSSrc *src = GST_GNOME_VFS_SRC (handler);
350
351   return src->uri_name;
352 }
353
354 static gboolean
355 gst_gnome_vfs_src_uri_set_uri (GstURIHandler * handler, const gchar * uri)
356 {
357   GstGnomeVFSSrc *src = GST_GNOME_VFS_SRC (handler);
358
359   if (GST_STATE (src) == GST_STATE_PLAYING ||
360       GST_STATE (src) == GST_STATE_PAUSED)
361     return FALSE;
362
363   g_object_set (G_OBJECT (src), "location", uri, NULL);
364
365   return TRUE;
366 }
367
368 static void
369 gst_gnome_vfs_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
370 {
371   GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
372
373   iface->get_type = gst_gnome_vfs_src_uri_get_type;
374   iface->get_protocols = gst_gnome_vfs_src_uri_get_protocols;
375   iface->get_uri = gst_gnome_vfs_src_uri_get_uri;
376   iface->set_uri = gst_gnome_vfs_src_uri_set_uri;
377 }
378
379 static void
380 gst_gnome_vfs_src_set_property (GObject * object, guint prop_id,
381     const GValue * value, GParamSpec * pspec)
382 {
383   GstGnomeVFSSrc *src;
384   gchar cwd[PATH_MAX];
385
386   src = GST_GNOME_VFS_SRC (object);
387
388   switch (prop_id) {
389     case ARG_LOCATION:
390       /* the element must be stopped or paused in order to do this */
391       if (GST_STATE (src) == GST_STATE_PLAYING ||
392           GST_STATE (src) == GST_STATE_PAUSED)
393         break;
394
395       if (src->uri) {
396         gnome_vfs_uri_unref (src->uri);
397         src->uri = NULL;
398       }
399       if (src->uri_name) {
400         g_free (src->uri_name);
401         src->uri_name = NULL;
402       }
403
404       if (g_value_get_string (value)) {
405         const gchar *location = g_value_get_string (value);
406
407         if (!strchr (location, ':')) {
408           gchar *newloc = gnome_vfs_escape_path_string (location);
409
410           if (*newloc == '/')
411             src->uri_name = g_strdup_printf ("file://%s", newloc);
412           else
413             src->uri_name =
414                 g_strdup_printf ("file://%s/%s", getcwd (cwd, PATH_MAX),
415                 newloc);
416           g_free (newloc);
417         } else
418           src->uri_name = g_strdup (location);
419
420         src->uri = gnome_vfs_uri_new (src->uri_name);
421       }
422       break;
423     case ARG_HANDLE:
424       if (GST_STATE (src) == GST_STATE_NULL ||
425           GST_STATE (src) == GST_STATE_READY) {
426         if (src->uri) {
427           gnome_vfs_uri_unref (src->uri);
428           src->uri = NULL;
429         }
430         if (src->uri_name) {
431           g_free (src->uri_name);
432           src->uri_name = NULL;
433         }
434         src->handle = g_value_get_boxed (value);
435       }
436       break;
437     case ARG_IRADIO_MODE:
438       src->iradio_mode = g_value_get_boolean (value);
439       break;
440     default:
441       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
442       break;
443   }
444 }
445
446 static void
447 gst_gnome_vfs_src_get_property (GObject * object, guint prop_id, GValue * value,
448     GParamSpec * pspec)
449 {
450   GstGnomeVFSSrc *src;
451
452   src = GST_GNOME_VFS_SRC (object);
453
454   switch (prop_id) {
455     case ARG_LOCATION:
456       g_value_set_string (value, src->uri_name);
457       break;
458     case ARG_HANDLE:
459       g_value_set_boxed (value, src->handle);
460       break;
461     case ARG_IRADIO_MODE:
462       g_value_set_boolean (value, src->iradio_mode);
463       break;
464     case ARG_IRADIO_NAME:
465       g_value_set_string (value, src->iradio_name);
466       break;
467     case ARG_IRADIO_GENRE:
468       g_value_set_string (value, src->iradio_genre);
469       break;
470     case ARG_IRADIO_URL:
471       g_value_set_string (value, src->iradio_url);
472       break;
473     case ARG_IRADIO_TITLE:
474       g_mutex_lock (src->audiocast_udpdata_mutex);
475       g_value_set_string (value, src->iradio_title);
476       g_mutex_unlock (src->audiocast_udpdata_mutex);
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 /*
517  * icecast/audiocast metadata extraction support code
518  */
519
520 static int
521 audiocast_init (GstGnomeVFSSrc * src)
522 {
523   int pipefds[2];
524   GError *error = NULL;
525
526   if (!src->iradio_mode)
527     return TRUE;
528   GST_DEBUG_OBJECT (src, "audiocast: registering listener");
529   if (audiocast_register_listener (&src->audiocast_port,
530           &src->audiocast_fd) < 0) {
531     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
532         ("Unable to listen on UDP port %d", src->audiocast_port));
533     close (src->audiocast_fd);
534     return FALSE;
535   }
536   GST_DEBUG_OBJECT (src, "audiocast: creating pipe");
537   src->audiocast_notify_queue = NULL;
538   if (pipe (pipefds) < 0) {
539     close (src->audiocast_fd);
540     return FALSE;
541   }
542   src->audiocast_thread_die_infd = pipefds[0];
543   src->audiocast_thread_die_outfd = pipefds[1];
544   GST_DEBUG_OBJECT (src, "audiocast: creating audiocast thread");
545   src->audiocast_thread =
546       g_thread_create ((GThreadFunc) audiocast_thread_run, src, TRUE, &error);
547   if (error != NULL) {
548     GST_ELEMENT_ERROR (src, RESOURCE, TOO_LAZY, (NULL),
549         ("Unable to create thread: %s", error->message));
550     close (src->audiocast_fd);
551     return FALSE;
552   }
553   return TRUE;
554 }
555
556 static int
557 audiocast_register_listener (gint * port, gint * fd)
558 {
559   struct sockaddr_in sin;
560   int sock;
561   socklen_t sinlen = sizeof (struct sockaddr_in);
562
563   GST_DEBUG ("audiocast: establishing UDP listener");
564
565   if ((sock = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
566     goto lose;
567
568   memset (&sin, 0, sinlen);
569   sin.sin_family = AF_INET;
570   sin.sin_addr.s_addr = g_htonl (INADDR_ANY);
571
572   if (bind (sock, (struct sockaddr *) &sin, sinlen) < 0)
573     goto lose_and_close;
574
575   memset (&sin, 0, sinlen);
576   if (getsockname (sock, (struct sockaddr *) &sin, &sinlen) < 0)
577     goto lose_and_close;
578
579   GST_DEBUG ("audiocast: listening on local %s:%d", inet_ntoa (sin.sin_addr),
580       g_ntohs (sin.sin_port));
581
582   *port = g_ntohs (sin.sin_port);
583   *fd = sock;
584
585   return 0;
586 lose_and_close:
587   close (sock);
588 lose:
589   return -1;
590 }
591
592 static void
593 audiocast_do_notifications (GstGnomeVFSSrc * src)
594 {
595   /* Send any pending notifications we got from the UDP thread. */
596   if (src->iradio_mode) {
597     GList *entry;
598
599     g_mutex_lock (src->audiocast_queue_mutex);
600     for (entry = src->audiocast_notify_queue; entry; entry = entry->next)
601       g_object_notify (G_OBJECT (src), (const gchar *) entry->data);
602     g_list_free (src->audiocast_notify_queue);
603     src->audiocast_notify_queue = NULL;
604     g_mutex_unlock (src->audiocast_queue_mutex);
605   }
606 }
607
608 static gpointer
609 audiocast_thread_run (GstGnomeVFSSrc * src)
610 {
611   char buf[1025], **lines;
612   gsize len;
613   fd_set fdset, readset;
614   struct sockaddr_in from;
615   socklen_t fromlen = sizeof (struct sockaddr_in);
616
617   FD_ZERO (&fdset);
618
619   FD_SET (src->audiocast_fd, &fdset);
620   FD_SET (src->audiocast_thread_die_infd, &fdset);
621
622   while (1) {
623     GST_DEBUG ("audiocast thread: dropping into select");
624     readset = fdset;
625     if (select (FD_SETSIZE, &readset, NULL, NULL, NULL) < 0) {
626       perror ("select");
627       return NULL;
628     }
629     if (FD_ISSET (src->audiocast_thread_die_infd, &readset)) {
630       char buf[1];
631
632       GST_DEBUG ("audiocast thread: got die character");
633       if (read (src->audiocast_thread_die_infd, buf, 1) != 1)
634         g_warning ("gnomevfssrc: could not read from audiocast fd");
635       close (src->audiocast_thread_die_infd);
636       close (src->audiocast_fd);
637       return NULL;
638     }
639     GST_DEBUG ("audiocast thread: reading data");
640     len =
641         recvfrom (src->audiocast_fd, buf, sizeof (buf) - 1, 0,
642         (struct sockaddr *) &from, &fromlen);
643     if (len < 0 && errno == EAGAIN)
644       continue;
645     else if (len >= 0) {
646       int i;
647       char *valptr, *value;
648
649       buf[len] = '\0';
650       lines = g_strsplit (buf, "\n", 0);
651       if (!lines)
652         continue;
653
654       for (i = 0; lines[i]; i++) {
655         while ((lines[i][strlen (lines[i]) - 1] == '\n') ||
656             (lines[i][strlen (lines[i]) - 1] == '\r'))
657           lines[i][strlen (lines[i]) - 1] = '\0';
658
659         valptr = strchr (lines[i], ':');
660
661         if (!valptr)
662           continue;
663         else
664           valptr++;
665
666         g_strstrip (valptr);
667         if (!strlen (valptr))
668           continue;
669
670         value = gst_gnome_vfs_src_unicodify (valptr);
671         if (!value) {
672           g_print ("Unable to convert \"%s\" to UTF-8!\n", valptr);
673           continue;
674         }
675
676         if (!strncmp (lines[i], "x-audiocast-streamtitle", 23)) {
677           g_mutex_lock (src->audiocast_udpdata_mutex);
678           g_free (src->iradio_title);
679           src->iradio_title = value;
680           g_mutex_unlock (src->audiocast_udpdata_mutex);
681
682           g_mutex_lock (src->audiocast_queue_mutex);
683           src->audiocast_notify_queue =
684               g_list_append (src->audiocast_notify_queue, "iradio-title");
685           GST_DEBUG_OBJECT (src, "audiocast title: %s\n", src->iradio_title);
686           g_mutex_unlock (src->audiocast_queue_mutex);
687         } else if (!strncmp (lines[i], "x-audiocast-streamurl", 21)) {
688           g_mutex_lock (src->audiocast_udpdata_mutex);
689           g_free (src->iradio_url);
690           src->iradio_url = value;
691           g_mutex_unlock (src->audiocast_udpdata_mutex);
692
693           g_mutex_lock (src->audiocast_queue_mutex);
694           src->audiocast_notify_queue =
695               g_list_append (src->audiocast_notify_queue, "iradio-url");
696           GST_DEBUG_OBJECT (src, "audiocast url: %s\n", src->iradio_title);
697           g_mutex_unlock (src->audiocast_queue_mutex);
698         } else if (!strncmp (lines[i], "x-audiocast-udpseqnr", 20)) {
699           gchar outbuf[120];
700
701           sprintf (outbuf, "x-audiocast-ack: %ld \r\n", atol (value));
702           g_free (value);
703
704           if (sendto (src->audiocast_fd, outbuf, strlen (outbuf), 0,
705                   (struct sockaddr *) &from, fromlen) <= 0) {
706             g_print ("Error sending response to server: %s\n",
707                 strerror (errno));
708             continue;
709           }
710           GST_DEBUG_OBJECT (src, "sent audiocast ack: %s\n", outbuf);
711         }
712       }
713       g_strfreev (lines);
714     }
715   }
716   return NULL;
717 }
718
719 static void
720 audiocast_thread_kill (GstGnomeVFSSrc * src)
721 {
722   if (!src->audiocast_thread)
723     return;
724
725   /*
726      We rely on this hack to kill the
727      audiocast thread.  If we get icecast
728      metadata, then we don't need the
729      audiocast metadata too.
730    */
731   GST_DEBUG ("audiocast: writing die character");
732   if (write (src->audiocast_thread_die_outfd, "q", 1) != 1)
733     g_critical ("gnomevfssrc: could not write to audiocast thread fd");
734   close (src->audiocast_thread_die_outfd);
735   GST_DEBUG ("audiocast: joining thread");
736   g_thread_join (src->audiocast_thread);
737   src->audiocast_thread = NULL;
738 }
739
740 static void
741 gst_gnome_vfs_src_send_additional_headers_callback (gconstpointer in,
742     gsize in_size, gpointer out, gsize out_size, gpointer callback_data)
743 {
744   GstGnomeVFSSrc *src = GST_GNOME_VFS_SRC (callback_data);
745   GnomeVFSModuleCallbackAdditionalHeadersOut *out_args =
746       (GnomeVFSModuleCallbackAdditionalHeadersOut *) out;
747
748   if (!src->iradio_mode)
749     return;
750   GST_DEBUG_OBJECT (src, "sending headers\n");
751
752   out_args->headers = g_list_append (out_args->headers,
753       g_strdup ("icy-metadata:1\r\n"));
754   out_args->headers = g_list_append (out_args->headers,
755       g_strdup_printf ("x-audiocast-udpport: %d\r\n", src->audiocast_port));
756 }
757
758 static void
759 gst_gnome_vfs_src_received_headers_callback (gconstpointer in,
760     gsize in_size, gpointer out, gsize out_size, gpointer callback_data)
761 {
762   GList *i;
763   gint icy_metaint;
764   GstGnomeVFSSrc *src = GST_GNOME_VFS_SRC (callback_data);
765   GnomeVFSModuleCallbackReceivedHeadersIn *in_args =
766       (GnomeVFSModuleCallbackReceivedHeadersIn *) in;
767
768   /* This is only used for internet radio stuff right now */
769   if (!src->iradio_mode)
770     return;
771
772   for (i = in_args->headers; i; i = i->next) {
773     char *data = (char *) i->data;
774     char *key = data;
775     char *value = strchr (data, ':');
776
777     if (!value)
778       continue;
779
780     value++;
781     g_strstrip (value);
782     if (!strlen (value))
783       continue;
784
785     /* Icecast stuff */
786     if (strncmp (data, "icy-metaint:", 12) == 0) {      /* ugh */
787       if (sscanf (data + 12, "%d", &icy_metaint) == 1) {
788         src->icy_metaint = icy_metaint;
789         GST_DEBUG_OBJECT (src, "got icy-metaint %d, killing audiocast thread",
790             src->icy_metaint);
791         audiocast_thread_kill (src);
792         continue;
793       }
794     }
795
796     if (!strncmp (data, "icy-", 4))
797       key = data + 4;
798     else if (!strncmp (data, "x-audiocast-", 12))
799       key = data + 12;
800     else
801       continue;
802
803     GST_DEBUG_OBJECT (src, "key: %s", key);
804     if (!strncmp (key, "name", 4)) {
805       g_free (src->iradio_name);
806       src->iradio_name = gst_gnome_vfs_src_unicodify (value);
807       if (src->iradio_name)
808         g_object_notify (G_OBJECT (src), "iradio-name");
809     } else if (!strncmp (key, "genre", 5)) {
810       g_free (src->iradio_genre);
811       src->iradio_genre = gst_gnome_vfs_src_unicodify (value);
812       if (src->iradio_genre)
813         g_object_notify (G_OBJECT (src), "iradio-genre");
814     } else if (!strncmp (key, "url", 3)) {
815       g_free (src->iradio_url);
816       src->iradio_url = gst_gnome_vfs_src_unicodify (value);
817       if (src->iradio_url)
818         g_object_notify (G_OBJECT (src), "iradio-url");
819     }
820   }
821 }
822
823 static void
824 gst_gnome_vfs_src_push_callbacks (GstGnomeVFSSrc * src)
825 {
826   if (src->http_callbacks_pushed)
827     return;
828
829   GST_DEBUG_OBJECT (src, "pushing callbacks");
830   gnome_vfs_module_callback_push
831       (GNOME_VFS_MODULE_CALLBACK_HTTP_SEND_ADDITIONAL_HEADERS,
832       gst_gnome_vfs_src_send_additional_headers_callback, src, NULL);
833   gnome_vfs_module_callback_push
834       (GNOME_VFS_MODULE_CALLBACK_HTTP_RECEIVED_HEADERS,
835       gst_gnome_vfs_src_received_headers_callback, src, NULL);
836
837   src->http_callbacks_pushed = TRUE;
838 }
839
840 static void
841 gst_gnome_vfs_src_pop_callbacks (GstGnomeVFSSrc * src)
842 {
843   if (!src->http_callbacks_pushed)
844     return;
845
846   GST_DEBUG_OBJECT (src, "popping callbacks");
847   gnome_vfs_module_callback_pop
848       (GNOME_VFS_MODULE_CALLBACK_HTTP_SEND_ADDITIONAL_HEADERS);
849   gnome_vfs_module_callback_pop
850       (GNOME_VFS_MODULE_CALLBACK_HTTP_RECEIVED_HEADERS);
851 }
852
853 static void
854 gst_gnome_vfs_src_get_icy_metadata (GstGnomeVFSSrc * src)
855 {
856   GnomeVFSFileSize length = 0;
857   GnomeVFSResult res;
858   gint metadata_length;
859   guchar foobyte;
860   guchar *data;
861   guchar *pos;
862   gchar **tags;
863   int i;
864
865   GST_DEBUG_OBJECT (src, "reading icecast metadata");
866
867   while (length == 0) {
868     res = gnome_vfs_read (src->handle, &foobyte, 1, &length);
869     if (res != GNOME_VFS_OK)
870       return;
871   }
872
873   metadata_length = foobyte * 16;
874
875   if (metadata_length == 0)
876     return;
877
878   data = g_new (guchar, metadata_length + 1);
879   pos = data;
880
881   while (pos - data < metadata_length) {
882     res = gnome_vfs_read (src->handle, pos,
883         metadata_length - (pos - data), &length);
884     /* FIXME: better error handling here? */
885     if (res != GNOME_VFS_OK) {
886       g_free (data);
887       return;
888     }
889
890     pos += length;
891   }
892
893   data[metadata_length] = 0;
894   tags = g_strsplit ((gchar *) data, "';", 0);
895
896   for (i = 0; tags[i]; i++) {
897     if (!g_ascii_strncasecmp (tags[i], "StreamTitle=", 12)) {
898       g_free (src->iradio_title);
899       src->iradio_title = gst_gnome_vfs_src_unicodify (tags[i] + 13);
900       if (src->iradio_title) {
901         GST_DEBUG_OBJECT (src, "sending notification on icecast title");
902         g_object_notify (G_OBJECT (src), "iradio-title");
903       } else
904         g_print ("Unable to convert icecast title \"%s\" to UTF-8!\n",
905             tags[i] + 13);
906
907     }
908     if (!g_ascii_strncasecmp (tags[i], "StreamUrl=", 10)) {
909       g_free (src->iradio_url);
910       src->iradio_url = gst_gnome_vfs_src_unicodify (tags[i] + 11);
911       if (src->iradio_url) {
912         GST_DEBUG_OBJECT (src, "sending notification on icecast url");
913         g_object_notify (G_OBJECT (src), "iradio-url");
914       } else
915         g_print ("Unable to convert icecast url \"%s\" to UTF-8!\n",
916             tags[i] + 11);
917     }
918   }
919
920   g_strfreev (tags);
921 }
922
923 /* end of icecast/audiocast metadata extraction support code */
924
925 /*
926  * Read a new buffer from src->reqoffset, takes care of events
927  * and seeking and such.
928  */
929 static GstFlowReturn
930 gst_gnome_vfs_src_create (GstBaseSrc * basesrc, guint64 offset, guint size,
931     GstBuffer ** buffer)
932 {
933   GnomeVFSResult res;
934   GstBuffer *buf;
935   GnomeVFSFileSize readbytes;
936   guint8 *data;
937   GstGnomeVFSSrc *src;
938
939   src = GST_GNOME_VFS_SRC (basesrc);
940
941   GST_DEBUG ("now at %llu, reading %lld, size %u", src->curoffset, offset,
942       size);
943
944   /* seek if required */
945   if (src->curoffset != offset) {
946     GST_DEBUG ("need to seek");
947     if (src->seekable) {
948       GST_DEBUG ("seeking to %lld", offset);
949       res = gnome_vfs_seek (src->handle, GNOME_VFS_SEEK_START, offset);
950       if (res != GNOME_VFS_OK)
951         goto seek_failed;
952       src->curoffset = offset;
953     } else {
954       goto cannot_seek;
955     }
956   }
957
958   audiocast_do_notifications (src);
959
960   if (src->iradio_mode && src->icy_metaint > 0) {
961     buf = gst_buffer_new_and_alloc (src->icy_metaint);
962
963     data = GST_BUFFER_DATA (buf);
964
965     /* try to read */
966     GST_DEBUG_OBJECT (src, "doing read: icy_count: %" G_GINT64_FORMAT,
967         src->icy_count);
968
969     res = gnome_vfs_read (src->handle, data,
970         src->icy_metaint - src->icy_count, &readbytes);
971
972     if (res == GNOME_VFS_ERROR_EOF || (res == GNOME_VFS_OK && readbytes == 0))
973       goto eos;
974
975     if (res != GNOME_VFS_OK)
976       goto read_failed;
977
978     src->icy_count += readbytes;
979     GST_BUFFER_OFFSET (buf) = src->curoffset;
980     GST_BUFFER_SIZE (buf) = readbytes;
981     src->curoffset += readbytes;
982
983     if (src->icy_count == src->icy_metaint) {
984       gst_gnome_vfs_src_get_icy_metadata (src);
985       src->icy_count = 0;
986     }
987   } else {
988     buf = gst_buffer_new_and_alloc (size);
989
990     data = GST_BUFFER_DATA (buf);
991     GST_BUFFER_OFFSET (buf) = src->curoffset;
992
993     res = gnome_vfs_read (src->handle, data, size, &readbytes);
994
995     if (res == GNOME_VFS_ERROR_EOF || (res == GNOME_VFS_OK && readbytes == 0))
996       goto eos;
997
998     GST_BUFFER_SIZE (buf) = readbytes;
999
1000     if (res != GNOME_VFS_OK)
1001       goto read_failed;
1002
1003     src->curoffset += readbytes;
1004   }
1005
1006   /* we're done, return the buffer */
1007   *buffer = buf;
1008
1009   return GST_FLOW_OK;
1010
1011 seek_failed:
1012   {
1013     GST_ELEMENT_ERROR (src, RESOURCE, SEEK, (NULL),
1014         ("Failed to seek to requested position %" G_GINT64_FORMAT ": %s",
1015             offset, gnome_vfs_result_to_string (res)));
1016     return GST_FLOW_ERROR;
1017   }
1018 cannot_seek:
1019   {
1020     GST_ELEMENT_ERROR (src, RESOURCE, SEEK, (NULL),
1021         ("Requested seek from %" G_GINT64_FORMAT " to %" G_GINT64_FORMAT
1022             "on non-seekable stream", src->curoffset, offset));
1023     return GST_FLOW_ERROR;
1024   }
1025 read_failed:
1026   {
1027     gst_buffer_unref (buf);
1028     GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
1029         ("Failed to read data: %s", gnome_vfs_result_to_string (res)));
1030     return GST_FLOW_ERROR;
1031   }
1032 eos:
1033   {
1034     gst_buffer_unref (buf);
1035     GST_DEBUG_OBJECT (src, "Reading data gave EOS");
1036     gst_pad_push_event (basesrc->srcpad, gst_event_new_eos ());
1037     return GST_FLOW_UNEXPECTED;
1038   }
1039 }
1040
1041 static gboolean
1042 gst_gnome_vfs_src_is_seekable (GstBaseSrc * basesrc)
1043 {
1044   GstGnomeVFSSrc *src;
1045
1046   src = GST_GNOME_VFS_SRC (basesrc);
1047
1048   return src->seekable;
1049 }
1050
1051 static gboolean
1052 gst_gnome_vfs_src_get_size (GstBaseSrc * basesrc, guint64 * size)
1053 {
1054   GstGnomeVFSSrc *src;
1055
1056   src = GST_GNOME_VFS_SRC (basesrc);
1057
1058   GST_DEBUG_OBJECT (src, "size %" G_GUINT64_FORMAT, src->size);
1059
1060   if (src->size == (GnomeVFSFileSize) - 1)
1061     return FALSE;
1062
1063   *size = src->size;
1064
1065   return TRUE;
1066 }
1067
1068 /* open the file, do stuff necessary to go to READY state */
1069 static gboolean
1070 gst_gnome_vfs_src_start (GstBaseSrc * basesrc)
1071 {
1072   GnomeVFSResult res;
1073   GnomeVFSFileInfo *info;
1074   GstGnomeVFSSrc *src;
1075
1076   src = GST_GNOME_VFS_SRC (basesrc);
1077
1078   if (!audiocast_init (src))
1079     return FALSE;
1080
1081   gst_gnome_vfs_src_push_callbacks (src);
1082
1083   if (src->uri != NULL) {
1084     if ((res = gnome_vfs_open_uri (&src->handle, src->uri,
1085                 GNOME_VFS_OPEN_READ)) != GNOME_VFS_OK) {
1086       gchar *filename = gnome_vfs_uri_to_string (src->uri,
1087           GNOME_VFS_URI_HIDE_PASSWORD);
1088
1089       gst_gnome_vfs_src_pop_callbacks (src);
1090       audiocast_thread_kill (src);
1091
1092       if (res == GNOME_VFS_ERROR_NOT_FOUND ||
1093           res == GNOME_VFS_ERROR_SERVICE_NOT_AVAILABLE) {
1094         GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL),
1095             ("Could not open vfs file \"%s\" for reading: %s",
1096                 filename, gnome_vfs_result_to_string (res)));
1097       } else {
1098         GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
1099             ("Could not open vfs file \"%s\" for reading: %s",
1100                 filename, gnome_vfs_result_to_string (res)));
1101       }
1102       g_free (filename);
1103       return FALSE;
1104     }
1105     src->own_handle = TRUE;
1106   } else if (!src->handle) {
1107     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), ("No filename given"));
1108     return FALSE;
1109   } else {
1110     src->own_handle = FALSE;
1111   }
1112
1113   src->size = (GnomeVFSFileSize) - 1;
1114   info = gnome_vfs_file_info_new ();
1115   if ((res = gnome_vfs_get_file_info_from_handle (src->handle,
1116               info, GNOME_VFS_FILE_INFO_DEFAULT)) == GNOME_VFS_OK) {
1117     if (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE) {
1118       src->size = info->size;
1119       GST_DEBUG_OBJECT (src, "size: %llu bytes", src->size);
1120     } else
1121       GST_LOG_OBJECT (src, "filesize not known");
1122   } else {
1123     GST_WARNING_OBJECT (src, "getting info failed: %s",
1124         gnome_vfs_result_to_string (res));
1125   }
1126   gnome_vfs_file_info_unref (info);
1127
1128   audiocast_do_notifications (src);
1129
1130   if (gnome_vfs_seek (src->handle, GNOME_VFS_SEEK_CURRENT, 0)
1131       == GNOME_VFS_OK) {
1132     src->seekable = TRUE;
1133   } else {
1134     src->seekable = FALSE;
1135   }
1136
1137   return TRUE;
1138 }
1139
1140 static gboolean
1141 gst_gnome_vfs_src_stop (GstBaseSrc * basesrc)
1142 {
1143   GstGnomeVFSSrc *src;
1144
1145   src = GST_GNOME_VFS_SRC (basesrc);
1146
1147   gst_gnome_vfs_src_pop_callbacks (src);
1148   audiocast_thread_kill (src);
1149
1150   if (src->own_handle) {
1151     gnome_vfs_close (src->handle);
1152     src->handle = NULL;
1153   }
1154   src->size = (GnomeVFSFileSize) - 1;
1155   src->curoffset = 0;
1156
1157   return TRUE;
1158 }