From e9178fa082116d4bf733b184a8b6951112c17900 Mon Sep 17 00:00:00 2001
From: Matthew Waters <matthew@centricular.com>
Date: Thu, 10 Nov 2016 17:18:36 +1100
Subject: [PATCH] smoothstreaming: implement adaptivedemux's
 get_live_seek_range()

Allows seeking through the available fragments that are still available
on the server as specified by the DVRWindowLength attribute in the
manifest.

https://bugzilla.gnome.org/show_bug.cgi?id=774178
---
Upstream-Status: Backport
Signed-off-by: Khem Raj <raj.khem@gmail.com>

 ext/smoothstreaming/gstmssdemux.c    | 13 ++++++
 ext/smoothstreaming/gstmssmanifest.c | 84 ++++++++++++++++++++++++++++++++++++
 ext/smoothstreaming/gstmssmanifest.h |  1 +
 3 files changed, 98 insertions(+)

diff --git a/ext/smoothstreaming/gstmssdemux.c b/ext/smoothstreaming/gstmssdemux.c
index 9d0aece2b..b66e19514 100644
--- a/ext/smoothstreaming/gstmssdemux.c
+++ b/ext/smoothstreaming/gstmssdemux.c
@@ -138,6 +138,8 @@ gst_mss_demux_get_manifest_update_interval (GstAdaptiveDemux * demux);
 static GstFlowReturn
 gst_mss_demux_update_manifest_data (GstAdaptiveDemux * demux,
     GstBuffer * buffer);
+static gboolean gst_mss_demux_get_live_seek_range (GstAdaptiveDemux * demux,
+    gint64 * start, gint64 * stop);
 
 static void
 gst_mss_demux_class_init (GstMssDemuxClass * klass)
@@ -192,6 +194,8 @@ gst_mss_demux_class_init (GstMssDemuxClass * klass)
       gst_mss_demux_stream_update_fragment_info;
   gstadaptivedemux_class->update_manifest_data =
       gst_mss_demux_update_manifest_data;
+  gstadaptivedemux_class->get_live_seek_range =
+      gst_mss_demux_get_live_seek_range;
 
   GST_DEBUG_CATEGORY_INIT (mssdemux_debug, "mssdemux", 0, "mssdemux plugin");
 }
@@ -659,3 +663,12 @@ gst_mss_demux_update_manifest_data (GstAdaptiveDemux * demux,
   gst_mss_manifest_reload_fragments (mssdemux->manifest, buffer);
   return GST_FLOW_OK;
 }
+
+static gboolean
+gst_mss_demux_get_live_seek_range (GstAdaptiveDemux * demux, gint64 * start,
+    gint64 * stop)
+{
+  GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
+
+  return gst_mss_manifest_get_live_seek_range (mssdemux->manifest, start, stop);
+}
diff --git a/ext/smoothstreaming/gstmssmanifest.c b/ext/smoothstreaming/gstmssmanifest.c
index 1b72e8de1..317b3cef9 100644
--- a/ext/smoothstreaming/gstmssmanifest.c
+++ b/ext/smoothstreaming/gstmssmanifest.c
@@ -42,6 +42,7 @@ GST_DEBUG_CATEGORY_EXTERN (mssdemux_debug);
 
 #define MSS_PROP_BITRATE              "Bitrate"
 #define MSS_PROP_DURATION             "d"
+#define MSS_PROP_DVR_WINDOW_LENGTH    "DVRWindowLength"
 #define MSS_PROP_LANGUAGE             "Language"
 #define MSS_PROP_NUMBER               "n"
 #define MSS_PROP_REPETITIONS          "r"
@@ -94,6 +95,7 @@ struct _GstMssManifest
   xmlNodePtr xmlrootnode;
 
   gboolean is_live;
+  gint64 dvr_window;
 
   GString *protection_system_id;
   gchar *protection_data;
@@ -330,6 +332,22 @@ gst_mss_manifest_new (GstBuffer * data)
     xmlFree (live_str);
   }
 
