static gboolean gst_hls_demux_update_thread (GstHLSDemux * demux) { /* Loop for the updates. It's started when the first fragments are cached and * schedules the next update of the playlist (for lives sources) and the next * update of fragments. When a new fragment is downloaded, it compares the * download time with the next scheduled update to check if we can or should * switch to a different bitrate */ g_mutex_lock (demux->thread_lock); while (TRUE) { /* block until the next scheduled update or the signal to quit this thread */ if (g_cond_timed_wait (demux->thread_cond, demux->thread_lock, &demux->next_update)) { goto quit; } /* update the playlist for live sources */ if (gst_m3u8_client_is_live (demux->client)) { if (!gst_hls_demux_update_playlist (demux, TRUE)) { GST_ERROR_OBJECT (demux, "Could not update the playlist"); goto quit; } } /* schedule the next update */ gst_hls_demux_schedule (demux); /* if it's a live source and the playlist couldn't be updated, there aren't * more fragments in the playlist, so we just wait for the next schedulled * update */ if (gst_m3u8_client_is_live (demux->client) && demux->client->update_failed_count > 0) { GST_WARNING_OBJECT (demux, "The playlist hasn't been updated, failed count is %d", demux->client->update_failed_count); continue; } /* fetch the next fragment */ if (!gst_hls_demux_get_next_fragment (demux, TRUE)) { if (!demux->end_of_playlist && !demux->cancelled) GST_ERROR_OBJECT (demux, "Could not fetch the next fragment"); goto quit; } /* try to switch to another bitrate if needed */ gst_hls_demux_switch_playlist (demux); } quit: { g_mutex_unlock (demux->thread_lock); return TRUE; } }
static gboolean gst_hls_demux_cache_fragments (GstHLSDemux * demux) { gint i; /* Start parsing the main playlist */ gst_m3u8_client_set_current (demux->client, demux->client->main); if (gst_m3u8_client_is_live (demux->client)) { if (!gst_hls_demux_update_playlist (demux, FALSE)) { GST_ERROR_OBJECT (demux, "Could not fetch the main playlist %s", demux->client->main->uri); return FALSE; } } /* If this playlist is a variant playlist, select the first one * and update it */ if (gst_m3u8_client_has_variant_playlist (demux->client)) { GstM3U8 *child = demux->client->main->lists->data; gst_m3u8_client_set_current (demux->client, child); if (!gst_hls_demux_update_playlist (demux, FALSE)) { GST_ERROR_OBJECT (demux, "Could not fetch the child playlist %s", child->uri); return FALSE; } } /* If it's a live source, set the sequence number to the end of the list * and substract the 'fragmets_cache' to start from the last fragment*/ if (gst_m3u8_client_is_live (demux->client)) { demux->client->sequence += g_list_length (demux->client->current->files); if (demux->client->sequence >= demux->fragments_cache) demux->client->sequence -= demux->fragments_cache; else demux->client->sequence = 0; } /* Cache the first fragments */ for (i = 0; i < demux->fragments_cache - 1; i++) { if (!gst_hls_demux_get_next_fragment (demux, FALSE)) { if (!demux->cancelled) GST_ERROR_OBJECT (demux, "Error caching the first fragments"); return FALSE; } /* make sure we stop caching fragments if something cancelled it */ if (demux->cancelled) return FALSE; } g_get_current_time (&demux->next_update); demux->need_cache = FALSE; return TRUE; }
static gboolean gst_hls_demux_is_live (GstAdaptiveDemux * demux) { GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux); return gst_m3u8_client_is_live (hlsdemux->client); }
static void check_on_demand_playlist (const gchar * data) { GstM3U8Client *client; GstM3U8 *pl; GstM3U8MediaFile *file; client = load_playlist (data); pl = client->current; /* Sequence should be 0 as it's an ondemand playlist */ assert_equals_int (client->sequence, 0); /* Check that we are not live */ assert_equals_int (gst_m3u8_client_is_live (client), FALSE); /* Check number of entries */ assert_equals_int (g_list_length (pl->files), 4); /* Check first media segments */ file = GST_M3U8_MEDIA_FILE (g_list_first (pl->files)->data); assert_equals_string (file->uri, "http://media.example.com/001.ts"); assert_equals_int (file->sequence, 0); /* Check last media segments */ file = GST_M3U8_MEDIA_FILE (g_list_last (pl->files)->data); assert_equals_string (file->uri, "http://media.example.com/004.ts"); assert_equals_int (file->sequence, 3); gst_m3u8_client_free (client); }
static gboolean gst_hls_demux_sink_event (GstPad * pad, GstEvent * event) { GstHLSDemux *demux = GST_HLS_DEMUX (gst_pad_get_parent (pad)); GstQuery *query; gboolean ret; gchar *uri; switch (event->type) { case GST_EVENT_EOS:{ gchar *playlist; if (demux->playlist == NULL) { GST_WARNING_OBJECT (demux, "Received EOS without a playlist."); break; } GST_DEBUG_OBJECT (demux, "Got EOS on the sink pad: main playlist fetched"); query = gst_query_new_uri (); ret = gst_pad_peer_query (demux->sinkpad, query); if (ret) { gst_query_parse_uri (query, &uri); gst_hls_demux_set_location (demux, uri); g_free (uri); } gst_query_unref (query); playlist = gst_hls_src_buf_to_utf8_playlist ((gchar *) GST_BUFFER_DATA (demux->playlist), GST_BUFFER_SIZE (demux->playlist)); gst_buffer_unref (demux->playlist); if (playlist == NULL) { GST_WARNING_OBJECT (demux, "Error validating first playlist."); } else if (!gst_m3u8_client_update (demux->client, playlist)) { /* In most cases, this will happen if we set a wrong url in the * source element and we have received the 404 HTML response instead of * the playlist */ GST_ELEMENT_ERROR (demux, STREAM, DECODE, ("Invalid playlist."), NULL); return FALSE; } if (!ret && gst_m3u8_client_is_live (demux->client)) { GST_ELEMENT_ERROR (demux, RESOURCE, NOT_FOUND, ("Failed querying the playlist uri, " "required for live sources."), NULL); return FALSE; } gst_task_start (demux->task); gst_event_unref (event); return TRUE; } default: break; } return gst_pad_event_default (pad, event); }
static gboolean gst_hls_demux_update_playlist (GstHLSDemux * demux, gboolean update) { GstFragment *download; GstBuffer *buf; gchar *playlist; gboolean updated = FALSE; const gchar *uri = gst_m3u8_client_get_current_uri (demux->client); download = gst_uri_downloader_fetch_uri (demux->downloader, uri); if (download == NULL) return FALSE; buf = gst_fragment_get_buffer (download); playlist = gst_hls_src_buf_to_utf8_playlist (buf); g_object_unref (download); if (playlist == NULL) { GST_WARNING_OBJECT (demux, "Couldn't not validate playlist encoding"); return FALSE; } updated = gst_m3u8_client_update (demux->client, playlist); /* If it's a live source, do not let the sequence number go beyond * three fragments before the end of the list */ if (updated && update == FALSE && demux->client->current && gst_m3u8_client_is_live (demux->client)) { guint last_sequence; GST_M3U8_CLIENT_LOCK (demux->client); last_sequence = GST_M3U8_MEDIA_FILE (g_list_last (demux->client->current-> files)->data)->sequence; if (demux->client->sequence >= last_sequence - 3) { GST_DEBUG_OBJECT (demux, "Sequence is beyond playlist. Moving back to %d", last_sequence - 3); demux->need_segment = TRUE; demux->client->sequence = last_sequence - 3; } GST_M3U8_CLIENT_UNLOCK (demux->client); } return updated; }
static gboolean gst_hls_demux_src_query (GstPad * pad, GstQuery * query) { GstHLSDemux *hlsdemux; gboolean ret = FALSE; if (query == NULL) return FALSE; hlsdemux = GST_HLS_DEMUX (gst_pad_get_element_private (pad)); switch (query->type) { case GST_QUERY_DURATION:{ GstClockTime duration = -1; GstFormat fmt; gst_query_parse_duration (query, &fmt, NULL); if (fmt == GST_FORMAT_TIME) { duration = gst_m3u8_client_get_duration (hlsdemux->client); if (GST_CLOCK_TIME_IS_VALID (duration) && duration > 0) { gst_query_set_duration (query, GST_FORMAT_TIME, duration); ret = TRUE; } } GST_INFO_OBJECT (hlsdemux, "GST_QUERY_DURATION returns %s with duration %" GST_TIME_FORMAT, ret ? "TRUE" : "FALSE", GST_TIME_ARGS (duration)); break; } case GST_QUERY_URI: if (hlsdemux->client) { /* FIXME: Do we answer with the variant playlist, with the current * playlist or the the uri of the least downlowaded fragment? */ gst_query_set_uri (query, hlsdemux->client->current->uri); ret = TRUE; } break; case GST_QUERY_SEEKING:{ GstFormat fmt; gint64 stop = -1; gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL); GST_INFO_OBJECT (hlsdemux, "Received GST_QUERY_SEEKING with format %d", fmt); if (fmt == GST_FORMAT_TIME) { GstClockTime duration; duration = gst_m3u8_client_get_duration (hlsdemux->client); if (GST_CLOCK_TIME_IS_VALID (duration) && duration > 0) stop = duration; gst_query_set_seeking (query, fmt, !gst_m3u8_client_is_live (hlsdemux->client), 0, stop); ret = TRUE; GST_INFO_OBJECT (hlsdemux, "GST_QUERY_SEEKING returning with stop : %" GST_TIME_FORMAT, GST_TIME_ARGS (stop)); } break; } default: /* Don't fordward queries upstream because of the special nature of this * "demuxer", which relies on the upstream element only to be fed with the * first playlist */ break; } return ret; }
static gboolean gst_hls_demux_src_event (GstPad * pad, GstEvent * event) { GstHLSDemux *demux; demux = GST_HLS_DEMUX (gst_pad_get_element_private (pad)); switch (event->type) { case GST_EVENT_SEEK: { gdouble rate; GstFormat format; GstSeekFlags flags; GstSeekType start_type, stop_type; gint64 start, stop; GList *walk; gint current_pos; gint current_sequence; gint target_second; GstM3U8MediaFile *file; GST_INFO_OBJECT (demux, "Received GST_EVENT_SEEK"); if (gst_m3u8_client_is_live (demux->client)) { GST_WARNING_OBJECT (demux, "Received seek event for live stream"); return FALSE; } gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start, &stop_type, &stop); if (format != GST_FORMAT_TIME) return FALSE; GST_DEBUG_OBJECT (demux, "seek event, rate: %f start: %" GST_TIME_FORMAT " stop: %" GST_TIME_FORMAT, rate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop)); file = GST_M3U8_MEDIA_FILE (demux->client->current->files->data); current_sequence = file->sequence; current_pos = 0; target_second = start / GST_SECOND; GST_DEBUG_OBJECT (demux, "Target seek to %d", target_second); for (walk = demux->client->current->files; walk; walk = walk->next) { file = walk->data; current_sequence = file->sequence; if (current_pos <= target_second && target_second < current_pos + file->duration) { break; } current_pos += file->duration; } if (walk == NULL) { GST_WARNING_OBJECT (demux, "Could not find seeked fragment"); return FALSE; } if (flags & GST_SEEK_FLAG_FLUSH) { GST_DEBUG_OBJECT (demux, "sending flush start"); gst_pad_push_event (demux->srcpad, gst_event_new_flush_start ()); } demux->cancelled = TRUE; gst_task_pause (demux->task); g_mutex_lock (demux->fetcher_lock); gst_hls_demux_stop_fetcher (demux, TRUE); g_mutex_unlock (demux->fetcher_lock); g_mutex_lock (demux->thread_lock); g_cond_signal (demux->thread_cond); g_mutex_unlock (demux->thread_lock); gst_task_pause (demux->task); /* wait for streaming to finish */ g_static_rec_mutex_lock (&demux->task_lock); demux->need_cache = TRUE; while (!g_queue_is_empty (demux->queue)) { GstBuffer *buf = g_queue_pop_head (demux->queue); gst_buffer_unref (buf); } GST_DEBUG_OBJECT (demux, "seeking to sequence %d", current_sequence); demux->client->sequence = current_sequence; demux->position = start; demux->need_segment = TRUE; if (flags & GST_SEEK_FLAG_FLUSH) { GST_DEBUG_OBJECT (demux, "sending flush stop"); gst_pad_push_event (demux->srcpad, gst_event_new_flush_stop ()); } demux->cancelled = FALSE; gst_task_start (demux->task); g_static_rec_mutex_unlock (&demux->task_lock); return TRUE; } default: break; } return gst_pad_event_default (pad, event); }
static gboolean gst_hls_demux_update_playlist (GstHLSDemux * demux, gboolean update, GError ** err) { GstAdaptiveDemux *adaptive_demux = GST_ADAPTIVE_DEMUX (demux); GstFragment *download; GstBuffer *buf; gchar *playlist; gboolean main_checked = FALSE, updated = FALSE; gchar *uri, *main_uri; retry: uri = gst_m3u8_client_get_current_uri (demux->client); main_uri = gst_m3u8_client_get_uri (demux->client); download = gst_uri_downloader_fetch_uri (adaptive_demux->downloader, uri, main_uri, TRUE, TRUE, TRUE, err); g_free (main_uri); if (download == NULL) { gchar *base_uri; if (!update || main_checked || !gst_m3u8_client_has_variant_playlist (demux->client)) { g_free (uri); return FALSE; } g_clear_error (err); main_uri = gst_m3u8_client_get_uri (demux->client); GST_INFO_OBJECT (demux, "Updating playlist %s failed, attempt to refresh variant playlist %s", uri, main_uri); download = gst_uri_downloader_fetch_uri (adaptive_demux->downloader, main_uri, NULL, TRUE, TRUE, TRUE, err); g_free (main_uri); if (download == NULL) { g_free (uri); return FALSE; } buf = gst_fragment_get_buffer (download); playlist = gst_hls_src_buf_to_utf8_playlist (buf); gst_buffer_unref (buf); if (playlist == NULL) { GST_WARNING_OBJECT (demux, "Failed to validate variant playlist encoding"); g_free (uri); g_object_unref (download); g_set_error (err, GST_STREAM_ERROR, GST_STREAM_ERROR_WRONG_TYPE, "Couldn't validate playlist encoding"); return FALSE; } g_free (uri); if (download->redirect_permanent && download->redirect_uri) { uri = download->redirect_uri; base_uri = NULL; } else { uri = download->uri; base_uri = download->redirect_uri; } if (!gst_m3u8_client_update_variant_playlist (demux->client, playlist, uri, base_uri)) { GST_WARNING_OBJECT (demux, "Failed to update the variant playlist"); g_object_unref (download); g_set_error (err, GST_STREAM_ERROR, GST_STREAM_ERROR_FAILED, "Couldn't update playlist"); return FALSE; } g_object_unref (download); main_checked = TRUE; goto retry; } g_free (uri); /* Set the base URI of the playlist to the redirect target if any */ GST_M3U8_CLIENT_LOCK (demux->client); g_free (demux->client->current->uri); g_free (demux->client->current->base_uri); if (download->redirect_permanent && download->redirect_uri) { demux->client->current->uri = g_strdup (download->redirect_uri); demux->client->current->base_uri = NULL; } else { demux->client->current->uri = g_strdup (download->uri); demux->client->current->base_uri = g_strdup (download->redirect_uri); } GST_M3U8_CLIENT_UNLOCK (demux->client); buf = gst_fragment_get_buffer (download); playlist = gst_hls_src_buf_to_utf8_playlist (buf); gst_buffer_unref (buf); g_object_unref (download); if (playlist == NULL) { GST_WARNING_OBJECT (demux, "Couldn't validate playlist encoding"); g_set_error (err, GST_STREAM_ERROR, GST_STREAM_ERROR_WRONG_TYPE, "Couldn't validate playlist encoding"); return FALSE; } updated = gst_m3u8_client_update (demux->client, playlist); if (!updated) { GST_WARNING_OBJECT (demux, "Couldn't update playlist"); g_set_error (err, GST_STREAM_ERROR, GST_STREAM_ERROR_FAILED, "Couldn't update playlist"); return FALSE; } /* If it's a live source, do not let the sequence number go beyond * three fragments before the end of the list */ if (update == FALSE && demux->client->current && gst_m3u8_client_is_live (demux->client)) { gint64 last_sequence, first_sequence; GST_M3U8_CLIENT_LOCK (demux->client); last_sequence = GST_M3U8_MEDIA_FILE (g_list_last (demux->client->current-> files)->data)->sequence; first_sequence = GST_M3U8_MEDIA_FILE (demux->client->current->files->data)->sequence; GST_DEBUG_OBJECT (demux, "sequence:%" G_GINT64_FORMAT " , first_sequence:%" G_GINT64_FORMAT " , last_sequence:%" G_GINT64_FORMAT, demux->client->sequence, first_sequence, last_sequence); if (demux->client->sequence >= last_sequence - 3) { //demux->need_segment = TRUE; /* Make sure we never go below the minimum sequence number */ demux->client->sequence = MAX (first_sequence, last_sequence - 3); GST_DEBUG_OBJECT (demux, "Sequence is beyond playlist. Moving back to %" G_GINT64_FORMAT, demux->client->sequence); } GST_M3U8_CLIENT_UNLOCK (demux->client); } else if (demux->client->current && !gst_m3u8_client_is_live (demux->client)) { GstClockTime current_pos, target_pos; guint sequence = 0; GList *walk; /* Sequence numbers are not guaranteed to be the same in different * playlists, so get the correct fragment here based on the current * position */ GST_M3U8_CLIENT_LOCK (demux->client); /* Valid because hlsdemux only has a single output */ if (GST_ADAPTIVE_DEMUX_CAST (demux)->streams) { GstAdaptiveDemuxStream *stream = GST_ADAPTIVE_DEMUX_CAST (demux)->streams->data; target_pos = stream->segment.position; } else { target_pos = 0; } if (GST_CLOCK_TIME_IS_VALID (demux->client->sequence_position)) { target_pos = MAX (target_pos, demux->client->sequence_position); } GST_LOG_OBJECT (demux, "Looking for sequence position %" GST_TIME_FORMAT " in updated playlist", GST_TIME_ARGS (target_pos)); current_pos = 0; for (walk = demux->client->current->files; walk; walk = walk->next) { GstM3U8MediaFile *file = walk->data; sequence = file->sequence; if (current_pos <= target_pos && target_pos < current_pos + file->duration) { break; } current_pos += file->duration; } /* End of playlist */ if (!walk) sequence++; demux->client->sequence = sequence; demux->client->sequence_position = current_pos; GST_M3U8_CLIENT_UNLOCK (demux->client); } return updated; }
static gboolean gst_hls_demux_cache_fragments (GstHLSDemux * demux) { gint i; /* If this playlist is a variant playlist, select the first one * and update it */ if (gst_m3u8_client_has_variant_playlist (demux->client)) { GstM3U8 *child = NULL; GST_M3U8_CLIENT_LOCK (demux->client); child = demux->client->main->current_variant->data; GST_M3U8_CLIENT_UNLOCK (demux->client); gst_m3u8_client_set_current (demux->client, child); if (!gst_hls_demux_update_playlist (demux)) { GST_ERROR_OBJECT (demux, "Could not fetch the child playlist %s", child->uri); return FALSE; } } /* If it's a live source, set the sequence number to the end of the list * and substract the 'fragmets_cache' to start from the last fragment*/ if (gst_m3u8_client_is_live (demux->client)) { GST_M3U8_CLIENT_LOCK (demux->client); demux->client->sequence += g_list_length (demux->client->current->files); if (demux->client->sequence >= demux->fragments_cache) demux->client->sequence -= demux->fragments_cache; else demux->client->sequence = 0; gst_m3u8_client_get_current_position (demux->client, &demux->position); GST_M3U8_CLIENT_UNLOCK (demux->client); } else { GstClockTime duration = gst_m3u8_client_get_duration (demux->client); GST_DEBUG_OBJECT (demux, "Sending duration message : %" GST_TIME_FORMAT, GST_TIME_ARGS (duration)); if (duration != GST_CLOCK_TIME_NONE) gst_element_post_message (GST_ELEMENT (demux), gst_message_new_duration (GST_OBJECT (demux), GST_FORMAT_TIME, duration)); } /* Cache the first fragments */ for (i = 0; i < demux->fragments_cache; i++) { gst_element_post_message (GST_ELEMENT (demux), gst_message_new_buffering (GST_OBJECT (demux), 100 * i / demux->fragments_cache)); g_get_current_time (&demux->next_update); g_time_val_add (&demux->next_update, gst_m3u8_client_get_target_duration (demux->client) / GST_SECOND * G_USEC_PER_SEC); if (!gst_hls_demux_get_next_fragment (demux)) { if (demux->end_of_playlist) break; if (!demux->cancelled) GST_ERROR_OBJECT (demux, "Error caching the first fragments"); return FALSE; } /* make sure we stop caching fragments if something cancelled it */ if (demux->cancelled) return FALSE; gst_hls_demux_switch_playlist (demux); } gst_element_post_message (GST_ELEMENT (demux), gst_message_new_buffering (GST_OBJECT (demux), 100)); g_get_current_time (&demux->next_update); demux->need_cache = FALSE; return TRUE; }
static gboolean gst_hls_demux_cache_fragments (GstHLSDemux * demux) { gint i; /* If this playlist is a variant playlist, select the first one * and update it */ if (gst_m3u8_client_has_variant_playlist (demux->client)) { GstM3U8 *child = NULL; if (demux->connection_speed == 0) { GST_M3U8_CLIENT_LOCK (demux->client); child = demux->client->main->current_variant->data; GST_M3U8_CLIENT_UNLOCK (demux->client); } else { GList *tmp = gst_m3u8_client_get_playlist_for_bitrate (demux->client, demux->connection_speed); child = GST_M3U8 (tmp->data); } gst_m3u8_client_set_current (demux->client, child); if (!gst_hls_demux_update_playlist (demux, FALSE)) { GST_ERROR_OBJECT (demux, "Could not fetch the child playlist %s", child->uri); return FALSE; } } if (!gst_m3u8_client_is_live (demux->client)) { GstClockTime duration = gst_m3u8_client_get_duration (demux->client); GST_DEBUG_OBJECT (demux, "Sending duration message : %" GST_TIME_FORMAT, GST_TIME_ARGS (duration)); if (duration != GST_CLOCK_TIME_NONE) gst_element_post_message (GST_ELEMENT (demux), gst_message_new_duration (GST_OBJECT (demux), GST_FORMAT_TIME, duration)); } /* Cache the first fragments */ for (i = 0; i < demux->fragments_cache; i++) { gst_element_post_message (GST_ELEMENT (demux), gst_message_new_buffering (GST_OBJECT (demux), 100 * i / demux->fragments_cache)); g_get_current_time (&demux->next_update); if (!gst_hls_demux_get_next_fragment (demux, TRUE)) { if (demux->end_of_playlist) break; if (!demux->cancelled) GST_ERROR_OBJECT (demux, "Error caching the first fragments"); return FALSE; } /* make sure we stop caching fragments if something cancelled it */ if (demux->cancelled) return FALSE; gst_hls_demux_switch_playlist (demux); } gst_element_post_message (GST_ELEMENT (demux), gst_message_new_buffering (GST_OBJECT (demux), 100)); g_get_current_time (&demux->next_update); demux->need_cache = FALSE; return TRUE; }
void gst_hls_demux_updates_loop (GstHLSDemux * demux) { /* Loop for the updates. It's started when the first fragments are cached and * schedules the next update of the playlist (for lives sources) and the next * update of fragments. When a new fragment is downloaded, it compares the * download time with the next scheduled update to check if we can or should * switch to a different bitrate */ /* block until the next scheduled update or the signal to quit this thread */ g_mutex_lock (&demux->updates_timed_lock); GST_DEBUG_OBJECT (demux, "Started updates task"); while (TRUE) { /* schedule the next update */ gst_hls_demux_schedule (demux); /* block until the next scheduled update or the signal to quit this thread */ if (g_cond_timed_wait (GST_TASK_GET_COND (demux->updates_task), &demux->updates_timed_lock, &demux->next_update)) { goto quit; } /* update the playlist for live sources */ if (gst_m3u8_client_is_live (demux->client)) { if (!gst_hls_demux_update_playlist (demux, TRUE)) { demux->client->update_failed_count++; if (demux->client->update_failed_count < DEFAULT_FAILED_COUNT) { GST_WARNING_OBJECT (demux, "Could not update the playlist"); continue; } else { GST_ELEMENT_ERROR (demux, RESOURCE, NOT_FOUND, ("Could not update the playlist"), (NULL)); goto quit; } } } /* if it's a live source and the playlist couldn't be updated, there aren't * more fragments in the playlist, so we just wait for the next schedulled * update */ if (gst_m3u8_client_is_live (demux->client) && demux->client->update_failed_count > 0) { GST_WARNING_OBJECT (demux, "The playlist hasn't been updated, failed count is %d", demux->client->update_failed_count); continue; } /* fetch the next fragment */ if (g_queue_is_empty (demux->queue)) { if (!gst_hls_demux_get_next_fragment (demux, FALSE)) { if (!demux->end_of_playlist && !demux->cancelled) { demux->client->update_failed_count++; if (demux->client->update_failed_count < DEFAULT_FAILED_COUNT) { GST_WARNING_OBJECT (demux, "Could not fetch the next fragment"); continue; } else { GST_ELEMENT_ERROR (demux, RESOURCE, NOT_FOUND, ("Could not fetch the next fragment"), (NULL)); goto quit; } } } else { demux->client->update_failed_count = 0; /* try to switch to another bitrate if needed */ gst_hls_demux_switch_playlist (demux); } } } quit: { GST_DEBUG_OBJECT (demux, "Stopped updates task"); gst_hls_demux_stop (demux); g_mutex_unlock (&demux->updates_timed_lock); } }
static gboolean gst_hls_demux_src_event (GstPad * pad, GstObject * parent, GstEvent * event) { GstHLSDemux *demux; demux = GST_HLS_DEMUX (parent); switch (event->type) { case GST_EVENT_SEEK: { gdouble rate; GstFormat format; GstSeekFlags flags; GstSeekType start_type, stop_type; gint64 start, stop; GList *walk; GstClockTime position, current_pos, target_pos; gint current_sequence; GstM3U8MediaFile *file; GST_INFO_OBJECT (demux, "Received GST_EVENT_SEEK"); if (gst_m3u8_client_is_live (demux->client)) { GST_WARNING_OBJECT (demux, "Received seek event for live stream"); return FALSE; } gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start, &stop_type, &stop); if (format != GST_FORMAT_TIME) return FALSE; GST_DEBUG_OBJECT (demux, "seek event, rate: %f start: %" GST_TIME_FORMAT " stop: %" GST_TIME_FORMAT, rate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop)); GST_M3U8_CLIENT_LOCK (demux->client); file = GST_M3U8_MEDIA_FILE (demux->client->current->files->data); current_sequence = file->sequence; current_pos = 0; target_pos = (GstClockTime) start; for (walk = demux->client->current->files; walk; walk = walk->next) { file = walk->data; current_sequence = file->sequence; if (current_pos <= target_pos && target_pos < current_pos + file->duration) { break; } current_pos += file->duration; } GST_M3U8_CLIENT_UNLOCK (demux->client); if (walk == NULL) { GST_WARNING_OBJECT (demux, "Could not find seeked fragment"); return FALSE; } if (flags & GST_SEEK_FLAG_FLUSH) { GST_DEBUG_OBJECT (demux, "sending flush start"); gst_pad_push_event (demux->srcpad, gst_event_new_flush_start ()); } demux->cancelled = TRUE; gst_task_pause (demux->stream_task); gst_uri_downloader_cancel (demux->downloader); gst_task_stop (demux->updates_task); gst_task_pause (demux->stream_task); /* wait for streaming to finish */ g_rec_mutex_lock (&demux->stream_lock); demux->need_cache = TRUE; while (!g_queue_is_empty (demux->queue)) { GstFragment *fragment = g_queue_pop_head (demux->queue); g_object_unref (fragment); } g_queue_clear (demux->queue); GST_M3U8_CLIENT_LOCK (demux->client); GST_DEBUG_OBJECT (demux, "seeking to sequence %d", current_sequence); demux->client->sequence = current_sequence; gst_m3u8_client_get_current_position (demux->client, &position); demux->position_shift = start - position; demux->need_segment = TRUE; GST_M3U8_CLIENT_UNLOCK (demux->client); if (flags & GST_SEEK_FLAG_FLUSH) { GST_DEBUG_OBJECT (demux, "sending flush stop"); gst_pad_push_event (demux->srcpad, gst_event_new_flush_stop (TRUE)); } demux->cancelled = FALSE; gst_task_start (demux->stream_task); g_rec_mutex_unlock (&demux->stream_lock); return TRUE; } default: break; } return gst_pad_event_default (pad, parent, event); }