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