+  /* the entire file is always available for non-live streams */
+  if (!manifest->is_live) {
+    manifest->dvr_window = 0;
+  } else {
+    /* if 0, or non-existent, the length is infinite */
+    gchar *dvr_window_str = (gchar *) xmlGetProp (root,
+        (xmlChar *) MSS_PROP_DVR_WINDOW_LENGTH);
+    if (dvr_window_str) {
+      manifest->dvr_window = g_ascii_strtoull (dvr_window_str, NULL, 10);
+      xmlFree (dvr_window_str);
+      if (manifest->dvr_window <= 0) {
+        manifest->dvr_window = 0;
+      }
+    }
+  }
+
   for (nodeiter = root->children; nodeiter; nodeiter = nodeiter->next) {
     if (nodeiter->type == XML_ELEMENT_NODE
         && (strcmp ((const char *) nodeiter->name, "StreamIndex") == 0)) {
@@ -1406,3 +1424,69 @@ gst_mss_stream_get_lang (GstMssStream * stream)
 {
   return stream->lang;
 }
+
+static GstClockTime
+gst_mss_manifest_get_dvr_window_length_clock_time (GstMssManifest * manifest)
+{
+  gint64 timescale;
+
+  /* the entire file is always available for non-live streams */
+  if (manifest->dvr_window == 0)
+    return GST_CLOCK_TIME_NONE;
+
+  timescale = gst_mss_manifest_get_timescale (manifest);
+  return (GstClockTime) gst_util_uint64_scale_round (manifest->dvr_window,
+      GST_SECOND, timescale);
+}
+
+static gboolean
+gst_mss_stream_get_live_seek_range (GstMssStream * stream, gint64 * start,
+    gint64 * stop)
+{
+  GList *l;
+  GstMssStreamFragment *fragment;
+  guint64 timescale = gst_mss_stream_get_timescale (stream);
+
+  g_return_val_if_fail (stream->active, FALSE);
+
+  /* XXX: assumes all the data in the stream is still available */
+  l = g_list_first (stream->fragments);
+  fragment = (GstMssStreamFragment *) l->data;
+  *start = gst_util_uint64_scale_round (fragment->time, GST_SECOND, timescale);
+
+  l = g_list_last (stream->fragments);
+  fragment = (GstMssStreamFragment *) l->data;
+  *stop = gst_util_uint64_scale_round (fragment->time + fragment->duration *
+      fragment->repetitions, GST_SECOND, timescale);
+
+  return TRUE;
+}
+
+gboolean
+gst_mss_manifest_get_live_seek_range (GstMssManifest * manifest, gint64 * start,
+    gint64 * stop)
+{
+  GSList *iter;
+  gboolean ret = FALSE;
+
+  for (iter = manifest->streams; iter; iter = g_slist_next (iter)) {
+    GstMssStream *stream = iter->data;
+
+    if (stream->active) {
+      /* FIXME: bound this correctly for multiple streams */
+      if (!(ret = gst_mss_stream_get_live_seek_range (stream, start, stop)))
+        break;
+    }
+  }
+
+  if (ret && gst_mss_manifest_is_live (manifest)) {
+    GstClockTime dvr_window =
+        gst_mss_manifest_get_dvr_window_length_clock_time (manifest);
+
+    if (GST_CLOCK_TIME_IS_VALID (dvr_window) && *stop - *start > dvr_window) {
+      *start = *stop - dvr_window;
+    }
+  }
+
+  return ret;
+}
diff --git a/ext/smoothstreaming/gstmssmanifest.h b/ext/smoothstreaming/gstmssmanifest.h
index af7419c23..6b7b1f971 100644
--- a/ext/smoothstreaming/gstmssmanifest.h
+++ b/ext/smoothstreaming/gstmssmanifest.h
@@ -54,6 +54,7 @@ void gst_mss_manifest_reload_fragments (GstMssManifest * manifest, GstBuffer * d
 GstClockTime gst_mss_manifest_get_min_fragment_duration (GstMssManifest * manifest);
 const gchar * gst_mss_manifest_get_protection_system_id (GstMssManifest * manifest);
 const gchar * gst_mss_manifest_get_protection_data (GstMssManifest * manifest);
+gboolean gst_mss_manifest_get_live_seek_range (GstMssManifest * manifest, gint64 * start, gint64 * stop);
 
 GstMssStreamType gst_mss_stream_get_type (GstMssStream *stream);
 GstCaps * gst_mss_stream_get_caps (GstMssStream * stream);
-- 
2.11.0

