diff --git a/ext/smoothstreaming/gstmssdemux.c b/ext/smoothstreaming/gstmssdemux.c index 9d0aece2b8..b66e195143 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 1b72e8de19..317b3cef9b 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 af7419c236..6b7b1f971b 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);