]> code.ossystems Code Review - openembedded-core.git/blob
4832c18e789848830c065d704a5364a3f1fe6375
[openembedded-core.git] /
1 From 73721ad4e9e2d32e1c8b6a3b4aaa98401530e58a Mon Sep 17 00:00:00 2001
2 From: Philippe Normand <philn@igalia.com>
3 Date: Tue, 29 Nov 2016 14:43:41 +0100
4 Subject: [PATCH] mssdemux: improved live playback support
5
6 When a MSS server hosts a live stream the fragments listed in the
7 manifest usually don't have accurate timestamps and duration, except
8 for the first fragment, which additionally stores timing information
9 for the few upcoming fragments. In this scenario it is useless to
10 periodically fetch and update the manifest and the fragments list can
11 be incrementally built by parsing the first/current fragment.
12
13 https://bugzilla.gnome.org/show_bug.cgi?id=755036
14 ---
15 Upstream-Status: Backport
16 Signed-off-by: Khem Raj <raj.khem@gmail.com>
17
18  ext/smoothstreaming/Makefile.am               |   2 +
19  ext/smoothstreaming/gstmssdemux.c             |  60 ++++++
20  ext/smoothstreaming/gstmssfragmentparser.c    | 266 ++++++++++++++++++++++++++
21  ext/smoothstreaming/gstmssfragmentparser.h    |  84 ++++++++
22  ext/smoothstreaming/gstmssmanifest.c          | 158 ++++++++++++++-
23  ext/smoothstreaming/gstmssmanifest.h          |   7 +
24  gst-libs/gst/adaptivedemux/gstadaptivedemux.c |  27 ++-
25  gst-libs/gst/adaptivedemux/gstadaptivedemux.h |  14 ++
26  8 files changed, 606 insertions(+), 12 deletions(-)
27  create mode 100644 ext/smoothstreaming/gstmssfragmentparser.c
28  create mode 100644 ext/smoothstreaming/gstmssfragmentparser.h
29
30 diff --git a/ext/smoothstreaming/Makefile.am b/ext/smoothstreaming/Makefile.am
31 index 4faf9df9f..a5e1ad6ae 100644
32 --- a/ext/smoothstreaming/Makefile.am
33 +++ b/ext/smoothstreaming/Makefile.am
34 @@ -13,8 +13,10 @@ libgstsmoothstreaming_la_LIBADD = \
35  libgstsmoothstreaming_la_LDFLAGS = ${GST_PLUGIN_LDFLAGS}
36  libgstsmoothstreaming_la_SOURCES = gstsmoothstreaming-plugin.c \
37         gstmssdemux.c \
38 +       gstmssfragmentparser.c \
39         gstmssmanifest.c
40  libgstsmoothstreaming_la_LIBTOOLFLAGS = --tag=disable-static
41  
42  noinst_HEADERS = gstmssdemux.h \
43 +       gstmssfragmentparser.h \
44         gstmssmanifest.h
45 diff --git a/ext/smoothstreaming/gstmssdemux.c b/ext/smoothstreaming/gstmssdemux.c
46 index 12fb40497..120d9c22b 100644
47 --- a/ext/smoothstreaming/gstmssdemux.c
48 +++ b/ext/smoothstreaming/gstmssdemux.c
49 @@ -135,11 +135,18 @@ gst_mss_demux_stream_update_fragment_info (GstAdaptiveDemuxStream * stream);
50  static gboolean gst_mss_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek);
51  static gint64
52  gst_mss_demux_get_manifest_update_interval (GstAdaptiveDemux * demux);
53 +static gint64
54 +gst_mss_demux_stream_get_fragment_waiting_time (GstAdaptiveDemuxStream *
55 +    stream);
56  static GstFlowReturn
57  gst_mss_demux_update_manifest_data (GstAdaptiveDemux * demux,
58      GstBuffer * buffer);
59  static gboolean gst_mss_demux_get_live_seek_range (GstAdaptiveDemux * demux,
60      gint64 * start, gint64 * stop);
61 +static GstFlowReturn gst_mss_demux_data_received (GstAdaptiveDemux * demux,
62 +    GstAdaptiveDemuxStream * stream, GstBuffer * buffer);
63 +static gboolean
64 +gst_mss_demux_requires_periodical_playlist_update (GstAdaptiveDemux * demux);
65  
66  static void
67  gst_mss_demux_class_init (GstMssDemuxClass * klass)
68 @@ -192,10 +199,15 @@ gst_mss_demux_class_init (GstMssDemuxClass * klass)
69        gst_mss_demux_stream_select_bitrate;
70    gstadaptivedemux_class->stream_update_fragment_info =
71        gst_mss_demux_stream_update_fragment_info;
72 +  gstadaptivedemux_class->stream_get_fragment_waiting_time =
73 +      gst_mss_demux_stream_get_fragment_waiting_time;
74    gstadaptivedemux_class->update_manifest_data =
75        gst_mss_demux_update_manifest_data;
76    gstadaptivedemux_class->get_live_seek_range =
77        gst_mss_demux_get_live_seek_range;
78 +  gstadaptivedemux_class->data_received = gst_mss_demux_data_received;
79 +  gstadaptivedemux_class->requires_periodical_playlist_update =
80 +      gst_mss_demux_requires_periodical_playlist_update;
81  
82    GST_DEBUG_CATEGORY_INIT (mssdemux_debug, "mssdemux", 0, "mssdemux plugin");
83  }
84 @@ -650,6 +662,13 @@ gst_mss_demux_get_manifest_update_interval (GstAdaptiveDemux * demux)
85    return interval;
86  }
87  
88 +static gint64
89 +gst_mss_demux_stream_get_fragment_waiting_time (GstAdaptiveDemuxStream * stream)
90 +{
91 +  /* Wait a second for live streams so we don't try premature fragments downloading */
92 +  return GST_SECOND;
93 +}
94 +
95  static GstFlowReturn
96  gst_mss_demux_update_manifest_data (GstAdaptiveDemux * demux,
97      GstBuffer * buffer)
98 @@ -670,3 +689,44 @@ gst_mss_demux_get_live_seek_range (GstAdaptiveDemux * demux, gint64 * start,
99  
100    return gst_mss_manifest_get_live_seek_range (mssdemux->manifest, start, stop);
101  }
102 +
103 +static GstFlowReturn
104 +gst_mss_demux_data_received (GstAdaptiveDemux * demux,
105 +    GstAdaptiveDemuxStream * stream, GstBuffer * buffer)
106 +{
107 +  GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
108 +  GstMssDemuxStream *mssstream = (GstMssDemuxStream *) stream;
109 +  gsize available;
110 +
111 +  if (!gst_mss_manifest_is_live (mssdemux->manifest)) {
112 +    return GST_ADAPTIVE_DEMUX_CLASS (parent_class)->data_received (demux,
113 +        stream, buffer);
114 +  }
115 +
116 +  if (gst_mss_stream_fragment_parsing_needed (mssstream->manifest_stream)) {
117 +    gst_mss_manifest_live_adapter_push (mssstream->manifest_stream, buffer);
118 +    available =
119 +        gst_mss_manifest_live_adapter_available (mssstream->manifest_stream);
120 +    // FIXME: try to reduce this minimal size.
121 +    if (available < 4096) {
122 +      return GST_FLOW_OK;
123 +    } else {
124 +      GST_LOG_OBJECT (stream->pad, "enough data, parsing fragment.");
125 +      buffer =
126 +          gst_mss_manifest_live_adapter_take_buffer (mssstream->manifest_stream,
127 +          available);
128 +      gst_mss_stream_parse_fragment (mssstream->manifest_stream, buffer);
129 +    }
130 +  }
131 +
132 +  return GST_ADAPTIVE_DEMUX_CLASS (parent_class)->data_received (demux, stream,
133 +      buffer);
134 +}
135 +
136 +static gboolean
137 +gst_mss_demux_requires_periodical_playlist_update (GstAdaptiveDemux * demux)
138 +{
139 +  GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
140 +
141 +  return (!gst_mss_manifest_is_live (mssdemux->manifest));
142 +}
143 diff --git a/ext/smoothstreaming/gstmssfragmentparser.c b/ext/smoothstreaming/gstmssfragmentparser.c
144 new file mode 100644
145 index 000000000..b554d4f31
146 --- /dev/null
147 +++ b/ext/smoothstreaming/gstmssfragmentparser.c
148 @@ -0,0 +1,266 @@
149 +/*
150 + * Microsoft Smooth-Streaming fragment parsing library
151 + *
152 + * gstmssfragmentparser.h
153 + *
154 + * Copyright (C) 2016 Igalia S.L
155 + * Copyright (C) 2016 Metrological
156 + *   Author: Philippe Normand <philn@igalia.com>
157 + *
158 + * This library is free software; you can redistribute it and/or
159 + * modify it under the terms of the GNU Library General Public
160 + * License as published by the Free Software Foundation; either
161 + * version 2.1 of the License, or (at your option) any later version.
162 + *
163 + * This library is distributed in the hope that it will be useful,
164 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
165 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
166 + * Library General Public License for more details.
167 + *
168 + * You should have received a copy of the GNU Library General Public
169 + * License along with this library (COPYING); if not, write to the
170 + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
171 + * Boston, MA 02111-1307, USA.
172 + */
173 +
174 +#include "gstmssfragmentparser.h"
175 +#include <gst/base/gstbytereader.h>
176 +#include <string.h>
177 +
178 +GST_DEBUG_CATEGORY_EXTERN (mssdemux_debug);
179 +#define GST_CAT_DEFAULT mssdemux_debug
180 +
181 +void
182 +gst_mss_fragment_parser_init (GstMssFragmentParser * parser)
183 +{
184 +  parser->status = GST_MSS_FRAGMENT_HEADER_PARSER_INIT;
185 +  parser->tfrf.entries_count = 0;
186 +}
187 +
188 +void
189 +gst_mss_fragment_parser_clear (GstMssFragmentParser * parser)
190 +{
191 +  parser->tfrf.entries_count = 0;
192 +  if (parser->tfrf.entries) {
193 +    g_free (parser->tfrf.entries);
194 +    parser->tfrf.entries = 0;
195 +  }
196 +}
197 +
198 +static gboolean
199 +_parse_tfrf_box (GstMssFragmentParser * parser, GstByteReader * reader)
200 +{
201 +  guint8 version;
202 +  guint32 flags = 0;
203 +  guint8 fragment_count = 0;
204 +  guint8 index = 0;
205 +
206 +  if (!gst_byte_reader_get_uint8 (reader, &version)) {
207 +    GST_ERROR ("Error getting box's version field");
208 +    return FALSE;
209 +  }
210 +
211 +  if (!gst_byte_reader_get_uint24_be (reader, &flags)) {
212 +    GST_ERROR ("Error getting box's flags field");
213 +    return FALSE;
214 +  }
215 +
216 +  gst_byte_reader_get_uint8 (reader, &fragment_count);
217 +  parser->tfrf.entries_count = fragment_count;
218 +  parser->tfrf.entries =
219 +      g_malloc (sizeof (GstTfrfBoxEntry) * parser->tfrf.entries_count);
220 +  for (index = 0; index < fragment_count; index++) {
221 +    guint64 absolute_time = 0;
222 +    guint64 absolute_duration = 0;
223 +    if (version & 0x01) {
224 +      gst_byte_reader_get_uint64_be (reader, &absolute_time);
225 +      gst_byte_reader_get_uint64_be (reader, &absolute_duration);
226 +    } else {
227 +      guint32 time = 0;
228 +      guint32 duration = 0;
229 +      gst_byte_reader_get_uint32_be (reader, &time);
230 +      gst_byte_reader_get_uint32_be (reader, &duration);
231 +      time = ~time;
232 +      duration = ~duration;
233 +      absolute_time = ~time;
234 +      absolute_duration = ~duration;
235 +    }
236 +    parser->tfrf.entries[index].time = absolute_time;
237 +    parser->tfrf.entries[index].duration = absolute_duration;
238 +  }
239 +
240 +  GST_LOG ("tfrf box parsed");
241 +  return TRUE;
242 +}
243 +
244 +static gboolean
245 +_parse_tfxd_box (GstMssFragmentParser * parser, GstByteReader * reader)
246 +{
247 +  guint8 version;
248 +  guint32 flags = 0;
249 +  guint64 absolute_time = 0;
250 +  guint64 absolute_duration = 0;
251 +
252 +  if (!gst_byte_reader_get_uint8 (reader, &version)) {
253 +    GST_ERROR ("Error getting box's version field");
254 +    return FALSE;
255 +  }
256 +
257 +  if (!gst_byte_reader_get_uint24_be (reader, &flags)) {
258 +    GST_ERROR ("Error getting box's flags field");
259 +    return FALSE;
260 +  }
261 +
262 +  if (version & 0x01) {
263 +    gst_byte_reader_get_uint64_be (reader, &absolute_time);
264 +    gst_byte_reader_get_uint64_be (reader, &absolute_duration);
265 +  } else {
266 +    guint32 time = 0;
267 +    guint32 duration = 0;
268 +    gst_byte_reader_get_uint32_be (reader, &time);
269 +    gst_byte_reader_get_uint32_be (reader, &duration);
270 +    time = ~time;
271 +    duration = ~duration;
272 +    absolute_time = ~time;
273 +    absolute_duration = ~duration;
274 +  }
275 +
276 +  parser->tfxd.time = absolute_time;
277 +  parser->tfxd.duration = absolute_duration;
278 +  GST_LOG ("tfxd box parsed");
279 +  return TRUE;
280 +}
281 +
282 +gboolean
283 +gst_mss_fragment_parser_add_buffer (GstMssFragmentParser * parser,
284 +    GstBuffer * buffer)
285 +{
286 +  GstByteReader reader;
287 +  GstMapInfo info;
288 +  guint32 size;
289 +  guint32 fourcc;
290 +  const guint8 *uuid;
291 +  gboolean error = FALSE;
292 +  gboolean mdat_box_found = FALSE;
293 +
294 +  static const guint8 tfrf_uuid[] = {
295 +    0xd4, 0x80, 0x7e, 0xf2, 0xca, 0x39, 0x46, 0x95,
296 +    0x8e, 0x54, 0x26, 0xcb, 0x9e, 0x46, 0xa7, 0x9f
297 +  };
298 +
299 +  static const guint8 tfxd_uuid[] = {
300 +    0x6d, 0x1d, 0x9b, 0x05, 0x42, 0xd5, 0x44, 0xe6,
301 +    0x80, 0xe2, 0x14, 0x1d, 0xaf, 0xf7, 0x57, 0xb2
302 +  };
303 +
304 +  static const guint8 piff_uuid[] = {
305 +    0xa2, 0x39, 0x4f, 0x52, 0x5a, 0x9b, 0x4f, 0x14,
306 +    0xa2, 0x44, 0x6c, 0x42, 0x7c, 0x64, 0x8d, 0xf4
307 +  };
308 +
309 +  if (!gst_buffer_map (buffer, &info, GST_MAP_READ)) {
310 +    return FALSE;
311 +  }
312 +
313 +  gst_byte_reader_init (&reader, info.data, info.size);
314 +  GST_TRACE ("Total buffer size: %u", gst_byte_reader_get_size (&reader));
315 +
316 +  size = gst_byte_reader_get_uint32_be_unchecked (&reader);
317 +  fourcc = gst_byte_reader_get_uint32_le_unchecked (&reader);
318 +  if (fourcc == GST_MSS_FRAGMENT_FOURCC_MOOF) {
319 +    GST_TRACE ("moof box found");
320 +    size = gst_byte_reader_get_uint32_be_unchecked (&reader);
321 +    fourcc = gst_byte_reader_get_uint32_le_unchecked (&reader);
322 +    if (fourcc == GST_MSS_FRAGMENT_FOURCC_MFHD) {
323 +      gst_byte_reader_skip_unchecked (&reader, size - 8);
324 +
325 +      size = gst_byte_reader_get_uint32_be_unchecked (&reader);
326 +      fourcc = gst_byte_reader_get_uint32_le_unchecked (&reader);
327 +      if (fourcc == GST_MSS_FRAGMENT_FOURCC_TRAF) {
328 +        size = gst_byte_reader_get_uint32_be_unchecked (&reader);
329 +        fourcc = gst_byte_reader_get_uint32_le_unchecked (&reader);
330 +        if (fourcc == GST_MSS_FRAGMENT_FOURCC_TFHD) {
331 +          gst_byte_reader_skip_unchecked (&reader, size - 8);
332 +
333 +          size = gst_byte_reader_get_uint32_be_unchecked (&reader);
334 +          fourcc = gst_byte_reader_get_uint32_le_unchecked (&reader);
335 +          if (fourcc == GST_MSS_FRAGMENT_FOURCC_TRUN) {
336 +            GST_TRACE ("trun box found, size: %" G_GUINT32_FORMAT, size);
337 +            if (!gst_byte_reader_skip (&reader, size - 8)) {
338 +              GST_WARNING ("Failed to skip trun box, enough data?");
339 +              error = TRUE;
340 +              goto beach;
341 +            }
342 +          }
343 +        }
344 +      }
345 +    }
346 +  }
347 +
348 +  while (!mdat_box_found) {
349 +    GST_TRACE ("remaining data: %u", gst_byte_reader_get_remaining (&reader));
350 +    if (!gst_byte_reader_get_uint32_be (&reader, &size)) {
351 +      GST_WARNING ("Failed to get box size, enough data?");
352 +      error = TRUE;
353 +      break;
354 +    }
355 +
356 +    GST_TRACE ("box size: %" G_GUINT32_FORMAT, size);
357 +    if (!gst_byte_reader_get_uint32_le (&reader, &fourcc)) {
358 +      GST_WARNING ("Failed to get fourcc, enough data?");
359 +      error = TRUE;
360 +      break;
361 +    }
362 +
363 +    if (fourcc == GST_MSS_FRAGMENT_FOURCC_MDAT) {
364 +      GST_LOG ("mdat box found");
365 +      mdat_box_found = TRUE;
366 +      break;
367 +    }
368 +
369 +    if (fourcc != GST_MSS_FRAGMENT_FOURCC_UUID) {
370 +      GST_ERROR ("invalid UUID fourcc: %" GST_FOURCC_FORMAT,
371 +          GST_FOURCC_ARGS (fourcc));
372 +      error = TRUE;
373 +      break;
374 +    }
375 +
376 +    if (!gst_byte_reader_peek_data (&reader, 16, &uuid)) {
377 +      GST_ERROR ("not enough data in UUID box");
378 +      error = TRUE;
379 +      break;
380 +    }
381 +
382 +    if (memcmp (uuid, piff_uuid, 16) == 0) {
383 +      gst_byte_reader_skip_unchecked (&reader, size - 8);
384 +      GST_LOG ("piff box detected");
385 +    }
386 +
387 +    if (memcmp (uuid, tfrf_uuid, 16) == 0) {
388 +      gst_byte_reader_get_data (&reader, 16, &uuid);
389 +      if (!_parse_tfrf_box (parser, &reader)) {
390 +        GST_ERROR ("txrf box parsing error");
391 +        error = TRUE;
392 +        break;
393 +      }
394 +    }
395 +
396 +    if (memcmp (uuid, tfxd_uuid, 16) == 0) {
397 +      gst_byte_reader_get_data (&reader, 16, &uuid);
398 +      if (!_parse_tfxd_box (parser, &reader)) {
399 +        GST_ERROR ("tfrf box parsing error");
400 +        error = TRUE;
401 +        break;
402 +      }
403 +    }
404 +  }
405 +
406 +beach:
407 +
408 +  if (!error)
409 +    parser->status = GST_MSS_FRAGMENT_HEADER_PARSER_FINISHED;
410 +
411 +  GST_LOG ("Fragment parsing successful: %s", error ? "no" : "yes");
412 +  gst_buffer_unmap (buffer, &info);
413 +  return !error;
414 +}
415 diff --git a/ext/smoothstreaming/gstmssfragmentparser.h b/ext/smoothstreaming/gstmssfragmentparser.h
416 new file mode 100644
417 index 000000000..cf4711865
418 --- /dev/null
419 +++ b/ext/smoothstreaming/gstmssfragmentparser.h
420 @@ -0,0 +1,84 @@
421 +/*
422 + * Microsoft Smooth-Streaming fragment parsing library
423 + *
424 + * gstmssfragmentparser.h
425 + *
426 + * Copyright (C) 2016 Igalia S.L
427 + * Copyright (C) 2016 Metrological
428 + *   Author: Philippe Normand <philn@igalia.com>
429 + *
430 + * This library is free software; you can redistribute it and/or
431 + * modify it under the terms of the GNU Library General Public
432 + * License as published by the Free Software Foundation; either
433 + * version 2.1 of the License, or (at your option) any later version.
434 + *
435 + * This library is distributed in the hope that it will be useful,
436 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
437 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
438 + * Library General Public License for more details.
439 + *
440 + * You should have received a copy of the GNU Library General Public
441 + * License along with this library (COPYING); if not, write to the
442 + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
443 + * Boston, MA 02111-1307, USA.
444 + */
445 +
446 +#ifndef __GST_MSS_FRAGMENT_PARSER_H__
447 +#define __GST_MSS_FRAGMENT_PARSER_H__
448 +
449 +#include <gst/gst.h>
450 +
451 +G_BEGIN_DECLS
452 +
453 +#define GST_MSS_FRAGMENT_FOURCC_MOOF GST_MAKE_FOURCC('m','o','o','f')
454 +#define GST_MSS_FRAGMENT_FOURCC_MFHD GST_MAKE_FOURCC('m','f','h','d')
455 +#define GST_MSS_FRAGMENT_FOURCC_TRAF GST_MAKE_FOURCC('t','r','a','f')
456 +#define GST_MSS_FRAGMENT_FOURCC_TFHD GST_MAKE_FOURCC('t','f','h','d')
457 +#define GST_MSS_FRAGMENT_FOURCC_TRUN GST_MAKE_FOURCC('t','r','u','n')
458 +#define GST_MSS_FRAGMENT_FOURCC_UUID GST_MAKE_FOURCC('u','u','i','d')
459 +#define GST_MSS_FRAGMENT_FOURCC_MDAT GST_MAKE_FOURCC('m','d','a','t')
460 +
461 +typedef struct _GstTfxdBox
462 +{
463 +  guint8 version;
464 +  guint32 flags;
465 +
466 +  guint64 time;
467 +  guint64 duration;
468 +} GstTfxdBox;
469 +
470 +typedef struct _GstTfrfBoxEntry
471 +{
472 +  guint64 time;
473 +  guint64 duration;
474 +} GstTfrfBoxEntry;
475 +
476 +typedef struct _GstTfrfBox
477 +{
478 +  guint8 version;
479 +  guint32 flags;
480 +
481 +  gint entries_count;
482 +  GstTfrfBoxEntry *entries;
483 +} GstTfrfBox;
484 +
485 +typedef enum _GstFragmentHeaderParserStatus
486 +{
487 +  GST_MSS_FRAGMENT_HEADER_PARSER_INIT,
488 +  GST_MSS_FRAGMENT_HEADER_PARSER_FINISHED
489 +} GstFragmentHeaderParserStatus;
490 +
491 +typedef struct _GstMssFragmentParser
492 +{
493 +  GstFragmentHeaderParserStatus status;
494 +  GstTfxdBox tfxd;
495 +  GstTfrfBox tfrf;
496 +} GstMssFragmentParser;
497 +
498 +void gst_mss_fragment_parser_init (GstMssFragmentParser * parser);
499 +void gst_mss_fragment_parser_clear (GstMssFragmentParser * parser);
500 +gboolean gst_mss_fragment_parser_add_buffer (GstMssFragmentParser * parser, GstBuffer * buf);
501 +
502 +G_END_DECLS
503 +
504 +#endif /* __GST_MSS_FRAGMENT_PARSER_H__ */
505 diff --git a/ext/smoothstreaming/gstmssmanifest.c b/ext/smoothstreaming/gstmssmanifest.c
506 index 144bbb42d..e1031ba55 100644
507 --- a/ext/smoothstreaming/gstmssmanifest.c
508 +++ b/ext/smoothstreaming/gstmssmanifest.c
509 @@ -1,5 +1,7 @@
510  /* GStreamer
511   * Copyright (C) 2012 Smart TV Alliance
512 + * Copyright (C) 2016 Igalia S.L
513 + * Copyright (C) 2016 Metrological
514   *  Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>, Collabora Ltd.
515   *
516   * gstmssmanifest.c:
517 @@ -31,6 +33,7 @@
518  #include <gst/codecparsers/gsth264parser.h>
519  
520  #include "gstmssmanifest.h"
521 +#include "gstmssfragmentparser.h"
522  
523  GST_DEBUG_CATEGORY_EXTERN (mssdemux_debug);
524  #define GST_CAT_DEFAULT mssdemux_debug
525 @@ -74,12 +77,17 @@ struct _GstMssStream
526    gboolean active;              /* if the stream is currently being used */
527    gint selectedQualityIndex;
528  
529 +  gboolean has_live_fragments;
530 +  GstAdapter *live_adapter;
531 +
532    GList *fragments;
533    GList *qualities;
534  
535    gchar *url;
536    gchar *lang;
537  
538 +  GstMssFragmentParser fragment_parser;
539 +
540    guint fragment_repetition_index;
541    GList *current_fragment;
542    GList *current_quality;
543 @@ -96,6 +104,7 @@ struct _GstMssManifest
544  
545    gboolean is_live;
546    gint64 dvr_window;
547 +  guint64 look_ahead_fragment_count;
548  
549    GString *protection_system_id;
550    gchar *protection_data;
551 @@ -235,7 +244,8 @@ compare_bitrate (GstMssStreamQuality * a, GstMssStreamQuality * b)
552  }
553  
554  static void
555 -_gst_mss_stream_init (GstMssStream * stream, xmlNodePtr node)
556 +_gst_mss_stream_init (GstMssManifest * manifest, GstMssStream * stream,
557 +    xmlNodePtr node)
558  {
559    xmlNodePtr iter;
560    GstMssFragmentListBuilder builder;
561 @@ -248,9 +258,21 @@ _gst_mss_stream_init (GstMssStream * stream, xmlNodePtr node)
562    stream->url = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_URL);
563    stream->lang = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_LANGUAGE);
564  
565 +  /* for live playback each fragment usually has timing
566 +   * information for the few next look-ahead fragments so the
567 +   * playlist can be built incrementally from the first fragment
568 +   * of the manifest.
569 +   */
570 +
571 +  GST_DEBUG ("Live stream: %s, look-ahead fragments: %" G_GUINT64_FORMAT,
572 +      manifest->is_live ? "yes" : "no", manifest->look_ahead_fragment_count);
573 +  stream->has_live_fragments = manifest->is_live
574 +      && manifest->look_ahead_fragment_count;
575 +
576    for (iter = node->children; iter; iter = iter->next) {
577      if (node_has_type (iter, MSS_NODE_STREAM_FRAGMENT)) {
578 -      gst_mss_fragment_list_builder_add (&builder, iter);
579 +      if (!stream->has_live_fragments || !builder.fragments)
580 +        gst_mss_fragment_list_builder_add (&builder, iter);
581      } else if (node_has_type (iter, MSS_NODE_STREAM_QUALITY)) {
582        GstMssStreamQuality *quality = gst_mss_stream_quality_new (iter);
583        stream->qualities = g_list_prepend (stream->qualities, quality);
584 @@ -259,17 +281,24 @@ _gst_mss_stream_init (GstMssStream * stream, xmlNodePtr node)
585      }
586    }
587  
588 -  stream->fragments = g_list_reverse (builder.fragments);
589 +  if (stream->has_live_fragments) {
590 +    stream->live_adapter = gst_adapter_new ();
591 +  }
592 +
593 +  if (builder.fragments) {
594 +    stream->fragments = g_list_reverse (builder.fragments);
595 +    stream->current_fragment = stream->fragments;
596 +  }
597  
598    /* order them from smaller to bigger based on bitrates */
599    stream->qualities =
600        g_list_sort (stream->qualities, (GCompareFunc) compare_bitrate);
601 -
602 -  stream->current_fragment = stream->fragments;
603    stream->current_quality = stream->qualities;
604  
605    stream->regex_bitrate = g_regex_new ("\\{[Bb]itrate\\}", 0, 0, NULL);
606    stream->regex_position = g_regex_new ("\\{start[ _]time\\}", 0, 0, NULL);
607 +
608 +  gst_mss_fragment_parser_init (&stream->fragment_parser);
609  }
610  
611  
612 @@ -315,6 +344,7 @@ gst_mss_manifest_new (GstBuffer * data)
613    xmlNodePtr nodeiter;
614    gchar *live_str;
615    GstMapInfo mapinfo;
616 +  gchar *look_ahead_fragment_count_str;
617  
618    if (!gst_buffer_map (data, &mapinfo, GST_MAP_READ)) {
619      return NULL;
620 @@ -335,6 +365,7 @@ gst_mss_manifest_new (GstBuffer * data)
621    /* the entire file is always available for non-live streams */
622    if (!manifest->is_live) {
623      manifest->dvr_window = 0;
624 +    manifest->look_ahead_fragment_count = 0;
625    } else {
626      /* if 0, or non-existent, the length is infinite */
627      gchar *dvr_window_str = (gchar *) xmlGetProp (root,
628 @@ -346,6 +377,17 @@ gst_mss_manifest_new (GstBuffer * data)
629          manifest->dvr_window = 0;
630        }
631      }
632 +
633 +    look_ahead_fragment_count_str =
634 +        (gchar *) xmlGetProp (root, (xmlChar *) "LookAheadFragmentCount");
635 +    if (look_ahead_fragment_count_str) {
636 +      manifest->look_ahead_fragment_count =
637 +          g_ascii_strtoull (look_ahead_fragment_count_str, NULL, 10);
638 +      xmlFree (look_ahead_fragment_count_str);
639 +      if (manifest->look_ahead_fragment_count <= 0) {
640 +        manifest->look_ahead_fragment_count = 0;
641 +      }
642 +    }
643    }
644  
645    for (nodeiter = root->children; nodeiter; nodeiter = nodeiter->next) {
646 @@ -354,7 +396,7 @@ gst_mss_manifest_new (GstBuffer * data)
647        GstMssStream *stream = g_new0 (GstMssStream, 1);
648  
649        manifest->streams = g_slist_append (manifest->streams, stream);
650 -      _gst_mss_stream_init (stream, nodeiter);
651 +      _gst_mss_stream_init (manifest, stream, nodeiter);
652      }
653  
654      if (nodeiter->type == XML_ELEMENT_NODE
655 @@ -371,6 +413,11 @@ gst_mss_manifest_new (GstBuffer * data)
656  static void
657  gst_mss_stream_free (GstMssStream * stream)
658  {
659 +  if (stream->live_adapter) {
660 +    gst_adapter_clear (stream->live_adapter);
661 +    g_object_unref (stream->live_adapter);
662 +  }
663 +
664    g_list_free_full (stream->fragments, g_free);
665    g_list_free_full (stream->qualities,
666        (GDestroyNotify) gst_mss_stream_quality_free);
667 @@ -379,6 +426,7 @@ gst_mss_stream_free (GstMssStream * stream)
668    g_regex_unref (stream->regex_position);
669    g_regex_unref (stream->regex_bitrate);
670    g_free (stream);
671 +  gst_mss_fragment_parser_clear (&stream->fragment_parser);
672  }
673  
674  void
675 @@ -1079,6 +1127,9 @@ GstFlowReturn
676  gst_mss_stream_advance_fragment (GstMssStream * stream)
677  {
678    GstMssStreamFragment *fragment;
679 +  const gchar *stream_type_name =
680 +      gst_mss_stream_type_name (gst_mss_stream_get_type (stream));
681 +
682    g_return_val_if_fail (stream->active, GST_FLOW_ERROR);
683  
684    if (stream->current_fragment == NULL)
685 @@ -1086,14 +1137,20 @@ gst_mss_stream_advance_fragment (GstMssStream * stream)
686  
687    fragment = stream->current_fragment->data;
688    stream->fragment_repetition_index++;
689 -  if (stream->fragment_repetition_index < fragment->repetitions) {
690 -    return GST_FLOW_OK;
691 -  }
692 +  if (stream->fragment_repetition_index < fragment->repetitions)
693 +    goto beach;
694  
695    stream->fragment_repetition_index = 0;
696    stream->current_fragment = g_list_next (stream->current_fragment);
697 +
698 +  GST_DEBUG ("Advanced to fragment #%d on %s stream", fragment->number,
699 +      stream_type_name);
700    if (stream->current_fragment == NULL)
701      return GST_FLOW_EOS;
702 +
703 +beach:
704 +  gst_mss_fragment_parser_clear (&stream->fragment_parser);
705 +  gst_mss_fragment_parser_init (&stream->fragment_parser);
706    return GST_FLOW_OK;
707  }
708  
709 @@ -1173,6 +1230,11 @@ gst_mss_stream_seek (GstMssStream * stream, gboolean forward,
710    GST_DEBUG ("Stream %s seeking to %" G_GUINT64_FORMAT, stream->url, time);
711    for (iter = stream->fragments; iter; iter = g_list_next (iter)) {
712      fragment = iter->data;
713 +    if (stream->has_live_fragments) {
714 +      if (fragment->time + fragment->repetitions * fragment->duration > time)
715 +        stream->current_fragment = iter;
716 +      break;
717 +    }
718      if (fragment->time + fragment->repetitions * fragment->duration > time) {
719        stream->current_fragment = iter;
720        stream->fragment_repetition_index =
721 @@ -1256,9 +1318,14 @@ static void
722  gst_mss_stream_reload_fragments (GstMssStream * stream, xmlNodePtr streamIndex)
723  {
724    xmlNodePtr iter;
725 -  guint64 current_gst_time = gst_mss_stream_get_fragment_gst_timestamp (stream);
726 +  guint64 current_gst_time;
727    GstMssFragmentListBuilder builder;
728  
729 +  if (stream->has_live_fragments)
730 +    return;
731 +
732 +  current_gst_time = gst_mss_stream_get_fragment_gst_timestamp (stream);
733 +
734    gst_mss_fragment_list_builder_init (&builder);
735  
736    GST_DEBUG ("Current position: %" GST_TIME_FORMAT,
737 @@ -1514,3 +1581,74 @@ gst_mss_manifest_get_live_seek_range (GstMssManifest * manifest, gint64 * start,
738  
739    return ret;
740  }
741 +
742 +void
743 +gst_mss_manifest_live_adapter_push (GstMssStream * stream, GstBuffer * buffer)
744 +{
745 +  gst_adapter_push (stream->live_adapter, buffer);
746 +}
747 +
748 +gsize
749 +gst_mss_manifest_live_adapter_available (GstMssStream * stream)
750 +{
751 +  return gst_adapter_available (stream->live_adapter);
752 +}
753 +
754 +GstBuffer *
755 +gst_mss_manifest_live_adapter_take_buffer (GstMssStream * stream, gsize nbytes)
756 +{
757 +  return gst_adapter_take_buffer (stream->live_adapter, nbytes);
758 +}
759 +
760 +gboolean
761 +gst_mss_stream_fragment_parsing_needed (GstMssStream * stream)
762 +{
763 +  return stream->fragment_parser.status == GST_MSS_FRAGMENT_HEADER_PARSER_INIT;
764 +}
765 +
766 +void
767 +gst_mss_stream_parse_fragment (GstMssStream * stream, GstBuffer * buffer)
768 +{
769 +  GstMssStreamFragment *current_fragment = NULL;
770 +  const gchar *stream_type_name;
771 +  guint8 index;
772 +
773 +  if (!stream->has_live_fragments)
774 +    return;
775 +
776 +  if (!gst_mss_fragment_parser_add_buffer (&stream->fragment_parser, buffer))
777 +    return;
778 +
779 +  current_fragment = stream->current_fragment->data;
780 +  current_fragment->time = stream->fragment_parser.tfxd.time;
781 +  current_fragment->duration = stream->fragment_parser.tfxd.duration;
782 +
783 +  stream_type_name =
784 +      gst_mss_stream_type_name (gst_mss_stream_get_type (stream));
785 +
786 +  for (index = 0; index < stream->fragment_parser.tfrf.entries_count; index++) {
787 +    GList *l = g_list_last (stream->fragments);
788 +    GstMssStreamFragment *last;
789 +    GstMssStreamFragment *fragment;
790 +
791 +    if (l == NULL)
792 +      break;
793 +
794 +    last = (GstMssStreamFragment *) l->data;
795 +
796 +    if (last->time == stream->fragment_parser.tfrf.entries[index].time)
797 +      continue;
798 +
799 +    fragment = g_new (GstMssStreamFragment, 1);
800 +    fragment->number = last->number + 1;
801 +    fragment->repetitions = 1;
802 +    fragment->time = stream->fragment_parser.tfrf.entries[index].time;
803 +    fragment->duration = stream->fragment_parser.tfrf.entries[index].duration;
804 +
805 +    stream->fragments = g_list_append (stream->fragments, fragment);
806 +    GST_LOG ("Adding fragment number: %u to %s stream, time: %" G_GUINT64_FORMAT
807 +        ", duration: %" G_GUINT64_FORMAT ", repetitions: %u",
808 +        fragment->number, stream_type_name,
809 +        fragment->time, fragment->duration, fragment->repetitions);
810 +  }
811 +}
812 diff --git a/ext/smoothstreaming/gstmssmanifest.h b/ext/smoothstreaming/gstmssmanifest.h
813 index 6b7b1f971..03b066ae5 100644
814 --- a/ext/smoothstreaming/gstmssmanifest.h
815 +++ b/ext/smoothstreaming/gstmssmanifest.h
816 @@ -26,6 +26,7 @@
817  #include <glib.h>
818  #include <gio/gio.h>
819  #include <gst/gst.h>
820 +#include <gst/base/gstadapter.h>
821  
822  G_BEGIN_DECLS
823  
824 @@ -73,5 +74,11 @@ const gchar * gst_mss_stream_get_lang (GstMssStream * stream);
825  
826  const gchar * gst_mss_stream_type_name (GstMssStreamType streamtype);
827  
828 +void gst_mss_manifest_live_adapter_push(GstMssStream * stream, GstBuffer * buffer);
829 +gsize gst_mss_manifest_live_adapter_available(GstMssStream * stream);
830 +GstBuffer * gst_mss_manifest_live_adapter_take_buffer(GstMssStream * stream, gsize nbytes);
831 +gboolean gst_mss_stream_fragment_parsing_needed(GstMssStream * stream);
832 +void gst_mss_stream_parse_fragment(GstMssStream * stream, GstBuffer * buffer);
833 +
834  G_END_DECLS
835  #endif /* __GST_MSS_MANIFEST_H__ */
836 diff --git a/gst-libs/gst/adaptivedemux/gstadaptivedemux.c b/gst-libs/gst/adaptivedemux/gstadaptivedemux.c
837 index 634e4f388..ddca726b6 100644
838 --- a/gst-libs/gst/adaptivedemux/gstadaptivedemux.c
839 +++ b/gst-libs/gst/adaptivedemux/gstadaptivedemux.c
840 @@ -291,6 +291,9 @@ gst_adaptive_demux_wait_until (GstClock * clock, GCond * cond, GMutex * mutex,
841      GstClockTime end_time);
842  static gboolean gst_adaptive_demux_clock_callback (GstClock * clock,
843      GstClockTime time, GstClockID id, gpointer user_data);
844 +static gboolean
845 +gst_adaptive_demux_requires_periodical_playlist_update_default (GstAdaptiveDemux
846 +    * demux);
847  
848  /* we can't use G_DEFINE_ABSTRACT_TYPE because we need the klass in the _init
849   * method to get to the padtemplates */
850 @@ -412,6 +415,9 @@ gst_adaptive_demux_class_init (GstAdaptiveDemuxClass * klass)
851    klass->data_received = gst_adaptive_demux_stream_data_received_default;
852    klass->finish_fragment = gst_adaptive_demux_stream_finish_fragment_default;
853    klass->update_manifest = gst_adaptive_demux_update_manifest_default;
854 +  klass->requires_periodical_playlist_update =
855 +      gst_adaptive_demux_requires_periodical_playlist_update_default;
856 +
857  }
858  
859  static void
860 @@ -686,7 +692,9 @@ gst_adaptive_demux_sink_event (GstPad * pad, GstObject * parent,
861              demux->priv->stop_updates_task = FALSE;
862              g_mutex_unlock (&demux->priv->updates_timed_lock);
863              /* Task to periodically update the manifest */
864 -            gst_task_start (demux->priv->updates_task);
865 +            if (demux_class->requires_periodical_playlist_update (demux)) {
866 +              gst_task_start (demux->priv->updates_task);
867 +            }
868            }
869          } else {
870            /* no streams */
871 @@ -2125,6 +2133,13 @@ gst_adaptive_demux_stream_data_received_default (GstAdaptiveDemux * demux,
872    return gst_adaptive_demux_stream_push_buffer (stream, buffer);
873  }
874  
875 +static gboolean
876 +gst_adaptive_demux_requires_periodical_playlist_update_default (GstAdaptiveDemux
877 +    * demux)
878 +{
879 +  return TRUE;
880 +}
881 +
882  static GstFlowReturn
883  _src_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
884  {
885 @@ -3338,7 +3353,15 @@ gst_adaptive_demux_stream_download_loop (GstAdaptiveDemuxStream * stream)
886        GST_DEBUG_OBJECT (stream->pad, "EOS, checking to stop download loop");
887        /* we push the EOS after releasing the object lock */
888        if (gst_adaptive_demux_is_live (demux)) {
889 -        if (gst_adaptive_demux_stream_wait_manifest_update (demux, stream)) {
890 +        GstAdaptiveDemuxClass *demux_class =
891 +            GST_ADAPTIVE_DEMUX_GET_CLASS (demux);
892 +
893 +        /* this might be a fragment download error, refresh the manifest, just in case */
894 +        if (!demux_class->requires_periodical_playlist_update (demux)) {
895 +          ret = gst_adaptive_demux_update_manifest (demux);
896 +          break;
897 +        } else if (gst_adaptive_demux_stream_wait_manifest_update (demux,
898 +                stream)) {
899            goto end;
900          }
901          gst_task_stop (stream->download_task);
902 diff --git a/gst-libs/gst/adaptivedemux/gstadaptivedemux.h b/gst-libs/gst/adaptivedemux/gstadaptivedemux.h
903 index 780f4d93f..9a1a1b7d1 100644
904 --- a/gst-libs/gst/adaptivedemux/gstadaptivedemux.h
905 +++ b/gst-libs/gst/adaptivedemux/gstadaptivedemux.h
906 @@ -459,6 +459,20 @@ struct _GstAdaptiveDemuxClass
907     * selected period.
908     */
909    GstClockTime (*get_period_start_time) (GstAdaptiveDemux *demux);
910 +
911 +  /**
912 +   * requires_periodical_playlist_update:
913 +   * @demux: #GstAdaptiveDemux
914 +   *
915 +   * Some adaptive streaming protocols allow the client to download
916 +   * the playlist once and build up the fragment list based on the
917 +   * current fragment metadata. For those protocols the demuxer
918 +   * doesn't need to periodically refresh the playlist. This vfunc
919 +   * is relevant only for live playback scenarios.
920 +   *
921 +   * Return: %TRUE if the playlist needs to be refreshed periodically by the demuxer.
922 +   */
923 +  gboolean (*requires_periodical_playlist_update) (GstAdaptiveDemux * demux);
924  };
925  
926  GType    gst_adaptive_demux_get_type (void);
927 -- 
928 2.11.0
929