gstgnomevfssrc.c 33.1 KB
Newer Older
1 2 3 4
/* GStreamer
 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
 *                    2000 Wim Taymans <wtay@chello.be>
 *                    2001 Bastien Nocera <hadess@hadess.net>
5
 *                    2002 Kristian Rietveld <kris@gtk.org>
6
 *                    2002,2003 Colin Walters <walters@gnu.org>
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 *
 * gnomevfssrc.c:
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#define BROKEN_SIG 1
/*#undef BROKEN_SIG */

29 30 31 32
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

33
#include <sys/types.h>
34 35 36 37 38
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
39 40 41 42
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
Wim Taymans's avatar
Wim Taymans committed
43 44
#include <errno.h>
#include <string.h>
45 46 47

#include <gst/gst.h>
#include <libgnomevfs/gnome-vfs.h>
48 49
/* gnome-vfs.h doesn't include the following header, which we need: */
#include <libgnomevfs/gnome-vfs-standard-callbacks.h>
50 51 52 53 54 55 56 57 58 59 60 61 62 63

GstElementDetails gst_gnomevfssrc_details;

#define GST_TYPE_GNOMEVFSSRC \
  (gst_gnomevfssrc_get_type())
#define GST_GNOMEVFSSRC(obj) \
  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GNOMEVFSSRC,GstGnomeVFSSrc))
#define GST_GNOMEVFSSRC_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GNOMEVFSSRC,GstGnomeVFSSrcClass))
#define GST_IS_GNOMEVFSSRC(obj) \
  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GNOMEVFSSRC))
#define GST_IS_GNOMEVFSSRC_CLASS(obj) \
  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GNOMEVFSSRC))

64 65
static GStaticMutex count_lock = G_STATIC_MUTEX_INIT;
static gint ref_count = 0;
66
static gboolean vfs_owner = FALSE;
67

68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
typedef enum {
	GST_GNOMEVFSSRC_OPEN = GST_ELEMENT_FLAG_LAST,

	GST_GNOMEVFSSRC_FLAG_LAST = GST_ELEMENT_FLAG_LAST + 2,
} GstGnomeVFSSrcFlags;

typedef struct _GstGnomeVFSSrc GstGnomeVFSSrc;
typedef struct _GstGnomeVFSSrcClass GstGnomeVFSSrcClass;

struct _GstGnomeVFSSrc {
	GstElement element;
	/* pads */
	GstPad *srcpad;

	/* filename */
	gchar *filename;
	/* uri */
	GnomeVFSURI *uri;

	/* handle */
	GnomeVFSHandle *handle;
89 90
	/* Seek stuff */
	gboolean need_flush;
91 92 93 94 95 96

	/* details for fallback synchronous read */
	GnomeVFSFileSize size;
	GnomeVFSFileOffset curoffset;	/* current offset in file */
	gulong bytes_per_read;		/* bytes per read */
	gboolean new_seek;
97
	gboolean in_first_get;
98 99 100

	/* icecast/audiocast metadata extraction handling */
	gboolean iradio_mode;
101
	gboolean http_callbacks_pushed;
102 103 104 105 106 107 108 109 110 111 112 113

	gint icy_metaint;
	GnomeVFSFileSize icy_count;

	gchar *iradio_name;
	gchar *iradio_genre;
	gchar *iradio_url;
	gchar *iradio_title;

	GThread *audiocast_thread;
	GList *audiocast_notify_queue;
	GMutex *audiocast_queue_mutex;
114
	GMutex *audiocast_udpdata_mutex;
115 116 117 118
	gint audiocast_thread_die_infd;
	gint audiocast_thread_die_outfd;
	gint audiocast_port;
	gint audiocast_fd;
119 120 121 122 123 124
};

struct _GstGnomeVFSSrcClass {
	GstElementClass parent_class;
};

125
/* elementfactory information */
126 127 128
GstElementDetails gst_gnomevfssrc_details = {
	"GnomeVFS Source",
	"Source/File",
129
	"LGPL",
130 131 132 133 134 135
	"Read from any GnomeVFS file",
	VERSION,
	"Bastien Nocera <hadess@hadess.net>",
	"(C) 2001",
};

136
GST_PAD_FORMATS_FUNCTION (gst_gnomevfssrc_get_formats,
137 138 139 140
	GST_FORMAT_BYTES
)

GST_PAD_QUERY_TYPE_FUNCTION (gst_gnomevfssrc_get_query_types,
141 142
	GST_QUERY_TOTAL,
	GST_QUERY_POSITION
143 144
)

145
GST_PAD_EVENT_MASK_FUNCTION (gst_gnomevfssrc_get_event_mask,
146 147 148 149 150 151 152 153
  { GST_EVENT_SEEK, GST_SEEK_METHOD_CUR |
                    GST_SEEK_METHOD_SET |
                    GST_SEEK_METHOD_END |
                    GST_SEEK_FLAG_FLUSH },
  { GST_EVENT_FLUSH, 0 },
  { GST_EVENT_SIZE, 0 }
)

154 155 156 157 158 159 160 161 162
/* GnomeVFSSrc signals and args */
enum {
	LAST_SIGNAL
};

enum {
	ARG_0,
	ARG_LOCATION,
	ARG_BYTESPERREAD,
163 164 165 166 167
	ARG_IRADIO_MODE,
	ARG_IRADIO_NAME,
	ARG_IRADIO_GENRE,
	ARG_IRADIO_URL,
	ARG_IRADIO_TITLE,
168 169 170 171
};

GType gst_gnomevfssrc_get_type(void);

Wim Taymans's avatar
Wim Taymans committed
172 173
static void 		gst_gnomevfssrc_class_init	(GstGnomeVFSSrcClass *klass);
static void 		gst_gnomevfssrc_init		(GstGnomeVFSSrc *gnomevfssrc);
174
static void 		gst_gnomevfssrc_dispose		(GObject *object);
175

Wim Taymans's avatar
Wim Taymans committed
176 177 178 179
static void 		gst_gnomevfssrc_set_property	(GObject *object, guint prop_id,
							 const GValue *value, GParamSpec *pspec);
static void 		gst_gnomevfssrc_get_property	(GObject *object, guint prop_id,
							 GValue *value, GParamSpec *pspec);
180

