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.
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.
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.
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.
29 #include <libxml/parser.h>
30 #include <libxml/tree.h>
32 #include "gstmssmanifest.h"
33 #include "gstmssfragmentparser.h"
35 GST_DEBUG_CATEGORY_EXTERN (mssdemux2_debug);
36 #define GST_CAT_DEFAULT mssdemux2_debug
38 #define DEFAULT_TIMESCALE 10000000
40 #define MSS_NODE_STREAM_FRAGMENT "c"
41 #define MSS_NODE_STREAM_QUALITY "QualityLevel"
43 #define MSS_PROP_BITRATE "Bitrate"
44 #define MSS_PROP_DURATION "d"
45 #define MSS_PROP_DVR_WINDOW_LENGTH "DVRWindowLength"
46 #define MSS_PROP_LANGUAGE "Language"
47 #define MSS_PROP_NAME "Name"
48 #define MSS_PROP_NUMBER "n"
49 #define MSS_PROP_REPETITIONS "r"
50 #define MSS_PROP_STREAM_DURATION "Duration"
51 #define MSS_PROP_TIME "t"
52 #define MSS_PROP_TIMESCALE "TimeScale"
53 #define MSS_PROP_URL "Url"
55 #define GST_MSSMANIFEST_LIVE_MIN_FRAGMENT_DISTANCE 3
57 typedef struct _GstMssStreamFragment
63 } GstMssStreamFragment;
65 typedef struct _GstMssStreamQuality
71 } GstMssStreamQuality;
77 gboolean active; /* if the stream is currently being used */
78 gint selectedQualityIndex;
80 gboolean has_live_fragments;
81 GstAdapter *live_adapter;
90 GstMssFragmentParser fragment_parser;
92 guint fragment_repetition_index;
93 GList *current_fragment;
94 GList *current_quality;
96 /* TODO move this to somewhere static */
97 GRegex *regex_bitrate;
98 GRegex *regex_position;
101 struct _GstMssManifest
104 xmlNodePtr xmlrootnode;
108 guint64 look_ahead_fragment_count;
110 GString *protection_system_id;
111 gchar *protection_data;
116 /* For parsing and building a fragments list */
117 typedef struct _GstMssFragmentListBuilder
121 GstMssStreamFragment *previous_fragment;
122 guint fragment_number;
123 guint64 fragment_time_accum;
124 } GstMssFragmentListBuilder;
127 gst_mss_fragment_list_builder_init (GstMssFragmentListBuilder * builder)
129 builder->fragments = NULL;
130 builder->previous_fragment = NULL;
131 builder->fragment_time_accum = 0;
132 builder->fragment_number = 0;
136 gst_mss_fragment_list_builder_add (GstMssFragmentListBuilder * builder,
142 gchar *repetition_str;
143 GstMssStreamFragment *fragment = g_new (GstMssStreamFragment, 1);
145 duration_str = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_DURATION);
146 time_str = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_TIME);
147 seqnum_str = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_NUMBER);
149 (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_REPETITIONS);
151 /* use the node's seq number or use the previous + 1 */
153 fragment->number = g_ascii_strtoull (seqnum_str, NULL, 10);
154 xmlFree (seqnum_str);
155 builder->fragment_number = fragment->number;
157 fragment->number = builder->fragment_number;
159 builder->fragment_number = fragment->number + 1;
161 if (repetition_str) {
162 fragment->repetitions = g_ascii_strtoull (repetition_str, NULL, 10);
163 xmlFree (repetition_str);
165 fragment->repetitions = 1;
169 fragment->time = g_ascii_strtoull (time_str, NULL, 10);
172 builder->fragment_time_accum = fragment->time;
174 fragment->time = builder->fragment_time_accum;
177 /* if we have a previous fragment, means we need to set its duration */
178 if (builder->previous_fragment)
179 builder->previous_fragment->duration =
181 builder->previous_fragment->time) /
182 builder->previous_fragment->repetitions;
185 fragment->duration = g_ascii_strtoull (duration_str, NULL, 10);
187 builder->previous_fragment = NULL;
188 builder->fragment_time_accum += fragment->duration * fragment->repetitions;
189 xmlFree (duration_str);
191 /* store to set the duration at the next iteration */
192 builder->previous_fragment = fragment;
195 /* we reverse it later */
196 builder->fragments = g_list_prepend (builder->fragments, fragment);
197 GST_LOG ("Adding fragment number: %u, time: %" G_GUINT64_FORMAT
198 ", duration: %" G_GUINT64_FORMAT ", repetitions: %u",
199 fragment->number, fragment->time, fragment->duration,
200 fragment->repetitions);
203 static GstBuffer *gst_buffer_from_hex_string (const gchar * s);
206 node_has_type (xmlNodePtr node, const gchar * name)
208 return strcmp ((gchar *) node->name, name) == 0;
211 static GstMssStreamQuality *
212 gst_mss_stream_quality_new (xmlNodePtr node)
214 GstMssStreamQuality *q = g_slice_new (GstMssStreamQuality);
217 q->bitrate_str = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_BITRATE);
219 if (q->bitrate_str != NULL)
220 q->bitrate = g_ascii_strtoull (q->bitrate_str, NULL, 10);
228 gst_mss_stream_quality_free (GstMssStreamQuality * quality)
230 g_return_if_fail (quality != NULL);
232 xmlFree (quality->bitrate_str);
233 g_slice_free (GstMssStreamQuality, quality);
237 compare_bitrate (GstMssStreamQuality * a, GstMssStreamQuality * b)
239 if (a->bitrate > b->bitrate)
241 if (a->bitrate < b->bitrate)
248 _gst_mss_stream_init (GstMssManifest * manifest, GstMssStream * stream,
252 GstMssFragmentListBuilder builder;
254 gst_mss_fragment_list_builder_init (&builder);
256 stream->xmlnode = node;
258 /* get the base url path generator */
259 stream->name = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_NAME);
260 stream->url = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_URL);
261 stream->lang = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_LANGUAGE);
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
269 GST_DEBUG ("lang '%s' url %s", stream->lang, stream->url);
270 GST_DEBUG ("Live stream: %s, look-ahead fragments: %" G_GUINT64_FORMAT,
271 manifest->is_live ? "yes" : "no", manifest->look_ahead_fragment_count);
272 stream->has_live_fragments = manifest->is_live
273 && manifest->look_ahead_fragment_count;
275 for (iter = node->children; iter; iter = iter->next) {
276 GST_LOG ("Handling child '%s'", iter->name);
277 if (node_has_type (iter, MSS_NODE_STREAM_FRAGMENT)) {
278 gst_mss_fragment_list_builder_add (&builder, iter);
279 } else if (node_has_type (iter, MSS_NODE_STREAM_QUALITY)) {
280 GstMssStreamQuality *quality = gst_mss_stream_quality_new (iter);
281 stream->qualities = g_list_prepend (stream->qualities, quality);
283 /* TODO gst log this */
287 if (stream->has_live_fragments) {
288 stream->live_adapter = gst_adapter_new ();
291 if (builder.fragments) {
292 stream->fragments = g_list_reverse (builder.fragments);
293 if (manifest->is_live) {
294 GList *iter = g_list_last (stream->fragments);
297 for (i = 0; i < GST_MSSMANIFEST_LIVE_MIN_FRAGMENT_DISTANCE; i++) {
298 if (g_list_previous (iter)) {
299 iter = g_list_previous (iter);
304 stream->current_fragment = iter;
306 stream->current_fragment = stream->fragments;
310 /* order them from smaller to bigger based on bitrates */
312 g_list_sort (stream->qualities, (GCompareFunc) compare_bitrate);
313 stream->current_quality = stream->qualities;
315 stream->regex_bitrate = g_regex_new ("\\{[Bb]itrate\\}", 0, 0, NULL);
316 stream->regex_position = g_regex_new ("\\{start[ _]time\\}", 0, 0, NULL);
318 gst_mss_fragment_parser_init (&stream->fragment_parser);
323 _gst_mss_parse_protection (GstMssManifest * manifest,
324 xmlNodePtr protection_node)
328 for (nodeiter = protection_node->children; nodeiter;
329 nodeiter = nodeiter->next) {
330 if (nodeiter->type == XML_ELEMENT_NODE
331 && (strcmp ((const char *) nodeiter->name, "ProtectionHeader") == 0)) {
332 xmlChar *system_id_attribute =
333 xmlGetProp (nodeiter, (xmlChar *) "SystemID");
334 gchar *value = (gchar *) system_id_attribute;
335 int id_len = strlen (value);
338 if (value[0] == '{') {
343 system_id = g_string_new (value);
344 system_id = g_string_ascii_down (system_id);
345 if (value[id_len - 1] == '}')
346 system_id = g_string_truncate (system_id, id_len - 1);
348 manifest->protection_system_id = system_id;
349 manifest->protection_data = (gchar *) xmlNodeGetContent (nodeiter);
350 xmlFree (system_id_attribute);
357 gst_mss_manifest_new (GstBuffer * data)
359 GstMssManifest *manifest;
364 gchar *look_ahead_fragment_count_str;
366 if (!gst_buffer_map (data, &mapinfo, GST_MAP_READ)) {
370 manifest = g_malloc0 (sizeof (GstMssManifest));
372 manifest->xml = xmlReadMemory ((const gchar *) mapinfo.data,
373 mapinfo.size, "manifest", NULL, 0);
374 root = manifest->xmlrootnode = xmlDocGetRootElement (manifest->xml);
376 GST_WARNING ("No root node ! Invalid manifest");
377 gst_mss_manifest_free (manifest);
381 live_str = (gchar *) xmlGetProp (root, (xmlChar *) "IsLive");
383 manifest->is_live = g_ascii_strcasecmp (live_str, "true") == 0;
387 /* the entire file is always available for non-live streams */
388 if (!manifest->is_live) {
389 manifest->dvr_window = 0;
390 manifest->look_ahead_fragment_count = 0;
392 /* if 0, or non-existent, the length is infinite */
393 gchar *dvr_window_str = (gchar *) xmlGetProp (root,
394 (xmlChar *) MSS_PROP_DVR_WINDOW_LENGTH);
395 if (dvr_window_str) {
396 manifest->dvr_window = g_ascii_strtoull (dvr_window_str, NULL, 10);
397 xmlFree (dvr_window_str);
398 if (manifest->dvr_window <= 0) {
399 manifest->dvr_window = 0;
403 look_ahead_fragment_count_str =
404 (gchar *) xmlGetProp (root, (xmlChar *) "LookAheadFragmentCount");
405 if (look_ahead_fragment_count_str) {
406 manifest->look_ahead_fragment_count =
407 g_ascii_strtoull (look_ahead_fragment_count_str, NULL, 10);
408 xmlFree (look_ahead_fragment_count_str);
409 if (manifest->look_ahead_fragment_count <= 0) {
410 manifest->look_ahead_fragment_count = 0;
415 for (nodeiter = root->children; nodeiter; nodeiter = nodeiter->next) {
416 if (nodeiter->type == XML_ELEMENT_NODE
417 && (strcmp ((const char *) nodeiter->name, "StreamIndex") == 0)) {
418 GstMssStream *stream = g_new0 (GstMssStream, 1);
420 manifest->streams = g_slist_append (manifest->streams, stream);
421 _gst_mss_stream_init (manifest, stream, nodeiter);
424 if (nodeiter->type == XML_ELEMENT_NODE
425 && (strcmp ((const char *) nodeiter->name, "Protection") == 0)) {
426 _gst_mss_parse_protection (manifest, nodeiter);
430 gst_buffer_unmap (data, &mapinfo);
436 gst_mss_stream_free (GstMssStream * stream)
438 if (stream->live_adapter) {
439 gst_adapter_clear (stream->live_adapter);
440 g_object_unref (stream->live_adapter);
443 g_list_free_full (stream->fragments, g_free);
444 g_list_free_full (stream->qualities,
445 (GDestroyNotify) gst_mss_stream_quality_free);
446 xmlFree (stream->url);
447 xmlFree (stream->name);
448 xmlFree (stream->lang);
449 g_regex_unref (stream->regex_position);
450 g_regex_unref (stream->regex_bitrate);
451 gst_mss_fragment_parser_clear (&stream->fragment_parser);
456 gst_mss_manifest_free (GstMssManifest * manifest)
458 g_return_if_fail (manifest != NULL);
460 g_slist_free_full (manifest->streams, (GDestroyNotify) gst_mss_stream_free);
462 if (manifest->protection_system_id != NULL)
463 g_string_free (manifest->protection_system_id, TRUE);
464 xmlFree (manifest->protection_data);
466 xmlFreeDoc (manifest->xml);
471 gst_mss_manifest_get_protection_system_id (GstMssManifest * manifest)
473 if (manifest->protection_system_id != NULL)
474 return manifest->protection_system_id->str;
479 gst_mss_manifest_get_protection_data (GstMssManifest * manifest)
481 return manifest->protection_data;
485 gst_mss_manifest_get_streams (GstMssManifest * manifest)
487 return manifest->streams;
491 gst_mss_stream_get_type (GstMssStream * stream)
493 gchar *prop = (gchar *) xmlGetProp (stream->xmlnode, (xmlChar *) "Type");
494 GstMssStreamType ret = MSS_STREAM_TYPE_UNKNOWN;
497 return MSS_STREAM_TYPE_UNKNOWN;
499 if (strcmp (prop, "video") == 0) {
500 ret = MSS_STREAM_TYPE_VIDEO;
501 } else if (strcmp (prop, "audio") == 0) {
502 ret = MSS_STREAM_TYPE_AUDIO;
504 GST_DEBUG ("Unsupported stream type: %s", prop);
511 _gst_mss_stream_video_caps_from_fourcc (gchar * fourcc)
516 if (strcmp (fourcc, "H264") == 0 || strcmp (fourcc, "AVC1") == 0) {
517 return gst_caps_new_simple ("video/x-h264", "stream-format", G_TYPE_STRING,
519 } else if (strcmp (fourcc, "WVC1") == 0) {
520 return gst_caps_new_simple ("video/x-wmv", "wmvversion", G_TYPE_INT, 3,
521 "format", G_TYPE_STRING, "WVC1", NULL);
527 _gst_mss_stream_audio_caps_from_fourcc (gchar * fourcc)
532 if (strcmp (fourcc, "AACL") == 0) {
533 return gst_caps_new_simple ("audio/mpeg", "mpegversion", G_TYPE_INT, 4,
535 } else if (strcmp (fourcc, "WmaPro") == 0 || strcmp (fourcc, "WMAP") == 0) {
536 return gst_caps_new_simple ("audio/x-wma", "wmaversion", G_TYPE_INT, 3,
543 _gst_mss_stream_audio_caps_from_audio_tag (gint audiotag)
547 return gst_caps_new_simple ("audio/mpeg", "mpegversion", G_TYPE_INT, 1,
548 "layer", G_TYPE_INT, 3, NULL);
550 return gst_caps_new_simple ("audio/mpeg", "mpegversion", G_TYPE_INT, 4,
558 /* copied and adapted from h264parse */
560 _make_h264_codec_data (GstBuffer * sps, GstBuffer * pps)
563 gint sps_size = 0, pps_size = 0, num_sps = 0, num_pps = 0;
564 guint8 profile_idc = 0, profile_comp = 0, level_idc = 0;
567 GstMapInfo spsinfo, ppsinfo, codecdatainfo;
569 if (gst_buffer_get_size (sps) < 4)
572 gst_buffer_map (sps, &spsinfo, GST_MAP_READ);
573 gst_buffer_map (pps, &ppsinfo, GST_MAP_READ);
575 sps_size += spsinfo.size + 2;
576 profile_idc = spsinfo.data[1];
577 profile_comp = spsinfo.data[2];
578 level_idc = spsinfo.data[3];
581 pps_size += ppsinfo.size + 2;
584 buf = gst_buffer_new_and_alloc (5 + 1 + sps_size + 1 + pps_size);
585 gst_buffer_map (buf, &codecdatainfo, GST_MAP_WRITE);
586 data = codecdatainfo.data;
589 data[0] = 1; /* AVC Decoder Configuration Record ver. 1 */
590 data[1] = profile_idc; /* profile_idc */
591 data[2] = profile_comp; /* profile_compability */
592 data[3] = level_idc; /* level_idc */
593 data[4] = 0xfc | (nl - 1); /* nal_length_size_minus1 */
594 data[5] = 0xe0 | num_sps; /* number of SPSs */
597 GST_WRITE_UINT16_BE (data, spsinfo.size);
598 memcpy (data + 2, spsinfo.data, spsinfo.size);
599 data += 2 + spsinfo.size;
603 GST_WRITE_UINT16_BE (data, ppsinfo.size);
604 memcpy (data + 2, ppsinfo.data, ppsinfo.size);
605 data += 2 + ppsinfo.size;
607 gst_buffer_unmap (sps, &spsinfo);
608 gst_buffer_unmap (pps, &ppsinfo);
609 gst_buffer_unmap (buf, &codecdatainfo);
615 _gst_mss_stream_add_h264_codec_data (GstCaps * caps, const gchar * codecdatastr)
623 /* search for the sps start */
624 if (g_str_has_prefix (codecdatastr, "00000001")) {
625 sps_str = (gchar *) codecdatastr + 8;
627 return; /* invalid mss codec data */
630 /* search for the pps start */
631 pps_str = g_strstr_len (sps_str, -1, "00000001");
633 return; /* invalid mss codec data */
637 sps = gst_buffer_from_hex_string (sps_str);
640 pps_str = pps_str + 8;
641 pps = gst_buffer_from_hex_string (pps_str);
643 buffer = _make_h264_codec_data (sps, pps);
644 gst_buffer_unref (sps);
645 gst_buffer_unref (pps);
647 if (buffer != NULL) {
648 gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, buffer, NULL);
649 gst_buffer_unref (buffer);
654 _gst_mss_stream_video_caps_from_qualitylevel_xml (GstMssStreamQuality * q)
656 xmlNodePtr node = q->xmlnode;
658 GstStructure *structure;
659 gchar *fourcc = (gchar *) xmlGetProp (node, (xmlChar *) "FourCC");
660 gchar *max_width = (gchar *) xmlGetProp (node, (xmlChar *) "MaxWidth");
661 gchar *max_height = (gchar *) xmlGetProp (node, (xmlChar *) "MaxHeight");
663 (gchar *) xmlGetProp (node, (xmlChar *) "CodecPrivateData");
666 max_width = (gchar *) xmlGetProp (node, (xmlChar *) "Width");
668 max_height = (gchar *) xmlGetProp (node, (xmlChar *) "Height");
670 caps = _gst_mss_stream_video_caps_from_fourcc (fourcc);
674 structure = gst_caps_get_structure (caps, 0);
677 gst_structure_set (structure, "width", G_TYPE_INT,
678 (int) g_ascii_strtoull (max_width, NULL, 10), NULL);
681 gst_structure_set (structure, "height", G_TYPE_INT,
682 (int) g_ascii_strtoull (max_height, NULL, 10), NULL);
685 if (codec_data && strlen (codec_data)) {
686 if (strcmp (fourcc, "H264") == 0 || strcmp (fourcc, "AVC1") == 0) {
687 _gst_mss_stream_add_h264_codec_data (caps, codec_data);
689 GstBuffer *buffer = gst_buffer_from_hex_string ((gchar *) codec_data);
690 gst_structure_set (structure, "codec_data", GST_TYPE_BUFFER, buffer,
692 gst_buffer_unref (buffer);
699 xmlFree (max_height);
700 xmlFree (codec_data);
706 _frequency_index_from_sampling_rate (guint sampling_rate)
708 static const guint aac_sample_rates[] = { 96000, 88200, 64000, 48000, 44100,
709 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350
714 for (i = 0; i < G_N_ELEMENTS (aac_sample_rates); i++) {
715 if (aac_sample_rates[i] == sampling_rate)
722 _make_aacl_codec_data (guint64 sampling_rate, guint64 channels)
726 guint8 frequency_index;
731 frequency_index = _frequency_index_from_sampling_rate (sampling_rate);
732 if (frequency_index == 15)
735 buf = gst_buffer_new_and_alloc (buf_size);
736 gst_buffer_map (buf, &info, GST_MAP_WRITE);
739 data[0] = 2 << 3; /* AAC-LC object type is 2 */
740 data[0] += frequency_index >> 1;
741 data[1] = (frequency_index & 0x01) << 7;
743 /* Sampling rate is not in frequencies table, write manually */
744 if (frequency_index == 15) {
745 data[1] += sampling_rate >> 17;
746 data[2] = (sampling_rate >> 9) & 0xFF;
747 data[3] = (sampling_rate >> 1) & 0xFF;
748 data[4] = sampling_rate & 0x01;
752 data[1] += (channels & 0x0F) << 3;
754 gst_buffer_unmap (buf, &info);
760 _gst_mss_stream_audio_caps_from_qualitylevel_xml (GstMssStreamQuality * q)
762 xmlNodePtr node = q->xmlnode;
763 GstCaps *caps = NULL;
764 GstStructure *structure;
765 gchar *fourcc = (gchar *) xmlGetProp (node, (xmlChar *) "FourCC");
766 gchar *audiotag = (gchar *) xmlGetProp (node, (xmlChar *) "AudioTag");
767 gchar *channels_str = (gchar *) xmlGetProp (node, (xmlChar *) "Channels");
768 gchar *rate_str = (gchar *) xmlGetProp (node, (xmlChar *) "SamplingRate");
769 gchar *depth_str = (gchar *) xmlGetProp (node, (xmlChar *) "BitsPerSample");
770 gchar *block_align_str =
771 (gchar *) xmlGetProp (node, (xmlChar *) "PacketSize");
772 gchar *codec_data_str =
773 (gchar *) xmlGetProp (node, (xmlChar *) "CodecPrivateData");
774 GstBuffer *codec_data = NULL;
776 gint block_align = 0;
781 if (!fourcc) /* sometimes the fourcc is omitted, we fallback to the Subtype in the StreamIndex node */
782 fourcc = (gchar *) xmlGetProp (node->parent, (xmlChar *) "Subtype");
785 caps = _gst_mss_stream_audio_caps_from_fourcc (fourcc);
786 } else if (audiotag) {
787 atag = g_ascii_strtoull (audiotag, NULL, 10);
788 caps = _gst_mss_stream_audio_caps_from_audio_tag (atag);
794 structure = gst_caps_get_structure (caps, 0);
795 if (codec_data_str && strlen (codec_data_str)) {
796 codec_data = gst_buffer_from_hex_string ((gchar *) codec_data_str);
800 rate = (gint) g_ascii_strtoull (rate_str, NULL, 10);
802 channels = (int) g_ascii_strtoull (channels_str, NULL, 10);
804 depth = (gint) g_ascii_strtoull (depth_str, NULL, 10);
806 block_align = (int) g_ascii_strtoull (block_align_str, NULL, 10);
810 codec_data_str = (gchar *) xmlGetProp (node, (xmlChar *) "WaveFormatEx");
812 if (codec_data_str != NULL) {
813 codec_data_len = strlen (codec_data_str) / 2;
815 /* a WAVEFORMATEX structure is 18 bytes */
816 if (codec_data_str && codec_data_len >= 18) {
818 codec_data = gst_buffer_from_hex_string ((gchar *) codec_data_str);
820 /* since this is a WAVEFORMATEX, try to get the block_align and rate */
821 gst_buffer_map (codec_data, &mapinfo, GST_MAP_READ);
823 channels = GST_READ_UINT16_LE (mapinfo.data + 2);
826 rate = GST_READ_UINT32_LE (mapinfo.data + 4);
829 block_align = GST_READ_UINT16_LE (mapinfo.data + 12);
832 depth = GST_READ_UINT16_LE (mapinfo.data + 14);
834 gst_buffer_unmap (codec_data, &mapinfo);
836 /* Consume all the WAVEFORMATEX structure, and pass only the rest of
837 * the data as the codec private data */
838 gst_buffer_resize (codec_data, 18, -1);
840 GST_WARNING ("Dropping WaveFormatEx: data is %d bytes, "
841 "but at least 18 bytes are expected", codec_data_len);
846 if (!codec_data && ((fourcc && strcmp (fourcc, "AACL") == 0) || atag == 255)
847 && rate && channels) {
848 codec_data = _make_aacl_codec_data (rate, channels);
852 gst_structure_set (structure, "block_align", G_TYPE_INT, block_align, NULL);
855 gst_structure_set (structure, "channels", G_TYPE_INT, channels, NULL);
858 gst_structure_set (structure, "rate", G_TYPE_INT, rate, NULL);
861 gst_structure_set (structure, "depth", G_TYPE_INT, depth, NULL);
864 gst_structure_set (structure, "bitrate", G_TYPE_INT, (int) q->bitrate,
868 gst_structure_set (structure, "codec_data", GST_TYPE_BUFFER, codec_data,
873 gst_buffer_unref (codec_data);
876 xmlFree (channels_str);
879 xmlFree (block_align_str);
880 xmlFree (codec_data_str);
886 gst_mss_stream_set_active (GstMssStream * stream, gboolean active)
888 stream->active = active;
892 gst_mss_stream_get_timescale (GstMssStream * stream)
895 guint64 ts = DEFAULT_TIMESCALE;
898 (gchar *) xmlGetProp (stream->xmlnode, (xmlChar *) MSS_PROP_TIMESCALE);
901 (gchar *) xmlGetProp (stream->xmlnode->parent,
902 (xmlChar *) MSS_PROP_TIMESCALE);
906 ts = g_ascii_strtoull (timescale, NULL, 10);
913 gst_mss_manifest_get_timescale (GstMssManifest * manifest)
916 guint64 ts = DEFAULT_TIMESCALE;
919 (gchar *) xmlGetProp (manifest->xmlrootnode,
920 (xmlChar *) MSS_PROP_TIMESCALE);
922 ts = g_ascii_strtoull (timescale, NULL, 10);
929 gst_mss_manifest_get_duration (GstMssManifest * manifest)
934 /* try the property */
936 (gchar *) xmlGetProp (manifest->xmlrootnode,
937 (xmlChar *) MSS_PROP_STREAM_DURATION);
939 dur = g_ascii_strtoull (duration, NULL, 10);
942 /* else use the fragment list */
947 for (iter = manifest->streams; iter; iter = g_slist_next (iter)) {
948 GstMssStream *stream = iter->data;
950 if (stream->active) {
951 if (stream->fragments) {
952 GList *l = g_list_last (stream->fragments);
953 GstMssStreamFragment *fragment = (GstMssStreamFragment *) l->data;
955 fragment->time + fragment->duration * fragment->repetitions;
956 max_dur = MAX (frag_dur, max_dur);
970 * Gets the duration in nanoseconds
973 gst_mss_manifest_get_gst_duration (GstMssManifest * manifest)
975 guint64 duration = -1;
977 GstClockTime gstdur = GST_CLOCK_TIME_NONE;
979 duration = gst_mss_manifest_get_duration (manifest);
980 timescale = gst_mss_manifest_get_timescale (manifest);
982 if (duration != -1 && timescale != -1)
984 (GstClockTime) gst_util_uint64_scale_round (duration, GST_SECOND,
991 gst_mss_manifest_get_min_fragment_duration (GstMssManifest * manifest)
994 GstClockTime dur = GST_CLOCK_TIME_NONE;
995 GstClockTime iter_dur;
997 for (iter = manifest->streams; iter; iter = g_slist_next (iter)) {
998 GstMssStream *stream = iter->data;
1000 iter_dur = gst_mss_stream_get_fragment_gst_duration (stream);
1001 if (iter_dur != GST_CLOCK_TIME_NONE && iter_dur != 0) {
1002 if (GST_CLOCK_TIME_IS_VALID (dur)) {
1003 dur = MIN (dur, iter_dur);
1014 gst_mss_stream_get_caps (GstMssStream * stream)
1016 GstMssStreamType streamtype = gst_mss_stream_get_type (stream);
1017 GstMssStreamQuality *qualitylevel = stream->current_quality->data;
1018 GstCaps *caps = NULL;
1020 if (streamtype == MSS_STREAM_TYPE_VIDEO)
1021 caps = _gst_mss_stream_video_caps_from_qualitylevel_xml (qualitylevel);
1022 else if (streamtype == MSS_STREAM_TYPE_AUDIO)
1023 caps = _gst_mss_stream_audio_caps_from_qualitylevel_xml (qualitylevel);
1029 gst_mss_stream_get_fragment_url (GstMssStream * stream, gchar ** url)
1032 gchar *start_time_str;
1034 GstMssStreamFragment *fragment;
1035 GstMssStreamQuality *quality = stream->current_quality->data;
1037 g_return_val_if_fail (stream->active, GST_FLOW_ERROR);
1039 if (stream->current_fragment == NULL) /* stream is over */
1040 return GST_FLOW_EOS;
1042 fragment = stream->current_fragment->data;
1045 fragment->time + fragment->duration * stream->fragment_repetition_index;
1046 start_time_str = g_strdup_printf ("%" G_GUINT64_FORMAT, time);
1048 tmp = g_regex_replace_literal (stream->regex_bitrate, stream->url,
1049 strlen (stream->url), 0, quality->bitrate_str, 0, NULL);
1050 *url = g_regex_replace_literal (stream->regex_position, tmp,
1051 strlen (tmp), 0, start_time_str, 0, NULL);
1054 g_free (start_time_str);
1057 return GST_FLOW_ERROR;
1063 gst_mss_stream_get_fragment_gst_timestamp (GstMssStream * stream)
1067 GstMssStreamFragment *fragment;
1069 g_return_val_if_fail (stream->active, GST_CLOCK_TIME_NONE);
1071 if (!stream->current_fragment) {
1072 GList *last = g_list_last (stream->fragments);
1074 return GST_CLOCK_TIME_NONE;
1076 fragment = last->data;
1077 time = fragment->time + (fragment->duration * fragment->repetitions);
1079 fragment = stream->current_fragment->data;
1082 (fragment->duration * stream->fragment_repetition_index);
1085 timescale = gst_mss_stream_get_timescale (stream);
1086 return (GstClockTime) gst_util_uint64_scale_round (time, GST_SECOND,
1091 gst_mss_stream_get_fragment_gst_duration (GstMssStream * stream)
1095 GstMssStreamFragment *fragment;
1097 g_return_val_if_fail (stream->active, GST_FLOW_ERROR);
1099 if (!stream->current_fragment)
1100 return GST_CLOCK_TIME_NONE;
1102 fragment = stream->current_fragment->data;
1104 dur = fragment->duration;
1105 timescale = gst_mss_stream_get_timescale (stream);
1106 return (GstClockTime) gst_util_uint64_scale_round (dur, GST_SECOND,
1111 gst_mss_stream_has_next_fragment (GstMssStream * stream)
1113 g_return_val_if_fail (stream->active, FALSE);
1115 if (stream->current_fragment == NULL)
1122 gst_mss_stream_advance_fragment (GstMssStream * stream)
1124 GstMssStreamFragment *fragment;
1125 const gchar *stream_type_name =
1126 gst_mss_stream_type_name (gst_mss_stream_get_type (stream));
1128 g_return_val_if_fail (stream->active, GST_FLOW_ERROR);
1130 if (stream->current_fragment == NULL)
1131 return GST_FLOW_EOS;
1133 fragment = stream->current_fragment->data;
1134 stream->fragment_repetition_index++;
1135 if (stream->fragment_repetition_index < fragment->repetitions)
1138 stream->fragment_repetition_index = 0;
1139 stream->current_fragment = g_list_next (stream->current_fragment);
1141 GST_DEBUG ("Advanced to fragment #%d on %s stream", fragment->number,
1143 if (stream->current_fragment == NULL)
1144 return GST_FLOW_EOS;
1147 gst_mss_fragment_parser_clear (&stream->fragment_parser);
1148 gst_mss_fragment_parser_init (&stream->fragment_parser);
1153 gst_mss_stream_regress_fragment (GstMssStream * stream)
1155 GstMssStreamFragment *fragment;
1156 g_return_val_if_fail (stream->active, GST_FLOW_ERROR);
1158 if (stream->current_fragment == NULL)
1159 return GST_FLOW_EOS;
1161 fragment = stream->current_fragment->data;
1162 if (stream->fragment_repetition_index == 0) {
1163 stream->current_fragment = g_list_previous (stream->current_fragment);
1164 if (stream->current_fragment == NULL)
1165 return GST_FLOW_EOS;
1166 fragment = stream->current_fragment->data;
1167 stream->fragment_repetition_index = fragment->repetitions - 1;
1169 stream->fragment_repetition_index--;
1175 gst_mss_stream_type_name (GstMssStreamType streamtype)
1177 switch (streamtype) {
1178 case MSS_STREAM_TYPE_VIDEO:
1180 case MSS_STREAM_TYPE_AUDIO:
1182 case MSS_STREAM_TYPE_UNKNOWN:
1189 * Seeks all streams to the fragment that contains the set time
1191 * @forward: if this is forward playback
1192 * @time: time in nanoseconds
1195 gst_mss_manifest_seek (GstMssManifest * manifest, gboolean forward, gint64 time)
1199 for (iter = manifest->streams; iter; iter = g_slist_next (iter)) {
1200 GstMssStream *stream = iter->data;
1202 gst_mss_manifest_live_adapter_clear (stream);
1203 gst_mss_stream_seek (stream, forward, 0, time, NULL);
1207 #define SNAP_AFTER(forward,flags) \
1208 ((forward && (flags & GST_SEEK_FLAG_SNAP_AFTER)) || \
1209 (!forward && (flags & GST_SEEK_FLAG_SNAP_BEFORE)))
1212 * Seeks this stream to the fragment that contains the sample at time
1214 * @time: time in nanoseconds
1217 gst_mss_stream_seek (GstMssStream * stream, gboolean forward,
1218 GstSeekFlags flags, gint64 time, gint64 * final_time)
1222 GstMssStreamFragment *fragment = NULL;
1224 timescale = gst_mss_stream_get_timescale (stream);
1225 time = gst_util_uint64_scale_round (time, timescale, GST_SECOND);
1227 GST_DEBUG ("Stream %s seeking to %" G_GUINT64_FORMAT, stream->url, time);
1228 for (iter = stream->fragments; iter; iter = g_list_next (iter)) {
1229 fragment = iter->data;
1230 if (fragment->time + fragment->repetitions * fragment->duration > time) {
1231 stream->current_fragment = iter;
1232 stream->fragment_repetition_index =
1233 (time - fragment->time) / fragment->duration;
1234 if (((time - fragment->time) % fragment->duration) == 0) {
1236 /* for reverse playback, start from the previous fragment when we are
1237 * exactly at a limit */
1239 stream->fragment_repetition_index--;
1240 } else if (SNAP_AFTER (forward, flags))
1241 stream->fragment_repetition_index++;
1243 if (stream->fragment_repetition_index == fragment->repetitions) {
1244 /* move to the next one */
1245 stream->fragment_repetition_index = 0;
1246 stream->current_fragment = g_list_next (iter);
1248 stream->current_fragment ? stream->current_fragment->data : NULL;
1250 } else if (stream->fragment_repetition_index == -1) {
1251 if (g_list_previous (iter)) {
1252 stream->current_fragment = g_list_previous (iter);
1253 fragment = stream->current_fragment->data;
1254 g_assert (fragment);
1255 stream->fragment_repetition_index = fragment->repetitions - 1;
1257 stream->fragment_repetition_index = 0;
1266 GST_DEBUG ("Stream %s seeked to fragment time %" G_GUINT64_FORMAT
1267 " repetition %u", stream->url,
1268 fragment ? fragment->time : GST_CLOCK_TIME_NONE,
1269 stream->fragment_repetition_index);
1272 *final_time = gst_util_uint64_scale_round (fragment->time +
1273 stream->fragment_repetition_index * fragment->duration,
1274 GST_SECOND, timescale);
1276 GstMssStreamFragment *last_fragment = g_list_last (iter)->data;
1277 *final_time = gst_util_uint64_scale_round (last_fragment->time +
1278 last_fragment->repetitions * last_fragment->duration,
1279 GST_SECOND, timescale);
1285 gst_mss_manifest_get_current_bitrate (GstMssManifest * manifest)
1287 guint64 bitrate = 0;
1290 for (iter = gst_mss_manifest_get_streams (manifest); iter;
1291 iter = g_slist_next (iter)) {
1292 GstMssStream *stream = iter->data;
1293 if (stream->active && stream->current_quality) {
1294 GstMssStreamQuality *q = stream->current_quality->data;
1296 bitrate += q->bitrate;
1304 gst_mss_manifest_is_live (GstMssManifest * manifest)
1306 return manifest->is_live;
1310 gst_mss_stream_reload_fragments (GstMssStream * stream, xmlNodePtr streamIndex)
1313 gint64 current_gst_time;
1314 GstMssFragmentListBuilder builder;
1316 current_gst_time = gst_mss_stream_get_fragment_gst_timestamp (stream);
1318 gst_mss_fragment_list_builder_init (&builder);
1320 GST_DEBUG ("Current position: %" GST_TIME_FORMAT,
1321 GST_TIME_ARGS (current_gst_time));
1323 for (iter = streamIndex->children; iter; iter = iter->next) {
1324 if (node_has_type (iter, MSS_NODE_STREAM_FRAGMENT)) {
1325 gst_mss_fragment_list_builder_add (&builder, iter);
1327 /* TODO gst log this */
1331 /* store the new fragments list */
1332 if (builder.fragments) {
1333 g_list_free_full (stream->fragments, g_free);
1334 stream->fragments = g_list_reverse (builder.fragments);
1335 stream->current_fragment = stream->fragments;
1336 /* TODO Verify how repositioning here works for reverse
1337 * playback - it might start from the wrong fragment */
1338 gst_mss_stream_seek (stream, TRUE, 0, current_gst_time, NULL);
1343 gst_mss_manifest_reload_fragments_from_xml (GstMssManifest * manifest,
1346 xmlNodePtr nodeiter;
1347 GSList *streams = manifest->streams;
1349 /* we assume the server is providing the streams in the same order in
1351 for (nodeiter = root->children; nodeiter && streams;
1352 nodeiter = nodeiter->next) {
1353 if (nodeiter->type == XML_ELEMENT_NODE
1354 && (strcmp ((const char *) nodeiter->name, "StreamIndex") == 0)) {
1355 gst_mss_stream_reload_fragments (streams->data, nodeiter);
1356 streams = g_slist_next (streams);
1362 gst_mss_manifest_reload_fragments (GstMssManifest * manifest, GstBuffer * data)
1368 gst_buffer_map (data, &info, GST_MAP_READ);
1370 xml = xmlReadMemory ((const gchar *) info.data,
1371 info.size, "manifest", NULL, 0);
1372 root = xmlDocGetRootElement (xml);
1374 gst_mss_manifest_reload_fragments_from_xml (manifest, root);
1378 gst_buffer_unmap (data, &info);
1382 gst_mss_stream_select_bitrate (GstMssStream * stream, guint64 bitrate)
1384 GList *iter = stream->current_quality;
1386 GstMssStreamQuality *q = iter->data;
1388 while (q->bitrate > bitrate) {
1389 next = g_list_previous (iter);
1398 while (q->bitrate < bitrate) {
1399 GstMssStreamQuality *next_q;
1400 next = g_list_next (iter);
1402 next_q = next->data;
1403 if (next_q->bitrate < bitrate) {
1414 if (iter == stream->current_quality)
1416 stream->current_quality = iter;
1421 gst_mss_stream_get_current_bitrate (GstMssStream * stream)
1423 GstMssStreamQuality *q;
1424 if (stream->current_quality == NULL)
1427 q = stream->current_quality->data;
1432 * gst_mss_manifest_change_bitrate:
1433 * @manifest: the manifest
1434 * @bitrate: the maximum bitrate to use (bps)
1436 * Iterates over the active streams and changes their bitrates to the maximum
1437 * value so that the bitrates of all streams are not larger than
1440 * Return: %TRUE if any stream changed its bitrate
1443 gst_mss_manifest_change_bitrate (GstMssManifest * manifest, guint64 bitrate)
1445 gboolean ret = FALSE;
1448 /* TODO This algorithm currently sets the same bitrate for all streams,
1449 * it should actually use the sum of all streams bitrates to compare to
1450 * the target value */
1454 bitrate = G_MAXUINT64;
1457 for (iter = gst_mss_manifest_get_streams (manifest); iter;
1458 iter = g_slist_next (iter)) {
1459 GstMssStream *stream = iter->data;
1460 if (stream->active) {
1461 ret = ret | gst_mss_stream_select_bitrate (stream, bitrate);
1469 gst_buffer_from_hex_string (const gchar * s)
1471 GstBuffer *buffer = NULL;
1482 buffer = gst_buffer_new_and_alloc (len / 2);
1483 gst_buffer_map (buffer, &info, GST_MAP_WRITE);
1485 for (i = 0; i < len / 2; i++) {
1486 if (!isxdigit ((int) s[i * 2]) || !isxdigit ((int) s[i * 2 + 1])) {
1487 gst_buffer_unref (buffer);
1491 ts[0] = s[i * 2 + 0];
1492 ts[1] = s[i * 2 + 1];
1495 data[i] = (guint8) strtoul (ts, NULL, 16);
1498 gst_buffer_unmap (buffer, &info);
1503 gst_mss_stream_get_lang (GstMssStream * stream)
1505 return stream->lang;
1509 gst_mss_stream_get_name (GstMssStream * stream)
1511 return stream->name;
1515 gst_mss_manifest_get_dvr_window_length_clock_time (GstMssManifest * manifest)
1519 /* the entire file is always available for non-live streams */
1520 if (manifest->dvr_window == 0)
1521 return GST_CLOCK_TIME_NONE;
1523 timescale = gst_mss_manifest_get_timescale (manifest);
1524 return (GstClockTime) gst_util_uint64_scale_round (manifest->dvr_window,
1525 GST_SECOND, timescale);
1529 gst_mss_stream_get_live_seek_range (GstMssStream * stream, gint64 * start,
1533 GstMssStreamFragment *fragment;
1534 guint64 timescale = gst_mss_stream_get_timescale (stream);
1536 g_return_val_if_fail (stream->active, FALSE);
1538 /* XXX: assumes all the data in the stream is still available */
1539 l = g_list_first (stream->fragments);
1540 fragment = (GstMssStreamFragment *) l->data;
1541 *start = gst_util_uint64_scale_round (fragment->time, GST_SECOND, timescale);
1543 l = g_list_last (stream->fragments);
1544 fragment = (GstMssStreamFragment *) l->data;
1545 *stop = gst_util_uint64_scale_round (fragment->time + fragment->duration *
1546 fragment->repetitions, GST_SECOND, timescale);
1552 gst_mss_manifest_get_live_seek_range (GstMssManifest * manifest, gint64 * start,
1556 gboolean ret = FALSE;
1558 for (iter = manifest->streams; iter; iter = g_slist_next (iter)) {
1559 GstMssStream *stream = iter->data;
1561 if (stream->active) {
1562 /* FIXME: bound this correctly for multiple streams */
1563 if (!(ret = gst_mss_stream_get_live_seek_range (stream, start, stop)))
1568 if (ret && gst_mss_manifest_is_live (manifest)) {
1569 GstClockTime dvr_window =
1570 gst_mss_manifest_get_dvr_window_length_clock_time (manifest);
1572 if (GST_CLOCK_TIME_IS_VALID (dvr_window) && *stop - *start > dvr_window) {
1573 *start = *stop - dvr_window;
1581 gst_mss_manifest_live_adapter_push (GstMssStream * stream, GstBuffer * buffer)
1583 gst_adapter_push (stream->live_adapter, buffer);
1587 gst_mss_manifest_live_adapter_available (GstMssStream * stream)
1589 return gst_adapter_available (stream->live_adapter);
1593 gst_mss_manifest_live_adapter_take_buffer (GstMssStream * stream, gsize nbytes)
1595 return gst_adapter_take_buffer (stream->live_adapter, nbytes);
1599 gst_mss_manifest_live_adapter_clear (GstMssStream * stream)
1601 if (stream->live_adapter)
1602 gst_adapter_clear (stream->live_adapter);
1606 gst_mss_stream_fragment_parsing_needed (GstMssStream * stream)
1608 return stream->fragment_parser.status == GST_MSS_FRAGMENT_HEADER_PARSER_INIT;
1612 gst_mss_stream_parse_fragment (GstMssStream * stream, GstBuffer * buffer)
1614 const gchar *stream_type_name;
1619 if (!stream->has_live_fragments)
1622 if (!gst_mss_fragment_parser_add_buffer (&stream->fragment_parser, buffer))
1625 moof = stream->fragment_parser.moof;
1626 traf = &g_array_index (moof->traf, GstTrafBox, 0);
1629 gst_mss_stream_type_name (gst_mss_stream_get_type (stream));
1631 for (index = 0; index < traf->tfrf->entries_count; index++) {
1632 GstTfrfBoxEntry *entry =
1633 &g_array_index (traf->tfrf->entries, GstTfrfBoxEntry, index);
1634 GList *l = g_list_last (stream->fragments);
1635 GstMssStreamFragment *last;
1636 GstMssStreamFragment *fragment;
1637 guint64 parsed_time = entry->time;
1638 guint64 parsed_duration = entry->duration;
1643 last = (GstMssStreamFragment *) l->data;
1645 /* only add the fragment to the list if it's outside the time in the
1647 if (last->time >= entry->time)
1650 fragment = g_new (GstMssStreamFragment, 1);
1651 fragment->number = last->number + 1;
1652 fragment->repetitions = 1;
1653 fragment->time = parsed_time;
1654 fragment->duration = parsed_duration;
1656 stream->fragments = g_list_append (stream->fragments, fragment);
1657 GST_LOG ("Adding fragment number: %u to %s stream, time: %"
1658 G_GUINT64_FORMAT ", duration: %" G_GUINT64_FORMAT ", repetitions: %u",
1659 fragment->number, stream_type_name, fragment->time,
1660 fragment->duration, fragment->repetitions);