Tizen 2.0 Release
[framework/multimedia/gst-plugins-bad0.10.git] / gst / hls / m3u8.c
1 /* GStreamer
2  * Copyright (C) 2010 Marc-Andre Lureau <marcandre.lureau@gmail.com>
3  *
4  * m3u8.c:
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include <stdlib.h>
23 #include <math.h>
24 #include <errno.h>
25 #include <glib.h>
26
27 #include <gst/glib-compat-private.h>
28 #include "gstfragmented.h"
29 #include "m3u8.h"
30
31 #define GST_CAT_DEFAULT fragmented_debug
32
33 static GstM3U8 *gst_m3u8_new (void);
34 static void gst_m3u8_free (GstM3U8 * m3u8);
35 static gboolean gst_m3u8_update (GstM3U8 * m3u8, gchar * data,
36     gboolean * updated);
37 static GstM3U8MediaFile *gst_m3u8_media_file_new (gchar * uri,
38     gchar * title, GstClockTime duration, guint sequence);
39 static void gst_m3u8_media_file_free (GstM3U8MediaFile * self);
40 static GstM3U8Key *gst_m3u8_key_new (GstM3U8EncryptionMethod method,
41     gchar * uri, guint8 *iv, guint sequence);
42 static void gst_m3u8_key_free (GstM3U8Key * self);
43
44 static GstM3U8 *
45 gst_m3u8_new (void)
46 {
47   GstM3U8 *m3u8;
48
49   m3u8 = g_new0 (GstM3U8, 1);
50
51   return m3u8;
52 }
53
54 static void
55 gst_m3u8_set_uri (GstM3U8 * self, gchar * uri)
56 {
57   g_return_if_fail (self != NULL);
58
59   if (self->uri)
60     g_free (self->uri);
61   self->uri = uri;
62 }
63
64 static void
65 gst_m3u8_free (GstM3U8 * self)
66 {
67   g_return_if_fail (self != NULL);
68
69   g_free (self->uri);
70   g_free (self->allowcache);
71   g_free (self->codecs);
72   g_free (self->cipher_ctx);
73
74   g_list_foreach (self->files, (GFunc) gst_m3u8_media_file_free, NULL);
75   g_list_free (self->files);
76
77   g_list_foreach (self->keys, (GFunc) gst_m3u8_key_free, NULL);
78   g_list_free (self->keys);
79
80   g_free (self->last_data);
81   g_list_foreach (self->lists, (GFunc) gst_m3u8_free, NULL);
82   g_list_free (self->lists);
83
84   g_free (self);
85 }
86
87 static GstM3U8MediaFile *
88 gst_m3u8_media_file_new (gchar * uri, gchar * title, GstClockTime duration,
89     guint sequence)
90 {
91   GstM3U8MediaFile *file;
92
93   file = g_new0 (GstM3U8MediaFile, 1);
94   file->uri = uri;
95   file->title = title;
96   file->duration = duration;
97   file->sequence = sequence;
98
99   return file;
100 }
101
102 static void
103 gst_m3u8_media_file_free (GstM3U8MediaFile * self)
104 {
105   g_return_if_fail (self != NULL);
106
107   g_free (self->title);
108   g_free (self->uri);
109   g_free (self);
110 }
111
112 static GstM3U8Key *
113 gst_m3u8_key_new (GstM3U8EncryptionMethod method, gchar * uri,
114   guint8 *iv, guint sequence)
115 {
116   GstM3U8Key *key;
117
118   key = g_new0 (GstM3U8Key, 1);
119   key->method = method;
120   key->uri = uri;
121   key->iv = iv;
122   key->sequence = sequence;
123   key->data = NULL;
124
125   return key;
126 }
127
128 static void
129 gst_m3u8_key_free (GstM3U8Key * self)
130 {
131   g_return_if_fail (self != NULL);
132
133   g_free (self->uri);
134   g_free (self->iv);
135   g_free (self->data);
136   g_free (self);
137 }
138
139 static gboolean
140 int_from_string (gchar * ptr, gchar ** endptr, gint * val)
141 {
142   gchar *end;
143   glong ret;
144
145   g_return_val_if_fail (ptr != NULL, FALSE);
146   g_return_val_if_fail (val != NULL, FALSE);
147
148   errno = 0;
149   ret = strtol (ptr, &end, 10);
150   if ((errno == ERANGE && (ret == LONG_MAX || ret == LONG_MIN))
151       || (errno != 0 && ret == 0)) {
152     GST_WARNING ("%s", g_strerror (errno));
153     return FALSE;
154   }
155
156   if (ret > G_MAXINT) {
157     GST_WARNING ("%s", g_strerror (ERANGE));
158     return FALSE;
159   }
160
161   if (endptr)
162     *endptr = end;
163
164   *val = (gint) ret;
165
166   return end != ptr;
167 }
168
169 static gboolean
170 double_from_string (gchar * ptr, gchar ** endptr, gdouble * val)
171 {
172   gchar *end;
173   gdouble ret;
174
175   g_return_val_if_fail (ptr != NULL, FALSE);
176   g_return_val_if_fail (val != NULL, FALSE);
177
178   errno = 0;
179   ret = strtod (ptr, &end);
180   if ((errno == ERANGE && (ret == HUGE_VAL || ret == -HUGE_VAL))
181       || (errno != 0 && ret == 0)) {
182     GST_WARNING ("%s", g_strerror (errno));
183     return FALSE;
184   }
185
186   if (!isfinite (ret)) {
187     GST_WARNING ("%s", g_strerror (ERANGE));
188     return FALSE;
189   }
190
191   if (endptr)
192     *endptr = end;
193
194   *val = (gint) ret;
195
196   return end != ptr;
197 }
198
199 static gboolean
200 iv_from_string (gchar * ptr, gchar ** endptr, guint8 * val)
201 {
202   guint8 idx;
203   guint8 hex;
204
205   g_return_val_if_fail (ptr != NULL, FALSE);
206   g_return_val_if_fail (val != NULL, FALSE);
207
208   if (*ptr == '0' && (*(ptr + 1) == 'x' || *(ptr + 1) == 'X')) {
209     ptr = ptr + 2;  /* skip 0x or 0X */
210   }
211
212   for (idx = 0; idx < GST_M3U8_IV_LEN; idx++) {
213     hex = *ptr++ - '0';
214     if (hex >= 16)
215       return FALSE;
216     *(val + idx) = hex * 16;
217     hex = *ptr++ - '0';
218     if (hex >= 16)
219       return FALSE;
220     *(val + idx) += hex;
221   }
222
223   if (endptr)
224     *endptr = ptr;
225
226   return TRUE;
227 }
228
229 static gboolean
230 iv_from_uint (guint uint, guint8 * val)
231 {
232   guint8 idx;
233
234   g_return_val_if_fail (val != NULL, FALSE);
235
236   val = val + GST_M3U8_IV_LEN - 1;
237   for (idx = 0; idx < sizeof(guint); idx++) {
238     *val-- = (guint8)uint;
239     uint = uint >> 8;
240   }
241
242   return TRUE;
243 }
244
245 static gboolean
246 make_valid_uri (gchar *prefix, gchar *suffix, gchar **uri)
247 {
248   gchar *slash;
249
250   if (!prefix) {
251     GST_WARNING ("uri prefix not set, can't build a valid uri");
252     return FALSE;
253   }
254   slash = g_utf8_strrchr (prefix, -1, '/');
255   if (!slash) {
256     GST_WARNING ("Can't build a valid uri");
257     return FALSE;
258   }
259
260   *slash = '\0';
261   *uri = g_strdup_printf ("%s/%s", prefix, suffix);
262   *slash = '/';
263
264   return TRUE;
265 }
266
267 static gboolean
268 parse_attributes (gchar ** ptr, gchar ** a, gchar ** v)
269 {
270   gchar *end, *p;
271
272   g_return_val_if_fail (ptr != NULL, FALSE);
273   g_return_val_if_fail (*ptr != NULL, FALSE);
274   g_return_val_if_fail (a != NULL, FALSE);
275   g_return_val_if_fail (v != NULL, FALSE);
276
277   /* [attribute=value,]* */
278
279   *a = *ptr;
280   end = p = g_utf8_strchr (*ptr, -1, ',');
281   if (end) {
282     do {
283       end = g_utf8_next_char (end);
284     } while (end && *end == ' ');
285     *p = '\0';
286   }
287
288   *v = p = g_utf8_strchr (*ptr, -1, '=');
289   if (*v) {
290     *v = g_utf8_next_char (*v);
291     *p = '\0';
292   } else {
293     GST_WARNING ("missing = after attribute");
294     return FALSE;
295   }
296
297   *ptr = end;
298   return TRUE;
299 }
300
301 static gint
302 _m3u8_compare_uri (GstM3U8 * a, gchar * uri)
303 {
304   g_return_val_if_fail (a != NULL, 0);
305   g_return_val_if_fail (uri != NULL, 0);
306
307   return g_strcmp0 (a->uri, uri);
308 }
309
310 static gint
311 gst_m3u8_compare_playlist_by_bitrate (gconstpointer a, gconstpointer b)
312 {
313   return ((GstM3U8 *) (a))->bandwidth - ((GstM3U8 *) (b))->bandwidth;
314 }
315
316 /*
317  * @data: a m3u8 playlist text data, taking ownership
318  */
319 static gboolean
320 gst_m3u8_update (GstM3U8 * self, gchar * data, gboolean * updated)
321 {
322   gint val;
323   GstClockTime duration;
324   gchar *title, *end;
325 //  gboolean discontinuity;
326   GstM3U8 *list;
327
328   g_return_val_if_fail (self != NULL, FALSE);
329   g_return_val_if_fail (data != NULL, FALSE);
330   g_return_val_if_fail (updated != NULL, FALSE);
331
332   *updated = TRUE;
333
334   /* check if the data changed since last update */
335   if (self->last_data && g_str_equal (self->last_data, data)) {
336     GST_DEBUG ("Playlist is the same as previous one");
337     *updated = FALSE;
338     g_free (data);
339     return TRUE;
340   }
341
342   if (!g_str_has_prefix (data, "#EXTM3U")) {
343     GST_WARNING ("Data doesn't start with #EXTM3U");
344     *updated = FALSE;
345     g_free (data);
346     return FALSE;
347   }
348
349   g_free (self->last_data);
350   self->last_data = data;
351
352   if (self->files) {
353     g_list_foreach (self->files, (GFunc) gst_m3u8_media_file_free, NULL);
354     g_list_free (self->files);
355     self->files = NULL;
356   }
357
358   if (self->keys) {
359     g_list_foreach (self->keys, (GFunc) gst_m3u8_key_free, NULL);
360     g_list_free (self->keys);
361     self->keys = NULL;
362   }
363
364   list = NULL;
365   duration = 0;
366   title = NULL;
367   data += 7;
368   while (TRUE) {
369     end = g_utf8_strchr (data, -1, '\n');
370     if (end)
371       *end = '\0';
372
373     if (data[0] != '#') {
374       gchar *r;
375
376       if (duration <= 0 && list == NULL) {
377         GST_LOG ("%s: got line without EXTINF or EXTSTREAMINF, dropping", data);
378         goto next_line;
379       }
380
381       if (!gst_uri_is_valid (data)) {
382         if (!make_valid_uri(self->uri, data, &data))
383           goto next_line;
384       } else {
385         data = g_strdup (data);
386       }
387
388       r = g_utf8_strchr (data, -1, '\r');
389       if (r)
390         *r = '\0';
391
392       if (list != NULL) {
393         if (g_list_find_custom (self->lists, data,
394                 (GCompareFunc) _m3u8_compare_uri)) {
395           GST_DEBUG ("Already have a list with this URI");
396           gst_m3u8_free (list);
397           g_free (data);
398         } else {
399           gst_m3u8_set_uri (list, data);
400           self->lists = g_list_append (self->lists, list);
401         }
402         list = NULL;
403       } else {
404         GstM3U8MediaFile *file;
405         file =
406             gst_m3u8_media_file_new (data, title, duration,
407             self->mediasequence++);
408         duration = 0;
409         title = NULL;
410         self->files = g_list_append (self->files, file);
411       }
412
413     } else if (g_str_has_prefix (data, "#EXT-X-ENDLIST")) {
414       self->endlist = TRUE;
415     } else if (g_str_has_prefix (data, "#EXT-X-VERSION:")) {
416       if (int_from_string (data + 15, &data, &val))
417         self->version = val;
418     } else if (g_str_has_prefix (data, "#EXT-X-STREAM-INF:")) {
419       gchar *v, *a;
420
421       if (list != NULL) {
422         GST_WARNING ("Found a list without a uri..., dropping");
423         gst_m3u8_free (list);
424       }
425
426       list = gst_m3u8_new ();
427       data = data + 18;
428       while (data && parse_attributes (&data, &a, &v)) {
429         if (g_str_equal (a, "BANDWIDTH")) {
430           if (!int_from_string (v, NULL, &list->bandwidth))
431             GST_WARNING ("Error while reading BANDWIDTH");
432         } else if (g_str_equal (a, "PROGRAM-ID")) {
433           if (!int_from_string (v, NULL, &list->program_id))
434             GST_WARNING ("Error while reading PROGRAM-ID");
435         } else if (g_str_equal (a, "CODECS")) {
436           g_free (list->codecs);
437           list->codecs = g_strdup (v);
438         } else if (g_str_equal (a, "RESOLUTION")) {
439           if (!int_from_string (v, &v, &list->width))
440             GST_WARNING ("Error while reading RESOLUTION width");
441           if (!v || *v != '=') {
442             GST_WARNING ("Missing height");
443           } else {
444             v = g_utf8_next_char (v);
445             if (!int_from_string (v, NULL, &list->height))
446               GST_WARNING ("Error while reading RESOLUTION height");
447           }
448         }
449       }
450     } else if (g_str_has_prefix (data, "#EXT-X-TARGETDURATION:")) {
451       if (int_from_string (data + 22, &data, &val))
452         self->targetduration = val * GST_SECOND;
453     } else if (g_str_has_prefix (data, "#EXT-X-MEDIA-SEQUENCE:")) {
454       if (int_from_string (data + 22, &data, &val))
455         self->mediasequence = val;
456     } else if (g_str_has_prefix (data, "#EXT-X-DISCONTINUITY")) {
457       /* discontinuity = TRUE; */
458     } else if (g_str_has_prefix (data, "#EXT-X-PROGRAM-DATE-TIME:")) {
459       /* <YYYY-MM-DDThh:mm:ssZ> */
460       GST_DEBUG ("FIXME parse date");
461     } else if (g_str_has_prefix (data, "#EXT-X-ALLOW-CACHE:")) {
462       g_free (self->allowcache);
463       self->allowcache = g_strdup (data + 19);
464     } else if (g_str_has_prefix (data, "#EXTINF:")) {
465       gdouble fval;
466       if (!double_from_string (data + 8, &data, &fval)) {
467         GST_WARNING ("Can't read EXTINF duration");
468         goto next_line;
469       }
470       duration = fval * (gdouble) GST_SECOND;
471       if (duration > self->targetduration)
472         GST_WARNING ("EXTINF duration > TARGETDURATION");
473       if (!data || *data != ',')
474         goto next_line;
475       data = g_utf8_next_char (data);
476       if (data != end) {
477         g_free (title);
478         title = g_strdup (data);
479       }
480     } else if (g_str_has_prefix (data, "#EXT-X-KEY:")) {
481       gchar *v, *a;
482       GstM3U8Key *key;
483       GstM3U8EncryptionMethod encryption;
484       gchar *key_uri;
485       guint8 *iv;
486
487       data = data + 11;
488       encryption = GST_M3U8_ENCRYPTED_NONE;
489       key_uri = NULL;
490       iv = NULL;
491       while (data && parse_attributes (&data, &a, &v)) {
492         if (g_str_equal (a, "METHOD")) {
493           if (g_str_equal (v, "NONE")) {
494             encryption = GST_M3U8_ENCRYPTED_NONE;
495           } else if (g_str_equal (v, "AES-128")) {
496             encryption = GST_M3U8_ENCRYPTED_AES_128;
497           } else {
498             GST_WARNING ("Unsuppported encryption method..., skipping");
499             goto next_line;
500           }
501         } else if (g_str_equal (a, "URI")) {
502           gchar *dq, *r;
503
504           if (*v == '"') {
505             v = v + 1;  /* skip first double quote in uri */
506           }
507           dq = g_utf8_strrchr (v, -1, '"');
508           if (dq)
509             *dq = '\0';
510           if (!gst_uri_is_valid (v)) {
511             if (!make_valid_uri(self->uri, v, &key_uri))
512               goto next_line;
513           } else {
514             key_uri = g_strdup(v);
515           }
516           r = g_utf8_strchr (v, -1, '\r');
517           if (r)
518             *r = '\0';
519         } else if (g_str_equal (a, "IV")) {
520           iv = g_malloc0 (GST_M3U8_IV_LEN);
521           if (!iv_from_string (data, &data, iv)) {
522             GST_WARNING ("Can't read IV");
523             goto next_line;
524           }
525         }
526       }
527       if ((encryption != GST_M3U8_ENCRYPTED_NONE) && (key_uri != NULL)) {
528         if (iv == NULL) {
529           iv = g_malloc0 (GST_M3U8_IV_LEN);
530           if (!iv_from_uint (self->mediasequence, iv)) {
531             GST_WARNING ("Can't convert IV from sequence");
532             goto next_line;
533           }
534         }
535       } else {
536         g_free(key_uri);
537         g_free(iv);
538         if (encryption != GST_M3U8_ENCRYPTED_NONE) {
539           GST_WARNING ("Key uri not set");
540           goto next_line;
541         }
542       }
543       key = gst_m3u8_key_new (encryption, key_uri, iv,
544           self->mediasequence);
545       self->keys = g_list_prepend (self->keys, key);
546     } else {
547       GST_LOG ("Ignored line: %s", data);
548     }
549
550   next_line:
551     if (!end)
552       break;
553     data = g_utf8_next_char (end);      /* skip \n */
554   }
555
556   /* redorder playlists by bitrate */
557   if (self->lists) {
558     gchar *top_variant_uri = NULL;
559
560     if (!self->current_variant)
561       top_variant_uri = GST_M3U8 (self->lists->data)->uri;
562     else
563       top_variant_uri = GST_M3U8 (self->current_variant->data)->uri;
564
565     self->lists =
566         g_list_sort (self->lists,
567         (GCompareFunc) gst_m3u8_compare_playlist_by_bitrate);
568
569     self->current_variant = g_list_find_custom (self->lists, top_variant_uri,
570         (GCompareFunc) _m3u8_compare_uri);
571   }
572
573   return TRUE;
574 }
575
576 GstM3U8Client *
577 gst_m3u8_client_new (const gchar * uri)
578 {
579   GstM3U8Client *client;
580
581   g_return_val_if_fail (uri != NULL, NULL);
582
583   client = g_new0 (GstM3U8Client, 1);
584   client->main = gst_m3u8_new ();
585   client->current = NULL;
586   client->sequence = -1;
587   client->update_failed_count = 0;
588   client->lock = g_mutex_new ();
589   gst_m3u8_set_uri (client->main, g_strdup (uri));
590
591   return client;
592 }
593
594 void
595 gst_m3u8_client_free (GstM3U8Client * self)
596 {
597   g_return_if_fail (self != NULL);
598
599   gst_m3u8_free (self->main);
600   g_mutex_free (self->lock);
601   g_free (self);
602 }
603
604 void
605 gst_m3u8_client_set_current (GstM3U8Client * self, GstM3U8 * m3u8)
606 {
607   g_return_if_fail (self != NULL);
608
609   GST_M3U8_CLIENT_LOCK (self);
610   if (m3u8 != self->current) {
611     self->current = m3u8;
612     self->update_failed_count = 0;
613   }
614   GST_M3U8_CLIENT_UNLOCK (self);
615 }
616
617 gboolean
618 gst_m3u8_client_update (GstM3U8Client * self, gchar * data)
619 {
620   GstM3U8 *m3u8;
621   gboolean updated = FALSE;
622   gboolean ret = FALSE;
623
624   g_return_val_if_fail (self != NULL, FALSE);
625
626   GST_M3U8_CLIENT_LOCK (self);
627   m3u8 = self->current ? self->current : self->main;
628
629   if (!gst_m3u8_update (m3u8, data, &updated))
630     goto out;
631
632   if (!updated) {
633     self->update_failed_count++;
634     goto out;
635   }
636
637   /* select the first playlist, for now */
638   if (!self->current) {
639     if (self->main->lists) {
640       self->current = self->main->current_variant->data;
641     } else {
642       self->current = self->main;
643     }
644   }
645
646   if (m3u8->files && self->sequence == -1) {
647     self->sequence =
648         GST_M3U8_MEDIA_FILE (g_list_first (m3u8->files)->data)->sequence;
649     GST_DEBUG ("Setting first sequence at %d", self->sequence);
650   }
651
652   ret = TRUE;
653 out:
654   GST_M3U8_CLIENT_UNLOCK (self);
655   return ret;
656 }
657
658 static gboolean
659 _find_next (GstM3U8MediaFile * file, GstM3U8Client * client)
660 {
661   GST_DEBUG ("Found fragment %d", file->sequence);
662   if (file->sequence >= client->sequence)
663     return FALSE;
664   return TRUE;
665 }
666
667 static gboolean
668 _find_key (GstM3U8Key * key, GstM3U8Client * client)
669 {
670   if (key->sequence <= client->sequence)
671     return FALSE;
672   return TRUE;
673 }
674
675 void
676 gst_m3u8_client_get_current_position (GstM3U8Client * client,
677     GstClockTime * timestamp)
678 {
679   GList *l;
680   GList *walk;
681
682   l = g_list_find_custom (client->current->files, client,
683       (GCompareFunc) _find_next);
684
685   *timestamp = 0;
686   for (walk = client->current->files; walk; walk = walk->next) {
687     if (walk == l)
688       break;
689     *timestamp += GST_M3U8_MEDIA_FILE (walk->data)->duration;
690   }
691 }
692
693 gboolean
694 gst_m3u8_client_get_next_fragment (GstM3U8Client * client,
695     gboolean * discontinuity, const gchar ** uri, GstClockTime * duration,
696     GstClockTime * timestamp, GstM3U8Key ** key)
697 {
698   GList *l;
699   GstM3U8MediaFile *file;
700
701   g_return_val_if_fail (client != NULL, FALSE);
702   g_return_val_if_fail (client->current != NULL, FALSE);
703   g_return_val_if_fail (discontinuity != NULL, FALSE);
704
705   GST_M3U8_CLIENT_LOCK (client);
706   GST_DEBUG ("Looking for fragment %d", client->sequence);
707   l = g_list_find_custom (client->current->files, client,
708       (GCompareFunc) _find_next);
709   if (l == NULL) {
710     GST_M3U8_CLIENT_UNLOCK (client);
711     return FALSE;
712   }
713
714   gst_m3u8_client_get_current_position (client, timestamp);
715
716   file = GST_M3U8_MEDIA_FILE (l->data);
717
718   *key = NULL;
719   if (client->current->keys) {
720     l = g_list_find_custom (client->current->keys, client,
721         (GCompareFunc) _find_key);
722     if (l && (GST_M3U8_KEY (l->data)->method != GST_M3U8_ENCRYPTED_NONE))
723       *key = GST_M3U8_KEY (l->data);
724   }
725
726   *discontinuity = client->sequence != file->sequence;
727   client->sequence = file->sequence + 1;
728
729   *uri = file->uri;
730   *duration = file->duration;
731
732   GST_M3U8_CLIENT_UNLOCK (client);
733   return TRUE;
734 }
735
736 static void
737 _sum_duration (GstM3U8MediaFile * self, GstClockTime * duration)
738 {
739   *duration += self->duration;
740 }
741
742 GstClockTime
743 gst_m3u8_client_get_duration (GstM3U8Client * client)
744 {
745   GstClockTime duration = 0;
746
747   g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
748
749   GST_M3U8_CLIENT_LOCK (client);
750   /* We can only get the duration for on-demand streams */
751   if (!client->current->endlist) {
752     GST_M3U8_CLIENT_UNLOCK (client);
753     return GST_CLOCK_TIME_NONE;
754   }
755
756   g_list_foreach (client->current->files, (GFunc) _sum_duration, &duration);
757   GST_M3U8_CLIENT_UNLOCK (client);
758   return duration;
759 }
760
761 GstClockTime
762 gst_m3u8_client_get_target_duration (GstM3U8Client * client)
763 {
764   GstClockTime duration = 0;
765
766   g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
767
768   GST_M3U8_CLIENT_LOCK (client);
769   duration = client->current->targetduration;
770   GST_M3U8_CLIENT_UNLOCK (client);
771   return duration;
772 }
773
774 const gchar *
775 gst_m3u8_client_get_uri (GstM3U8Client * client)
776 {
777   const gchar *uri;
778
779   g_return_val_if_fail (client != NULL, NULL);
780
781   GST_M3U8_CLIENT_LOCK (client);
782   uri = client->main->uri;
783   GST_M3U8_CLIENT_UNLOCK (client);
784   return uri;
785 }
786
787 const gchar *
788 gst_m3u8_client_get_current_uri (GstM3U8Client * client)
789 {
790   const gchar *uri;
791
792   g_return_val_if_fail (client != NULL, NULL);
793
794   GST_M3U8_CLIENT_LOCK (client);
795   uri = client->current->uri;
796   GST_M3U8_CLIENT_UNLOCK (client);
797   return uri;
798 }
799
800 gboolean
801 gst_m3u8_client_has_variant_playlist (GstM3U8Client * client)
802 {
803   gboolean ret;
804
805   g_return_val_if_fail (client != NULL, FALSE);
806
807   GST_M3U8_CLIENT_LOCK (client);
808   ret = (client->main->lists != NULL);
809   GST_M3U8_CLIENT_UNLOCK (client);
810   return ret;
811 }
812
813 gboolean
814 gst_m3u8_client_is_live (GstM3U8Client * client)
815 {
816   gboolean ret;
817
818   g_return_val_if_fail (client != NULL, FALSE);
819
820   GST_M3U8_CLIENT_LOCK (client);
821   if (!client->current || client->current->endlist)
822     ret = FALSE;
823   else
824     ret = TRUE;
825   GST_M3U8_CLIENT_UNLOCK (client);
826   return ret;
827 }
828
829 GList *
830 gst_m3u8_client_get_playlist_for_bitrate (GstM3U8Client * client, guint bitrate)
831 {
832   GList *list, *current_variant;
833
834   GST_M3U8_CLIENT_LOCK (client);
835   current_variant = client->main->current_variant;
836
837   /*  Go to the highest possible bandwidth allowed */
838   while (GST_M3U8 (current_variant->data)->bandwidth < bitrate) {
839     list = g_list_next (current_variant);
840     if (!list)
841       break;
842     current_variant = list;
843   }
844
845   while (GST_M3U8 (current_variant->data)->bandwidth > bitrate) {
846     list = g_list_previous (current_variant);
847     if (!list)
848       break;
849     current_variant = list;
850   }
851   GST_M3U8_CLIENT_UNLOCK (client);
852
853   return current_variant;
854 }
855
856 gboolean
857 gst_m3u8_client_decrypt_init (GstM3U8Client * client, GstM3U8Key * key)
858 {
859   if (!client->current->cipher_ctx)
860     client->current->cipher_ctx = g_malloc0 (sizeof(EVP_CIPHER_CTX));
861
862   EVP_CIPHER_CTX_init (client->current->cipher_ctx);
863   if (key->method == GST_M3U8_ENCRYPTED_AES_128) {
864     return EVP_DecryptInit_ex (client->current->cipher_ctx, EVP_aes_128_cbc(),
865         NULL, key->data, key->iv);
866   } else {
867     return FALSE;
868   }
869 }
870
871 gboolean
872 gst_m3u8_client_decrypt_update (GstM3U8Client * client, guint8 * out_data,
873     gint * out_size, guint8 * in_data, gint in_size)
874 {
875   g_return_val_if_fail (client->current->cipher_ctx != NULL, FALSE);
876
877   return EVP_DecryptUpdate (client->current->cipher_ctx, out_data, out_size,
878       in_data, in_size);
879 }