tizen 2.3.1 release
[framework/multimedia/gst-plugins-ext0.10.git] / hlsdemux2 / src / m3u8.c
1 /* GStreamer
2  * Copyright (C) 2010 Marc-Andre Lureau <marcandre.lureau@gmail.com>
3  * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd. All rights reserved.
4  *
5  * m3u8.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 <stdlib.h>
24 #include <math.h>
25 #include <errno.h>
26 #include <glib.h>
27 #include <string.h>
28 #include "m3u8.h"
29
30 static GstM3U8 *gst_m3u8_new (void);
31 static void gst_m3u8_free (GstM3U8 * m3u8);
32 static gboolean gst_m3u8_update (GstM3U8 * m3u8, gchar * data,
33     gboolean * updated);
34 static GstM3U8MediaFile *
35 gst_m3u8_media_file_new (gchar * uri, gchar * title, GstClockTime duration,
36     gchar *key_url, gchar *IV, guint sequence);
37 static void gst_m3u8_media_file_free (GstM3U8MediaFile * self);
38 static void gst_m3u8_key_free (GstM3U8Key * self);
39
40 #define GST_CAT_DEFAULT hlsdemux2_m3u8_debug
41
42 #define BITRATE_SWITCH_UPPER_THRESHOLD 0.3
43 #define BITRATE_SWITCH_LOWER_THRESHOLD 0
44 #define SWITCH_TRIGGER_POINT 1
45
46 static GstM3U8 *
47 gst_m3u8_new (void)
48 {
49   GstM3U8 *m3u8;
50
51   m3u8 = g_new0 (GstM3U8, 1);
52   m3u8->last_data = NULL;
53   return m3u8;
54 }
55
56 static void
57 gst_m3u8_set_uri (GstM3U8 * self, gchar * uri)
58 {
59   g_return_if_fail (self != NULL);
60
61   if (self->uri)
62     g_free (self->uri);
63   self->uri = uri;
64 }
65
66 static void
67 gst_m3u8_free (GstM3U8 * self)
68 {
69   g_return_if_fail (self != NULL);
70
71   g_free (self->uri);
72   g_free (self->allowcache);
73   g_free (self->codecs);
74   //g_free (self->cipher_ctx);
75
76   g_list_foreach (self->files, (GFunc) gst_m3u8_media_file_free, NULL);
77   g_list_free (self->files);
78
79   g_free (self->last_data);
80   g_list_foreach (self->lists, (GFunc) gst_m3u8_free, NULL);
81   g_list_free (self->lists);
82
83   g_free (self);
84 }
85
86 static GstM3U8MediaFile *
87 gst_m3u8_media_file_new (gchar * uri, gchar * title, GstClockTime duration,
88     gchar *key_url, gchar *IV, guint sequence)
89 {
90   GstM3U8MediaFile *file;
91
92   file = g_new0 (GstM3U8MediaFile, 1);
93   file->uri = uri;
94   file->title = title;
95   file->duration = duration;
96   file->sequence = sequence;
97
98   GST_DEBUG ("Duration of the file = %"GST_TIME_FORMAT"\n", GST_TIME_ARGS(duration));
99
100   if (key_url != NULL)
101     file->key_url = g_strdup (key_url);
102   else
103     file->key_url = NULL;
104
105   file->iv = IV;
106
107   return file;
108 }
109
110 static void
111 gst_m3u8_media_file_free (GstM3U8MediaFile * self)
112 {
113   g_return_if_fail (self != NULL);
114
115   if (self->key_url) {
116     g_free (self->key_url);
117     self->key_url = NULL;
118   }
119   if (self->iv) {
120     g_free (self->iv);
121     self->iv = NULL;
122   }
123   if (self->title) {
124     g_free (self->title);
125     self->title = NULL;
126   }
127   if (self->uri){
128     g_free (self->uri);
129     self->uri = NULL;
130   }
131   g_free (self);
132 }
133
134 static gchar *
135 gst_m3u8_getIV_from_mediasequence (GstM3U8 *self)
136 {
137   gchar *IV = NULL;
138
139   IV = g_malloc0 (16);
140   if (!IV) {
141     GST_ERROR ("Failed to allocate memory...");
142     return NULL;
143   }
144
145   if (self->mediasequence > INT_MAX) {
146     GST_ERROR ("media sequnece is greater than INT_MAX...yet to handle");
147   }
148
149   IV [15] = (gchar)(self->mediasequence);
150   IV [14] = (gchar)(self->mediasequence >> 8);
151   IV [13] = (gchar)(self->mediasequence >> 16);
152   IV [12] = (gchar)(self->mediasequence >> 24);
153   return IV;
154 }
155
156 static gboolean
157 int_from_string (gchar * ptr, gchar ** endptr, gint * val, gint base)
158 {
159   gchar *end;
160
161   g_return_val_if_fail (ptr != NULL, FALSE);
162   g_return_val_if_fail (val != NULL, FALSE);
163
164   errno = 0;
165   *val = strtol (ptr, &end, base);
166   if ((errno == ERANGE && (*val == LONG_MAX || *val == LONG_MIN))
167       || (errno != 0 && *val == 0)) {
168     GST_WARNING ("%s", g_strerror (errno));
169     return FALSE;
170   }
171
172   if (endptr)
173     *endptr = end;
174
175   return end != ptr;
176 }
177
178 static gboolean
179 double_from_string (gchar * ptr, gchar ** endptr, gdouble * val)
180 {
181   gchar *end;
182   gdouble ret;
183
184   g_return_val_if_fail (ptr != NULL, FALSE);
185   g_return_val_if_fail (val != NULL, FALSE);
186
187   errno = 0;
188   ret = g_strtod (ptr, &end);
189   if ((errno == ERANGE && (ret == HUGE_VAL || ret == -HUGE_VAL))
190       || (errno != 0 && ret == 0)) {
191     GST_WARNING ("%s", g_strerror (errno));
192     return FALSE;
193   }
194
195   if (!isfinite (ret)) {
196     GST_WARNING ("%s", g_strerror (ERANGE));
197     return FALSE;
198   }
199
200   if (endptr)
201     *endptr = end;
202
203   *val = (gdouble) ret;
204
205   return end != ptr;
206 }
207
208 static gboolean
209 iv_from_string (gchar * ptr, gchar ** endptr, guint8 * val)
210 {
211   guint8 idx;
212   guint8 hex;
213
214   g_return_val_if_fail (ptr != NULL, FALSE);
215   g_return_val_if_fail (val != NULL, FALSE);
216
217   if (*ptr == '0' && (*(ptr + 1) == 'x' || *(ptr + 1) == 'X')) {
218     ptr = ptr + 2;  /* skip 0x or 0X */
219   }
220
221   for (idx = 0; idx < GST_M3U8_IV_LEN; idx++) {
222     hex = *ptr++ - '0';
223     if (hex >= 16)
224       return FALSE;
225     *(val + idx) = hex * 16;
226     hex = *ptr++ - '0';
227     if (hex >= 16)
228       return FALSE;
229     *(val + idx) += hex;
230   }
231
232   if (endptr)
233     *endptr = ptr;
234
235   return TRUE;
236 }
237
238 static gboolean
239 iv_from_uint (guint uint, guint8 * val)
240 {
241   guint8 idx;
242
243   g_return_val_if_fail (val != NULL, FALSE);
244
245   val = val + GST_M3U8_IV_LEN - 1;
246   for (idx = 0; idx < sizeof(guint); idx++) {
247     *val-- = (guint8)uint;
248     uint = uint >> 8;
249   }
250   return TRUE;
251 }
252
253 static gboolean
254 make_valid_uri (gchar *prefix, gchar *suffix, gchar **uri)
255 {
256   gchar *slash;
257
258   if (!prefix) {
259     GST_WARNING ("uri prefix not set, can't build a valid uri");
260     return FALSE;
261   }
262   slash = g_utf8_strrchr (prefix, -1, '/');
263   if (!slash) {
264     GST_WARNING ("Can't build a valid uri");
265     return FALSE;
266   }
267
268   *slash = '\0';
269   *uri = g_strdup_printf ("%s/%s", prefix, suffix);
270   *slash = '/';
271
272   return TRUE;
273 }
274
275 static gboolean
276 parse_attributes (gchar ** ptr, gchar ** a, gchar ** v)
277 {
278   gchar *end, *p;
279
280   g_return_val_if_fail (ptr != NULL, FALSE);
281   g_return_val_if_fail (*ptr != NULL, FALSE);
282   g_return_val_if_fail (a != NULL, FALSE);
283   g_return_val_if_fail (v != NULL, FALSE);
284
285   /* [attribute=value,]* */
286
287   *a = *ptr;
288   end = p = g_utf8_strchr (*ptr, -1, ',');
289   if (end) {
290     do {
291       end = g_utf8_next_char (end);
292     } while (end && *end == ' ');
293     *p = '\0';
294   }
295
296   *v = p = g_utf8_strchr (*ptr, -1, '=');
297   if (*v) {
298     *v = g_utf8_next_char (*v);
299     *p = '\0';
300   } else {
301     GST_WARNING ("missing = after attribute");
302     return FALSE;
303   }
304
305   *ptr = end;
306   return TRUE;
307 }
308
309 static gint
310 _m3u8_compare_uri (GstM3U8 * a, gchar * uri)
311 {
312   g_return_val_if_fail (a != NULL, 0);
313   g_return_val_if_fail (uri != NULL, 0);
314
315   return g_strcmp0 (a->uri, uri);
316 }
317
318 static gint
319 gst_m3u8_compare_playlist_by_bitrate (gconstpointer a, gconstpointer b)
320 {
321   return ((GstM3U8 *) (a))->bandwidth - ((GstM3U8 *) (b))->bandwidth;
322 }
323
324 /*
325  * @data: a m3u8 playlist text data, taking ownership
326  */
327 static gboolean
328 gst_m3u8_update (GstM3U8 * self, gchar *data, gboolean * updated)
329 {
330   gint val;
331   GstClockTime duration = 0;
332   gchar *title, *end;
333 //  gboolean discontinuity;
334   GstM3U8 *list;
335   gchar  *key_url = NULL;
336   gchar *IV = NULL;
337   gchar *actual_data = NULL;
338
339   g_return_val_if_fail (self != NULL, FALSE);
340   g_return_val_if_fail (data != NULL, FALSE);
341   g_return_val_if_fail (updated != NULL, FALSE);
342
343   *updated = TRUE;
344
345   /* check if the data changed since last update */
346   if (self->last_data && g_str_equal (self->last_data, data)) {
347     GST_DEBUG ("Playlist is the same as previous one");
348     *updated = FALSE;
349     g_free (data);
350     return TRUE;
351   }
352
353   if (!g_str_has_prefix (data, "#EXTM3U")) {
354     GST_WARNING ("Data doesn't start with #EXTM3U");
355     *updated = FALSE;
356     g_free (data);
357     return FALSE;
358   }
359
360   if (self->last_data)
361     g_free (self->last_data);
362
363   self->last_data = g_strdup(data);
364
365   actual_data = data;
366
367   if (self->files) {
368     g_list_foreach (self->files, (GFunc) gst_m3u8_media_file_free, NULL);
369     g_list_free (self->files);
370     self->files = NULL;
371   }
372
373   list = NULL;
374   duration = 0;
375   title = NULL;
376   data += 7;
377
378   while (TRUE) {
379
380     end = g_utf8_strchr (data, -1, '\n');
381     if (end)
382       *end = '\0';
383
384     if (data[0] != '#') {
385       gchar *r;
386       gchar *uri = NULL;
387
388       if (duration <= 0 && list == NULL) {
389         GST_LOG ("%s: got line without EXTINF or EXTSTREAMINF, dropping", data);
390         goto next_line;
391       }
392
393       if (!gst_uri_is_valid (data)) {
394         if (!make_valid_uri(self->uri, data, &uri))
395           goto next_line;
396       } else {
397         uri = g_strdup (data);
398       }
399
400       r = g_utf8_strchr (uri, -1, '\r');
401       if (r)
402         *r = '\0';
403
404       if (list != NULL) {
405         if (g_list_find_custom (self->lists, uri, (GCompareFunc) _m3u8_compare_uri)) {
406           GST_DEBUG ("Already have a list with this URI");
407           gst_m3u8_free (list);
408           g_free (uri);
409         } else {
410           gst_m3u8_set_uri (list, uri);
411           self->lists = g_list_append (self->lists, list);
412         }
413         list = NULL;
414       } else {
415         GstM3U8MediaFile *file;
416         gchar *send_IV = NULL;
417
418         if (key_url) {
419           GST_DEBUG ("AES-128 key url = %s", key_url);
420           if (NULL == IV) {
421             /* IV is not present in EXT-X-KEY tag. Prepare IV based on mediasequence */
422             GST_DEBUG ("IV is not in EXT-X-KEY tag... generating from media_seq_num = %d", self->mediasequence);
423             send_IV = gst_m3u8_getIV_from_mediasequence (self);
424           } else {
425             int i=0;
426             send_IV = g_malloc0(16);
427             for(i=0;i<16;i++){
428               send_IV[i]=IV[i];
429             }
430           }
431         }
432
433         file =
434             gst_m3u8_media_file_new (uri, title, duration, key_url, send_IV,
435             self->mediasequence++);
436         duration = 0;
437         title = NULL;
438         send_IV = NULL;
439
440         self->files = g_list_append (self->files, file);
441         GST_DEBUG ("------------>>>>>>appending uri = %s and key = %s\n", file->uri, file->key_url);
442       }
443     } else if (g_str_has_prefix (data, "#EXT-X-ENDLIST")) {
444       GST_DEBUG ("*************** End list is present ****************");
445       self->endlist = TRUE;
446     } else if (g_str_has_prefix (data, "#EXT-X-VERSION:")) {
447       if (int_from_string (data + 15, &data, &val, 10))
448         self->version = val;
449     } else if (g_str_has_prefix (data, "#EXT-X-STREAM-INF:")) {
450       gchar *v, *a;
451
452       if (list != NULL) {
453         GST_WARNING ("Found a list without a uri..., dropping");
454         gst_m3u8_free (list);
455       }
456
457       list = gst_m3u8_new ();
458       data = data + 18;
459       while (data && parse_attributes (&data, &a, &v)) {
460         if (g_str_equal (a, "BANDWIDTH")) {
461           if (!int_from_string (v, NULL, &list->bandwidth, 10))
462             GST_WARNING ("Error while reading BANDWIDTH");
463         } else if (g_str_equal (a, "PROGRAM-ID")) {
464           if (!int_from_string (v, NULL, &list->program_id, 10))
465             GST_WARNING ("Error while reading PROGRAM-ID");
466         } else if (g_str_equal (a, "CODECS")) {
467           g_free (list->codecs);
468           list->codecs = g_strdup (v);
469         } else if (g_str_equal (a, "RESOLUTION")) {
470           if (!int_from_string (v, &v, &list->width, 10))
471             GST_WARNING ("Error while reading RESOLUTION width");
472           if (!v || *v != '=') {
473             GST_WARNING ("Missing height");
474           } else {
475             v = g_utf8_next_char (v);
476             if (!int_from_string (v, NULL, &list->height, 10))
477               GST_WARNING ("Error while reading RESOLUTION height");
478           }
479         }
480       }
481     } else if (g_str_has_prefix (data, "#EXT-X-TARGETDURATION:")) {
482       if (int_from_string (data + 22, &data, &val, 10))
483         self->targetduration = val * GST_SECOND;
484     } else if (g_str_has_prefix (data, "#EXT-X-MEDIA-SEQUENCE:")) {
485       if (int_from_string (data + 22, &data, &val, 10))
486         self->mediasequence = val;
487     } else if (g_str_has_prefix (data, "#EXT-X-DISCONTINUITY")) {
488       /* discontinuity = TRUE; */
489     } else if (g_str_has_prefix (data, "#EXT-X-PROGRAM-DATE-TIME:")) {
490       /* <YYYY-MM-DDThh:mm:ssZ> */
491       GST_DEBUG ("FIXME parse date");
492     } else if (g_str_has_prefix (data, "#EXT-X-ALLOW-CACHE:")) {
493       g_free (self->allowcache);
494       self->allowcache = g_strdup (data + 19);
495     } else if (g_str_has_prefix (data, "#EXTINF:")) {
496       gdouble fval;
497       if (!double_from_string (data + 8, &data, &fval)) {
498         GST_WARNING ("Can't read EXTINF duration");
499         goto next_line;
500       }
501       duration = fval * (gdouble) GST_SECOND;
502       if (duration > self->targetduration)
503         GST_WARNING ("EXTINF duration > TARGETDURATION");
504       if (!data || *data != ',')
505         goto next_line;
506       data = g_utf8_next_char (data);
507       if (data != end) {
508         g_free (title);
509         title = g_strdup (data);
510       }
511     } else if (g_str_has_prefix (data, "#EXT-X-KEY:"))  {
512       gchar *val, *attr;
513       GST_DEBUG ("Found EXT-X-KEY tag...");
514
515       /* handling encrypted content */
516       data = data + 11; /* skipping "#EXT-X-KEY:" tag */
517
518       while (data && parse_attributes (&data, &attr, &val)) {
519         if (g_str_equal (attr, "METHOD")) {
520           if (g_str_equal (val, "NONE")) {
521             GST_LOG ("Non encrypted file...and skipping current line and going to next line\n");
522             goto next_line;
523           } else if (g_str_equal (val, "AES-128")) {
524             /* media files are encrypted */
525             GST_LOG ("media files are encrypted with AES-128\n");
526             // TODO: indicate in flag whether encrypted files or not
527           }
528         } else if (g_str_equal (attr, "URI")) {
529           gchar *end_dq = NULL;
530
531           if (!val){
532             GST_ERROR ("val is NULL");
533             break;
534           }
535
536           val = val + 1; /* eliminating first double quote in url */
537           if (!val) {
538             GST_ERROR ("val is NULL");
539             break;
540           }
541
542           end_dq = g_utf8_strrchr (val, -1, '"');
543           if (!end_dq) {
544             GST_ERROR ("end_dq is NULL");
545             break;
546           }
547
548           *end_dq = '\0';
549           GST_DEBUG ("Key URI = %s\n", val);
550
551           if (!gst_uri_is_valid (val)) {
552             gchar *slash;
553             if (!self->uri) {
554               GST_WARNING ("uri not set, can't build a valid uri");
555               goto next_line;
556             }
557             slash = g_utf8_strrchr (self->uri, -1, '/');
558             if (!slash) {
559               GST_WARNING ("Can't build a valid uri");
560               goto next_line;
561             }
562             *slash = '\0';
563             key_url = g_strdup_printf ("%s/%s", self->uri, val);
564             *slash = '/';
565           } else {
566             key_url= g_strdup (val);
567           }
568         } else if (g_str_equal (attr, "IV")) {
569           gint iv_len = 0;
570           gchar tmp_byte[3];
571           gint tmp_val = 0;
572           gint idx = 0;
573           gchar *car = NULL;
574
575           if (IV) {
576             g_free (IV);
577             IV = NULL;
578           }
579
580           IV = g_malloc0 (16);
581           if (NULL == IV) {
582             GST_ERROR ("Failed to allocate memory...\n");
583             goto error;
584           }
585
586           /* eliminating 0x/0X prefix */
587           val = val + 2;
588           car = g_utf8_strchr (val, -1, '\r');
589           if (car)
590             *car = '\0';
591
592           iv_len = g_utf8_strlen(val, -1);
593           if (iv_len < 1 || iv_len >32) {
594             GST_ERROR ("Wrong IV and iv_len = %d", iv_len);
595             goto error;
596           }
597
598           val+=iv_len;
599           idx = 15;
600           while (iv_len > 0) {
601             // TODO: val need to incremented I feel.. check again
602             val-=(iv_len==1)?1:2;
603             g_utf8_strncpy(tmp_byte, val, (iv_len==1)?1:2);
604             tmp_byte[2] = '\0';
605             tmp_val = 0;
606             if (!int_from_string (tmp_byte, NULL, &tmp_val, 16))
607               GST_WARNING ("Error while reading PROGRAM-ID");
608             IV[idx] = tmp_val;
609             idx--;
610             iv_len = iv_len - 2;
611           }
612         }
613       }
614     } else {
615       GST_LOG ("Ignored line: %s", data);
616     }
617
618   next_line:
619     if (!end)
620       break;
621     data = g_utf8_next_char (end);      /* skip \n */
622   }
623
624   /* redorder playlists by bitrate */
625   if (self->lists) {
626     gchar *top_variant_uri = NULL;
627
628     if (!self->current_variant)
629       top_variant_uri = GST_M3U8 (self->lists->data)->uri;
630     else
631       top_variant_uri = GST_M3U8 (self->current_variant->data)->uri;
632
633     self->lists =
634         g_list_sort (self->lists,
635         (GCompareFunc) gst_m3u8_compare_playlist_by_bitrate);
636
637     self->current_variant = g_list_find_custom (self->lists, top_variant_uri,
638         (GCompareFunc) _m3u8_compare_uri);
639   }
640
641   g_free (actual_data);
642   actual_data = NULL;
643   g_free (key_url);
644   key_url = NULL;
645   g_free (title);
646   title = NULL;
647   return TRUE;
648
649 error:
650   g_free (actual_data);
651   actual_data = NULL;
652   g_free (key_url);
653   key_url = NULL;
654   g_free (title);
655   title = NULL;
656   return FALSE;
657 }
658
659
660 GstM3U8Client *
661 gst_m3u8_client_new (const gchar * uri)
662 {
663   GstM3U8Client *client;
664
665   g_return_val_if_fail (uri != NULL, NULL);
666
667   client = g_new0 (GstM3U8Client, 1);
668   client->main = gst_m3u8_new ();
669   client->current = NULL;
670   client->sequence = -1;
671   client->update_failed_count = 0;
672   client->lock = g_mutex_new ();
673   gst_m3u8_set_uri (client->main, g_strdup (uri));
674
675   return client;
676 }
677
678 void
679 gst_m3u8_client_free (GstM3U8Client * self)
680 {
681   g_return_if_fail (self != NULL);
682
683   gst_m3u8_free (self->main);
684   g_mutex_free (self->lock);
685   g_free (self);
686 }
687
688 void
689 gst_m3u8_client_set_current (GstM3U8Client * self, GstM3U8 * m3u8)
690 {
691   g_return_if_fail (self != NULL);
692
693   GST_M3U8_CLIENT_LOCK (self);
694   if (m3u8 != self->current) {
695     self->current = m3u8;
696     self->update_failed_count = 0;
697   }
698   GST_M3U8_CLIENT_UNLOCK (self);
699 }
700
701 static guint
702 _get_start_sequence (GstM3U8Client * client)
703 {
704   GList *l;
705   GstClockTime duration_limit, duration_count = 0;
706   guint sequence = -1;
707
708   if (client->current->endlist) {
709     l = g_list_first (client->current->files);
710     sequence = GST_M3U8_MEDIA_FILE (l->data)->sequence;
711   } else {
712     duration_limit = client->current->targetduration * 3;
713     for (l = g_list_last (client->current->files); l; l = l->prev) {
714       duration_count += GST_M3U8_MEDIA_FILE (l->data)->duration;
715       sequence = GST_M3U8_MEDIA_FILE (l->data)->sequence;
716       if (duration_count >= duration_limit) {
717         break;
718       }
719     }
720   }
721   return sequence;
722 }
723
724 gboolean
725 gst_m3u8_client_update (GstM3U8Client * self, gchar * data)
726 {
727   GstM3U8 *m3u8;
728   gboolean updated = FALSE;
729   gboolean ret = FALSE;
730
731   g_return_val_if_fail (self != NULL, FALSE);
732
733   GST_M3U8_CLIENT_LOCK (self);
734   m3u8 = self->current ? self->current : self->main;
735
736   if (!gst_m3u8_update (m3u8, data, &updated))
737     goto out;
738
739   if (!updated) {
740     self->update_failed_count++;
741     goto out;
742   }
743
744   /* select the first playlist, for now */
745   if (!self->current) {
746     if (self->main->lists) {
747       self->current = self->main->current_variant->data;
748     } else {
749       self->current = self->main;
750     }
751   }
752
753   if (m3u8->files && self->sequence == -1) {
754 #if 0
755     self->sequence = GST_M3U8_MEDIA_FILE (g_list_first (m3u8->files)->data)->sequence;
756 #else
757     /* (spec : 6.3.3) In live case, the client SHOULD NOT choose a segment
758         which starts less than three target durations from the end of the Playlist file */
759     self->sequence = _get_start_sequence (self);
760 #endif
761     GST_DEBUG ("Setting first sequence at %d", self->sequence);
762   }
763
764   ret = TRUE;
765 out:
766   GST_M3U8_CLIENT_UNLOCK (self);
767   return ret;
768 }
769
770 static gboolean
771 _find_next (GstM3U8MediaFile * file, GstM3U8Client * client)
772 {
773   GST_DEBUG ("Found fragment %d", file->sequence);
774   if (file->sequence >= client->sequence)
775     return FALSE;
776   return TRUE;
777 }
778
779 static gboolean
780 _find_key (GstM3U8Key * key, GstM3U8Client * client)
781 {
782   if (key->sequence <= client->sequence)
783     return FALSE;
784   return TRUE;
785 }
786
787 void
788 gst_m3u8_client_get_current_position (GstM3U8Client * client,
789     GstClockTime * timestamp)
790 {
791   GList *l;
792   GList *walk;
793
794   l = g_list_find_custom (client->current->files, client,
795       (GCompareFunc) _find_next);
796
797   *timestamp = 0;
798   for (walk = client->current->files; walk; walk = walk->next) {
799     if (walk == l)
800       break;
801     *timestamp += GST_M3U8_MEDIA_FILE (walk->data)->duration;
802   }
803 }
804
805 gboolean
806 gst_m3u8_client_get_next_fragment (GstM3U8Client * client, GstClockTime cur_running_time,
807     gboolean * discontinuity, const gchar ** uri, GstClockTime * duration,
808     GstClockTime * timestamp, gchar **key_uri, gchar **iv)
809 {
810   GList *l;
811   GstM3U8MediaFile *file;
812
813   g_return_val_if_fail (client != NULL, FALSE);
814   g_return_val_if_fail (client->current != NULL, FALSE);
815   g_return_val_if_fail (discontinuity != NULL, FALSE);
816
817   GST_M3U8_CLIENT_LOCK (client);
818
819   GST_DEBUG ("Looking for fragment %d", client->sequence);
820
821   if (client->main && client->main->lists && client->current->endlist) {
822     GList *walk = NULL;
823     GstClockTime current_pos, target_pos;
824     gint current_sequence;
825     gboolean found_sequence = FALSE;
826
827     /* VOD playlist, so find mediasequence from time */
828     // TODO: need to handle when live converts to VOD by putting END_LIST tag
829
830     file = GST_M3U8_MEDIA_FILE (client->current->files->data);
831     current_sequence = file->sequence;
832     current_pos = 0 ;
833     target_pos = cur_running_time;
834
835     for (walk = client->current->files; walk; walk = walk->next) {
836       file = walk->data;
837
838       current_sequence = file->sequence;
839       if (current_pos <= target_pos &&
840         target_pos < current_pos + file->duration) {
841         found_sequence = TRUE;
842         break;
843       }
844       current_pos += file->duration;
845     }
846
847     if (found_sequence) {
848       GST_ERROR ("changing client sequence from %d -> %d", client->sequence, current_sequence);
849       client->sequence = current_sequence;
850     } else {
851       GST_WARNING ("max file_seq = %d and client_seq = %d", current_sequence, client->sequence);
852       GST_M3U8_CLIENT_UNLOCK (client);
853       return FALSE;
854     }
855   }
856
857   l = g_list_find_custom (client->current->files, client, (GCompareFunc) _find_next);
858   if (l == NULL) {
859     GST_M3U8_CLIENT_UNLOCK (client);
860     return FALSE;
861   }
862
863   gst_m3u8_client_get_current_position (client, timestamp);
864
865   file = GST_M3U8_MEDIA_FILE (l->data);
866
867   if (file->key_url)
868     *key_uri = g_strdup (file->key_url);
869
870   if (file->iv) {
871     *iv = g_malloc0 (GST_M3U8_IV_LEN);
872     //*iv = file->iv;
873     memcpy (*iv, file->iv, GST_M3U8_IV_LEN);
874   }
875
876   *discontinuity = client->sequence != file->sequence;
877   client->sequence = file->sequence + 1;
878
879   if (file->uri)
880     *uri = g_strdup (file->uri);
881
882   *duration = file->duration;
883
884   GST_M3U8_CLIENT_UNLOCK (client);
885   return TRUE;
886 }
887
888 void gst_m3u8_client_decrement_sequence (GstM3U8Client * client)
889 {
890   GST_M3U8_CLIENT_LOCK (client);
891   client->sequence = client->sequence - 1;
892   GST_M3U8_CLIENT_UNLOCK (client);
893 }
894
895 static void
896 _sum_duration (GstM3U8MediaFile * self, GstClockTime * duration)
897 {
898   *duration += self->duration;
899 }
900
901 GstClockTime
902 gst_m3u8_client_get_duration (GstM3U8Client * client)
903 {
904   GstClockTime duration = 0;
905
906   g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
907
908   GST_M3U8_CLIENT_LOCK (client);
909   /* We can only get the duration for on-demand streams */
910   if (!client->current->endlist) {
911     GST_M3U8_CLIENT_UNLOCK (client);
912     return GST_CLOCK_TIME_NONE;
913   }
914
915   g_list_foreach (client->current->files, (GFunc) _sum_duration, &duration);
916   GST_M3U8_CLIENT_UNLOCK (client);
917   return duration;
918 }
919
920 GstClockTime
921 gst_m3u8_client_get_target_duration (GstM3U8Client * client)
922 {
923   GstClockTime duration = 0;
924
925   g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
926
927   GST_M3U8_CLIENT_LOCK (client);
928   duration = client->current->targetduration;
929   GST_M3U8_CLIENT_UNLOCK (client);
930   return duration;
931 }
932
933 /* used for LIVE case only, to update playlist file */
934 GstClockTime
935 gst_m3u8_client_get_last_fragment_duration (GstM3U8Client * client)
936 {
937   GstClockTime duration = 0;
938   GstM3U8MediaFile *last_file = NULL;
939   GList *last_list = NULL;
940
941   g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
942
943   GST_M3U8_CLIENT_LOCK (client);
944
945   if (!client->current) {
946     GST_ERROR ("current variant is NULL.. return CLOCK_NONE");
947     GST_M3U8_CLIENT_UNLOCK (client);
948     return GST_CLOCK_TIME_NONE;
949   }
950
951   if (!client->current->files) {
952     GST_WARNING ("Due to playlist updation problem.. did not update the playlist");
953     GST_M3U8_CLIENT_UNLOCK (client);
954     return 0; /* update playlist immediately */
955   }
956
957   last_list = g_list_last (client->current->files);
958
959   if (!last_list) {
960     GST_WARNING ("list does not have last object... return target_duration");
961     GST_M3U8_CLIENT_UNLOCK (client);
962     return client->current->targetduration;
963   }
964
965   last_file = (GstM3U8MediaFile *)(last_list->data);
966   if(last_file)
967     duration = last_file->duration;
968   else
969     duration=client->current->targetduration;
970
971   GST_DEBUG ("Last file duration = %"GST_TIME_FORMAT"\n", GST_TIME_ARGS(duration));
972   GST_M3U8_CLIENT_UNLOCK (client);
973   return duration;
974 }
975
976 const gchar *
977 gst_m3u8_client_get_uri (GstM3U8Client * client)
978 {
979   const gchar *uri;
980
981   g_return_val_if_fail (client != NULL, NULL);
982
983   GST_M3U8_CLIENT_LOCK (client);
984   uri = client->main->uri;
985   GST_M3U8_CLIENT_UNLOCK (client);
986   return uri;
987 }
988
989 const gchar *
990 gst_m3u8_client_get_current_uri (GstM3U8Client * client)
991 {
992   const gchar *uri;
993
994   g_return_val_if_fail (client != NULL, NULL);
995
996   GST_M3U8_CLIENT_LOCK (client);
997   uri = client->current->uri;
998   GST_M3U8_CLIENT_UNLOCK (client);
999   return uri;
1000 }
1001
1002 gint
1003 gst_m3u8_client_get_current_bandwidth (GstM3U8Client * client)
1004 {
1005   gint bandwidth;
1006
1007   g_return_val_if_fail (client != NULL, -1);
1008
1009   GST_M3U8_CLIENT_LOCK (client);
1010   bandwidth = client->current->bandwidth;
1011   GST_M3U8_CLIENT_UNLOCK (client);
1012   return bandwidth;
1013 }
1014
1015 gboolean
1016 gst_m3u8_client_has_variant_playlist (GstM3U8Client * client)
1017 {
1018   gboolean ret;
1019
1020   g_return_val_if_fail (client != NULL, FALSE);
1021
1022   GST_M3U8_CLIENT_LOCK (client);
1023   ret = (client->main->lists != NULL);
1024   GST_M3U8_CLIENT_UNLOCK (client);
1025   return ret;
1026 }
1027
1028 gboolean
1029 gst_m3u8_client_is_live (GstM3U8Client * client)
1030 {
1031   gboolean ret;
1032
1033   g_return_val_if_fail (client != NULL, FALSE);
1034
1035   GST_M3U8_CLIENT_LOCK (client);
1036   if (!client->current || client->current->endlist)
1037     ret = FALSE;
1038   else
1039     ret = TRUE;
1040   GST_M3U8_CLIENT_UNLOCK (client);
1041   return ret;
1042 }
1043
1044 GList *
1045 gst_m3u8_client_get_next_higher_bw_playlist (GstM3U8Client * client)
1046 {
1047   GList *current_variant;
1048
1049   g_return_val_if_fail (client != NULL, FALSE);
1050
1051   GST_M3U8_CLIENT_LOCK (client);
1052   current_variant = g_list_next (client->main->current_variant);
1053   if (!current_variant) {
1054     GST_WARNING ("no next variant available...");
1055     GST_M3U8_CLIENT_UNLOCK (client);
1056     return NULL;
1057   }
1058
1059   GST_DEBUG ("next variant = %p and uri = %s", current_variant, GST_M3U8(current_variant->data)->uri);
1060   GST_M3U8_CLIENT_UNLOCK (client);
1061   return current_variant;
1062
1063 }
1064
1065 GList *
1066 gst_m3u8_client_get_next_lower_bw_playlist (GstM3U8Client * client)
1067 {
1068   GList *current_variant;
1069
1070   g_return_val_if_fail (client != NULL, FALSE);
1071
1072   GST_M3U8_CLIENT_LOCK (client);
1073   current_variant = g_list_previous (client->main->current_variant);
1074   if (!current_variant) {
1075     GST_WARNING ("no previous variant available...");
1076     GST_M3U8_CLIENT_UNLOCK (client);
1077     return NULL;
1078   }
1079
1080   GST_DEBUG ("previous variant = %p and uri = %s", current_variant, GST_M3U8(current_variant->data)->uri);
1081   GST_M3U8_CLIENT_UNLOCK (client);
1082   return current_variant;
1083 }
1084
1085
1086 #ifndef SWITCH_TRIGGER_POINT
1087 GList *
1088 gst_m3u8_client_get_playlist_for_bitrate (GstM3U8Client * client, guint bitrate)
1089 {
1090   GList *list, *current_variant;
1091
1092   GST_M3U8_CLIENT_LOCK (client);
1093   current_variant = client->main->current_variant;
1094
1095   /*  Go to the highest possible bandwidth allowed */
1096   while (GST_M3U8 (current_variant->data)->bandwidth < bitrate) {
1097     list = g_list_next (current_variant);
1098     if (!list)
1099       break;
1100     current_variant = list;
1101   }
1102
1103   while (GST_M3U8 (current_variant->data)->bandwidth > bitrate) {
1104     list = g_list_previous (current_variant);
1105     if (!list)
1106       break;
1107     current_variant = list;
1108   }
1109   GST_M3U8_CLIENT_UNLOCK (client);
1110
1111   return current_variant;
1112 }
1113 #else
1114 GList *
1115 gst_m3u8_client_get_playlist_for_bitrate (GstM3U8Client * client, guint bitrate)
1116 {
1117   GList *next_variant = NULL;
1118   GList *current_variant = NULL;
1119   guint current_bandwidth = 0;
1120
1121   GST_M3U8_CLIENT_LOCK (client);
1122   current_variant = client->main->current_variant;
1123   current_bandwidth = GST_M3U8 (current_variant->data)->bandwidth;
1124
1125   if ((current_bandwidth + BITRATE_SWITCH_UPPER_THRESHOLD * current_bandwidth) < bitrate) {
1126     GST_DEBUG ("Switch to upper band...\n");
1127
1128     if (!g_list_next (current_variant)) {
1129       GST_DEBUG ("no next upper fragment...\n");
1130       GST_M3U8_CLIENT_UNLOCK (client);
1131       return current_variant;
1132     } else {
1133       GST_DEBUG (">>>>> UP : current_bw = %d, bitrate = %d\n", current_bandwidth, bitrate);
1134       next_variant = current_variant;
1135
1136       /*  Go to the highest possible bandwidth allowed */
1137       while ((current_bandwidth + BITRATE_SWITCH_UPPER_THRESHOLD * current_bandwidth) < bitrate) {
1138         current_variant = next_variant;
1139         next_variant = g_list_next (next_variant);
1140         if (!next_variant) {
1141           GST_DEBUG ("no next upper fragment...\n");
1142           GST_M3U8_CLIENT_UNLOCK (client);
1143           return current_variant;
1144         }
1145
1146         current_bandwidth = GST_M3U8 (next_variant->data)->bandwidth;
1147         GST_DEBUG ("current_bw in while= %d & URI = %s\n", current_bandwidth, GST_M3U8 (next_variant->data)->uri);
1148       }
1149
1150       /* as condition failed fallback to previous */
1151       GST_M3U8_CLIENT_UNLOCK (client);
1152
1153       return current_variant;
1154     }
1155   } else if ((current_bandwidth + BITRATE_SWITCH_LOWER_THRESHOLD * current_bandwidth) > bitrate) {
1156     GST_DEBUG ("Switch to lower band...\n");
1157
1158     if (!g_list_previous (current_variant)) {
1159       GST_DEBUG ("no previous lower fragment...to go further down\n");
1160       GST_M3U8_CLIENT_UNLOCK (client);
1161       return current_variant;
1162     } else {
1163
1164       GST_DEBUG (">>>>> LOW : current_bw = %d, bitrate = %d\n", current_bandwidth, bitrate);
1165       next_variant = current_variant;
1166
1167       /*  Go to the lowest possible bandwidth allowed */
1168       while ((current_bandwidth + BITRATE_SWITCH_LOWER_THRESHOLD * current_bandwidth) > bitrate) {
1169
1170         next_variant = g_list_previous (next_variant);
1171         if (!next_variant) {
1172           GST_DEBUG ("no previous lower fragment...\n");
1173           GST_M3U8_CLIENT_UNLOCK (client);
1174           return current_variant;
1175         }
1176
1177         current_variant = next_variant;
1178
1179         current_bandwidth = GST_M3U8 (current_variant->data)->bandwidth;
1180         GST_DEBUG ("current_bw in while= %d\n", current_bandwidth);
1181       }
1182
1183       GST_M3U8_CLIENT_UNLOCK (client);
1184
1185       /* as condition failed fallback to previous */
1186       return current_variant;
1187     }
1188   } else {
1189     GST_DEBUG ("No need to switch .... returning current_variant..\n");
1190   }
1191
1192   GST_M3U8_CLIENT_UNLOCK (client);
1193
1194   return current_variant;
1195 }
1196 #endif
1197
1198 gboolean
1199 gst_m3u8_client_decrypt_init (GstM3U8Client * client, unsigned char *key, gchar *iv)
1200 {
1201   //if (!client->current->cipher_ctx)
1202  //   client->current->cipher_ctx = g_malloc0 (sizeof(EVP_CIPHER_CTX));
1203
1204   EVP_CIPHER_CTX_init (&(client->cipher_ctx));
1205   EVP_CIPHER_CTX_set_padding(&(client->cipher_ctx), 0); // added to check avodining final buffer decryption error
1206
1207   return EVP_DecryptInit_ex (&(client->cipher_ctx), EVP_aes_128_cbc(), NULL, key, iv);
1208 }
1209
1210 gboolean
1211 gst_m3u8_client_decrypt_update (GstM3U8Client * client, guint8 * out_data,
1212     gint * out_size, guint8 * in_data, gint in_size)
1213 {
1214   //g_return_val_if_fail (client->current->cipher_ctx != NULL, FALSE);
1215
1216   return EVP_DecryptUpdate (&(client->cipher_ctx), out_data, out_size,
1217       in_data, in_size);
1218 }
1219
1220 gboolean
1221 gst_m3u8_client_decrypt_final (GstM3U8Client * client, guint8 * out_data,
1222     gint * out_size)
1223 {
1224   //g_return_val_if_fail (client->current->cipher_ctx != NULL, FALSE);
1225
1226   return EVP_DecryptFinal_ex (&(client->cipher_ctx), out_data, out_size);
1227 }
1228
1229 gboolean
1230 gst_m3u8_client_decrypt_deinit (GstM3U8Client * client)
1231 {
1232   //g_return_val_if_fail (client->current->cipher_ctx != NULL, FALSE);
1233
1234   return EVP_CIPHER_CTX_cleanup (&(client->cipher_ctx));
1235 }
1236
1237
1238 gboolean
1239 gst_m3u8_client_is_playlist_download_needed (GstM3U8Client * self)
1240 {
1241   /* download is not need, if last_data is present & not live (i.e. we can reuse last VOD child playlist)*/
1242   if (self->current->last_data && !gst_m3u8_client_is_live(self))
1243     return FALSE;
1244   else
1245     return TRUE;
1246 }