souphttpsrc: allow seeks in ready
authorThiago Santos <thiago.sousa.santos@collabora.com>
Wed, 19 Jun 2013 17:06:40 +0000 (14:06 -0300)
committerThiago Santos <thiago.sousa.santos@collabora.com>
Mon, 1 Jul 2013 16:55:13 +0000 (13:55 -0300)
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
ext/soup/gstsouphttpsrc.h

index 0e08ee9..469ebc8 100644 (file)
@@ -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;
 }
index 491b69d..79498b9 100644 (file)
@@ -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 {