1 /* GStreamer
2 * Copyright (C) 2012 Smart TV Alliance
3 * Copyright (C) 2016 Igalia S.L
4 * Copyright (C) 2016 Metrological
5 * Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>, Collabora Ltd.
6 *
7 * gstmssmanifest.c:
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
23 */
24
25 #include <glib.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <ctype.h>
29 #include <libxml/parser.h>
30 #include <libxml/tree.h>
31
32 /* for parsing h264 codec data */
33 #include <gst/codecparsers/gsth264parser.h>
34
35 #include "gstmssmanifest.h"
36 #include "gstmssfragmentparser.h"
37
38 GST_DEBUG_CATEGORY_EXTERN (mssdemux_debug);
39 #define GST_CAT_DEFAULT mssdemux_debug
40
41 #define DEFAULT_TIMESCALE 10000000
42
43 #define MSS_NODE_STREAM_FRAGMENT "c"
44 #define MSS_NODE_STREAM_QUALITY "QualityLevel"
45
46 #define MSS_PROP_BITRATE "Bitrate"
47 #define MSS_PROP_DURATION "d"
48 #define MSS_PROP_DVR_WINDOW_LENGTH "DVRWindowLength"
49 #define MSS_PROP_LANGUAGE "Language"
50 #define MSS_PROP_NUMBER "n"
51 #define MSS_PROP_REPETITIONS "r"
52 #define MSS_PROP_STREAM_DURATION "Duration"
53 #define MSS_PROP_TIME "t"
54 #define MSS_PROP_TIMESCALE "TimeScale"
55 #define MSS_PROP_URL "Url"
56
57 #define GST_MSSMANIFEST_LIVE_MIN_FRAGMENT_DISTANCE 3
58
59 typedef struct _GstMssStreamFragment
60 {
61 guint number;
62 guint64 time;
63 guint64 duration;
64 guint repetitions;
65 } GstMssStreamFragment;
66
67 typedef struct _GstMssStreamQuality
68 {
69 xmlNodePtr xmlnode;
70
71 gchar *bitrate_str;
72 guint64 bitrate;
73 } GstMssStreamQuality;
74
75 struct _GstMssStream
76 {
77 xmlNodePtr xmlnode;
78
79 gboolean active; /* if the stream is currently being used */
80 gint selectedQualityIndex;
81
82 gboolean has_live_fragments;
83 GstAdapter *live_adapter;
84
85 GList *fragments;
86 GList *qualities;
87
88 gchar *url;
89 gchar *lang;
90
91 GstMssFragmentParser fragment_parser;
92
93 guint fragment_repetition_index;
94 GList *current_fragment;
95 GList *current_quality;
96
97 /* TODO move this to somewhere static */
98 GRegex *regex_bitrate;
99 GRegex *regex_position;
100 };
101
102 struct _GstMssManifest
103 {
104 xmlDocPtr xml;
105 xmlNodePtr xmlrootnode;
106
107 gboolean is_live;
108 gint64 dvr_window;
109 guint64 look_ahead_fragment_count;
110
111 GString *protection_system_id;
112 gchar *protection_data;
113
114 GSList *streams;
115 };
116
117 /* For parsing and building a fragments list */
118 typedef struct _GstMssFragmentListBuilder
119 {
120 GList *fragments;
121
122 GstMssStreamFragment *previous_fragment;
123 guint fragment_number;
124 guint64 fragment_time_accum;
125 } GstMssFragmentListBuilder;
126
127 static void
gst_mss_fragment_list_builder_init(GstMssFragmentListBuilder * builder)128 gst_mss_fragment_list_builder_init (GstMssFragmentListBuilder * builder)
129 {
130 builder->fragments = NULL;
131 builder->previous_fragment = NULL;
132 builder->fragment_time_accum = 0;
133 builder->fragment_number = 0;
134 }
135
136 static void
gst_mss_fragment_list_builder_add(GstMssFragmentListBuilder * builder,xmlNodePtr node)137 gst_mss_fragment_list_builder_add (GstMssFragmentListBuilder * builder,
138 xmlNodePtr node)
139 {
140 gchar *duration_str;
141 gchar *time_str;
142 gchar *seqnum_str;
143 gchar *repetition_str;
144 GstMssStreamFragment *fragment = g_new (GstMssStreamFragment, 1);
145
146 duration_str = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_DURATION);
147 time_str = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_TIME);
148 seqnum_str = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_NUMBER);
149 repetition_str =
150 (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_REPETITIONS);
151
152 /* use the node's seq number or use the previous + 1 */
153 if (seqnum_str) {
154 fragment->number = g_ascii_strtoull (seqnum_str, NULL, 10);
155 xmlFree (seqnum_str);
156 builder->fragment_number = fragment->number;
157 } else {
158 fragment->number = builder->fragment_number;
159 }
160 builder->fragment_number = fragment->number + 1;
161
162 if (repetition_str) {
163 fragment->repetitions = g_ascii_strtoull (repetition_str, NULL, 10);
164 xmlFree (repetition_str);
165 } else {
166 fragment->repetitions = 1;
167 }
168
169 if (time_str) {
170 fragment->time = g_ascii_strtoull (time_str, NULL, 10);
171
172 xmlFree (time_str);
173 builder->fragment_time_accum = fragment->time;
174 } else {
175 fragment->time = builder->fragment_time_accum;
176 }
177
178 /* if we have a previous fragment, means we need to set its duration */
179 if (builder->previous_fragment)
180 builder->previous_fragment->duration =
181 (fragment->time -
182 builder->previous_fragment->time) /
183 builder->previous_fragment->repetitions;
184
185 if (duration_str) {
186 fragment->duration = g_ascii_strtoull (duration_str, NULL, 10);
187
188 builder->previous_fragment = NULL;
189 builder->fragment_time_accum += fragment->duration * fragment->repetitions;
190 xmlFree (duration_str);
191 } else {
192 /* store to set the duration at the next iteration */
193 builder->previous_fragment = fragment;
194 }
195
196 /* we reverse it later */
197 builder->fragments = g_list_prepend (builder->fragments, fragment);
198 GST_LOG ("Adding fragment number: %u, time: %" G_GUINT64_FORMAT
199 ", duration: %" G_GUINT64_FORMAT ", repetitions: %u",
200 fragment->number, fragment->time, fragment->duration,
201 fragment->repetitions);
202 }
203
204 static GstBuffer *gst_buffer_from_hex_string (const gchar * s);
205
206 static gboolean
node_has_type(xmlNodePtr node,const gchar * name)207 node_has_type (xmlNodePtr node, const gchar * name)
208 {
209 return strcmp ((gchar *) node->name, name) == 0;
210 }
211
212 static GstMssStreamQuality *
gst_mss_stream_quality_new(xmlNodePtr node)213 gst_mss_stream_quality_new (xmlNodePtr node)
214 {
215 GstMssStreamQuality *q = g_slice_new (GstMssStreamQuality);
216
217 q->xmlnode = node;
218 q->bitrate_str = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_BITRATE);
219
220 if (q->bitrate_str != NULL)
221 q->bitrate = g_ascii_strtoull (q->bitrate_str, NULL, 10);
222 else
223 q->bitrate = 0;
224
225 return q;
226 }
227
228 static void
gst_mss_stream_quality_free(GstMssStreamQuality * quality)229 gst_mss_stream_quality_free (GstMssStreamQuality * quality)
230 {
231 g_return_if_fail (quality != NULL);
232
233 xmlFree (quality->bitrate_str);
234 g_slice_free (GstMssStreamQuality, quality);
235 }
236
237 static gint
compare_bitrate(GstMssStreamQuality * a,GstMssStreamQuality * b)238 compare_bitrate (GstMssStreamQuality * a, GstMssStreamQuality * b)
239 {
240 if (a->bitrate > b->bitrate)
241 return 1;
242 if (a->bitrate < b->bitrate)
243 return -1;
244 return 0;
245
246 }
247
248 static void
_gst_mss_stream_init(GstMssManifest * manifest,GstMssStream * stream,xmlNodePtr node)249 _gst_mss_stream_init (GstMssManifest * manifest, GstMssStream * stream,
250 xmlNodePtr node)
251 {
252 xmlNodePtr iter;
253 GstMssFragmentListBuilder builder;
254
255 gst_mss_fragment_list_builder_init (&builder);
256
257 stream->xmlnode = node;
258
259 /* get the base url path generator */
260 stream->url = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_URL);
261 stream->lang = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_LANGUAGE);
262
263 /* for live playback each fragment usually has timing
264 * information for the few next look-ahead fragments so the
265 * playlist can be built incrementally from the first fragment
266 * of the manifest.
267 */
268
269 GST_DEBUG ("Live stream: %s, look-ahead fragments: %" G_GUINT64_FORMAT,
270 manifest->is_live ? "yes" : "no", manifest->look_ahead_fragment_count);
271 stream->has_live_fragments = manifest->is_live
272 && manifest->look_ahead_fragment_count;
273
274 for (iter = node->children; iter; iter = iter->next) {
275 if (node_has_type (iter, MSS_NODE_STREAM_FRAGMENT)) {
276 gst_mss_fragment_list_builder_add (&builder, iter);
277 } else if (node_has_type (iter, MSS_NODE_STREAM_QUALITY)) {
278 GstMssStreamQuality *quality = gst_mss_stream_quality_new (iter);
279 stream->qualities = g_list_prepend (stream->qualities, quality);
280 } else {
281 /* TODO gst log this */
282 }
283 }
284
285 if (stream->has_live_fragments) {
286 stream->live_adapter = gst_adapter_new ();
287 }
288
289 if (builder.fragments) {
290 stream->fragments = g_list_reverse (builder.fragments);
291 if (manifest->is_live) {
292 GList *iter = g_list_last (stream->fragments);
293 gint i;
294
295 for (i = 0; i < GST_MSSMANIFEST_LIVE_MIN_FRAGMENT_DISTANCE; i++) {
296 if (g_list_previous (iter)) {
297 iter = g_list_previous (iter);
298 } else {
299 break;
300 }
301 }
302 stream->current_fragment = iter;
303 } else {
304 stream->current_fragment = stream->fragments;
305 }
306 }
307
308 /* order them from smaller to bigger based on bitrates */
309 stream->qualities =
310 g_list_sort (stream->qualities, (GCompareFunc) compare_bitrate);
311 stream->current_quality = stream->qualities;
312
313 stream->regex_bitrate = g_regex_new ("\\{[Bb]itrate\\}", 0, 0, NULL);
314 stream->regex_position = g_regex_new ("\\{start[ _]time\\}", 0, 0, NULL);
315
316 gst_mss_fragment_parser_init (&stream->fragment_parser);
317 }
318
319
320 static void
_gst_mss_parse_protection(GstMssManifest * manifest,xmlNodePtr protection_node)321 _gst_mss_parse_protection (GstMssManifest * manifest,
322 xmlNodePtr protection_node)
323 {
324 xmlNodePtr nodeiter;
325
326 for (nodeiter = protection_node->children; nodeiter;
327 nodeiter = nodeiter->next) {
328 if (nodeiter->type == XML_ELEMENT_NODE
329 && (strcmp ((const char *) nodeiter->name, "ProtectionHeader") == 0)) {
330 xmlChar *system_id_attribute =
331 xmlGetProp (nodeiter, (xmlChar *) "SystemID");
332 gchar *value = (gchar *) system_id_attribute;
333 int id_len = strlen (value);
334 GString *system_id;
335
336 if (value[0] == '{') {
337 value++;
338 id_len--;
339 }
340
341 system_id = g_string_new (value);
342 system_id = g_string_ascii_down (system_id);
343 if (value[id_len - 1] == '}')
344 system_id = g_string_truncate (system_id, id_len - 1);
345
346 manifest->protection_system_id = system_id;
347 manifest->protection_data = (gchar *) xmlNodeGetContent (nodeiter);
348 xmlFree (system_id_attribute);
349 break;
350 }
351 }
352 }
353
354 GstMssManifest *
gst_mss_manifest_new(GstBuffer * data)355 gst_mss_manifest_new (GstBuffer * data)
356 {
357 GstMssManifest *manifest;
358 xmlNodePtr root;
359 xmlNodePtr nodeiter;
360 gchar *live_str;
361 GstMapInfo mapinfo;
362 gchar *look_ahead_fragment_count_str;
363
364 if (!gst_buffer_map (data, &mapinfo, GST_MAP_READ)) {
365 return NULL;
366 }
367
368 manifest = g_malloc0 (sizeof (GstMssManifest));
369
370 manifest->xml = xmlReadMemory ((const gchar *) mapinfo.data,
371 mapinfo.size, "manifest", NULL, 0);
372 root = manifest->xmlrootnode = xmlDocGetRootElement (manifest->xml);
373 if (root == NULL) {
374 GST_WARNING ("No root node ! Invalid manifest");
375 gst_mss_manifest_free (manifest);
376 return NULL;
377 }
378
379 live_str = (gchar *) xmlGetProp (root, (xmlChar *) "IsLive");
380 if (live_str) {
381 manifest->is_live = g_ascii_strcasecmp (live_str, "true") == 0;
382 xmlFree (live_str);
383 }
384
385 /* the entire file is always available for non-live streams */
386 if (!manifest->is_live) {
387 manifest->dvr_window = 0;
388 manifest->look_ahead_fragment_count = 0;
389 } else {
390 /* if 0, or non-existent, the length is infinite */
391 gchar *dvr_window_str = (gchar *) xmlGetProp (root,
392 (xmlChar *) MSS_PROP_DVR_WINDOW_LENGTH);
393 if (dvr_window_str) {
394 manifest->dvr_window = g_ascii_strtoull (dvr_window_str, NULL, 10);
395 xmlFree (dvr_window_str);
396 if (manifest->dvr_window <= 0) {
397 manifest->dvr_window = 0;
398 }
399 }
400
401 look_ahead_fragment_count_str =
402 (gchar *) xmlGetProp (root, (xmlChar *) "LookAheadFragmentCount");
403 if (look_ahead_fragment_count_str) {
404 manifest->look_ahead_fragment_count =
405 g_ascii_strtoull (look_ahead_fragment_count_str, NULL, 10);
406 xmlFree (look_ahead_fragment_count_str);
407 if (manifest->look_ahead_fragment_count <= 0) {
408 manifest->look_ahead_fragment_count = 0;
409 }
410 }
411 }
412
413 for (nodeiter = root->children; nodeiter; nodeiter = nodeiter->next) {
414 if (nodeiter->type == XML_ELEMENT_NODE
415 && (strcmp ((const char *) nodeiter->name, "StreamIndex") == 0)) {
416 GstMssStream *stream = g_new0 (GstMssStream, 1);
417
418 manifest->streams = g_slist_append (manifest->streams, stream);
419 _gst_mss_stream_init (manifest, stream, nodeiter);
420 }
421
422 if (nodeiter->type == XML_ELEMENT_NODE
423 && (strcmp ((const char *) nodeiter->name, "Protection") == 0)) {
424 _gst_mss_parse_protection (manifest, nodeiter);
425 }
426 }
427
428 gst_buffer_unmap (data, &mapinfo);
429
430 return manifest;
431 }
432
433 static void
gst_mss_stream_free(GstMssStream * stream)434 gst_mss_stream_free (GstMssStream * stream)
435 {
436 if (stream->live_adapter) {
437 gst_adapter_clear (stream->live_adapter);
438 g_object_unref (stream->live_adapter);
439 }
440
441 g_list_free_full (stream->fragments, g_free);
442 g_list_free_full (stream->qualities,
443 (GDestroyNotify) gst_mss_stream_quality_free);
444 xmlFree (stream->url);
445 xmlFree (stream->lang);
446 g_regex_unref (stream->regex_position);
447 g_regex_unref (stream->regex_bitrate);
448 gst_mss_fragment_parser_clear (&stream->fragment_parser);
449 g_free (stream);
450 }
451
452 void
gst_mss_manifest_free(GstMssManifest * manifest)453 gst_mss_manifest_free (GstMssManifest * manifest)
454 {
455 g_return_if_fail (manifest != NULL);
456
457 g_slist_free_full (manifest->streams, (GDestroyNotify) gst_mss_stream_free);
458
459 if (manifest->protection_system_id != NULL)
460 g_string_free (manifest->protection_system_id, TRUE);
461 xmlFree (manifest->protection_data);
462
463 xmlFreeDoc (manifest->xml);
464 g_free (manifest);
465 }
466
467 const gchar *
gst_mss_manifest_get_protection_system_id(GstMssManifest * manifest)468 gst_mss_manifest_get_protection_system_id (GstMssManifest * manifest)
469 {
470 if (manifest->protection_system_id != NULL)
471 return manifest->protection_system_id->str;
472 return NULL;
473 }
474
475 const gchar *
gst_mss_manifest_get_protection_data(GstMssManifest * manifest)476 gst_mss_manifest_get_protection_data (GstMssManifest * manifest)
477 {
478 return manifest->protection_data;
479 }
480
481 GSList *
gst_mss_manifest_get_streams(GstMssManifest * manifest)482 gst_mss_manifest_get_streams (GstMssManifest * manifest)
483 {
484 return manifest->streams;
485 }
486
487 GstMssStreamType
gst_mss_stream_get_type(GstMssStream * stream)488 gst_mss_stream_get_type (GstMssStream * stream)
489 {
490 gchar *prop = (gchar *) xmlGetProp (stream->xmlnode, (xmlChar *) "Type");
491 GstMssStreamType ret = MSS_STREAM_TYPE_UNKNOWN;
492
493 if (prop == NULL)
494 return MSS_STREAM_TYPE_UNKNOWN;
495
496 if (strcmp (prop, "video") == 0) {
497 ret = MSS_STREAM_TYPE_VIDEO;
498 } else if (strcmp (prop, "audio") == 0) {
499 ret = MSS_STREAM_TYPE_AUDIO;
500 } else {
501 GST_DEBUG ("Unsupported stream type: %s", prop);
502 }
503 xmlFree (prop);
504 return ret;
505 }
506
507 static GstCaps *
_gst_mss_stream_video_caps_from_fourcc(gchar * fourcc)508 _gst_mss_stream_video_caps_from_fourcc (gchar * fourcc)
509 {
510 if (!fourcc)
511 return NULL;
512
513 if (strcmp (fourcc, "H264") == 0 || strcmp (fourcc, "AVC1") == 0) {
514 return gst_caps_new_simple ("video/x-h264", "stream-format", G_TYPE_STRING,
515 "avc", NULL);
516 } else if (strcmp (fourcc, "WVC1") == 0) {
517 return gst_caps_new_simple ("video/x-wmv", "wmvversion", G_TYPE_INT, 3,
518 "format", G_TYPE_STRING, "WVC1", NULL);
519 }
520 return NULL;
521 }
522
523 static GstCaps *
_gst_mss_stream_audio_caps_from_fourcc(gchar * fourcc)524 _gst_mss_stream_audio_caps_from_fourcc (gchar * fourcc)
525 {
526 if (!fourcc)
527 return NULL;
528
529 if (strcmp (fourcc, "AACL") == 0) {
530 return gst_caps_new_simple ("audio/mpeg", "mpegversion", G_TYPE_INT, 4,
531 NULL);
532 } else if (strcmp (fourcc, "WmaPro") == 0 || strcmp (fourcc, "WMAP") == 0) {
533 return gst_caps_new_simple ("audio/x-wma", "wmaversion", G_TYPE_INT, 3,
534 NULL);
535 }
536 return NULL;
537 }
538
539 static GstCaps *
_gst_mss_stream_audio_caps_from_audio_tag(gint audiotag)540 _gst_mss_stream_audio_caps_from_audio_tag (gint audiotag)
541 {
542 switch (audiotag) {
543 case 83: /* MP3 */
544 return gst_caps_new_simple ("audio/mpeg", "mpegversion", G_TYPE_INT, 1,
545 "layer", G_TYPE_INT, 3, NULL);
546 case 255: /* AAC */
547 return gst_caps_new_simple ("audio/mpeg", "mpegversion", G_TYPE_INT, 4,
548 NULL);
549 default:
550 break;
551 }
552 return NULL;
553 }
554
555 /* copied and adapted from h264parse */
556 static GstBuffer *
_make_h264_codec_data(GstBuffer * sps,GstBuffer * pps)557 _make_h264_codec_data (GstBuffer * sps, GstBuffer * pps)
558 {
559 GstBuffer *buf;
560 gint sps_size = 0, pps_size = 0, num_sps = 0, num_pps = 0;
561 guint8 profile_idc = 0, profile_comp = 0, level_idc = 0;
562 guint8 *data;
563 gint nl;
564 GstMapInfo spsinfo, ppsinfo, codecdatainfo;
565
566 if (gst_buffer_get_size (sps) < 4)
567 return NULL;
568
569 gst_buffer_map (sps, &spsinfo, GST_MAP_READ);
570 gst_buffer_map (pps, &ppsinfo, GST_MAP_READ);
571
572 sps_size += spsinfo.size + 2;
573 profile_idc = spsinfo.data[1];
574 profile_comp = spsinfo.data[2];
575 level_idc = spsinfo.data[3];
576 num_sps = 1;
577
578 pps_size += ppsinfo.size + 2;
579 num_pps = 1;
580
581 buf = gst_buffer_new_and_alloc (5 + 1 + sps_size + 1 + pps_size);
582 gst_buffer_map (buf, &codecdatainfo, GST_MAP_WRITE);
583 data = codecdatainfo.data;
584 nl = 4;
585
586 data[0] = 1; /* AVC Decoder Configuration Record ver. 1 */
587 data[1] = profile_idc; /* profile_idc */
588 data[2] = profile_comp; /* profile_compability */
589 data[3] = level_idc; /* level_idc */
590 data[4] = 0xfc | (nl - 1); /* nal_length_size_minus1 */
591 data[5] = 0xe0 | num_sps; /* number of SPSs */
592
593 data += 6;
594 GST_WRITE_UINT16_BE (data, spsinfo.size);
595 memcpy (data + 2, spsinfo.data, spsinfo.size);
596 data += 2 + spsinfo.size;
597
598 data[0] = num_pps;
599 data++;
600 GST_WRITE_UINT16_BE (data, ppsinfo.size);
601 memcpy (data + 2, ppsinfo.data, ppsinfo.size);
602 data += 2 + ppsinfo.size;
603
604 gst_buffer_unmap (sps, &spsinfo);
605 gst_buffer_unmap (pps, &ppsinfo);
606 gst_buffer_unmap (buf, &codecdatainfo);
607
608 return buf;
609 }
610
611 static void
_gst_mss_stream_add_h264_codec_data(GstCaps * caps,const gchar * codecdatastr)612 _gst_mss_stream_add_h264_codec_data (GstCaps * caps, const gchar * codecdatastr)
613 {
614 GstBuffer *sps;
615 GstBuffer *pps;
616 GstBuffer *buffer;
617 gchar *sps_str;
618 gchar *pps_str;
619 GstH264NalUnit nalu = { 0, };
620 GstH264SPS sps_struct;
621 GstH264ParserResult parseres;
622 GstMapInfo spsinfo;
623
624 /* search for the sps start */
625 if (g_str_has_prefix (codecdatastr, "00000001")) {
626 sps_str = (gchar *) codecdatastr + 8;
627 } else {
628 return; /* invalid mss codec data */
629 }
630
631 /* search for the pps start */
632 pps_str = g_strstr_len (sps_str, -1, "00000001");
633 if (!pps_str) {
634 return; /* invalid mss codec data */
635 }
636
637 pps_str[0] = '\0';
638 sps = gst_buffer_from_hex_string (sps_str);
639 pps_str[0] = '0';
640
641 pps_str = pps_str + 8;
642 pps = gst_buffer_from_hex_string (pps_str);
643
644 gst_buffer_map (sps, &spsinfo, GST_MAP_READ);
645
646 nalu.ref_idc = (spsinfo.data[0] & 0x60) >> 5;
647 nalu.type = GST_H264_NAL_SPS;
648 nalu.size = spsinfo.size - 1;
649 nalu.data = spsinfo.data + 1;
650 nalu.offset = 0;
651 nalu.sc_offset = 0;
652 nalu.valid = TRUE;
653 nalu.header_bytes = 0;
654 nalu.extension_type = GST_H264_NAL_EXTENSION_NONE;
655
656 parseres = gst_h264_parse_sps (&nalu, &sps_struct);
657 if (parseres == GST_H264_PARSER_OK) {
658 gint fps_num, fps_den;
659
660 /* MSS apparently only supports non-interlaced/progressive H.264 content */
661 gst_h264_video_calculate_framerate (&sps_struct, 0, 0, &fps_num, &fps_den);
662
663 gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION,
664 fps_num, fps_den, NULL);
665 }
666
667 buffer = _make_h264_codec_data (sps, pps);
668 gst_buffer_unmap (sps, &spsinfo);
669 gst_buffer_unref (sps);
670 gst_buffer_unref (pps);
671
672 if (buffer != NULL) {
673 gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, buffer, NULL);
674 gst_buffer_unref (buffer);
675 }
676 }
677
678 static GstCaps *
_gst_mss_stream_video_caps_from_qualitylevel_xml(GstMssStreamQuality * q)679 _gst_mss_stream_video_caps_from_qualitylevel_xml (GstMssStreamQuality * q)
680 {
681 xmlNodePtr node = q->xmlnode;
682 GstCaps *caps;
683 GstStructure *structure;
684 gchar *fourcc = (gchar *) xmlGetProp (node, (xmlChar *) "FourCC");
685 gchar *max_width = (gchar *) xmlGetProp (node, (xmlChar *) "MaxWidth");
686 gchar *max_height = (gchar *) xmlGetProp (node, (xmlChar *) "MaxHeight");
687 gchar *codec_data =
688 (gchar *) xmlGetProp (node, (xmlChar *) "CodecPrivateData");
689
690 if (!max_width)
691 max_width = (gchar *) xmlGetProp (node, (xmlChar *) "Width");
692 if (!max_height)
693 max_height = (gchar *) xmlGetProp (node, (xmlChar *) "Height");
694
695 caps = _gst_mss_stream_video_caps_from_fourcc (fourcc);
696 if (!caps)
697 goto end;
698
699 structure = gst_caps_get_structure (caps, 0);
700
701 if (max_width) {
702 gst_structure_set (structure, "width", G_TYPE_INT,
703 (int) g_ascii_strtoull (max_width, NULL, 10), NULL);
704 }
705 if (max_height) {
706 gst_structure_set (structure, "height", G_TYPE_INT,
707 (int) g_ascii_strtoull (max_height, NULL, 10), NULL);
708 }
709
710 if (codec_data && strlen (codec_data)) {
711 if (strcmp (fourcc, "H264") == 0 || strcmp (fourcc, "AVC1") == 0) {
712 _gst_mss_stream_add_h264_codec_data (caps, codec_data);
713 } else {
714 GstBuffer *buffer = gst_buffer_from_hex_string ((gchar *) codec_data);
715 gst_structure_set (structure, "codec_data", GST_TYPE_BUFFER, buffer,
716 NULL);
717 gst_buffer_unref (buffer);
718 }
719 }
720
721 end:
722 xmlFree (fourcc);
723 xmlFree (max_width);
724 xmlFree (max_height);
725 xmlFree (codec_data);
726
727 return caps;
728 }
729
730 static guint8
_frequency_index_from_sampling_rate(guint sampling_rate)731 _frequency_index_from_sampling_rate (guint sampling_rate)
732 {
733 static const guint aac_sample_rates[] = { 96000, 88200, 64000, 48000, 44100,
734 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350
735 };
736
737 guint8 i;
738
739 for (i = 0; i < G_N_ELEMENTS (aac_sample_rates); i++) {
740 if (aac_sample_rates[i] == sampling_rate)
741 return i;
742 }
743 return 15;
744 }
745
746 static GstBuffer *
_make_aacl_codec_data(guint64 sampling_rate,guint64 channels)747 _make_aacl_codec_data (guint64 sampling_rate, guint64 channels)
748 {
749 GstBuffer *buf;
750 guint8 *data;
751 guint8 frequency_index;
752 guint8 buf_size;
753 GstMapInfo info;
754
755 buf_size = 2;
756 frequency_index = _frequency_index_from_sampling_rate (sampling_rate);
757 if (frequency_index == 15)
758 buf_size += 3;
759
760 buf = gst_buffer_new_and_alloc (buf_size);
761 gst_buffer_map (buf, &info, GST_MAP_WRITE);
762 data = info.data;
763
764 data[0] = 2 << 3; /* AAC-LC object type is 2 */
765 data[0] += frequency_index >> 1;
766 data[1] = (frequency_index & 0x01) << 7;
767
768 /* Sampling rate is not in frequencies table, write manually */
769 if (frequency_index == 15) {
770 data[1] += sampling_rate >> 17;
771 data[2] = (sampling_rate >> 9) & 0xFF;
772 data[3] = (sampling_rate >> 1) & 0xFF;
773 data[4] = sampling_rate & 0x01;
774 data += 3;
775 }
776
777 data[1] += (channels & 0x0F) << 3;
778
779 gst_buffer_unmap (buf, &info);
780
781 return buf;
782 }
783
784 static GstCaps *
_gst_mss_stream_audio_caps_from_qualitylevel_xml(GstMssStreamQuality * q)785 _gst_mss_stream_audio_caps_from_qualitylevel_xml (GstMssStreamQuality * q)
786 {
787 xmlNodePtr node = q->xmlnode;
788 GstCaps *caps = NULL;
789 GstStructure *structure;
790 gchar *fourcc = (gchar *) xmlGetProp (node, (xmlChar *) "FourCC");
791 gchar *audiotag = (gchar *) xmlGetProp (node, (xmlChar *) "AudioTag");
792 gchar *channels_str = (gchar *) xmlGetProp (node, (xmlChar *) "Channels");
793 gchar *rate_str = (gchar *) xmlGetProp (node, (xmlChar *) "SamplingRate");
794 gchar *depth_str = (gchar *) xmlGetProp (node, (xmlChar *) "BitsPerSample");
795 gchar *block_align_str =
796 (gchar *) xmlGetProp (node, (xmlChar *) "PacketSize");
797 gchar *codec_data_str =
798 (gchar *) xmlGetProp (node, (xmlChar *) "CodecPrivateData");
799 GstBuffer *codec_data = NULL;
800 gint depth = 0;
801 gint block_align = 0;
802 gint rate = 0;
803 gint channels = 0;
804 gint atag = 0;
805
806 if (!fourcc) /* sometimes the fourcc is omitted, we fallback to the Subtype in the StreamIndex node */
807 fourcc = (gchar *) xmlGetProp (node->parent, (xmlChar *) "Subtype");
808
809 if (fourcc) {
810 caps = _gst_mss_stream_audio_caps_from_fourcc (fourcc);
811 } else if (audiotag) {
812 atag = g_ascii_strtoull (audiotag, NULL, 10);
813 caps = _gst_mss_stream_audio_caps_from_audio_tag (atag);
814 }
815
816 if (!caps)
817 goto end;
818
819 structure = gst_caps_get_structure (caps, 0);
820 if (codec_data_str && strlen (codec_data_str)) {
821 codec_data = gst_buffer_from_hex_string ((gchar *) codec_data_str);
822 }
823
824 if (rate_str)
825 rate = (gint) g_ascii_strtoull (rate_str, NULL, 10);
826 if (channels_str)
827 channels = (int) g_ascii_strtoull (channels_str, NULL, 10);
828 if (depth_str)
829 depth = (gint) g_ascii_strtoull (depth_str, NULL, 10);
830 if (block_align_str)
831 block_align = (int) g_ascii_strtoull (block_align_str, NULL, 10);
832
833 if (!codec_data) {
834 gint codec_data_len;
835 codec_data_str = (gchar *) xmlGetProp (node, (xmlChar *) "WaveFormatEx");
836
837 if (codec_data_str != NULL) {
838 codec_data_len = strlen (codec_data_str) / 2;
839
840 /* a WAVEFORMATEX structure is 18 bytes */
841 if (codec_data_str && codec_data_len >= 18) {
842 GstMapInfo mapinfo;
843 codec_data = gst_buffer_from_hex_string ((gchar *) codec_data_str);
844
845 /* since this is a WAVEFORMATEX, try to get the block_align and rate */
846 gst_buffer_map (codec_data, &mapinfo, GST_MAP_READ);
847 if (!channels_str) {
848 channels = GST_READ_UINT16_LE (mapinfo.data + 2);
849 }
850 if (!rate_str) {
851 rate = GST_READ_UINT32_LE (mapinfo.data + 4);
852 }
853 if (!block_align) {
854 block_align = GST_READ_UINT16_LE (mapinfo.data + 12);
855 }
856 if (!depth) {
857 depth = GST_READ_UINT16_LE (mapinfo.data + 14);
858 }
859 gst_buffer_unmap (codec_data, &mapinfo);
860
861 /* Consume all the WAVEFORMATEX structure, and pass only the rest of
862 * the data as the codec private data */
863 gst_buffer_resize (codec_data, 18, -1);
864 } else {
865 GST_WARNING ("Dropping WaveFormatEx: data is %d bytes, "
866 "but at least 18 bytes are expected", codec_data_len);
867 }
868 }
869 }
870
871 if (!codec_data && ((fourcc && strcmp (fourcc, "AACL") == 0) || atag == 255)
872 && rate && channels) {
873 codec_data = _make_aacl_codec_data (rate, channels);
874 }
875
876 if (block_align)
877 gst_structure_set (structure, "block_align", G_TYPE_INT, block_align, NULL);
878
879 if (channels)
880 gst_structure_set (structure, "channels", G_TYPE_INT, channels, NULL);
881
882 if (rate)
883 gst_structure_set (structure, "rate", G_TYPE_INT, rate, NULL);
884
885 if (depth)
886 gst_structure_set (structure, "depth", G_TYPE_INT, depth, NULL);
887
888 if (q->bitrate)
889 gst_structure_set (structure, "bitrate", G_TYPE_INT, (int) q->bitrate,
890 NULL);
891
892 if (codec_data)
893 gst_structure_set (structure, "codec_data", GST_TYPE_BUFFER, codec_data,
894 NULL);
895
896 end:
897 if (codec_data)
898 gst_buffer_unref (codec_data);
899 xmlFree (fourcc);
900 xmlFree (audiotag);
901 xmlFree (channels_str);
902 xmlFree (rate_str);
903 xmlFree (depth_str);
904 xmlFree (block_align_str);
905 xmlFree (codec_data_str);
906
907 return caps;
908 }
909
910 void
gst_mss_stream_set_active(GstMssStream * stream,gboolean active)911 gst_mss_stream_set_active (GstMssStream * stream, gboolean active)
912 {
913 stream->active = active;
914 }
915
916 guint64
gst_mss_stream_get_timescale(GstMssStream * stream)917 gst_mss_stream_get_timescale (GstMssStream * stream)
918 {
919 gchar *timescale;
920 guint64 ts = DEFAULT_TIMESCALE;
921
922 timescale =
923 (gchar *) xmlGetProp (stream->xmlnode, (xmlChar *) MSS_PROP_TIMESCALE);
924 if (!timescale) {
925 timescale =
926 (gchar *) xmlGetProp (stream->xmlnode->parent,
927 (xmlChar *) MSS_PROP_TIMESCALE);
928 }
929
930 if (timescale) {
931 ts = g_ascii_strtoull (timescale, NULL, 10);
932 xmlFree (timescale);
933 }
934 return ts;
935 }
936
937 guint64
gst_mss_manifest_get_timescale(GstMssManifest * manifest)938 gst_mss_manifest_get_timescale (GstMssManifest * manifest)
939 {
940 gchar *timescale;
941 guint64 ts = DEFAULT_TIMESCALE;
942
943 timescale =
944 (gchar *) xmlGetProp (manifest->xmlrootnode,
945 (xmlChar *) MSS_PROP_TIMESCALE);
946 if (timescale) {
947 ts = g_ascii_strtoull (timescale, NULL, 10);
948 xmlFree (timescale);
949 }
950 return ts;
951 }
952
953 guint64
gst_mss_manifest_get_duration(GstMssManifest * manifest)954 gst_mss_manifest_get_duration (GstMssManifest * manifest)
955 {
956 gchar *duration;
957 guint64 dur = 0;
958
959 /* try the property */
960 duration =
961 (gchar *) xmlGetProp (manifest->xmlrootnode,
962 (xmlChar *) MSS_PROP_STREAM_DURATION);
963 if (duration) {
964 dur = g_ascii_strtoull (duration, NULL, 10);
965 xmlFree (duration);
966 }
967 /* else use the fragment list */
968 if (dur <= 0) {
969 guint64 max_dur = 0;
970 GSList *iter;
971
972 for (iter = manifest->streams; iter; iter = g_slist_next (iter)) {
973 GstMssStream *stream = iter->data;
974
975 if (stream->active) {
976 if (stream->fragments) {
977 GList *l = g_list_last (stream->fragments);
978 GstMssStreamFragment *fragment = (GstMssStreamFragment *) l->data;
979 guint64 frag_dur =
980 fragment->time + fragment->duration * fragment->repetitions;
981 max_dur = MAX (frag_dur, max_dur);
982 }
983 }
984 }
985
986 if (max_dur != 0)
987 dur = max_dur;
988 }
989
990 return dur;
991 }
992
993
994 /*
995 * Gets the duration in nanoseconds
996 */
997 GstClockTime
gst_mss_manifest_get_gst_duration(GstMssManifest * manifest)998 gst_mss_manifest_get_gst_duration (GstMssManifest * manifest)
999 {
1000 guint64 duration = -1;
1001 guint64 timescale;
1002 GstClockTime gstdur = GST_CLOCK_TIME_NONE;
1003
1004 duration = gst_mss_manifest_get_duration (manifest);
1005 timescale = gst_mss_manifest_get_timescale (manifest);
1006
1007 if (duration != -1 && timescale != -1)
1008 gstdur =
1009 (GstClockTime) gst_util_uint64_scale_round (duration, GST_SECOND,
1010 timescale);
1011
1012 return gstdur;
1013 }
1014
1015 GstClockTime
gst_mss_manifest_get_min_fragment_duration(GstMssManifest * manifest)1016 gst_mss_manifest_get_min_fragment_duration (GstMssManifest * manifest)
1017 {
1018 GSList *iter;
1019 GstClockTime dur = GST_CLOCK_TIME_NONE;
1020 GstClockTime iter_dur;
1021
1022 for (iter = manifest->streams; iter; iter = g_slist_next (iter)) {
1023 GstMssStream *stream = iter->data;
1024
1025 iter_dur = gst_mss_stream_get_fragment_gst_duration (stream);
1026 if (iter_dur != GST_CLOCK_TIME_NONE && iter_dur != 0) {
1027 if (GST_CLOCK_TIME_IS_VALID (dur)) {
1028 dur = MIN (dur, iter_dur);
1029 } else {
1030 dur = iter_dur;
1031 }
1032 }
1033 }
1034
1035 return dur;
1036 }
1037
1038 GstCaps *
gst_mss_stream_get_caps(GstMssStream * stream)1039 gst_mss_stream_get_caps (GstMssStream * stream)
1040 {
1041 GstMssStreamType streamtype = gst_mss_stream_get_type (stream);
1042 GstMssStreamQuality *qualitylevel = stream->current_quality->data;
1043 GstCaps *caps = NULL;
1044
1045 if (streamtype == MSS_STREAM_TYPE_VIDEO)
1046 caps = _gst_mss_stream_video_caps_from_qualitylevel_xml (qualitylevel);
1047 else if (streamtype == MSS_STREAM_TYPE_AUDIO)
1048 caps = _gst_mss_stream_audio_caps_from_qualitylevel_xml (qualitylevel);
1049
1050 return caps;
1051 }
1052
1053 GstFlowReturn
gst_mss_stream_get_fragment_url(GstMssStream * stream,gchar ** url)1054 gst_mss_stream_get_fragment_url (GstMssStream * stream, gchar ** url)
1055 {
1056 gchar *tmp;
1057 gchar *start_time_str;
1058 guint64 time;
1059 GstMssStreamFragment *fragment;
1060 GstMssStreamQuality *quality = stream->current_quality->data;
1061
1062 g_return_val_if_fail (stream->active, GST_FLOW_ERROR);
1063
1064 if (stream->current_fragment == NULL) /* stream is over */
1065 return GST_FLOW_EOS;
1066
1067 fragment = stream->current_fragment->data;
1068
1069 time =
1070 fragment->time + fragment->duration * stream->fragment_repetition_index;
1071 start_time_str = g_strdup_printf ("%" G_GUINT64_FORMAT, time);
1072
1073 tmp = g_regex_replace_literal (stream->regex_bitrate, stream->url,
1074 strlen (stream->url), 0, quality->bitrate_str, 0, NULL);
1075 *url = g_regex_replace_literal (stream->regex_position, tmp,
1076 strlen (tmp), 0, start_time_str, 0, NULL);
1077
1078 g_free (tmp);
1079 g_free (start_time_str);
1080
1081 if (*url == NULL)
1082 return GST_FLOW_ERROR;
1083
1084 return GST_FLOW_OK;
1085 }
1086
1087 GstClockTime
gst_mss_stream_get_fragment_gst_timestamp(GstMssStream * stream)1088 gst_mss_stream_get_fragment_gst_timestamp (GstMssStream * stream)
1089 {
1090 guint64 time;
1091 guint64 timescale;
1092 GstMssStreamFragment *fragment;
1093
1094 g_return_val_if_fail (stream->active, GST_CLOCK_TIME_NONE);
1095
1096 if (!stream->current_fragment) {
1097 GList *last = g_list_last (stream->fragments);
1098 if (last == NULL)
1099 return GST_CLOCK_TIME_NONE;
1100
1101 fragment = last->data;
1102 time = fragment->time + (fragment->duration * fragment->repetitions);
1103 } else {
1104 fragment = stream->current_fragment->data;
1105 time =
1106 fragment->time +
1107 (fragment->duration * stream->fragment_repetition_index);
1108 }
1109
1110 timescale = gst_mss_stream_get_timescale (stream);
1111 return (GstClockTime) gst_util_uint64_scale_round (time, GST_SECOND,
1112 timescale);
1113 }
1114
1115 GstClockTime
gst_mss_stream_get_fragment_gst_duration(GstMssStream * stream)1116 gst_mss_stream_get_fragment_gst_duration (GstMssStream * stream)
1117 {
1118 guint64 dur;
1119 guint64 timescale;
1120 GstMssStreamFragment *fragment;
1121
1122 g_return_val_if_fail (stream->active, GST_FLOW_ERROR);
1123
1124 if (!stream->current_fragment)
1125 return GST_CLOCK_TIME_NONE;
1126
1127 fragment = stream->current_fragment->data;
1128
1129 dur = fragment->duration;
1130 timescale = gst_mss_stream_get_timescale (stream);
1131 return (GstClockTime) gst_util_uint64_scale_round (dur, GST_SECOND,
1132 timescale);
1133 }
1134
1135 gboolean
gst_mss_stream_has_next_fragment(GstMssStream * stream)1136 gst_mss_stream_has_next_fragment (GstMssStream * stream)
1137 {
1138 g_return_val_if_fail (stream->active, FALSE);
1139
1140 if (stream->current_fragment == NULL)
1141 return FALSE;
1142
1143 return TRUE;
1144 }
1145
1146 GstFlowReturn
gst_mss_stream_advance_fragment(GstMssStream * stream)1147 gst_mss_stream_advance_fragment (GstMssStream * stream)
1148 {
1149 GstMssStreamFragment *fragment;
1150 const gchar *stream_type_name =
1151 gst_mss_stream_type_name (gst_mss_stream_get_type (stream));
1152
1153 g_return_val_if_fail (stream->active, GST_FLOW_ERROR);
1154
1155 if (stream->current_fragment == NULL)
1156 return GST_FLOW_EOS;
1157
1158 fragment = stream->current_fragment->data;
1159 stream->fragment_repetition_index++;
1160 if (stream->fragment_repetition_index < fragment->repetitions)
1161 goto beach;
1162
1163 stream->fragment_repetition_index = 0;
1164 stream->current_fragment = g_list_next (stream->current_fragment);
1165
1166 GST_DEBUG ("Advanced to fragment #%d on %s stream", fragment->number,
1167 stream_type_name);
1168 if (stream->current_fragment == NULL)
1169 return GST_FLOW_EOS;
1170
1171 beach:
1172 gst_mss_fragment_parser_clear (&stream->fragment_parser);
1173 gst_mss_fragment_parser_init (&stream->fragment_parser);
1174 return GST_FLOW_OK;
1175 }
1176
1177 GstFlowReturn
gst_mss_stream_regress_fragment(GstMssStream * stream)1178 gst_mss_stream_regress_fragment (GstMssStream * stream)
1179 {
1180 GstMssStreamFragment *fragment;
1181 g_return_val_if_fail (stream->active, GST_FLOW_ERROR);
1182
1183 if (stream->current_fragment == NULL)
1184 return GST_FLOW_EOS;
1185
1186 fragment = stream->current_fragment->data;
1187 if (stream->fragment_repetition_index == 0) {
1188 stream->current_fragment = g_list_previous (stream->current_fragment);
1189 if (stream->current_fragment == NULL)
1190 return GST_FLOW_EOS;
1191 fragment = stream->current_fragment->data;
1192 stream->fragment_repetition_index = fragment->repetitions - 1;
1193 } else {
1194 stream->fragment_repetition_index--;
1195 }
1196 return GST_FLOW_OK;
1197 }
1198
1199 const gchar *
gst_mss_stream_type_name(GstMssStreamType streamtype)1200 gst_mss_stream_type_name (GstMssStreamType streamtype)
1201 {
1202 switch (streamtype) {
1203 case MSS_STREAM_TYPE_VIDEO:
1204 return "video";
1205 case MSS_STREAM_TYPE_AUDIO:
1206 return "audio";
1207 case MSS_STREAM_TYPE_UNKNOWN:
1208 default:
1209 return "unknown";
1210 }
1211 }
1212
1213 /*
1214 * Seeks all streams to the fragment that contains the set time
1215 *
1216 * @forward: if this is forward playback
1217 * @time: time in nanoseconds
1218 */
1219 void
gst_mss_manifest_seek(GstMssManifest * manifest,gboolean forward,guint64 time)1220 gst_mss_manifest_seek (GstMssManifest * manifest, gboolean forward,
1221 guint64 time)
1222 {
1223 GSList *iter;
1224
1225 for (iter = manifest->streams; iter; iter = g_slist_next (iter)) {
1226 GstMssStream *stream = iter->data;
1227
1228 gst_mss_manifest_live_adapter_clear (stream);
1229 gst_mss_stream_seek (stream, forward, 0, time, NULL);
1230 }
1231 }
1232
1233 #define SNAP_AFTER(forward,flags) \
1234 ((forward && (flags & GST_SEEK_FLAG_SNAP_AFTER)) || \
1235 (!forward && (flags & GST_SEEK_FLAG_SNAP_BEFORE)))
1236
1237 /*
1238 * Seeks this stream to the fragment that contains the sample at time
1239 *
1240 * @time: time in nanoseconds
1241 */
1242 void
gst_mss_stream_seek(GstMssStream * stream,gboolean forward,GstSeekFlags flags,guint64 time,guint64 * final_time)1243 gst_mss_stream_seek (GstMssStream * stream, gboolean forward,
1244 GstSeekFlags flags, guint64 time, guint64 * final_time)
1245 {
1246 GList *iter;
1247 guint64 timescale;
1248 GstMssStreamFragment *fragment = NULL;
1249
1250 timescale = gst_mss_stream_get_timescale (stream);
1251 time = gst_util_uint64_scale_round (time, timescale, GST_SECOND);
1252
1253 GST_DEBUG ("Stream %s seeking to %" G_GUINT64_FORMAT, stream->url, time);
1254 for (iter = stream->fragments; iter; iter = g_list_next (iter)) {
1255 fragment = iter->data;
1256 if (fragment->time + fragment->repetitions * fragment->duration > time) {
1257 stream->current_fragment = iter;
1258 stream->fragment_repetition_index =
1259 (time - fragment->time) / fragment->duration;
1260 if (((time - fragment->time) % fragment->duration) == 0) {
1261
1262 /* for reverse playback, start from the previous fragment when we are
1263 * exactly at a limit */
1264 if (!forward)
1265 stream->fragment_repetition_index--;
1266 } else if (SNAP_AFTER (forward, flags))
1267 stream->fragment_repetition_index++;
1268
1269 if (stream->fragment_repetition_index == fragment->repetitions) {
1270 /* move to the next one */
1271 stream->fragment_repetition_index = 0;
1272 stream->current_fragment = g_list_next (iter);
1273 fragment =
1274 stream->current_fragment ? stream->current_fragment->data : NULL;
1275
1276 } else if (stream->fragment_repetition_index == -1) {
1277 if (g_list_previous (iter)) {
1278 stream->current_fragment = g_list_previous (iter);
1279 fragment = stream->current_fragment->data;
1280 g_assert (fragment);
1281 stream->fragment_repetition_index = fragment->repetitions - 1;
1282 } else {
1283 stream->fragment_repetition_index = 0;
1284 }
1285 }
1286
1287 break;
1288 }
1289
1290 }
1291
1292 GST_DEBUG ("Stream %s seeked to fragment time %" G_GUINT64_FORMAT
1293 " repetition %u", stream->url,
1294 fragment ? fragment->time : GST_CLOCK_TIME_NONE,
1295 stream->fragment_repetition_index);
1296 if (final_time) {
1297 if (fragment) {
1298 *final_time = gst_util_uint64_scale_round (fragment->time +
1299 stream->fragment_repetition_index * fragment->duration,
1300 GST_SECOND, timescale);
1301 } else {
1302 GstMssStreamFragment *last_fragment = g_list_last (iter)->data;
1303 *final_time = gst_util_uint64_scale_round (last_fragment->time +
1304 last_fragment->repetitions * last_fragment->duration,
1305 GST_SECOND, timescale);
1306 }
1307 }
1308 }
1309
1310 guint64
gst_mss_manifest_get_current_bitrate(GstMssManifest * manifest)1311 gst_mss_manifest_get_current_bitrate (GstMssManifest * manifest)
1312 {
1313 guint64 bitrate = 0;
1314 GSList *iter;
1315
1316 for (iter = gst_mss_manifest_get_streams (manifest); iter;
1317 iter = g_slist_next (iter)) {
1318 GstMssStream *stream = iter->data;
1319 if (stream->active && stream->current_quality) {
1320 GstMssStreamQuality *q = stream->current_quality->data;
1321
1322 bitrate += q->bitrate;
1323 }
1324 }
1325
1326 return bitrate;
1327 }
1328
1329 gboolean
gst_mss_manifest_is_live(GstMssManifest * manifest)1330 gst_mss_manifest_is_live (GstMssManifest * manifest)
1331 {
1332 return manifest->is_live;
1333 }
1334
1335 static void
gst_mss_stream_reload_fragments(GstMssStream * stream,xmlNodePtr streamIndex)1336 gst_mss_stream_reload_fragments (GstMssStream * stream, xmlNodePtr streamIndex)
1337 {
1338 xmlNodePtr iter;
1339 guint64 current_gst_time;
1340 GstMssFragmentListBuilder builder;
1341
1342 current_gst_time = gst_mss_stream_get_fragment_gst_timestamp (stream);
1343
1344 gst_mss_fragment_list_builder_init (&builder);
1345
1346 GST_DEBUG ("Current position: %" GST_TIME_FORMAT,
1347 GST_TIME_ARGS (current_gst_time));
1348
1349 for (iter = streamIndex->children; iter; iter = iter->next) {
1350 if (node_has_type (iter, MSS_NODE_STREAM_FRAGMENT)) {
1351 gst_mss_fragment_list_builder_add (&builder, iter);
1352 } else {
1353 /* TODO gst log this */
1354 }
1355 }
1356
1357 /* store the new fragments list */
1358 if (builder.fragments) {
1359 g_list_free_full (stream->fragments, g_free);
1360 stream->fragments = g_list_reverse (builder.fragments);
1361 stream->current_fragment = stream->fragments;
1362 /* TODO Verify how repositioning here works for reverse
1363 * playback - it might start from the wrong fragment */
1364 gst_mss_stream_seek (stream, TRUE, 0, current_gst_time, NULL);
1365 }
1366 }
1367
1368 static void
gst_mss_manifest_reload_fragments_from_xml(GstMssManifest * manifest,xmlNodePtr root)1369 gst_mss_manifest_reload_fragments_from_xml (GstMssManifest * manifest,
1370 xmlNodePtr root)
1371 {
1372 xmlNodePtr nodeiter;
1373 GSList *streams = manifest->streams;
1374
1375 /* we assume the server is providing the streams in the same order in
1376 * every manifest */
1377 for (nodeiter = root->children; nodeiter && streams;
1378 nodeiter = nodeiter->next) {
1379 if (nodeiter->type == XML_ELEMENT_NODE
1380 && (strcmp ((const char *) nodeiter->name, "StreamIndex") == 0)) {
1381 gst_mss_stream_reload_fragments (streams->data, nodeiter);
1382 streams = g_slist_next (streams);
1383 }
1384 }
1385 }
1386
1387 void
gst_mss_manifest_reload_fragments(GstMssManifest * manifest,GstBuffer * data)1388 gst_mss_manifest_reload_fragments (GstMssManifest * manifest, GstBuffer * data)
1389 {
1390 xmlDocPtr xml;
1391 xmlNodePtr root;
1392 GstMapInfo info;
1393
1394 gst_buffer_map (data, &info, GST_MAP_READ);
1395
1396 xml = xmlReadMemory ((const gchar *) info.data,
1397 info.size, "manifest", NULL, 0);
1398 root = xmlDocGetRootElement (xml);
1399
1400 gst_mss_manifest_reload_fragments_from_xml (manifest, root);
1401
1402 xmlFreeDoc (xml);
1403
1404 gst_buffer_unmap (data, &info);
1405 }
1406
1407 gboolean
gst_mss_stream_select_bitrate(GstMssStream * stream,guint64 bitrate)1408 gst_mss_stream_select_bitrate (GstMssStream * stream, guint64 bitrate)
1409 {
1410 GList *iter = stream->current_quality;
1411 GList *next;
1412 GstMssStreamQuality *q = iter->data;
1413
1414 while (q->bitrate > bitrate) {
1415 next = g_list_previous (iter);
1416 if (next) {
1417 iter = next;
1418 q = iter->data;
1419 } else {
1420 break;
1421 }
1422 }
1423
1424 while (q->bitrate < bitrate) {
1425 GstMssStreamQuality *next_q;
1426 next = g_list_next (iter);
1427 if (next) {
1428 next_q = next->data;
1429 if (next_q->bitrate < bitrate) {
1430 iter = next;
1431 q = iter->data;
1432 } else {
1433 break;
1434 }
1435 } else {
1436 break;
1437 }
1438 }
1439
1440 if (iter == stream->current_quality)
1441 return FALSE;
1442 stream->current_quality = iter;
1443 return TRUE;
1444 }
1445
1446 guint64
gst_mss_stream_get_current_bitrate(GstMssStream * stream)1447 gst_mss_stream_get_current_bitrate (GstMssStream * stream)
1448 {
1449 GstMssStreamQuality *q;
1450 if (stream->current_quality == NULL)
1451 return 0;
1452
1453 q = stream->current_quality->data;
1454 return q->bitrate;
1455 }
1456
1457 /**
1458 * gst_mss_manifest_change_bitrate:
1459 * @manifest: the manifest
1460 * @bitrate: the maximum bitrate to use (bps)
1461 *
1462 * Iterates over the active streams and changes their bitrates to the maximum
1463 * value so that the bitrates of all streams are not larger than
1464 * @bitrate.
1465 *
1466 * Return: %TRUE if any stream changed its bitrate
1467 */
1468 gboolean
gst_mss_manifest_change_bitrate(GstMssManifest * manifest,guint64 bitrate)1469 gst_mss_manifest_change_bitrate (GstMssManifest * manifest, guint64 bitrate)
1470 {
1471 gboolean ret = FALSE;
1472 GSList *iter;
1473
1474 /* TODO This algorithm currently sets the same bitrate for all streams,
1475 * it should actually use the sum of all streams bitrates to compare to
1476 * the target value */
1477
1478 if (bitrate == 0) {
1479 /* use maximum */
1480 bitrate = G_MAXUINT64;
1481 }
1482
1483 for (iter = gst_mss_manifest_get_streams (manifest); iter;
1484 iter = g_slist_next (iter)) {
1485 GstMssStream *stream = iter->data;
1486 if (stream->active) {
1487 ret = ret | gst_mss_stream_select_bitrate (stream, bitrate);
1488 }
1489 }
1490
1491 return ret;
1492 }
1493
1494 static GstBuffer *
gst_buffer_from_hex_string(const gchar * s)1495 gst_buffer_from_hex_string (const gchar * s)
1496 {
1497 GstBuffer *buffer = NULL;
1498 gint len;
1499 gchar ts[3];
1500 guint8 *data;
1501 gint i;
1502 GstMapInfo info;
1503
1504 len = strlen (s);
1505 if (len & 1)
1506 return NULL;
1507
1508 buffer = gst_buffer_new_and_alloc (len / 2);
1509 gst_buffer_map (buffer, &info, GST_MAP_WRITE);
1510 data = info.data;
1511 for (i = 0; i < len / 2; i++) {
1512 if (!isxdigit ((int) s[i * 2]) || !isxdigit ((int) s[i * 2 + 1])) {
1513 gst_buffer_unref (buffer);
1514 return NULL;
1515 }
1516
1517 ts[0] = s[i * 2 + 0];
1518 ts[1] = s[i * 2 + 1];
1519 ts[2] = 0;
1520
1521 data[i] = (guint8) strtoul (ts, NULL, 16);
1522 }
1523
1524 gst_buffer_unmap (buffer, &info);
1525 return buffer;
1526 }
1527
1528 const gchar *
gst_mss_stream_get_lang(GstMssStream * stream)1529 gst_mss_stream_get_lang (GstMssStream * stream)
1530 {
1531 return stream->lang;
1532 }
1533
1534 static GstClockTime
gst_mss_manifest_get_dvr_window_length_clock_time(GstMssManifest * manifest)1535 gst_mss_manifest_get_dvr_window_length_clock_time (GstMssManifest * manifest)
1536 {
1537 gint64 timescale;
1538
1539 /* the entire file is always available for non-live streams */
1540 if (manifest->dvr_window == 0)
1541 return GST_CLOCK_TIME_NONE;
1542
1543 timescale = gst_mss_manifest_get_timescale (manifest);
1544 return (GstClockTime) gst_util_uint64_scale_round (manifest->dvr_window,
1545 GST_SECOND, timescale);
1546 }
1547
1548 static gboolean
gst_mss_stream_get_live_seek_range(GstMssStream * stream,gint64 * start,gint64 * stop)1549 gst_mss_stream_get_live_seek_range (GstMssStream * stream, gint64 * start,
1550 gint64 * stop)
1551 {
1552 GList *l;
1553 GstMssStreamFragment *fragment;
1554 guint64 timescale = gst_mss_stream_get_timescale (stream);
1555
1556 g_return_val_if_fail (stream->active, FALSE);
1557
1558 /* XXX: assumes all the data in the stream is still available */
1559 l = g_list_first (stream->fragments);
1560 fragment = (GstMssStreamFragment *) l->data;
1561 *start = gst_util_uint64_scale_round (fragment->time, GST_SECOND, timescale);
1562
1563 l = g_list_last (stream->fragments);
1564 fragment = (GstMssStreamFragment *) l->data;
1565 *stop = gst_util_uint64_scale_round (fragment->time + fragment->duration *
1566 fragment->repetitions, GST_SECOND, timescale);
1567
1568 return TRUE;
1569 }
1570
1571 gboolean
gst_mss_manifest_get_live_seek_range(GstMssManifest * manifest,gint64 * start,gint64 * stop)1572 gst_mss_manifest_get_live_seek_range (GstMssManifest * manifest, gint64 * start,
1573 gint64 * stop)
1574 {
1575 GSList *iter;
1576 gboolean ret = FALSE;
1577
1578 for (iter = manifest->streams; iter; iter = g_slist_next (iter)) {
1579 GstMssStream *stream = iter->data;
1580
1581 if (stream->active) {
1582 /* FIXME: bound this correctly for multiple streams */
1583 if (!(ret = gst_mss_stream_get_live_seek_range (stream, start, stop)))
1584 break;
1585 }
1586 }
1587
1588 if (ret && gst_mss_manifest_is_live (manifest)) {
1589 GstClockTime dvr_window =
1590 gst_mss_manifest_get_dvr_window_length_clock_time (manifest);
1591
1592 if (GST_CLOCK_TIME_IS_VALID (dvr_window) && *stop - *start > dvr_window) {
1593 *start = *stop - dvr_window;
1594 }
1595 }
1596
1597 return ret;
1598 }
1599
1600 void
gst_mss_manifest_live_adapter_push(GstMssStream * stream,GstBuffer * buffer)1601 gst_mss_manifest_live_adapter_push (GstMssStream * stream, GstBuffer * buffer)
1602 {
1603 gst_adapter_push (stream->live_adapter, buffer);
1604 }
1605
1606 gsize
gst_mss_manifest_live_adapter_available(GstMssStream * stream)1607 gst_mss_manifest_live_adapter_available (GstMssStream * stream)
1608 {
1609 return gst_adapter_available (stream->live_adapter);
1610 }
1611
1612 GstBuffer *
gst_mss_manifest_live_adapter_take_buffer(GstMssStream * stream,gsize nbytes)1613 gst_mss_manifest_live_adapter_take_buffer (GstMssStream * stream, gsize nbytes)
1614 {
1615 return gst_adapter_take_buffer (stream->live_adapter, nbytes);
1616 }
1617
1618 void
gst_mss_manifest_live_adapter_clear(GstMssStream * stream)1619 gst_mss_manifest_live_adapter_clear (GstMssStream * stream)
1620 {
1621 if (stream->live_adapter)
1622 gst_adapter_clear (stream->live_adapter);
1623 }
1624
1625 gboolean
gst_mss_stream_fragment_parsing_needed(GstMssStream * stream)1626 gst_mss_stream_fragment_parsing_needed (GstMssStream * stream)
1627 {
1628 return stream->fragment_parser.status == GST_MSS_FRAGMENT_HEADER_PARSER_INIT;
1629 }
1630
1631 void
gst_mss_stream_parse_fragment(GstMssStream * stream,GstBuffer * buffer)1632 gst_mss_stream_parse_fragment (GstMssStream * stream, GstBuffer * buffer)
1633 {
1634 const gchar *stream_type_name;
1635 guint8 index;
1636 GstMoofBox *moof;
1637 GstTrafBox *traf;
1638
1639 if (!stream->has_live_fragments)
1640 return;
1641
1642 if (!gst_mss_fragment_parser_add_buffer (&stream->fragment_parser, buffer))
1643 return;
1644
1645 moof = stream->fragment_parser.moof;
1646 traf = &g_array_index (moof->traf, GstTrafBox, 0);
1647
1648 stream_type_name =
1649 gst_mss_stream_type_name (gst_mss_stream_get_type (stream));
1650
1651 for (index = 0; index < traf->tfrf->entries_count; index++) {
1652 GstTfrfBoxEntry *entry =
1653 &g_array_index (traf->tfrf->entries, GstTfrfBoxEntry, index);
1654 GList *l = g_list_last (stream->fragments);
1655 GstMssStreamFragment *last;
1656 GstMssStreamFragment *fragment;
1657 guint64 parsed_time = entry->time;
1658 guint64 parsed_duration = entry->duration;
1659
1660 if (l == NULL)
1661 break;
1662
1663 last = (GstMssStreamFragment *) l->data;
1664
1665 /* only add the fragment to the list if it's outside the time in the
1666 * current list */
1667 if (last->time >= entry->time)
1668 continue;
1669
1670 fragment = g_new (GstMssStreamFragment, 1);
1671 fragment->number = last->number + 1;
1672 fragment->repetitions = 1;
1673 fragment->time = parsed_time;
1674 fragment->duration = parsed_duration;
1675
1676 stream->fragments = g_list_append (stream->fragments, fragment);
1677 GST_LOG ("Adding fragment number: %u to %s stream, time: %"
1678 G_GUINT64_FORMAT ", duration: %" G_GUINT64_FORMAT ", repetitions: %u",
1679 fragment->number, stream_type_name, fragment->time,
1680 fragment->duration, fragment->repetitions);
1681 }
1682 }
1683