Wim Taymans's avatar
Wim Taymans committed
181
static GstBuffer*	gst_gnomevfssrc_get		(GstPad *pad);
182

Wim Taymans's avatar
Wim Taymans committed
183 184
static GstElementStateReturn 
			gst_gnomevfssrc_change_state	(GstElement *element);
185

Wim Taymans's avatar
Wim Taymans committed
186 187 188
static void 		gst_gnomevfssrc_close_file	(GstGnomeVFSSrc *src);
static gboolean 	gst_gnomevfssrc_open_file	(GstGnomeVFSSrc *src);
static gboolean 	gst_gnomevfssrc_srcpad_event 	(GstPad *pad, GstEvent *event);
189
static gboolean 	gst_gnomevfssrc_srcpad_query 	(GstPad *pad, GstQueryType type,
Wim Taymans's avatar
Wim Taymans committed
190
		              				 GstFormat *format, gint64 *value);
191 192 193 194 195 196 197

static int audiocast_init(GstGnomeVFSSrc *src);
static int audiocast_register_listener(gint *port, gint *fd);
static void audiocast_do_notifications(GstGnomeVFSSrc *src);
static gpointer audiocast_thread_run(GstGnomeVFSSrc *src);
static void audiocast_thread_kill(GstGnomeVFSSrc *src);

198 199 200 201 202 203 204 205
static GstElementClass *parent_class = NULL;

GType gst_gnomevfssrc_get_type(void)
{
	static GType gnomevfssrc_type = 0;

	if (!gnomevfssrc_type) {
		static const GTypeInfo gnomevfssrc_info = {
206 207 208 209 210 211 212 213
			sizeof(GstGnomeVFSSrcClass),      NULL,
			NULL,
			(GClassInitFunc) gst_gnomevfssrc_class_init,
			NULL,
			NULL,
			sizeof(GstGnomeVFSSrc),
			0,
			(GInstanceInitFunc) gst_gnomevfssrc_init,
214 215
		};
		gnomevfssrc_type =
216 217 218 219
			g_type_register_static(GST_TYPE_ELEMENT,
					"GstGnomeVFSSrc",
					&gnomevfssrc_info,
					0);
220 221 222 223 224 225 226 227 228 229 230 231 232 233
	}
	return gnomevfssrc_type;
}

static void gst_gnomevfssrc_class_init(GstGnomeVFSSrcClass *klass)
{
	GObjectClass *gobject_class;
	GstElementClass *gstelement_class;

	gobject_class = (GObjectClass *) klass;
	gstelement_class = (GstElementClass *) klass;

	parent_class = g_type_class_ref(GST_TYPE_ELEMENT);

234
      	gst_element_class_install_std_props (
235 236 237 238 239
               GST_ELEMENT_CLASS (klass),
               "bytesperread", ARG_BYTESPERREAD, G_PARAM_READWRITE,
               "location",     ARG_LOCATION,     G_PARAM_READWRITE,
               NULL);

240 241
	gobject_class->dispose         = gst_gnomevfssrc_dispose;

242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
	/* icecast stuff */
	g_object_class_install_property (gobject_class,
					 ARG_IRADIO_MODE,
					 g_param_spec_boolean ("iradio-mode",
							       "iradio-mode",
							       "Enable internet radio mode (extraction of icecast/audiocast metadata)",
							       FALSE,
							       G_PARAM_READWRITE));
	g_object_class_install_property (gobject_class,
					 ARG_IRADIO_NAME,
					 g_param_spec_string ("iradio-name",
							      "iradio-name",
							      "Name of the stream",
							      NULL,
							      G_PARAM_READABLE));
	g_object_class_install_property (gobject_class,
					 ARG_IRADIO_GENRE,
					 g_param_spec_string ("iradio-genre",
							      "iradio-genre",
							      "Genre of the stream",

							      NULL,
							      G_PARAM_READABLE));
	g_object_class_install_property (gobject_class,
					 ARG_IRADIO_URL,
					 g_param_spec_string ("iradio-url",
							      "iradio-url",
							      "Homepage URL for radio stream",
							      NULL,
							      G_PARAM_READABLE));
	g_object_class_install_property (gobject_class,
					 ARG_IRADIO_TITLE,
					 g_param_spec_string ("iradio-title",
							      "iradio-title",
							      "Name of currently playing song",
							      NULL,
							      G_PARAM_READABLE));

280 281 282 283 284 285 286 287 288 289
	gstelement_class->set_property = gst_gnomevfssrc_set_property;
	gstelement_class->get_property = gst_gnomevfssrc_get_property;

	gstelement_class->change_state = gst_gnomevfssrc_change_state;
}

static void gst_gnomevfssrc_init(GstGnomeVFSSrc *gnomevfssrc)
{
	gnomevfssrc->srcpad = gst_pad_new("src", GST_PAD_SRC);
	gst_pad_set_get_function(gnomevfssrc->srcpad, gst_gnomevfssrc_get);
290 291
	gst_pad_set_event_mask_function (gnomevfssrc->srcpad, 
			gst_gnomevfssrc_get_event_mask);
292 293
	gst_pad_set_event_function (gnomevfssrc->srcpad,
			gst_gnomevfssrc_srcpad_event);
294 295
	gst_pad_set_query_type_function (gnomevfssrc->srcpad, 
			gst_gnomevfssrc_get_query_types);
Wim Taymans's avatar
Wim Taymans committed
296 297
	gst_pad_set_query_function (gnomevfssrc->srcpad,
			gst_gnomevfssrc_srcpad_query);
298 299
	gst_pad_set_formats_function (gnomevfssrc->srcpad, 
			gst_gnomevfssrc_get_formats);
300 301 302 303 304 305 306
	gst_element_add_pad(GST_ELEMENT(gnomevfssrc), gnomevfssrc->srcpad);

	gnomevfssrc->uri = NULL;
	gnomevfssrc->handle = NULL;
	gnomevfssrc->curoffset = 0;
	gnomevfssrc->bytes_per_read = 4096;
	gnomevfssrc->new_seek = FALSE;
307
	gnomevfssrc->in_first_get = TRUE;
308

309 310 311
	gnomevfssrc->icy_metaint = 0;

	gnomevfssrc->iradio_mode = FALSE;
312
	gnomevfssrc->http_callbacks_pushed = FALSE;
313 314 315 316 317 318
	gnomevfssrc->icy_count = 0;
	gnomevfssrc->iradio_name = NULL;
	gnomevfssrc->iradio_genre = NULL;
	gnomevfssrc->iradio_url = NULL;
	gnomevfssrc->iradio_title = NULL;

319
	gnomevfssrc->audiocast_udpdata_mutex = g_mutex_new(); 
320 321 322 323
	gnomevfssrc->audiocast_queue_mutex = g_mutex_new(); 
	gnomevfssrc->audiocast_notify_queue = NULL;
	gnomevfssrc->audiocast_thread = NULL;
		
324 325 326 327 328
	g_static_mutex_lock (&count_lock);
	if (ref_count == 0) {
		/* gnome vfs engine init */
		if (gnome_vfs_initialized() == FALSE) {
			gnome_vfs_init();
329
			vfs_owner = TRUE;
330 331 332 333 334 335 336 337 338 339 340
		}
	}
	ref_count++;
	g_static_mutex_unlock (&count_lock);
}

