static gboolean gst_hls_demux_process_manifest (GstAdaptiveDemux * demux, GstBuffer * buf) { GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux); gchar *playlist = NULL; if (hlsdemux->client) gst_m3u8_client_free (hlsdemux->client); hlsdemux->client = gst_m3u8_client_new (demux->manifest_uri, demux->manifest_base_uri); GST_INFO_OBJECT (demux, "Changed location: %s (base uri: %s)", demux->manifest_uri, GST_STR_NULL (demux->manifest_base_uri)); playlist = gst_hls_src_buf_to_utf8_playlist (buf); if (playlist == NULL) { GST_WARNING_OBJECT (demux, "Error validating first playlist."); return FALSE; } else if (!gst_m3u8_client_update (hlsdemux->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 this playlist is a variant playlist, select the first one * and update it */ if (gst_m3u8_client_has_variant_playlist (hlsdemux->client)) { GstM3U8 *child = NULL; GError *err = NULL; if (demux->connection_speed == 0) { GST_M3U8_CLIENT_LOCK (hlsdemux->client); child = hlsdemux->client->main->current_variant->data; GST_M3U8_CLIENT_UNLOCK (hlsdemux->client); } else { GList *tmp = gst_m3u8_client_get_playlist_for_bitrate (hlsdemux->client, demux->connection_speed); GST_M3U8_CLIENT_LOCK (hlsdemux->client); hlsdemux->client->main->current_variant = tmp; GST_M3U8_CLIENT_UNLOCK (hlsdemux->client); child = GST_M3U8 (tmp->data); } gst_m3u8_client_set_current (hlsdemux->client, child); if (!gst_hls_demux_update_playlist (hlsdemux, FALSE, &err)) { GST_ELEMENT_ERROR_FROM_ERROR (demux, "Could not fetch the child playlist", err); return FALSE; } } return gst_hls_demux_setup_streams (demux); }
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_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; }