#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);
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);
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,
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);
}
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);
}
}
+#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)
{
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 */
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;
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 ();
/* 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.
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;
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)
{
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);
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)) {
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.")),
{
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++;
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;
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
goto retry;
}
}
+
+ if (ret == GST_FLOW_FLUSHING) {
+ g_mutex_lock (&src->mutex);
+ src->retry_count = 0;
+ g_mutex_unlock (&src->mutex);
+ }
+
return ret;
}
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);
}
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;
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:
return (src->proxy != NULL);
}
-static guint
+static GstURIType
gst_soup_http_src_uri_get_type (GType type)
{
return GST_URI_SRC;