static void
gst_gnomevfssrc_dispose (GObject *object)
{
	g_static_mutex_lock (&count_lock);
	ref_count--;
341
	if (ref_count == 0 && vfs_owner) {
342 343 344 345 346 347 348
		if (gnome_vfs_initialized() == TRUE) {
			gnome_vfs_shutdown();
		}
	}
	g_static_mutex_unlock (&count_lock);

  	G_OBJECT_CLASS (parent_class)->dispose (object);
349 350 351 352 353 354
}


static void gst_gnomevfssrc_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
	GstGnomeVFSSrc *src;
355 356
        const gchar *location;
        gchar cwd[PATH_MAX];
357 358 359 360 361 362 363 364 365 366 367 368

	/* it's not null if we got it, but it might not be ours */
	g_return_if_fail(GST_IS_GNOMEVFSSRC(object));

	src = GST_GNOMEVFSSRC(object);

	switch (prop_id) {
	case ARG_LOCATION:
		/* the element must be stopped or paused in order to do this */
		g_return_if_fail((GST_STATE(src) < GST_STATE_PLAYING)
				 || (GST_STATE(src) == GST_STATE_PAUSED));

369
		g_free(src->filename);
370

Colin Walters's avatar
Colin Walters committed
371
		/* clear the filename if we get a NULL */
372
		if (g_value_get_string (value) == NULL) {
Wim Taymans's avatar
Wim Taymans committed
373
			gst_element_set_state(GST_ELEMENT(object), GST_STATE_NULL);
374 375 376
			src->filename = NULL;
		} else {
			/* otherwise set the new filename */
377
                        location = g_value_get_string (value);
378
                        /* if it's not a proper uri, default to file: -- this
379 380
                         * is a crude test */
                        if (!strchr (location, ':'))
381 382 383 384
			{
				gchar *newloc = gnome_vfs_escape_path_string(location);
                                if (*newloc == '/')
                                        src->filename = g_strdup_printf ("file://%s", newloc);
385
                                else
386 387 388
                                        src->filename = g_strdup_printf ("file://%s/%s", getcwd(cwd, PATH_MAX), newloc);
				g_free(newloc);
			}
389 390
                        else
                                src->filename = g_strdup (g_value_get_string (value));
391 392 393 394 395 396 397 398 399 400 401
		}

		if ((GST_STATE(src) == GST_STATE_PAUSED)
		    && (src->filename != NULL)) {
			gst_gnomevfssrc_close_file(src);
			gst_gnomevfssrc_open_file(src);
		}
		break;
	case ARG_BYTESPERREAD:
		src->bytes_per_read = g_value_get_int (value);
		break;
402 403 404
	case ARG_IRADIO_MODE:
		src->iradio_mode = g_value_get_boolean (value);
		break;
405
	default:
406
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
		break;
	}
}

static void gst_gnomevfssrc_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
	GstGnomeVFSSrc *src;

	/* it's not null if we got it, but it might not be ours */
	g_return_if_fail(GST_IS_GNOMEVFSSRC(object));

	src = GST_GNOMEVFSSRC(object);

	switch (prop_id) {
	case ARG_LOCATION:
		g_value_set_string (value, src->filename);
		break;
	case ARG_BYTESPERREAD:
		g_value_set_int (value, src->bytes_per_read);
		break;
427 428 429 430 431 432 433 434 435 436 437 438 439
	case ARG_IRADIO_MODE:
		g_value_set_boolean (value, src->iradio_mode);
		break;
	case ARG_IRADIO_NAME:
		g_value_set_string (value, src->iradio_name);
		break;
	case ARG_IRADIO_GENRE:
		g_value_set_string (value, src->iradio_genre);
		break;
	case ARG_IRADIO_URL:
		g_value_set_string (value, src->iradio_url);
		break;
	case ARG_IRADIO_TITLE:
440
		g_mutex_lock(src->audiocast_udpdata_mutex);
441
		g_value_set_string (value, src->iradio_title);
442
		g_mutex_unlock(src->audiocast_udpdata_mutex);
443
		break;
444 445 446 447 448 449
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}

Colin Walters's avatar
Colin Walters committed
450 451 452 453 454
static char *
unicodify (const char *str, int len, ...)
{
	char *ret = NULL, *cset;
	va_list args;
455
	gsize bytes_read, bytes_written;
Colin Walters's avatar
Colin Walters committed
456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482

	if (g_utf8_validate (str, len, NULL))
		return g_strndup (str, len >= 0 ? len : strlen (str));

	va_start (args, len);
	while ((cset = va_arg (args, char *)) != NULL)
	{
		if (!strcmp (cset, "locale"))
			ret = g_locale_to_utf8 (str, len, &bytes_read,
						&bytes_written, NULL);
		else
			ret = g_convert (str, len, "UTF-8", cset,
					 &bytes_read, &bytes_written, NULL);
		if (ret)
			break;
	}
	va_end (args);

	return ret;
}

static char *
gst_gnomevfssrc_unicodify (const char *str)
{
	return unicodify (str, -1, "locale", "ISO-8859-1", NULL);
}

483 484 485 486 487 488 489 490 491 492
/*
 * icecast/audiocast metadata extraction support code
 */

