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