udapted vi.po
[rhythmbox.git] / daapsharing / rb-daap-src.c
blobe6dc250080c787ee4e3a8688c4a520838311fac5
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 * Implementatin of DAAP (iTunes Music Sharing) GStreamer source
5 * Copyright (C) 2005 Charles Schmidt <cschmidt2@emich.edu>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "config.h"
25 #include <string.h>
26 #include <netdb.h>
27 #include <netinet/in.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <sys/ioctl.h>
33 #include <netdb.h>
34 #include <unistd.h>
35 #include <ctype.h>
37 #include <libsoup/soup-headers.h>
38 #include <libsoup/soup-misc.h>
40 #include <glib/gi18n.h>
41 #include <gst/gst.h>
42 #ifdef HAVE_GSTREAMER_0_10
43 #include <gst/base/gstbasesrc.h>
44 #include <gst/base/gstpushsrc.h>
45 #endif
47 #include "rb-daap-source.h"
48 #include "rb-daap-src.h"
49 #include "rb-debug.h"
50 #include "rb-daap-plugin.h"
52 #define RB_TYPE_DAAP_SRC (rb_daap_src_get_type())
53 #define RB_DAAP_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),RB_TYPE_DAAP_SRC,RBDAAPSrc))
54 #define RB_DAAP_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),RB_TYPE_DAAP_SRC,RBDAAPSrcClass))
55 #define RB_IS_DAAP_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),RB_TYPE_DAAP_SRC))
56 #define RB_IS_DAAP_SRC_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),RB_TYPE_DAAP_SRC))
58 #define RESPONSE_BUFFER_SIZE (4096)
60 #ifdef HAVE_GSTREAMER_0_8
61 typedef enum {
62 RB_DAAP_SRC_OPEN = GST_ELEMENT_FLAG_LAST,
64 RB_DAAP_SRC_FLAG_LAST = GST_ELEMENT_FLAG_LAST + 2
65 } RBDAAPSrcFlags;
66 #endif
68 typedef struct _RBDAAPSrc RBDAAPSrc;
69 typedef struct _RBDAAPSrcClass RBDAAPSrcClass;
71 struct _RBDAAPSrc
73 #ifdef HAVE_GSTREAMER_0_8
74 GstElement element;
75 GstPad *srcpad;
76 #else
77 GstPushSrc parent;
78 #endif
80 /* uri */
81 gchar *daap_uri;
83 /* connection */
84 int sock_fd;
85 gchar *buffer_base;
86 gchar *buffer;
87 guint buffer_size;
88 guint32 bytes_per_read;
89 gboolean chunked;
90 gboolean first_chunk;
92 gint64 size;
94 /* Seek stuff */
95 gint64 curoffset;
96 gint64 seek_bytes;
97 gboolean do_seek;
98 #ifdef HAVE_GSTREAMER_0_8
99 gboolean need_flush;
100 gboolean send_discont;
101 glong seek_time_to_return;
102 glong seek_time;
103 #endif
106 struct _RBDAAPSrcClass
108 #ifdef HAVE_GSTREAMER_0_8
109 GstElementClass parent_class;
110 #else
111 GstPushSrcClass parent_class;
112 #endif
115 #ifdef HAVE_GSTREAMER_0_10
116 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
117 GST_PAD_SRC,
118 GST_PAD_ALWAYS,
119 GST_STATIC_CAPS_ANY);
120 #endif
122 GST_DEBUG_CATEGORY_STATIC (rb_daap_src_debug);
123 #define GST_CAT_DEFAULT rb_daap_src_debug
125 static GstElementDetails rb_daap_src_details =
126 GST_ELEMENT_DETAILS ("RBDAAP Source",
127 "Source/File",
128 "Read a DAAP (music share) file",
129 "Charles Schmidt <cschmidt2@emich.edu");
131 static RBDaapPlugin *daap_plugin = NULL;
133 static void rb_daap_src_uri_handler_init (gpointer g_iface, gpointer iface_data);
135 static void
136 _do_init (GType daap_src_type)
138 static const GInterfaceInfo urihandler_info = {
139 rb_daap_src_uri_handler_init,
140 NULL,
141 NULL
143 GST_DEBUG_CATEGORY_INIT (rb_daap_src_debug,
144 "daapsrc", GST_DEBUG_FG_WHITE,
145 "Rhythmbox built in DAAP source element");
147 g_type_add_interface_static (daap_src_type, GST_TYPE_URI_HANDLER,
148 &urihandler_info);
151 #ifdef HAVE_GSTREAMER_0_8
152 GST_BOILERPLATE_FULL (RBDAAPSrc, rb_daap_src, GstElement, GST_TYPE_ELEMENT, _do_init);
153 #else
154 GST_BOILERPLATE_FULL (RBDAAPSrc, rb_daap_src, GstElement, GST_TYPE_PUSH_SRC, _do_init);
155 #endif
157 static void rb_daap_src_finalize (GObject *object);
158 static void rb_daap_src_set_property (GObject *object,
159 guint prop_id,
160 const GValue *value,
161 GParamSpec *pspec);
162 static void rb_daap_src_get_property (GObject *object,
163 guint prop_id,
164 GValue *value,
165 GParamSpec *pspec);
167 #ifdef HAVE_GSTREAMER_0_8
168 static GstData *rb_daap_src_get (GstPad *pad);
170 static GstElementStateReturn rb_daap_src_change_state (GstElement *element);
172 static void rb_daap_src_close_file (RBDAAPSrc *src);
173 static gboolean rb_daap_src_open_file (RBDAAPSrc *src);
174 static gboolean rb_daap_src_srcpad_event (GstPad *pad,
175 GstEvent *event);
176 static gboolean rb_daap_src_srcpad_query (GstPad *pad,
177 GstQueryType type,
178 GstFormat *format,
179 gint64 *value);
180 #else
181 static gboolean rb_daap_src_start (GstBaseSrc *bsrc);
182 static gboolean rb_daap_src_stop (GstBaseSrc *bsrc);
183 static gboolean rb_daap_src_is_seekable (GstBaseSrc *bsrc);
184 static gboolean rb_daap_src_get_size (GstBaseSrc *src, guint64 *size);
185 static gboolean rb_daap_src_do_seek (GstBaseSrc *src, GstSegment *segment);
186 static GstFlowReturn rb_daap_src_create (GstPushSrc *psrc, GstBuffer **outbuf);
187 #endif
189 void
190 rb_daap_src_set_plugin (RBPlugin *plugin)
192 g_assert (RB_IS_DAAP_PLUGIN (plugin));
193 daap_plugin = RB_DAAP_PLUGIN (plugin);
196 #ifdef HAVE_GSTREAMER_0_8
198 static const GstFormat *
199 rb_daap_src_get_formats (GstPad *pad)
201 static const GstFormat formats[] = {
202 GST_FORMAT_BYTES,
206 return formats;
209 static const GstQueryType *
210 rb_daap_src_get_query_types (GstPad *pad)
212 static const GstQueryType types[] = {
213 GST_QUERY_TOTAL,
214 GST_QUERY_POSITION,
218 return types;
221 static const GstEventMask *
222 rb_daap_src_get_event_mask (GstPad *pad)
224 static const GstEventMask masks[] = {
225 // {GST_EVENT_SEEK, GST_SEEK_METHOD_CUR | GST_SEEK_METHOD_SET | GST_SEEK_METHOD_END | GST_SEEK_FLAG_FLUSH},
226 {GST_EVENT_FLUSH, 0},
227 {GST_EVENT_SIZE, 0},
228 {0, 0},
231 return masks;
234 #endif
236 enum
238 PROP_0,
239 PROP_LOCATION,
240 PROP_SEEKABLE,
241 PROP_BYTESPERREAD
244 static void
245 rb_daap_src_base_init (gpointer g_class)
247 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
248 #ifdef HAVE_GSTREAMER_0_10
249 gst_element_class_add_pad_template (element_class,
250 gst_static_pad_template_get (&srctemplate));
251 #endif
252 gst_element_class_set_details (element_class, &rb_daap_src_details);
255 static void
256 rb_daap_src_class_init (RBDAAPSrcClass *klass)
258 #ifdef HAVE_GSTREAMER_0_8
259 GObjectClass *gobject_class;
260 GstElementClass *gstelement_class;
262 gobject_class = (GObjectClass *) klass;
263 gstelement_class = (GstElementClass *) klass;
265 parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
267 gst_element_class_install_std_props (GST_ELEMENT_CLASS (klass),
268 "bytesperread", PROP_BYTESPERREAD, G_PARAM_READWRITE,
269 "location", PROP_LOCATION, G_PARAM_READWRITE, NULL);
271 gobject_class->finalize = rb_daap_src_finalize;
273 g_object_class_install_property (gobject_class,
274 PROP_SEEKABLE,
275 g_param_spec_boolean ("seekable",
276 "seekable",
277 "TRUE if stream is seekable",
278 TRUE,
279 G_PARAM_READABLE));
281 gstelement_class->set_property = rb_daap_src_set_property;
282 gstelement_class->get_property = rb_daap_src_get_property;
284 gstelement_class->change_state = rb_daap_src_change_state;
285 #else
286 GObjectClass *gobject_class;
287 GstElementClass *gstelement_class;
288 GstBaseSrcClass *gstbasesrc_class;
289 GstPushSrcClass *gstpushsrc_class;
291 gobject_class = G_OBJECT_CLASS (klass);
292 gstelement_class = GST_ELEMENT_CLASS (klass);
293 gstbasesrc_class = (GstBaseSrcClass *) klass;
294 gstpushsrc_class = (GstPushSrcClass *) klass;
296 parent_class = g_type_class_ref (GST_TYPE_PUSH_SRC);
298 gobject_class->set_property = rb_daap_src_set_property;
299 gobject_class->get_property = rb_daap_src_get_property;
300 gobject_class->finalize = rb_daap_src_finalize;
302 g_object_class_install_property (gobject_class, PROP_LOCATION,
303 g_param_spec_string ("location",
304 "file location",
305 "location of the file to read",
306 NULL,
307 G_PARAM_READWRITE));
309 gstbasesrc_class->start = GST_DEBUG_FUNCPTR (rb_daap_src_start);
310 gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (rb_daap_src_stop);
311 gstbasesrc_class->is_seekable = GST_DEBUG_FUNCPTR (rb_daap_src_is_seekable);
312 gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (rb_daap_src_get_size);
313 gstbasesrc_class->do_seek = GST_DEBUG_FUNCPTR (rb_daap_src_do_seek);
315 gstpushsrc_class->create = GST_DEBUG_FUNCPTR (rb_daap_src_create);
316 #endif
319 #ifdef HAVE_GSTREAMER_0_8
320 static void
321 rb_daap_src_init (RBDAAPSrc *src)
322 #else
323 static void
324 rb_daap_src_init (RBDAAPSrc *src, RBDAAPSrcClass *klass)
325 #endif
327 src->daap_uri = NULL;
328 src->sock_fd = -1;
329 src->curoffset = 0;
330 src->bytes_per_read = 4096 * 2;
332 #ifdef HAVE_GSTREAMER_0_8
333 src->seek_bytes = 0;
335 src->send_discont = FALSE;
336 src->need_flush = FALSE;
338 src->srcpad = gst_pad_new ("src", GST_PAD_SRC);
339 gst_pad_set_get_function (src->srcpad,
340 rb_daap_src_get);
341 gst_pad_set_event_mask_function (src->srcpad,
342 rb_daap_src_get_event_mask);
343 gst_pad_set_event_function (src->srcpad,
344 rb_daap_src_srcpad_event);
345 gst_pad_set_query_type_function (src->srcpad,
346 rb_daap_src_get_query_types);
347 gst_pad_set_query_function (src->srcpad,
348 rb_daap_src_srcpad_query);
349 gst_pad_set_formats_function (src->srcpad,
350 rb_daap_src_get_formats);
351 gst_element_add_pad (GST_ELEMENT (src), src->srcpad);
352 #endif
355 static void
356 rb_daap_src_finalize (GObject *object)
358 RBDAAPSrc *src;
359 src = RB_DAAP_SRC (object);
361 #ifdef HAVE_GSTREAMER_0_8
362 if (GST_FLAG_IS_SET (src, RB_DAAP_SRC_OPEN)) {
363 rb_daap_src_close_file (src);
365 #endif
367 g_free (src->daap_uri);
368 src->daap_uri = NULL;
370 if (src->sock_fd != -1)
371 close (src->sock_fd);
373 G_OBJECT_CLASS (parent_class)->finalize (object);
376 static void
377 rb_daap_src_set_property (GObject *object,
378 guint prop_id,
379 const GValue *value,
380 GParamSpec *pspec)
382 RBDAAPSrc *src = RB_DAAP_SRC (object);
384 switch (prop_id) {
385 case PROP_LOCATION:
386 #ifdef HAVE_GSTREAMER_0_8
387 /* the element must be stopped or paused in order to do src */
388 if (GST_STATE (src) == GST_STATE_PLAYING || GST_STATE (src) == GST_STATE_PAUSED) {
389 break;
391 #else
392 /* XXX check stuff */
393 #endif
395 if (src->daap_uri) {
396 g_free (src->daap_uri);
397 src->daap_uri = NULL;
399 src->daap_uri = g_strdup (g_value_get_string (value));
400 break;
401 #ifdef HAVE_GSTREAMER_0_8
402 case PROP_BYTESPERREAD:
403 src->bytes_per_read = g_value_get_int (value);
404 break;
405 #endif
406 default:
407 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
408 break;
412 static void
413 rb_daap_src_get_property (GObject *object,
414 guint prop_id,
415 GValue *value,
416 GParamSpec *pspec)
418 RBDAAPSrc *src = RB_DAAP_SRC (object);
420 switch (prop_id) {
421 case PROP_LOCATION:
422 g_value_set_string (value, src->daap_uri);
423 break;
424 #ifdef HAVE_GSTREAMER_0_8
425 case PROP_SEEKABLE:
426 g_value_set_boolean (value, FALSE);
427 break;
428 case PROP_BYTESPERREAD:
429 g_value_set_int (value, src->bytes_per_read);
430 break;
431 #endif
432 default:
433 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
434 break;
438 static gint
439 rb_daap_src_write (RBDAAPSrc *src, const guchar *buf, size_t count)
441 size_t bytes_written = 0;
443 while (bytes_written < count) {
444 ssize_t wrote = send (src->sock_fd, buf + bytes_written, count - bytes_written, MSG_NOSIGNAL);
446 if (wrote < 0) {
447 GST_WARNING ("error while writing: %s", g_strerror (errno));
448 return wrote;
450 if (wrote == 0)
451 break;
453 bytes_written += wrote;
456 GST_DEBUG_OBJECT (src, "wrote %d bytes succesfully", bytes_written);
457 return bytes_written;
460 static gint
461 rb_daap_src_read (RBDAAPSrc *src, guchar *buf, size_t count)
463 size_t bytes_read = 0;
465 if (src->buffer_size > 0) {
466 bytes_read = count;
467 if (bytes_read > src->buffer_size)
468 bytes_read = src->buffer_size;
470 GST_DEBUG_OBJECT (src, "reading %d bytes from buffer", bytes_read);
471 memcpy (buf, src->buffer, bytes_read);
472 src->buffer += bytes_read;
473 src->buffer_size -= bytes_read;
475 if (src->buffer_size == 0) {
476 g_free (src->buffer_base);
477 src->buffer_base = NULL;
478 src->buffer = NULL;
482 while (bytes_read < count) {
483 ssize_t ret = read (src->sock_fd, buf + bytes_read, count - bytes_read);
485 if (ret < 0) {
486 GST_WARNING ("error while reading: %s", g_strerror (errno));
487 return ret;
489 if (ret == 0)
490 break;
491 bytes_read += ret;
494 GST_DEBUG_OBJECT (src, "read %d bytes succesfully", bytes_read);
495 return bytes_read;
498 static gboolean
499 _expect_char (RBDAAPSrc *src, guchar expected)
501 guchar ch;
502 if (rb_daap_src_read (src, &ch, sizeof (ch)) <= 0)
503 return FALSE;
504 if (ch != expected) {
505 GST_DEBUG_OBJECT (src, "Expected char %d next, but got %d", expected, ch);
506 return FALSE;
508 return TRUE;
511 static gboolean
512 rb_daap_src_read_chunk_size (RBDAAPSrc *src, gboolean first_chunk, gint64 *chunk_size)
514 gchar chunk_buf[30];
515 gchar ch;
516 gint i = 0;
517 memset (&chunk_buf, 0, sizeof (chunk_buf));
519 GST_DEBUG_OBJECT (src, "reading next chunk size; first_chunk = %d", first_chunk);
520 if (!first_chunk) {
521 if (!_expect_char (src, '\r') ||
522 !_expect_char (src, '\n')) {
523 return FALSE;
527 while (1) {
528 if (rb_daap_src_read (src, (guchar *)&ch, sizeof(ch)) <= 0)
529 return FALSE;
531 if (ch == '\r') {
532 if (!_expect_char (src, '\n')) {
533 return FALSE;
535 *chunk_size = strtoul (chunk_buf, NULL, 16);
536 if (*chunk_size == 0) {
537 /* EOS */
538 GST_DEBUG_OBJECT (src, "got EOS chunk");
539 return TRUE;
540 } else if (*chunk_size == ULONG_MAX) {
541 /* overflow */
542 GST_DEBUG_OBJECT (src, "HTTP chunk size overflowed");
543 return FALSE;
546 GST_DEBUG_OBJECT (src, "got HTTP chunk size %lu", *chunk_size);
547 return TRUE;
548 } else if (isxdigit (ch)) {
549 chunk_buf[i++] = ch;
550 } else {
551 GST_DEBUG_OBJECT (src, "HTTP chunk size included illegal character %c", ch);
552 return FALSE;
556 g_assert_not_reached ();
559 static void
560 _split_uri (const gchar *daap_uri, gchar **host, guint *port, gchar **path)
562 gint locationlen;
563 const gchar *pathstart = NULL;
564 const gchar *hostport = NULL;
565 const gchar *portstart = NULL;
567 locationlen = strlen (daap_uri);
568 hostport = daap_uri + 7;
569 pathstart = strchr (hostport, '/');
571 if (pathstart) {
572 *path = g_strdup (pathstart);
573 } else {
574 *path = g_strdup ("/");
575 pathstart = daap_uri + locationlen;
578 portstart = strrchr (hostport, ':');
579 if (portstart) {
580 *host = g_strndup (hostport, portstart - hostport);
581 *port = strtoul (portstart + 1, NULL, 0);
582 } else {
583 *host = g_strndup (hostport, pathstart - hostport);
584 *port = 3869;
588 static gboolean
589 rb_daap_src_open (RBDAAPSrc *src)
591 int ret;
592 struct sockaddr_in server;
593 RBDAAPSource *source;
594 gchar *headers;
595 gchar *host;
596 guint port;
597 gchar *path;
598 GHashTable *header_table;
599 gchar *request;
600 gchar *response;
601 gchar *end_headers;
602 size_t readsize;
603 gboolean ok = TRUE;
604 guint http_status;
605 gchar *http_status_phrase = NULL;
607 if (src->buffer_base) {
608 g_free (src->buffer_base);
609 src->buffer_base = NULL;
610 src->buffer = NULL;
611 src->buffer_size = 0;
614 rb_debug ("Connecting to DAAP source: %s", src->daap_uri);
616 /* connect */
617 src->sock_fd = socket (AF_INET, SOCK_STREAM, 0);
618 if (src->sock_fd == -1) {
619 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), GST_ERROR_SYSTEM);
620 return FALSE;
623 _split_uri (src->daap_uri, &host, &port, &path);
625 server.sin_family = AF_INET;
626 server.sin_port = htons (port);
627 server.sin_addr.s_addr = inet_addr (host);
628 memset (&server.sin_zero, 0, sizeof (server.sin_zero));
630 GST_DEBUG_OBJECT (src, "connecting to server %s:%d", host, port);
631 ret = connect (src->sock_fd, (struct sockaddr *) &server, sizeof (struct sockaddr));
632 if (ret) {
633 if (errno == ECONNREFUSED) {
634 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
635 (_("Connection to %s:%d refused."), host, port),
636 (NULL));
637 } else {
638 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
639 ("Connect to %s:%d failed: %s", host, port,
640 g_strerror (errno)));
642 g_free (host);
643 g_free (path);
644 return FALSE;
647 /* construct request */
648 source = rb_daap_plugin_find_source_for_uri (daap_plugin, src->daap_uri);
649 if (source == NULL) {
650 g_warning ("Unable to lookup source for URI: %s", src->daap_uri);
651 return FALSE;
654 /* The following can fail if the source is no longer connected */
655 #ifdef HAVE_GSTREAMER_0_8
656 headers = rb_daap_source_get_headers (source, src->daap_uri, src->seek_time, &src->seek_bytes);
657 #else
658 headers = rb_daap_source_get_headers (source, src->daap_uri, src->seek_bytes);
659 #endif
660 if (headers == NULL) {
661 g_free (host);
662 g_free (path);
663 return FALSE;
666 request = g_strdup_printf ("GET %s HTTP/1.1\r\nHost: %s\r\n%s\r\n",
667 path, host, headers);
668 g_free (headers);
669 g_free (host);
670 g_free (path);
672 /* send request */
673 GST_DEBUG_OBJECT (src, "Sending HTTP request:\n%s", request);
674 if (rb_daap_src_write (src, (guchar *)request, strlen (request)) <= 0) {
675 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
676 ("Sending HTTP request to %s failed: %s",
677 src->daap_uri, g_strerror (errno)));
678 g_free (request);
679 return FALSE;
681 g_free (request);
683 /* read response */
684 response = g_malloc0 (RESPONSE_BUFFER_SIZE + 1);
685 readsize = rb_daap_src_read (src, (guchar *)response, RESPONSE_BUFFER_SIZE);
686 if (readsize <= 0) {
687 g_free (response);
688 GST_DEBUG_OBJECT (src, "Error while reading HTTP response header");
689 return FALSE;
691 response[readsize] = '\0';
692 GST_DEBUG_OBJECT (src, "Got HTTP response:\n%s", response);
694 end_headers = strstr (response, "\r\n\r\n");
695 if (!end_headers) {
696 /* this means the DAAP server returned more than 4k of headers.
697 * not terribly likely.
699 g_free (response);
700 GST_DEBUG_OBJECT (src, "HTTP response header way too long");
701 return FALSE;
704 /* libsoup wants the headers null-terminated, despite taking a parameter
705 * specifying how long they are.
707 end_headers[2] = '\0';
708 end_headers += 4;
710 header_table = g_hash_table_new (soup_str_case_hash, soup_str_case_equal);
711 if (soup_headers_parse_response (response,
712 (end_headers - response),
713 header_table,
714 NULL,
715 &http_status,
716 &http_status_phrase)) {
717 if (http_status == 200 || http_status == 206) {
718 GSList *val;
720 val = g_hash_table_lookup (header_table, "Transfer-Encoding");
721 if (val) {
722 if (g_strcasecmp ((gchar *)val->data, "chunked") == 0) {
723 src->chunked = TRUE;
724 } else {
725 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
726 ("Unknown HTTP transfer encoding \"%s\"", val->data));
728 } else {
729 src->chunked = FALSE;
730 val = g_hash_table_lookup (header_table, "Content-Length");
731 if (val) {
732 char *e;
733 src->size = strtoul ((char *)val->data, &e, 10);
734 if (e == val->data) {
735 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
736 ("Couldn't read HTTP content length \"%s\"", val->data));
737 ok = FALSE;
739 } else {
740 GST_DEBUG_OBJECT (src, "Response doesn't have a content length");
741 src->size = 0;
745 } else {
746 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
747 ("HTTP error: %s", http_status_phrase),
748 (NULL));
749 ok = FALSE;
751 } else {
752 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
753 ("Unable to parse HTTP response"));
754 ok = FALSE;
756 g_free (http_status_phrase);
757 g_hash_table_destroy (header_table);
759 /* copy remaining data into a new buffer */
760 if (ok) {
761 src->buffer_size = readsize - (end_headers - response);
762 src->buffer_base = g_malloc0 (src->buffer_size);
763 src->buffer = src->buffer_base;
764 memcpy (src->buffer_base, response + (readsize - src->buffer_size), src->buffer_size);
766 g_free (response);
768 return ok;
771 static gboolean
772 #ifdef HAVE_GSTREAMER_0_8
773 rb_daap_src_open_file (RBDAAPSrc *src)
775 #else
776 rb_daap_src_start (GstBaseSrc *bsrc)
778 RBDAAPSrc *src = RB_DAAP_SRC (bsrc);
779 #endif
780 if (src->sock_fd != -1) {
781 close (src->sock_fd);
784 src->curoffset = 0;
786 if (rb_daap_src_open (src)) {
787 src->buffer = src->buffer_base;
788 #ifdef HAVE_GSTREAMER_0_8
789 src->seek_time_to_return = src->seek_time;
790 if (src->seek_bytes != 0) {
791 src->need_flush = TRUE;
792 src->send_discont = TRUE;
794 GST_FLAG_SET (src, RB_DAAP_SRC_OPEN);
795 #else
796 src->curoffset = src->seek_bytes;
797 #endif
798 if (src->chunked) {
799 src->first_chunk = TRUE;
800 src->size = 0;
802 return TRUE;
803 } else {
804 return FALSE;
808 #ifdef HAVE_GSTREAMER_0_8
809 static void
810 rb_daap_src_close_file (RBDAAPSrc *src)
812 if (src->sock_fd != -1) {
813 close (src->sock_fd);
814 src->sock_fd = -1;
816 src->seek_bytes = 0;
817 src->curoffset = 0;
818 src->size = 0;
819 src->send_discont = FALSE;
821 GST_FLAG_UNSET (src, RB_DAAP_SRC_OPEN);
823 #else
824 static gboolean
825 rb_daap_src_stop (GstBaseSrc *bsrc)
827 /* don't do anything - this seems to get called during setup, but
828 * we don't get started again afterwards.
830 return TRUE;
832 #endif
834 #ifdef HAVE_GSTREAMER_0_8
835 static GstData *
836 rb_daap_src_get (GstPad *pad)
837 #else
838 static GstFlowReturn
839 rb_daap_src_create (GstPushSrc *psrc, GstBuffer **outbuf)
840 #endif
842 RBDAAPSrc *src;
843 size_t readsize;
844 GstBuffer *buf = NULL;
846 #ifdef HAVE_GSTREAMER_0_8
847 g_return_val_if_fail (pad != NULL, NULL);
848 g_return_val_if_fail (GST_IS_PAD (pad), NULL);
849 src = RB_DAAP_SRC (GST_OBJECT_PARENT (pad));
850 g_return_val_if_fail (GST_FLAG_IS_SET (src, RB_DAAP_SRC_OPEN), NULL);
851 #else
852 src = RB_DAAP_SRC (psrc);
853 #endif
855 if (src->do_seek) {
856 if (src->sock_fd != -1) {
857 close (src->sock_fd);
858 src->sock_fd = -1;
860 #ifdef HAVE_GSTREAMER_0_8
861 if (!rb_daap_src_open_file (src))
862 return GST_DATA (gst_event_new (GST_EVENT_EOS));
863 #else
864 if (!rb_daap_src_start (GST_BASE_SRC (src)))
865 return GST_FLOW_ERROR;
866 #endif
867 src->do_seek = FALSE;
870 #ifdef HAVE_GSTREAMER_0_8
871 /* try to negotiate here */
872 if (!gst_pad_is_negotiated (pad)) {
873 if (GST_PAD_LINK_FAILED (gst_pad_renegotiate (pad))) {
874 GST_ELEMENT_ERROR (src, CORE, NEGOTIATION, (NULL), GST_ERROR_SYSTEM);
875 gst_buffer_unref (buf);
876 return GST_DATA (gst_event_new (GST_EVENT_EOS));
880 if (src->need_flush) {
881 GstEvent *event = gst_event_new_flush ();
883 src->need_flush = FALSE;
884 return GST_DATA (event);
887 if (src->send_discont) {
888 GstEvent *event;
890 src->send_discont = FALSE;
891 event = gst_event_new_discontinuous (FALSE, GST_FORMAT_BYTES, src->curoffset + src->seek_bytes, NULL);
892 return GST_DATA (event);
894 #endif
896 /* get a new chunk, if we need one */
897 if (src->chunked && src->size == 0) {
898 if (!rb_daap_src_read_chunk_size (src, src->first_chunk, &src->size)) {
899 #ifdef HAVE_GSTREAMER_0_8
900 return GST_DATA (gst_event_new (GST_EVENT_EOS));
901 #else
902 return GST_FLOW_ERROR;
903 #endif
904 } else if (src->size == 0) {
905 /* EOS */
906 #ifdef HAVE_GSTREAMER_0_8
907 gst_element_set_eos (GST_ELEMENT (src));
908 return GST_DATA (gst_event_new (GST_EVENT_EOS));
909 #else
910 return GST_FLOW_UNEXPECTED;
911 #endif
913 src->first_chunk = FALSE;
916 readsize = src->bytes_per_read;
917 if (src->chunked && readsize > src->size)
918 readsize = src->size;
920 #ifdef HAVE_GSTREAMER_0_8
921 buf = gst_buffer_new ();
922 g_return_val_if_fail (buf, NULL);
923 GST_BUFFER_DATA (buf) = g_malloc0 (readsize);
924 g_return_val_if_fail (GST_BUFFER_DATA (buf) != NULL, NULL);
925 #else
926 buf = gst_buffer_new_and_alloc (readsize);
927 #endif
929 GST_LOG_OBJECT (src, "Reading %d bytes", readsize);
930 readsize = rb_daap_src_read (src, GST_BUFFER_DATA (buf), readsize);
931 if (readsize < 0) {
932 GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
933 gst_buffer_unref (buf);
934 #ifdef HAVE_GSTREAMER_0_8
935 return GST_DATA (gst_event_new (GST_EVENT_EOS));
936 #else
937 return GST_FLOW_ERROR;
938 #endif
941 if (readsize == 0) {
942 GST_DEBUG ("blocking read returns 0, EOS");
943 gst_buffer_unref (buf);
944 #ifdef HAVE_GSTREAMER_0_8
945 gst_element_set_eos (GST_ELEMENT (src));
946 return GST_DATA (gst_event_new (GST_EVENT_EOS));
947 #else
948 return GST_FLOW_UNEXPECTED;
949 #endif
952 if (src->chunked)
953 src->size -= readsize;
955 GST_BUFFER_OFFSET (buf) = src->curoffset;
956 GST_BUFFER_SIZE (buf) = readsize;
957 GST_BUFFER_TIMESTAMP (buf) = GST_CLOCK_TIME_NONE;
958 src->curoffset += readsize;
960 GST_LOG_OBJECT (src,
961 "Returning buffer from _get of size %d, ts %"
962 GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT
963 ", offset %" G_GINT64_FORMAT ", offset_end %" G_GINT64_FORMAT,
964 GST_BUFFER_SIZE (buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
965 GST_TIME_ARGS (GST_BUFFER_DURATION (buf)),
966 GST_BUFFER_OFFSET (buf), GST_BUFFER_OFFSET_END (buf));
967 #ifdef HAVE_GSTREAMER_0_8
968 return GST_DATA (buf);
969 #else
970 *outbuf = buf;
971 return GST_FLOW_OK;
972 #endif
975 #ifdef HAVE_GSTREAMER_0_8
976 static GstElementStateReturn
977 rb_daap_src_change_state (GstElement *element)
979 g_return_val_if_fail (RB_IS_DAAP_SRC (element), GST_STATE_FAILURE);
981 switch (GST_STATE_TRANSITION (element)) {
982 case GST_STATE_READY_TO_PAUSED:
983 if (!GST_FLAG_IS_SET (element, RB_DAAP_SRC_OPEN)) {
984 if (!rb_daap_src_open_file (RB_DAAP_SRC (element))) {
985 return GST_STATE_FAILURE;
988 break;
989 case GST_STATE_PAUSED_TO_READY:
990 if (GST_FLAG_IS_SET (element, RB_DAAP_SRC_OPEN)) {
991 rb_daap_src_close_file (RB_DAAP_SRC (element));
993 break;
994 case GST_STATE_NULL_TO_READY:
995 case GST_STATE_READY_TO_NULL:
996 default:
997 break;
1000 if (GST_ELEMENT_CLASS (parent_class)->change_state) {
1001 return GST_ELEMENT_CLASS (parent_class)->change_state (element);
1004 return GST_STATE_SUCCESS;
1007 static gboolean
1008 rb_daap_src_srcpad_event (GstPad *pad,
1009 GstEvent *event)
1011 RBDAAPSrc *src = RB_DAAP_SRC (GST_PAD_PARENT (pad));
1013 switch (GST_EVENT_TYPE (event)) {
1014 case GST_EVENT_SEEK: {
1015 gint64 desired_offset = 0;
1017 if (GST_EVENT_SEEK_FORMAT (event) != GST_FORMAT_BYTES) {
1018 gst_event_unref (event);
1019 return FALSE;
1022 switch (GST_EVENT_SEEK_METHOD (event)) {
1023 case GST_SEEK_METHOD_SET:
1024 desired_offset = (gint64) GST_EVENT_SEEK_OFFSET (event);
1025 break;
1026 case GST_SEEK_METHOD_CUR:
1027 desired_offset = src->curoffset + GST_EVENT_SEEK_OFFSET (event);
1028 break;
1029 case GST_SEEK_METHOD_END:
1030 if (src->size == 0) {
1031 return FALSE;
1033 desired_offset = src->size - ABS (GST_EVENT_SEEK_OFFSET (event));
1034 break;
1035 default:
1036 gst_event_unref (event);
1037 return FALSE;
1040 return FALSE;
1041 break;
1043 case GST_EVENT_SIZE:
1044 if (GST_EVENT_SIZE_FORMAT (event) != GST_FORMAT_BYTES) {
1045 gst_event_unref (event);
1046 return FALSE;
1048 src->bytes_per_read = GST_EVENT_SIZE_VALUE (event);
1049 g_object_notify (G_OBJECT (src), "bytesperread");
1050 break;
1051 case GST_EVENT_FLUSH:
1052 src->need_flush = TRUE;
1053 break;
1054 default:
1055 gst_event_unref (event);
1056 return FALSE;
1057 break;
1060 gst_event_unref (event);
1062 return TRUE;
1065 static gboolean
1066 rb_daap_src_srcpad_query (GstPad *pad,
1067 GstQueryType type,
1068 GstFormat *format,
1069 gint64 *value)
1071 RBDAAPSrc *src = RB_DAAP_SRC (gst_pad_get_parent (pad));
1073 switch (type) {
1074 case GST_QUERY_TOTAL:
1075 if (*format != GST_FORMAT_BYTES || src->size == 0) {
1076 return FALSE;
1078 *value = src->size;
1079 break;
1080 case GST_QUERY_POSITION:
1081 switch (*format) {
1082 case GST_FORMAT_BYTES:
1083 *value = src->curoffset;
1084 break;
1085 case GST_FORMAT_PERCENT:
1086 return FALSE; /* FIXME */
1087 if (src->size == 0) {
1088 return FALSE;
1090 *value = src->curoffset * GST_FORMAT_PERCENT_MAX / src->size;
1091 break;
1092 default:
1093 return FALSE;
1095 break;
1096 default:
1097 return FALSE;
1098 break;
1101 return TRUE;
1103 #else
1105 gboolean
1106 rb_daap_src_is_seekable (GstBaseSrc *bsrc)
1108 return TRUE;
1111 gboolean
1112 rb_daap_src_do_seek (GstBaseSrc *bsrc, GstSegment *segment)
1114 RBDAAPSrc *src = RB_DAAP_SRC (bsrc);
1115 if (segment->format == GST_FORMAT_BYTES) {
1116 src->do_seek = TRUE;
1117 src->seek_bytes = segment->start;
1118 return TRUE;
1119 } else {
1120 return FALSE;
1124 gboolean
1125 rb_daap_src_get_size (GstBaseSrc *bsrc, guint64 *size)
1127 RBDAAPSrc *src = RB_DAAP_SRC (bsrc);
1128 if (src->chunked == FALSE && src->size > 0) {
1129 *size = src->size;
1130 return TRUE;
1132 return FALSE;
1135 #endif
1137 static gboolean
1138 plugin_init (GstPlugin *plugin)
1140 gboolean ret = gst_element_register (plugin, "rbdaapsrc", GST_RANK_PRIMARY, RB_TYPE_DAAP_SRC);
1141 return ret;
1144 GST_PLUGIN_DEFINE_STATIC (GST_VERSION_MAJOR,
1145 GST_VERSION_MINOR,
1146 "rbdaap",
1147 "element to access DAAP music share files",
1148 plugin_init,
1149 VERSION,
1150 "GPL",
1151 PACKAGE,
1152 "");
1154 #ifdef HAVE_GSTREAMER_0_8
1155 /*** RB DAAP SEEK INTERFACE **************************************************/
1157 void
1158 rb_daap_src_set_time (GstElement *element, glong time)
1160 RBDAAPSrc *src = RB_DAAP_SRC (element);
1161 src->seek_time = time;
1162 src->do_seek = TRUE;
1165 glong
1166 rb_daap_src_get_time (GstElement *element)
1168 RBDAAPSrc *src = RB_DAAP_SRC (element);
1169 return src->seek_time_to_return;
1172 #endif
1173 /*** GSTURIHANDLER INTERFACE *************************************************/
1175 static guint
1176 rb_daap_src_uri_get_type (void)
1178 return GST_URI_SRC;
1181 static gchar **
1182 rb_daap_src_uri_get_protocols (void)
1184 static gchar *protocols[] = {"daap", NULL};
1186 return protocols;
1189 static const gchar *
1190 rb_daap_src_uri_get_uri (GstURIHandler *handler)
1192 RBDAAPSrc *src = RB_DAAP_SRC (handler);
1194 return src->daap_uri;
1197 static gboolean
1198 rb_daap_src_uri_set_uri (GstURIHandler *handler,
1199 const gchar *uri)
1201 RBDAAPSrc *src = RB_DAAP_SRC (handler);
1203 if (GST_STATE (src) == GST_STATE_PLAYING || GST_STATE (src) == GST_STATE_PAUSED) {
1204 return FALSE;
1207 g_object_set (G_OBJECT (src), "location", uri, NULL);
1209 return TRUE;
1212 static void
1213 rb_daap_src_uri_handler_init (gpointer g_iface,
1214 gpointer iface_data)
1216 GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
1218 iface->get_type = rb_daap_src_uri_get_type;
1219 iface->get_protocols = rb_daap_src_uri_get_protocols;
1220 iface->get_uri = rb_daap_src_uri_get_uri;
1221 iface->set_uri = rb_daap_src_uri_set_uri;