2 * Copyright (C) 2010 Marc-Andre Lureau <marcandre.lureau@gmail.com>
3 * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd. All rights reserved.
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.
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,
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);
40 #define GST_CAT_DEFAULT hlsdemux2_m3u8_debug
42 #define BITRATE_SWITCH_UPPER_THRESHOLD 0.3
43 #define BITRATE_SWITCH_LOWER_THRESHOLD 0
44 #define SWITCH_TRIGGER_POINT 1
51 m3u8 = g_new0 (GstM3U8, 1);
52 m3u8->last_data = NULL;
57 gst_m3u8_set_uri (GstM3U8 * self, gchar * uri)
59 g_return_if_fail (self != NULL);
67 gst_m3u8_free (GstM3U8 * self)
69 g_return_if_fail (self != NULL);
72 g_free (self->allowcache);
73 g_free (self->codecs);
74 //g_free (self->cipher_ctx);
76 g_list_foreach (self->files, (GFunc) gst_m3u8_media_file_free, NULL);
77 g_list_free (self->files);
79 g_free (self->last_data);
80 g_list_foreach (self->lists, (GFunc) gst_m3u8_free, NULL);
81 g_list_free (self->lists);
86 static GstM3U8MediaFile *
87 gst_m3u8_media_file_new (gchar * uri, gchar * title, GstClockTime duration,
88 gchar *key_url, gchar *IV, guint sequence)
90 GstM3U8MediaFile *file;
92 file = g_new0 (GstM3U8MediaFile, 1);
95 file->duration = duration;
96 file->sequence = sequence;
98 GST_DEBUG ("Duration of the file = %"GST_TIME_FORMAT"\n", GST_TIME_ARGS(duration));
101 file->key_url = g_strdup (key_url);
103 file->key_url = NULL;
111 gst_m3u8_media_file_free (GstM3U8MediaFile * self)
113 g_return_if_fail (self != NULL);
116 g_free (self->key_url);
117 self->key_url = NULL;
124 g_free (self->title);
135 gst_m3u8_getIV_from_mediasequence (GstM3U8 *self)
141 GST_ERROR ("Failed to allocate memory...");
145 if (self->mediasequence > INT_MAX) {
146 GST_ERROR ("media sequnece is greater than INT_MAX...yet to handle");
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);
157 int_from_string (gchar * ptr, gchar ** endptr, gint * val, gint base)
161 g_return_val_if_fail (ptr != NULL, FALSE);
162 g_return_val_if_fail (val != NULL, FALSE);
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));
179 double_from_string (gchar * ptr, gchar ** endptr, gdouble * val)
184 g_return_val_if_fail (ptr != NULL, FALSE);
185 g_return_val_if_fail (val != NULL, FALSE);
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));
195 if (!isfinite (ret)) {
196 GST_WARNING ("%s", g_strerror (ERANGE));
203 *val = (gdouble) ret;
209 iv_from_string (gchar * ptr, gchar ** endptr, guint8 * val)
214 g_return_val_if_fail (ptr != NULL, FALSE);
215 g_return_val_if_fail (val != NULL, FALSE);
217 if (*ptr == '0' && (*(ptr + 1) == 'x' || *(ptr + 1) == 'X')) {
218 ptr = ptr + 2; /* skip 0x or 0X */
221 for (idx = 0; idx < GST_M3U8_IV_LEN; idx++) {
225 *(val + idx) = hex * 16;
239 iv_from_uint (guint uint, guint8 * val)
243 g_return_val_if_fail (val != NULL, FALSE);
245 val = val + GST_M3U8_IV_LEN - 1;
246 for (idx = 0; idx < sizeof(guint); idx++) {
247 *val-- = (guint8)uint;
254 make_valid_uri (gchar *prefix, gchar *suffix, gchar **uri)
259 GST_WARNING ("uri prefix not set, can't build a valid uri");
262 slash = g_utf8_strrchr (prefix, -1, '/');
264 GST_WARNING ("Can't build a valid uri");
269 *uri = g_strdup_printf ("%s/%s", prefix, suffix);
276 parse_attributes (gchar ** ptr, gchar ** a, gchar ** v)
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);
285 /* [attribute=value,]* */
288 end = p = g_utf8_strchr (*ptr, -1, ',');
291 end = g_utf8_next_char (end);
292 } while (end && *end == ' ');
296 *v = p = g_utf8_strchr (*ptr, -1, '=');
298 *v = g_utf8_next_char (*v);
301 GST_WARNING ("missing = after attribute");
310 _m3u8_compare_uri (GstM3U8 * a, gchar * uri)
312 g_return_val_if_fail (a != NULL, 0);
313 g_return_val_if_fail (uri != NULL, 0);
315 return g_strcmp0 (a->uri, uri);
319 gst_m3u8_compare_playlist_by_bitrate (gconstpointer a, gconstpointer b)
321 return ((GstM3U8 *) (a))->bandwidth - ((GstM3U8 *) (b))->bandwidth;
325 * @data: a m3u8 playlist text data, taking ownership
328 gst_m3u8_update (GstM3U8 * self, gchar *data, gboolean * updated)
331 GstClockTime duration = 0;
333 // gboolean discontinuity;
335 gchar *key_url = NULL;
337 gchar *actual_data = NULL;
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);
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");
353 if (!g_str_has_prefix (data, "#EXTM3U")) {
354 GST_WARNING ("Data doesn't start with #EXTM3U");
361 g_free (self->last_data);
363 self->last_data = g_strdup(data);
368 g_list_foreach (self->files, (GFunc) gst_m3u8_media_file_free, NULL);
369 g_list_free (self->files);
380 end = g_utf8_strchr (data, -1, '\n');
384 if (data[0] != '#') {
388 if (duration <= 0 && list == NULL) {
389 GST_LOG ("%s: got line without EXTINF or EXTSTREAMINF, dropping", data);
393 if (!gst_uri_is_valid (data)) {
394 if (!make_valid_uri(self->uri, data, &uri))
397 uri = g_strdup (data);
400 r = g_utf8_strchr (uri, -1, '\r');
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);
410 gst_m3u8_set_uri (list, uri);
411 self->lists = g_list_append (self->lists, list);
415 GstM3U8MediaFile *file;
416 gchar *send_IV = NULL;
419 GST_DEBUG ("AES-128 key url = %s", key_url);
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);
426 send_IV = g_malloc0(16);
434 gst_m3u8_media_file_new (uri, title, duration, key_url, send_IV,
435 self->mediasequence++);
440 self->files = g_list_append (self->files, file);
441 GST_DEBUG ("------------>>>>>>appending uri = %s and key = %s\n", file->uri, file->key_url);
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))
449 } else if (g_str_has_prefix (data, "#EXT-X-STREAM-INF:")) {
453 GST_WARNING ("Found a list without a uri..., dropping");
454 gst_m3u8_free (list);
457 list = gst_m3u8_new ();
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");
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");
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:")) {
497 if (!double_from_string (data + 8, &data, &fval)) {
498 GST_WARNING ("Can't read EXTINF duration");
501 duration = fval * (gdouble) GST_SECOND;
502 if (duration > self->targetduration)
503 GST_WARNING ("EXTINF duration > TARGETDURATION");
504 if (!data || *data != ',')
506 data = g_utf8_next_char (data);
509 title = g_strdup (data);
511 } else if (g_str_has_prefix (data, "#EXT-X-KEY:")) {
513 GST_DEBUG ("Found EXT-X-KEY tag...");
515 /* handling encrypted content */
516 data = data + 11; /* skipping "#EXT-X-KEY:" tag */
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");
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
528 } else if (g_str_equal (attr, "URI")) {
529 gchar *end_dq = NULL;
532 GST_ERROR ("val is NULL");
536 val = val + 1; /* eliminating first double quote in url */
538 GST_ERROR ("val is NULL");
542 end_dq = g_utf8_strrchr (val, -1, '"');
544 GST_ERROR ("end_dq is NULL");
549 GST_DEBUG ("Key URI = %s\n", val);
551 if (!gst_uri_is_valid (val)) {
554 GST_WARNING ("uri not set, can't build a valid uri");
557 slash = g_utf8_strrchr (self->uri, -1, '/');
559 GST_WARNING ("Can't build a valid uri");
563 key_url = g_strdup_printf ("%s/%s", self->uri, val);
566 key_url= g_strdup (val);
568 } else if (g_str_equal (attr, "IV")) {
582 GST_ERROR ("Failed to allocate memory...\n");
586 /* eliminating 0x/0X prefix */
588 car = g_utf8_strchr (val, -1, '\r');
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);
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);
606 if (!int_from_string (tmp_byte, NULL, &tmp_val, 16))
607 GST_WARNING ("Error while reading PROGRAM-ID");
615 GST_LOG ("Ignored line: %s", data);
621 data = g_utf8_next_char (end); /* skip \n */
624 /* redorder playlists by bitrate */
626 gchar *top_variant_uri = NULL;
628 if (!self->current_variant)
629 top_variant_uri = GST_M3U8 (self->lists->data)->uri;
631 top_variant_uri = GST_M3U8 (self->current_variant->data)->uri;
634 g_list_sort (self->lists,
635 (GCompareFunc) gst_m3u8_compare_playlist_by_bitrate);
637 self->current_variant = g_list_find_custom (self->lists, top_variant_uri,
638 (GCompareFunc) _m3u8_compare_uri);
641 g_free (actual_data);
650 g_free (actual_data);
661 gst_m3u8_client_new (const gchar * uri)
663 GstM3U8Client *client;
665 g_return_val_if_fail (uri != NULL, NULL);
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));
679 gst_m3u8_client_free (GstM3U8Client * self)
681 g_return_if_fail (self != NULL);
683 gst_m3u8_free (self->main);
684 g_mutex_free (self->lock);
689 gst_m3u8_client_set_current (GstM3U8Client * self, GstM3U8 * m3u8)
691 g_return_if_fail (self != NULL);
693 GST_M3U8_CLIENT_LOCK (self);
694 if (m3u8 != self->current) {
695 self->current = m3u8;
696 self->update_failed_count = 0;
698 GST_M3U8_CLIENT_UNLOCK (self);
702 _get_start_sequence (GstM3U8Client * client)
705 GstClockTime duration_limit, duration_count = 0;
708 if (client->current->endlist) {
709 l = g_list_first (client->current->files);
710 sequence = GST_M3U8_MEDIA_FILE (l->data)->sequence;
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) {
725 gst_m3u8_client_update (GstM3U8Client * self, gchar * data)
728 gboolean updated = FALSE;
729 gboolean ret = FALSE;
731 g_return_val_if_fail (self != NULL, FALSE);
733 GST_M3U8_CLIENT_LOCK (self);
734 m3u8 = self->current ? self->current : self->main;
736 if (!gst_m3u8_update (m3u8, data, &updated))
740 self->update_failed_count++;
744 /* select the first playlist, for now */
745 if (!self->current) {
746 if (self->main->lists) {
747 self->current = self->main->current_variant->data;
749 self->current = self->main;
753 if (m3u8->files && self->sequence == -1) {
755 self->sequence = GST_M3U8_MEDIA_FILE (g_list_first (m3u8->files)->data)->sequence;
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);
761 GST_DEBUG ("Setting first sequence at %d", self->sequence);
766 GST_M3U8_CLIENT_UNLOCK (self);
771 _find_next (GstM3U8MediaFile * file, GstM3U8Client * client)
773 GST_DEBUG ("Found fragment %d", file->sequence);
774 if (file->sequence >= client->sequence)
780 _find_key (GstM3U8Key * key, GstM3U8Client * client)
782 if (key->sequence <= client->sequence)
788 gst_m3u8_client_get_current_position (GstM3U8Client * client,
789 GstClockTime * timestamp)
794 l = g_list_find_custom (client->current->files, client,
795 (GCompareFunc) _find_next);
798 for (walk = client->current->files; walk; walk = walk->next) {
801 *timestamp += GST_M3U8_MEDIA_FILE (walk->data)->duration;
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)
811 GstM3U8MediaFile *file;
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);
817 GST_M3U8_CLIENT_LOCK (client);
819 GST_DEBUG ("Looking for fragment %d", client->sequence);
821 if (client->main && client->main->lists && client->current->endlist) {
823 GstClockTime current_pos, target_pos;
824 gint current_sequence;
825 gboolean found_sequence = FALSE;
827 /* VOD playlist, so find mediasequence from time */
828 // TODO: need to handle when live converts to VOD by putting END_LIST tag
830 file = GST_M3U8_MEDIA_FILE (client->current->files->data);
831 current_sequence = file->sequence;
833 target_pos = cur_running_time;
835 for (walk = client->current->files; walk; walk = walk->next) {
838 current_sequence = file->sequence;
839 if (current_pos <= target_pos &&
840 target_pos < current_pos + file->duration) {
841 found_sequence = TRUE;
844 current_pos += file->duration;
847 if (found_sequence) {
848 GST_ERROR ("changing client sequence from %d -> %d", client->sequence, current_sequence);
849 client->sequence = current_sequence;
851 GST_WARNING ("max file_seq = %d and client_seq = %d", current_sequence, client->sequence);
852 GST_M3U8_CLIENT_UNLOCK (client);
857 l = g_list_find_custom (client->current->files, client, (GCompareFunc) _find_next);
859 GST_M3U8_CLIENT_UNLOCK (client);
863 gst_m3u8_client_get_current_position (client, timestamp);
865 file = GST_M3U8_MEDIA_FILE (l->data);
868 *key_uri = g_strdup (file->key_url);
871 *iv = g_malloc0 (GST_M3U8_IV_LEN);
873 memcpy (*iv, file->iv, GST_M3U8_IV_LEN);
876 *discontinuity = client->sequence != file->sequence;
877 client->sequence = file->sequence + 1;
880 *uri = g_strdup (file->uri);
882 *duration = file->duration;
884 GST_M3U8_CLIENT_UNLOCK (client);
888 void gst_m3u8_client_decrement_sequence (GstM3U8Client * client)
890 GST_M3U8_CLIENT_LOCK (client);
891 client->sequence = client->sequence - 1;
892 GST_M3U8_CLIENT_UNLOCK (client);
896 _sum_duration (GstM3U8MediaFile * self, GstClockTime * duration)
898 *duration += self->duration;
902 gst_m3u8_client_get_duration (GstM3U8Client * client)
904 GstClockTime duration = 0;
906 g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
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;
915 g_list_foreach (client->current->files, (GFunc) _sum_duration, &duration);
916 GST_M3U8_CLIENT_UNLOCK (client);
921 gst_m3u8_client_get_target_duration (GstM3U8Client * client)
923 GstClockTime duration = 0;
925 g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
927 GST_M3U8_CLIENT_LOCK (client);
928 duration = client->current->targetduration;
929 GST_M3U8_CLIENT_UNLOCK (client);
933 /* used for LIVE case only, to update playlist file */
935 gst_m3u8_client_get_last_fragment_duration (GstM3U8Client * client)
937 GstClockTime duration = 0;
938 GstM3U8MediaFile *last_file = NULL;
939 GList *last_list = NULL;
941 g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
943 GST_M3U8_CLIENT_LOCK (client);
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;
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 */
957 last_list = g_list_last (client->current->files);
960 GST_WARNING ("list does not have last object... return target_duration");
961 GST_M3U8_CLIENT_UNLOCK (client);
962 return client->current->targetduration;
965 last_file = (GstM3U8MediaFile *)(last_list->data);
967 duration = last_file->duration;
969 duration=client->current->targetduration;
971 GST_DEBUG ("Last file duration = %"GST_TIME_FORMAT"\n", GST_TIME_ARGS(duration));
972 GST_M3U8_CLIENT_UNLOCK (client);
977 gst_m3u8_client_get_uri (GstM3U8Client * client)
981 g_return_val_if_fail (client != NULL, NULL);
983 GST_M3U8_CLIENT_LOCK (client);
984 uri = client->main->uri;
985 GST_M3U8_CLIENT_UNLOCK (client);
990 gst_m3u8_client_get_current_uri (GstM3U8Client * client)
994 g_return_val_if_fail (client != NULL, NULL);
996 GST_M3U8_CLIENT_LOCK (client);
997 uri = client->current->uri;
998 GST_M3U8_CLIENT_UNLOCK (client);
1003 gst_m3u8_client_get_current_bandwidth (GstM3U8Client * client)
1007 g_return_val_if_fail (client != NULL, -1);
1009 GST_M3U8_CLIENT_LOCK (client);
1010 bandwidth = client->current->bandwidth;
1011 GST_M3U8_CLIENT_UNLOCK (client);
1016 gst_m3u8_client_has_variant_playlist (GstM3U8Client * client)
1020 g_return_val_if_fail (client != NULL, FALSE);
1022 GST_M3U8_CLIENT_LOCK (client);
1023 ret = (client->main->lists != NULL);
1024 GST_M3U8_CLIENT_UNLOCK (client);
1029 gst_m3u8_client_is_live (GstM3U8Client * client)
1033 g_return_val_if_fail (client != NULL, FALSE);
1035 GST_M3U8_CLIENT_LOCK (client);
1036 if (!client->current || client->current->endlist)
1040 GST_M3U8_CLIENT_UNLOCK (client);
1045 gst_m3u8_client_get_next_higher_bw_playlist (GstM3U8Client * client)
1047 GList *current_variant;
1049 g_return_val_if_fail (client != NULL, FALSE);
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);
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;
1066 gst_m3u8_client_get_next_lower_bw_playlist (GstM3U8Client * client)
1068 GList *current_variant;
1070 g_return_val_if_fail (client != NULL, FALSE);
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);
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;
1086 #ifndef SWITCH_TRIGGER_POINT
1088 gst_m3u8_client_get_playlist_for_bitrate (GstM3U8Client * client, guint bitrate)
1090 GList *list, *current_variant;
1092 GST_M3U8_CLIENT_LOCK (client);
1093 current_variant = client->main->current_variant;
1095 /* Go to the highest possible bandwidth allowed */
1096 while (GST_M3U8 (current_variant->data)->bandwidth < bitrate) {
1097 list = g_list_next (current_variant);
1100 current_variant = list;
1103 while (GST_M3U8 (current_variant->data)->bandwidth > bitrate) {
1104 list = g_list_previous (current_variant);
1107 current_variant = list;
1109 GST_M3U8_CLIENT_UNLOCK (client);
1111 return current_variant;
1115 gst_m3u8_client_get_playlist_for_bitrate (GstM3U8Client * client, guint bitrate)
1117 GList *next_variant = NULL;
1118 GList *current_variant = NULL;
1119 guint current_bandwidth = 0;
1121 GST_M3U8_CLIENT_LOCK (client);
1122 current_variant = client->main->current_variant;
1123 current_bandwidth = GST_M3U8 (current_variant->data)->bandwidth;
1125 if ((current_bandwidth + BITRATE_SWITCH_UPPER_THRESHOLD * current_bandwidth) < bitrate) {
1126 GST_DEBUG ("Switch to upper band...\n");
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;
1133 GST_DEBUG (">>>>> UP : current_bw = %d, bitrate = %d\n", current_bandwidth, bitrate);
1134 next_variant = current_variant;
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;
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);
1150 /* as condition failed fallback to previous */
1151 GST_M3U8_CLIENT_UNLOCK (client);
1153 return current_variant;
1155 } else if ((current_bandwidth + BITRATE_SWITCH_LOWER_THRESHOLD * current_bandwidth) > bitrate) {
1156 GST_DEBUG ("Switch to lower band...\n");
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;
1164 GST_DEBUG (">>>>> LOW : current_bw = %d, bitrate = %d\n", current_bandwidth, bitrate);
1165 next_variant = current_variant;
1167 /* Go to the lowest possible bandwidth allowed */
1168 while ((current_bandwidth + BITRATE_SWITCH_LOWER_THRESHOLD * current_bandwidth) > bitrate) {
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;
1177 current_variant = next_variant;
1179 current_bandwidth = GST_M3U8 (current_variant->data)->bandwidth;
1180 GST_DEBUG ("current_bw in while= %d\n", current_bandwidth);
1183 GST_M3U8_CLIENT_UNLOCK (client);
1185 /* as condition failed fallback to previous */
1186 return current_variant;
1189 GST_DEBUG ("No need to switch .... returning current_variant..\n");
1192 GST_M3U8_CLIENT_UNLOCK (client);
1194 return current_variant;
1199 gst_m3u8_client_decrypt_init (GstM3U8Client * client, unsigned char *key, gchar *iv)
1201 //if (!client->current->cipher_ctx)
1202 // client->current->cipher_ctx = g_malloc0 (sizeof(EVP_CIPHER_CTX));
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
1207 return EVP_DecryptInit_ex (&(client->cipher_ctx), EVP_aes_128_cbc(), NULL, key, iv);
1211 gst_m3u8_client_decrypt_update (GstM3U8Client * client, guint8 * out_data,
1212 gint * out_size, guint8 * in_data, gint in_size)
1214 //g_return_val_if_fail (client->current->cipher_ctx != NULL, FALSE);
1216 return EVP_DecryptUpdate (&(client->cipher_ctx), out_data, out_size,
1221 gst_m3u8_client_decrypt_final (GstM3U8Client * client, guint8 * out_data,
1224 //g_return_val_if_fail (client->current->cipher_ctx != NULL, FALSE);
1226 return EVP_DecryptFinal_ex (&(client->cipher_ctx), out_data, out_size);
1230 gst_m3u8_client_decrypt_deinit (GstM3U8Client * client)
1232 //g_return_val_if_fail (client->current->cipher_ctx != NULL, FALSE);
1234 return EVP_CIPHER_CTX_cleanup (&(client->cipher_ctx));
1239 gst_m3u8_client_is_playlist_download_needed (GstM3U8Client * self)
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))