• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) 2012 Smart TV Alliance
3  *  Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>, Collabora Ltd.
4  *
5  * gstmssdemux.c:
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22 
23 /**
24  * SECTION:element-mssdemux
25  * @title: mssdemux
26  *
27  * Demuxes a Microsoft's Smooth Streaming manifest into its audio and/or video streams.
28  *
29  */
30 
31 /*
32  * == Internals
33  *
34  * = Smooth streaming in a few lines
35  * A SS stream is defined by a xml manifest file. This file has a list of
36  * tracks (StreamIndex), each one can have multiple QualityLevels, that define
37  * different encoding/bitrates. When playing a track, only one of those
38  * QualityLevels can be active at a time (per stream).
39  *
40  * The StreamIndex defines a URL with {time} and {bitrate} tags that are
41  * replaced by values indicated by the fragment start times and the selected
42  * QualityLevel, that generates the fragments URLs.
43  *
44  * Another relevant detail is that the Isomedia fragments for smoothstreaming
45  * won't contains a 'moov' atom, nor a 'stsd', so there is no information
46  * about the media type/configuration on the fragments, it must be extracted
47  * from the Manifest and passed downstream. mssdemux does this via GstCaps.
48  *
49  * = How mssdemux works
50  * There is a gstmssmanifest.c utility that holds the manifest and parses
51  * and has functions to extract information from it. mssdemux received the
52  * manifest from its sink pad and starts processing it when it gets EOS.
53  *
54  * The Manifest is parsed and the streams are exposed, 1 pad for each, with
55  * a initially selected QualityLevel. Each stream starts its own GstTaks that
56  * is responsible for downloading fragments and pushing them downstream.
57  *
58  * When a new connection-speed is set, mssdemux evaluates the available
59  * QualityLevels and might decide to switch to another one. In this case it
60  * pushes a new GstCaps event indicating the new caps on the pads.
61  *
62  * All operations that intend to update the GstTasks state should be protected
63  * with the GST_OBJECT_LOCK.
64  */
65 
66 #ifdef HAVE_CONFIG_H
67 #include "config.h"
68 #endif
69 
70 #include "gst/gst-i18n-plugin.h"
71 
72 #include <stdio.h>
73 #include <stdlib.h>
74 #include <string.h>
75 
76 #include "gstmssdemux.h"
77 
78 GST_DEBUG_CATEGORY (mssdemux_debug);
79 
80 #define DEFAULT_MAX_QUEUE_SIZE_BUFFERS 0
81 #define DEFAULT_BITRATE_LIMIT 0.8
82 
83 enum
84 {
85   PROP_0,
86 
87   PROP_MAX_QUEUE_SIZE_BUFFERS,
88   PROP_LAST
89 };
90 
91 static GstStaticPadTemplate gst_mss_demux_sink_template =
92 GST_STATIC_PAD_TEMPLATE ("sink",
93     GST_PAD_SINK,
94     GST_PAD_ALWAYS,
95     GST_STATIC_CAPS ("application/vnd.ms-sstr+xml")
96     );
97 
98 static GstStaticPadTemplate gst_mss_demux_videosrc_template =
99 GST_STATIC_PAD_TEMPLATE ("video_%02u",
100     GST_PAD_SRC,
101     GST_PAD_SOMETIMES,
102     GST_STATIC_CAPS_ANY);
103 
104 static GstStaticPadTemplate gst_mss_demux_audiosrc_template =
105 GST_STATIC_PAD_TEMPLATE ("audio_%02u",
106     GST_PAD_SRC,
107     GST_PAD_SOMETIMES,
108     GST_STATIC_CAPS_ANY);
109 
110 #define gst_mss_demux_parent_class parent_class
111 G_DEFINE_TYPE (GstMssDemux, gst_mss_demux, GST_TYPE_ADAPTIVE_DEMUX);
112 
113 static void gst_mss_demux_dispose (GObject * object);
114 static void gst_mss_demux_set_property (GObject * object, guint prop_id,
115     const GValue * value, GParamSpec * pspec);
116 static void gst_mss_demux_get_property (GObject * object, guint prop_id,
117     GValue * value, GParamSpec * pspec);
118 
119 static gboolean gst_mss_demux_is_live (GstAdaptiveDemux * demux);
120 static gboolean gst_mss_demux_process_manifest (GstAdaptiveDemux * demux,
121     GstBuffer * buffer);
122 static GstClockTime gst_mss_demux_get_duration (GstAdaptiveDemux * demux);
123 static void gst_mss_demux_reset (GstAdaptiveDemux * demux);
124 static GstFlowReturn gst_mss_demux_stream_seek (GstAdaptiveDemuxStream * stream,
125     gboolean forward, GstSeekFlags flags, GstClockTime ts,
126     GstClockTime * final_ts);
127 static gboolean gst_mss_demux_stream_has_next_fragment (GstAdaptiveDemuxStream *
128     stream);
129 static GstFlowReturn
130 gst_mss_demux_stream_advance_fragment (GstAdaptiveDemuxStream * stream);
131 static gboolean gst_mss_demux_stream_select_bitrate (GstAdaptiveDemuxStream *
132     stream, guint64 bitrate);
133 static GstFlowReturn
134 gst_mss_demux_stream_update_fragment_info (GstAdaptiveDemuxStream * stream);
135 static gboolean gst_mss_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek);
136 static gint64
137 gst_mss_demux_get_manifest_update_interval (GstAdaptiveDemux * demux);
138 static gint64
139 gst_mss_demux_stream_get_fragment_waiting_time (GstAdaptiveDemuxStream *
140     stream);
141 static GstFlowReturn
142 gst_mss_demux_update_manifest_data (GstAdaptiveDemux * demux,
143     GstBuffer * buffer);
144 static gboolean gst_mss_demux_get_live_seek_range (GstAdaptiveDemux * demux,
145     gint64 * start, gint64 * stop);
146 static GstFlowReturn gst_mss_demux_data_received (GstAdaptiveDemux * demux,
147     GstAdaptiveDemuxStream * stream, GstBuffer * buffer);
148 static gboolean
149 gst_mss_demux_requires_periodical_playlist_update (GstAdaptiveDemux * demux);
150 
151 static void
gst_mss_demux_class_init(GstMssDemuxClass * klass)152 gst_mss_demux_class_init (GstMssDemuxClass * klass)
153 {
154   GObjectClass *gobject_class;
155   GstElementClass *gstelement_class;
156   GstAdaptiveDemuxClass *gstadaptivedemux_class;
157 
158   gobject_class = (GObjectClass *) klass;
159   gstelement_class = (GstElementClass *) klass;
160   gstadaptivedemux_class = (GstAdaptiveDemuxClass *) klass;
161 
162   gst_element_class_add_static_pad_template (gstelement_class,
163       &gst_mss_demux_sink_template);
164   gst_element_class_add_static_pad_template (gstelement_class,
165       &gst_mss_demux_videosrc_template);
166   gst_element_class_add_static_pad_template (gstelement_class,
167       &gst_mss_demux_audiosrc_template);
168   gst_element_class_set_static_metadata (gstelement_class,
169       "Smooth Streaming demuxer", "Codec/Demuxer/Adaptive",
170       "Parse and demultiplex a Smooth Streaming manifest into audio and video "
171       "streams", "Thiago Santos <thiago.sousa.santos@collabora.com>");
172 
173   gobject_class->dispose = gst_mss_demux_dispose;
174   gobject_class->set_property = gst_mss_demux_set_property;
175   gobject_class->get_property = gst_mss_demux_get_property;
176 
177 #ifndef GST_REMOVE_DEPRECATED
178   g_object_class_install_property (gobject_class, PROP_MAX_QUEUE_SIZE_BUFFERS,
179       g_param_spec_uint ("max-queue-size-buffers", "Max queue size in buffers",
180           "Maximum buffers that can be stored in each internal stream queue "
181           "(0 = infinite) (deprecated)", 0, G_MAXUINT,
182           DEFAULT_MAX_QUEUE_SIZE_BUFFERS,
183           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED));
184 #endif
185 
186   gstadaptivedemux_class->process_manifest = gst_mss_demux_process_manifest;
187   gstadaptivedemux_class->is_live = gst_mss_demux_is_live;
188   gstadaptivedemux_class->get_duration = gst_mss_demux_get_duration;
189   gstadaptivedemux_class->get_manifest_update_interval =
190       gst_mss_demux_get_manifest_update_interval;
191   gstadaptivedemux_class->reset = gst_mss_demux_reset;
192   gstadaptivedemux_class->seek = gst_mss_demux_seek;
193   gstadaptivedemux_class->stream_seek = gst_mss_demux_stream_seek;
194   gstadaptivedemux_class->stream_advance_fragment =
195       gst_mss_demux_stream_advance_fragment;
196   gstadaptivedemux_class->stream_has_next_fragment =
197       gst_mss_demux_stream_has_next_fragment;
198   gstadaptivedemux_class->stream_select_bitrate =
199       gst_mss_demux_stream_select_bitrate;
200   gstadaptivedemux_class->stream_update_fragment_info =
201       gst_mss_demux_stream_update_fragment_info;
202   gstadaptivedemux_class->stream_get_fragment_waiting_time =
203       gst_mss_demux_stream_get_fragment_waiting_time;
204   gstadaptivedemux_class->update_manifest_data =
205       gst_mss_demux_update_manifest_data;
206   gstadaptivedemux_class->get_live_seek_range =
207       gst_mss_demux_get_live_seek_range;
208   gstadaptivedemux_class->data_received = gst_mss_demux_data_received;
209   gstadaptivedemux_class->requires_periodical_playlist_update =
210       gst_mss_demux_requires_periodical_playlist_update;
211 
212   GST_DEBUG_CATEGORY_INIT (mssdemux_debug, "mssdemux", 0, "mssdemux plugin");
213 }
214 
215 static void
gst_mss_demux_init(GstMssDemux * mssdemux)216 gst_mss_demux_init (GstMssDemux * mssdemux)
217 {
218   mssdemux->data_queue_max_size = DEFAULT_MAX_QUEUE_SIZE_BUFFERS;
219 
220   gst_adaptive_demux_set_stream_struct_size (GST_ADAPTIVE_DEMUX_CAST (mssdemux),
221       sizeof (GstMssDemuxStream));
222 }
223 
224 static void
gst_mss_demux_reset(GstAdaptiveDemux * demux)225 gst_mss_demux_reset (GstAdaptiveDemux * demux)
226 {
227   GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
228 
229   if (mssdemux->manifest) {
230     gst_mss_manifest_free (mssdemux->manifest);
231     mssdemux->manifest = NULL;
232   }
233   g_free (mssdemux->base_url);
234   mssdemux->base_url = NULL;
235 
236   mssdemux->n_videos = mssdemux->n_audios = 0;
237 
238 }
239 
240 static void
gst_mss_demux_dispose(GObject * object)241 gst_mss_demux_dispose (GObject * object)
242 {
243   gst_mss_demux_reset (GST_ADAPTIVE_DEMUX_CAST (object));
244 
245   G_OBJECT_CLASS (parent_class)->dispose (object);
246 }
247 
248 static void
gst_mss_demux_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)249 gst_mss_demux_set_property (GObject * object, guint prop_id,
250     const GValue * value, GParamSpec * pspec)
251 {
252   GstMssDemux *mssdemux = GST_MSS_DEMUX (object);
253 
254   switch (prop_id) {
255     case PROP_MAX_QUEUE_SIZE_BUFFERS:
256       mssdemux->data_queue_max_size = g_value_get_uint (value);
257       break;
258     default:
259       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
260       break;
261   }
262 }
263 
264 static void
gst_mss_demux_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)265 gst_mss_demux_get_property (GObject * object, guint prop_id, GValue * value,
266     GParamSpec * pspec)
267 {
268   GstMssDemux *mssdemux = GST_MSS_DEMUX (object);
269 
270   switch (prop_id) {
271     case PROP_MAX_QUEUE_SIZE_BUFFERS:
272       g_value_set_uint (value, mssdemux->data_queue_max_size);
273       break;
274     default:
275       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
276       break;
277   }
278 }
279 
280 static gboolean
gst_mss_demux_is_live(GstAdaptiveDemux * demux)281 gst_mss_demux_is_live (GstAdaptiveDemux * demux)
282 {
283   GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
284 
285   g_return_val_if_fail (mssdemux->manifest != NULL, FALSE);
286 
287   return gst_mss_manifest_is_live (mssdemux->manifest);
288 }
289 
290 static GstClockTime
gst_mss_demux_get_duration(GstAdaptiveDemux * demux)291 gst_mss_demux_get_duration (GstAdaptiveDemux * demux)
292 {
293   GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
294 
295   g_return_val_if_fail (mssdemux->manifest != NULL, FALSE);
296 
297   return gst_mss_manifest_get_gst_duration (mssdemux->manifest);
298 }
299 
300 static GstFlowReturn
gst_mss_demux_stream_update_fragment_info(GstAdaptiveDemuxStream * stream)301 gst_mss_demux_stream_update_fragment_info (GstAdaptiveDemuxStream * stream)
302 {
303   GstMssDemuxStream *mssstream = (GstMssDemuxStream *) stream;
304   GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (stream->demux);
305   GstFlowReturn ret;
306   gchar *path = NULL;
307 
308   gst_adaptive_demux_stream_fragment_clear (&stream->fragment);
309   ret = gst_mss_stream_get_fragment_url (mssstream->manifest_stream, &path);
310 
311   if (ret == GST_FLOW_OK) {
312     stream->fragment.uri = g_strdup_printf ("%s/%s", mssdemux->base_url, path);
313     stream->fragment.timestamp =
314         gst_mss_stream_get_fragment_gst_timestamp (mssstream->manifest_stream);
315     stream->fragment.duration =
316         gst_mss_stream_get_fragment_gst_duration (mssstream->manifest_stream);
317   }
318   g_free (path);
319 
320   return ret;
321 }
322 
323 static GstFlowReturn
gst_mss_demux_stream_seek(GstAdaptiveDemuxStream * stream,gboolean forward,GstSeekFlags flags,GstClockTime ts,GstClockTime * final_ts)324 gst_mss_demux_stream_seek (GstAdaptiveDemuxStream * stream, gboolean forward,
325     GstSeekFlags flags, GstClockTime ts, GstClockTime * final_ts)
326 {
327   GstMssDemuxStream *mssstream = (GstMssDemuxStream *) stream;
328 
329   gst_mss_stream_seek (mssstream->manifest_stream, forward, flags, ts,
330       final_ts);
331   return GST_FLOW_OK;
332 }
333 
334 static GstFlowReturn
gst_mss_demux_stream_advance_fragment(GstAdaptiveDemuxStream * stream)335 gst_mss_demux_stream_advance_fragment (GstAdaptiveDemuxStream * stream)
336 {
337   GstMssDemuxStream *mssstream = (GstMssDemuxStream *) stream;
338 
339   if (stream->demux->segment.rate >= 0)
340     return gst_mss_stream_advance_fragment (mssstream->manifest_stream);
341   else
342     return gst_mss_stream_regress_fragment (mssstream->manifest_stream);
343 }
344 
345 static GstCaps *
create_mss_caps(GstMssDemuxStream * stream,GstCaps * caps)346 create_mss_caps (GstMssDemuxStream * stream, GstCaps * caps)
347 {
348   return gst_caps_new_simple ("video/quicktime", "variant", G_TYPE_STRING,
349       "mss-fragmented", "timescale", G_TYPE_UINT64,
350       gst_mss_stream_get_timescale (stream->manifest_stream), "media-caps",
351       GST_TYPE_CAPS, caps, NULL);
352 }
353 
354 static GstPad *
_create_pad(GstMssDemux * mssdemux,GstMssStream * manifeststream)355 _create_pad (GstMssDemux * mssdemux, GstMssStream * manifeststream)
356 {
357   gchar *name = NULL;
358   GstPad *srcpad = NULL;
359   GstMssStreamType streamtype;
360   GstPadTemplate *tmpl = NULL;
361 
362   streamtype = gst_mss_stream_get_type (manifeststream);
363   GST_DEBUG_OBJECT (mssdemux, "Found stream of type: %s",
364       gst_mss_stream_type_name (streamtype));
365 
366   /* TODO use stream's name/bitrate/index as the pad name? */
367   if (streamtype == MSS_STREAM_TYPE_VIDEO) {
368     name = g_strdup_printf ("video_%02u", mssdemux->n_videos++);
369     tmpl = gst_static_pad_template_get (&gst_mss_demux_videosrc_template);
370   } else if (streamtype == MSS_STREAM_TYPE_AUDIO) {
371     name = g_strdup_printf ("audio_%02u", mssdemux->n_audios++);
372     tmpl = gst_static_pad_template_get (&gst_mss_demux_audiosrc_template);
373   }
374 
375   if (tmpl != NULL) {
376     srcpad = GST_PAD_CAST (gst_pad_new_from_template (tmpl, name));
377     g_free (name);
378     gst_object_unref (tmpl);
379   }
380   if (!srcpad) {
381     GST_WARNING_OBJECT (mssdemux, "Ignoring unknown type stream");
382     return NULL;
383   }
384 
385   return srcpad;
386 }
387 
388 static void
gst_mss_demux_apply_protection_system(GstCaps * caps,const gchar * selected_system)389 gst_mss_demux_apply_protection_system (GstCaps * caps,
390     const gchar * selected_system)
391 {
392   GstStructure *s;
393 
394   g_return_if_fail (selected_system);
395   s = gst_caps_get_structure (caps, 0);
396   gst_structure_set (s,
397       "original-media-type", G_TYPE_STRING, gst_structure_get_name (s),
398       GST_PROTECTION_SYSTEM_ID_CAPS_FIELD, G_TYPE_STRING, selected_system,
399       NULL);
400   gst_structure_set_name (s, "application/x-cenc");
401 
402 }
403 
404 static gboolean
gst_mss_demux_setup_streams(GstAdaptiveDemux * demux)405 gst_mss_demux_setup_streams (GstAdaptiveDemux * demux)
406 {
407   GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
408   GSList *streams = gst_mss_manifest_get_streams (mssdemux->manifest);
409   GSList *active_streams = NULL;
410   GSList *iter;
411   const gchar *protection_system_id =
412       gst_mss_manifest_get_protection_system_id (mssdemux->manifest);
413   const gchar *protection_data =
414       gst_mss_manifest_get_protection_data (mssdemux->manifest);
415   gboolean protected = protection_system_id && protection_data;
416   const gchar *selected_system = NULL;
417 
418   if (streams == NULL) {
419     GST_INFO_OBJECT (mssdemux, "No streams found in the manifest");
420     GST_ELEMENT_ERROR (mssdemux, STREAM, DEMUX,
421         (_("This file contains no playable streams.")),
422         ("no streams found at the Manifest"));
423     return FALSE;
424   }
425 
426   if (protected) {
427     const gchar *sys_ids[2] = { protection_system_id, NULL };
428 
429     selected_system = gst_protection_select_system (sys_ids);
430     if (!selected_system) {
431       GST_ERROR_OBJECT (mssdemux, "stream is protected, but no "
432           "suitable decryptor element has been found");
433       return FALSE;
434     }
435   }
436 
437   GST_INFO_OBJECT (mssdemux, "Changing max bitrate to %u",
438       demux->connection_speed);
439   gst_mss_manifest_change_bitrate (mssdemux->manifest, demux->connection_speed);
440 
441   GST_INFO_OBJECT (mssdemux, "Activating streams");
442 
443   for (iter = streams; iter; iter = g_slist_next (iter)) {
444     GstPad *srcpad = NULL;
445     GstMssDemuxStream *stream = NULL;
446     GstMssStream *manifeststream = iter->data;
447 
448     srcpad = _create_pad (mssdemux, manifeststream);
449 
450     if (!srcpad) {
451       continue;
452     }
453 
454     stream = (GstMssDemuxStream *)
455         gst_adaptive_demux_stream_new (GST_ADAPTIVE_DEMUX_CAST (mssdemux),
456         srcpad);
457     stream->manifest_stream = manifeststream;
458     gst_mss_stream_set_active (manifeststream, TRUE);
459     active_streams = g_slist_prepend (active_streams, stream);
460   }
461 
462   GST_INFO_OBJECT (mssdemux, "Changing max bitrate to %u",
463       demux->connection_speed);
464   gst_mss_manifest_change_bitrate (mssdemux->manifest, demux->connection_speed);
465 
466   for (iter = active_streams; iter; iter = g_slist_next (iter)) {
467     GstMssDemuxStream *stream = iter->data;
468     GstCaps *caps;
469     const gchar *lang;
470 
471     caps = gst_mss_stream_get_caps (stream->manifest_stream);
472 
473     if (protected) {
474       gst_mss_demux_apply_protection_system (caps, selected_system);
475     }
476 
477     gst_adaptive_demux_stream_set_caps (GST_ADAPTIVE_DEMUX_STREAM_CAST (stream),
478         create_mss_caps (stream, caps));
479     gst_caps_unref (caps);
480 
481     lang = gst_mss_stream_get_lang (stream->manifest_stream);
482     if (lang != NULL) {
483       GstTagList *tags;
484 
485       tags = gst_tag_list_new (GST_TAG_LANGUAGE_CODE, lang, NULL);
486       gst_adaptive_demux_stream_set_tags (GST_ADAPTIVE_DEMUX_STREAM_CAST
487           (stream), tags);
488     }
489 
490     if (protected) {
491       GstBuffer *protection_buffer =
492           gst_buffer_new_wrapped (g_strdup (protection_data),
493           strlen (protection_data));
494       GstEvent *event =
495           gst_event_new_protection (protection_system_id, protection_buffer,
496           "smooth-streaming");
497 
498       GST_LOG_OBJECT (stream, "Queueing Protection event on source pad");
499       gst_adaptive_demux_stream_queue_event ((GstAdaptiveDemuxStream *) stream,
500           event);
501       gst_buffer_unref (protection_buffer);
502     }
503   }
504 
505   g_slist_free (active_streams);
506   return TRUE;
507 }
508 
509 static void
gst_mss_demux_update_base_url(GstMssDemux * mssdemux)510 gst_mss_demux_update_base_url (GstMssDemux * mssdemux)
511 {
512   GstAdaptiveDemux *demux = GST_ADAPTIVE_DEMUX_CAST (mssdemux);
513   gchar *baseurl_end;
514 
515   g_free (mssdemux->base_url);
516 
517   mssdemux->base_url =
518       g_strdup (demux->manifest_base_uri ? demux->manifest_base_uri : demux->
519       manifest_uri);
520   baseurl_end = g_strrstr (mssdemux->base_url, "/Manifest");
521   if (baseurl_end == NULL) {
522     /* second try */
523     baseurl_end = g_strrstr (mssdemux->base_url, "/manifest");
524   }
525   if (baseurl_end) {
526     /* set the new end of the string */
527     baseurl_end[0] = '\0';
528   } else {
529     GST_WARNING_OBJECT (mssdemux, "Stream's URI didn't end with /manifest");
530   }
531 
532 }
533 
534 static gboolean
gst_mss_demux_process_manifest(GstAdaptiveDemux * demux,GstBuffer * buf)535 gst_mss_demux_process_manifest (GstAdaptiveDemux * demux, GstBuffer * buf)
536 {
537   GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
538 
539   gst_mss_demux_update_base_url (mssdemux);
540 
541   mssdemux->manifest = gst_mss_manifest_new (buf);
542   if (!mssdemux->manifest) {
543     GST_ELEMENT_ERROR (mssdemux, STREAM, FORMAT, ("Bad manifest file"),
544         ("Xml manifest file couldn't be parsed"));
545     return FALSE;
546   }
547   return gst_mss_demux_setup_streams (demux);
548 }
549 
550 static gboolean
gst_mss_demux_stream_select_bitrate(GstAdaptiveDemuxStream * stream,guint64 bitrate)551 gst_mss_demux_stream_select_bitrate (GstAdaptiveDemuxStream * stream,
552     guint64 bitrate)
553 {
554   GstMssDemuxStream *mssstream = (GstMssDemuxStream *) stream;
555   gboolean ret = FALSE;
556 
557   GST_DEBUG_OBJECT (stream->pad,
558       "Using stream download bitrate %" G_GUINT64_FORMAT, bitrate);
559 
560   if (gst_mss_stream_select_bitrate (mssstream->manifest_stream,
561           bitrate / MAX (1.0, ABS (stream->demux->segment.rate)))) {
562     GstCaps *caps;
563     GstCaps *msscaps;
564     GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (stream->demux);
565     const gchar *protection_system_id =
566         gst_mss_manifest_get_protection_system_id (mssdemux->manifest);
567     const gchar *protection_data =
568         gst_mss_manifest_get_protection_data (mssdemux->manifest);
569     gboolean protected = protection_system_id && protection_data;
570 
571     caps = gst_mss_stream_get_caps (mssstream->manifest_stream);
572 
573     GST_DEBUG_OBJECT (stream->pad,
574         "Starting streams reconfiguration due to bitrate changes");
575 
576     if (protected) {
577       const gchar *sys_ids[2] = { protection_system_id, NULL };
578       const gchar *selected_system = gst_protection_select_system (sys_ids);
579 
580       if (!selected_system) {
581         GST_ERROR_OBJECT (mssdemux, "stream is protected, but no "
582             "suitable decryptor element has been found");
583         gst_caps_unref (caps);
584         return FALSE;
585       }
586 
587       gst_mss_demux_apply_protection_system (caps, selected_system);
588     }
589 
590     msscaps = create_mss_caps (mssstream, caps);
591 
592     GST_DEBUG_OBJECT (stream->pad,
593         "Stream changed bitrate to %" G_GUINT64_FORMAT " caps: %"
594         GST_PTR_FORMAT,
595         gst_mss_stream_get_current_bitrate (mssstream->manifest_stream), caps);
596 
597     gst_caps_unref (caps);
598 
599     gst_adaptive_demux_stream_set_caps (stream, msscaps);
600     ret = TRUE;
601     GST_DEBUG_OBJECT (stream->pad, "Finished streams reconfiguration");
602   }
603   return ret;
604 }
605 
606 #define SEEK_UPDATES_PLAY_POSITION(r, start_type, stop_type) \
607   ((r >= 0 && start_type != GST_SEEK_TYPE_NONE) || \
608    (r < 0 && stop_type != GST_SEEK_TYPE_NONE))
609 
610 static gboolean
gst_mss_demux_seek(GstAdaptiveDemux * demux,GstEvent * seek)611 gst_mss_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek)
612 {
613   gdouble rate;
614   GstFormat format;
615   GstSeekFlags flags;
616   GstSeekType start_type, stop_type;
617   gint64 start, stop;
618   GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
619 
620   gst_event_parse_seek (seek, &rate, &format, &flags, &start_type, &start,
621       &stop_type, &stop);
622 
623   GST_DEBUG_OBJECT (mssdemux,
624       "seek event, rate: %f start: %" GST_TIME_FORMAT " stop: %"
625       GST_TIME_FORMAT, rate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
626 
627   if (SEEK_UPDATES_PLAY_POSITION (rate, start_type, stop_type)) {
628     if (rate >= 0)
629       gst_mss_manifest_seek (mssdemux->manifest, rate >= 0, start);
630     else
631       gst_mss_manifest_seek (mssdemux->manifest, rate >= 0, stop);
632   }
633 
634   return TRUE;
635 }
636 
637 static gboolean
gst_mss_demux_stream_has_next_fragment(GstAdaptiveDemuxStream * stream)638 gst_mss_demux_stream_has_next_fragment (GstAdaptiveDemuxStream * stream)
639 {
640   GstMssDemuxStream *mssstream = (GstMssDemuxStream *) stream;
641 
642   return gst_mss_stream_has_next_fragment (mssstream->manifest_stream);
643 }
644 
645 static gint64
gst_mss_demux_get_manifest_update_interval(GstAdaptiveDemux * demux)646 gst_mss_demux_get_manifest_update_interval (GstAdaptiveDemux * demux)
647 {
648   GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
649   GstClockTime interval;
650 
651   /* Not much information about this in the MSS spec. It seems that
652    * the fragments contain an UUID box that should tell the next
653    * fragments time and duration so one wouldn't need to fetch
654    * the Manifest again, but we need a fallback here. So use 2 times
655    * the current fragment duration */
656 
657   interval = gst_mss_manifest_get_min_fragment_duration (mssdemux->manifest);
658   if (!GST_CLOCK_TIME_IS_VALID (interval))
659     interval = 2 * GST_SECOND;  /* default to 2 seconds */
660 
661   interval = 2 * (interval / GST_USECOND);
662 
663   return interval;
664 }
665 
666 static gint64
gst_mss_demux_stream_get_fragment_waiting_time(GstAdaptiveDemuxStream * stream)667 gst_mss_demux_stream_get_fragment_waiting_time (GstAdaptiveDemuxStream * stream)
668 {
669   /* Wait a second for live streams so we don't try premature fragments downloading */
670   return GST_SECOND;
671 }
672 
673 static GstFlowReturn
gst_mss_demux_update_manifest_data(GstAdaptiveDemux * demux,GstBuffer * buffer)674 gst_mss_demux_update_manifest_data (GstAdaptiveDemux * demux,
675     GstBuffer * buffer)
676 {
677   GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
678 
679   gst_mss_demux_update_base_url (mssdemux);
680 
681   gst_mss_manifest_reload_fragments (mssdemux->manifest, buffer);
682   return GST_FLOW_OK;
683 }
684 
685 static gboolean
gst_mss_demux_get_live_seek_range(GstAdaptiveDemux * demux,gint64 * start,gint64 * stop)686 gst_mss_demux_get_live_seek_range (GstAdaptiveDemux * demux, gint64 * start,
687     gint64 * stop)
688 {
689   GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
690 
691   return gst_mss_manifest_get_live_seek_range (mssdemux->manifest, start, stop);
692 }
693 
694 static GstFlowReturn
gst_mss_demux_data_received(GstAdaptiveDemux * demux,GstAdaptiveDemuxStream * stream,GstBuffer * buffer)695 gst_mss_demux_data_received (GstAdaptiveDemux * demux,
696     GstAdaptiveDemuxStream * stream, GstBuffer * buffer)
697 {
698   GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
699   GstMssDemuxStream *mssstream = (GstMssDemuxStream *) stream;
700   gsize available;
701 
702   if (!gst_mss_manifest_is_live (mssdemux->manifest)) {
703     return GST_ADAPTIVE_DEMUX_CLASS (parent_class)->data_received (demux,
704         stream, buffer);
705   }
706 
707   if (gst_mss_stream_fragment_parsing_needed (mssstream->manifest_stream)) {
708     gst_mss_manifest_live_adapter_push (mssstream->manifest_stream, buffer);
709     available =
710         gst_mss_manifest_live_adapter_available (mssstream->manifest_stream);
711     // FIXME: try to reduce this minimal size.
712     if (available < 4096) {
713       return GST_FLOW_OK;
714     } else {
715       GST_LOG_OBJECT (stream->pad, "enough data, parsing fragment.");
716       buffer =
717           gst_mss_manifest_live_adapter_take_buffer (mssstream->manifest_stream,
718           available);
719       gst_mss_stream_parse_fragment (mssstream->manifest_stream, buffer);
720     }
721   }
722 
723   return GST_ADAPTIVE_DEMUX_CLASS (parent_class)->data_received (demux, stream,
724       buffer);
725 }
726 
727 static gboolean
gst_mss_demux_requires_periodical_playlist_update(GstAdaptiveDemux * demux)728 gst_mss_demux_requires_periodical_playlist_update (GstAdaptiveDemux * demux)
729 {
730   return TRUE;
731 }
732