souphttpsrc: Remove cookie_jar patch
[platform/upstream/gst-plugins-good.git] / ext / soup / gstsouphttpsrc.c
index 38e7b87..09d3828 100644 (file)
@@ -141,6 +141,12 @@ enum
 #define REDUCE_BLOCKSIZE_LIMIT 0.20
 #define REDUCE_BLOCKSIZE_COUNT 2
 #define REDUCE_BLOCKSIZE_FACTOR 0.5
+#define GROW_TIME_LIMIT (1 * GST_SECOND)
+
+#ifdef TIZEN_FEATURE_SOUP_MODIFICATION
+#define DLNA_OP_TIMED_SEEK  0x02
+#define DLNA_OP_BYTE_SEEK   0x01
+#endif
 
 static void gst_soup_http_src_uri_handler_init (gpointer g_iface,
     gpointer iface_data);
@@ -452,12 +458,27 @@ gst_soup_http_src_reset (GstSoupHTTPSrc * src)
 
   src->reduce_blocksize_count = 0;
   src->increase_blocksize_count = 0;
+  src->last_socket_read_time = 0;
+
+#ifdef TIZEN_FEATURE_SOUP_MODIFICATION
+  if (src->dash_oldest_segment) {
+    g_free (src->dash_oldest_segment);
+    src->dash_oldest_segment = NULL;
+  }
+  if (src->dash_newest_segment) {
+    g_free (src->dash_newest_segment);
+    src->dash_newest_segment = NULL;
+  }
+  src->dlna_opt = 0;
+#endif
 
   g_cancellable_reset (src->cancellable);
+  g_mutex_lock (&src->mutex);
   if (src->input_stream) {
     g_object_unref (src->input_stream);
     src->input_stream = NULL;
   }
+  g_mutex_unlock (&src->mutex);
 
   gst_caps_replace (&src->src_caps, NULL);
   g_free (src->iradio_name);
@@ -501,6 +522,12 @@ gst_soup_http_src_init (GstSoupHTTPSrc * src)
   src->max_retries = DEFAULT_RETRIES;
   src->method = DEFAULT_SOUP_METHOD;
   src->minimum_blocksize = gst_base_src_get_blocksize (GST_BASE_SRC_CAST (src));
