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.
27 #include <libxml/parser.h>
28 #include <libxml/tree.h>
30 /* for parsing h264 codec data */
31 #include <gst/codecparsers/gsth264parser.h>
33 #include "gstmssmanifest.h"
35 #define DEFAULT_TIMESCALE 10000000
37 #define MSS_NODE_STREAM_FRAGMENT "c"
38 #define MSS_NODE_STREAM_QUALITY "QualityLevel"
40 #define MSS_PROP_BITRATE "Bitrate"
41 #define MSS_PROP_DURATION "d"
42 #define MSS_PROP_NUMBER "n"
43 #define MSS_PROP_STREAM_DURATION "Duration"
44 #define MSS_PROP_TIME "t"
45 #define MSS_PROP_TIMESCALE "TimeScale"
46 #define MSS_PROP_URL "Url"
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;
93 static GstBuffer *gst_buffer_from_hex_string (const gchar * s);
96 node_has_type (xmlNodePtr node, const gchar * name)
98 return strcmp ((gchar *) node->name, name) == 0;
101 static GstMssStreamQuality *
102 gst_mss_stream_quality_new (xmlNodePtr node)
104 GstMssStreamQuality *q = g_slice_new (GstMssStreamQuality);
107 q->bitrate_str = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_BITRATE);
109 if (q->bitrate_str != NULL)
110 q->bitrate = g_ascii_strtoull (q->bitrate_str, NULL, 10);
118 gst_mss_stream_quality_free (GstMssStreamQuality * quality)
120 g_return_if_fail (quality != NULL);
122 xmlFree (quality->bitrate_str);
123 g_slice_free (GstMssStreamQuality, quality);
127 compare_bitrate (GstMssStreamQuality * a, GstMssStreamQuality * b)
129 if (a->bitrate > b->bitrate)
131 if (a->bitrate < b->bitrate)
138 _gst_mss_stream_init (GstMssStream * stream, xmlNodePtr node)
141 GstMssStreamFragment *previous_fragment = NULL;
142 guint fragment_number = 0;
143 guint64 fragment_time_accum = 0;
145 stream->xmlnode = node;
147 /* get the base url path generator */
148 stream->url = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_URL);
150 for (iter = node->children; iter; iter = iter->next) {
151 if (node_has_type (iter, MSS_NODE_STREAM_FRAGMENT)) {
155 GstMssStreamFragment *fragment = g_new (GstMssStreamFragment, 1);
157 duration_str = (gchar *) xmlGetProp (iter, (xmlChar *) MSS_PROP_DURATION);
158 time_str = (gchar *) xmlGetProp (iter, (xmlChar *) MSS_PROP_TIME);
159 seqnum_str = (gchar *) xmlGetProp (iter, (xmlChar *) MSS_PROP_NUMBER);
161 /* use the node's seq number or use the previous + 1 */
163 fragment->number = g_ascii_strtoull (seqnum_str, NULL, 10);
164 xmlFree (seqnum_str);
165 fragment_number = fragment->number;
167 fragment->number = fragment_number;
169 fragment_number = fragment->number + 1;
172 fragment->time = g_ascii_strtoull (time_str, NULL, 10);
175 fragment_time_accum = fragment->time;
177 fragment->time = fragment_time_accum;
180 /* if we have a previous fragment, means we need to set its duration */
181 if (previous_fragment)
182 previous_fragment->duration = fragment->time - previous_fragment->time;
185 fragment->duration = g_ascii_strtoull (duration_str, NULL, 10);
187 previous_fragment = NULL;
188 fragment_time_accum += fragment->duration;
189 xmlFree (duration_str);
191 /* store to set the duration at the next iteration */
192 previous_fragment = fragment;
195 /* we reverse it later */
196 stream->fragments = g_list_prepend (stream->fragments, fragment);
197 } else if (node_has_type (iter, MSS_NODE_STREAM_QUALITY)) {
198 GstMssStreamQuality *quality = gst_mss_stream_quality_new (iter);
199 stream->qualities = g_list_prepend (stream->qualities, quality);
201 /* TODO gst log this */
205 stream->fragments = g_list_reverse (stream->fragments);
207 /* order them from smaller to bigger based on bitrates */
209 g_list_sort (stream->qualities, (GCompareFunc) compare_bitrate);
211 stream->current_fragment = stream->fragments;
212 stream->current_quality = stream->qualities;
214 stream->regex_bitrate = g_regex_new ("\\{[Bb]itrate\\}", 0, 0, NULL);
215 stream->regex_position = g_regex_new ("\\{start[ _]time\\}", 0, 0, NULL);
219 gst_mss_manifest_new (GstBuffer * data)
221 GstMssManifest *manifest;
227 if (!gst_buffer_map (data, &mapinfo, GST_MAP_READ)) {
231 manifest = g_malloc0 (sizeof (GstMssManifest));
233 manifest->xml = xmlReadMemory ((const gchar *) mapinfo.data,
234 mapinfo.size, "manifest", NULL, 0);
235 root = manifest->xmlrootnode = xmlDocGetRootElement (manifest->xml);
237 live_str = (gchar *) xmlGetProp (root, (xmlChar *) "IsLive");
239 manifest->is_live = g_ascii_strcasecmp (live_str, "true") == 0;
243 for (nodeiter = root->children; nodeiter; nodeiter = nodeiter->next) {
244 if (nodeiter->type == XML_ELEMENT_NODE
245 && (strcmp ((const char *) nodeiter->name, "StreamIndex") == 0)) {
246 GstMssStream *stream = g_new0 (GstMssStream, 1);
248 manifest->streams = g_slist_append (manifest->streams, stream);
249 _gst_mss_stream_init (stream, nodeiter);
253 gst_buffer_unmap (data, &mapinfo);
259 gst_mss_stream_free (GstMssStream * stream)
261 g_list_free_full (stream->fragments, g_free);
262 g_list_free_full (stream->qualities,
263 (GDestroyNotify) gst_mss_stream_quality_free);
264 xmlFree (stream->url);
265 g_regex_unref (stream->regex_position);
266 g_regex_unref (stream->regex_bitrate);
271 gst_mss_manifest_free (GstMssManifest * manifest)
273 g_return_if_fail (manifest != NULL);
275 g_slist_free_full (manifest->streams, (GDestroyNotify) gst_mss_stream_free);
277 xmlFreeDoc (manifest->xml);
282 gst_mss_manifest_get_streams (GstMssManifest * manifest)
284 return manifest->streams;
288 gst_mss_stream_get_type (GstMssStream * stream)
290 gchar *prop = (gchar *) xmlGetProp (stream->xmlnode, (xmlChar *) "Type");
291 GstMssStreamType ret = MSS_STREAM_TYPE_UNKNOWN;
294 return MSS_STREAM_TYPE_UNKNOWN;
296 if (strcmp (prop, "video") == 0) {
297 ret = MSS_STREAM_TYPE_VIDEO;
298 } else if (strcmp (prop, "audio") == 0) {
299 ret = MSS_STREAM_TYPE_AUDIO;
306 _gst_mss_stream_video_caps_from_fourcc (gchar * fourcc)
311 if (strcmp (fourcc, "H264") == 0 || strcmp (fourcc, "AVC1") == 0) {
312 return gst_caps_new_simple ("video/x-h264", "stream-format", G_TYPE_STRING,
314 } else if (strcmp (fourcc, "WVC1") == 0) {
315 return gst_caps_new_simple ("video/x-wmv", "wmvversion", G_TYPE_INT, 3,
316 "format", G_TYPE_STRING, "WVC1", NULL);
322 _gst_mss_stream_audio_caps_from_fourcc (gchar * fourcc)
327 if (strcmp (fourcc, "AACL") == 0) {
328 return gst_caps_new_simple ("audio/mpeg", "mpegversion", G_TYPE_INT, 4,
330 } else if (strcmp (fourcc, "WmaPro") == 0 || strcmp (fourcc, "WMAP") == 0) {
331 return gst_caps_new_simple ("audio/x-wma", "wmaversion", G_TYPE_INT, 3,
337 /* copied and adapted from h264parse */
339 _make_h264_codec_data (GstBuffer * sps, GstBuffer * pps)
342 gint sps_size = 0, pps_size = 0, num_sps = 0, num_pps = 0;
343 guint8 profile_idc = 0, profile_comp = 0, level_idc = 0;
346 GstMapInfo spsinfo, ppsinfo, codecdatainfo;
348 if (gst_buffer_get_size (sps) < 4)
351 gst_buffer_map (sps, &spsinfo, GST_MAP_READ);
352 gst_buffer_map (pps, &ppsinfo, GST_MAP_READ);
354 sps_size += spsinfo.size + 2;
355 profile_idc = spsinfo.data[1];
356 profile_comp = spsinfo.data[2];
357 level_idc = spsinfo.data[3];
360 pps_size += ppsinfo.size + 2;
363 buf = gst_buffer_new_and_alloc (5 + 1 + sps_size + 1 + pps_size);
364 gst_buffer_map (buf, &codecdatainfo, GST_MAP_WRITE);
365 data = codecdatainfo.data;
368 data[0] = 1; /* AVC Decoder Configuration Record ver. 1 */
369 data[1] = profile_idc; /* profile_idc */
370 data[2] = profile_comp; /* profile_compability */
371 data[3] = level_idc; /* level_idc */
372 data[4] = 0xfc | (nl - 1); /* nal_length_size_minus1 */
373 data[5] = 0xe0 | num_sps; /* number of SPSs */
376 GST_WRITE_UINT16_BE (data, spsinfo.size);
377 memcpy (data + 2, spsinfo.data, spsinfo.size);
378 data += 2 + spsinfo.size;
382 GST_WRITE_UINT16_BE (data, ppsinfo.size);
383 memcpy (data + 2, ppsinfo.data, ppsinfo.size);
384 data += 2 + ppsinfo.size;
386 gst_buffer_unmap (sps, &spsinfo);
387 gst_buffer_unmap (pps, &ppsinfo);
388 gst_buffer_unmap (buf, &codecdatainfo);
394 _gst_mss_stream_add_h264_codec_data (GstCaps * caps, const gchar * codecdatastr)
402 GstH264SPS sps_struct;
403 GstH264ParserResult parseres;
406 /* search for the sps start */
407 if (g_str_has_prefix (codecdatastr, "00000001")) {
408 sps_str = (gchar *) codecdatastr + 8;
410 return; /* invalid mss codec data */
413 /* search for the pps start */
414 pps_str = g_strstr_len (sps_str, -1, "00000001");
416 return; /* invalid mss codec data */
420 sps = gst_buffer_from_hex_string (sps_str);
423 pps_str = pps_str + 8;
424 pps = gst_buffer_from_hex_string (pps_str);
426 gst_buffer_map (sps, &spsinfo, GST_MAP_READ);
428 nalu.ref_idc = (spsinfo.data[0] & 0x60) >> 5;
429 nalu.type = GST_H264_NAL_SPS;
430 nalu.size = spsinfo.size;
431 nalu.data = spsinfo.data;
436 parseres = gst_h264_parse_sps (&nalu, &sps_struct, TRUE);
437 if (parseres == GST_H264_PARSER_OK) {
438 gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION,
439 sps_struct.fps_num, sps_struct.fps_den, NULL);
442 buffer = _make_h264_codec_data (sps, pps);
443 gst_buffer_unmap (sps, &spsinfo);
444 gst_buffer_unref (sps);
445 gst_buffer_unref (pps);
447 if (buffer != NULL) {
448 gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, buffer, NULL);
449 gst_buffer_unref (buffer);
454 _gst_mss_stream_video_caps_from_qualitylevel_xml (GstMssStreamQuality * q)
456 xmlNodePtr node = q->xmlnode;
458 GstStructure *structure;
459 gchar *fourcc = (gchar *) xmlGetProp (node, (xmlChar *) "FourCC");
460 gchar *max_width = (gchar *) xmlGetProp (node, (xmlChar *) "MaxWidth");
461 gchar *max_height = (gchar *) xmlGetProp (node, (xmlChar *) "MaxHeight");
463 (gchar *) xmlGetProp (node, (xmlChar *) "CodecPrivateData");
466 max_width = (gchar *) xmlGetProp (node, (xmlChar *) "Width");
468 max_height = (gchar *) xmlGetProp (node, (xmlChar *) "Height");
470 caps = _gst_mss_stream_video_caps_from_fourcc (fourcc);
474 structure = gst_caps_get_structure (caps, 0);
477 gst_structure_set (structure, "width", G_TYPE_INT,
478 (int) g_ascii_strtoull (max_width, NULL, 10), NULL);
481 gst_structure_set (structure, "height", G_TYPE_INT,
482 (int) g_ascii_strtoull (max_height, NULL, 10), NULL);
485 if (codec_data && strlen (codec_data)) {
486 if (strcmp (fourcc, "H264") == 0 || strcmp (fourcc, "AVC1") == 0) {
487 _gst_mss_stream_add_h264_codec_data (caps, codec_data);
489 GstBuffer *buffer = gst_buffer_from_hex_string ((gchar *) codec_data);
490 gst_structure_set (structure, "codec_data", GST_TYPE_BUFFER, buffer,
492 gst_buffer_unref (buffer);
499 xmlFree (max_height);
500 xmlFree (codec_data);
506 _frequency_index_from_sampling_rate (guint sampling_rate)
508 static const guint aac_sample_rates[] = { 96000, 88200, 64000, 48000, 44100,
509 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350
514 for (i = 0; i < G_N_ELEMENTS (aac_sample_rates); i++) {
515 if (aac_sample_rates[i] == sampling_rate)
522 _make_aacl_codec_data (guint64 sampling_rate, guint64 channels)
526 guint8 frequency_index;
531 frequency_index = _frequency_index_from_sampling_rate (sampling_rate);
532 if (frequency_index == 15)
535 buf = gst_buffer_new_and_alloc (buf_size);
536 gst_buffer_map (buf, &info, GST_MAP_WRITE);
539 data[0] = 2 << 3; /* AAC-LC object type is 2 */
540 data[0] += frequency_index >> 1;
541 data[1] = (frequency_index & 0x01) << 7;
543 /* Sampling rate is not in frequencies table, write manually */
544 if (frequency_index == 15) {
545 data[1] += sampling_rate >> 17;
546 data[2] = (sampling_rate >> 9) & 0xFF;
547 data[3] = (sampling_rate >> 1) & 0xFF;
548 data[4] = sampling_rate & 0x01;
552 data[1] += (channels & 0x0F) << 3;
554 gst_buffer_unmap (buf, &info);
560 _gst_mss_stream_audio_caps_from_qualitylevel_xml (GstMssStreamQuality * q)
562 xmlNodePtr node = q->xmlnode;
564 GstStructure *structure;
565 gchar *fourcc = (gchar *) xmlGetProp (node, (xmlChar *) "FourCC");
566 gchar *channels_str = (gchar *) xmlGetProp (node, (xmlChar *) "Channels");
567 gchar *rate_str = (gchar *) xmlGetProp (node, (xmlChar *) "SamplingRate");
568 gchar *block_align_str =
569 (gchar *) xmlGetProp (node, (xmlChar *) "PacketSize");
570 gchar *codec_data_str =
571 (gchar *) xmlGetProp (node, (xmlChar *) "CodecPrivateData");
572 GstBuffer *codec_data = NULL;
573 gint block_align = 0;
577 if (!fourcc) /* sometimes the fourcc is omitted, we fallback to the Subtype in the StreamIndex node */
578 fourcc = (gchar *) xmlGetProp (node->parent, (xmlChar *) "Subtype");
580 caps = _gst_mss_stream_audio_caps_from_fourcc (fourcc);
584 structure = gst_caps_get_structure (caps, 0);
585 if (codec_data_str && strlen (codec_data_str)) {
586 codec_data = gst_buffer_from_hex_string ((gchar *) codec_data_str);
590 rate = (gint) g_ascii_strtoull (rate_str, NULL, 10);
592 channels = (int) g_ascii_strtoull (channels_str, NULL, 10);
594 block_align = (int) g_ascii_strtoull (block_align_str, NULL, 10);
598 codec_data_str = (gchar *) xmlGetProp (node, (xmlChar *) "WaveFormatEx");
599 if (codec_data_str && strlen (codec_data_str)) {
600 codec_data = gst_buffer_from_hex_string ((gchar *) codec_data_str);
602 /* since this is a waveformatex, try to get the block_align and rate */
603 gst_buffer_map (codec_data, &mapinfo, GST_MAP_READ);
604 if (mapinfo.size >= 14) {
606 channels = GST_READ_UINT16_LE (mapinfo.data + 2);
609 rate = GST_READ_UINT32_LE (mapinfo.data + 4);
611 block_align = GST_READ_UINT16_LE (mapinfo.data + 12);
613 gst_buffer_unmap (codec_data, &mapinfo);
617 if (!codec_data && strcmp (fourcc, "AACL") == 0 && rate && channels) {
618 codec_data = _make_aacl_codec_data (rate, channels);
622 gst_structure_set (structure, "block_align", G_TYPE_INT, block_align, NULL);
625 gst_structure_set (structure, "channels", G_TYPE_INT, channels, NULL);
628 gst_structure_set (structure, "rate", G_TYPE_INT, rate, NULL);
631 gst_structure_set (structure, "bitrate", G_TYPE_INT, q->bitrate, NULL);
634 gst_structure_set (structure, "codec_data", GST_TYPE_BUFFER, codec_data,
639 gst_buffer_unref (codec_data);
641 xmlFree (channels_str);
643 xmlFree (block_align_str);
644 xmlFree (codec_data_str);
650 gst_mss_stream_set_active (GstMssStream * stream, gboolean active)
652 stream->active = active;
656 gst_mss_stream_get_timescale (GstMssStream * stream)
659 guint64 ts = DEFAULT_TIMESCALE;
662 (gchar *) xmlGetProp (stream->xmlnode, (xmlChar *) MSS_PROP_TIMESCALE);
665 (gchar *) xmlGetProp (stream->xmlnode->parent,
666 (xmlChar *) MSS_PROP_TIMESCALE);
670 ts = g_ascii_strtoull (timescale, NULL, 10);
677 gst_mss_manifest_get_timescale (GstMssManifest * manifest)
680 guint64 ts = DEFAULT_TIMESCALE;
683 (gchar *) xmlGetProp (manifest->xmlrootnode,
684 (xmlChar *) MSS_PROP_TIMESCALE);
686 ts = g_ascii_strtoull (timescale, NULL, 10);
693 gst_mss_manifest_get_duration (GstMssManifest * manifest)
699 (gchar *) xmlGetProp (manifest->xmlrootnode,
700 (xmlChar *) MSS_PROP_STREAM_DURATION);
702 dur = g_ascii_strtoull (duration, NULL, 10);
710 * Gets the duration in nanoseconds
713 gst_mss_manifest_get_gst_duration (GstMssManifest * manifest)
715 guint64 duration = -1;
717 GstClockTime gstdur = GST_CLOCK_TIME_NONE;
719 duration = gst_mss_manifest_get_duration (manifest);
720 timescale = gst_mss_manifest_get_timescale (manifest);
722 if (duration != -1 && timescale != -1)
724 (GstClockTime) gst_util_uint64_scale_round (duration, GST_SECOND,
731 gst_mss_stream_get_caps (GstMssStream * stream)
733 GstMssStreamType streamtype = gst_mss_stream_get_type (stream);
734 GstMssStreamQuality *qualitylevel = stream->current_quality->data;
735 GstCaps *caps = NULL;
737 if (streamtype == MSS_STREAM_TYPE_VIDEO)
738 caps = _gst_mss_stream_video_caps_from_qualitylevel_xml (qualitylevel);
739 else if (streamtype == MSS_STREAM_TYPE_AUDIO)
740 caps = _gst_mss_stream_audio_caps_from_qualitylevel_xml (qualitylevel);
746 gst_mss_stream_get_fragment_url (GstMssStream * stream, gchar ** url)
749 gchar *start_time_str;
750 GstMssStreamFragment *fragment;
751 GstMssStreamQuality *quality = stream->current_quality->data;
753 g_return_val_if_fail (stream->active, GST_FLOW_ERROR);
755 if (stream->current_fragment == NULL) /* stream is over */
758 fragment = stream->current_fragment->data;
760 start_time_str = g_strdup_printf ("%" G_GUINT64_FORMAT, fragment->time);
762 tmp = g_regex_replace_literal (stream->regex_bitrate, stream->url,
763 strlen (stream->url), 0, quality->bitrate_str, 0, NULL);
764 *url = g_regex_replace_literal (stream->regex_position, tmp,
765 strlen (tmp), 0, start_time_str, 0, NULL);
768 g_free (start_time_str);
771 return GST_FLOW_ERROR;
777 gst_mss_stream_get_fragment_gst_timestamp (GstMssStream * stream)
781 GstMssStreamFragment *fragment;
783 g_return_val_if_fail (stream->active, GST_FLOW_ERROR);
785 if (!stream->current_fragment)
786 return GST_CLOCK_TIME_NONE;
788 fragment = stream->current_fragment->data;
790 time = fragment->time;
791 timescale = gst_mss_stream_get_timescale (stream);
792 return (GstClockTime) gst_util_uint64_scale_round (time, GST_SECOND,
797 gst_mss_stream_get_fragment_gst_duration (GstMssStream * stream)
801 GstMssStreamFragment *fragment;
803 g_return_val_if_fail (stream->active, GST_FLOW_ERROR);
805 if (!stream->current_fragment)
806 return GST_CLOCK_TIME_NONE;
808 fragment = stream->current_fragment->data;
810 dur = fragment->duration;
811 timescale = gst_mss_stream_get_timescale (stream);
812 return (GstClockTime) gst_util_uint64_scale_round (dur, GST_SECOND,
817 gst_mss_stream_advance_fragment (GstMssStream * stream)
819 g_return_val_if_fail (stream->active, GST_FLOW_ERROR);
821 if (stream->current_fragment == NULL)
824 stream->current_fragment = g_list_next (stream->current_fragment);
825 if (stream->current_fragment == NULL)
831 gst_mss_stream_type_name (GstMssStreamType streamtype)
833 switch (streamtype) {
834 case MSS_STREAM_TYPE_VIDEO:
836 case MSS_STREAM_TYPE_AUDIO:
838 case MSS_STREAM_TYPE_UNKNOWN:
845 * Seeks all streams to the fragment that contains the set time
847 * @time: time in nanoseconds
850 gst_mss_manifest_seek (GstMssManifest * manifest, guint64 time)
855 for (iter = manifest->streams; iter; iter = g_slist_next (iter)) {
856 ret = gst_mss_stream_seek (iter->data, time) & ret;
863 * Seeks this stream to the fragment that contains the sample at time
865 * @time: time in nanoseconds
868 gst_mss_stream_seek (GstMssStream * stream, guint64 time)
873 timescale = gst_mss_stream_get_timescale (stream);
874 time = gst_util_uint64_scale_round (time, timescale, GST_SECOND);
876 for (iter = stream->fragments; iter; iter = g_list_next (iter)) {
877 GList *next = g_list_next (iter);
879 GstMssStreamFragment *fragment = next->data;
881 if (fragment->time > time) {
882 stream->current_fragment = iter;
886 GstMssStreamFragment *fragment = iter->data;
887 if (fragment->time + fragment->duration > time) {
888 stream->current_fragment = iter;
890 stream->current_fragment = NULL; /* EOS */
900 gst_mss_manifest_get_current_bitrate (GstMssManifest * manifest)
905 for (iter = gst_mss_manifest_get_streams (manifest); iter;
906 iter = g_slist_next (iter)) {
907 GstMssStream *stream = iter->data;
908 if (stream->active && stream->current_quality) {
909 GstMssStreamQuality *q = stream->current_quality->data;
911 bitrate += q->bitrate;
919 gst_mss_manifest_is_live (GstMssManifest * manifest)
921 return manifest->is_live;
925 gst_mss_stream_reload_fragments (GstMssStream * stream, xmlNodePtr streamIndex)
928 GList *new_fragments = NULL;
929 GstMssStreamFragment *previous_fragment = NULL;
930 GstMssStreamFragment *current_fragment =
931 stream->current_fragment ? stream->current_fragment->data : NULL;
932 guint64 current_time = gst_mss_stream_get_fragment_gst_timestamp (stream);
933 guint fragment_number = 0;
934 guint64 fragment_time_accum = 0;
936 if (!current_fragment && stream->fragments) {
937 current_fragment = g_list_last (stream->fragments)->data;
938 } else if (g_list_previous (stream->current_fragment)) {
939 /* rewind one as this is the next to be pushed */
940 current_fragment = g_list_previous (stream->current_fragment)->data;
942 current_fragment = NULL;
945 if (current_fragment) {
946 current_time = current_fragment->time;
947 fragment_number = current_fragment->number;
948 fragment_time_accum = current_fragment->time;
951 for (iter = streamIndex->children; iter; iter = iter->next) {
952 if (node_has_type (iter, MSS_NODE_STREAM_FRAGMENT)) {
956 GstMssStreamFragment *fragment = g_new (GstMssStreamFragment, 1);
958 duration_str = (gchar *) xmlGetProp (iter, (xmlChar *) MSS_PROP_DURATION);
959 time_str = (gchar *) xmlGetProp (iter, (xmlChar *) MSS_PROP_TIME);
960 seqnum_str = (gchar *) xmlGetProp (iter, (xmlChar *) MSS_PROP_NUMBER);
962 /* use the node's seq number or use the previous + 1 */
964 fragment->number = g_ascii_strtoull (seqnum_str, NULL, 10);
965 xmlFree (seqnum_str);
967 fragment->number = fragment_number;
969 fragment_number = fragment->number + 1;
972 fragment->time = g_ascii_strtoull (time_str, NULL, 10);
974 fragment_time_accum = fragment->time;
976 fragment->time = fragment_time_accum;
979 /* if we have a previous fragment, means we need to set its duration */
980 if (previous_fragment)
981 previous_fragment->duration = fragment->time - previous_fragment->time;
984 fragment->duration = g_ascii_strtoull (duration_str, NULL, 10);
986 previous_fragment = NULL;
987 fragment_time_accum += fragment->duration;
988 xmlFree (duration_str);
990 /* store to set the duration at the next iteration */
991 previous_fragment = fragment;
994 if (fragment->time > current_time) {
995 new_fragments = g_list_append (new_fragments, fragment);
997 previous_fragment = NULL;
1002 /* TODO gst log this */
1006 /* store the new fragments list */
1007 if (new_fragments) {
1008 g_list_free_full (stream->fragments, g_free);
1009 stream->fragments = new_fragments;
1010 stream->current_fragment = new_fragments;
1015 gst_mss_manifest_reload_fragments_from_xml (GstMssManifest * manifest,
1018 xmlNodePtr nodeiter;
1019 GSList *streams = manifest->streams;
1021 /* we assume the server is providing the streams in the same order in
1023 for (nodeiter = root->children; nodeiter && streams;
1024 nodeiter = nodeiter->next) {
1025 if (nodeiter->type == XML_ELEMENT_NODE
1026 && (strcmp ((const char *) nodeiter->name, "StreamIndex") == 0)) {
1027 gst_mss_stream_reload_fragments (streams->data, nodeiter);
1028 streams = g_slist_next (streams);
1034 gst_mss_manifest_reload_fragments (GstMssManifest * manifest, GstBuffer * data)
1040 g_return_if_fail (manifest->is_live);
1042 gst_buffer_map (data, &info, GST_MAP_READ);
1044 xml = xmlReadMemory ((const gchar *) info.data,
1045 info.size, "manifest", NULL, 0);
1046 root = xmlDocGetRootElement (xml);
1048 gst_mss_manifest_reload_fragments_from_xml (manifest, root);
1052 gst_buffer_unmap (data, &info);
1056 gst_mss_stream_select_bitrate (GstMssStream * stream, guint64 bitrate)
1058 GList *iter = stream->current_quality;
1060 GstMssStreamQuality *q = iter->data;
1062 while (q->bitrate > bitrate) {
1063 next = g_list_previous (iter);
1072 while (q->bitrate < bitrate) {
1073 GstMssStreamQuality *next_q;
1074 next = g_list_next (iter);
1076 next_q = next->data;
1077 if (next_q->bitrate < bitrate) {
1088 if (iter == stream->current_quality)
1090 stream->current_quality = iter;
1095 gst_mss_stream_get_current_bitrate (GstMssStream * stream)
1097 GstMssStreamQuality *q;
1098 if (stream->current_quality == NULL)
1101 q = stream->current_quality->data;
1106 * gst_mss_manifest_change_bitrate:
1107 * @manifest: the manifest
1108 * @bitrate: the maximum bitrate to use (bps)
1110 * Iterates over the active streams and changes their bitrates to the maximum
1111 * value so that the bitrates of all streams are not larger than
1114 * Return: %TRUE if any stream changed its bitrate
1117 gst_mss_manifest_change_bitrate (GstMssManifest * manifest, guint64 bitrate)
1119 gboolean ret = FALSE;
1122 /* TODO This algorithm currently sets the same bitrate for all streams,
1123 * it should actually use the sum of all streams bitrates to compare to
1124 * the target value */
1128 bitrate = G_MAXUINT64;
1131 for (iter = gst_mss_manifest_get_streams (manifest); iter;
1132 iter = g_slist_next (iter)) {
1133 GstMssStream *stream = iter->data;
1134 if (stream->active) {
1135 ret = ret | gst_mss_stream_select_bitrate (stream, bitrate);
1143 gst_buffer_from_hex_string (const gchar * s)
1145 GstBuffer *buffer = NULL;
1156 buffer = gst_buffer_new_and_alloc (len / 2);
1157 gst_buffer_map (buffer, &info, GST_MAP_WRITE);
1159 for (i = 0; i < len / 2; i++) {
1160 if (!isxdigit ((int) s[i * 2]) || !isxdigit ((int) s[i * 2 + 1])) {
1161 gst_buffer_unref (buffer);
1165 ts[0] = s[i * 2 + 0];
1166 ts[1] = s[i * 2 + 1];
1169 data[i] = (guint8) strtoul (ts, NULL, 16);
1172 gst_buffer_unmap (buffer, &info);