mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-04-26 06:54:49 +00:00
hlsdemux2/playlist-loader: Implement more features
Implement limited retries on download errors before reporting it, and remember permanent redirects, with LL-HLS directives removed. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
This commit is contained in:
parent
9ae3978c72
commit
fe41db92db
1 changed files with 111 additions and 15 deletions
|
@ -34,6 +34,8 @@
|
||||||
GST_DEBUG_CATEGORY_EXTERN (gst_hls_demux2_debug);
|
GST_DEBUG_CATEGORY_EXTERN (gst_hls_demux2_debug);
|
||||||
#define GST_CAT_DEFAULT gst_hls_demux2_debug
|
#define GST_CAT_DEFAULT gst_hls_demux2_debug
|
||||||
|
|
||||||
|
#define MAX_DOWNLOAD_ERROR_COUNT 3
|
||||||
|
|
||||||
typedef enum _PlaylistLoaderState PlaylistLoaderState;
|
typedef enum _PlaylistLoaderState PlaylistLoaderState;
|
||||||
|
|
||||||
enum _PlaylistLoaderState
|
enum _PlaylistLoaderState
|
||||||
|
@ -68,6 +70,10 @@ struct _GstHLSDemuxPlaylistLoaderPrivate
|
||||||
gboolean delta_merge_failed;
|
gboolean delta_merge_failed;
|
||||||
gchar *current_playlist_uri;
|
gchar *current_playlist_uri;
|
||||||
GstHLSMediaPlaylist *current_playlist;
|
GstHLSMediaPlaylist *current_playlist;
|
||||||
|
|
||||||
|
gchar *current_playlist_redirect_uri;
|
||||||
|
|
||||||
|
guint download_error_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define gst_hls_demux_playlist_loader_parent_class parent_class
|
#define gst_hls_demux_playlist_loader_parent_class parent_class
|
||||||
|
@ -153,6 +159,7 @@ gst_hls_demux_playlist_loader_finalize (GObject * object)
|
||||||
if (priv->current_playlist)
|
if (priv->current_playlist)
|
||||||
gst_hls_media_playlist_unref (priv->current_playlist);
|
gst_hls_media_playlist_unref (priv->current_playlist);
|
||||||
g_free (priv->current_playlist_uri);
|
g_free (priv->current_playlist_uri);
|
||||||
|
g_free (priv->current_playlist_redirect_uri);
|
||||||
|
|
||||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||||
}
|
}
|
||||||
|
@ -187,8 +194,10 @@ gst_hls_demux_playlist_loader_start (GstHLSDemuxPlaylistLoader * pl)
|
||||||
{
|
{
|
||||||
GstHLSDemuxPlaylistLoaderPrivate *priv = pl->priv;
|
GstHLSDemuxPlaylistLoaderPrivate *priv = pl->priv;
|
||||||
|
|
||||||
if (priv->state != PLAYLIST_LOADER_STATE_STOPPED)
|
if (priv->state != PLAYLIST_LOADER_STATE_STOPPED) {
|
||||||
|
GST_LOG_OBJECT (pl, "Already started - state %d", priv->state);
|
||||||
return; /* Already active */
|
return; /* Already active */
|
||||||
|
}
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (pl, "Starting playlist loading");
|
GST_DEBUG_OBJECT (pl, "Starting playlist loading");
|
||||||
priv->state = PLAYLIST_LOADER_STATE_STARTING;
|
priv->state = PLAYLIST_LOADER_STATE_STARTING;
|
||||||
|
@ -217,6 +226,8 @@ gst_hls_demux_playlist_loader_stop (GstHLSDemuxPlaylistLoader * pl)
|
||||||
download_request_unref (priv->download_request);
|
download_request_unref (priv->download_request);
|
||||||
priv->download_request = NULL;
|
priv->download_request = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
priv->state = PLAYLIST_LOADER_STATE_STOPPED;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -292,6 +303,28 @@ struct PlaylistDownloadParams
|
||||||
#define HLS_MSN_QUERY_KEY "_HLS_msn"
|
#define HLS_MSN_QUERY_KEY "_HLS_msn"
|
||||||
#define HLS_PART_QUERY_KEY "_HLS_part"
|
#define HLS_PART_QUERY_KEY "_HLS_part"
|
||||||
|
|
||||||
|
static gchar *
|
||||||
|
remove_HLS_directives_from_uri (const gchar * playlist_uri)
|
||||||
|
{
|
||||||
|
|
||||||
|
/* Catch the simple case and keep NULL as NULL */
|
||||||
|
if (playlist_uri == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
GstUri *uri = gst_uri_from_string (playlist_uri);
|
||||||
|
gst_uri_remove_query_key (uri, HLS_SKIP_QUERY_KEY);
|
||||||
|
gst_uri_remove_query_key (uri, HLS_MSN_QUERY_KEY);
|
||||||
|
gst_uri_remove_query_key (uri, HLS_PART_QUERY_KEY);
|
||||||
|
|
||||||
|
GList *keys = gst_uri_get_query_keys (uri);
|
||||||
|
if (keys)
|
||||||
|
keys = g_list_sort (keys, (GCompareFunc) g_strcmp0);
|
||||||
|
gchar *out_uri = gst_uri_to_string_with_keys (uri, keys);
|
||||||
|
gst_uri_unref (uri);
|
||||||
|
|
||||||
|
return out_uri;
|
||||||
|
}
|
||||||
|
|
||||||
static gchar *
|
static gchar *
|
||||||
apply_directives_to_uri (GstHLSDemuxPlaylistLoader * pl,
|
apply_directives_to_uri (GstHLSDemuxPlaylistLoader * pl,
|
||||||
const gchar * playlist_uri, struct PlaylistDownloadParams *dl_params)
|
const gchar * playlist_uri, struct PlaylistDownloadParams *dl_params)
|
||||||
|
@ -328,6 +361,9 @@ apply_directives_to_uri (GstHLSDemuxPlaylistLoader * pl,
|
||||||
} else {
|
} else {
|
||||||
gst_uri_remove_query_key (uri, HLS_PART_QUERY_KEY);
|
gst_uri_remove_query_key (uri, HLS_PART_QUERY_KEY);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
gst_uri_remove_query_key (uri, HLS_MSN_QUERY_KEY);
|
||||||
|
gst_uri_remove_query_key (uri, HLS_PART_QUERY_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Produce the resulting URI with query arguments in UTF-8 order
|
/* Produce the resulting URI with query arguments in UTF-8 order
|
||||||
|
@ -392,6 +428,25 @@ get_playlist_reload_interval (GstHLSDemuxPlaylistLoader * pl,
|
||||||
return target_duration;
|
return target_duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_download_error (GstHLSDemuxPlaylistLoader * pl,
|
||||||
|
GstHLSDemuxPlaylistLoaderPrivate * priv)
|
||||||
|
{
|
||||||
|
if (++priv->download_error_count > MAX_DOWNLOAD_ERROR_COUNT) {
|
||||||
|
GST_DEBUG_OBJECT (pl,
|
||||||
|
"Reached %d download failures on URI %s. Reporting the failure",
|
||||||
|
priv->download_error_count, priv->loading_playlist_uri);
|
||||||
|
if (priv->error_cb)
|
||||||
|
priv->error_cb (pl, priv->loading_playlist_uri, priv->userdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The error callback may have provided a new playlist to load, which
|
||||||
|
* will have scheduled a state update immediately. In that case,
|
||||||
|
* don't trigger our own delayed retry */
|
||||||
|
if (priv->pending_cb_id == 0)
|
||||||
|
schedule_next_playlist_load (pl, priv, 100 * GST_MSECOND);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_download_complete (DownloadRequest * download, DownloadRequestState state,
|
on_download_complete (DownloadRequest * download, DownloadRequestState state,
|
||||||
GstHLSDemuxPlaylistLoader * pl)
|
GstHLSDemuxPlaylistLoader * pl)
|
||||||
|
@ -423,14 +478,21 @@ on_download_complete (DownloadRequest * download, DownloadRequestState state,
|
||||||
GstHLSMediaPlaylist *playlist = NULL;
|
GstHLSMediaPlaylist *playlist = NULL;
|
||||||
gchar *base_uri, *uri;
|
gchar *base_uri, *uri;
|
||||||
|
|
||||||
if (download->redirect_permanent && download->redirect_uri) {
|
if (download->redirect_uri) {
|
||||||
uri = g_strdup (download->redirect_uri);
|
/* Strip HLS request params from the playlist and redirect URI */
|
||||||
|
uri = remove_HLS_directives_from_uri (download->redirect_uri);
|
||||||
base_uri = NULL;
|
base_uri = NULL;
|
||||||
|
|
||||||
|
if (download->redirect_permanent) {
|
||||||
|
/* Store this redirect as the future request URI for this playlist */
|
||||||
|
g_free (priv->current_playlist_redirect_uri);
|
||||||
|
priv->current_playlist_redirect_uri = g_strdup (uri);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
uri = g_strdup (download->uri);
|
/* Strip HLS request params from the playlist and redirect URI */
|
||||||
base_uri = g_strdup (download->redirect_uri);
|
uri = remove_HLS_directives_from_uri (download->uri);
|
||||||
|
base_uri = remove_HLS_directives_from_uri (download->redirect_uri);
|
||||||
}
|
}
|
||||||
/* FIXME: Strip HLS request params from the playlist and redirect URI */
|
|
||||||
|
|
||||||
/* Calculate the newest time we know this playlist was valid to store on the HLS Media Playlist */
|
/* Calculate the newest time we know this playlist was valid to store on the HLS Media Playlist */
|
||||||
GstClockTime playlist_ts =
|
GstClockTime playlist_ts =
|
||||||
|
@ -447,7 +509,7 @@ on_download_complete (DownloadRequest * download, DownloadRequestState state,
|
||||||
|
|
||||||
if (playlist_data == NULL) {
|
if (playlist_data == NULL) {
|
||||||
GST_WARNING_OBJECT (pl, "Couldn't validate playlist encoding");
|
GST_WARNING_OBJECT (pl, "Couldn't validate playlist encoding");
|
||||||
goto delay_retry_out;
|
goto error_retry_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
GstHLSMediaPlaylist *current_playlist = priv->current_playlist;
|
GstHLSMediaPlaylist *current_playlist = priv->current_playlist;
|
||||||
|
@ -455,6 +517,27 @@ on_download_complete (DownloadRequest * download, DownloadRequestState state,
|
||||||
|| g_strcmp0 (priv->loading_playlist_uri,
|
|| g_strcmp0 (priv->loading_playlist_uri,
|
||||||
priv->current_playlist_uri) != 0);
|
priv->current_playlist_uri) != 0);
|
||||||
|
|
||||||
|
gboolean always_reload = FALSE;
|
||||||
|
#if 0
|
||||||
|
/* Test code the reports a playlist load error if we load
|
||||||
|
the same playlist 2 times in a row and the URI contains "video.m3u8"
|
||||||
|
https://playertest.longtailvideo.com/adaptive/elephants_dream_v4/redundant.m3u8
|
||||||
|
works as a test URL
|
||||||
|
*/
|
||||||
|
static gint playlist_load_counter = 0;
|
||||||
|
if (playlist_uri_change)
|
||||||
|
playlist_load_counter = 0;
|
||||||
|
else if (strstr (priv->loading_playlist_uri, "video.m3u8") != NULL) {
|
||||||
|
playlist_load_counter++;
|
||||||
|
if (playlist_load_counter > 1) {
|
||||||
|
g_print ("Triggering playlist failure for %s\n",
|
||||||
|
priv->loading_playlist_uri);
|
||||||
|
goto error_retry_out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
always_reload = TRUE;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!playlist_uri_change && current_playlist
|
if (!playlist_uri_change && current_playlist
|
||||||
&& gst_hls_media_playlist_has_same_data (current_playlist,
|
&& gst_hls_media_playlist_has_same_data (current_playlist,
|
||||||
playlist_data)) {
|
playlist_data)) {
|
||||||
|
@ -468,6 +551,7 @@ on_download_complete (DownloadRequest * download, DownloadRequestState state,
|
||||||
base_uri);
|
base_uri);
|
||||||
if (!playlist) {
|
if (!playlist) {
|
||||||
GST_WARNING_OBJECT (pl, "Couldn't parse playlist");
|
GST_WARNING_OBJECT (pl, "Couldn't parse playlist");
|
||||||
|
goto error_retry_out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -496,6 +580,9 @@ on_download_complete (DownloadRequest * download, DownloadRequestState state,
|
||||||
priv->current_playlist_uri = g_strdup (priv->loading_playlist_uri);
|
priv->current_playlist_uri = g_strdup (priv->loading_playlist_uri);
|
||||||
priv->current_playlist = playlist;
|
priv->current_playlist = playlist;
|
||||||
|
|
||||||
|
/* Successfully loaded the playlist. Forget any prior failures */
|
||||||
|
priv->download_error_count = 0;
|
||||||
|
|
||||||
if (priv->success_cb)
|
if (priv->success_cb)
|
||||||
priv->success_cb (pl, priv->current_playlist_uri, priv->current_playlist,
|
priv->success_cb (pl, priv->current_playlist_uri, priv->current_playlist,
|
||||||
priv->userdata);
|
priv->userdata);
|
||||||
|
@ -503,7 +590,7 @@ on_download_complete (DownloadRequest * download, DownloadRequestState state,
|
||||||
g_free (priv->loading_playlist_uri);
|
g_free (priv->loading_playlist_uri);
|
||||||
priv->loading_playlist_uri = NULL;
|
priv->loading_playlist_uri = NULL;
|
||||||
|
|
||||||
if (gst_hls_media_playlist_is_live (playlist)) {
|
if (gst_hls_media_playlist_is_live (playlist) || always_reload) {
|
||||||
/* Schedule the next playlist load. If we can do a blocking load,
|
/* Schedule the next playlist load. If we can do a blocking load,
|
||||||
* do it immediately, otherwise delayed */
|
* do it immediately, otherwise delayed */
|
||||||
if (playlist->can_block_reload) {
|
if (playlist->can_block_reload) {
|
||||||
|
@ -523,9 +610,9 @@ out:
|
||||||
g_free (base_uri);
|
g_free (base_uri);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
delay_retry_out:
|
error_retry_out:
|
||||||
/* Got invalid playlist data, retry soon */
|
/* Got invalid playlist data, retry soon or error out */
|
||||||
schedule_next_playlist_load (pl, priv, 100 * GST_MSECOND);
|
handle_download_error (pl, priv);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -544,10 +631,7 @@ on_download_error (DownloadRequest * download, DownloadRequestState state,
|
||||||
"Couldn't retrieve playlist, got HTTP status code %d",
|
"Couldn't retrieve playlist, got HTTP status code %d",
|
||||||
download->status_code);
|
download->status_code);
|
||||||
|
|
||||||
if (priv->error_cb)
|
handle_download_error (pl, priv);
|
||||||
priv->error_cb (pl, priv->loading_playlist_uri, priv->userdata);
|
|
||||||
|
|
||||||
start_playlist_download (pl, priv);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -576,6 +660,12 @@ start_playlist_download (GstHLSDemuxPlaylistLoader * pl,
|
||||||
if (!playlist_uri_change) {
|
if (!playlist_uri_change) {
|
||||||
GST_LOG_OBJECT (pl, "Updating the playlist");
|
GST_LOG_OBJECT (pl, "Updating the playlist");
|
||||||
|
|
||||||
|
/* If we have a redirect stored for this playlist URI, use that instead */
|
||||||
|
if (priv->current_playlist_redirect_uri) {
|
||||||
|
orig_uri = priv->current_playlist_redirect_uri;
|
||||||
|
GST_LOG_OBJECT (pl, "Using redirected playlist URI %s", orig_uri);
|
||||||
|
}
|
||||||
|
|
||||||
/* See if we can do a delta playlist update (if the playlist age is less than
|
/* See if we can do a delta playlist update (if the playlist age is less than
|
||||||
* one half of the Skip Boundary */
|
* one half of the Skip Boundary */
|
||||||
if (GST_CLOCK_TIME_IS_VALID (current_playlist->skip_boundary) && allow_skip) {
|
if (GST_CLOCK_TIME_IS_VALID (current_playlist->skip_boundary) && allow_skip) {
|
||||||
|
@ -595,6 +685,12 @@ start_playlist_download (GstHLSDemuxPlaylistLoader * pl,
|
||||||
GST_DEBUG_OBJECT (pl,
|
GST_DEBUG_OBJECT (pl,
|
||||||
"Doing full playlist update after failed delta request");
|
"Doing full playlist update after failed delta request");
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
/* This is the first time loading this playlist URI, clear the error counter
|
||||||
|
* and redirect URI */
|
||||||
|
priv->download_error_count = 0;
|
||||||
|
g_free (priv->current_playlist_redirect_uri);
|
||||||
|
priv->current_playlist_redirect_uri = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Blocking playlist reload check */
|
/* Blocking playlist reload check */
|
||||||
|
|
Loading…
Reference in a new issue