2 * Copyright (C) 2012 Smart TV Alliance
3 * Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>, Collabora Ltd.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
25 #include <libxml/parser.h>
26 #include <libxml/tree.h>
28 /* for parsing h264 codec data */
29 #include <gst/codecparsers/gsth264parser.h>
31 #include "gstmssmanifest.h"
33 #define DEFAULT_TIMESCALE 10000000
35 #define MSS_NODE_STREAM_FRAGMENT "c"
36 #define MSS_NODE_STREAM_QUALITY "QualityLevel"
38 #define MSS_PROP_BITRATE "Bitrate"
39 #define MSS_PROP_DURATION "d"
40 #define MSS_PROP_NUMBER "n"
41 #define MSS_PROP_STREAM_DURATION "Duration"
42 #define MSS_PROP_TIME "t"
43 #define MSS_PROP_TIMESCALE "TimeScale"
44 #define MSS_PROP_URL "Url"
46 /* TODO check if atoi is successful? */
48 typedef struct _GstMssStreamFragment
53 } GstMssStreamFragment;
55 typedef struct _GstMssStreamQuality
61 } GstMssStreamQuality;
67 gboolean active; /* if the stream is currently being used */
68 gint selectedQualityIndex;
75 GList *current_fragment;
76 GList *current_quality;
78 /* TODO move this to somewhere static */
79 GRegex *regex_bitrate;
80 GRegex *regex_position;
83 struct _GstMssManifest
86 xmlNodePtr xmlrootnode;
92 node_has_type (xmlNodePtr node, const gchar * name)
94 return strcmp ((gchar *) node->name, name) == 0;
97 static GstMssStreamQuality *
98 gst_mss_stream_quality_new (xmlNodePtr node)
100 GstMssStreamQuality *q = g_slice_new (GstMssStreamQuality);
103 q->bitrate_str = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_BITRATE);
104 q->bitrate = strtoull (q->bitrate_str, NULL, 10);
110 gst_mss_stream_quality_free (GstMssStreamQuality * quality)
112 g_return_if_fail (quality != NULL);
114 g_free (quality->bitrate_str);
115 g_slice_free (GstMssStreamQuality, quality);
119 compare_bitrate (GstMssStreamQuality * a, GstMssStreamQuality * b)
121 if (a->bitrate > b->bitrate)
123 if (a->bitrate < b->bitrate)
130 _gst_mss_stream_init (GstMssStream * stream, xmlNodePtr node)
133 GstMssStreamFragment *previous_fragment = NULL;
134 guint fragment_number = 0;
135 guint fragment_time_accum = 0;
136 GError *gerror = NULL;
138 stream->xmlnode = node;
140 /* get the base url path generator */
141 stream->url = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_URL);
143 for (iter = node->children; iter; iter = iter->next) {
144 if (node_has_type (iter, MSS_NODE_STREAM_FRAGMENT)) {
148 GstMssStreamFragment *fragment = g_new (GstMssStreamFragment, 1);
150 duration_str = (gchar *) xmlGetProp (iter, (xmlChar *) MSS_PROP_DURATION);
151 time_str = (gchar *) xmlGetProp (iter, (xmlChar *) MSS_PROP_TIME);
152 seqnum_str = (gchar *) xmlGetProp (iter, (xmlChar *) MSS_PROP_NUMBER);
154 /* use the node's seq number or use the previous + 1 */
156 fragment->number = atoi (seqnum_str);
159 fragment->number = fragment_number;
161 fragment_number = fragment->number + 1;
164 fragment->time = atoi (time_str);
167 fragment->time = fragment_time_accum;
170 /* if we have a previous fragment, means we need to set its duration */
171 if (previous_fragment)
172 previous_fragment->duration = fragment->time - previous_fragment->time;
175 fragment->duration = atoi (duration_str);
177 previous_fragment = NULL;
178 fragment_time_accum += fragment->duration;
179 g_free (duration_str);
181 /* store to set the duration at the next iteration */
182 previous_fragment = fragment;
185 /* we reverse it later */
186 stream->fragments = g_list_prepend (stream->fragments, fragment);
188 } else if (node_has_type (iter, MSS_NODE_STREAM_QUALITY)) {
189 GstMssStreamQuality *quality = gst_mss_stream_quality_new (iter);
190 stream->qualities = g_list_prepend (stream->qualities, quality);
192 /* TODO gst log this */
196 stream->fragments = g_list_reverse (stream->fragments);
198 /* order them from smaller to bigger based on bitrates */
200 g_list_sort (stream->qualities, (GCompareFunc) compare_bitrate);
202 stream->current_fragment = stream->fragments;
203 stream->current_quality = stream->qualities;
205 stream->regex_bitrate = g_regex_new ("\\{[Bb]itrate\\}", 0, 0, &gerror);
206 stream->regex_position = g_regex_new ("\\{start[ _]time\\}", 0, 0, &gerror);
210 gst_mss_manifest_new (const GstBuffer * data)
212 GstMssManifest *manifest;
216 manifest = g_malloc0 (sizeof (GstMssManifest));
218 manifest->xml = xmlReadMemory ((const gchar *) GST_BUFFER_DATA (data),
219 GST_BUFFER_SIZE (data), "manifest", NULL, 0);
220 root = manifest->xmlrootnode = xmlDocGetRootElement (manifest->xml);
222 for (nodeiter = root->children; nodeiter; nodeiter = nodeiter->next) {
223 if (nodeiter->type == XML_ELEMENT_NODE
224 && (strcmp ((const char *) nodeiter->name, "StreamIndex") == 0)) {
225 GstMssStream *stream = g_new0 (GstMssStream, 1);
227 manifest->streams = g_slist_append (manifest->streams, stream);
228 _gst_mss_stream_init (stream, nodeiter);
236 gst_mss_stream_free (GstMssStream * stream)
238 g_list_free_full (stream->fragments, g_free);
239 g_list_free_full (stream->qualities,
240 (GDestroyNotify) gst_mss_stream_quality_free);
241 g_free (stream->url);
242 g_regex_unref (stream->regex_position);
243 g_regex_unref (stream->regex_bitrate);
248 gst_mss_manifest_free (GstMssManifest * manifest)
250 g_return_if_fail (manifest != NULL);
252 g_slist_free_full (manifest->streams, (GDestroyNotify) gst_mss_stream_free);
254 xmlFreeDoc (manifest->xml);
259 gst_mss_manifest_get_streams (GstMssManifest * manifest)
261 return manifest->streams;
265 gst_mss_stream_get_type (GstMssStream * stream)
267 gchar *prop = (gchar *) xmlGetProp (stream->xmlnode, (xmlChar *) "Type");
268 GstMssStreamType ret = MSS_STREAM_TYPE_UNKNOWN;
270 if (strcmp (prop, "video") == 0) {
271 ret = MSS_STREAM_TYPE_VIDEO;
272 } else if (strcmp (prop, "audio") == 0) {
273 ret = MSS_STREAM_TYPE_AUDIO;
280 _gst_mss_stream_video_caps_from_fourcc (gchar * fourcc)
285 if (strcmp (fourcc, "H264") == 0 || strcmp (fourcc, "AVC1") == 0) {
286 return gst_caps_new_simple ("video/x-h264", "stream-format", G_TYPE_STRING,
288 } else if (strcmp (fourcc, "WVC1") == 0) {
289 return gst_caps_new_simple ("video/x-wmv", "wmvversion", G_TYPE_INT, 3,
296 _gst_mss_stream_audio_caps_from_fourcc (gchar * fourcc)
301 if (strcmp (fourcc, "AACL") == 0) {
302 return gst_caps_new_simple ("audio/mpeg", "mpegversion", G_TYPE_INT, 4,
304 } else if (strcmp (fourcc, "WmaPro") == 0) {
305 return gst_caps_new_simple ("audio/x-wma", "wmaversion", G_TYPE_INT, 2,
311 /* copied and adapted from h264parse */
313 _make_h264_codec_data (GstBuffer * sps, GstBuffer * pps)
316 gint sps_size = 0, pps_size = 0, num_sps = 0, num_pps = 0;
317 guint8 profile_idc = 0, profile_comp = 0, level_idc = 0;
321 sps_size += GST_BUFFER_SIZE (sps) + 2;
322 profile_idc = GST_BUFFER_DATA (sps)[1];
323 profile_comp = GST_BUFFER_DATA (sps)[2];
324 level_idc = GST_BUFFER_DATA (sps)[3];
327 pps_size += GST_BUFFER_SIZE (pps) + 2;
330 buf = gst_buffer_new_and_alloc (5 + 1 + sps_size + 1 + pps_size);
331 data = GST_BUFFER_DATA (buf);
334 data[0] = 1; /* AVC Decoder Configuration Record ver. 1 */
335 data[1] = profile_idc; /* profile_idc */
336 data[2] = profile_comp; /* profile_compability */
337 data[3] = level_idc; /* level_idc */
338 data[4] = 0xfc | (nl - 1); /* nal_length_size_minus1 */
339 data[5] = 0xe0 | num_sps; /* number of SPSs */
342 GST_WRITE_UINT16_BE (data, GST_BUFFER_SIZE (sps));
343 memcpy (data + 2, GST_BUFFER_DATA (sps), GST_BUFFER_SIZE (sps));
344 data += 2 + GST_BUFFER_SIZE (sps);
348 GST_WRITE_UINT16_BE (data, GST_BUFFER_SIZE (pps));
349 memcpy (data + 2, GST_BUFFER_DATA (pps), GST_BUFFER_SIZE (pps));
350 data += 2 + GST_BUFFER_SIZE (pps);
356 _gst_mss_stream_add_h264_codec_data (GstCaps * caps, const gchar * codecdatastr)
358 GValue sps_value = { 0, };
359 GValue pps_value = { 0, };
366 GstH264SPS sps_struct;
367 GstH264ParserResult parseres;
369 /* search for the sps start */
370 if (g_str_has_prefix (codecdatastr, "00000001")) {
371 sps_str = (gchar *) codecdatastr + 8;
373 return; /* invalid mss codec data */
376 /* search for the pps start */
377 pps_str = g_strstr_len (sps_str, -1, "00000001");
379 return; /* invalid mss codec data */
382 g_value_init (&sps_value, GST_TYPE_BUFFER);
384 gst_value_deserialize (&sps_value, sps_str);
387 g_value_init (&pps_value, GST_TYPE_BUFFER);
388 pps_str = pps_str + 8;
389 gst_value_deserialize (&pps_value, pps_str);
391 sps = gst_value_get_buffer (&sps_value);
392 pps = gst_value_get_buffer (&pps_value);
394 nalu.ref_idc = (GST_BUFFER_DATA (sps)[0] & 0x60) >> 5;
395 nalu.type = GST_H264_NAL_SPS;
396 nalu.size = GST_BUFFER_SIZE (sps);
397 nalu.data = GST_BUFFER_DATA (sps);
402 parseres = gst_h264_parse_sps (&nalu, &sps_struct, TRUE);
403 if (parseres == GST_H264_PARSER_OK) {
404 gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION,
405 sps_struct.fps_num, sps_struct.fps_den, NULL);
408 buffer = _make_h264_codec_data (sps, pps);
409 g_value_reset (&sps_value);
410 g_value_reset (&pps_value);
412 gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, buffer, NULL);
413 gst_buffer_unref (buffer);
417 _gst_mss_stream_video_caps_from_qualitylevel_xml (xmlNodePtr node)
420 GstStructure *structure;
421 gchar *fourcc = (gchar *) xmlGetProp (node, (xmlChar *) "FourCC");
422 gchar *max_width = (gchar *) xmlGetProp (node, (xmlChar *) "MaxWidth");
423 gchar *max_height = (gchar *) xmlGetProp (node, (xmlChar *) "MaxHeight");
425 (gchar *) xmlGetProp (node, (xmlChar *) "CodecPrivateData");
428 max_width = (gchar *) xmlGetProp (node, (xmlChar *) "Width");
430 max_height = (gchar *) xmlGetProp (node, (xmlChar *) "Height");
432 caps = _gst_mss_stream_video_caps_from_fourcc (fourcc);
436 structure = gst_caps_get_structure (caps, 0);
439 gst_structure_set (structure, "width", G_TYPE_INT, atoi (max_width), NULL);
441 gst_structure_set (structure, "height", G_TYPE_INT, atoi (max_height),
444 if (codec_data && strlen (codec_data)) {
445 if (strcmp (fourcc, "H264") == 0 || strcmp (fourcc, "AVC1") == 0) {
446 _gst_mss_stream_add_h264_codec_data (caps, codec_data);
448 GValue *value = g_new0 (GValue, 1);
449 g_value_init (value, GST_TYPE_BUFFER);
450 gst_value_deserialize (value, (gchar *) codec_data);
451 gst_structure_take_value (structure, "codec_data", value);
465 _gst_mss_stream_audio_caps_from_qualitylevel_xml (xmlNodePtr node)
468 GstStructure *structure;
469 gchar *fourcc = (gchar *) xmlGetProp (node, (xmlChar *) "FourCC");
470 gchar *channels = (gchar *) xmlGetProp (node, (xmlChar *) "Channels");
471 gchar *rate = (gchar *) xmlGetProp (node, (xmlChar *) "SamplingRate");
473 (gchar *) xmlGetProp (node, (xmlChar *) "CodecPrivateData");
475 if (!fourcc) /* sometimes the fourcc is omitted, we fallback to the Subtype in the StreamIndex node */
476 fourcc = (gchar *) xmlGetProp (node->parent, (xmlChar *) "Subtype");
478 codec_data = (gchar *) xmlGetProp (node, (xmlChar *) "WaveFormatEx");
480 caps = _gst_mss_stream_audio_caps_from_fourcc (fourcc);
484 structure = gst_caps_get_structure (caps, 0);
487 gst_structure_set (structure, "channels", G_TYPE_INT, atoi (channels),
490 gst_structure_set (structure, "rate", G_TYPE_INT, atoi (rate), NULL);
492 if (codec_data && strlen (codec_data)) {
493 GValue *value = g_new0 (GValue, 1);
494 g_value_init (value, GST_TYPE_BUFFER);
495 gst_value_deserialize (value, (gchar *) codec_data);
496 gst_structure_take_value (structure, "codec_data", value);
509 gst_mss_stream_set_active (GstMssStream * stream, gboolean active)
511 stream->active = active;
515 gst_mss_stream_get_timescale (GstMssStream * stream)
518 guint64 ts = DEFAULT_TIMESCALE;
521 (gchar *) xmlGetProp (stream->xmlnode, (xmlChar *) MSS_PROP_TIMESCALE);
524 (gchar *) xmlGetProp (stream->xmlnode->parent,
525 (xmlChar *) MSS_PROP_TIMESCALE);
529 ts = strtoull (timescale, NULL, 10);
536 gst_mss_manifest_get_timescale (GstMssManifest * manifest)
539 guint64 ts = DEFAULT_TIMESCALE;
542 (gchar *) xmlGetProp (manifest->xmlrootnode,
543 (xmlChar *) MSS_PROP_TIMESCALE);
545 ts = strtoull (timescale, NULL, 10);
552 gst_mss_manifest_get_duration (GstMssManifest * manifest)
558 (gchar *) xmlGetProp (manifest->xmlrootnode,
559 (xmlChar *) MSS_PROP_STREAM_DURATION);
561 dur = strtoull (duration, NULL, 10);
569 * Gets the duration in nanoseconds
572 gst_mss_manifest_get_gst_duration (GstMssManifest * manifest)
574 guint64 duration = -1;
576 GstClockTime gstdur = GST_CLOCK_TIME_NONE;
578 duration = gst_mss_manifest_get_duration (manifest);
579 timescale = gst_mss_manifest_get_timescale (manifest);
581 if (duration != -1 && timescale != -1)
583 (GstClockTime) gst_util_uint64_scale_round (duration, GST_SECOND,
590 gst_mss_stream_get_caps (GstMssStream * stream)
592 GstMssStreamType streamtype = gst_mss_stream_get_type (stream);
593 GstMssStreamQuality *qualitylevel = stream->current_quality->data;
594 GstCaps *caps = NULL;
596 if (streamtype == MSS_STREAM_TYPE_VIDEO)
598 _gst_mss_stream_video_caps_from_qualitylevel_xml
599 (qualitylevel->xmlnode);
600 else if (streamtype == MSS_STREAM_TYPE_AUDIO)
602 _gst_mss_stream_audio_caps_from_qualitylevel_xml
603 (qualitylevel->xmlnode);
609 gst_mss_stream_get_fragment_url (GstMssStream * stream, gchar ** url)
612 gchar *start_time_str;
613 GstMssStreamFragment *fragment;
614 GstMssStreamQuality *quality = stream->current_quality->data;
616 g_return_val_if_fail (stream->active, GST_FLOW_ERROR);
618 if (stream->current_fragment == NULL) /* stream is over */
619 return GST_FLOW_UNEXPECTED;
621 fragment = stream->current_fragment->data;
623 start_time_str = g_strdup_printf ("%" G_GUINT64_FORMAT, fragment->time);
625 tmp = g_regex_replace_literal (stream->regex_bitrate, stream->url,
626 strlen (stream->url), 0, quality->bitrate_str, 0, NULL);
627 *url = g_regex_replace_literal (stream->regex_position, tmp,
628 strlen (tmp), 0, start_time_str, 0, NULL);
631 g_free (start_time_str);
636 gst_mss_stream_get_fragment_gst_timestamp (GstMssStream * stream)
640 GstMssStreamFragment *fragment;
642 g_return_val_if_fail (stream->active, GST_FLOW_ERROR);
644 if (!stream->current_fragment)
645 return GST_CLOCK_TIME_NONE;
647 fragment = stream->current_fragment->data;
649 time = fragment->time;
650 timescale = gst_mss_stream_get_timescale (stream);
651 return (GstClockTime) gst_util_uint64_scale_round (time, GST_SECOND,
656 gst_mss_stream_get_fragment_gst_duration (GstMssStream * stream)
660 GstMssStreamFragment *fragment;
662 g_return_val_if_fail (stream->active, GST_FLOW_ERROR);
664 if (!stream->current_fragment)
665 return GST_CLOCK_TIME_NONE;
667 fragment = stream->current_fragment->data;
669 dur = fragment->duration;
670 timescale = gst_mss_stream_get_timescale (stream);
671 return (GstClockTime) gst_util_uint64_scale_round (dur, GST_SECOND,
676 gst_mss_stream_advance_fragment (GstMssStream * stream)
678 g_return_val_if_fail (stream->active, GST_FLOW_ERROR);
680 if (stream->current_fragment == NULL)
681 return GST_FLOW_UNEXPECTED;
683 stream->current_fragment = g_list_next (stream->current_fragment);
684 if (stream->current_fragment == NULL)
685 return GST_FLOW_UNEXPECTED;
690 gst_mss_stream_type_name (GstMssStreamType streamtype)
692 switch (streamtype) {
693 case MSS_STREAM_TYPE_VIDEO:
695 case MSS_STREAM_TYPE_AUDIO:
697 case MSS_STREAM_TYPE_UNKNOWN:
704 * Seeks all streams to the fragment that contains the set time
706 * @time: time in nanoseconds
709 gst_mss_manifest_seek (GstMssManifest * manifest, guint64 time)
714 for (iter = manifest->streams; iter; iter = g_slist_next (iter)) {
715 ret = gst_mss_stream_seek (iter->data, time) & ret;
722 * Seeks this stream to the fragment that contains the sample at time
724 * @time: time in nanoseconds
727 gst_mss_stream_seek (GstMssStream * stream, guint64 time)
732 timescale = gst_mss_stream_get_timescale (stream);
733 time = gst_util_uint64_scale_round (time, timescale, GST_SECOND);
735 for (iter = stream->fragments; iter; iter = g_list_next (iter)) {
736 GList *next = g_list_next (iter);
738 GstMssStreamFragment *fragment = next->data;
740 if (fragment->time > time) {
741 stream->current_fragment = iter;
745 GstMssStreamFragment *fragment = iter->data;
746 if (fragment->time + fragment->duration > time) {
747 stream->current_fragment = iter;
749 stream->current_fragment = NULL; /* EOS */
759 gst_mss_manifest_get_current_bitrate (GstMssManifest * manifest)
764 for (iter = gst_mss_manifest_get_streams (manifest); iter;
765 iter = g_slist_next (iter)) {
766 GstMssStream *stream = iter->data;
767 if (stream->active && stream->current_quality) {
768 GstMssStreamQuality *q = stream->current_quality->data;
770 bitrate += q->bitrate;
778 gst_mss_stream_select_bitrate (GstMssStream * stream, guint64 bitrate)
780 GList *iter = stream->current_quality;
782 GstMssStreamQuality *q = iter->data;
784 while (q->bitrate > bitrate) {
785 next = g_list_previous (iter);
794 while (q->bitrate < bitrate) {
795 GstMssStreamQuality *next_q;
796 next = g_list_next (iter);
799 if (next_q->bitrate < bitrate) {
810 if (iter == stream->current_quality)
812 stream->current_quality = iter;
817 * gst_mss_manifest_change_bitrate:
818 * @manifest: the manifest
819 * @bitrate: the maximum bitrate to use (bps)
821 * Iterates over the active streams and changes their bitrates to the maximum
822 * value so that the bitrates of all streams are not larger than
825 * Return: %TRUE if any stream changed its bitrate
828 gst_mss_manifest_change_bitrate (GstMssManifest * manifest, guint64 bitrate)
830 gboolean ret = FALSE;
833 /* TODO This algorithm currently sets the same bitrate for all streams,
834 * it should actually use the sum of all streams bitrates to compare to
835 * the target value */
839 bitrate = G_MAXUINT64;
842 for (iter = gst_mss_manifest_get_streams (manifest); iter;
843 iter = g_slist_next (iter)) {
844 GstMssStream *stream = iter->data;
845 if (stream->active) {
846 ret = ret | gst_mss_stream_select_bitrate (stream, bitrate);