2 * Copyright (C) 2010 Marc-Andre Lureau <marcandre.lureau@gmail.com>
3 * Copyright (C) 2010 Andoni Morales Alastruey <ylatuya@gmail.com>
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.
23 * SECTION:element-hlsdemux
25 * HTTP Live Streaming source element.
28 * <title>Example launch line</title>
30 * gst-launch hlsdemux location=http://devimages.apple.com/iphone/samples/bipbop/gear1/prog_index.m3u8 ! decodebin2 ! xvimagesink
34 * Last reviewed on 2010-10-07
43 #include <gst/base/gsttypefindhelper.h>
44 #include "gsthlsdemux.h"
46 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
51 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
54 GST_STATIC_CAPS ("playlist/m3u8"));
56 static GstStaticPadTemplate fetchertemplate = GST_STATIC_PAD_TEMPLATE ("sink",
61 GST_DEBUG_CATEGORY_STATIC (gst_hls_demux_debug);
62 #define GST_CAT_DEFAULT gst_hls_demux_debug
69 PROP_BITRATE_SWITCH_TOLERANCE,
73 static const float update_interval_factor[] = { 1, 0.5, 1.5, 3 };
75 #define DEFAULT_FRAGMENTS_CACHE 3
76 #define DEFAULT_FAILED_COUNT 3
77 #define DEFAULT_BITRATE_SWITCH_TOLERANCE 0.4
80 static void gst_hls_demux_set_property (GObject * object, guint prop_id,
81 const GValue * value, GParamSpec * pspec);
82 static void gst_hls_demux_get_property (GObject * object, guint prop_id,
83 GValue * value, GParamSpec * pspec);
84 static void gst_hls_demux_dispose (GObject * obj);
87 static GstStateChangeReturn
88 gst_hls_demux_change_state (GstElement * element, GstStateChange transition);
91 static GstBusSyncReply gst_hls_demux_fetcher_bus_handler (GstBus * bus,
92 GstMessage * message, gpointer data);
93 static GstFlowReturn gst_hls_demux_chain (GstPad * pad, GstBuffer * buf);
94 static gboolean gst_hls_demux_sink_event (GstPad * pad, GstEvent * event);
95 static GstFlowReturn gst_hls_demux_fetcher_chain (GstPad * pad, GstBuffer * buf);
96 static gboolean gst_hls_demux_fetcher_sink_event (GstPad * pad, GstEvent * event);
97 static void gst_hls_demux_loop (GstHLSDemux * demux);
98 static void gst_hls_demux_stop (GstHLSDemux * demux);
99 static void gst_hls_demux_stop_fetcher (GstHLSDemux * demux, gboolean cancelled);
100 static gboolean gst_hls_demux_start_update (GstHLSDemux * demux);
101 static gboolean gst_hls_demux_cache_fragments (GstHLSDemux * demux);
102 static gboolean gst_hls_demux_schedule (GstHLSDemux * demux);
103 static gboolean gst_hls_demux_switch_playlist (GstHLSDemux * demux);
104 static gboolean gst_hls_demux_get_next_fragment (GstHLSDemux * demux, gboolean retry);
105 static gboolean gst_hls_demux_update_playlist (GstHLSDemux * demux, gboolean retry);
106 static void gst_hls_demux_reset (GstHLSDemux * demux, gboolean dispose);
107 static gboolean gst_hls_demux_set_location (GstHLSDemux * demux, const gchar * uri);
110 _do_init (GType type)
112 GST_DEBUG_CATEGORY_INIT (gst_hls_demux_debug, "hlsdemux", 0, "hlsdemux element");
115 GST_BOILERPLATE_FULL (GstHLSDemux, gst_hls_demux, GstElement,
116 GST_TYPE_ELEMENT, _do_init);
119 gst_hls_demux_base_init (gpointer g_class)
121 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
123 gst_element_class_add_pad_template (element_class,
124 gst_static_pad_template_get (&srctemplate));
126 gst_element_class_add_pad_template (element_class,
127 gst_static_pad_template_get (&sinktemplate));
129 gst_element_class_set_details_simple (element_class,
132 "HTTP Live Streaming source",
133 "Marc-Andre Lureau <marcandre.lureau@gmail.com>\n"
134 "Andoni Morales Alastruey <ylatuya@gmail.com>");
138 gst_hls_demux_dispose (GObject * obj)
140 GstHLSDemux *demux = GST_HLS_DEMUX (obj);
142 g_cond_free (demux->fetcher_cond);
143 g_mutex_free (demux->fetcher_lock);
145 g_cond_free (demux->thread_cond);
146 g_mutex_free (demux->thread_lock);
148 if (GST_TASK_STATE (demux->task) != GST_TASK_STOPPED) {
149 gst_task_stop (demux->task);
150 gst_task_join (demux->task);
152 gst_object_unref (demux->task);
153 g_static_rec_mutex_free (&demux->task_lock);
155 gst_object_unref (demux->fetcher_bus);
156 gst_object_unref (demux->fetcherpad);
158 gst_hls_demux_reset (demux, TRUE);
160 G_OBJECT_CLASS (parent_class)->dispose (obj);
164 gst_hls_demux_class_init (GstHLSDemuxClass * klass)
166 GObjectClass *gobject_class;
167 GstElementClass *gstelement_class;
169 gobject_class = G_OBJECT_CLASS (klass);
170 gstelement_class = (GstElementClass *) klass;
172 parent_class = g_type_class_peek_parent (klass);
174 gobject_class->set_property = gst_hls_demux_set_property;
175 gobject_class->get_property = gst_hls_demux_get_property;
176 gobject_class->dispose = gst_hls_demux_dispose;
178 g_object_class_install_property (gobject_class, PROP_FRAGMENTS_CACHE,
179 g_param_spec_uint ("fragments-cache", "Fragments cache",
180 "Number of fragments needed to be cached to start playing",
181 2, G_MAXUINT, DEFAULT_FRAGMENTS_CACHE, G_PARAM_READWRITE));
183 g_object_class_install_property (gobject_class, PROP_BITRATE_SWITCH_TOLERANCE,
184 g_param_spec_float ("bitrate-switch-tolerance",
185 "Bitrate switch tolerance",
186 "Tolerance with respect of the fragment duration to switch to "
187 "a different bitrate if the client is too slow/fast.",
188 0, 1, DEFAULT_BITRATE_SWITCH_TOLERANCE, G_PARAM_READWRITE));
190 gstelement_class->change_state = gst_hls_demux_change_state;
194 gst_hls_demux_init (GstHLSDemux * demux, GstHLSDemuxClass * klass)
197 demux->sinkpad = gst_pad_new_from_static_template (&sinktemplate, "sink");
198 gst_pad_set_chain_function (demux->sinkpad,
199 GST_DEBUG_FUNCPTR (gst_hls_demux_chain));
200 gst_pad_set_event_function (demux->sinkpad,
201 GST_DEBUG_FUNCPTR (gst_hls_demux_sink_event));
202 gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad);
205 demux->srcpad = gst_pad_new_from_static_template (&srctemplate, "src");
206 gst_element_add_pad (GST_ELEMENT (demux), demux->srcpad);
209 demux->fetcherpad = gst_pad_new_from_static_template (&fetchertemplate, "sink");
210 gst_pad_set_chain_function (demux->fetcherpad,
211 GST_DEBUG_FUNCPTR (gst_hls_demux_fetcher_chain));
212 gst_pad_set_event_function (demux->fetcherpad,
213 GST_DEBUG_FUNCPTR (gst_hls_demux_fetcher_sink_event));
214 gst_pad_set_element_private (demux->fetcherpad, demux);
215 gst_pad_activate_push (demux->fetcherpad, TRUE);
218 demux->fragments_cache = DEFAULT_FRAGMENTS_CACHE;
219 demux->bitrate_switch_tol = DEFAULT_BITRATE_SWITCH_TOLERANCE;
221 demux->fetcher_bus = gst_bus_new ();
222 gst_bus_set_sync_handler (demux->fetcher_bus, gst_hls_demux_fetcher_bus_handler,
224 demux->thread_cond = g_cond_new ();
225 demux->thread_lock = g_mutex_new ();
226 demux->fetcher_cond = g_cond_new ();
227 demux->fetcher_lock = g_mutex_new ();
228 demux->queue = g_queue_new ();
229 g_static_rec_mutex_init (&demux->task_lock);
230 demux->task = gst_task_create ((GstTaskFunction) gst_hls_demux_loop, demux);
231 gst_task_set_lock (demux->task, &demux->task_lock);
235 gst_hls_demux_set_property (GObject * object, guint prop_id, const GValue * value,
238 GstHLSDemux *demux = GST_HLS_DEMUX (object);
241 case PROP_FRAGMENTS_CACHE:
242 demux->fragments_cache = g_value_get_uint (value);
244 case PROP_BITRATE_SWITCH_TOLERANCE:
245 demux->bitrate_switch_tol = g_value_get_float (value);
248 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
254 gst_hls_demux_get_property (GObject * object, guint prop_id, GValue * value,
257 GstHLSDemux *demux = GST_HLS_DEMUX (object);
260 case PROP_FRAGMENTS_CACHE:
261 g_value_set_uint (value, demux->fragments_cache);
263 case PROP_BITRATE_SWITCH_TOLERANCE:
264 g_value_set_float (value, demux->bitrate_switch_tol);
267 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
272 static GstStateChangeReturn
273 gst_hls_demux_change_state (GstElement * element, GstStateChange transition)
275 GstStateChangeReturn ret;
276 GstHLSDemux *demux = GST_HLS_DEMUX (element);
278 switch (transition) {
279 case GST_STATE_CHANGE_NULL_TO_READY:
280 gst_hls_demux_reset (demux, FALSE);
286 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
288 switch (transition) {
289 case GST_STATE_CHANGE_PAUSED_TO_READY:
290 demux->cancelled = TRUE;
291 gst_hls_demux_stop_fetcher (demux, TRUE);
300 gst_hls_demux_sink_event (GstPad * pad, GstEvent * event)
302 GstHLSDemux *demux = GST_HLS_DEMUX (gst_pad_get_parent (pad));
308 switch (event->type) {
312 if (demux->playlist == NULL) {
313 GST_WARNING_OBJECT (demux, "Received EOS without a playlist.");
317 GST_DEBUG_OBJECT (demux, "Got EOS on the sink pad: main playlist fetched");
319 query = gst_query_new_uri ();
320 ret = gst_pad_peer_query (demux->sinkpad, query);
322 gst_query_parse_uri (query, &uri);
323 gst_hls_demux_set_location (demux, uri);
327 playlist = g_strndup ((gchar *) GST_BUFFER_DATA (demux->playlist),
328 GST_BUFFER_SIZE (demux->playlist));
329 gst_buffer_unref (demux->playlist);
330 if (!gst_m3u8_client_update (demux->client, playlist)) {
331 /* In most cases, this will happen if we set a wrong url in the
332 * source element and we have received the 404 HTML response instead of
334 GST_ELEMENT_ERROR (demux, STREAM, DECODE, ("Invalid playlist."), NULL);
338 if (!ret && gst_m3u8_client_is_live (demux->client)) {
339 GST_ELEMENT_ERROR (demux, RESOURCE, NOT_FOUND,
340 ("Failed querying the playlist uri, "
341 "required for live sources."), NULL);
345 gst_task_start (demux->task);
346 gst_event_unref (event);
353 return gst_pad_event_default (pad, event);
357 gst_hls_demux_fetcher_sink_event (GstPad * pad, GstEvent * event)
359 GstHLSDemux *demux = GST_HLS_DEMUX (gst_pad_get_element_private (pad));
361 switch (event->type) {
363 GST_DEBUG_OBJECT (demux, "Got EOS on the fetcher pad");
364 /* signal we have fetched the URI */
365 if (!demux->cancelled)
366 g_cond_signal (demux->fetcher_cond);
372 gst_event_unref (event);
377 gst_hls_demux_chain (GstPad * pad, GstBuffer * buf)
379 GstHLSDemux *demux = GST_HLS_DEMUX (gst_pad_get_parent (pad));
381 if (demux->playlist == NULL)
382 demux->playlist = buf;
384 demux->playlist = gst_buffer_join (demux->playlist, buf);
386 gst_object_unref (demux);
392 gst_hls_demux_fetcher_chain (GstPad * pad, GstBuffer * buf)
394 GstHLSDemux *demux = GST_HLS_DEMUX (gst_pad_get_element_private (pad));
396 /* The source element can be an http source element. In case we get a 404,
397 * the html response will be sent downstream and demux->downloaded_uri
398 * will not be null, which might make us think that the request proceed
399 * successfully. But it will also post an error message in the bus that
400 * is handled synchronously and that will set demux->fetcher_error to TRUE,
401 * which is used to discard this buffer with the html response. */
402 if (demux->fetcher_error) {
406 GST_LOG_OBJECT (demux, "The uri fetcher received a new buffer of size %lld",
407 GST_BUFFER_SIZE (buf));
408 if (demux->downloaded_uri == NULL)
409 demux->downloaded_uri = buf;
411 demux->downloaded_uri = gst_buffer_join (demux->downloaded_uri, buf);
420 gst_hls_demux_stop_fetcher (GstHLSDemux * demux, gboolean cancelled)
424 /* When the fetcher is stopped while it's downloading, we will get an EOS that
425 * unblocks the fetcher thread and tries to stop it again from that thread.
426 * Here we check if the fetcher as already been stopped before continuing */
427 if (demux->fetcher == NULL || demux->stopping_fetcher)
430 GST_DEBUG_OBJECT (demux, "Stopping fetcher.");
431 demux->stopping_fetcher = TRUE;
432 /* set the element state to NULL */
433 gst_element_set_state (demux->fetcher, GST_STATE_NULL);
434 gst_element_get_state (demux->fetcher, NULL, NULL, GST_CLOCK_TIME_NONE);
435 /* unlink it from the internal pad */
436 pad = gst_pad_get_peer (demux->fetcherpad);
438 gst_pad_unlink (pad, demux->fetcherpad);
439 gst_object_unref (pad);
441 /* and finally unref it */
442 gst_object_unref (demux->fetcher);
443 demux->fetcher = NULL;
445 /* if we stopped it to cancell a download, free the cached buffer */
446 if (cancelled && demux->downloaded_uri != NULL) {
447 gst_buffer_unref (demux->downloaded_uri);
448 demux->downloaded_uri = NULL;
449 /* signal the fetcher thread that the download has finished/cancelled */
450 g_cond_signal (demux->fetcher_cond);
455 gst_hls_demux_stop (GstHLSDemux * demux)
457 gst_hls_demux_stop_fetcher (demux, TRUE);
458 if (GST_TASK_STATE (demux->task) != GST_TASK_STOPPED)
459 gst_task_stop (demux->task);
460 g_cond_signal (demux->thread_cond);
464 gst_hls_demux_loop (GstHLSDemux * demux)
469 /* Loop for the source pad task. The task is started when we have
470 * received the main playlist from the source element. It tries first to
471 * cache the first fragments and then it waits until it has more data in the
472 * queue. This task is woken up when we push a new fragment to the queue or
473 * when we reached the end of the playlist */
475 if (G_UNLIKELY (demux->need_cache)) {
476 if (!gst_hls_demux_cache_fragments (demux))
479 /* we can start now the updates thread */
480 gst_hls_demux_start_update (demux);
481 GST_INFO_OBJECT (demux, "First fragments cached successfully");
484 if (g_queue_is_empty (demux->queue)) {
485 if (demux->end_of_playlist) {
486 goto end_of_playlist;
488 GST_TASK_WAIT (demux->task);
491 buf = g_queue_pop_head (demux->queue);
492 ret = gst_pad_push (demux->srcpad, buf);
493 if (ret != GST_FLOW_OK)
500 GST_DEBUG_OBJECT (demux, "Reached end of playlist, sending EOS");
501 gst_pad_push_event (demux->srcpad, gst_event_new_eos ());
502 gst_hls_demux_stop (demux);
508 GST_ELEMENT_ERROR (demux, RESOURCE, NOT_FOUND,
509 ("Could not cache the first fragments"), NULL);
510 gst_hls_demux_stop (demux);
516 /* FIXME: handle error */
517 gst_hls_demux_stop (demux);
522 static GstBusSyncReply
523 gst_hls_demux_fetcher_bus_handler (GstBus * bus,
524 GstMessage * message, gpointer data)
526 GstHLSDemux *demux = GST_HLS_DEMUX (data);
528 if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR) {
529 demux->fetcher_error = TRUE;
530 g_cond_signal (demux->fetcher_cond);
533 gst_message_unref (message);
538 gst_hls_demux_make_fetcher (GstHLSDemux * demux, const gchar * uri)
542 if (!gst_uri_is_valid (uri))
545 GST_DEBUG_OBJECT (demux, "Creating fetcher for the URI:%s", uri);
546 demux->fetcher = gst_element_make_from_uri (GST_URI_SRC, uri, NULL);
550 demux->fetcher_error = FALSE;
551 demux->stopping_fetcher = FALSE;
552 gst_element_set_bus (GST_ELEMENT (demux->fetcher), demux->fetcher_bus);
554 g_object_set (G_OBJECT (demux->fetcher), "location", uri, NULL);
555 pad = gst_element_get_static_pad (demux->fetcher, "src");
557 gst_pad_link (pad, demux->fetcherpad);
558 gst_object_unref (pad);
564 gst_hls_demux_reset (GstHLSDemux * demux, gboolean dispose)
566 demux->need_cache = TRUE;
567 demux->thread_return = FALSE;
568 demux->accumulated_delay = 0;
569 demux->end_of_playlist = FALSE;
570 demux->cancelled = FALSE;
572 if (demux->input_caps) {
573 gst_caps_unref (demux->input_caps);
574 demux->input_caps = NULL;
577 if (demux->playlist) {
578 gst_buffer_unref (demux->playlist);
579 demux->playlist = NULL;
582 if (demux->downloaded_uri) {
583 gst_buffer_unref (demux->downloaded_uri);
584 demux->downloaded_uri = NULL;
588 gst_m3u8_client_free (demux->client);
591 demux->client = gst_m3u8_client_new ("");
594 while (!g_queue_is_empty (demux->queue)) {
595 GstBuffer *buf = g_queue_pop_head (demux->queue);
596 gst_buffer_unref (buf);
598 g_queue_clear (demux->queue);
602 gst_hls_demux_set_location (GstHLSDemux * demux, const gchar * uri)
605 gst_m3u8_client_free (demux->client);
606 demux->client = gst_m3u8_client_new (uri);
607 GST_INFO_OBJECT (demux, "Changed location: %s", uri);
612 gst_hls_demux_update_thread (GstHLSDemux * demux)
614 /* Loop for the updates. It's started when the first fragments are cached and
615 * schedules the next update of the playlist (for lives sources) and the next
616 * update of fragments. When a new fragment is downloaded, it compares the
617 * download time with the next scheduled update to check if we can or should
618 * switch to a different bitrate */
620 g_mutex_lock (demux->thread_lock);
622 /* block until the next scheduled update or the signal to quit this thread */
623 if (g_cond_timed_wait (demux->thread_cond, demux->thread_lock,
624 &demux->next_update)) {
628 /* update the playlist for live sources */
629 if (gst_m3u8_client_is_live (demux->client)) {
630 if (!gst_hls_demux_update_playlist (demux, TRUE)) {
631 GST_ERROR_OBJECT (demux, "Could not update the playlist");
636 /* schedule the next update */
637 gst_hls_demux_schedule (demux);
639 /* if it's a live source and the playlist couldn't be updated, there aren't
640 * more fragments in the playlist, so we just wait for the next schedulled
642 if (gst_m3u8_client_is_live (demux->client) &&
643 demux->client->update_failed_count > 0) {
644 GST_WARNING_OBJECT (demux,
645 "The playlist hasn't been updated, failed count is %d",
646 demux->client->update_failed_count);
650 /* fetch the next fragment */
651 if (!gst_hls_demux_get_next_fragment (demux, TRUE)) {
652 if (!demux->end_of_playlist && !demux->cancelled)
653 GST_ERROR_OBJECT (demux, "Could not fetch the next fragment");
657 /* try to switch to another bitrate if needed */
658 gst_hls_demux_switch_playlist (demux);
663 g_mutex_unlock (demux->thread_lock);
669 gst_hls_demux_start_update (GstHLSDemux * demux)
673 /* creates a new thread for the updates */
674 demux->updates_thread = g_thread_create (
675 (GThreadFunc) gst_hls_demux_update_thread, demux, TRUE, &error);
676 return (error != NULL);
680 gst_hls_demux_cache_fragments (GstHLSDemux * demux)
684 /* Start parsing the main playlist */
685 gst_m3u8_client_set_current (demux->client, demux->client->main);
687 if (gst_m3u8_client_is_live (demux->client)) {
688 if (!gst_hls_demux_update_playlist (demux, FALSE)) {
689 GST_ERROR_OBJECT (demux, "Could not fetch the main playlist %s",
690 demux->client->main->uri);
695 /* If this playlist is a variant playlist, select the first one
697 if (gst_m3u8_client_has_variant_playlist (demux->client)) {
698 GstM3U8 *child = demux->client->main->lists->data;
699 gst_m3u8_client_set_current (demux->client, child);
700 if (!gst_hls_demux_update_playlist (demux, FALSE)) {
701 GST_ERROR_OBJECT (demux, "Could not fetch the child playlist %s",
707 /* If it's a live source, set the sequence number to the end of the list
708 * and substract the 'fragmets_cache' to start from the last fragment*/
709 if (gst_m3u8_client_is_live (demux->client)) {
710 demux->client->sequence += g_list_length (demux->client->current->files);
711 if (demux->client->sequence >= demux->fragments_cache)
712 demux->client->sequence -= demux->fragments_cache;
714 demux->client->sequence = 0;
717 /* Cache the first fragments */
718 for (i = 0; i < demux->fragments_cache - 1; i++) {
719 if (!gst_hls_demux_get_next_fragment (demux, FALSE)) {
720 if (!demux->cancelled)
721 GST_ERROR_OBJECT (demux, "Error caching the first fragments");
724 /* make sure we stop caching fragments if something cancelled it */
725 if (demux->cancelled)
729 g_get_current_time (&demux->next_update);
731 demux->need_cache = FALSE;
736 gst_hls_demux_fetch_location (GstHLSDemux * demux, const gchar * uri)
738 GstStateChangeReturn ret;
739 gboolean bret = FALSE;
741 g_mutex_lock (demux->fetcher_lock);
743 if (!gst_hls_demux_make_fetcher (demux, uri)) {
747 ret = gst_element_set_state (demux->fetcher, GST_STATE_PLAYING);
748 if (ret == GST_STATE_CHANGE_FAILURE)
749 goto state_change_error;
751 /* wait until we have fetched the uri */
752 GST_DEBUG_OBJECT (demux, "Waiting to fetch the URI");
753 g_cond_wait (demux->fetcher_cond, demux->fetcher_lock);
755 gst_hls_demux_stop_fetcher (demux, FALSE);
757 if (demux->downloaded_uri != NULL) {
758 GST_INFO_OBJECT (demux, "URI fetched successfully");
765 GST_ELEMENT_ERROR (demux, RESOURCE, OPEN_READ,
766 ("Could not create an element to fetch the given URI."), ("URI: \"%s\"",
774 GST_ELEMENT_ERROR (demux, CORE, STATE_CHANGE,
775 ("Error changing state of the fetcher element."), NULL);
782 g_mutex_unlock (demux->fetcher_lock);
788 gst_hls_demux_update_playlist (GstHLSDemux * demux, gboolean retry)
792 GST_INFO_OBJECT (demux, "Updating the playlist %s", demux->client->current->uri);
793 if (!gst_hls_demux_fetch_location (demux, demux->client->current->uri))
796 playlist = g_strndup ((gchar *) GST_BUFFER_DATA (demux->downloaded_uri),
797 GST_BUFFER_SIZE (demux->downloaded_uri));
798 gst_m3u8_client_update (demux->client, playlist);
799 gst_buffer_unref (demux->downloaded_uri);
800 demux->downloaded_uri = NULL;
805 gst_hls_demux_change_playlist (GstHLSDemux * demux, gboolean is_fast)
808 if (!demux->client->main->lists->next)
810 demux->client->main->lists = g_list_next (demux->client->main->lists);
812 if (!demux->client->main->lists->prev)
814 demux->client->main->lists = g_list_previous (demux->client->main->lists);
817 gst_m3u8_client_set_current (demux->client, demux->client->main->lists->data);
818 gst_hls_demux_update_playlist (demux, TRUE);
819 GST_INFO_OBJECT (demux, "Client is %s, switching to bitrate %d",
820 is_fast ? "fast" : "slow", demux->client->current->bandwidth);
826 gst_hls_demux_schedule (GstHLSDemux * demux)
828 gfloat update_factor;
831 /* As defined in ยง6.3.4. Reloading the Playlist file:
832 * "If the client reloads a Playlist file and finds that it has not
833 * changed then it MUST wait for a period of time before retrying. The
834 * minimum delay is a multiple of the target duration. This multiple is
835 * 0.5 for the first attempt, 1.5 for the second, and 3.0 thereafter."
837 count = demux->client->update_failed_count;
839 update_factor = update_interval_factor[count];
841 update_factor = update_interval_factor[3];
843 /* schedule the next update using the target duration field of the
845 g_time_val_add (&demux->next_update,
846 demux->client->current->targetduration * update_factor * 1000000);
847 GST_DEBUG_OBJECT (demux, "Next update scheduled at %s",
848 g_time_val_to_iso8601 (&demux->next_update));
854 gst_hls_demux_switch_playlist (GstHLSDemux * demux)
859 g_get_current_time (&now);
860 if (!demux->client->main->lists)
863 /* compare the time when the fragment was downloaded with the time when it was
865 diff = (GST_TIMEVAL_TO_TIME (demux->next_update) - GST_TIMEVAL_TO_TIME (now));
866 limit = demux->client->current->targetduration * GST_SECOND *
867 demux->bitrate_switch_tol;
869 /* if we are on time switch to a higher bitrate */
871 gst_hls_demux_change_playlist (demux, TRUE);
872 } else if (diff < 0) {
873 /* if the client is too slow wait until it has accumulated a certain delay to
874 * switch to a lower bitrate */
875 demux->accumulated_delay -= diff;
876 if (demux->accumulated_delay >= limit) {
877 gst_hls_demux_change_playlist (demux, FALSE);
878 } else if (demux->accumulated_delay < 0) {
879 demux->accumulated_delay = 0;
886 gst_hls_demux_get_next_fragment (GstHLSDemux * demux, gboolean retry)
889 const gchar *next_fragment_uri;
892 next_fragment_uri = gst_m3u8_client_get_next_fragment (demux->client, &discont);
894 if (!next_fragment_uri) {
895 GST_INFO_OBJECT (demux, "This playlist doesn't contain more fragments");
896 demux->end_of_playlist = TRUE;
897 GST_TASK_SIGNAL (demux->task);
901 GST_INFO_OBJECT (demux, "Fetching next fragment %s", next_fragment_uri);
903 if (!gst_hls_demux_fetch_location (demux, next_fragment_uri))
906 buf = demux->downloaded_uri;
908 if (G_UNLIKELY (demux->input_caps == NULL)) {
909 demux->input_caps = gst_type_find_helper_for_buffer (NULL, buf, NULL);
910 if (demux->input_caps) {
911 gst_pad_set_caps (demux->srcpad, demux->input_caps);
912 GST_INFO_OBJECT (demux, "Input source caps: %" GST_PTR_FORMAT,
918 GST_DEBUG_OBJECT (demux, "Marking fragment as discontinuous");
919 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
922 g_queue_push_tail (demux->queue, buf);
923 GST_TASK_SIGNAL (demux->task);
924 demux->downloaded_uri = NULL;