static int audiocast_init(GstGnomeVFSSrc *src)
{
	int pipefds[2];
	GError *error = NULL;
	if (!src->iradio_mode)
		return TRUE;
493
	GST_DEBUG ("audiocast: registering listener");
494 495 496 497 498 499 500 501 502
	if (audiocast_register_listener(&src->audiocast_port, &src->audiocast_fd) < 0)
	{
		gst_element_error(GST_ELEMENT(src),
				  "opening vfs file \"%s\" (%s)",
				  src->filename, 
				  "unable to register UDP port");
		close(src->audiocast_fd);
		return FALSE;
	}
503
	GST_DEBUG ("audiocast: creating pipe");
504 505 506 507 508 509 510 511
	src->audiocast_notify_queue = NULL;
	if (pipe(pipefds) < 0)
	{
		close(src->audiocast_fd);
		return FALSE;
	}
	src->audiocast_thread_die_infd = pipefds[0];
	src->audiocast_thread_die_outfd = pipefds[1];
512
	GST_DEBUG ("audiocast: creating audiocast thread");
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530
	src->audiocast_thread = g_thread_create((GThreadFunc) audiocast_thread_run, src, TRUE, &error);
	if (error != NULL) {
		gst_element_error(GST_ELEMENT(src),
				  "opening vfs file \"%s\" (unable to create thread: %s)",
				  src->filename, 
				  error->message);
		close(src->audiocast_fd);
		return FALSE;
	}
	return TRUE;
}

static int audiocast_register_listener(gint *port, gint *fd)
{
	struct sockaddr_in sin;
	int sock;
	socklen_t sinlen = sizeof (struct sockaddr_in);

531
	GST_DEBUG ("audiocast: estabilishing UDP listener");
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546

	if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
		goto lose;
	
	memset(&sin, 0, sinlen);
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = g_htonl(INADDR_ANY);
			
	if (bind(sock, (struct sockaddr *)&sin, sinlen) < 0)
		goto lose_and_close;

	memset(&sin, 0, sinlen);
	if (getsockname(sock, (struct sockaddr *)&sin, &sinlen) < 0)
		goto lose_and_close;

547
	GST_DEBUG ("audiocast: listening on local %s:%d", inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port));
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588

	*port = g_ntohs(sin.sin_port);
	*fd = sock;

	return 0;
lose_and_close:
	close(sock);
lose:
	return -1;
}

static void audiocast_do_notifications(GstGnomeVFSSrc *src)
{
	/* Send any pending notifications we got from the UDP thread. */
	if (src->iradio_mode)
	{
		GList *entry;
		g_mutex_lock(src->audiocast_queue_mutex);
		for (entry = src->audiocast_notify_queue; entry; entry = entry->next)
			g_object_notify(G_OBJECT (src), (const gchar *) entry->data);
		g_list_free(src->audiocast_notify_queue);
		src->audiocast_notify_queue = NULL;
		g_mutex_unlock(src->audiocast_queue_mutex);
	}		
}

static gpointer audiocast_thread_run(GstGnomeVFSSrc *src)
{
	char buf[1025], **lines;
	gsize len;
	fd_set fdset, readset;
	struct sockaddr_in from;
	socklen_t fromlen = sizeof(struct sockaddr_in);

	FD_ZERO(&fdset);

	FD_SET(src->audiocast_fd, &fdset);
	FD_SET(src->audiocast_thread_die_infd, &fdset);

	while (1)
	{
589
		GST_DEBUG ("audiocast thread: dropping into select");
590 591 592 593 594 595 596
		readset = fdset;
		if (select(FD_SETSIZE, &readset, NULL, NULL, NULL) < 0) {
			perror("select");
			return NULL;
		}
		if (FD_ISSET(src->audiocast_thread_die_infd, &readset)) {
			char buf[1];
597
			GST_DEBUG ("audiocast thread: got die character");
598 599 600 601 602
			read(src->audiocast_thread_die_infd, buf, 1);
			close(src->audiocast_thread_die_infd);
			close(src->audiocast_fd);
			return NULL;
		}
603
		GST_DEBUG ("audiocast thread: reading data");
604 605 606 607 608 609
		len = recvfrom(src->audiocast_fd, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &from, &fromlen);
		if (len < 0 && errno == EAGAIN)
			continue;
		else if (len >= 0)
		{
			int i;
Colin Walters's avatar
Colin Walters committed
610
			char *valptr, *value;
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630
			buf[len] = '\0';
			lines = g_strsplit(buf, "\n", 0);
			if (!lines)
				continue;
	
			for (i = 0; lines[i]; i++) {
				while ((lines[i][strlen(lines[i]) - 1] == '\n') ||
				       (lines[i][strlen(lines[i]) - 1] == '\r'))
					lines[i][strlen(lines[i]) - 1] = '\0';
			
				valptr = strchr(lines[i], ':');
			
				if (!valptr)
					continue;
				else
					valptr++;
		
				g_strstrip(valptr);
				if (!strlen(valptr))
					continue;
Colin Walters's avatar
Colin Walters committed
631 632 633 634 635 636 637

				value = gst_gnomevfssrc_unicodify (valptr);
				if (!value)
				{
					g_print ("Unable to convert \"%s\" to UTF-8!\n", valptr);
					continue;
				}
638 639
		
				if (!strncmp(lines[i], "x-audiocast-streamtitle", 23)) {
640
					g_mutex_lock(src->audiocast_udpdata_mutex);
Colin Walters's avatar
Colin Walters committed
641 642
					g_free(src->iradio_title);
					src->iradio_title = value;
643
					g_mutex_unlock(src->audiocast_udpdata_mutex);
Colin Walters's avatar
Colin Walters committed
644

645 646
					g_mutex_lock(src->audiocast_queue_mutex);
					src->audiocast_notify_queue = g_list_append(src->audiocast_notify_queue, "iradio-title");
647
					GST_DEBUG ("audiocast title: %s\n", src->iradio_title);
648
					g_mutex_unlock(src->audiocast_queue_mutex);
649 650
				} else if (!strncmp(lines[i], "x-audiocast-streamurl", 21)) {
					g_mutex_lock(src->audiocast_udpdata_mutex);
Colin Walters's avatar
Colin Walters committed
651 652
					g_free(src->iradio_url);
					src->iradio_url = value;
653
					g_mutex_unlock(src->audiocast_udpdata_mutex);
Colin Walters's avatar
Colin Walters committed
654

655 656
					g_mutex_lock(src->audiocast_queue_mutex);
					src->audiocast_notify_queue = g_list_append(src->audiocast_notify_queue, "iradio-url");
657
					GST_DEBUG ("audiocast url: %s\n", src->iradio_title);
658
					g_mutex_unlock(src->audiocast_queue_mutex);
659 660
				} else if (!strncmp(lines[i], "x-audiocast-udpseqnr", 20)) {
					gchar outbuf[120];
Colin Walters's avatar
Colin Walters committed
661 662 663 664

					sprintf(outbuf, "x-audiocast-ack: %ld \r\n", atol(value));
					g_free (value);

665
					if (sendto(src->audiocast_fd, outbuf, strlen(outbuf), 0, (struct sockaddr *) &from, fromlen) <= 0) {
Colin Walters's avatar
Colin Walters committed
666
						g_print("Error sending response to server: %s\n", strerror(errno));
667 668
						continue;
					}
669
					GST_DEBUG ("sent audiocast ack: %s\n", outbuf);
670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688
				}
			}
			g_strfreev(lines);
		}
	}
	return NULL;
}

