ec845d470c742c930ac453f8901f0db4c95b00f1
[platform/upstream/gstreamer.git] / ext / smoothstreaming / gstmssmanifest.c
1 /* GStreamer
2  * Copyright (C) 2012 Smart TV Alliance
3  *  Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>, Collabora Ltd.
4  *
5  * gstmssmanifest.c:
6  *
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.
11  *
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.
16  *
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.
21  */
22
23 #include <glib.h>
24 #include <string.h>
25 #include <libxml/parser.h>
26 #include <libxml/tree.h>
27
28 /* for parsing h264 codec data */
29 #include <gst/codecparsers/gsth264parser.h>
30
31 #include "gstmssmanifest.h"
32
33 #define DEFAULT_TIMESCALE             10000000
34
35 #define MSS_NODE_STREAM_FRAGMENT      "c"
36 #define MSS_NODE_STREAM_QUALITY       "QualityLevel"
37
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"
45
46 /* TODO check if atoi is successful? */
47
48 typedef struct _GstMssStreamFragment
49 {
50   guint number;
51   guint64 time;
52   guint64 duration;
53 } GstMssStreamFragment;
54
55 struct _GstMssStream
56 {
57   xmlNodePtr xmlnode;
58
59   gint selectedQualityIndex;
60
61   GList *fragments;
62   GList *qualities;
63
64   gchar *url;
65
66   GList *current_fragment;
67   GList *current_quality;
68
69   /* TODO move this to somewhere static */
70   GRegex *regex_bitrate;
71   GRegex *regex_position;
72 };
73
74 struct _GstMssManifest
75 {
76   xmlDocPtr xml;
77   xmlNodePtr xmlrootnode;
78
79   GSList *streams;
80 };
81
82 static gboolean
83 node_has_type (xmlNodePtr node, const gchar * name)
84 {
85   return strcmp ((gchar *) node->name, name) == 0;
86 }
87
88 static void
89 _gst_mss_stream_init (GstMssStream * stream, xmlNodePtr node)
90 {
91   xmlNodePtr iter;
92   GstMssStreamFragment *previous_fragment = NULL;
93   guint fragment_number = 0;
94   guint fragment_time_accum = 0;
95   GError *gerror = NULL;
96
97   stream->xmlnode = node;
98
99   /* get the base url path generator */
100   stream->url = (gchar *) xmlGetProp (node, (xmlChar *) MSS_PROP_URL);
101
102   for (iter = node->children; iter; iter = iter->next) {
103     if (node_has_type (iter, MSS_NODE_STREAM_FRAGMENT)) {
104       gchar *duration_str;
105       gchar *time_str;
106       gchar *seqnum_str;
107       GstMssStreamFragment *fragment = g_new (GstMssStreamFragment, 1);
108
109       duration_str = (gchar *) xmlGetProp (iter, (xmlChar *) MSS_PROP_DURATION);
110       time_str = (gchar *) xmlGetProp (iter, (xmlChar *) MSS_PROP_TIME);
111       seqnum_str = (gchar *) xmlGetProp (iter, (xmlChar *) MSS_PROP_NUMBER);
112
113       /* use the node's seq number or use the previous + 1 */
114       if (seqnum_str) {
115         fragment->number = atoi (seqnum_str);
116         g_free (seqnum_str);
117       } else {
118         fragment->number = fragment_number;
119       }
120       fragment_number = fragment->number + 1;
121
122       if (time_str) {
123         fragment->time = atoi (time_str);
124         g_free (time_str);
125       } else {
126         fragment->time = fragment_time_accum;
127       }
128
129       /* if we have a previous fragment, means we need to set its duration */
130       if (previous_fragment)
131         previous_fragment->duration = fragment->time - previous_fragment->time;
132
133       if (duration_str) {
134         fragment->duration = atoi (duration_str);
135
136         previous_fragment = NULL;
137         fragment_time_accum += fragment->duration;
138         g_free (duration_str);
139       } else {
140         /* store to set the duration at the next iteration */
141         previous_fragment = fragment;
142       }
143
144       /* we reverse it later */
145       stream->fragments = g_list_prepend (stream->fragments, fragment);
146
147     } else if (node_has_type (iter, MSS_NODE_STREAM_QUALITY)) {
148       stream->qualities = g_list_prepend (stream->qualities, iter);
149     } else {
150       /* TODO gst log this */
151     }
152   }
153
154   stream->fragments = g_list_reverse (stream->fragments);
155   stream->qualities = g_list_reverse (stream->qualities);
156
157   stream->current_fragment = stream->fragments;
158   stream->current_quality = stream->qualities;
159
160   stream->regex_bitrate = g_regex_new ("\\{[Bb]itrate\\}", 0, 0, &gerror);
161   stream->regex_position = g_regex_new ("\\{start[ _]time\\}", 0, 0, &gerror);
162 }
163
164 GstMssManifest *
165 gst_mss_manifest_new (const GstBuffer * data)
166 {
167   GstMssManifest *manifest;
168   xmlNodePtr root;
169   xmlNodePtr nodeiter;
170
171   manifest = g_malloc0 (sizeof (GstMssManifest));
172
173   manifest->xml = xmlReadMemory ((const gchar *) GST_BUFFER_DATA (data),
174       GST_BUFFER_SIZE (data), "manifest", NULL, 0);
175   root = manifest->xmlrootnode = xmlDocGetRootElement (manifest->xml);
176
177   for (nodeiter = root->children; nodeiter; nodeiter = nodeiter->next) {
178     if (nodeiter->type == XML_ELEMENT_NODE
179         && (strcmp ((const char *) nodeiter->name, "StreamIndex") == 0)) {
180       GstMssStream *stream = g_new0 (GstMssStream, 1);
181
182       manifest->streams = g_slist_append (manifest->streams, stream);
183       _gst_mss_stream_init (stream, nodeiter);
184     }
185   }
186
187   return manifest;
188 }
189
190 static void
191 gst_mss_stream_free (GstMssStream * stream)
192 {
193   g_list_free_full (stream->fragments, g_free);
194   g_list_free (stream->qualities);
195   g_free (stream->url);
196   g_regex_unref (stream->regex_position);
197   g_regex_unref (stream->regex_bitrate);
198   g_free (stream);
199 }
200
201 void
202 gst_mss_manifest_free (GstMssManifest * manifest)
203 {
204   g_return_if_fail (manifest != NULL);
205
206   g_slist_free_full (manifest->streams, (GDestroyNotify) gst_mss_stream_free);
207
208   xmlFreeDoc (manifest->xml);
209   g_free (manifest);
210 }
211
212 GSList *
213 gst_mss_manifest_get_streams (GstMssManifest * manifest)
214 {
215   return manifest->streams;
216 }
217
218 GstMssStreamType
219 gst_mss_stream_get_type (GstMssStream * stream)
220 {
221   gchar *prop = (gchar *) xmlGetProp (stream->xmlnode, (xmlChar *) "Type");
222   GstMssStreamType ret = MSS_STREAM_TYPE_UNKNOWN;
223
224   if (strcmp (prop, "video") == 0) {
225     ret = MSS_STREAM_TYPE_VIDEO;
226   } else if (strcmp (prop, "audio") == 0) {
227     ret = MSS_STREAM_TYPE_AUDIO;
228   }
229   xmlFree (prop);
230   return ret;
231 }
232
233 static GstCaps *
234 _gst_mss_stream_video_caps_from_fourcc (gchar * fourcc)
235 {
236   if (!fourcc)
237     return NULL;
238
239   if (strcmp (fourcc, "H264") == 0 || strcmp (fourcc, "AVC1") == 0) {
240     return gst_caps_new_simple ("video/x-h264", "stream-format", G_TYPE_STRING,
241         "avc", NULL);
242   } else if (strcmp (fourcc, "WVC1") == 0) {
243     return gst_caps_new_simple ("video/x-wmv", "wmvversion", G_TYPE_INT, 3,
244         NULL);
245   }
246   return NULL;
247 }
248
249 static GstCaps *
250 _gst_mss_stream_audio_caps_from_fourcc (gchar * fourcc)
251 {
252   if (!fourcc)
253     return NULL;
254
255   if (strcmp (fourcc, "AACL") == 0) {
256     return gst_caps_new_simple ("audio/mpeg", "mpegversion", G_TYPE_INT, 4,
257         NULL);
258   } else if (strcmp (fourcc, "WmaPro") == 0) {
259     return gst_caps_new_simple ("audio/x-wma", "wmaversion", G_TYPE_INT, 2,
260         NULL);
261   }
262   return NULL;
263 }
264
265 /* copied and adapted from h264parse */
266 static GstBuffer *
267 _make_h264_codec_data (GstBuffer * sps, GstBuffer * pps)
268 {
269   GstBuffer *buf;
270   gint sps_size = 0, pps_size = 0, num_sps = 0, num_pps = 0;
271   guint8 profile_idc = 0, profile_comp = 0, level_idc = 0;
272   guint8 *data;
273   gint nl;
274
275   sps_size += GST_BUFFER_SIZE (sps) + 2;
276   profile_idc = GST_BUFFER_DATA (sps)[1];
277   profile_comp = GST_BUFFER_DATA (sps)[2];
278   level_idc = GST_BUFFER_DATA (sps)[3];
279   num_sps = 1;
280
281   pps_size += GST_BUFFER_SIZE (pps) + 2;
282   num_pps = 1;
283
284   buf = gst_buffer_new_and_alloc (5 + 1 + sps_size + 1 + pps_size);
285   data = GST_BUFFER_DATA (buf);
286   nl = 4;
287
288   data[0] = 1;                  /* AVC Decoder Configuration Record ver. 1 */
289   data[1] = profile_idc;        /* profile_idc                             */
290   data[2] = profile_comp;       /* profile_compability                     */
291   data[3] = level_idc;          /* level_idc                               */
292   data[4] = 0xfc | (nl - 1);    /* nal_length_size_minus1                  */
293   data[5] = 0xe0 | num_sps;     /* number of SPSs */
294
295   data += 6;
296   GST_WRITE_UINT16_BE (data, GST_BUFFER_SIZE (sps));
297   memcpy (data + 2, GST_BUFFER_DATA (sps), GST_BUFFER_SIZE (sps));
298   data += 2 + GST_BUFFER_SIZE (sps);
299
300   data[0] = num_pps;
301   data++;
302   GST_WRITE_UINT16_BE (data, GST_BUFFER_SIZE (pps));
303   memcpy (data + 2, GST_BUFFER_DATA (pps), GST_BUFFER_SIZE (pps));
304   data += 2 + GST_BUFFER_SIZE (pps);
305
306   return buf;
307 }
308
309 static void
310 _gst_mss_stream_add_h264_codec_data (GstCaps * caps, const gchar * codecdatastr)
311 {
312   GValue sps_value = { 0, };
313   GValue pps_value = { 0, };
314   GstBuffer *sps;
315   GstBuffer *pps;
316   GstBuffer *buffer;
317   gchar *sps_str;
318   gchar *pps_str;
319   GstH264NalUnit nalu;
320   GstH264SPS sps_struct;
321   GstH264ParserResult parseres;
322
323   /* search for the sps start */
324   if (g_str_has_prefix (codecdatastr, "00000001")) {
325     sps_str = (gchar *) codecdatastr + 8;
326   } else {
327     return;                     /* invalid mss codec data */
328   }
329
330   /* search for the pps start */
331   pps_str = g_strstr_len (sps_str, -1, "00000001");
332   if (!pps_str) {
333     return;                     /* invalid mss codec data */
334   }
335
336   g_value_init (&sps_value, GST_TYPE_BUFFER);
337   pps_str[0] = '\0';
338   gst_value_deserialize (&sps_value, sps_str);
339   pps_str[0] = '0';
340
341   g_value_init (&pps_value, GST_TYPE_BUFFER);
342   pps_str = pps_str + 8;
343   gst_value_deserialize (&pps_value, pps_str);
344
345   sps = gst_value_get_buffer (&sps_value);
346   pps = gst_value_get_buffer (&pps_value);
347
348   nalu.ref_idc = (GST_BUFFER_DATA (sps)[0] & 0x60) >> 5;
349   nalu.type = GST_H264_NAL_SPS;
350   nalu.size = GST_BUFFER_SIZE (sps);
351   nalu.data = GST_BUFFER_DATA (sps);
352   nalu.offset = 0;
353   nalu.sc_offset = 0;
354   nalu.valid = TRUE;
355
356   parseres = gst_h264_parse_sps (&nalu, &sps_struct, TRUE);
357   if (parseres == GST_H264_PARSER_OK) {
358     gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION,
359         sps_struct.fps_num, sps_struct.fps_den, NULL);
360   }
361
362   buffer = _make_h264_codec_data (sps, pps);
363   g_value_reset (&sps_value);
364   g_value_reset (&pps_value);
365
366   gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, buffer, NULL);
367   gst_buffer_unref (buffer);
368 }
369
370 static GstCaps *
371 _gst_mss_stream_video_caps_from_qualitylevel_xml (xmlNodePtr node)
372 {
373   GstCaps *caps;
374   GstStructure *structure;
375   gchar *fourcc = (gchar *) xmlGetProp (node, (xmlChar *) "FourCC");
376   gchar *max_width = (gchar *) xmlGetProp (node, (xmlChar *) "MaxWidth");
377   gchar *max_height = (gchar *) xmlGetProp (node, (xmlChar *) "MaxHeight");
378   gchar *codec_data =
379       (gchar *) xmlGetProp (node, (xmlChar *) "CodecPrivateData");
380
381   if (!max_width)
382     max_width = (gchar *) xmlGetProp (node, (xmlChar *) "Width");
383   if (!max_height)
384     max_height = (gchar *) xmlGetProp (node, (xmlChar *) "Height");
385
386   caps = _gst_mss_stream_video_caps_from_fourcc (fourcc);
387   if (!caps)
388     goto end;
389
390   structure = gst_caps_get_structure (caps, 0);
391
392   if (max_width)
393     gst_structure_set (structure, "width", G_TYPE_INT, atoi (max_width), NULL);
394   if (max_height)
395     gst_structure_set (structure, "height", G_TYPE_INT, atoi (max_height),
396         NULL);
397
398   if (codec_data && strlen (codec_data)) {
399     if (strcmp (fourcc, "H264") == 0 || strcmp (fourcc, "AVC1") == 0) {
400       _gst_mss_stream_add_h264_codec_data (caps, codec_data);
401     } else {
402       GValue *value = g_new0 (GValue, 1);
403       g_value_init (value, GST_TYPE_BUFFER);
404       gst_value_deserialize (value, (gchar *) codec_data);
405       gst_structure_take_value (structure, "codec_data", value);
406     }
407   }
408
409 end:
410   g_free (fourcc);
411   g_free (max_width);
412   g_free (max_height);
413   g_free (codec_data);
414
415   return caps;
416 }
417
418 static GstCaps *
419 _gst_mss_stream_audio_caps_from_qualitylevel_xml (xmlNodePtr node)
420 {
421   GstCaps *caps;
422   GstStructure *structure;
423   gchar *fourcc = (gchar *) xmlGetProp (node, (xmlChar *) "FourCC");
424   gchar *channels = (gchar *) xmlGetProp (node, (xmlChar *) "Channels");
425   gchar *rate = (gchar *) xmlGetProp (node, (xmlChar *) "SamplingRate");
426   gchar *codec_data =
427       (gchar *) xmlGetProp (node, (xmlChar *) "CodecPrivateData");
428
429   if (!fourcc)                  /* sometimes the fourcc is omitted, we fallback to the Subtype in the StreamIndex node */
430     fourcc = (gchar *) xmlGetProp (node->parent, (xmlChar *) "Subtype");
431   if (!codec_data)
432     codec_data = (gchar *) xmlGetProp (node, (xmlChar *) "WaveFormatEx");
433
434   caps = _gst_mss_stream_audio_caps_from_fourcc (fourcc);
435   if (!caps)
436     goto end;
437
438   structure = gst_caps_get_structure (caps, 0);
439
440   if (channels)
441     gst_structure_set (structure, "channels", G_TYPE_INT, atoi (channels),
442         NULL);
443   if (rate)
444     gst_structure_set (structure, "rate", G_TYPE_INT, atoi (rate), NULL);
445
446   if (codec_data && strlen (codec_data)) {
447     GValue *value = g_new0 (GValue, 1);
448     g_value_init (value, GST_TYPE_BUFFER);
449     gst_value_deserialize (value, (gchar *) codec_data);
450     gst_structure_take_value (structure, "codec_data", value);
451   }
452
453 end:
454   g_free (fourcc);
455   g_free (channels);
456   g_free (rate);
457   g_free (codec_data);
458
459   return caps;
460 }
461
462 guint64
463 gst_mss_stream_get_timescale (GstMssStream * stream)
464 {
465   gchar *timescale;
466   guint64 ts = DEFAULT_TIMESCALE;
467
468   timescale =
469       (gchar *) xmlGetProp (stream->xmlnode, (xmlChar *) MSS_PROP_TIMESCALE);
470   if (!timescale) {
471     timescale =
472         (gchar *) xmlGetProp (stream->xmlnode->parent,
473         (xmlChar *) MSS_PROP_TIMESCALE);
474   }
475
476   if (timescale) {
477     ts = strtoull (timescale, NULL, 10);
478     g_free (timescale);
479   }
480   return ts;
481 }
482
483 guint64
484 gst_mss_manifest_get_timescale (GstMssManifest * manifest)
485 {
486   gchar *timescale;
487   guint64 ts = DEFAULT_TIMESCALE;
488
489   timescale =
490       (gchar *) xmlGetProp (manifest->xmlrootnode,
491       (xmlChar *) MSS_PROP_TIMESCALE);
492   if (timescale) {
493     ts = strtoull (timescale, NULL, 10);
494     g_free (timescale);
495   }
496   return ts;
497 }
498
499 guint64
500 gst_mss_manifest_get_duration (GstMssManifest * manifest)
501 {
502   gchar *duration;
503   guint64 dur = -1;
504
505   duration =
506       (gchar *) xmlGetProp (manifest->xmlrootnode,
507       (xmlChar *) MSS_PROP_STREAM_DURATION);
508   if (duration) {
509     dur = strtoull (duration, NULL, 10);
510     g_free (duration);
511   }
512   return dur;
513 }
514
515
516 /**
517  * Gets the duration in nanoseconds
518  */
519 GstClockTime
520 gst_mss_manifest_get_gst_duration (GstMssManifest * manifest)
521 {
522   guint64 duration = -1;
523   guint64 timescale;
524   GstClockTime gstdur = GST_CLOCK_TIME_NONE;
525
526   duration = gst_mss_manifest_get_duration (manifest);
527   timescale = gst_mss_manifest_get_timescale (manifest);
528
529   if (duration != -1 && timescale != -1)
530     gstdur =
531         (GstClockTime) gst_util_uint64_scale_round (duration, GST_SECOND,
532         timescale);
533
534   return gstdur;
535 }
536
537 GstCaps *
538 gst_mss_stream_get_caps (GstMssStream * stream)
539 {
540   GstMssStreamType streamtype = gst_mss_stream_get_type (stream);
541   xmlNodePtr qualitylevel = stream->current_quality->data;
542   GstCaps *caps = NULL;
543
544   if (streamtype == MSS_STREAM_TYPE_VIDEO)
545     caps = _gst_mss_stream_video_caps_from_qualitylevel_xml (qualitylevel);
546   else if (streamtype == MSS_STREAM_TYPE_AUDIO)
547     caps = _gst_mss_stream_audio_caps_from_qualitylevel_xml (qualitylevel);
548
549   return caps;
550 }
551
552 GstFlowReturn
553 gst_mss_stream_get_fragment_url (GstMssStream * stream, gchar ** url)
554 {
555   gchar *tmp;
556   gchar *bitrate_str;
557   gchar *start_time_str;
558   GstMssStreamFragment *fragment;
559
560   if (stream->current_fragment == NULL) /* stream is over */
561     return GST_FLOW_UNEXPECTED;
562
563   fragment = stream->current_fragment->data;
564
565   bitrate_str =
566       (gchar *) xmlGetProp (stream->current_quality->data,
567       (xmlChar *) MSS_PROP_BITRATE);
568   start_time_str = g_strdup_printf ("%" G_GUINT64_FORMAT, fragment->time);
569
570   tmp = g_regex_replace_literal (stream->regex_bitrate, stream->url,
571       strlen (stream->url), 0, bitrate_str, 0, NULL);
572   *url = g_regex_replace_literal (stream->regex_position, tmp,
573       strlen (tmp), 0, start_time_str, 0, NULL);
574
575   g_free (tmp);
576   g_free (start_time_str);
577   g_free (bitrate_str);
578   return GST_FLOW_OK;
579 }
580
581 GstClockTime
582 gst_mss_stream_get_fragment_gst_timestamp (GstMssStream * stream)
583 {
584   guint64 time;
585   guint64 timescale;
586   GstMssStreamFragment *fragment;
587
588   if (!stream->current_fragment)
589     return GST_CLOCK_TIME_NONE;
590
591   fragment = stream->current_fragment->data;
592
593   time = fragment->time;
594   timescale = gst_mss_stream_get_timescale (stream);
595   return (GstClockTime) gst_util_uint64_scale_round (time, GST_SECOND,
596       timescale);
597 }
598
599 GstClockTime
600 gst_mss_stream_get_fragment_gst_duration (GstMssStream * stream)
601 {
602   guint64 dur;
603   guint64 timescale;
604   GstMssStreamFragment *fragment;
605
606   if (!stream->current_fragment)
607     return GST_CLOCK_TIME_NONE;
608
609   fragment = stream->current_fragment->data;
610
611   dur = fragment->duration;
612   timescale = gst_mss_stream_get_timescale (stream);
613   return (GstClockTime) gst_util_uint64_scale_round (dur, GST_SECOND,
614       timescale);
615 }
616
617 GstFlowReturn
618 gst_mss_stream_advance_fragment (GstMssStream * stream)
619 {
620   if (stream->current_fragment == NULL)
621     return GST_FLOW_UNEXPECTED;
622
623   stream->current_fragment = g_list_next (stream->current_fragment);
624   if (stream->current_fragment == NULL)
625     return GST_FLOW_UNEXPECTED;
626   return GST_FLOW_OK;
627 }
628
629 const gchar *
630 gst_mss_stream_type_name (GstMssStreamType streamtype)
631 {
632   switch (streamtype) {
633     case MSS_STREAM_TYPE_VIDEO:
634       return "video";
635     case MSS_STREAM_TYPE_AUDIO:
636       return "audio";
637     case MSS_STREAM_TYPE_UNKNOWN:
638     default:
639       return "unknown";
640   }
641 }
642
643 /**
644  * Seeks all streams to the fragment that contains the set time
645  *
646  * @time: time in nanoseconds
647  */
648 gboolean
649 gst_mss_manifest_seek (GstMssManifest * manifest, guint64 time)
650 {
651   gboolean ret = TRUE;
652   GSList *iter;
653
654   for (iter = manifest->streams; iter; iter = g_slist_next (iter)) {
655     ret = gst_mss_stream_seek (iter->data, time) & ret;
656   }
657
658   return ret;
659 }
660
661 /**
662  * Seeks this stream to the fragment that contains the sample at time
663  *
664  * @time: time in nanoseconds
665  */
666 gboolean
667 gst_mss_stream_seek (GstMssStream * stream, guint64 time)
668 {
669   GList *iter;
670   guint64 timescale;
671
672   timescale = gst_mss_stream_get_timescale (stream);
673   time = gst_util_uint64_scale_round (time, timescale, GST_SECOND);
674
675   for (iter = stream->fragments; iter; iter = g_list_next (iter)) {
676     GList *next = g_list_next (iter);
677     if (next) {
678       GstMssStreamFragment *fragment = next->data;
679
680       if (fragment->time > time) {
681         stream->current_fragment = iter;
682         break;
683       }
684     } else {
685       GstMssStreamFragment *fragment = iter->data;
686       if (fragment->time + fragment->duration > time) {
687         stream->current_fragment = iter;
688       } else {
689         stream->current_fragment = NULL;        /* EOS */
690       }
691       break;
692     }
693   }
694
695   return TRUE;
696 }