• 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 GST_ELEMENT_REGISTER_DEFINE (mssdemux, "mssdemux",
113     GST_RANK_PRIMARY, GST_TYPE_MSS_DEMUX);
114 static void gst_mss_demux_dispose (GObject * object);
115 static void gst_mss_demux_set_property (GObject * object, guint prop_id,
116     const GValue * value, GParamSpec * pspec);
117 static void gst_mss_demux_get_property (GObject * object, guint prop_id,
118     GValue * value, GParamSpec * pspec);
119 
120 static gboolean gst_mss_demux_is_live (GstAdaptiveDemux * demux);
121 static gboolean gst_mss_demux_process_manifest (GstAdaptiveDemux * demux,
122     GstBuffer * buffer);
123 static GstClockTime gst_mss_demux_get_duration (GstAdaptiveDemux * demux);
124 static void gst_mss_demux_reset (GstAdaptiveDemux * demux);
125 static GstFlowReturn gst_mss_demux_stream_seek (GstAdaptiveDemuxStream * stream,
126     gboolean forward, GstSeekFlags flags, GstClockTime ts,
127     GstClockTime * final_ts);
128 static gboolean gst_mss_demux_stream_has_next_fragment (GstAdaptiveDemuxStream *
129     stream);
130 static GstFlowReturn
131 gst_mss_demux_stream_advance_fragment (GstAdaptiveDemuxStream * stream);
132 static gboolean gst_mss_demux_stream_select_bitrate (GstAdaptiveDemuxStream *
133     stream, guint64 bitrate);
134 static GstFlowReturn
135 gst_mss_demux_stream_update_fragment_info (GstAdaptiveDemuxStream * stream);
136 static gboolean gst_mss_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek);
137 static gint64
138 gst_mss_demux_get_manifest_update_interval (GstAdaptiveDemux * demux);
139 static gint64
140 gst_mss_demux_stream_get_fragment_waiting_time (GstAdaptiveDemuxStream *
141     stream);
142 static GstFlowReturn
143 gst_mss_demux_update_manifest_data (GstAdaptiveDemux * demux,
144     GstBuffer * buffer);
145 static gboolean gst_mss_demux_get_live_seek_range (GstAdaptiveDemux * demux,
146     gint64 * start, gint64 * stop);
147 static GstFlowReturn gst_mss_demux_data_received (GstAdaptiveDemux * demux,
148     GstAdaptiveDemuxStream * stream, GstBuffer * buffer);
149 static gboolean
150 gst_mss_demux_requires_periodical_playlist_update (GstAdaptiveDemux * demux);
151 
152 static void
gst_mss_demux_class_init(GstMssDemuxClass * klass)153 gst_mss_demux_class_init (GstMssDemuxClass * klass)
154 {
155   GObjectClass *gobject_class;
156   GstElementClass *gstelement_class;
157   GstAdaptiveDemuxClass *gstadaptivedemux_class;
158 
159   gobject_class = (GObjectClass *) klass;
160   gstelement_class = (GstElementClass *) klass;
161   gstadaptivedemux_class = (GstAdaptiveDemuxClass *) klass;
162 
163   gst_element_class_add_static_pad_template (gstelement_class,
164       &gst_mss_demux_sink_template);
165   gst_element_class_add_static_pad_template (gstelement_class,
166       &gst_mss_demux_videosrc_template);
167   gst_element_class_add_static_pad_template (gstelement_class,
168       &gst_mss_demux_audiosrc_template);
169   gst_element_class_set_static_metadata (gstelement_class,
170       "Smooth Streaming demuxer", "Codec/Demuxer/Adaptive",
171       "Parse and demultiplex a Smooth Streaming manifest into audio and video "
172       "streams", "Thiago Santos <thiago.sousa.santos@collabora.com>");
173 
174   gobject_class->dispose = gst_mss_demux_dispose;
175   gobject_class->set_property = gst_mss_demux_set_property;
176   gobject_class->get_property = gst_mss_demux_get_property;
177 
178 #ifndef GST_REMOVE_DEPRECATED
179   g_object_class_install_property (gobject_class, PROP_MAX_QUEUE_SIZE_BUFFERS,
180       g_param_spec_uint ("max-queue-size-buffers", "Max queue size in buffers",
181           "Maximum buffers that can be stored in each internal stream queue "
182           "(0 = infinite) (deprecated)", 0, G_MAXUINT,
183           DEFAULT_MAX_QUEUE_SIZE_BUFFERS,
184           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED));
185 #endif
186 
187   gstadaptivedemux_class->process_manifest = gst_mss_demux_process_manifest;
188   gstadaptivedemux_class->is_live = gst_mss_demux_is_live;
189   gstadaptivedemux_class->get_duration = gst_mss_demux_get_duration;
190   gstadaptivedemux_class->get_manifest_update_interval =
191       gst_mss_demux_get_manifest_update_interval;
192   gstadaptivedemux_class->reset = gst_mss_demux_reset;
193   gstadaptivedemux_class->seek = gst_mss_demux_seek;
194   gstadaptivedemux_class->stream_seek = gst_mss_demux_stream_seek;
195   gstadaptivedemux_class->stream_advance_fragment =
196       gst_mss_demux_stream_advance_fragment;
197   gstadaptivedemux_class->stream_has_next_fragment =
198       gst_mss_demux_stream_has_next_fragment;
199   gstadaptivedemux_class->stream_select_bitrate =
200       gst_mss_demux_stream_select_bitrate;
201   gstadaptivedemux_class->stream_update_fragment_info =
202       gst_mss_demux_stream_update_fragment_info;
203   gstadaptivedemux_class->stream_get_fragment_waiting_time =
204       gst_mss_demux_stream_get_fragment_waiting_time;
205   gstadaptivedemux_class->update_manifest_data =
206       gst_mss_demux_update_manifest_data;
207   gstadaptivedemux_class->get_live_seek_range =
208       gst_mss_demux_get_live_seek_range;
209   gstadaptivedemux_class->data_received = gst_mss_demux_data_received;
210   gstadaptivedemux_class->requires_periodical_playlist_update =
211       gst_mss_demux_requires_periodical_playlist_update;
212 
213   GST_DEBUG_CATEGORY_INIT (mssdemux_debug, "mssdemux", 0, "mssdemux plugin");
214 }
215 
216 static void
gst_mss_demux_init(GstMssDemux * mssdemux)217 gst_mss_demux_init (GstMssDemux * mssdemux)
218 {
219   mssdemux->data_queue_max_size = DEFAULT_MAX_QUEUE_SIZE_BUFFERS;
220 
221   gst_adaptive_demux_set_stream_struct_size (GST_ADAPTIVE_DEMUX_CAST (mssdemux),
222       sizeof (GstMssDemuxStream));
223 }
224 
225 static void
gst_mss_demux_reset(GstAdaptiveDemux * demux)226 gst_mss_demux_reset (GstAdaptiveDemux * demux)
227 {
228   GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
229 
230   if (mssdemux->manifest) {
231     gst_mss_manifest_free (mssdemux->manifest);
232     mssdemux->manifest = NULL;
233   }
234   g_free (mssdemux->base_url);
235   mssdemux->base_url = NULL;
236 
237   mssdemux->n_videos = mssdemux->n_audios = 0;
238 
239 }
240 
241 static void
gst_mss_demux_dispose(GObject * object)242 gst_mss_demux_dispose (GObject * object)
243 {
244   gst_mss_demux_reset (GST_ADAPTIVE_DEMUX_CAST (object));
245 
246   G_OBJECT_CLASS (parent_class)->dispose (object);
247 }
248 
249 static void
gst_mss_demux_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)250 gst_mss_demux_set_property (GObject * object, guint prop_id,
251     const GValue * value, GParamSpec * pspec)
252 {
253   GstMssDemux *mssdemux = GST_MSS_DEMUX (object);
254 
255   switch (prop_id) {
256     case PROP_MAX_QUEUE_SIZE_BUFFERS:
257       mssdemux->data_queue_max_size = g_value_get_uint (value);
258       break;
259     default:
260       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
261       break;
262   }
263 }
264 
265 static void
gst_mss_demux_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)266 gst_mss_demux_get_property (GObject * object, guint prop_id, GValue * value,
267     GParamSpec * pspec)
268 {
269   GstMssDemux *mssdemux = GST_MSS_DEMUX (object);
270 
271   switch (prop_id) {
272     case PROP_MAX_QUEUE_SIZE_BUFFERS:
273       g_value_set_uint (value, mssdemux->data_queue_max_size);
274       break;
275     default:
276       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
277       break;
278   }
279 }
280 
281 static gboolean
gst_mss_demux_is_live(GstAdaptiveDemux * demux)282 gst_mss_demux_is_live (GstAdaptiveDemux * demux)
283 {
284   GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
285 
286   g_return_val_if_fail (mssdemux->manifest != NULL, FALSE);
287 
288   return gst_mss_manifest_is_live (mssdemux->manifest);
289 }
290 
291 static GstClockTime
gst_mss_demux_get_duration(GstAdaptiveDemux * demux)292 gst_mss_demux_get_duration (GstAdaptiveDemux * demux)
293 {
294   GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
295 
296   g_return_val_if_fail (mssdemux->manifest != NULL, FALSE);
297 
298   return gst_mss_manifest_get_gst_duration (mssdemux->manifest);
299 }
300 
301 static GstFlowReturn
gst_mss_demux_stream_update_fragment_info(GstAdaptiveDemuxStream * stream)302 gst_mss_demux_stream_update_fragment_info (GstAdaptiveDemuxStream * stream)
303 {
304   GstMssDemuxStream *mssstream = (GstMssDemuxStream *) stream;
305   GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (stream->demux);
306   GstFlowReturn ret;
307   gchar *path = NULL;
308 
309   gst_adaptive_demux_stream_fragment_clear (&stream->fragment);
310   ret = gst_mss_stream_get_fragment_url (mssstream->manifest_stream, &path);
311 
312   if (ret == GST_FLOW_OK) {
313     stream->fragment.uri = g_strdup_printf ("%s/%s", mssdemux->base_url, path);
314     stream->fragment.timestamp =
315         gst_mss_stream_get_fragment_gst_timestamp (mssstream->manifest_stream);
316     stream->fragment.duration =
317         gst_mss_stream_get_fragment_gst_duration (mssstream->manifest_stream);
318   }
319   g_free (path);
320 
321   return ret;
322 }
323 
324 static GstFlowReturn
gst_mss_demux_stream_seek(GstAdaptiveDemuxStream * stream,gboolean forward,GstSeekFlags flags,GstClockTime ts,GstClockTime * final_ts)325 gst_mss_demux_stream_seek (GstAdaptiveDemuxStream * stream, gboolean forward,
326     GstSeekFlags flags, GstClockTime ts, GstClockTime * final_ts)
327 {
328   GstMssDemuxStream *mssstream = (GstMssDemuxStream *) stream;
329 
330   gst_mss_stream_seek (mssstream->manifest_stream, forward, flags, ts,
331       final_ts);
332   return GST_FLOW_OK;
333 }
334 
335 static GstFlowReturn
gst_mss_demux_stream_advance_fragment(GstAdaptiveDemuxStream * stream)336 gst_mss_demux_stream_advance_fragment (GstAdaptiveDemuxStream * stream)
337 {
338   GstMssDemuxStream *mssstream = (GstMssDemuxStream *) stream;
339 
340   if (stream->demux->segment.rate >= 0)
341     return gst_mss_stream_advance_fragment (mssstream->manifest_stream);
342   else
343     return gst_mss_stream_regress_fragment (mssstream->manifest_stream);
344 }
345 
346 static GstCaps *
create_mss_caps(GstMssDemuxStream * stream,GstCaps * caps)347 create_mss_caps (GstMssDemuxStream * stream, GstCaps * caps)
348 {
349   return gst_caps_new_simple ("video/quicktime", "variant", G_TYPE_STRING,
350       "mss-fragmented", "timescale", G_TYPE_UINT64,
351       gst_mss_stream_get_timescale (stream->manifest_stream), "media-caps",
352       GST_TYPE_CAPS, caps, NULL);
353 }
354 
355 static GstPad *
_create_pad(GstMssDemux * mssdemux,GstMssStream * manifeststream)356 _create_pad (GstMssDemux * mssdemux, GstMssStream * manifeststream)
357 {
358   gchar *name = NULL;
359   GstPad *srcpad = NULL;
360   GstMssStreamType streamtype;
361   GstPadTemplate *tmpl = NULL;
362   GstCaps *caps = NULL;
363 
364   caps = gst_mss_stream_get_caps (manifeststream);
365 
366   if (!caps) {
367     GST_WARNING_OBJECT (mssdemux, "not creating pad for unrecognized stream");
368     return NULL;
369   }
370 
371   streamtype = gst_mss_stream_get_type (manifeststream);
372   GST_DEBUG_OBJECT (mssdemux, "Found stream of type: %s",
373       gst_mss_stream_type_name (streamtype));
374 
375   /* TODO use stream's name/bitrate/index as the pad name? */
376   if (streamtype == MSS_STREAM_TYPE_VIDEO) {
377     name = g_strdup_printf ("video_%02u", mssdemux->n_videos++);
378     tmpl = gst_static_pad_template_get (&gst_mss_demux_videosrc_template);
379   } else if (streamtype == MSS_STREAM_TYPE_AUDIO) {
380     name = g_strdup_printf ("audio_%02u", mssdemux->n_audios++);
381     tmpl = gst_static_pad_template_get (&gst_mss_demux_audiosrc_template);
382   }
383 
384   if (tmpl != NULL) {
385     srcpad = GST_PAD_CAST (gst_pad_new_from_template (tmpl, name));
386     g_free (name);
387     gst_object_unref (tmpl);
388   }
389   if (!srcpad) {
390     GST_WARNING_OBJECT (mssdemux, "Ignoring unknown type stream");
391     return NULL;
392   }
393 
394   return srcpad;
395 }
396 
397 static void
gst_mss_demux_apply_protection_system(GstCaps * caps,const gchar * selected_system)398 gst_mss_demux_apply_protection_system (GstCaps * caps,
399     const gchar * selected_system)
400 {
401   GstStructure *s;
402 
403   g_return_if_fail (selected_system);
404   s = gst_caps_get_structure (caps, 0);
405   gst_structure_set (s,
406       "original-media-type", G_TYPE_STRING, gst_structure_get_name (s),
407       GST_PROTECTION_SYSTEM_ID_CAPS_FIELD, G_TYPE_STRING, selected_system,
408       NULL);
409   gst_structure_set_name (s, "application/x-cenc");
410 
411 }
412 
413 static gboolean
gst_mss_demux_setup_streams(GstAdaptiveDemux * demux)414 gst_mss_demux_setup_streams (GstAdaptiveDemux * demux)
415 {
416   GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
417   GSList *streams = gst_mss_manifest_get_streams (mssdemux->manifest);
418   GSList *active_streams = NULL;
419   GSList *iter;
420   const gchar *protection_system_id =
421       gst_mss_manifest_get_protection_system_id (mssdemux->manifest);
422   const gchar *protection_data =
423       gst_mss_manifest_get_protection_data (mssdemux->manifest);
424   gboolean protected = protection_system_id && protection_data;
425   const gchar *selected_system = NULL;
426 
427   if (streams == NULL) {
428     GST_INFO_OBJECT (mssdemux, "No streams found in the manifest");
429     GST_ELEMENT_ERROR (mssdemux, STREAM, DEMUX,
430         (_("This file contains no playable streams.")),
431         ("no streams found at the Manifest"));
432     return FALSE;
433   }
434 
435   if (protected) {
436     const gchar *sys_ids[2] = { protection_system_id, NULL };
437 
438     selected_system = gst_protection_select_system (sys_ids);
439     if (!selected_system) {
440       GST_ERROR_OBJECT (mssdemux, "stream is protected, but no "
441           "suitable decryptor element has been found");
442       return FALSE;
443     }
444   }
445 
446   GST_INFO_OBJECT (mssdemux, "Changing max bitrate to %u",
447       demux->connection_speed);
448   gst_mss_manifest_change_bitrate (mssdemux->manifest, demux->connection_speed);
449 
450   GST_INFO_OBJECT (mssdemux, "Activating streams");
451 
452   for (iter = streams; iter; iter = g_slist_next (iter)) {
453     GstPad *srcpad = NULL;
454     GstMssDemuxStream *stream = NULL;
455     GstMssStream *manifeststream = iter->data;
456 
457     srcpad = _create_pad (mssdemux, manifeststream);
458 
459     if (!srcpad) {
460       continue;
461     }
462 
463     stream = (GstMssDemuxStream *)
464         gst_adaptive_demux_stream_new (GST_ADAPTIVE_DEMUX_CAST (mssdemux),
465         srcpad);
466     stream->manifest_stream = manifeststream;
467     gst_mss_stream_set_active (manifeststream, TRUE);
468     active_streams = g_slist_prepend (active_streams, stream);
469   }
470 
471   GST_INFO_OBJECT (mssdemux, "Changing max bitrate to %u",
472       demux->connection_speed);
473   gst_mss_manifest_change_bitrate (mssdemux->manifest, demux->connection_speed);
474 
475   for (iter = active_streams; iter; iter = g_slist_next (iter)) {
476     GstMssDemuxStream *stream = iter->data;
477     GstCaps *caps;
478     const gchar *lang;
479 
480     caps = gst_mss_stream_get_caps (stream->manifest_stream);
481 
482     if (protected) {
483       gst_mss_demux_apply_protection_system (caps, selected_system);
484     }
485 
486     gst_adaptive_demux_stream_set_caps (GST_ADAPTIVE_DEMUX_STREAM_CAST (stream),
487         create_mss_caps (stream, caps));
488     gst_caps_unref (caps);
489 
490     lang = gst_mss_stream_get_lang (stream->manifest_stream);
491     if (lang != NULL) {
492       GstTagList *tags;
493 
494       tags = gst_tag_list_new (GST_TAG_LANGUAGE_CODE, lang, NULL);
495       gst_adaptive_demux_stream_set_tags (GST_ADAPTIVE_DEMUX_STREAM_CAST
496           (stream), tags);
497     }
498 
499     if (protected) {
500       GstBuffer *protection_buffer =
501           gst_buffer_new_wrapped (g_strdup (protection_data),
502           strlen (protection_data));
503       GstEvent *event =
504           gst_event_new_protection (protection_system_id, protection_buffer,
505           "smooth-streaming");
506 
507       GST_LOG_OBJECT (stream, "Queueing Protection event on source pad");
508       gst_adaptive_demux_stream_queue_event ((GstAdaptiveDemuxStream *) stream,
509           event);
510       gst_buffer_unref (protection_buffer);
511     }
512   }
513 
514   g_slist_free (active_streams);
515   return TRUE;
516 }
517 
518 static void
gst_mss_demux_update_base_url(GstMssDemux * mssdemux)519 gst_mss_demux_update_base_url (GstMssDemux * mssdemux)
520 {
521   GstAdaptiveDemux *demux = GST_ADAPTIVE_DEMUX_CAST (mssdemux);
522   gchar *baseurl_end;
523 
524   g_free (mssdemux->base_url);
525 
526   mssdemux->base_url =
527       g_strdup (demux->manifest_base_uri ? demux->manifest_base_uri : demux->
528       manifest_uri);
529   baseurl_end = g_strrstr (mssdemux->base_url, "/Manifest");
530   if (baseurl_end == NULL) {
531     /* second try */
532     baseurl_end = g_strrstr (mssdemux->base_url, "/manifest");
533   }
534   if (baseurl_end) {
535     /* set the new end of the string */
536     baseurl_end[0] = '\0';
537   } else {
538     GST_WARNING_OBJECT (mssdemux, "Stream's URI didn't end with /manifest");
539   }
540 
541 }
542 
543 static gboolean
gst_mss_demux_process_manifest(GstAdaptiveDemux * demux,GstBuffer * buf)544 gst_mss_demux_process_manifest (GstAdaptiveDemux * demux, GstBuffer * buf)
545 {
546   GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
547 
548   gst_mss_demux_update_base_url (mssdemux);
549 
550   mssdemux->manifest = gst_mss_manifest_new (buf);
551   if (!mssdemux->manifest) {
552     GST_ELEMENT_ERROR (mssdemux, STREAM, FORMAT, ("Bad manifest file"),
553         ("Xml manifest file couldn't be parsed"));
554     return FALSE;
555   }
556   return gst_mss_demux_setup_streams (demux);
557 }
558 
559 static gboolean
gst_mss_demux_stream_select_bitrate(GstAdaptiveDemuxStream * stream,guint64 bitrate)560 gst_mss_demux_stream_select_bitrate (GstAdaptiveDemuxStream * stream,
561     guint64 bitrate)
562 {
563   GstMssDemuxStream *mssstream = (GstMssDemuxStream *) stream;
564   gboolean ret = FALSE;
565 
566   GST_DEBUG_OBJECT (stream->pad,
567       "Using stream download bitrate %" G_GUINT64_FORMAT, bitrate);
568 
569   if (gst_mss_stream_select_bitrate (mssstream->manifest_stream,
570           bitrate / MAX (1.0, ABS (stream->demux->segment.rate)))) {
571     GstCaps *caps;
572     GstCaps *msscaps;
573     GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (stream->demux);
574     const gchar *protection_system_id =
575         gst_mss_manifest_get_protection_system_id (mssdemux->manifest);
576     const gchar *protection_data =
577         gst_mss_manifest_get_protection_data (mssdemux->manifest);
578     gboolean protected = protection_system_id && protection_data;
579 
580     caps = gst_mss_stream_get_caps (mssstream->manifest_stream);
581 
582     GST_DEBUG_OBJECT (stream->pad,
583         "Starting streams reconfiguration due to bitrate changes");
584 
585     if (protected) {
586       const gchar *sys_ids[2] = { protection_system_id, NULL };
587       const gchar *selected_system = gst_protection_select_system (sys_ids);
588 
589       if (!selected_system) {
590         GST_ERROR_OBJECT (mssdemux, "stream is protected, but no "
591             "suitable decryptor element has been found");
592         gst_caps_unref (caps);
593         return FALSE;
594       }
595 
596       gst_mss_demux_apply_protection_system (caps, selected_system);
597     }
598 
599     msscaps = create_mss_caps (mssstream, caps);
600 
601     GST_DEBUG_OBJECT (stream->pad,
602         "Stream changed bitrate to %" G_GUINT64_FORMAT " caps: %"
603         GST_PTR_FORMAT,
604         gst_mss_stream_get_current_bitrate (mssstream->manifest_stream), caps);
605 
606     gst_caps_unref (caps);
607 
608     gst_adaptive_demux_stream_set_caps (stream, msscaps);
609     ret = TRUE;
610     GST_DEBUG_OBJECT (stream->pad, "Finished streams reconfiguration");
611   }
612   return ret;
613 }
614 
615 #define SEEK_UPDATES_PLAY_POSITION(r, start_type, stop_type) \
616   ((r >= 0 && start_type != GST_SEEK_TYPE_NONE) || \
617    (r < 0 && stop_type != GST_SEEK_TYPE_NONE))
618 
619 static gboolean
gst_mss_demux_seek(GstAdaptiveDemux * demux,GstEvent * seek)620 gst_mss_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek)
621 {
622   gdouble rate;
623   GstFormat format;
624   GstSeekFlags flags;
625   GstSeekType start_type, stop_type;
626   gint64 start, stop;
627   GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
628 
629   gst_event_parse_seek (seek, &rate, &format, &flags, &start_type, &start,
630       &stop_type, &stop);
631 
632   GST_DEBUG_OBJECT (mssdemux,
633       "seek event, rate: %f start: %" GST_TIME_FORMAT " stop: %"
634       GST_TIME_FORMAT, rate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
635 
636   if (SEEK_UPDATES_PLAY_POSITION (rate, start_type, stop_type)) {
637     if (rate >= 0)
638       gst_mss_manifest_seek (mssdemux->manifest, rate >= 0, start);
639     else
640       gst_mss_manifest_seek (mssdemux->manifest, rate >= 0, stop);
641   }
642 
643   return TRUE;
644 }
645 
646 static gboolean
gst_mss_demux_stream_has_next_fragment(GstAdaptiveDemuxStream * stream)647 gst_mss_demux_stream_has_next_fragment (GstAdaptiveDemuxStream * stream)
648 {
649   GstMssDemuxStream *mssstream = (GstMssDemuxStream *) stream;
650 
651   return gst_mss_stream_has_next_fragment (mssstream->manifest_stream);
652 }
653 
654 static gint64
gst_mss_demux_get_manifest_update_interval(GstAdaptiveDemux * demux)655 gst_mss_demux_get_manifest_update_interval (GstAdaptiveDemux * demux)
656 {
657   GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
658   GstClockTime interval;
659 
660   /* Not much information about this in the MSS spec. It seems that
661    * the fragments contain an UUID box that should tell the next
662    * fragments time and duration so one wouldn't need to fetch
663    * the Manifest again, but we need a fallback here. So use 2 times
664    * the current fragment duration */
665 
666   interval = gst_mss_manifest_get_min_fragment_duration (mssdemux->manifest);
667   if (!GST_CLOCK_TIME_IS_VALID (interval))
668     interval = 2 * GST_SECOND;  /* default to 2 seconds */
669 
670   interval = 2 * (interval / GST_USECOND);
671 
672   return interval;
673 }
674 
675 static gint64
gst_mss_demux_stream_get_fragment_waiting_time(GstAdaptiveDemuxStream * stream)676 gst_mss_demux_stream_get_fragment_waiting_time (GstAdaptiveDemuxStream * stream)
677 {
678   /* Wait a second for live streams so we don't try premature fragments downloading */
679   return GST_SECOND;
680 }
681 
682 static GstFlowReturn
gst_mss_demux_update_manifest_data(GstAdaptiveDemux * demux,GstBuffer * buffer)683 gst_mss_demux_update_manifest_data (GstAdaptiveDemux * demux,
684     GstBuffer * buffer)
685 {
686   GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
687 
688   gst_mss_demux_update_base_url (mssdemux);
689 
690   gst_mss_manifest_reload_fragments (mssdemux->manifest, buffer);
691   return GST_FLOW_OK;
692 }
693 
694 static gboolean
gst_mss_demux_get_live_seek_range(GstAdaptiveDemux * demux,gint64 * start,gint64 * stop)695 gst_mss_demux_get_live_seek_range (GstAdaptiveDemux * demux, gint64 * start,
696     gint64 * stop)
697 {
698   GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
699 
700   return gst_mss_manifest_get_live_seek_range (mssdemux->manifest, start, stop);
701 }
702 
703 static GstFlowReturn
gst_mss_demux_data_received(GstAdaptiveDemux * demux,GstAdaptiveDemuxStream * stream,GstBuffer * buffer)704 gst_mss_demux_data_received (GstAdaptiveDemux * demux,
705     GstAdaptiveDemuxStream * stream, GstBuffer * buffer)
706 {
707   GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
708   GstMssDemuxStream *mssstream = (GstMssDemuxStream *) stream;
709   gsize available;
710 
711   if (!gst_mss_manifest_is_live (mssdemux->manifest)) {
712     return GST_ADAPTIVE_DEMUX_CLASS (parent_class)->data_received (demux,
713         stream, buffer);
714   }
715 
716   if (gst_mss_stream_fragment_parsing_needed (mssstream->manifest_stream)) {
717     gst_mss_manifest_live_adapter_push (mssstream->manifest_stream, buffer);
718     available =
719         gst_mss_manifest_live_adapter_available (mssstream->manifest_stream);
720     // FIXME: try to reduce this minimal size.
721     if (available < 4096) {
722       return GST_FLOW_OK;
723     } else {
724       GST_LOG_OBJECT (stream->pad, "enough data, parsing fragment.");
725       buffer =
726           gst_mss_manifest_live_adapter_take_buffer (mssstream->manifest_stream,
727           available);
728       gst_mss_stream_parse_fragment (mssstream->manifest_stream, buffer);
729     }
730   }
731 
732   return GST_ADAPTIVE_DEMUX_CLASS (parent_class)->data_received (demux, stream,
733       buffer);
734 }
735 
736 static gboolean
gst_mss_demux_requires_periodical_playlist_update(GstAdaptiveDemux * demux)737 gst_mss_demux_requires_periodical_playlist_update (GstAdaptiveDemux * demux)
738 {
739   return TRUE;
740 }
741