static void audiocast_thread_kill(GstGnomeVFSSrc *src)
{
	if (!src->audiocast_thread)
		return;
	
	/*
	  We rely on this hack to kill the
	  audiocast thread.  If we get icecast
	  metadata, then we don't need the
	  audiocast metadata too.
	*/
689
	GST_DEBUG ("audiocast: writing die character");
690 691
	write(src->audiocast_thread_die_outfd, "q", 1);
	close(src->audiocast_thread_die_outfd);
692
	GST_DEBUG ("audiocast: joining thread");
693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709
	g_thread_join(src->audiocast_thread);
	src->audiocast_thread = NULL;
}

static void
gst_gnomevfssrc_send_additional_headers_callback (gconstpointer in,
						  gsize         in_size,
						  gpointer      out,
						  gsize         out_size,
						  gpointer      callback_data)
{
	GstGnomeVFSSrc *src = GST_GNOMEVFSSRC (callback_data);
	GnomeVFSModuleCallbackAdditionalHeadersOut *out_args =
                (GnomeVFSModuleCallbackAdditionalHeadersOut *)out;

	if (!src->iradio_mode)
		return;
710
	GST_DEBUG ("sending headers\n");
711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730

        out_args->headers = g_list_append (out_args->headers,
                                           g_strdup ("icy-metadata:1\r\n"));
        out_args->headers = g_list_append (out_args->headers,
                                           g_strdup_printf ("x-audiocast-udpport: %d\r\n", src->audiocast_port));
}

static void
gst_gnomevfssrc_received_headers_callback (gconstpointer in,
					   gsize         in_size,
					   gpointer      out,
					   gsize         out_size,
					   gpointer      callback_data)
{
	GList *i;
	gint icy_metaint;
	GstGnomeVFSSrc *src = GST_GNOMEVFSSRC (callback_data);
        GnomeVFSModuleCallbackReceivedHeadersIn *in_args =
                (GnomeVFSModuleCallbackReceivedHeadersIn *)in;

731
	/* This is only used for internet radio stuff right now */
732 733 734 735 736
	if (!src->iradio_mode)
		return;

        for (i = in_args->headers; i; i = i->next) {
		char *data = (char *) i->data;
737
		char *key = data;
738 739 740 741 742 743 744 745 746 747 748 749 750 751
		char *value = strchr(data, ':');
		if (!value)
			continue;

		value++;
		g_strstrip(value);
		if (!strlen(value))
			continue;

		/* Icecast stuff */
                if (!strncmp (data, "icy-metaint:", 12)) /* ugh */
		{ 
                        sscanf (data + 12, "%d", &icy_metaint);
			src->icy_metaint = icy_metaint;
752
			GST_DEBUG ("got icy-metaint %d, killing audiocast thread",
753
				   src->icy_metaint);
754 755 756 757 758 759 760 761 762 763 764
			audiocast_thread_kill(src);
			continue;
		}

		if (!strncmp(data, "icy-", 4))
			key = data + 4;
		else if (!strncmp(data, "x-audiocast-", 12))
			key = data + 12;
		else
			continue;

765
		GST_DEBUG ("key: %s", key);
766
                if (!strncmp (key, "name", 4)) {
Colin Walters's avatar
Colin Walters committed
767 768
			g_free (src->iradio_name);
			src->iradio_name = gst_gnomevfssrc_unicodify (value);
769
			if (src->iradio_name)
Colin Walters's avatar
Colin Walters committed
770
				g_object_notify (G_OBJECT (src), "iradio-name");
771
                } else if (!strncmp (key, "genre", 5)) {
Colin Walters's avatar
Colin Walters committed
772 773
			g_free (src->iradio_genre);
			src->iradio_genre = gst_gnomevfssrc_unicodify (value);
774
			if (src->iradio_genre)
Colin Walters's avatar
Colin Walters committed
775
				g_object_notify (G_OBJECT (src), "iradio-genre");
776
                } else if (!strncmp (key, "url", 3)) {
Colin Walters's avatar
Colin Walters committed
777 778
			g_free (src->iradio_url);
			src->iradio_url = gst_gnomevfssrc_unicodify (value);
779
			if (src->iradio_url)
Colin Walters's avatar
Colin Walters committed
780
				g_object_notify (G_OBJECT (src), "iradio-url");
781 782 783 784 785 786 787
		}
        }
}

static void
gst_gnomevfssrc_push_callbacks (GstGnomeVFSSrc *gnomevfssrc)
{
788
	if (gnomevfssrc->http_callbacks_pushed)
789 790
		return;

791
	GST_DEBUG ("pushing callbacks");
792 793 794 795 796 797 798 799 800
	gnome_vfs_module_callback_push (GNOME_VFS_MODULE_CALLBACK_HTTP_SEND_ADDITIONAL_HEADERS,
					gst_gnomevfssrc_send_additional_headers_callback,
					gnomevfssrc,
					NULL);
	gnome_vfs_module_callback_push (GNOME_VFS_MODULE_CALLBACK_HTTP_RECEIVED_HEADERS,
					gst_gnomevfssrc_received_headers_callback,
					gnomevfssrc,
					NULL);

801
	gnomevfssrc->http_callbacks_pushed = TRUE;
802 803 804 805 806
}