+#ifdef TIZEN_FEATURE_SOUP_MODIFICATION
+  src->dash_oldest_segment = NULL;
+  src->dash_newest_segment = NULL;
+  src->received_total = 0;
+  src->dlna_opt = 0;
+#endif
   proxy = g_getenv ("http_proxy");
   if (!gst_soup_http_src_set_proxy (src, proxy)) {
     GST_WARNING_OBJECT (src,
@@ -711,7 +738,6 @@ gst_soup_http_src_get_property (GObject * object, guint prop_id,
         g_value_set_static_string (value, "");
       else {
         char *proxy = soup_uri_to_string (src->proxy, FALSE);
-
         g_value_set_string (value, proxy);
         g_free (proxy);
       }
@@ -804,7 +830,14 @@ gst_soup_http_src_add_range_header (GstSoupHTTPSrc * src, guint64 offset,
   gint rc;
 
   soup_message_headers_remove (src->msg->request_headers, "Range");
-  if (offset || stop_offset != -1) {
+
+/* This changes are needed to enable Seekable Contents from server.
+   We have observed that , for few specific networks ( VODAFONE ) , without theabove headers ,
+   Youtube is sending non-seekable contents to the Client. */
+#ifndef TIZEN_FEATURE_SOUP_MODIFICATION
+  if (offset || stop_offset != -1)
+#endif
+  {
     if (stop_offset != -1) {
       g_assert (offset != stop_offset);
 
@@ -1110,6 +1143,29 @@ insert_http_header (const gchar * name, const gchar * value, gpointer user_data)
   }
 }
 
+#ifdef TIZEN_FEATURE_SOUP_MODIFICATION
+static void
+gst_soup_http_src_headers_foreach (const gchar * name, const gchar * val,
+    gpointer src)
+{
+  GST_INFO_OBJECT (src, " %s: %s", name, val);
+
+  if (g_ascii_strcasecmp (name, "Dash-Oldest-Segment") == 0) {
+    if (val) {
+      GstSoupHTTPSrc * tmp = src;
+      tmp->dash_oldest_segment = g_strdup (val);
+      GST_INFO_OBJECT (src, "Dash-Oldest-Segment set as %s ", tmp->dash_oldest_segment);
+    }
+  } else if (g_ascii_strcasecmp (name, "Dash-Newest-Segment") == 0) {
+    if (val) {
+      GstSoupHTTPSrc * tmp = src;
+      tmp->dash_newest_segment = g_strdup (val);
+      GST_INFO_OBJECT (src, "Dash-Newest-Segment set as %s ", tmp->dash_newest_segment);
+    }
+  }
+}
+#endif
+
 static GstFlowReturn
 gst_soup_http_src_got_headers (GstSoupHTTPSrc * src, SoupMessage * msg)
 {
@@ -1121,9 +1177,15 @@ gst_soup_http_src_got_headers (GstSoupHTTPSrc * src, SoupMessage * msg)
   GstEvent *http_headers_event;
   GstStructure *http_headers, *headers;
   const gchar *accept_ranges;
+#ifdef TIZEN_FEATURE_SOUP_MODIFICATION
+  gint64 start = 0, stop = 0, total = 0;
+#endif
 
-  GST_INFO_OBJECT (src, "got headers");
-
+  GST_INFO_OBJECT (src, "got headers : %d", msg->status_code);
+#ifdef TIZEN_FEATURE_SOUP_MODIFICATION
+  soup_message_headers_foreach (msg->response_headers,
+      gst_soup_http_src_headers_foreach, src);
+#endif
   if (msg->status_code == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED &&
       src->proxy_id && src->proxy_pw) {
     /* wait for authenticate callback */
@@ -1167,11 +1229,68 @@ gst_soup_http_src_got_headers (GstSoupHTTPSrc * src, SoupMessage * msg)
   gst_event_replace (&src->http_headers_event, http_headers_event);
   gst_event_unref (http_headers_event);
 
+#ifdef TIZEN_FEATURE_SOUP_MODIFICATION
+  /* Parse DLNA OP CODE */
+  if ((value = soup_message_headers_get_one
+        (msg->response_headers, "contentFeatures.dlna.org")) != NULL) {
+    gchar **token = NULL;
+    gchar **ptr = NULL;
+
+    GST_DEBUG_OBJECT (src, "DLNA server response");
+
+    token = g_strsplit (value, ";", 0);
+    for (ptr = token ; *ptr ; ptr++) {
+      gchar *tmp = NULL;
+      gchar *op_code = NULL;
+
+      if (!strlen (*ptr))
+        continue;
+
+      tmp = g_ascii_strup (*ptr, strlen (*ptr));
+      if (!strstr (tmp, "DLNA.ORG_OP")) {
+        g_free (tmp);
+        continue;
+      }
+
+      g_free (tmp);
+
+      op_code = strchr (*ptr, '=');
+      if (op_code) {
+        op_code++;
+
+        src->dlna_opt = (atoi (op_code) / 10 << 1) | (atoi (op_code) % 10);
+        GST_DEBUG_OBJECT (src, "dlna op code: %s (0x%X)", op_code, src->dlna_opt);
+        break;
+      }
+    }
+    g_strfreev (token);
+  }
+#endif
+
   /* Parse Content-Length. */
   if (soup_message_headers_get_encoding (msg->response_headers) ==
       SOUP_ENCODING_CONTENT_LENGTH) {
-    newsize = src->request_position +
-        soup_message_headers_get_content_length (msg->response_headers);
+#ifdef TIZEN_FEATURE_SOUP_MODIFICATION
+      if (msg->status_code == SOUP_STATUS_PARTIAL_CONTENT) {
+        newsize = src->request_position +
+            soup_message_headers_get_content_length (msg->response_headers);
+      } else {
+        if (soup_message_headers_get_content_range(msg->response_headers, &start, &stop, &total) && (total > 0)) {
+          GST_DEBUG_OBJECT (src, "get range header : %" G_GINT64_FORMAT
+                                  "~%" G_GINT64_FORMAT"/%"G_GINT64_FORMAT, start, stop, total);
+          newsize = (guint64)total;
+        } else {
+          if ((src->have_size) && (src->content_size <= src->request_position)) {
+            newsize = src->content_size;
+          } else {
+            newsize = soup_message_headers_get_content_length (msg->response_headers);
+          }
+        }
+      }
+#else
+      newsize = src->request_position +
+          soup_message_headers_get_content_length (msg->response_headers);
+#endif
     if (!src->have_size || (src->content_size != newsize)) {
       src->content_size = newsize;
       src->have_size = TRUE;
@@ -1194,6 +1313,21 @@ gst_soup_http_src_got_headers (GstSoupHTTPSrc * src, SoupMessage * msg)
     if (g_ascii_strcasecmp (accept_ranges, "none") == 0)
       src->seekable = FALSE;
   }
+#ifdef TIZEN_FEATURE_SOUP_MODIFICATION
+  else if (src->dlna_opt & DLNA_OP_BYTE_SEEK) {
+    if (src->have_size) {
+      GST_DEBUG_OBJECT (src, "DLNA server is seekable");
+      src->seekable = TRUE;
+    }
+  }
+  /* The Range request header is always included.
+   * @ref gst_soup_http_src_add_range_header() */
+  else if ((msg->status_code == SOUP_STATUS_OK) &&
+    (soup_message_headers_get_content_range (msg->response_headers, &start, &stop, &total) == FALSE)) {
+    GST_DEBUG_OBJECT (src, "there is no accept range header");
+    src->seekable = FALSE;
+  }
+#endif
 
   /* Icecast stuff */
   tag_list = gst_tag_list_new_empty ();
@@ -1410,12 +1544,22 @@ gst_soup_http_src_parse_status (SoupMessage * msg, GstSoupHTTPSrc * src)
     /* when content_size is unknown and we have just finished receiving
      * a body message, requests that go beyond the content limits will result
      * in an error. Here we convert those to EOS */
+#ifdef TIZEN_FEATURE_SOUP_MODIFICATION
+    if (msg->status_code == SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE &&
+        ((src->have_body && !src->have_size) ||
+         (src->have_size && src->request_position >= src->content_size))) {
+      GST_DEBUG_OBJECT (src, "Requested range out of limits and received full "
+          "body, returning EOS");
+      return GST_FLOW_EOS;
+    }
+#else
     if (msg->status_code == SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE &&
         src->have_body && !src->have_size) {
       GST_DEBUG_OBJECT (src, "Requested range out of limits and received full "
           "body, returning EOS");
       return GST_FLOW_EOS;
     }
+#endif
 
     /* FIXME: reason_phrase is not translated and not suitable for user
      * error dialog according to libsoup documentation.
@@ -1491,6 +1635,14 @@ gst_soup_http_src_build_message (GstSoupHTTPSrc * src, const gchar * method)
     soup_message_headers_append (src->msg->request_headers, "icy-metadata",
         "1");
   }
+
+#ifdef TIZEN_FEATURE_SOUP_MODIFICATION
+  /* This changes are needed to enable Seekable Contents from server.
+   We have observed that , for few specific networks ( VODAFONE ) , without theabove headers ,
+   Youtube is sending non-seekable contents to the Client. */
+  soup_message_headers_append (src->msg->request_headers, "Accept-Ranges","bytes");
+#endif
+
   if (src->cookies) {
     gchar **cookie;
 
@@ -1516,9 +1668,15 @@ gst_soup_http_src_build_message (GstSoupHTTPSrc * src, const gchar * method)
 
   gst_soup_http_src_add_extra_headers (src);
 
+#ifdef TIZEN_FEATURE_SOUP_MODIFICATION
+  soup_message_headers_foreach (src->msg->request_headers,
+      gst_soup_http_src_headers_foreach, src);
+#endif
+
   return TRUE;
 }
 
+/* Lock taken */
 static GstFlowReturn
 gst_soup_http_src_send_message (GstSoupHTTPSrc * src)
 {
@@ -1526,6 +1684,7 @@ gst_soup_http_src_send_message (GstSoupHTTPSrc * src)
   GError *error = NULL;
 
   g_return_val_if_fail (src->msg != NULL, GST_FLOW_ERROR);
+  g_assert (src->input_stream == NULL);
 
   src->input_stream =
       soup_session_send (src->session, src->msg, src->cancellable, &error);
@@ -1580,7 +1739,14 @@ gst_soup_http_src_do_request (GstSoupHTTPSrc * src, const gchar * method)
   if (src->msg && src->request_position > 0) {
     gst_soup_http_src_add_range_header (src, src->request_position,
         src->stop_position);
-  }
+  } else if (src->msg && src->request_position == 0)
+    soup_message_headers_remove (src->msg->request_headers, "Range");
+
+  /* add_range_header() has the side effect of setting read_position to
+   * the requested position. This *needs* to be set regardless of having
+   * a message or not. Failure to do so would result in calculation being
+   * done with stale/wrong read position */
+  src->read_position = src->request_position;
 
   if (!src->msg) {
     if (!gst_soup_http_src_build_message (src, method)) {
@@ -1596,8 +1762,14 @@ gst_soup_http_src_do_request (GstSoupHTTPSrc * src, const gchar * method)
   ret = gst_soup_http_src_send_message (src);
 
   /* Check if Range header was respected. */
+#ifdef TIZEN_FEATURE_SOUP_MODIFICATION
+  if (ret == GST_FLOW_OK && src->request_position > 0 &&
+      (src->msg->status_code != SOUP_STATUS_PARTIAL_CONTENT) &&
+         (src->request_position < src->content_size)) {
+#else
   if (ret == GST_FLOW_OK && src->request_position > 0 &&
       src->msg->status_code != SOUP_STATUS_PARTIAL_CONTENT) {
+#endif
     src->seekable = FALSE;
     GST_ELEMENT_ERROR_WITH_DETAILS (src, RESOURCE, SEEK,
         (_("Server does not support seeking.")),
@@ -1623,10 +1795,15 @@ gst_soup_http_src_check_update_blocksize (GstSoupHTTPSrc * src,
 {
   guint blocksize = gst_base_src_get_blocksize (GST_BASE_SRC_CAST (src));
 
-  GST_LOG_OBJECT (src, "Checking to update blocksize. Read:%" G_GINT64_FORMAT
-      " blocksize:%u", bytes_read, blocksize);
+  gint64 time_since_last_read =
+      g_get_monotonic_time () * GST_USECOND - src->last_socket_read_time;
 
-  if (bytes_read >= blocksize * GROW_BLOCKSIZE_LIMIT) {
+  GST_LOG_OBJECT (src, "Checking to update blocksize. Read: %" G_GINT64_FORMAT
+      " bytes, blocksize: %u bytes, time since last read: %" GST_TIME_FORMAT,
+      bytes_read, blocksize, GST_TIME_ARGS (time_since_last_read));
+
+  if (bytes_read >= blocksize * GROW_BLOCKSIZE_LIMIT
+      && time_since_last_read <= GROW_TIME_LIMIT) {
     src->reduce_blocksize_count = 0;
     src->increase_blocksize_count++;
 
@@ -1636,7 +1813,8 @@ gst_soup_http_src_check_update_blocksize (GstSoupHTTPSrc * src,
       gst_base_src_set_blocksize (GST_BASE_SRC_CAST (src), blocksize);
       src->increase_blocksize_count = 0;
     }
-  } else if (bytes_read < blocksize * REDUCE_BLOCKSIZE_LIMIT) {
+  } else if (bytes_read < blocksize * REDUCE_BLOCKSIZE_LIMIT
+      || time_since_last_read > GROW_TIME_LIMIT) {
     src->reduce_blocksize_count++;
     src->increase_blocksize_count = 0;
 
@@ -1719,12 +1897,17 @@ gst_soup_http_src_read_buffer (GstSoupHTTPSrc * src, GstBuffer ** outbuf)
     GST_BUFFER_OFFSET (*outbuf) = bsrc->segment.position;
     ret = GST_FLOW_OK;
     gst_soup_http_src_update_position (src, read_bytes);
+#ifdef TIZEN_FEATURE_SOUP_MODIFICATION
+    src->received_total += read_bytes;
+#endif
 
     /* Got some data, reset retry counter */
     src->retry_count = 0;
 
     gst_soup_http_src_check_update_blocksize (src, read_bytes);
 
+    src->last_socket_read_time = g_get_monotonic_time () * GST_USECOND;
+
     /* If we're at the end of a range request, read again to let libsoup
      * finalize the request. This allows to reuse the connection again later,
      * otherwise we would have to cancel the message and close the connection
@@ -1828,6 +2011,13 @@ done:
       goto retry;
     }
   }
+
+  if (ret == GST_FLOW_FLUSHING) {
+    g_mutex_lock (&src->mutex);
+    src->retry_count = 0;
+    g_mutex_unlock (&src->mutex);
+  }
+
   return ret;
 }
 
@@ -1838,6 +2028,16 @@ gst_soup_http_src_start (GstBaseSrc * bsrc)
 
   GST_DEBUG_OBJECT (src, "start(\"%s\")", src->location);
 
+#ifdef TIZEN_FEATURE_SOUP_MODIFICATION
+  if (src->dash_oldest_segment) {
+    g_free (src->dash_oldest_segment);
+    src->dash_oldest_segment = NULL;
+  }
+  if (src->dash_newest_segment) {
+    g_free (src->dash_newest_segment);
+    src->dash_newest_segment = NULL;
+  }
+#endif
   return gst_soup_http_src_session_open (src);
 }
 
@@ -1866,6 +2066,13 @@ gst_soup_http_src_change_state (GstElement * element, GstStateChange transition)
   src = GST_SOUP_HTTP_SRC (element);
 
   switch (transition) {
+#ifdef TIZEN_FEATURE_SOUP_MODIFICATION
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      GST_WARNING_OBJECT (src, "Last read pos"
+            ": %" G_GINT64_FORMAT ", received total : %" G_GINT64_FORMAT,
+            src->read_position, src->received_total);
+      break;
+#endif
     case GST_STATE_CHANGE_READY_TO_NULL:
       gst_soup_http_src_session_close (src);
       break;
@@ -2055,6 +2262,13 @@ gst_soup_http_src_query (GstBaseSrc * bsrc, GstQuery * query)
     case GST_QUERY_SCHEDULING:
       gst_query_parse_scheduling (query, &flags, &minsize, &maxsize, &align);
       flags |= GST_SCHEDULING_FLAG_BANDWIDTH_LIMITED;
+
+#ifdef TIZEN_FEATURE_SOUP_MODIFICATION
+      if (gst_soup_http_src_is_seekable(bsrc)) {
+          GST_DEBUG_OBJECT (src, "set seekable flag");
+          flags |= GST_SCHEDULING_FLAG_SEEKABLE;
+      }
+#endif
       gst_query_set_scheduling (query, flags, minsize, maxsize, align);
       break;
     default:
@@ -2120,7 +2334,7 @@ gst_soup_http_src_set_proxy (GstSoupHTTPSrc * src, const gchar * uri)
   return (src->proxy != NULL);
 }
 
-static guint
+static GstURIType
 gst_soup_http_src_uri_get_type (GType type)
 {
   return GST_URI_SRC;