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