static void
gst_gnomevfssrc_pop_callbacks (GstGnomeVFSSrc *gnomevfssrc)
{
807
	if (!gnomevfssrc->http_callbacks_pushed)
808 809
		return;

810
	GST_DEBUG ("popping callbacks");
811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826
	gnome_vfs_module_callback_pop (GNOME_VFS_MODULE_CALLBACK_HTTP_SEND_ADDITIONAL_HEADERS);
	gnome_vfs_module_callback_pop (GNOME_VFS_MODULE_CALLBACK_HTTP_RECEIVED_HEADERS);
}

static void
gst_gnomevfssrc_get_icy_metadata (GstGnomeVFSSrc *src)
{
	GnomeVFSFileSize length = 0;
        GnomeVFSResult res;
        gint metadata_length;
        guchar foobyte;
        guchar *data;
        guchar *pos;
	gchar **tags;
	int i;

827
	GST_DEBUG ("reading icecast metadata");
828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860

        while (length == 0) {
                res = gnome_vfs_read (src->handle, &foobyte, 1, &length);
                if (res != GNOME_VFS_OK)
                        return;
        }

        metadata_length = foobyte * 16;

        if (metadata_length == 0)
                return;

	data = g_new (gchar, metadata_length + 1);
        pos = data;

        while (pos - data < metadata_length) {
                res = gnome_vfs_read (src->handle, pos,
                                      metadata_length - (pos - data),
                                      &length);
		/* FIXME: better error handling here? */
                if (res != GNOME_VFS_OK) {
                        g_free (data);
                        return;
                }

                pos += length;
        }

        data[metadata_length] = 0;
	tags = g_strsplit(data, "';", 0);
	
	for (i = 0; tags[i]; i++)
	{
861
		if (!g_ascii_strncasecmp(tags[i], "StreamTitle=", 12))
862
		{
Colin Walters's avatar
Colin Walters committed
863 864
			g_free (src->iradio_title);
			src->iradio_title = gst_gnomevfssrc_unicodify (tags[i]+13);
865
			if (src->iradio_title)
Colin Walters's avatar
Colin Walters committed
866
			{
867
				GST_DEBUG ("sending notification on icecast title");
Colin Walters's avatar
Colin Walters committed
868 869 870 871 872 873
				g_object_notify (G_OBJECT (src), "iradio-title");
			}
			else
				g_print ("Unable to convert icecast title \"%s\" to UTF-8!\n",
					 tags[i]+13);
			
874
		}
875
		if (!g_ascii_strncasecmp(tags[i], "StreamUrl=", 10))
876
		{
Colin Walters's avatar
Colin Walters committed
877 878
			g_free (src->iradio_url);
			src->iradio_url = gst_gnomevfssrc_unicodify (tags[i]+11);
879
			if (src->iradio_url)
Colin Walters's avatar
Colin Walters committed
880
			{
881
				GST_DEBUG ("sending notification on icecast url");
Colin Walters's avatar
Colin Walters committed
882 883 884 885 886
				g_object_notify (G_OBJECT (src), "iradio-url");
			}
			else
				g_print ("Unable to convert icecast url \"%s\" to UTF-8!\n",
					 tags[i]+11);
887
		}
888 889 890 891 892 893 894
	}

	g_strfreev(tags);
}

/* end of icecast/audiocast metadata extraction support code */

895 896 897 898 899 900 901 902 903 904 905 906
/**
 * gst_gnomevfssrc_get:
 * @pad: #GstPad to push a buffer from
 *
 * Push a new buffer from the gnomevfssrc at the current offset.
 */
