static int ssl_verify_callback (void *data, int failures, const ne_ssl_certificate * cert) { GstNeonhttpSrc *src = GST_NEONHTTP_SRC (data); if ((failures & NE_SSL_UNTRUSTED) && src->accept_self_signed && !ne_ssl_cert_signedby (cert)) { GST_ELEMENT_INFO (src, RESOURCE, READ, (NULL), ("Accepting self-signed server certificate")); failures &= ~NE_SSL_UNTRUSTED; } if (failures & NE_SSL_NOTYETVALID) GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), ("Server certificate not valid yet")); if (failures & NE_SSL_EXPIRED) GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), ("Server certificate has expired")); if (failures & NE_SSL_IDMISMATCH) GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), ("Server certificate doesn't match hostname")); if (failures & NE_SSL_UNTRUSTED) GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), ("Server certificate signer not trusted")); GST_DEBUG_OBJECT (src, "failures: %d\n", failures); return failures; }
static const gchar * gst_neonhttp_src_uri_get_uri (GstURIHandler * handler) { GstNeonhttpSrc *src = GST_NEONHTTP_SRC (handler); return src->location; }
static gboolean gst_neonhttp_src_query (GstBaseSrc * bsrc, GstQuery * query) { GstNeonhttpSrc *src = GST_NEONHTTP_SRC (bsrc); gboolean ret; switch (GST_QUERY_TYPE (query)) { case GST_QUERY_URI: gst_query_set_uri (query, src->location); ret = TRUE; break; default: ret = FALSE; break; } if (!ret) ret = GST_BASE_SRC_CLASS (parent_class)->query (bsrc, query); switch (GST_QUERY_TYPE (query)) { case GST_QUERY_SCHEDULING: { GstSchedulingFlags flags; gint minsize, maxsize, align; gst_query_parse_scheduling (query, &flags, &minsize, &maxsize, &align); flags |= GST_SCHEDULING_FLAG_BANDWIDTH_LIMITED; gst_query_set_scheduling (query, flags, minsize, maxsize, align); break; } default: break; } return ret; }
static void gst_neonhttp_src_dispose (GObject * gobject) { GstNeonhttpSrc *src = GST_NEONHTTP_SRC (gobject); ne_uri_free (&src->uri); ne_uri_free (&src->proxy); g_free (src->user_agent); if (src->cookies) { g_strfreev (src->cookies); src->cookies = NULL; } 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; } if (src->location) { ne_free (src->location); } if (src->query_string) { ne_free (src->query_string); } G_OBJECT_CLASS (parent_class)->dispose (gobject); }
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; if (src->read_position == segment->start) return TRUE; res = gst_neonhttp_src_send_request_and_redirect (src, &session, &request, segment->start, src->automatic_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 gst_neonhttp_src_uri_set_uri (GstURIHandler * handler, const gchar * uri) { GstNeonhttpSrc *src = GST_NEONHTTP_SRC (handler); return gst_neonhttp_src_set_location (src, uri); }
static gchar * gst_neonhttp_src_uri_get_uri (GstURIHandler * handler) { GstNeonhttpSrc *src = GST_NEONHTTP_SRC (handler); /* FIXME: make thread-safe */ return g_strdup (src->location); }
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; }
static GstFlowReturn gst_neonhttp_src_create (GstPushSrc * psrc, GstBuffer ** outbuf) { GstNeonhttpSrc *src; GstBaseSrc *basesrc; GstFlowReturn ret; gint read; src = GST_NEONHTTP_SRC (psrc); basesrc = GST_BASE_SRC_CAST (psrc); /* The caller should know the number of bytes and not read beyond EOS. */ if (G_UNLIKELY (src->eos)) goto eos; /* Create the buffer. */ ret = gst_pad_alloc_buffer (GST_BASE_SRC_PAD (basesrc), basesrc->segment.last_stop, basesrc->blocksize, src->icy_caps ? src->icy_caps : GST_PAD_CAPS (GST_BASE_SRC_PAD (basesrc)), outbuf); if (G_UNLIKELY (ret != GST_FLOW_OK)) goto done; read = gst_neonhttp_src_request_dispatch (src, *outbuf); if (G_UNLIKELY (read < 0)) goto read_error; GST_LOG_OBJECT (src, "returning %u bytes", GST_BUFFER_SIZE (*outbuf)); done: return ret; /* ERRORS */ eos: { GST_DEBUG_OBJECT (src, "EOS reached"); return GST_FLOW_UNEXPECTED; } read_error: { GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), ("Could not read any bytes (%i, %s)", read, ne_get_error (src->session))); gst_buffer_unref (*outbuf); *outbuf = NULL; return GST_FLOW_ERROR; } }
/* close the socket and associated resources * used both to recover from errors and go to NULL state */ static gboolean gst_neonhttp_src_stop (GstBaseSrc * bsrc) { GstNeonhttpSrc *src; src = GST_NEONHTTP_SRC (bsrc); if (src->iradio_name) { g_free (src->iradio_name); src->iradio_name = NULL; } if (src->iradio_genre) { g_free (src->iradio_genre); src->iradio_genre = NULL; } if (src->iradio_url) { g_free (src->iradio_url); src->iradio_url = NULL; } if (src->icy_caps) { gst_caps_unref (src->icy_caps); src->icy_caps = NULL; } src->eos = FALSE; src->content_size = -1; src->read_position = 0; src->seekable = TRUE; gst_neonhttp_src_close_session (src); #ifndef GST_DISABLE_GST_DEBUG ne_debug_init (NULL, 0); #endif ne_oom_callback (NULL); ne_sock_exit (); return TRUE; }
static gboolean gst_neonhttp_src_query (GstBaseSrc * bsrc, GstQuery * query) { GstNeonhttpSrc *src = GST_NEONHTTP_SRC (bsrc); gboolean ret; switch (GST_QUERY_TYPE (query)) { case GST_QUERY_URI: gst_query_set_uri (query, src->location); ret = TRUE; break; default: ret = FALSE; break; } if (!ret) ret = GST_BASE_SRC_CLASS (parent_class)->query (bsrc, query); return ret; }
/* close the socket and associated resources * used both to recover from errors and go to NULL state */ static gboolean gst_neonhttp_src_stop (GstBaseSrc * bsrc) { GstNeonhttpSrc *src; src = GST_NEONHTTP_SRC (bsrc); src->eos = FALSE; src->content_size = -1; src->read_position = 0; src->seekable = TRUE; gst_neonhttp_src_close_session (src); #ifndef GST_DISABLE_GST_DEBUG ne_debug_init (NULL, 0); #endif ne_oom_callback (NULL); ne_sock_exit (); return TRUE; }
static GstFlowReturn gst_neonhttp_src_fill (GstPushSrc * psrc, GstBuffer * outbuf) { GstNeonhttpSrc *src; gint read; src = GST_NEONHTTP_SRC (psrc); /* The caller should know the number of bytes and not read beyond EOS. */ if (G_UNLIKELY (src->eos)) goto eos; read = gst_neonhttp_src_request_dispatch (src, outbuf); if (G_UNLIKELY (read < 0)) goto read_error; GST_LOG_OBJECT (src, "returning %" G_GSIZE_FORMAT " bytes, " "offset %" G_GUINT64_FORMAT, gst_buffer_get_size (outbuf), GST_BUFFER_OFFSET (outbuf)); return GST_FLOW_OK; /* ERRORS */ eos: { GST_DEBUG_OBJECT (src, "EOS reached"); return GST_FLOW_EOS; } read_error: { GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), ("Could not read any bytes (%i, %s)", read, ne_get_error (src->session))); return GST_FLOW_ERROR; } }
static void gst_neonhttp_src_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstNeonhttpSrc *src = GST_NEONHTTP_SRC (object); switch (prop_id) { case PROP_PROXY: { const gchar *proxy; proxy = g_value_get_string (value); if (proxy == NULL) { GST_WARNING ("proxy property cannot be NULL"); goto done; } if (!gst_neonhttp_src_set_proxy (src, proxy)) { GST_WARNING ("badly formated proxy"); goto done; } break; } case PROP_LOCATION: { const gchar *location; location = g_value_get_string (value); if (location == NULL) { GST_WARNING ("location property cannot be NULL"); goto done; } if (!gst_neonhttp_src_set_location (src, location, NULL)) { GST_WARNING ("badly formated location"); goto done; } break; } case PROP_USER_AGENT: g_free (src->user_agent); src->user_agent = g_strdup (g_value_get_string (value)); break; case PROP_COOKIES: if (src->cookies) g_strfreev (src->cookies); src->cookies = (gchar **) g_value_dup_boxed (value); break; case PROP_AUTOMATIC_REDIRECT: src->automatic_redirect = g_value_get_boolean (value); break; case PROP_ACCEPT_SELF_SIGNED: src->accept_self_signed = g_value_get_boolean (value); break; case PROP_CONNECT_TIMEOUT: src->connect_timeout = g_value_get_uint (value); break; case PROP_READ_TIMEOUT: src->read_timeout = g_value_get_uint (value); break; #ifndef GST_DISABLE_GST_DEBUG case PROP_NEON_HTTP_DEBUG: src->neon_http_debug = g_value_get_boolean (value); break; #endif case PROP_IRADIO_MODE: src->iradio_mode = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } done: return; }
/* 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; #ifndef GST_DISABLE_GST_DEBUG if (src->neon_http_debug) ne_debug_init (stderr, NE_DBG_HTTP); #endif ne_oom_callback (oom_callback); res = ne_sock_init (); if (res != 0) goto init_failed; res = gst_neonhttp_src_send_request_and_redirect (src, &src->session, &src->request, 0, src->automatic_redirect); if (res != NE_OK || !src->session) { if (res == HTTP_SOCKET_ERROR) { goto socket_error; } else if (res == HTTP_REQUEST_WRONG_PROXY) { goto wrong_proxy; } else { goto begin_req_failed; } } content_length = ne_get_response_header (src->request, "Content-Length"); if (content_length) src->content_size = g_ascii_strtoull (content_length, NULL, 10); else src->content_size = -1; if (TRUE) { /* Icecast stuff */ const gchar *str_value; GstTagList *tags; gchar *iradio_name; gchar *iradio_url; gchar *iradio_genre; gint icy_metaint; tags = gst_tag_list_new_empty (); str_value = ne_get_response_header (src->request, "icy-metaint"); if (str_value) { if (sscanf (str_value, "%d", &icy_metaint) == 1) { GstCaps *icy_caps; icy_caps = gst_caps_new_simple ("application/x-icy", "metadata-interval", G_TYPE_INT, icy_metaint, NULL); gst_base_src_set_caps (GST_BASE_SRC (src), icy_caps); } } /* FIXME: send tags with name, genre, url */ str_value = ne_get_response_header (src->request, "icy-name"); if (str_value) { iradio_name = gst_neonhttp_src_unicodify (str_value); if (iradio_name) { gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_ORGANIZATION, iradio_name, NULL); g_free (iradio_name); } } str_value = ne_get_response_header (src->request, "icy-genre"); if (str_value) { iradio_genre = gst_neonhttp_src_unicodify (str_value); if (iradio_genre) { gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_GENRE, iradio_genre, NULL); g_free (iradio_genre); } } str_value = ne_get_response_header (src->request, "icy-url"); if (str_value) { iradio_url = gst_neonhttp_src_unicodify (str_value); if (iradio_url) { gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_LOCATION, iradio_url, NULL); g_free (iradio_url); } } if (!gst_tag_list_is_empty (tags)) { GST_DEBUG_OBJECT (src, "pushing tag list %" GST_PTR_FORMAT, tags); gst_pad_push_event (GST_BASE_SRC_PAD (src), gst_event_new_tag (tags)); } else { gst_tag_list_unref (tags); } } return TRUE; /* ERRORS */ init_failed: { GST_ELEMENT_ERROR (src, LIBRARY, INIT, (NULL), ("ne_sock_init() failed: %d", res)); return FALSE; } socket_error: { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), ("HTTP Request failed when opening socket: %d", res)); return FALSE; } wrong_proxy: { GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, (NULL), ("Proxy Server URI is invalid - make sure that either both proxy host " "and port are specified or neither.")); return FALSE; } begin_req_failed: { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), ("Could not begin request: %d", res)); return FALSE; } }
static void gst_neonhttp_src_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstNeonhttpSrc *neonhttpsrc = GST_NEONHTTP_SRC (object); 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 { g_value_set_static_string (value, ""); } break; } case PROP_LOCATION: { gchar *str; 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_static_string (value, ""); } break; } case PROP_USER_AGENT: g_value_set_string (value, neonhttpsrc->user_agent); break; case PROP_COOKIES: g_value_set_boxed (value, neonhttpsrc->cookies); break; case PROP_AUTOMATIC_REDIRECT: g_value_set_boolean (value, neonhttpsrc->automatic_redirect); break; case PROP_ACCEPT_SELF_SIGNED: g_value_set_boolean (value, neonhttpsrc->accept_self_signed); break; case PROP_CONNECT_TIMEOUT: g_value_set_uint (value, neonhttpsrc->connect_timeout); break; case PROP_READ_TIMEOUT: g_value_set_uint (value, neonhttpsrc->read_timeout); break; #ifndef GST_DISABLE_GST_DEBUG case PROP_NEON_HTTP_DEBUG: g_value_set_boolean (value, neonhttpsrc->neon_http_debug); break; #endif case PROP_IRADIO_MODE: g_value_set_boolean (value, neonhttpsrc->iradio_mode); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
/* 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; #ifndef GST_DISABLE_GST_DEBUG if (src->neon_http_debug) ne_debug_init (stderr, NE_DBG_HTTP); #endif ne_oom_callback (oom_callback); res = ne_sock_init (); if (res != 0) goto init_failed; res = gst_neonhttp_src_send_request_and_redirect (src, &src->session, &src->request, 0, src->automatic_redirect); if (res != NE_OK || !src->session) { if (res == HTTP_SOCKET_ERROR) { goto socket_error; } else if (res == HTTP_REQUEST_WRONG_PROXY) { goto wrong_proxy; } else { goto begin_req_failed; } } content_length = ne_get_response_header (src->request, "Content-Length"); if (content_length) src->content_size = g_ascii_strtoull (content_length, NULL, 10); else src->content_size = -1; if (src->iradio_mode) { /* Icecast stuff */ const gchar *str_value; gint gint_value; str_value = ne_get_response_header (src->request, "icy-metaint"); if (str_value) { if (sscanf (str_value, "%d", &gint_value) == 1) { if (src->icy_caps) { gst_caps_unref (src->icy_caps); src->icy_caps = NULL; } src->icy_metaint = gint_value; src->icy_caps = gst_caps_new_simple ("application/x-icy", "metadata-interval", G_TYPE_INT, src->icy_metaint, NULL); } } str_value = ne_get_response_header (src->request, "icy-name"); if (str_value) { if (src->iradio_name) { g_free (src->iradio_name); src->iradio_name = NULL; } src->iradio_name = gst_neonhttp_src_unicodify (str_value); } str_value = ne_get_response_header (src->request, "icy-genre"); if (str_value) { if (src->iradio_genre) { g_free (src->iradio_genre); src->iradio_genre = NULL; } src->iradio_genre = gst_neonhttp_src_unicodify (str_value); } str_value = ne_get_response_header (src->request, "icy-url"); if (str_value) { if (src->iradio_url) { g_free (src->iradio_url); src->iradio_url = NULL; } src->iradio_url = gst_neonhttp_src_unicodify (str_value); } } return TRUE; /* ERRORS */ init_failed: { GST_ELEMENT_ERROR (src, LIBRARY, INIT, (NULL), ("ne_sock_init() failed: %d", res)); return FALSE; } socket_error: { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), ("HTTP Request failed when opening socket: %d", res)); return FALSE; } wrong_proxy: { GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, (NULL), ("Proxy Server URI is invalid - make sure that either both proxy host " "and port are specified or neither.")); return FALSE; } begin_req_failed: { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), ("Could not begin request: %d", res)); return FALSE; } }