From 5bc5f4a0f6d9b82f72b39926d8ad81600dac4604 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Wed, 19 Jun 2013 14:06:40 -0300 Subject: [PATCH] souphttpsrc: allow seeks in ready On is_seekable, check if the server's headers have already been received. If not, do a HEAD request to get them before responding to basesrc. https://bugzilla.gnome.org/show_bug.cgi?id=702206 --- ext/soup/gstsouphttpsrc.c | 165 ++++++++++++++++++++++++++++++++-------------- ext/soup/gstsouphttpsrc.h | 4 ++ 2 files changed, 119 insertions(+), 50 deletions(-) diff --git a/ext/soup/gstsouphttpsrc.c b/ext/soup/gstsouphttpsrc.c index 0e08ee9..469ebc8 100644 --- a/ext/soup/gstsouphttpsrc.c +++ b/ext/soup/gstsouphttpsrc.c @@ -136,13 +136,15 @@ static gboolean gst_soup_http_src_set_location (GstSoupHTTPSrc * src, static gboolean gst_soup_http_src_set_proxy (GstSoupHTTPSrc * src, const gchar * uri); static char *gst_soup_http_src_unicodify (const char *str); -static gboolean gst_soup_http_src_build_message (GstSoupHTTPSrc * src); +static gboolean gst_soup_http_src_build_message (GstSoupHTTPSrc * src, + const gchar * method); static void gst_soup_http_src_cancel_message (GstSoupHTTPSrc * src); static void gst_soup_http_src_queue_message (GstSoupHTTPSrc * src); static gboolean gst_soup_http_src_add_range_header (GstSoupHTTPSrc * src, guint64 offset); static void gst_soup_http_src_session_unpause_message (GstSoupHTTPSrc * src); static void gst_soup_http_src_session_pause_message (GstSoupHTTPSrc * src); +static gboolean gst_soup_http_src_session_open (GstSoupHTTPSrc * src); static void gst_soup_http_src_session_close (GstSoupHTTPSrc * src); static void gst_soup_http_src_parse_status (SoupMessage * msg, GstSoupHTTPSrc * src); @@ -273,6 +275,7 @@ gst_soup_http_src_reset (GstSoupHTTPSrc * src) src->interrupted = FALSE; src->retry = FALSE; src->have_size = FALSE; + src->got_headers = FALSE; src->seekable = FALSE; src->read_position = 0; src->request_position = 0; @@ -292,6 +295,8 @@ gst_soup_http_src_init (GstSoupHTTPSrc * src) { const gchar *proxy; + g_mutex_init (&src->mutex); + g_cond_init (&src->request_finished_cond); src->location = NULL; src->automatic_redirect = TRUE; src->user_agent = g_strdup (DEFAULT_USER_AGENT); @@ -322,6 +327,8 @@ gst_soup_http_src_finalize (GObject * gobject) GST_DEBUG_OBJECT (src, "finalize"); + g_mutex_clear (&src->mutex); + g_cond_clear (&src->request_finished_cond); g_free (src->location); g_free (src->user_agent); if (src->proxy != NULL) { @@ -627,6 +634,57 @@ gst_soup_http_src_session_pause_message (GstSoupHTTPSrc * src) soup_session_pause_message (src->session, src->msg); } +static gboolean +gst_soup_http_src_session_open (GstSoupHTTPSrc * src) +{ + if (src->session) { + GST_DEBUG_OBJECT (src, "Session is already open"); + return TRUE; + } + + if (!src->location) { + GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (_("No URL set.")), + ("Missing location property")); + return FALSE; + } + + src->context = g_main_context_new (); + + src->loop = g_main_loop_new (src->context, TRUE); + if (!src->loop) { + GST_ELEMENT_ERROR (src, LIBRARY, INIT, + (NULL), ("Failed to start GMainLoop")); + g_main_context_unref (src->context); + return FALSE; + } + + GST_DEBUG_OBJECT (src, "Creating session"); + if (src->proxy == NULL) { + src->session = + soup_session_async_new_with_options (SOUP_SESSION_ASYNC_CONTEXT, + src->context, SOUP_SESSION_USER_AGENT, src->user_agent, + SOUP_SESSION_TIMEOUT, src->timeout, + SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_PROXY_RESOLVER_DEFAULT, + NULL); + } else { + src->session = + soup_session_async_new_with_options (SOUP_SESSION_ASYNC_CONTEXT, + src->context, SOUP_SESSION_PROXY_URI, src->proxy, + SOUP_SESSION_TIMEOUT, src->timeout, + SOUP_SESSION_USER_AGENT, src->user_agent, NULL); + } + + if (!src->session) { + GST_ELEMENT_ERROR (src, LIBRARY, INIT, + (NULL), ("Failed to create async session")); + return FALSE; + } + + g_signal_connect (src->session, "authenticate", + G_CALLBACK (gst_soup_http_src_authenticate_cb), src); + return TRUE; +} + static void gst_soup_http_src_session_close (GstSoupHTTPSrc * src) { @@ -687,6 +745,7 @@ gst_soup_http_src_got_headers_cb (SoupMessage * msg, GstSoupHTTPSrc * src) return; src->session_io_status = GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_RUNNING; + src->got_headers = TRUE; /* Parse Content-Length. */ if (soup_message_headers_get_encoding (msg->response_headers) == @@ -825,6 +884,7 @@ gst_soup_http_src_got_headers_cb (SoupMessage * msg, GstSoupHTTPSrc * src) if (src->loop) g_main_loop_quit (src->loop); } + g_cond_signal (&src->request_finished_cond); } /* Have body. Signal EOS. */ @@ -876,6 +936,7 @@ gst_soup_http_src_finished_cb (SoupMessage * msg, GstSoupHTTPSrc * src) } if (src->loop) g_main_loop_quit (src->loop); + g_cond_signal (&src->request_finished_cond); } /* Buffer lifecycle management. @@ -1092,9 +1153,9 @@ gst_soup_http_src_parse_status (SoupMessage * msg, GstSoupHTTPSrc * src) } static gboolean -gst_soup_http_src_build_message (GstSoupHTTPSrc * src) +gst_soup_http_src_build_message (GstSoupHTTPSrc * src, const gchar * method) { - src->msg = soup_message_new (SOUP_METHOD_GET, src->location); + src->msg = soup_message_new (method, src->location); if (!src->msg) { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, ("Error parsing URL."), ("URL: %s", src->location)); @@ -1141,12 +1202,10 @@ gst_soup_http_src_build_message (GstSoupHTTPSrc * src) } static GstFlowReturn -gst_soup_http_src_create (GstPushSrc * psrc, GstBuffer ** outbuf) +gst_soup_http_src_do_request (GstSoupHTTPSrc * src, const gchar * method, + GstBuffer ** outbuf) { - GstSoupHTTPSrc *src; - - src = GST_SOUP_HTTP_SRC (psrc); - + GST_LOG_OBJECT (src, "Running request for method: %s", method); if (src->msg && (src->request_position != src->read_position)) { if (src->session_io_status == GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_IDLE) { gst_soup_http_src_add_range_header (src, src->request_position); @@ -1158,8 +1217,9 @@ gst_soup_http_src_create (GstPushSrc * psrc, GstBuffer ** outbuf) } } if (!src->msg) - if (!gst_soup_http_src_build_message (src)) + if (!gst_soup_http_src_build_message (src, method)) { return GST_FLOW_ERROR; + } src->ret = GST_FLOW_CUSTOM_ERROR; src->outbuf = outbuf; @@ -1170,8 +1230,9 @@ gst_soup_http_src_create (GstPushSrc * psrc, GstBuffer ** outbuf) } if (src->retry) { GST_DEBUG_OBJECT (src, "Reconnecting"); - if (!gst_soup_http_src_build_message (src)) + if (!gst_soup_http_src_build_message (src, method)) { return GST_FLOW_ERROR; + } src->retry = FALSE; continue; } @@ -1201,9 +1262,24 @@ gst_soup_http_src_create (GstPushSrc * psrc, GstBuffer ** outbuf) if (src->ret == GST_FLOW_CUSTOM_ERROR) src->ret = GST_FLOW_EOS; + g_cond_signal (&src->request_finished_cond); return src->ret; } +static GstFlowReturn +gst_soup_http_src_create (GstPushSrc * psrc, GstBuffer ** outbuf) +{ + GstSoupHTTPSrc *src; + GstFlowReturn ret; + + src = GST_SOUP_HTTP_SRC (psrc); + + g_mutex_lock (&src->mutex); + ret = gst_soup_http_src_do_request (src, SOUP_METHOD_GET, outbuf); + g_mutex_unlock (&src->mutex); + return ret; +} + static gboolean gst_soup_http_src_start (GstBaseSrc * bsrc) { @@ -1211,46 +1287,7 @@ gst_soup_http_src_start (GstBaseSrc * bsrc) GST_DEBUG_OBJECT (src, "start(\"%s\")", src->location); - if (!src->location) { - GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (_("No URL set.")), - ("Missing location property")); - return FALSE; - } - - src->context = g_main_context_new (); - - src->loop = g_main_loop_new (src->context, TRUE); - if (!src->loop) { - GST_ELEMENT_ERROR (src, LIBRARY, INIT, - (NULL), ("Failed to start GMainLoop")); - g_main_context_unref (src->context); - return FALSE; - } - - if (src->proxy == NULL) { - src->session = - soup_session_async_new_with_options (SOUP_SESSION_ASYNC_CONTEXT, - src->context, SOUP_SESSION_USER_AGENT, src->user_agent, - SOUP_SESSION_TIMEOUT, src->timeout, - SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_PROXY_RESOLVER_DEFAULT, - NULL); - } else { - src->session = - soup_session_async_new_with_options (SOUP_SESSION_ASYNC_CONTEXT, - src->context, SOUP_SESSION_PROXY_URI, src->proxy, - SOUP_SESSION_TIMEOUT, src->timeout, - SOUP_SESSION_USER_AGENT, src->user_agent, NULL); - } - - if (!src->session) { - GST_ELEMENT_ERROR (src, LIBRARY, INIT, - (NULL), ("Failed to create async session")); - return FALSE; - } - - g_signal_connect (src->session, "authenticate", - G_CALLBACK (gst_soup_http_src_authenticate_cb), src); - return TRUE; + return gst_soup_http_src_session_open (src); } static gboolean @@ -1288,6 +1325,7 @@ gst_soup_http_src_unlock (GstBaseSrc * bsrc) src->interrupted = TRUE; if (src->loop) g_main_loop_quit (src->loop); + g_cond_signal (&src->request_finished_cond); return TRUE; } @@ -1325,6 +1363,33 @@ static gboolean gst_soup_http_src_is_seekable (GstBaseSrc * bsrc) { GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (bsrc); + GstFlowReturn ret = GST_FLOW_OK; + + /* Special case to check if the server allows range requests + * before really starting to get data in the buffer creation + * loops. + */ + if (!src->got_headers && GST_STATE (src) != GST_STATE_NULL) { + g_mutex_lock (&src->mutex); + while (!src->got_headers && !src->interrupted && ret == GST_FLOW_OK) { + if ((src->msg && src->msg->method != SOUP_METHOD_HEAD) && + src->session_io_status != GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_IDLE) { + /* wait for the current request to finish */ + g_cond_wait (&src->request_finished_cond, &src->mutex); + } else { + if (gst_soup_http_src_session_open (src)) { + ret = gst_soup_http_src_do_request (src, SOUP_METHOD_HEAD, NULL); + } + } + } + if (src->ret == GST_FLOW_EOS) { + /* A HEAD request shouldn't lead to EOS */ + src->ret = GST_FLOW_OK; + } + /* resets status to idle */ + gst_soup_http_src_cancel_message (src); + g_mutex_unlock (&src->mutex); + } return src->seekable; } diff --git a/ext/soup/gstsouphttpsrc.h b/ext/soup/gstsouphttpsrc.h index 491b69d..79498b9 100644 --- a/ext/soup/gstsouphttpsrc.h +++ b/ext/soup/gstsouphttpsrc.h @@ -68,6 +68,7 @@ struct _GstSoupHTTPSrc { gboolean interrupted; /* Signal unlock(). */ gboolean retry; /* Should attempt to reconnect. */ + gboolean got_headers; /* Already received headers from the server */ gboolean have_size; /* Received and parsed Content-Length header. */ guint64 content_size; /* Value of Content-Length header. */ @@ -86,6 +87,9 @@ struct _GstSoupHTTPSrc { GstStructure *extra_headers; guint timeout; + + GMutex mutex; + GCond request_finished_cond; }; struct _GstSoupHTTPSrcClass { -- 2.7.4