static GstBuffer *gst_gnomevfssrc_get(GstPad *pad)
{
	GstGnomeVFSSrc *src;
	GnomeVFSResult result = 0;
	GstBuffer *buf;
	GnomeVFSFileSize readbytes;
Benjamin Otte's avatar
Benjamin Otte committed
907
	guint8 *data;
908 909 910 911 912 913 914 915 916

	g_return_val_if_fail(pad != NULL, NULL);
	src = GST_GNOMEVFSSRC(gst_pad_get_parent(pad));
	g_return_val_if_fail(GST_FLAG_IS_SET(src, GST_GNOMEVFSSRC_OPEN),
			     NULL);

	/* deal with EOF state */
	if ((src->curoffset >= src->size) && (src->size != 0))
	{
Wim Taymans's avatar
Wim Taymans committed
917
		gst_element_set_eos (GST_ELEMENT (src));
Wim Taymans's avatar
Wim Taymans committed
918
		return GST_BUFFER (gst_event_new (GST_EVENT_EOS));
919 920 921 922 923 924 925
	}

	/* create the buffer */
	/* FIXME: should eventually use a bufferpool for this */
	buf = gst_buffer_new();
	g_return_val_if_fail(buf, NULL);

926 927
	audiocast_do_notifications(src);

928
	if (src->iradio_mode && src->icy_metaint > 0) {
929
		GST_BUFFER_DATA (buf) = g_malloc0 (src->icy_metaint);
Benjamin Otte's avatar
Benjamin Otte committed
930
		data = GST_BUFFER_DATA (buf);
931 932
		g_return_val_if_fail (GST_BUFFER_DATA (buf) != NULL, NULL);

933 934 935 936 937
		GST_BUFFER_SIZE (buf) = 0;
		/* FIXME GROSS HACK: We try to read in at least 8000
		 * bytes of data so that mp3 typefinding will work. */
		do
		{
938
			GST_DEBUG ("doing read: icy_count: %" G_GINT64_FORMAT, src->icy_count);
Benjamin Otte's avatar
Benjamin Otte committed
939
			result = gnome_vfs_read (src->handle, data,
940 941 942 943 944 945 946 947 948 949 950 951 952 953
						 src->icy_metaint - src->icy_count,
						 &readbytes);

			/* EOS? */
			if (readbytes == 0) {
				gst_buffer_unref (buf);
				gst_element_set_eos (GST_ELEMENT (src));
				src->in_first_get = FALSE;
				return GST_BUFFER (gst_event_new (GST_EVENT_EOS));
			}
			
			src->icy_count += readbytes;
			GST_BUFFER_OFFSET (buf) = src->curoffset;
			GST_BUFFER_SIZE (buf) += readbytes;
Benjamin Otte's avatar
Benjamin Otte committed
954
		      	data += readbytes;
955 956 957 958 959 960 961 962 963
			src->curoffset += readbytes;
			
			if (src->icy_count == src->icy_metaint) {
				gst_gnomevfssrc_get_icy_metadata (src);
				src->icy_count = 0;
			}
		} while (src->in_first_get
			 && GST_BUFFER_OFFSET (buf) < 8000 &&
			 src->icy_metaint - src->icy_count >= 8000);
964
		src->in_first_get = FALSE;
965 966 967 968 969 970 971
	} else {
		/* allocate the space for the buffer data */
		GST_BUFFER_DATA(buf) = g_malloc(src->bytes_per_read);
		g_return_val_if_fail(GST_BUFFER_DATA(buf) != NULL, NULL);

		if (src->new_seek)
		{
Wim Taymans's avatar
Wim Taymans committed
972 973
			GstEvent *event;

974
			gst_buffer_unref (buf);
975
			GST_DEBUG ("new seek %" G_GINT64_FORMAT, src->curoffset);
976
			src->new_seek = FALSE;
Wim Taymans's avatar
Wim Taymans committed
977

978
			GST_DEBUG ("gnomevfssrc sending discont");
Wim Taymans's avatar
Wim Taymans committed
979 980
			event = gst_event_new_discontinuous (FALSE, GST_FORMAT_BYTES, src->curoffset, NULL);
			src->need_flush = FALSE;
981

Wim Taymans's avatar
Wim Taymans committed
982
			return GST_BUFFER (event);
983 984 985 986 987
		}

		result = gnome_vfs_read(src->handle, GST_BUFFER_DATA(buf),
				   src->bytes_per_read, &readbytes);

988
		GST_DEBUG ("read: %s, readbytes: %" G_GINT64_FORMAT,
989 990 991 992 993
				gnome_vfs_result_to_string(result), readbytes);
		/* deal with EOS */
		if (readbytes == 0)
		{
			gst_buffer_unref(buf);
Wim Taymans's avatar
Wim Taymans committed
994

Wim Taymans's avatar
Wim Taymans committed
995
			gst_element_set_eos (GST_ELEMENT (src));
Wim Taymans's avatar
Wim Taymans committed
996 997

			return GST_BUFFER (gst_event_new (GST_EVENT_EOS));
998 999 1000 1001 1002 1003 1004
		}
		
		GST_BUFFER_OFFSET(buf) = src->curoffset;
		GST_BUFFER_SIZE(buf) = readbytes;
		src->curoffset += readbytes;
	}

Wim Taymans's avatar
Wim Taymans committed
1005
	GST_BUFFER_TIMESTAMP (buf) = -1;
Colin Walters's avatar
Colin Walters committed
1006

1007 1008 1009 1010
	/* we're done, return the buffer */
	return buf;
}

1011
/* open the file, do stuff necessary to go to READY state */
1012 1013 1014
static gboolean gst_gnomevfssrc_open_file(GstGnomeVFSSrc *src)
{
	GnomeVFSResult result;
1015
	GnomeVFSFileInfo *info;
1016 1017 1018 1019 1020 1021 1022

	g_return_val_if_fail(!GST_FLAG_IS_SET(src, GST_GNOMEVFSSRC_OPEN),
			     FALSE);

	/* create the uri */
	src->uri = gnome_vfs_uri_new(src->filename);
	if (!src->uri) {
1023 1024
		gst_element_error(GST_ELEMENT(src), "creating uri \"%s\" (%s)",
				  src->filename, strerror (errno));
1025 1026 1027
		return FALSE;
	}

1028 1029
	if (!audiocast_init(src))
		return FALSE;
1030
			
1031 1032 1033 1034 1035 1036 1037 1038 1039
	gst_gnomevfssrc_push_callbacks (src);
	
	result = gnome_vfs_open_uri(&(src->handle), src->uri,
				    GNOME_VFS_OPEN_READ);
	if (result != GNOME_VFS_OK)
	{
		gst_gnomevfssrc_pop_callbacks (src);
		audiocast_thread_kill(src);
		gst_element_error(GST_ELEMENT(src),
Wim Taymans's avatar
Wim Taymans committed
1040
				  "opening vfs file \"%s\" (%s)",
1041 1042 1043 1044 1045 1046 1047
				  src->filename, 
				  gnome_vfs_result_to_string(result));
		return FALSE;
	}
	
	info = gnome_vfs_file_info_new ();
	if (gnome_vfs_get_file_info_from_handle (src->handle, info,
Colin Walters's avatar
Colin Walters committed
1048
						 GNOME_VFS_FILE_INFO_DEFAULT)
1049 1050 1051 1052 1053 1054
	    == GNOME_VFS_OK)
	{
		if (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)
			src->size = info->size;
	}
	else
1055
		GST_DEBUG ("getting info failed: %s", gnome_vfs_result_to_string (result));
1056

1057
	gnome_vfs_file_info_unref(info);
1058
	
1059
	GST_DEBUG ("size %" G_GINT64_FORMAT, src->size);
1060 1061 1062
	
	audiocast_do_notifications(src);
	
1063
	GST_DEBUG ("open result: %s", gnome_vfs_result_to_string (result));
1064

1065 1066
	src->in_first_get = TRUE;

1067 1068 1069 1070 1071 1072 1073 1074 1075
	GST_FLAG_SET(src, GST_GNOMEVFSSRC_OPEN);

	return TRUE;
}

static void gst_gnomevfssrc_close_file(GstGnomeVFSSrc *src)
{
	g_return_if_fail(GST_FLAG_IS_SET(src, GST_GNOMEVFSSRC_OPEN));

1076 1077 1078
	gst_gnomevfssrc_pop_callbacks (src);
	audiocast_thread_kill(src);

1079
	gnome_vfs_close(src->handle);
1080

1081
	gnome_vfs_uri_unref(src->uri);
1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093
	src->size = 0;
	src->curoffset = 0;
	src->new_seek = FALSE;

	GST_FLAG_UNSET(src, GST_GNOMEVFSSRC_OPEN);
}

