summaryrefslogtreecommitdiffstats
path: root/ext
diff options
context:
space:
mode:
authorEdgard Lima <edgard.lima@indt.org.br>2006-12-22 16:23:32 +0000
committerEdgard Lima <edgard.lima@indt.org.br>2006-12-22 16:23:32 +0000
commit983cd81f2cea48f64b53f461e189686ab088ac9b (patch)
tree35e30e3121cac04f44ddd671a8f016a5b75eb090 /ext
parentb9925f8b22fd4cea03dbad2983aa309529b8f50e (diff)
downloadgst-plugins-bad-983cd81f2cea48f64b53f461e189686ab088ac9b.tar.gz
gst-plugins-bad-983cd81f2cea48f64b53f461e189686ab088ac9b.tar.bz2
gst-plugins-bad-983cd81f2cea48f64b53f461e189686ab088ac9b.zip
Added seek support.
Original commit message from CVS: Added seek support.
Diffstat (limited to 'ext')
-rw-r--r--ext/neon/gstneonhttpsrc.c947
-rw-r--r--ext/neon/gstneonhttpsrc.h5
2 files changed, 514 insertions, 438 deletions
diff --git a/ext/neon/gstneonhttpsrc.c b/ext/neon/gstneonhttpsrc.c
index 5777dbb4..4c380c97 100644
--- a/ext/neon/gstneonhttpsrc.c
+++ b/ext/neon/gstneonhttpsrc.c
@@ -1,5 +1,7 @@
/* GStreamer
* Copyright (C) <2005> Edgard Lima <edgard.lima@indt.org.br>
+ * Copyright (C) <2006> Rosfran Borges <rosfran.borges@indt.org.br>
+ * Copyright (C) <2006> Andre Moreira Magalhaes <andre.magalhaes@indt.org.br>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -29,20 +31,24 @@
#define HTTP_DEFAULT_HOST "localhost"
#define HTTP_DEFAULT_PORT 80
#define HTTPS_DEFAULT_PORT 443
+#define HTTP_SOCKET_ERROR -2
+#define HTTP_REQUEST_WRONG_PROXY -1
GST_DEBUG_CATEGORY_STATIC (neonhttpsrc_debug);
#define GST_CAT_DEFAULT neonhttpsrc_debug
-#define MAX_READ_SIZE (4 * 1024)
+#define MAX_READ_SIZE (4 * 1024)
/* max number of HTTP redirects, when iterating over a sequence of HTTP 302 status code */
-#define MAX_HTTP_REDIRECTS_NUMBER 5
+#define MAX_HTTP_REDIRECTS_NUMBER 5
static const GstElementDetails gst_neonhttp_src_details =
GST_ELEMENT_DETAILS ("HTTP client source",
"Source/Network",
"Receive data as a client over the network via HTTP using NEON",
- "Edgard Lima <edgard.lima@indt.org.br>");
+ "Edgard Lima <edgard.lima@indt.org.br>, "
+ "Rosfran Borges <rosfran.borges@indt.org.br>, "
+ "Andre Moreira Magalhaes <andre.magalhaes@indt.org.br>");
static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
@@ -60,34 +66,41 @@ enum
PROP_IRADIO_NAME,
PROP_IRADIO_GENRE,
PROP_IRADIO_URL,
- PROP_NEON_HTTP_REDIRECT
+ PROP_NEON_HTTP_REDIRECT,
#ifndef GST_DISABLE_GST_DEBUG
- , PROP_NEON_HTTP_DBG
+ PROP_NEON_HTTP_DBG
#endif
};
-static void oom_callback ();
-
-static gboolean set_proxy (GstNeonhttpSrc * src, const char *uri,
- ne_uri * parsed, gboolean set_default);
-static gboolean set_uri (GstNeonhttpSrc * src, const char *uri, ne_uri * parsed,
- gboolean * ishttps, gchar ** uristr, gboolean set_default);
-
-static void gst_neonhttp_src_finalize (GObject * gobject);
+static void gst_neonhttp_src_uri_handler_init (gpointer g_iface,
+ gpointer iface_data);
+static void gst_neonhttp_src_dispose (GObject * gobject);
+static void gst_neonhttp_src_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_neonhttp_src_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
static GstFlowReturn gst_neonhttp_src_create (GstPushSrc * psrc,
GstBuffer ** outbuf);
static gboolean gst_neonhttp_src_start (GstBaseSrc * bsrc);
static gboolean gst_neonhttp_src_stop (GstBaseSrc * bsrc);
static gboolean gst_neonhttp_src_get_size (GstBaseSrc * bsrc, guint64 * size);
-
-static void gst_neonhttp_src_set_property (GObject * object, guint prop_id,
- const GValue * value, GParamSpec * pspec);
-static void gst_neonhttp_src_get_property (GObject * object, guint prop_id,
- GValue * value, GParamSpec * pspec);
-
-static void
-gst_neonhttp_src_uri_handler_init (gpointer g_iface, gpointer iface_data);
+static gboolean gst_neonhttp_src_is_seekable (GstBaseSrc * bsrc);
+static gboolean gst_neonhttp_src_do_seek (GstBaseSrc * bsrc,
+ GstSegment * segment);
+
+static gboolean gst_neonhttp_src_set_proxy (GstNeonhttpSrc * src,
+ const gchar * uri, ne_uri * parsed, gboolean set_default);
+static gboolean gst_neonhttp_src_set_uri (GstNeonhttpSrc * src,
+ const gchar * uri, ne_uri * parsed, gboolean * ishttps, gchar ** uristr,
+ gboolean set_default);
+static gint gst_neonhttp_src_send_request_and_redirect (GstNeonhttpSrc * src,
+ ne_session ** ses, ne_request ** req, gint64 offset, gboolean do_redir);
+static gint gst_neonhttp_src_request_dispatch (GstNeonhttpSrc * src,
+ GstBuffer * outbuf);
+static void gst_neonhttp_src_close_session (GstNeonhttpSrc * src);
+static gchar *gst_neonhttp_src_unicodify (const gchar * str);
+static void oom_callback (void);
static void
_urihandler_init (GType type)
@@ -123,15 +136,15 @@ gst_neonhttp_src_class_init (GstNeonhttpSrcClass * klass)
{
GObjectClass *gobject_class;
GstBaseSrcClass *gstbasesrc_class;
- GstPushSrcClass *gstpush_src_class;
+ GstPushSrcClass *gstpushsrc_class;
gobject_class = (GObjectClass *) klass;
gstbasesrc_class = (GstBaseSrcClass *) klass;
- gstpush_src_class = (GstPushSrcClass *) klass;
+ gstpushsrc_class = (GstPushSrcClass *) klass;
gobject_class->set_property = gst_neonhttp_src_set_property;
gobject_class->get_property = gst_neonhttp_src_get_property;
- gobject_class->finalize = gst_neonhttp_src_finalize;
+ gobject_class->dispose = gst_neonhttp_src_dispose;
g_object_class_install_property
(gobject_class, PROP_LOCATION,
@@ -202,127 +215,213 @@ gst_neonhttp_src_class_init (GstNeonhttpSrcClass * klass)
gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_neonhttp_src_start);
gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_neonhttp_src_stop);
gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_neonhttp_src_get_size);
+ gstbasesrc_class->is_seekable =
+ GST_DEBUG_FUNCPTR (gst_neonhttp_src_is_seekable);
+ gstbasesrc_class->do_seek = GST_DEBUG_FUNCPTR (gst_neonhttp_src_do_seek);
- gstpush_src_class->create = GST_DEBUG_FUNCPTR (gst_neonhttp_src_create);
+ gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_neonhttp_src_create);
GST_DEBUG_CATEGORY_INIT (neonhttpsrc_debug, "neonhttpsrc", 0,
"NEON HTTP Client Source");
}
static void
-gst_neonhttp_src_init (GstNeonhttpSrc * this, GstNeonhttpSrcClass * g_class)
+gst_neonhttp_src_init (GstNeonhttpSrc * src, GstNeonhttpSrcClass * g_class)
{
- this->session = NULL;
- this->request = NULL;
-
- memset (&this->uri, 0, sizeof (this->uri));
- this->uristr = NULL;
- memset (&this->proxy, 0, sizeof (this->proxy));
- this->ishttps = FALSE;
- this->content_size = -1;
-
- set_uri (this, NULL, &this->uri, &this->ishttps, &this->uristr, TRUE);
- set_proxy (this, NULL, &this->proxy, TRUE);
-
- this->user_agent = g_strdup ("neonhttpsrc");
-
- this->iradio_mode = FALSE;
- this->iradio_name = NULL;
- this->iradio_genre = NULL;
- this->iradio_url = NULL;
- this->icy_caps = NULL;
- this->icy_metaint = 0;
+ memset (&src->uri, 0, sizeof (src->uri));
+ memset (&src->proxy, 0, sizeof (src->proxy));
+ src->content_size = -1;
+
+ gst_neonhttp_src_set_uri (src, NULL, &src->uri, &src->ishttps, &src->uristr,
+ TRUE);
+ gst_neonhttp_src_set_proxy (src, NULL, &src->proxy, TRUE);
+
+ src->user_agent = g_strdup ("neonhttpsrc");
+ src->seekable = TRUE;
}
static void
-gst_neonhttp_src_finalize (GObject * gobject)
+gst_neonhttp_src_dispose (GObject * gobject)
{
- GstNeonhttpSrc *this = GST_NEONHTTP_SRC (gobject);
+ GstNeonhttpSrc *src = GST_NEONHTTP_SRC (gobject);
- ne_uri_free (&this->uri);
- ne_uri_free (&this->proxy);
+ ne_uri_free (&src->uri);
+ ne_uri_free (&src->proxy);
- g_free (this->user_agent);
- g_free (this->iradio_name);
- g_free (this->iradio_genre);
- g_free (this->iradio_url);
+ g_free (src->user_agent);
+ g_free (src->iradio_name);
+ g_free (src->iradio_genre);
+ g_free (src->iradio_url);
- if (this->icy_caps) {
- gst_caps_unref (this->icy_caps);
- this->icy_caps = NULL;
+ if (src->icy_caps) {
+ gst_caps_unref (src->icy_caps);
+ src->icy_caps = NULL;
}
- if (this->request) {
- ne_request_destroy (this->request);
- this->request = NULL;
+ if (src->request) {
+ ne_request_destroy (src->request);
+ src->request = NULL;
}
- if (this->session) {
- ne_close_connection (this->session);
- ne_session_destroy (this->session);
- this->session = NULL;
+ if (src->session) {
+ ne_close_connection (src->session);
+ ne_session_destroy (src->session);
+ src->session = NULL;
}
- if (this->uristr) {
- ne_free (this->uristr);
+ if (src->uristr) {
+ ne_free (src->uristr);
}
- G_OBJECT_CLASS (parent_class)->finalize (gobject);
+ G_OBJECT_CLASS (parent_class)->dispose (gobject);
}
-static int
-request_dispatch (GstNeonhttpSrc * src, GstBuffer * outbuf)
+static void
+gst_neonhttp_src_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
{
- int ret;
- int read = 0;
- int sizetoread = GST_BUFFER_SIZE (outbuf);
+ GstNeonhttpSrc *src = GST_NEONHTTP_SRC (object);
- /* Loop sending the request:
- * Retry whilst authentication fails and we supply it. */
-
- ssize_t len = 0;
-
- while (sizetoread > 0) {
- len = ne_read_response_block (src->request,
- (char *) GST_BUFFER_DATA (outbuf) + read, sizetoread);
- if (len > 0) {
- read += len;
- sizetoread -= len;
- } else {
+ switch (prop_id) {
+ case PROP_PROXY:
+ {
+ if (!g_value_get_string (value)) {
+ GST_WARNING ("proxy property cannot be NULL");
+ goto done;
+ }
+ if (!gst_neonhttp_src_set_proxy (src, g_value_get_string (value),
+ &src->proxy, FALSE)) {
+ GST_WARNING ("badly formated proxy");
+ goto done;
+ }
break;
}
-
+ case PROP_URI:
+ case PROP_LOCATION:
+ {
+ if (!g_value_get_string (value)) {
+ GST_WARNING ("location property cannot be NULL");
+ goto done;
+ }
+ if (!gst_neonhttp_src_set_uri (src, g_value_get_string (value), &src->uri,
+ &src->ishttps, &src->uristr, FALSE)) {
+ GST_WARNING ("badly formated location");
+ goto done;
+ }
+ break;
+ }
+ case PROP_USER_AGENT:
+ {
+ if (src->user_agent) {
+ g_free (src->user_agent);
+ src->user_agent = NULL;
+ }
+ if (g_value_get_string (value)) {
+ src->user_agent = g_strdup (g_value_get_string (value));
+ }
+ break;
+ }
+ case PROP_IRADIO_MODE:
+ {
+ src->iradio_mode = g_value_get_boolean (value);
+ break;
+ }
+ case PROP_NEON_HTTP_REDIRECT:
+ {
+ src->neon_http_redirect = g_value_get_boolean (value);
+ break;
+ }
+#ifndef GST_DISABLE_GST_DEBUG
+ case PROP_NEON_HTTP_DBG:
+ {
+ src->neon_http_msgs_dbg = g_value_get_boolean (value);
+ break;
+ }
+#endif
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
}
+done:
+ return;
+}
- GST_BUFFER_SIZE (outbuf) = read;
+static void
+gst_neonhttp_src_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstNeonhttpSrc *neonhttpsrc = GST_NEONHTTP_SRC (object);
- if (len < 0) {
- read = -2;
- goto done;
- } else if (len == 0) {
- ret = ne_end_request (src->request);
- if (ret != NE_RETRY) {
- if (ret == NE_OK) {
- src->eos = TRUE;
+ switch (prop_id) {
+ case PROP_PROXY:
+ {
+ gchar *str;
+
+ if (neonhttpsrc->proxy.host) {
+ str = ne_uri_unparse (&neonhttpsrc->proxy);
+ if (!str)
+ break;
+ g_value_set_string (value, str);
+ ne_free (str);
} else {
- read = -3;
+ g_value_set_string (value, "");
}
+ break;
}
- goto done;
- }
+ case PROP_URI:
+ case PROP_LOCATION:
+ {
+ gchar *str;
-done:
- return read;
+ if (neonhttpsrc->uri.host) {
+ str = ne_uri_unparse (&neonhttpsrc->uri);
+ if (!str)
+ break;
+ g_value_set_string (value, str);
+ ne_free (str);
+ } else {
+ g_value_set_string (value, "");
+ }
+ break;
+ }
+ case PROP_USER_AGENT:
+ {
+ g_value_set_string (value, neonhttpsrc->user_agent);
+ break;
+ }
+ case PROP_IRADIO_MODE:
+ g_value_set_boolean (value, neonhttpsrc->iradio_mode);
+ break;
+ case PROP_IRADIO_NAME:
+ g_value_set_string (value, neonhttpsrc->iradio_name);
+ break;
+ case PROP_IRADIO_GENRE:
+ g_value_set_string (value, neonhttpsrc->iradio_genre);
+ break;
+ case PROP_IRADIO_URL:
+ g_value_set_string (value, neonhttpsrc->iradio_url);
+ break;
+ case PROP_NEON_HTTP_REDIRECT:
+ g_value_set_boolean (value, neonhttpsrc->neon_http_redirect);
+ break;
+#ifndef GST_DISABLE_GST_DEBUG
+ case PROP_NEON_HTTP_DBG:
+ g_value_set_boolean (value, neonhttpsrc->neon_http_msgs_dbg);
+ break;
+#endif
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
}
-
static GstFlowReturn
gst_neonhttp_src_create (GstPushSrc * psrc, GstBuffer ** outbuf)
{
GstNeonhttpSrc *src;
GstBaseSrc *basesrc;
GstFlowReturn ret;
- int read;
+ gint read;
src = GST_NEONHTTP_SRC (psrc);
basesrc = GST_BASE_SRC_CAST (psrc);
@@ -340,7 +439,7 @@ gst_neonhttp_src_create (GstPushSrc * psrc, GstBuffer ** outbuf)
if (G_UNLIKELY (ret != GST_FLOW_OK))
goto done;
- read = request_dispatch (src, *outbuf);
+ read = gst_neonhttp_src_request_dispatch (src, *outbuf);
if (G_UNLIKELY (read < 0))
goto read_error;
@@ -364,56 +463,13 @@ read_error:
}
}
-/* The following two charset mangling functions were copied from gnomevfssrc.
- * Preserve them under the unverified assumption that they do something vaguely
- * worthwhile.
- */
-static char *
-unicodify (const char *str, int len, ...)
-{
- char *ret = NULL, *cset;
- va_list args;
- gsize bytes_read, bytes_written;
-
- 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_neonhttp_src_unicodify (const char *str)
-{
- return unicodify (str, -1, "locale", "ISO-8859-1", NULL);
-}
-
-#define HTTP_SOCKET_ERROR -2
-#define HTTP_REQUEST_WRONG_PROXY -1
-
-/**
- * Try to send the HTTP request to the Icecast server, and if possible deals with
- * all the probable redirections (HTTP status code == 302)
- */
-static gint
-send_request_and_redirect (GstNeonhttpSrc * src, gboolean do_redir)
+/* create a socket for connecting to remote server */
+static gboolean
+gst_neonhttp_src_start (GstBaseSrc * bsrc)
{
+ GstNeonhttpSrc *src = GST_NEONHTTP_SRC (bsrc);
+ const gchar *content_length;
gint res;
- gint http_status = 0;
-
- guint request_count = 0;
#ifndef GST_DISABLE_GST_DEBUG
if (src->neon_http_msgs_dbg)
@@ -422,90 +478,13 @@ send_request_and_redirect (GstNeonhttpSrc * src, gboolean do_redir)
ne_oom_callback (oom_callback);
- if ((res = ne_sock_init ()) != 0)
- return HTTP_SOCKET_ERROR;
-
- do {
-
- src->session =
- ne_session_create (src->uri.scheme, src->uri.host, src->uri.port);
-
- if (src->proxy.host && src->proxy.port) {
- ne_session_proxy (src->session, src->proxy.host, src->proxy.port);
- } else if (src->proxy.host || src->proxy.port) {
- /* both proxy host and port must be specified or none */
- return HTTP_REQUEST_WRONG_PROXY;
- }
-
- src->request = ne_request_create (src->session, "GET", src->uri.path);
-
- if (src->user_agent) {
- ne_add_request_header (src->request, "User-Agent", src->user_agent);
- }
-
- if (src->iradio_mode) {
- ne_add_request_header (src->request, "icy-metadata", "1");
- }
-
- res = ne_begin_request (src->request);
-
- if (res == NE_OK) {
- /* When the HTTP status code is 302, it is not the SHOUTcast streaming content yet;
- * Reload the HTTP request with a new URI value */
- http_status = ne_get_status (src->request)->code;
- if (http_status == 302) {
- const gchar *redir;
-
- /* the new URI value to go when redirecting can be found on the 'Location' HTTP header */
- redir = ne_get_response_header (src->request, "Location");
- if (redir != NULL) {
- ne_uri_free (&src->uri);
- set_uri (src, redir, &src->uri, &src->ishttps, &src->uristr, FALSE);
-#ifndef GST_DISABLE_GST_DEBUG
- if (src->neon_http_msgs_dbg)
- GST_LOG_OBJECT (src,
- "--> Got HTTP Status Code %d; Using 'Location' header [%s]",
- http_status, src->uri.host);
-#endif
- }
-
- }
- /* if - http_status == 302 */
- }
- /* if - NE_OK */
- ++request_count;
- if (http_status == 302) {
- GST_WARNING_OBJECT (src, "%s %s.",
- (request_count < MAX_HTTP_REDIRECTS_NUMBER)
- && do_redir ? "Redirecting to" :
- "WILL NOT redirect, try it again with a different URI; an alternative is",
- src->uri.host);
- /* FIXME: when not redirecting automatically, shouldn't we post a
- * redirect element message on the bus? */
- }
-#ifndef GST_DISABLE_GST_DEBUG
- if (src->neon_http_msgs_dbg)
- GST_LOG_OBJECT (src, "--> request_count = %d", request_count);
-#endif
-
- /* do the redirect, go back to send another HTTP request now using the 'Location' */
- } while (do_redir && (request_count < MAX_HTTP_REDIRECTS_NUMBER)
- && http_status == 302);
-
- return res;
-
-}
-
-/* create a socket for connecting to remote server */
-static gboolean
-gst_neonhttp_src_start (GstBaseSrc * bsrc)
-{
- GstNeonhttpSrc *src = GST_NEONHTTP_SRC (bsrc);
- const char *content_length;
+ if (ne_sock_init () != 0)
+ return FALSE;
- gint res = send_request_and_redirect (src, src->neon_http_redirect);
+ res = gst_neonhttp_src_send_request_and_redirect (src,
+ &src->session, &src->request, 0, src->neon_http_redirect);
- if (res != NE_OK) {
+ if (res != NE_OK || !src->session) {
if (res == HTTP_SOCKET_ERROR) {
#ifndef GST_DISABLE_GST_DEBUG
if (src->neon_http_msgs_dbg) {
@@ -540,7 +519,7 @@ gst_neonhttp_src_start (GstBaseSrc * bsrc)
if (src->iradio_mode) {
/* Icecast stuff */
- const char *str_value;
+ const gchar *str_value;
gint gint_value;
str_value = ne_get_response_header (src->request, "icy-metaint");
@@ -605,21 +584,6 @@ begin_req_failed:
}
}
-static gboolean
-gst_neonhttp_src_get_size (GstBaseSrc * bsrc, guint64 * size)
-{
- GstNeonhttpSrc *src;
-
- src = GST_NEONHTTP_SRC (bsrc);
-
- if (src->content_size != -1)
- return FALSE;
-
- *size = src->content_size;
-
- return TRUE;
-}
-
/* close the socket and associated resources
* used both to recover from errors and go to NULL state */
static gboolean
@@ -649,76 +613,78 @@ gst_neonhttp_src_stop (GstBaseSrc * bsrc)
src->icy_caps = NULL;
}
- if (src->request) {
- ne_request_destroy (src->request);
- src->request = NULL;
- }
+ src->eos = FALSE;
+ src->content_size = -1;
+ src->read_position = 0;
+ src->seekable = TRUE;
- if (src->session) {
- ne_close_connection (src->session);
- ne_session_destroy (src->session);
- src->session = NULL;
- }
+ gst_neonhttp_src_close_session (src);
- src->eos = FALSE;
+#ifndef GST_DISABLE_GST_DEBUG
+ ne_debug_init (NULL, 0);
+#endif
+ ne_oom_callback (NULL);
+ ne_sock_exit ();
return TRUE;
}
static gboolean
-set_proxy (GstNeonhttpSrc * src, const char *uri, ne_uri * parsed,
- gboolean set_default)
+gst_neonhttp_src_get_size (GstBaseSrc * bsrc, guint64 * size)
{
- ne_uri_free (parsed);
-
- if (set_default) {
- const char *str = g_getenv ("http_proxy");
+ GstNeonhttpSrc *src;
- if (str) {
- if (ne_uri_parse (str, parsed) != 0)
- goto cannot_parse;
- }
- return TRUE;
- }
+ src = GST_NEONHTTP_SRC (bsrc);
- if (ne_uri_parse (uri, parsed) != 0)
- goto error;
+ if (src->content_size == -1)
+ return FALSE;
- if (parsed->scheme)
- GST_WARNING ("The proxy schema shouldn't be defined");
+ *size = src->content_size;
- if (parsed->host && !parsed->port)
- goto error;
+ return TRUE;
+}
-#ifdef NEON_026_OR_LATER
- if (!parsed->path || parsed->userinfo)
- goto error;
-#else
- if (!parsed->path || parsed->authinfo)
- goto error;
-#endif
+static gboolean
+gst_neonhttp_src_is_seekable (GstBaseSrc * bsrc)
+{
return TRUE;
+}
- /* ERRORS */
-error:
- {
- ne_uri_free (parsed);
+static gboolean
+gst_neonhttp_src_do_seek (GstBaseSrc * bsrc, GstSegment * segment)
+{
+ GstNeonhttpSrc *src;
+ gint res;
+ ne_session *session = NULL;
+ ne_request *request = NULL;
+
+ src = GST_NEONHTTP_SRC (bsrc);
+
+ if (!src->seekable)
return FALSE;
- }
-cannot_parse:
- {
- GST_WARNING_OBJECT (src,
- "The proxy set on http_proxy env var isn't well formated");
- ne_uri_free (parsed);
+
+ if (src->read_position == segment->start)
+ return TRUE;
+
+ res = gst_neonhttp_src_send_request_and_redirect (src,
+ &session, &request, segment->start, src->neon_http_redirect);
+
+ /* if we are able to seek, replace the session */
+ if (res == NE_OK && session) {
+ gst_neonhttp_src_close_session (src);
+ src->session = session;
+ src->request = request;
+ src->read_position = segment->start;
return TRUE;
}
+
+ return FALSE;
}
static gboolean
-set_uri (GstNeonhttpSrc * src, const char *uri, ne_uri * parsed,
- gboolean * ishttps, gchar ** uristr, gboolean set_default)
+gst_neonhttp_src_set_uri (GstNeonhttpSrc * src, const gchar * uri,
+ ne_uri * parsed, gboolean * ishttps, gchar ** uristr, gboolean set_default)
{
-
ne_uri_free (parsed);
if (uristr && *uristr) {
ne_free (*uristr);
@@ -778,175 +744,271 @@ parse_error:
}
}
-static void
-gst_neonhttp_src_set_property (GObject * object, guint prop_id,
- const GValue * value, GParamSpec * pspec)
+static gboolean
+gst_neonhttp_src_set_proxy (GstNeonhttpSrc * src, const gchar * uri,
+ ne_uri * parsed, gboolean set_default)
{
- GstNeonhttpSrc *this = GST_NEONHTTP_SRC (object);
+ ne_uri_free (parsed);
- switch (prop_id) {
- case PROP_PROXY:
- {
- if (!g_value_get_string (value)) {
- GST_WARNING ("proxy property cannot be NULL");
- goto done;
- }
- if (!set_proxy (this, g_value_get_string (value), &this->proxy, FALSE)) {
- GST_WARNING ("badly formated proxy");
- goto done;
- }
- break;
+ if (set_default) {
+ const gchar *str = g_getenv ("http_proxy");
+
+ if (str) {
+ if (ne_uri_parse (str, parsed) != 0)
+ goto cannot_parse;
}
- case PROP_URI:
- case PROP_LOCATION:
- {
- if (!g_value_get_string (value)) {
- GST_WARNING ("location property cannot be NULL");
- goto done;
- }
- if (!set_uri (this, g_value_get_string (value), &this->uri,
- &this->ishttps, &this->uristr, FALSE)) {
- GST_WARNING ("badly formated location");
- goto done;
- }
- break;
+ return TRUE;
+ }
+
+ if (ne_uri_parse (uri, parsed) != 0)
+ goto error;
+
+ if (parsed->scheme)
+ GST_WARNING ("The proxy schema shouldn't be defined");
+
+ if (parsed->host && !parsed->port)
+ goto error;
+
+#ifdef NEON_026_OR_LATER
+ if (!parsed->path || parsed->userinfo)
+ goto error;
+#else
+ if (!parsed->path || parsed->authinfo)
+ goto error;
+#endif
+ return TRUE;
+
+ /* ERRORS */
+error:
+ {
+ ne_uri_free (parsed);
+ return FALSE;
+ }
+cannot_parse:
+ {
+ GST_WARNING_OBJECT (src,
+ "The proxy set on http_proxy env var isn't well formated");
+ ne_uri_free (parsed);
+ return TRUE;
+ }
+}
+
+/**
+ * Try to send the HTTP request to the Icecast server, and if possible deals with
+ * all the probable redirections (HTTP status code == 302)
+ */
+static gint
+gst_neonhttp_src_send_request_and_redirect (GstNeonhttpSrc * src,
+ ne_session ** ses, ne_request ** req, gint64 offset, gboolean do_redir)
+{
+ ne_session *session = NULL;
+ ne_request *request = NULL;
+ gint res;
+ gint http_status = 0;
+ guint request_count = 0;
+
+ do {
+ if (src->proxy.host && src->proxy.port) {
+ session =
+ ne_session_create (src->uri.scheme, src->uri.host, src->uri.port);
+ ne_session_proxy (session, src->proxy.host, src->proxy.port);
+ } else if (src->proxy.host || src->proxy.port) {
+ /* both proxy host and port must be specified or none */
+ return HTTP_REQUEST_WRONG_PROXY;
+ } else {
+ session =
+ ne_session_create (src->uri.scheme, src->uri.host, src->uri.port);
}
- case PROP_USER_AGENT:
- {
- if (this->user_agent) {
- g_free (this->user_agent);
- this->user_agent = NULL;
- }
- if (g_value_get_string (value)) {
- this->user_agent = g_strdup (g_value_get_string (value));
- }
- break;
+
+ request = ne_request_create (session, "GET", src->uri.path);
+
+ if (src->user_agent) {
+ ne_add_request_header (request, "User-Agent", src->user_agent);
}
- case PROP_IRADIO_MODE:
- {
- this->iradio_mode = g_value_get_boolean (value);
- break;
+
+ if (src->iradio_mode) {
+ ne_add_request_header (request, "icy-metadata", "1");
}
- case PROP_NEON_HTTP_REDIRECT:
- {
- this->neon_http_redirect = g_value_get_boolean (value);
- break;
+
+ if (offset > 0) {
+ ne_print_request_header (request, "Range",
+ "bytes=%" G_GINT64_FORMAT "-", offset);
}
+
+ res = ne_begin_request (request);
+
+ if (res == NE_OK) {
+ /* When the HTTP status code is 302, it is not the SHOUTcast streaming content yet;
+ * Reload the HTTP request with a new URI value */
+ http_status = ne_get_status (request)->code;
+ if (http_status == 302 && do_redir) {
+ const gchar *redir;
+
+ /* the new URI value to go when redirecting can be found on the 'Location' HTTP header */
+ redir = ne_get_response_header (request, "Location");
+ if (redir != NULL) {
+ ne_uri_free (&src->uri);
+ gst_neonhttp_src_set_uri (src, redir, &src->uri, &src->ishttps,
+ &src->uristr, FALSE);
#ifndef GST_DISABLE_GST_DEBUG
- case PROP_NEON_HTTP_DBG:
- {
- this->neon_http_msgs_dbg = g_value_get_boolean (value);
- break;
+ if (src->neon_http_msgs_dbg)
+ GST_LOG_OBJECT (src,
+ "--> Got HTTP Status Code %d; Using 'Location' header [%s]",
+ http_status, src->uri.host);
+#endif
+ }
+ }
}
+
+ if ((res != NE_OK) ||
+ (offset == 0 && http_status != 200) ||
+ (offset > 0 && http_status != 206 && http_status != 302)) {
+ ne_request_destroy (request);
+ request = NULL;
+ ne_close_connection (session);
+ ne_session_destroy (session);
+ session = NULL;
+ if (offset > 0 && http_status != 206 && http_status != 302) {
+ src->seekable = FALSE;
+ }
+ }
+
+ /* if - NE_OK */
+ if (http_status == 302 && do_redir) {
+ ++request_count;
+ GST_WARNING_OBJECT (src, "%s %s.",
+ (request_count < MAX_HTTP_REDIRECTS_NUMBER)
+ && do_redir ? "Redirecting to" :
+ "WILL NOT redirect, try it again with a different URI; an alternative is",
+ src->uri.host);
+ /* FIXME: when not redirecting automatically, shouldn't we post a
+ * redirect element message on the bus? */
+#ifndef GST_DISABLE_GST_DEBUG
+ if (src->neon_http_msgs_dbg)
+ GST_LOG_OBJECT (src, "--> request_count = %d", request_count);
#endif
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
+ }
+ /* do the redirect, go back to send another HTTP request now using the 'Location' */
+ } while (do_redir && (request_count < MAX_HTTP_REDIRECTS_NUMBER)
+ && http_status == 302);
+
+ if (session) {
+ *ses = session;
+ *req = request;
}
-done:
- return;
+
+ return res;
}
-static void
-gst_neonhttp_src_get_property (GObject * object, guint prop_id,
- GValue * value, GParamSpec * pspec)
+static gint
+gst_neonhttp_src_request_dispatch (GstNeonhttpSrc * src, GstBuffer * outbuf)
{
- GstNeonhttpSrc *neonhttpsrc = GST_NEONHTTP_SRC (object);
+ gint ret;
+ gint read = 0;
+ gint sizetoread = GST_BUFFER_SIZE (outbuf);
- switch (prop_id) {
- case PROP_PROXY:
- {
- char *str;
+ /* Loop sending the request:
+ * Retry whilst authentication fails and we supply it. */
- if (neonhttpsrc->proxy.host) {
- str = ne_uri_unparse (&neonhttpsrc->proxy);
- if (!str)
- break;
- g_value_set_string (value, str);
- ne_free (str);
- } else {
- g_value_set_string (value, "");
- }
+ ssize_t len = 0;
+
+ while (sizetoread > 0) {
+ len = ne_read_response_block (src->request,
+ (gchar *) GST_BUFFER_DATA (outbuf) + read, sizetoread);
+ if (len > 0) {
+ read += len;
+ sizetoread -= len;
+ } else {
break;
}
- case PROP_URI:
- case PROP_LOCATION:
- {
- char *str;
- if (neonhttpsrc->uri.host) {
- str = ne_uri_unparse (&neonhttpsrc->uri);
- if (!str)
- break;
- g_value_set_string (value, str);
- ne_free (str);
+ }
+
+ GST_BUFFER_SIZE (outbuf) = read;
+
+ if (len < 0) {
+ read = -2;
+ goto done;
+ } else if (len == 0) {
+ ret = ne_end_request (src->request);
+ if (ret != NE_RETRY) {
+ if (ret == NE_OK) {
+ src->eos = TRUE;
} else {
- g_value_set_string (value, "");
+ read = -3;
}
- break;
- }
- case PROP_USER_AGENT:
- {
- g_value_set_string (value, neonhttpsrc->user_agent);
- break;
}
- case PROP_IRADIO_MODE:
- g_value_set_boolean (value, neonhttpsrc->iradio_mode);
- break;
- case PROP_IRADIO_NAME:
- g_value_set_string (value, neonhttpsrc->iradio_name);
- break;
- case PROP_IRADIO_GENRE:
- g_value_set_string (value, neonhttpsrc->iradio_genre);
- break;
- case PROP_IRADIO_URL:
- g_value_set_string (value, neonhttpsrc->iradio_url);
- break;
- case PROP_NEON_HTTP_REDIRECT:
- g_value_set_boolean (value, neonhttpsrc->neon_http_redirect);
- break;
-#ifndef GST_DISABLE_GST_DEBUG
- case PROP_NEON_HTTP_DBG:
- g_value_set_boolean (value, neonhttpsrc->neon_http_msgs_dbg);
- break;
-#endif
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
+ goto done;
}
+
+ if (read > 0)
+ src->read_position += read;
+
+done:
+ return read;
}
-/* entry point to initialize the plug-in
- * initialize the plug-in itself
- * register the element factories and pad templates
- * register the features
- */
-static gboolean
-plugin_init (GstPlugin * plugin)
+static void
+gst_neonhttp_src_close_session (GstNeonhttpSrc * src)
{
- return gst_element_register (plugin, "neonhttpsrc", GST_RANK_NONE,
- GST_TYPE_NEONHTTP_SRC);
+ if (src->request) {
+ ne_request_destroy (src->request);
+ src->request = NULL;
+ }
+
+ if (src->session) {
+ ne_close_connection (src->session);
+ ne_session_destroy (src->session);
+ src->session = NULL;
+ }
}
-/* this is the structure that gst-register looks for
- * so keep the name plugin_desc, or you cannot get your plug-in registered */
-GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
- GST_VERSION_MINOR,
- "neon",
- "lib neon http client src",
- plugin_init, VERSION, "LGPL", "GStreamer", "http://gstreamer.net/")
+/* The following two charset mangling functions were copied from gnomevfssrc.
+ * Preserve them under the unverified assumption that they do something vaguely
+ * worthwhile.
+ */
+static gchar *
+unicodify (const gchar * str, gint len, ...)
+{
+ gchar *ret = NULL, *cset;
+ va_list args;
+ gsize bytes_read, bytes_written;
+
+ 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, gchar *)) != 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);
-/*** GSTURIHANDLER INTERFACE *************************************************/
- static guint gst_neonhttp_src_uri_get_type (void)
+ return ret;
+}
+
+static gchar *
+gst_neonhttp_src_unicodify (const gchar * str)
+{
+ return unicodify (str, -1, "locale", "ISO-8859-1", NULL);
+}
+
+/* GstURIHandler Interface */
+static guint
+gst_neonhttp_src_uri_get_type (void)
{
return GST_URI_SRC;
}
+
static gchar **
gst_neonhttp_src_uri_get_protocols (void)
{
static gchar *protocols[] = { "http", "https", NULL };
-
return protocols;
}
@@ -963,8 +1025,8 @@ gst_neonhttp_src_uri_set_uri (GstURIHandler * handler, const gchar * uri)
{
GstNeonhttpSrc *src = GST_NEONHTTP_SRC (handler);
- return set_uri (src, uri, &src->uri, &src->ishttps, &src->uristr, FALSE);
-
+ return gst_neonhttp_src_set_uri (src, uri, &src->uri, &src->ishttps,
+ &src->uristr, FALSE);
}
static void
@@ -978,7 +1040,6 @@ gst_neonhttp_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
iface->set_uri = gst_neonhttp_src_uri_set_uri;
}
-
/* NEON CALLBACK */
static void
oom_callback ()
@@ -986,12 +1047,22 @@ oom_callback ()
GST_ERROR ("memory exeception in neon");
}
-void
-size_header_handler (void *userdata, const char *value)
+/* entry point to initialize the plug-in
+ * initialize the plug-in itself
+ * register the element factories and pad templates
+ * register the features
+ */
+static gboolean
+plugin_init (GstPlugin * plugin)
{
- GstNeonhttpSrc *src = GST_NEONHTTP_SRC (userdata);
-
- src->content_size = g_ascii_strtoull (value, NULL, 10);
-
- GST_DEBUG_OBJECT (src, "content size = %lld bytes", src->content_size);
+ return gst_element_register (plugin, "neonhttpsrc", GST_RANK_NONE,
+ GST_TYPE_NEONHTTP_SRC);
}
+
+/* this is the structure that gst-register looks for
+ * so keep the name plugin_desc, or you cannot get your plug-in registered */
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "neon",
+ "lib neon http client src",
+ plugin_init, VERSION, "LGPL", "GStreamer", "http://gstreamer.net/")
diff --git a/ext/neon/gstneonhttpsrc.h b/ext/neon/gstneonhttpsrc.h
index 486b76c3..df52fc57 100644
--- a/ext/neon/gstneonhttpsrc.h
+++ b/ext/neon/gstneonhttpsrc.h
@@ -1,5 +1,7 @@
/* GStreamer
* Copyright (C) <2005> Edgard Lima <edgard.lima@indt.org.br>
+ * Copyright (C) <2006> Rosfran Borges <rosfran.borges@indt.org.br>
+ * Copyright (C) <2006> Andre Moreira Magalhaes <andre.magalhaes@indt.org.br>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -69,6 +71,9 @@ struct _GstNeonhttpSrc {
/* enable Neon HTTP debug messages */
gboolean neon_http_msgs_dbg;
+
+ gint64 read_position;
+ gboolean seekable;
};
struct _GstNeonhttpSrcClass {