summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKhem Raj <raj.khem@gmail.com>2016-12-22 00:14:14 -0800
committerRichard Purdie <richard.purdie@linuxfoundation.org>2017-01-19 22:45:42 +0000
commit23c37ffb25a41cd8b30a3fb56731fd6753478092 (patch)
treeec74555cd6d8f3723fae690bea37c38b59f7c129
parent1f2a3cdadac1560b0e03a7be25f452ad48c27ddb (diff)
downloadopenembedded-core-contrib-23c37ffb25a41cd8b30a3fb56731fd6753478092.tar.gz
openembedded-core-contrib-23c37ffb25a41cd8b30a3fb56731fd6753478092.tar.bz2
openembedded-core-contrib-23c37ffb25a41cd8b30a3fb56731fd6753478092.zip
gstreamer1.0-plugins-bad: Backport patches for improving live playback
Signed-off-by: Khem Raj <raj.khem@gmail.com> Signed-off-by: Ross Burton <ross.burton@intel.com>
-rw-r--r--meta/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0001-mssdemux-improved-live-playback-support.patch929
-rw-r--r--meta/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0001-smoothstreaming-implement-adaptivedemux-s-get_live_s.patch183
-rw-r--r--meta/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0001-smoothstreaming-use-the-duration-from-the-list-of-fr.patch62
-rw-r--r--meta/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.10.2.bb3
4 files changed, 1177 insertions, 0 deletions
diff --git a/meta/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0001-mssdemux-improved-live-playback-support.patch b/meta/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0001-mssdemux-improved-live-playback-support.patch
new file mode 100644
index 0000000000..4832c18e78
--- /dev/null
+++ b/meta/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0001-mssdemux-improved-live-playback-support.patch
@@ -0,0 +1,929 @@
+From 73721ad4e9e2d32e1c8b6a3b4aaa98401530e58a Mon Sep 17 00:00:00 2001
+From: Philippe Normand <philn@igalia.com>
+Date: Tue, 29 Nov 2016 14:43:41 +0100
+Subject: [PATCH] mssdemux: improved live playback support
+
+When a MSS server hosts a live stream the fragments listed in the
+manifest usually don't have accurate timestamps and duration, except
+for the first fragment, which additionally stores timing information
+for the few upcoming fragments. In this scenario it is useless to
+periodically fetch and update the manifest and the fragments list can
+be incrementally built by parsing the first/current fragment.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=755036
+---
+Upstream-Status: Backport
+Signed-off-by: Khem Raj <raj.khem@gmail.com>
+
+ ext/smoothstreaming/Makefile.am | 2 +
+ ext/smoothstreaming/gstmssdemux.c | 60 ++++++
+ ext/smoothstreaming/gstmssfragmentparser.c | 266 ++++++++++++++++++++++++++
+ ext/smoothstreaming/gstmssfragmentparser.h | 84 ++++++++
+ ext/smoothstreaming/gstmssmanifest.c | 158 ++++++++++++++-
+ ext/smoothstreaming/gstmssmanifest.h | 7 +
+ gst-libs/gst/adaptivedemux/gstadaptivedemux.c | 27 ++-
+ gst-libs/gst/adaptivedemux/gstadaptivedemux.h | 14 ++
+ 8 files changed, 606 insertions(+), 12 deletions(-)
+ create mode 100644 ext/smoothstreaming/gstmssfragmentparser.c
+ create mode 100644 ext/smoothstreaming/gstmssfragmentparser.h
+
+diff --git a/ext/smoothstreaming/Makefile.am b/ext/smoothstreaming/Makefile.am
+index 4faf9df9f..a5e1ad6ae 100644
+--- a/ext/smoothstreaming/Makefile.am
++++ b/ext/smoothstreaming/Makefile.am
+@@ -13,8 +13,10 @@ libgstsmoothstreaming_la_LIBADD = \
+ libgstsmoothstreaming_la_LDFLAGS = ${GST_PLUGIN_LDFLAGS}
+ libgstsmoothstreaming_la_SOURCES = gstsmoothstreaming-plugin.c \
+ gstmssdemux.c \
++ gstmssfragmentparser.c \
+ gstmssmanifest.c
+ libgstsmoothstreaming_la_LIBTOOLFLAGS = --tag=disable-static
+
+ noinst_HEADERS = gstmssdemux.h \
++ gstmssfragmentparser.h \
+ gstmssmanifest.h
+diff --git a/ext/smoothstreaming/gstmssdemux.c b/ext/smoothstreaming/gstmssdemux.c
+index 12fb40497..120d9c22b 100644
+--- a/ext/smoothstreaming/gstmssdemux.c
++++ b/ext/smoothstreaming/gstmssdemux.c
+@@ -135,11 +135,18 @@ gst_mss_demux_stream_update_fragment_info (GstAdaptiveDemuxStream * stream);
+ static gboolean gst_mss_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek);
+ static gint64
+ gst_mss_demux_get_manifest_update_interval (GstAdaptiveDemux * demux);
++static gint64
++gst_mss_demux_stream_get_fragment_waiting_time (GstAdaptiveDemuxStream *
++ stream);
+ 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 GstFlowReturn gst_mss_demux_data_received (GstAdaptiveDemux * demux,
++ GstAdaptiveDemuxStream * stream, GstBuffer * buffer);
++static gboolean
++gst_mss_demux_requires_periodical_playlist_update (GstAdaptiveDemux * demux);
+
+ static void
+ gst_mss_demux_class_init (GstMssDemuxClass * klass)
+@@ -192,10 +199,15 @@ gst_mss_demux_class_init (GstMssDemuxClass * klass)
+ gst_mss_demux_stream_select_bitrate;
+ gstadaptivedemux_class->stream_update_fragment_info =
+ gst_mss_demux_stream_update_fragment_info;
++ gstadaptivedemux_class->stream_get_fragment_waiting_time =
++ gst_mss_demux_stream_get_fragment_waiting_time;
+ gstadaptivedemux_class->update_manifest_data =
+ gst_mss_demux_update_manifest_data;
+ gstadaptivedemux_class->get_live_seek_range =
+ gst_mss_demux_get_live_seek_range;
++ gstadaptivedemux_class->data_received = gst_mss_demux_data_received;
++ gstadaptivedemux_class->requires_periodical_playlist_update =
++ gst_mss_demux_requires_periodical_playlist_update;
+
+ GST_DEBUG_CATEGORY_INIT (mssdemux_debug, "mssdemux", 0, "mssdemux plugin");
+ }
+@@ -650,6 +662,13 @@ gst_mss_demux_get_manifest_update_interval (GstAdaptiveDemux * demux)
+ return interval;
+ }
+
++static gint64
++gst_mss_demux_stream_get_fragment_waiting_time (GstAdaptiveDemuxStream * stream)
++{
++ /* Wait a second for live streams so we don't try premature fragments downloading */
++ return GST_SECOND;
++}
++
+ static GstFlowReturn
+ gst_mss_demux_update_manifest_data (GstAdaptiveDemux * demux,
+ GstBuffer * buffer)
+@@ -670,3 +689,44 @@ gst_mss_demux_get_live_seek_range (GstAdaptiveDemux * demux, gint64 * start,
+
+ return gst_mss_manifest_get_live_seek_range (mssdemux->manifest, start, stop);
+ }
++
++static GstFlowReturn
++gst_mss_demux_data_received (GstAdaptiveDemux * demux,
++ GstAdaptiveDemuxStream * stream, GstBuffer * buffer)
++{
++ GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
++ GstMssDemuxStream *mssstream = (GstMssDemuxStream *) stream;
++ gsize available;
++
++ if (!gst_mss_manifest_is_live (mssdemux->manifest)) {
++ return GST_ADAPTIVE_DEMUX_CLASS (parent_class)->data_received (demux,
++ stream, buffer);
++ }
++
++ if (gst_mss_stream_fragment_parsing_needed (mssstream->manifest_stream)) {
++ gst_mss_manifest_live_adapter_push (mssstream->manifest_stream, buffer);
++ available =
++ gst_mss_manifest_live_adapter_available (mssstream->manifest_stream);
++ // FIXME: try to reduce this minimal size.
++ if (available < 4096) {
++ return GST_FLOW_OK;
++ } else {
++ GST_LOG_OBJECT (stream->pad, "enough data, parsing fragment.");
++ buffer =
++ gst_mss_manifest_live_adapter_take_buffer (mssstream->manifest_stream,
++ available);
++ gst_mss_stream_parse_fragment (mssstream->manifest_stream, buffer);
++ }
++ }
++
++ return GST_ADAPTIVE_DEMUX_CLASS (parent_class)->data_received (demux, stream,
++ buffer);
++}
++
++static gboolean
++gst_mss_demux_requires_periodical_playlist_update (GstAdaptiveDemux * demux)
++{
++ GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
++
++ return (!gst_mss_manifest_is_live (mssdemux->manifest));
++}
+diff --git a/ext/smoothstreaming/gstmssfragmentparser.c b/ext/smoothstreaming/gstmssfragmentparser.c
+new file mode 100644
+index 000000000..b554d4f31
+--- /dev/null
++++ b/ext/smoothstreaming/gstmssfragmentparser.c
+@@ -0,0 +1,266 @@
++/*
++ * Microsoft Smooth-Streaming fragment parsing library
++ *
++ * gstmssfragmentparser.h
++ *
++ * Copyright (C) 2016 Igalia S.L
++ * Copyright (C) 2016 Metrological
++ * Author: Philippe Normand <philn@igalia.com>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public
++ * License along with this library (COPYING); if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
++ * Boston, MA 02111-1307, USA.
++ */
++
++#include "gstmssfragmentparser.h"
++#include <gst/base/gstbytereader.h>
++#include <string.h>
++
++GST_DEBUG_CATEGORY_EXTERN (mssdemux_debug);
++#define GST_CAT_DEFAULT mssdemux_debug
++
++void
++gst_mss_fragment_parser_init (GstMssFragmentParser * parser)
++{
++ parser->status = GST_MSS_FRAGMENT_HEADER_PARSER_INIT;
++ parser->tfrf.entries_count = 0;
++}
++
++void
++gst_mss_fragment_parser_clear (GstMssFragmentParser * parser)
++{
++ parser->tfrf.entries_count = 0;
++ if (parser->tfrf.entries) {
++ g_free (parser->tfrf.entries);
++ parser->tfrf.entries = 0;
++ }
++}
++
++static gboolean
++_parse_tfrf_box (GstMssFragmentParser * parser, GstByteReader * reader)
++{
++ guint8 version;
++ guint32 flags = 0;
++ guint8 fragment_count = 0;
++ guint8 index = 0;
++
++ if (!gst_byte_reader_get_uint8 (reader, &version)) {
++ GST_ERROR ("Error getting box's version field");
++ return FALSE;
++ }
++
++ if (!gst_byte_reader_get_uint24_be (reader, &flags)) {
++ GST_ERROR ("Error getting box's flags field");
++ return FALSE;
++ }
++
++ gst_byte_reader_get_uint8 (reader, &fragment_count);
++ parser->tfrf.entries_count = fragment_count;
++ parser->tfrf.entries =
++ g_malloc (sizeof (GstTfrfBoxEntry) * parser->tfrf.entries_count);
++ for (index = 0; index < fragment_count; index++) {
++ guint64 absolute_time = 0;
++ guint64 absolute_duration = 0;
++ if (version & 0x01) {
++ gst_byte_reader_get_uint64_be (reader, &absolute_time);
++ gst_byte_reader_get_uint64_be (reader, &absolute_duration);
++ } else {
++ guint32 time = 0;
++ guint32 duration = 0;
++ gst_byte_reader_get_uint32_be (reader, &time);
++ gst_byte_reader_get_uint32_be (reader, &duration);
++ time = ~time;
++ duration = ~duration;
++ absolute_time = ~time;
++ absolute_duration = ~duration;
++ }
++ parser->tfrf.entries[index].time = absolute_time;
++ parser->tfrf.entries[index].duration = absolute_duration;
++ }
++
++ GST_LOG ("tfrf box parsed");
++ return TRUE;
++}
++
++static gboolean
++_parse_tfxd_box (GstMssFragmentParser * parser, GstByteReader * reader)
++{
++ guint8 version;
++ guint32 flags = 0;
++ guint64 absolute_time = 0;
++ guint64 absolute_duration = 0;
++
++ if (!gst_byte_reader_get_uint8 (reader, &version)) {
++ GST_ERROR ("Error getting box's version field");
++ return FALSE;
++ }
++
++ if (!gst_byte_reader_get_uint24_be (reader, &flags)) {
++ GST_ERROR ("Error getting box's flags field");
++ return FALSE;
++ }
++
++ if (version & 0x01) {
++ gst_byte_reader_get_uint64_be (reader, &absolute_time);
++ gst_byte_reader_get_uint64_be (reader, &absolute_duration);
++ } else {
++ guint32 time = 0;
++ guint32 duration = 0;
++ gst_byte_reader_get_uint32_be (reader, &time);
++ gst_byte_reader_get_uint32_be (reader, &duration);
++ time = ~time;
++ duration = ~duration;
++ absolute_time = ~time;
++ absolute_duration = ~duration;
++ }
++
++ parser->tfxd.time = absolute_time;
++ parser->tfxd.duration = absolute_duration;
++ GST_LOG ("tfxd box parsed");
++ return TRUE;
++}
++
++gboolean
++gst_mss_fragment_parser_add_buffer (GstMssFragmentParser * parser,
++ GstBuffer * buffer)
++{
++ GstByteReader reader;
++ GstMapInfo info;
++ guint32 size;
++ guint32 fourcc;
++ const guint8 *uuid;
++ gboolean error = FALSE;
++ gboolean mdat_box_found = FALSE;
++
++ static const guint8 tfrf_uuid[] = {
++ 0xd4, 0x80, 0x7e, 0xf2, 0xca, 0x39, 0x46, 0x95,
++ 0x8e, 0x54, 0x26, 0xcb, 0x9e, 0x46, 0xa7, 0x9f
++ };
++
++ static const guint8 tfxd_uuid[] = {
++ 0x6d, 0x1d, 0x9b, 0x05, 0x42, 0xd5, 0x44, 0xe6,
++ 0x80, 0xe2, 0x14, 0x1d, 0xaf, 0xf7, 0x57, 0xb2
++ };
++
++ static const guint8 piff_uuid[] = {
++ 0xa2, 0x39, 0x4f, 0x52, 0x5a, 0x9b, 0x4f, 0x14,
++ 0xa2, 0x44, 0x6c, 0x42, 0x7c, 0x64, 0x8d, 0xf4
++ };
++
++ if (!gst_buffer_map (buffer, &info, GST_MAP_READ)) {
++ return FALSE;
++ }
++
++ gst_byte_reader_init (&reader, info.data, info.size);
++ GST_TRACE ("Total buffer size: %u", gst_byte_reader_get_size (&reader));
++
++ size = gst_byte_reader_get_uint32_be_unchecked (&reader);
++ fourcc = gst_byte_reader_get_uint32_le_unchecked (&reader);
++ if (fourcc == GST_MSS_FRAGMENT_FOURCC_MOOF) {
++ GST_TRACE ("moof box found");
++ size = gst_byte_reader_get_uint32_be_unchecked (&reader);
++ fourcc = gst_byte_reader_get_uint32_le_unchecked (&reader);
++ if (fourcc == GST_MSS_FRAGMENT_FOURCC_MFHD) {
++ gst_byte_reader_skip_unchecked (&reader, size - 8);
++
++ size = gst_byte_reader_get_uint32_be_unchecked (&reader);
++ fourcc = gst_byte_reader_get_uint32_le_unchecked (&reader);
++ if (fourcc == GST_MSS_FRAGMENT_FOURCC_TRAF) {
++ size = gst_byte_reader_get_uint32_be_unchecked (&reader);
++ fourcc = gst_byte_reader_get_uint32_le_unchecked (&reader);
++ if (fourcc == GST_MSS_FRAGMENT_FOURCC_TFHD) {
++ gst_byte_reader_skip_unchecked (&reader, size - 8);
++
++ size = gst_byte_reader_get_uint32_be_unchecked (&reader);
++ fourcc = gst_byte_reader_get_uint32_le_unchecked (&reader);
++ if (fourcc == GST_MSS_FRAGMENT_FOURCC_TRUN) {
++ GST_TRACE ("trun box found, size: %" G_GUINT32_FORMAT, size);
++ if (!gst_byte_reader_skip (&reader, size - 8)) {
++ GST_WARNING ("Failed to skip trun box, enough data?");
++ error = TRUE;
++ goto beach;
++ }
++ }
++ }
++ }
++ }
++ }
++
++ while (!mdat_box_found) {
++ GST_TRACE ("remaining data: %u", gst_byte_reader_get_remaining (&reader));
++ if (!gst_byte_reader_get_uint32_be (&reader, &size)) {
++ GST_WARNING ("Failed to get box size, enough data?");
++ error = TRUE;
++ break;
++ }
++
++ GST_TRACE ("box size: %" G_GUINT32_FORMAT, size);
++ if (!gst_byte_reader_get_uint32_le (&reader, &fourcc)) {
++ GST_WARNING ("Failed to get fourcc, enough data?");
++ error = TRUE;
++ break;
++ }
++
++ if (fourcc == GST_MSS_FRAGMENT_FOURCC_MDAT) {
++ GST_LOG ("mdat box found");
++ mdat_box_found = TRUE;
++ break;
++ }
++
++ if (fourcc != GST_MSS_FRAGMENT_FOURCC_UUID) {
++ GST_ERROR ("invalid UUID fourcc: %" GST_FOURCC_FORMAT,
++ GST_FOURCC_ARGS (fourcc));
++ error = TRUE;
++ break;
++ }
++
++ if (!gst_byte_reader_peek_data (&reader, 16, &uuid)) {
++ GST_ERROR ("not enough data in UUID box");
++ error = TRUE;
++ break;
++ }
++
++ if (memcmp (uuid, piff_uuid, 16) == 0) {
++ gst_byte_reader_skip_unchecked (&reader, size - 8);
++ GST_LOG ("piff box detected");
++ }
++
++ if (memcmp (uuid, tfrf_uuid, 16) == 0) {
++ gst_byte_reader_get_data (&reader, 16, &uuid);
++ if (!_parse_tfrf_box (parser, &reader)) {
++ GST_ERROR ("txrf box parsing error");
++ error = TRUE;
++ break;
++ }
++ }
++
++ if (memcmp (uuid, tfxd_uuid, 16) == 0) {
++ gst_byte_reader_get_data (&reader, 16, &uuid);
++ if (!_parse_tfxd_box (parser, &reader)) {
++ GST_ERROR ("tfrf box parsing error");
++ error = TRUE;
++ break;
++ }
++ }
++ }
++
++beach:
++
++ if (!error)
++ parser->status = GST_MSS_FRAGMENT_HEADER_PARSER_FINISHED;
++
++ GST_LOG ("Fragment parsing successful: %s", error ? "no" : "yes");
++ gst_buffer_unmap (buffer, &info);
++ return !error;
++}
+diff --git a/ext/smoothstreaming/gstmssfragmentparser.h b/ext/smoothstreaming/gstmssfragmentparser.h
+new file mode 100644
+index 000000000..cf4711865
+--- /dev/null
++++ b/ext/smoothstreaming/gstmssfragmentparser.h
+@@ -0,0 +1,84 @@
++/*
++ * Microsoft Smooth-Streaming fragment parsing library
++ *
++ * gstmssfragmentparser.h
++ *
++ * Copyright (C) 2016 Igalia S.L
++ * Copyright (C) 2016 Metrological
++ * Author: Philippe Normand <philn@igalia.com>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public
++ * License along with this library (COPYING); if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
++ * Boston, MA 02111-1307, USA.
++ */
++
++#ifndef __GST_MSS_FRAGMENT_PARSER_H__
++#define __GST_MSS_FRAGMENT_PARSER_H__
++
++#include <gst/gst.h>
++
++G_BEGIN_DECLS
++
++#define GST_MSS_FRAGMENT_FOURCC_MOOF GST_MAKE_FOURCC('m','o','o','f')
++#define GST_MSS_FRAGMENT_FOURCC_MFHD GST_MAKE_FOURCC('m','f','h','d')
++#define GST_MSS_FRAGMENT_FOURCC_TRAF GST_MAKE_FOURCC('t','r','a','f')
++#define GST_MSS_FRAGMENT_FOURCC_TFHD GST_MAKE_FOURCC('t','f','h','d')
++#define GST_MSS_FRAGMENT_FOURCC_TRUN GST_MAKE_FOURCC('t','r','u','n')
++#define GST_MSS_FRAGMENT_FOURCC_UUID GST_MAKE_FOURCC('u','u','i','d')
++#define GST_MSS_FRAGMENT_FOURCC_MDAT GST_MAKE_FOURCC('m','d','a','t')
++
++typedef struct _GstTfxdBox
++{
++ guint8 version;
++ guint32 flags;
++
++ guint64 time;
++ guint64 duration;
++} GstTfxdBox;
++
++typedef struct _GstTfrfBoxEntry
++{
++ guint64 time;
++ guint64 duration;
++} GstTfrfBoxEntry;
++
++typedef struct _GstTfrfBox
++{
++ guint8 version;
++ guint32 flags;
++
++ gint entries_count;
++ GstTfrfBoxEntry *entries;
++} GstTfrfBox;
++
++typedef enum _GstFragmentHeaderParserStatus
++{
++ GST_MSS_FRAGMENT_HEADER_PARSER_INIT,
++ GST_MSS_FRAGMENT_HEADER_PARSER_FINISHED
++} GstFragmentHeaderParserStatus;
++
++typedef struct _GstMssFragmentParser
++{
++ GstFragmentHeaderParserStatus status;
++ GstTfxdBox tfxd;
++ GstTfrfBox tfrf;
++} GstMssFragmentParser;
++
++void gst_mss_fragment_parser_init (GstMssFragmentParser * parser);
++void gst_mss_fragment_parser_clear (GstMssFragmentParser * parser);
++gboolean gst_mss_fragment_parser_add_buffer (GstMssFragmentParser * parser, GstBuffer * buf);
++
++G_END_DECLS
++
++#endif /* __GST_MSS_FRAGMENT_PARSER_H__ */
+diff --git a/ext/smoothstreaming/gstmssmanifest.c b/ext/smoothstreaming/gstmssmanifest.c
+index 144bbb42d..e1031ba55 100644
+--- a/ext/smoothstreaming/gstmssmanifest.c
++++ b/ext/smoothstreaming/gstmssmanifest.c
+@@ -1,5 +1,7 @@
+ /* GStreamer
+ * Copyright (C) 2012 Smart TV Alliance
++ * Copyright (C) 2016 Igalia S.L
++ * Copyright (C) 2016 Metrological
+ * Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>, Collabora Ltd.
+ *
+ * gstmssmanifest.c:
+@@ -31,6 +33,7 @@
+ #include <gst/codecparsers/gsth264parser.h>
+
+ #include "gstmssmanifest.h"
++#include "gstmssfragmentparser.h"
+
+ GST_DEBUG_CATEGORY_EXTERN (mssdemux_debug);
+ #define GST_CAT_DEFAULT mssdemux_debug
+@@ -74,12 +77,17 @@ struct _GstMssStream
+ gboolean active; /* if the stream is currently being used */
+ gint selectedQualityIndex;
+
++ gboolean has_live_fragments;
++ GstAdapter *live_adapter;
++
+ GList *fragments;
+ GList *qualities;
+
+ gchar *url;
+ gchar *lang;
+
++ GstMssFragmentParser fragment_parser;
++
+ guint fragment_repetition_index;
+ GList *current_fragment;
+ GList *current_quality;
+@@ -96,6 +104,7 @@ struct _GstMssManifest
+
+ gboolean is_live;
+ gint64 dvr_window;
++ guint64 look_ahead_fragment_count;
+
+ GString *protection_system_id;
+ gchar *protection_data;
+@@ -235,7 +244,8 @@ compare_bitrate (GstMssStreamQuality * a, GstMssStreamQuality * b)
+ }
+
+ static void
+-_gst_mss_stream_init (GstMssStream * stream, xmlNodePtr node)
++_gst_mss_stream_init (GstMssManifest * manifest, GstMssStream * stream,
++ xmlNodePtr node)
+ {
+ xmlNodePtr iter;
+ GstMssFragmentListBuilder builder;
+@@ -248,9 +258,21 @@ _gst_mss_stream_init (GstMssStream * stream, xmlNodePtr node)
+ stream->url = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_URL);
+ stream->lang = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_LANGUAGE);
+
++ /* for live playback each fragment usually has timing
++ * information for the few next look-ahead fragments so the
++ * playlist can be built incrementally from the first fragment
++ * of the manifest.
++ */
++
++ GST_DEBUG ("Live stream: %s, look-ahead fragments: %" G_GUINT64_FORMAT,
++ manifest->is_live ? "yes" : "no", manifest->look_ahead_fragment_count);
++ stream->has_live_fragments = manifest->is_live
++ && manifest->look_ahead_fragment_count;
++
+ for (iter = node->children; iter; iter = iter->next) {
+ if (node_has_type (iter, MSS_NODE_STREAM_FRAGMENT)) {
+- gst_mss_fragment_list_builder_add (&builder, iter);
++ if (!stream->has_live_fragments || !builder.fragments)
++ gst_mss_fragment_list_builder_add (&builder, iter);
+ } else if (node_has_type (iter, MSS_NODE_STREAM_QUALITY)) {
+ GstMssStreamQuality *quality = gst_mss_stream_quality_new (iter);
+ stream->qualities = g_list_prepend (stream->qualities, quality);
+@@ -259,17 +281,24 @@ _gst_mss_stream_init (GstMssStream * stream, xmlNodePtr node)
+ }
+ }
+
+- stream->fragments = g_list_reverse (builder.fragments);
++ if (stream->has_live_fragments) {
++ stream->live_adapter = gst_adapter_new ();
++ }
++
++ if (builder.fragments) {
++ stream->fragments = g_list_reverse (builder.fragments);
++ stream->current_fragment = stream->fragments;
++ }
+
+ /* order them from smaller to bigger based on bitrates */
+ stream->qualities =
+ g_list_sort (stream->qualities, (GCompareFunc) compare_bitrate);
+-
+- stream->current_fragment = stream->fragments;
+ stream->current_quality = stream->qualities;
+
+ stream->regex_bitrate = g_regex_new ("\\{[Bb]itrate\\}", 0, 0, NULL);
+ stream->regex_position = g_regex_new ("\\{start[ _]time\\}", 0, 0, NULL);
++
++ gst_mss_fragment_parser_init (&stream->fragment_parser);
+ }
+
+
+@@ -315,6 +344,7 @@ gst_mss_manifest_new (GstBuffer * data)
+ xmlNodePtr nodeiter;
+ gchar *live_str;
+ GstMapInfo mapinfo;
++ gchar *look_ahead_fragment_count_str;
+
+ if (!gst_buffer_map (data, &mapinfo, GST_MAP_READ)) {
+ return NULL;
+@@ -335,6 +365,7 @@ gst_mss_manifest_new (GstBuffer * data)
+ /* the entire file is always available for non-live streams */
+ if (!manifest->is_live) {
+ manifest->dvr_window = 0;
++ manifest->look_ahead_fragment_count = 0;
+ } else {
+ /* if 0, or non-existent, the length is infinite */
+ gchar *dvr_window_str = (gchar *) xmlGetProp (root,
+@@ -346,6 +377,17 @@ gst_mss_manifest_new (GstBuffer * data)
+ manifest->dvr_window = 0;
+ }
+ }
++
++ look_ahead_fragment_count_str =
++ (gchar *) xmlGetProp (root, (xmlChar *) "LookAheadFragmentCount");
++ if (look_ahead_fragment_count_str) {
++ manifest->look_ahead_fragment_count =
++ g_ascii_strtoull (look_ahead_fragment_count_str, NULL, 10);
++ xmlFree (look_ahead_fragment_count_str);
++ if (manifest->look_ahead_fragment_count <= 0) {
++ manifest->look_ahead_fragment_count = 0;
++ }
++ }
+ }
+
+ for (nodeiter = root->children; nodeiter; nodeiter = nodeiter->next) {
+@@ -354,7 +396,7 @@ gst_mss_manifest_new (GstBuffer * data)
+ GstMssStream *stream = g_new0 (GstMssStream, 1);
+
+ manifest->streams = g_slist_append (manifest->streams, stream);
+- _gst_mss_stream_init (stream, nodeiter);
++ _gst_mss_stream_init (manifest, stream, nodeiter);
+ }
+
+ if (nodeiter->type == XML_ELEMENT_NODE
+@@ -371,6 +413,11 @@ gst_mss_manifest_new (GstBuffer * data)
+ static void
+ gst_mss_stream_free (GstMssStream * stream)
+ {
++ if (stream->live_adapter) {
++ gst_adapter_clear (stream->live_adapter);
++ g_object_unref (stream->live_adapter);
++ }
++
+ g_list_free_full (stream->fragments, g_free);
+ g_list_free_full (stream->qualities,
+ (GDestroyNotify) gst_mss_stream_quality_free);
+@@ -379,6 +426,7 @@ gst_mss_stream_free (GstMssStream * stream)
+ g_regex_unref (stream->regex_position);
+ g_regex_unref (stream->regex_bitrate);
+ g_free (stream);
++ gst_mss_fragment_parser_clear (&stream->fragment_parser);
+ }
+
+ void
+@@ -1079,6 +1127,9 @@ GstFlowReturn
+ gst_mss_stream_advance_fragment (GstMssStream * stream)
+ {
+ GstMssStreamFragment *fragment;
++ const gchar *stream_type_name =
++ gst_mss_stream_type_name (gst_mss_stream_get_type (stream));
++
+ g_return_val_if_fail (stream->active, GST_FLOW_ERROR);
+
+ if (stream->current_fragment == NULL)
+@@ -1086,14 +1137,20 @@ gst_mss_stream_advance_fragment (GstMssStream * stream)
+
+ fragment = stream->current_fragment->data;
+ stream->fragment_repetition_index++;
+- if (stream->fragment_repetition_index < fragment->repetitions) {
+- return GST_FLOW_OK;
+- }
++ if (stream->fragment_repetition_index < fragment->repetitions)
++ goto beach;
+
+ stream->fragment_repetition_index = 0;
+ stream->current_fragment = g_list_next (stream->current_fragment);
++
++ GST_DEBUG ("Advanced to fragment #%d on %s stream", fragment->number,
++ stream_type_name);
+ if (stream->current_fragment == NULL)
+ return GST_FLOW_EOS;
++
++beach:
++ gst_mss_fragment_parser_clear (&stream->fragment_parser);
++ gst_mss_fragment_parser_init (&stream->fragment_parser);
+ return GST_FLOW_OK;
+ }
+
+@@ -1173,6 +1230,11 @@ gst_mss_stream_seek (GstMssStream * stream, gboolean forward,
+ GST_DEBUG ("Stream %s seeking to %" G_GUINT64_FORMAT, stream->url, time);
+ for (iter = stream->fragments; iter; iter = g_list_next (iter)) {
+ fragment = iter->data;
++ if (stream->has_live_fragments) {
++ if (fragment->time + fragment->repetitions * fragment->duration > time)
++ stream->current_fragment = iter;
++ break;
++ }
+ if (fragment->time + fragment->repetitions * fragment->duration > time) {
+ stream->current_fragment = iter;
+ stream->fragment_repetition_index =
+@@ -1256,9 +1318,14 @@ static void
+ gst_mss_stream_reload_fragments (GstMssStream * stream, xmlNodePtr streamIndex)
+ {
+ xmlNodePtr iter;
+- guint64 current_gst_time = gst_mss_stream_get_fragment_gst_timestamp (stream);
++ guint64 current_gst_time;
+ GstMssFragmentListBuilder builder;
+
++ if (stream->has_live_fragments)
++ return;
++
++ current_gst_time = gst_mss_stream_get_fragment_gst_timestamp (stream);
++
+ gst_mss_fragment_list_builder_init (&builder);
+
+ GST_DEBUG ("Current position: %" GST_TIME_FORMAT,
+@@ -1514,3 +1581,74 @@ gst_mss_manifest_get_live_seek_range (GstMssManifest * manifest, gint64 * start,
+
+ return ret;
+ }
++
++void
++gst_mss_manifest_live_adapter_push (GstMssStream * stream, GstBuffer * buffer)
++{
++ gst_adapter_push (stream->live_adapter, buffer);
++}
++
++gsize
++gst_mss_manifest_live_adapter_available (GstMssStream * stream)
++{
++ return gst_adapter_available (stream->live_adapter);
++}
++
++GstBuffer *
++gst_mss_manifest_live_adapter_take_buffer (GstMssStream * stream, gsize nbytes)
++{
++ return gst_adapter_take_buffer (stream->live_adapter, nbytes);
++}
++
++gboolean
++gst_mss_stream_fragment_parsing_needed (GstMssStream * stream)
++{
++ return stream->fragment_parser.status == GST_MSS_FRAGMENT_HEADER_PARSER_INIT;
++}
++
++void
++gst_mss_stream_parse_fragment (GstMssStream * stream, GstBuffer * buffer)
++{
++ GstMssStreamFragment *current_fragment = NULL;
++ const gchar *stream_type_name;
++ guint8 index;
++
++ if (!stream->has_live_fragments)
++ return;
++
++ if (!gst_mss_fragment_parser_add_buffer (&stream->fragment_parser, buffer))
++ return;
++
++ current_fragment = stream->current_fragment->data;
++ current_fragment->time = stream->fragment_parser.tfxd.time;
++ current_fragment->duration = stream->fragment_parser.tfxd.duration;
++
++ stream_type_name =
++ gst_mss_stream_type_name (gst_mss_stream_get_type (stream));
++
++ for (index = 0; index < stream->fragment_parser.tfrf.entries_count; index++) {
++ GList *l = g_list_last (stream->fragments);
++ GstMssStreamFragment *last;
++ GstMssStreamFragment *fragment;
++
++ if (l == NULL)
++ break;
++
++ last = (GstMssStreamFragment *) l->data;
++
++ if (last->time == stream->fragment_parser.tfrf.entries[index].time)
++ continue;
++
++ fragment = g_new (GstMssStreamFragment, 1);
++ fragment->number = last->number + 1;
++ fragment->repetitions = 1;
++ fragment->time = stream->fragment_parser.tfrf.entries[index].time;
++ fragment->duration = stream->fragment_parser.tfrf.entries[index].duration;
++
++ stream->fragments = g_list_append (stream->fragments, fragment);
++ GST_LOG ("Adding fragment number: %u to %s stream, time: %" G_GUINT64_FORMAT
++ ", duration: %" G_GUINT64_FORMAT ", repetitions: %u",
++ fragment->number, stream_type_name,
++ fragment->time, fragment->duration, fragment->repetitions);
++ }
++}
+diff --git a/ext/smoothstreaming/gstmssmanifest.h b/ext/smoothstreaming/gstmssmanifest.h
+index 6b7b1f971..03b066ae5 100644
+--- a/ext/smoothstreaming/gstmssmanifest.h
++++ b/ext/smoothstreaming/gstmssmanifest.h
+@@ -26,6 +26,7 @@
+ #include <glib.h>
+ #include <gio/gio.h>
+ #include <gst/gst.h>
++#include <gst/base/gstadapter.h>
+
+ G_BEGIN_DECLS
+
+@@ -73,5 +74,11 @@ const gchar * gst_mss_stream_get_lang (GstMssStream * stream);
+
+ const gchar * gst_mss_stream_type_name (GstMssStreamType streamtype);
+
++void gst_mss_manifest_live_adapter_push(GstMssStream * stream, GstBuffer * buffer);
++gsize gst_mss_manifest_live_adapter_available(GstMssStream * stream);
++GstBuffer * gst_mss_manifest_live_adapter_take_buffer(GstMssStream * stream, gsize nbytes);
++gboolean gst_mss_stream_fragment_parsing_needed(GstMssStream * stream);
++void gst_mss_stream_parse_fragment(GstMssStream * stream, GstBuffer * buffer);
++
+ G_END_DECLS
+ #endif /* __GST_MSS_MANIFEST_H__ */
+diff --git a/gst-libs/gst/adaptivedemux/gstadaptivedemux.c b/gst-libs/gst/adaptivedemux/gstadaptivedemux.c
+index 634e4f388..ddca726b6 100644
+--- a/gst-libs/gst/adaptivedemux/gstadaptivedemux.c
++++ b/gst-libs/gst/adaptivedemux/gstadaptivedemux.c
+@@ -291,6 +291,9 @@ gst_adaptive_demux_wait_until (GstClock * clock, GCond * cond, GMutex * mutex,
+ GstClockTime end_time);
+ static gboolean gst_adaptive_demux_clock_callback (GstClock * clock,
+ GstClockTime time, GstClockID id, gpointer user_data);
++static gboolean
++gst_adaptive_demux_requires_periodical_playlist_update_default (GstAdaptiveDemux
++ * demux);
+
+ /* we can't use G_DEFINE_ABSTRACT_TYPE because we need the klass in the _init
+ * method to get to the padtemplates */
+@@ -412,6 +415,9 @@ gst_adaptive_demux_class_init (GstAdaptiveDemuxClass * klass)
+ klass->data_received = gst_adaptive_demux_stream_data_received_default;
+ klass->finish_fragment = gst_adaptive_demux_stream_finish_fragment_default;
+ klass->update_manifest = gst_adaptive_demux_update_manifest_default;
++ klass->requires_periodical_playlist_update =
++ gst_adaptive_demux_requires_periodical_playlist_update_default;
++
+ }
+
+ static void
+@@ -686,7 +692,9 @@ gst_adaptive_demux_sink_event (GstPad * pad, GstObject * parent,
+ demux->priv->stop_updates_task = FALSE;
+ g_mutex_unlock (&demux->priv->updates_timed_lock);
+ /* Task to periodically update the manifest */
+- gst_task_start (demux->priv->updates_task);
++ if (demux_class->requires_periodical_playlist_update (demux)) {
++ gst_task_start (demux->priv->updates_task);
++ }
+ }
+ } else {
+ /* no streams */
+@@ -2125,6 +2133,13 @@ gst_adaptive_demux_stream_data_received_default (GstAdaptiveDemux * demux,
+ return gst_adaptive_demux_stream_push_buffer (stream, buffer);
+ }
+
++static gboolean
++gst_adaptive_demux_requires_periodical_playlist_update_default (GstAdaptiveDemux
++ * demux)
++{
++ return TRUE;
++}
++
+ static GstFlowReturn
+ _src_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
+ {
+@@ -3338,7 +3353,15 @@ gst_adaptive_demux_stream_download_loop (GstAdaptiveDemuxStream * stream)
+ GST_DEBUG_OBJECT (stream->pad, "EOS, checking to stop download loop");
+ /* we push the EOS after releasing the object lock */
+ if (gst_adaptive_demux_is_live (demux)) {
+- if (gst_adaptive_demux_stream_wait_manifest_update (demux, stream)) {
++ GstAdaptiveDemuxClass *demux_class =
++ GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
++
++ /* this might be a fragment download error, refresh the manifest, just in case */
++ if (!demux_class->requires_periodical_playlist_update (demux)) {
++ ret = gst_adaptive_demux_update_manifest (demux);
++ break;
++ } else if (gst_adaptive_demux_stream_wait_manifest_update (demux,
++ stream)) {
+ goto end;
+ }
+ gst_task_stop (stream->download_task);
+diff --git a/gst-libs/gst/adaptivedemux/gstadaptivedemux.h b/gst-libs/gst/adaptivedemux/gstadaptivedemux.h
+index 780f4d93f..9a1a1b7d1 100644
+--- a/gst-libs/gst/adaptivedemux/gstadaptivedemux.h
++++ b/gst-libs/gst/adaptivedemux/gstadaptivedemux.h
+@@ -459,6 +459,20 @@ struct _GstAdaptiveDemuxClass
+ * selected period.
+ */
+ GstClockTime (*get_period_start_time) (GstAdaptiveDemux *demux);
++
++ /**
++ * requires_periodical_playlist_update:
++ * @demux: #GstAdaptiveDemux
++ *
++ * Some adaptive streaming protocols allow the client to download
++ * the playlist once and build up the fragment list based on the
++ * current fragment metadata. For those protocols the demuxer
++ * doesn't need to periodically refresh the playlist. This vfunc
++ * is relevant only for live playback scenarios.
++ *
++ * Return: %TRUE if the playlist needs to be refreshed periodically by the demuxer.
++ */
++ gboolean (*requires_periodical_playlist_update) (GstAdaptiveDemux * demux);
+ };
+
+ GType gst_adaptive_demux_get_type (void);
+--
+2.11.0
+
diff --git a/meta/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0001-smoothstreaming-implement-adaptivedemux-s-get_live_s.patch b/meta/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0001-smoothstreaming-implement-adaptivedemux-s-get_live_s.patch
new file mode 100644
index 0000000000..76d29e151b
--- /dev/null
+++ b/meta/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0001-smoothstreaming-implement-adaptivedemux-s-get_live_s.patch
@@ -0,0 +1,183 @@
+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
+
diff --git a/meta/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0001-smoothstreaming-use-the-duration-from-the-list-of-fr.patch b/meta/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0001-smoothstreaming-use-the-duration-from-the-list-of-fr.patch
new file mode 100644
index 0000000000..4e51040863
--- /dev/null
+++ b/meta/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0001-smoothstreaming-use-the-duration-from-the-list-of-fr.patch
@@ -0,0 +1,62 @@
+From 0fbee8f37427b88339194b22ba9aa210772a8613 Mon Sep 17 00:00:00 2001
+From: Matthew Waters <matthew@centricular.com>
+Date: Thu, 10 Nov 2016 17:20:27 +1100
+Subject: [PATCH] smoothstreaming: use the duration from the list of fragments
+ if not present in the manifest
+
+Provides a more accurate duration for live streams that may be minutes
+or hours in front of the earliest fragment.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=774178
+---
+Upstream-Status: Backport
+Signed-off-by: Khem Raj <raj.khem@gmail.com>
+
+ ext/smoothstreaming/gstmssmanifest.c | 24 ++++++++++++++++++++++++
+ 1 file changed, 24 insertions(+)
+
+diff --git a/ext/smoothstreaming/gstmssmanifest.c b/ext/smoothstreaming/gstmssmanifest.c
+index 317b3cef9..144bbb42d 100644
+--- a/ext/smoothstreaming/gstmssmanifest.c
++++ b/ext/smoothstreaming/gstmssmanifest.c
+@@ -888,6 +888,7 @@ gst_mss_manifest_get_duration (GstMssManifest * manifest)
+ gchar *duration;
+ guint64 dur = -1;
+
++ /* try the property */
+ duration =
+ (gchar *) xmlGetProp (manifest->xmlrootnode,
+ (xmlChar *) MSS_PROP_STREAM_DURATION);
+@@ -895,6 +896,29 @@ gst_mss_manifest_get_duration (GstMssManifest * manifest)
+ dur = g_ascii_strtoull (duration, NULL, 10);
+ xmlFree (duration);
+ }
++ /* else use the fragment list */
++ if (dur <= 0) {
++ guint64 max_dur = 0;
++ GSList *iter;
++
++ for (iter = manifest->streams; iter; iter = g_slist_next (iter)) {
++ GstMssStream *stream = iter->data;
++
++ if (stream->active) {
++ if (stream->fragments) {
++ GList *l = g_list_last (stream->fragments);
++ GstMssStreamFragment *fragment = (GstMssStreamFragment *) l->data;
++ guint64 frag_dur =
++ fragment->time + fragment->duration * fragment->repetitions;
++ max_dur = MAX (frag_dur, max_dur);
++ }
++ }
++ }
++
++ if (max_dur != 0)
++ dur = max_dur;
++ }
++
+ return dur;
+ }
+
+--
+2.11.0
+
diff --git a/meta/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.10.2.bb b/meta/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.10.2.bb
index 73cf591f98..f487b09cb2 100644
--- a/meta/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.10.2.bb
+++ b/meta/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.10.2.bb
@@ -15,6 +15,9 @@ SRC_URI = " \
file://0009-glimagesink-Downrank-to-marginal.patch \
file://0001-introspection.m4-prefix-pkgconfig-paths-with-PKG_CON.patch \
file://0001-Prepend-PKG_CONFIG_SYSROOT_DIR-to-pkg-config-output.patch \
+ file://0001-smoothstreaming-implement-adaptivedemux-s-get_live_s.patch \
+ file://0001-smoothstreaming-use-the-duration-from-the-list-of-fr.patch \
+ file://0001-mssdemux-improved-live-playback-support.patch \
"
SRC_URI[md5sum] = "823f4c33fe27c61332c0122273217988"
SRC_URI[sha256sum] = "0795ca9303a99cc7e44dda0e6e18524de02b39892e4b68eaba488f7b9db53a3a"