static GstElementStateReturn gst_gnomevfssrc_change_state(GstElement *element)
{
	g_return_val_if_fail(GST_IS_GNOMEVFSSRC(element),
			     GST_STATE_FAILURE);

1094
	switch (GST_STATE_TRANSITION (element)) {
Wim Taymans's avatar
Wim Taymans committed
1095
	case GST_STATE_READY_TO_PAUSED:
1096 1097
		if (!GST_FLAG_IS_SET(element, GST_GNOMEVFSSRC_OPEN)) {
			if (!gst_gnomevfssrc_open_file
1098
					(GST_GNOMEVFSSRC(element)))
1099 1100
				return GST_STATE_FAILURE;
		}
1101
		break;
Wim Taymans's avatar
Wim Taymans committed
1102
	case GST_STATE_PAUSED_TO_READY:
1103 1104
		if (GST_FLAG_IS_SET(element, GST_GNOMEVFSSRC_OPEN))
			gst_gnomevfssrc_close_file(GST_GNOMEVFSSRC
1105 1106
					(element));
		break;
Wim Taymans's avatar
Wim Taymans committed
1107 1108
	case GST_STATE_NULL_TO_READY:
	case GST_STATE_READY_TO_NULL:
1109 1110
	default:
		break;
1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125
	}

	if (GST_ELEMENT_CLASS(parent_class)->change_state)
		return GST_ELEMENT_CLASS(parent_class)->
		    change_state(element);

	return GST_STATE_SUCCESS;
}

static gboolean plugin_init(GModule *module, GstPlugin *plugin)
{
	GstElementFactory *factory;

	/* create an elementfactory for the aasink element */
	factory =
1126
	    gst_element_factory_new("gnomevfssrc", GST_TYPE_GNOMEVFSSRC,
1127 1128 1129 1130 1131 1132 1133 1134
				   &gst_gnomevfssrc_details);
	g_return_val_if_fail(factory != NULL, FALSE);

	gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));

	return TRUE;
}

1135

Wim Taymans's avatar
Wim Taymans committed
1136
static gboolean
1137
gst_gnomevfssrc_srcpad_query (GstPad *pad, GstQueryType type,
Wim Taymans's avatar
Wim Taymans committed
1138 1139 1140 1141 1142
		              GstFormat *format, gint64 *value)
{
	GstGnomeVFSSrc *src = GST_GNOMEVFSSRC (gst_pad_get_parent (pad));

	switch (type) {
1143
	case GST_QUERY_TOTAL:
Wim Taymans's avatar
Wim Taymans committed
1144 1145 1146 1147 1148
		if (*format != GST_FORMAT_BYTES) {
			return FALSE;
		}
		*value = src->size;
		break;
1149
	case GST_QUERY_POSITION:
Wim Taymans's avatar
Wim Taymans committed
1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161
		if (*format != GST_FORMAT_BYTES) {
			return FALSE;
		}
		*value = src->curoffset;
		break;
	default:
		return FALSE;
		break;
	}
	return TRUE;
} 

1162 1163 1164 1165 1166 1167 1168
static gboolean
gst_gnomevfssrc_srcpad_event (GstPad *pad, GstEvent *event)
{
	GstGnomeVFSSrc *src = GST_GNOMEVFSSRC(GST_PAD_PARENT(pad));

	switch (GST_EVENT_TYPE (event)) {
	case GST_EVENT_SEEK:
1169
        {
Wim Taymans's avatar
Wim Taymans committed
1170
		gint64 desired_offset;
1171
		GnomeVFSResult result;
Wim Taymans's avatar
Wim Taymans committed
1172

1173
		if (GST_EVENT_SEEK_FORMAT (event) != GST_FORMAT_BYTES) {
1174
			gst_event_unref (event);
1175 1176 1177 1178
			return FALSE;
		}
		switch (GST_EVENT_SEEK_METHOD (event)) {
		case GST_SEEK_METHOD_SET:
Wim Taymans's avatar
Wim Taymans committed
1179
			desired_offset = (guint64) GST_EVENT_SEEK_OFFSET (event);
1180
			break;
1181
		case GST_SEEK_METHOD_CUR:
Wim Taymans's avatar
Wim Taymans committed
1182
			desired_offset = src->curoffset + GST_EVENT_SEEK_OFFSET (event);
1183
			break;
1184
		case GST_SEEK_METHOD_END:
Wim Taymans's avatar
Wim Taymans committed
1185
			desired_offset = src->size - ABS (GST_EVENT_SEEK_OFFSET (event));
1186 1187
			break;
		default:
1188
			gst_event_unref (event);
1189 1190 1191
			return FALSE;
			break;
		}
1192 1193
		
		result = gnome_vfs_seek(src->handle,
Wim Taymans's avatar
Wim Taymans committed
1194
					GNOME_VFS_SEEK_START, desired_offset);
1195
		GST_DEBUG ("new_seek: %s",
1196 1197 1198 1199 1200
			  gnome_vfs_result_to_string(result));
		
		if (result != GNOME_VFS_OK) {
			gst_event_unref (event);
			return FALSE;
Wim Taymans's avatar
Wim Taymans committed
1201 1202 1203
		}
		src->curoffset = desired_offset;
		src->new_seek = TRUE;
1204
		src->need_flush = GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH;
1205
		break;
1206
	}
Wim Taymans's avatar
Wim Taymans committed
1207 1208
	case GST_EVENT_SIZE:
		if (GST_EVENT_SIZE_FORMAT (event) != GST_FORMAT_BYTES) {
1209
			gst_event_unref (event);
Wim Taymans's avatar
Wim Taymans committed
1210 1211 1212 1213 1214 1215
			return FALSE;
		}
		src->bytes_per_read = GST_EVENT_SIZE_VALUE (event);
		g_object_notify (G_OBJECT (src), "bytesperread");
		break;

1216 1217 1218 1219
	case GST_EVENT_FLUSH:
		src->need_flush = TRUE;
		break;
	default:
1220
		gst_event_unref (event);
1221 1222 1223
		return FALSE;
		break;
	}
1224
	gst_event_unref (event);
1225 1226 1227 1228

	return TRUE;
}

1229 1230 1231 1232 1233 1234
GstPluginDesc plugin_desc = {
	GST_VERSION_MAJOR,
	GST_VERSION_MINOR,
	"gnomevfssrc",
	plugin_init
};
1235 1236 1237 1238 1239 1240

/*
  Local Variables:
  c-file-style: "linux"
  End:
*/