mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-24 09:10:36 +00:00
hlsdemux: Reload the variant playlist if refreshing a playlist or downloading a fragment fails
This can happen if the playlists have moved due to the variant playlist now being redirected to another target. This currently only works as long as the referenced playlists don't change in relation to the variant playlist, and the new location is purely due to a new path triggered by a new redirection target of the variant playlist, or a new redirection target of the playlist itself. https://bugzilla.gnome.org/show_bug.cgi?id=731164
This commit is contained in:
parent
e259557c5a
commit
babd8969f2
3 changed files with 241 additions and 13 deletions
|
@ -1490,15 +1490,65 @@ gst_hls_demux_update_playlist (GstHLSDemux * demux, gboolean update,
|
||||||
GstFragment *download;
|
GstFragment *download;
|
||||||
GstBuffer *buf;
|
GstBuffer *buf;
|
||||||
gchar *playlist;
|
gchar *playlist;
|
||||||
gboolean updated = FALSE;
|
gboolean main_checked = FALSE, updated = FALSE;
|
||||||
const gchar *uri = gst_m3u8_client_get_current_uri (demux->client);
|
const gchar *uri;
|
||||||
|
|
||||||
|
retry:
|
||||||
|
uri = gst_m3u8_client_get_current_uri (demux->client);
|
||||||
download =
|
download =
|
||||||
gst_uri_downloader_fetch_uri (demux->downloader, uri,
|
gst_uri_downloader_fetch_uri (demux->downloader, uri,
|
||||||
demux->client->main ? demux->client->main->uri : NULL, TRUE, TRUE, TRUE,
|
demux->client->main ? demux->client->main->uri : NULL, TRUE, TRUE, TRUE,
|
||||||
err);
|
err);
|
||||||
if (download == NULL)
|
if (download == NULL) {
|
||||||
return FALSE;
|
if (update && !main_checked
|
||||||
|
&& gst_m3u8_client_has_variant_playlist (demux->client)
|
||||||
|
&& demux->client->main) {
|
||||||
|
GError *err2 = NULL;
|
||||||
|
GST_INFO_OBJECT (demux,
|
||||||
|
"Updating playlist %s failed, attempt to refresh variant playlist %s",
|
||||||
|
uri, demux->client->main->uri);
|
||||||
|
download =
|
||||||
|
gst_uri_downloader_fetch_uri (demux->downloader,
|
||||||
|
demux->client->main->uri, NULL, TRUE, TRUE, TRUE, &err2);
|
||||||
|
g_clear_error (&err2);
|
||||||
|
if (download != NULL) {
|
||||||
|
gchar *base_uri;
|
||||||
|
|
||||||
|
buf = gst_fragment_get_buffer (download);
|
||||||
|
playlist = gst_hls_src_buf_to_utf8_playlist (buf);
|
||||||
|
|
||||||
|
if (playlist == NULL) {
|
||||||
|
GST_WARNING_OBJECT (demux,
|
||||||
|
"Failed to validate variant playlist encoding");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_unref (download);
|
||||||
|
|
||||||
|
g_clear_error (err);
|
||||||
|
main_checked = TRUE;
|
||||||
|
goto retry;
|
||||||
|
} else {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Set the base URI of the playlist to the redirect target if any */
|
/* Set the base URI of the playlist to the redirect target if any */
|
||||||
GST_M3U8_CLIENT_LOCK (demux->client);
|
GST_M3U8_CLIENT_LOCK (demux->client);
|
||||||
|
@ -1540,8 +1590,8 @@ gst_hls_demux_update_playlist (GstHLSDemux * demux, gboolean update,
|
||||||
|
|
||||||
GST_M3U8_CLIENT_LOCK (demux->client);
|
GST_M3U8_CLIENT_LOCK (demux->client);
|
||||||
last_sequence =
|
last_sequence =
|
||||||
GST_M3U8_MEDIA_FILE (g_list_last (demux->client->current->files)->
|
GST_M3U8_MEDIA_FILE (g_list_last (demux->client->current->
|
||||||
data)->sequence;
|
files)->data)->sequence;
|
||||||
|
|
||||||
if (demux->client->sequence >= last_sequence - 3) {
|
if (demux->client->sequence >= last_sequence - 3) {
|
||||||
GST_DEBUG_OBJECT (demux, "Sequence is beyond playlist. Moving back to %u",
|
GST_DEBUG_OBJECT (demux, "Sequence is beyond playlist. Moving back to %u",
|
||||||
|
@ -1642,8 +1692,8 @@ retry_failover_protection:
|
||||||
gst_m3u8_client_set_current (demux->client, previous_variant->data);
|
gst_m3u8_client_set_current (demux->client, previous_variant->data);
|
||||||
/* Try a lower bitrate (or stop if we just tried the lowest) */
|
/* Try a lower bitrate (or stop if we just tried the lowest) */
|
||||||
if (GST_M3U8 (previous_variant->data)->iframe && new_bandwidth ==
|
if (GST_M3U8 (previous_variant->data)->iframe && new_bandwidth ==
|
||||||
GST_M3U8 (g_list_first (demux->client->main->iframe_lists)->
|
GST_M3U8 (g_list_first (demux->client->main->iframe_lists)->data)->
|
||||||
data)->bandwidth)
|
bandwidth)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
else if (!GST_M3U8 (previous_variant->data)->iframe && new_bandwidth ==
|
else if (!GST_M3U8 (previous_variant->data)->iframe && new_bandwidth ==
|
||||||
GST_M3U8 (g_list_first (demux->client->main->lists)->data)->bandwidth)
|
GST_M3U8 (g_list_first (demux->client->main->lists)->data)->bandwidth)
|
||||||
|
|
185
ext/hls/m3u8.c
185
ext/hls/m3u8.c
|
@ -50,7 +50,7 @@ gst_m3u8_new (void)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_m3u8_set_uri (GstM3U8 * self, gchar * uri, gchar * base_uri)
|
gst_m3u8_set_uri (GstM3U8 * self, gchar * uri, gchar * base_uri, gchar * name)
|
||||||
{
|
{
|
||||||
g_return_if_fail (self != NULL);
|
g_return_if_fail (self != NULL);
|
||||||
|
|
||||||
|
@ -59,6 +59,9 @@ gst_m3u8_set_uri (GstM3U8 * self, gchar * uri, gchar * base_uri)
|
||||||
|
|
||||||
g_free (self->base_uri);
|
g_free (self->base_uri);
|
||||||
self->base_uri = base_uri;
|
self->base_uri = base_uri;
|
||||||
|
|
||||||
|
g_free (self->name);
|
||||||
|
self->name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -68,6 +71,7 @@ gst_m3u8_free (GstM3U8 * self)
|
||||||
|
|
||||||
g_free (self->uri);
|
g_free (self->uri);
|
||||||
g_free (self->base_uri);
|
g_free (self->base_uri);
|
||||||
|
g_free (self->name);
|
||||||
g_free (self->codecs);
|
g_free (self->codecs);
|
||||||
g_free (self->key);
|
g_free (self->key);
|
||||||
|
|
||||||
|
@ -109,6 +113,85 @@ gst_m3u8_media_file_free (GstM3U8MediaFile * self)
|
||||||
g_free (self);
|
g_free (self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GstM3U8MediaFile *
|
||||||
|
gst_m3u8_media_file_copy (const GstM3U8MediaFile * self, gpointer user_data)
|
||||||
|
{
|
||||||
|
g_return_if_fail (self != NULL);
|
||||||
|
|
||||||
|
return gst_m3u8_media_file_new (g_strdup (self->uri), g_strdup (self->title),
|
||||||
|
self->duration, self->sequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstM3U8 *
|
||||||
|
_m3u8_copy (const GstM3U8 * self, GstM3U8 * parent)
|
||||||
|
{
|
||||||
|
GstM3U8 *dup;
|
||||||
|
|
||||||
|
g_return_if_fail (self != NULL);
|
||||||
|
|
||||||
|
dup = gst_m3u8_new ();
|
||||||
|
dup->uri = g_strdup (self->uri);
|
||||||
|
dup->base_uri = g_strdup (self->base_uri);
|
||||||
|
dup->name = g_strdup (self->name);
|
||||||
|
dup->endlist = self->endlist;
|
||||||
|
dup->version = self->version;
|
||||||
|
dup->targetduration = self->targetduration;
|
||||||
|
dup->allowcache = self->allowcache;
|
||||||
|
dup->key = g_strdup (self->key);
|
||||||
|
dup->bandwidth = self->bandwidth;
|
||||||
|
dup->program_id = self->program_id;
|
||||||
|
dup->codecs = g_strdup (self->codecs);
|
||||||
|
dup->width = self->width;
|
||||||
|
dup->height = self->height;
|
||||||
|
dup->iframe = self->iframe;
|
||||||
|
dup->files =
|
||||||
|
g_list_copy_deep (self->files, (GCopyFunc) gst_m3u8_media_file_copy,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
/* private */
|
||||||
|
dup->last_data = g_strdup (self->last_data);
|
||||||
|
dup->lists = g_list_copy_deep (self->lists, (GCopyFunc) _m3u8_copy, dup);
|
||||||
|
dup->iframe_lists =
|
||||||
|
g_list_copy_deep (self->iframe_lists, (GCopyFunc) _m3u8_copy, dup);
|
||||||
|
/* NOTE: current_variant will get set in gst_m3u8_copy () */
|
||||||
|
dup->parent = parent;
|
||||||
|
dup->mediasequence = self->mediasequence;
|
||||||
|
return dup;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstM3U8 *
|
||||||
|
gst_m3u8_copy (const GstM3U8 * self)
|
||||||
|
{
|
||||||
|
GList *entry;
|
||||||
|
guint n;
|
||||||
|
|
||||||
|
GstM3U8 *dup = _m3u8_copy (self, NULL);
|
||||||
|
|
||||||
|
if (self->current_variant != NULL) {
|
||||||
|
for (n = 0, entry = self->lists; entry; entry = entry->next, n++) {
|
||||||
|
if (entry == self->current_variant) {
|
||||||
|
dup->current_variant = g_list_nth (dup->lists, n);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dup->current_variant) {
|
||||||
|
for (n = 0, entry = self->iframe_lists; entry; entry = entry->next, n++) {
|
||||||
|
if (entry == self->current_variant) {
|
||||||
|
dup->current_variant = g_list_nth (dup->iframe_lists, n);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dup->current_variant) {
|
||||||
|
GST_ERROR ("Failed to determine current playlist");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dup;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
int_from_string (gchar * ptr, gchar ** endptr, gint * val)
|
int_from_string (gchar * ptr, gchar ** endptr, gint * val)
|
||||||
{
|
{
|
||||||
|
@ -320,6 +403,7 @@ gst_m3u8_update (GstM3U8 * self, gchar * data, gboolean * updated)
|
||||||
*r = '\0';
|
*r = '\0';
|
||||||
|
|
||||||
if (data[0] != '#' && data[0] != '\0') {
|
if (data[0] != '#' && data[0] != '\0') {
|
||||||
|
gchar *name = data;
|
||||||
if (duration <= 0 && list == NULL) {
|
if (duration <= 0 && list == NULL) {
|
||||||
GST_LOG ("%s: got line without EXTINF or EXTSTREAMINF, dropping", data);
|
GST_LOG ("%s: got line without EXTINF or EXTSTREAMINF, dropping", data);
|
||||||
goto next_line;
|
goto next_line;
|
||||||
|
@ -336,7 +420,7 @@ gst_m3u8_update (GstM3U8 * self, gchar * data, gboolean * updated)
|
||||||
gst_m3u8_free (list);
|
gst_m3u8_free (list);
|
||||||
g_free (data);
|
g_free (data);
|
||||||
} else {
|
} else {
|
||||||
gst_m3u8_set_uri (list, data, NULL);
|
gst_m3u8_set_uri (list, data, NULL, g_strdup (name));
|
||||||
self->lists = g_list_append (self->lists, list);
|
self->lists = g_list_append (self->lists, list);
|
||||||
}
|
}
|
||||||
list = NULL;
|
list = NULL;
|
||||||
|
@ -397,6 +481,7 @@ gst_m3u8_update (GstM3U8 * self, gchar * data, gboolean * updated)
|
||||||
GstM3U8 *new_list;
|
GstM3U8 *new_list;
|
||||||
|
|
||||||
new_list = gst_m3u8_new ();
|
new_list = gst_m3u8_new ();
|
||||||
|
new_list->parent = self;
|
||||||
new_list->iframe = iframe;
|
new_list->iframe = iframe;
|
||||||
data = data + (iframe ? 26 : 18);
|
data = data + (iframe ? 26 : 18);
|
||||||
while (data && parse_attributes (&data, &a, &v)) {
|
while (data && parse_attributes (&data, &a, &v)) {
|
||||||
|
@ -420,6 +505,7 @@ gst_m3u8_update (GstM3U8 * self, gchar * data, gboolean * updated)
|
||||||
GST_WARNING ("Error while reading RESOLUTION height");
|
GST_WARNING ("Error while reading RESOLUTION height");
|
||||||
}
|
}
|
||||||
} else if (iframe && g_str_equal (a, "URI")) {
|
} else if (iframe && g_str_equal (a, "URI")) {
|
||||||
|
gchar *name;
|
||||||
gchar *uri = g_strdup (v);
|
gchar *uri = g_strdup (v);
|
||||||
gchar *urip = uri;
|
gchar *urip = uri;
|
||||||
int len = strlen (uri);
|
int len = strlen (uri);
|
||||||
|
@ -430,12 +516,15 @@ gst_m3u8_update (GstM3U8 * self, gchar * data, gboolean * updated)
|
||||||
if (uri[0] == '"')
|
if (uri[0] == '"')
|
||||||
uri += 1;
|
uri += 1;
|
||||||
|
|
||||||
|
name = g_strdup (uri);
|
||||||
uri = uri_join (self->base_uri ? self->base_uri : self->uri, uri);
|
uri = uri_join (self->base_uri ? self->base_uri : self->uri, uri);
|
||||||
g_free (urip);
|
g_free (urip);
|
||||||
|
|
||||||
if (uri == NULL)
|
if (uri == NULL) {
|
||||||
|
g_free (name);
|
||||||
continue;
|
continue;
|
||||||
gst_m3u8_set_uri (new_list, uri, NULL);
|
}
|
||||||
|
gst_m3u8_set_uri (new_list, uri, NULL, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -620,7 +709,7 @@ gst_m3u8_client_new (const gchar * uri, const gchar * base_uri)
|
||||||
client->sequence_position = 0;
|
client->sequence_position = 0;
|
||||||
client->update_failed_count = 0;
|
client->update_failed_count = 0;
|
||||||
g_mutex_init (&client->lock);
|
g_mutex_init (&client->lock);
|
||||||
gst_m3u8_set_uri (client->main, g_strdup (uri), g_strdup (base_uri));
|
gst_m3u8_set_uri (client->main, g_strdup (uri), g_strdup (base_uri), NULL);
|
||||||
|
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
@ -695,6 +784,92 @@ out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gint
|
||||||
|
_find_m3u8_list_match (const GstM3U8 * a, const GstM3U8 * b)
|
||||||
|
{
|
||||||
|
if (g_strcmp0 (a->name, b->name) == 0 &&
|
||||||
|
a->bandwidth == b->bandwidth &&
|
||||||
|
a->program_id == b->program_id &&
|
||||||
|
g_strcmp0 (a->codecs, b->codecs) == 0 &&
|
||||||
|
a->width == b->width &&
|
||||||
|
a->height == b->height && a->iframe == b->iframe) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gst_m3u8_client_update_variant_playlist (GstM3U8Client * self, gchar * data,
|
||||||
|
const gchar * uri, const gchar * base_uri)
|
||||||
|
{
|
||||||
|
gboolean ret = FALSE;
|
||||||
|
GList *list_entry, *unmatched_lists;
|
||||||
|
GstM3U8Client *new_client;
|
||||||
|
GstM3U8 *old;
|
||||||
|
|
||||||
|
g_return_val_if_fail (self != NULL, FALSE);
|
||||||
|
|
||||||
|
new_client = gst_m3u8_client_new (uri, base_uri);
|
||||||
|
if (gst_m3u8_client_update (new_client, data)) {
|
||||||
|
if (!new_client->main->lists) {
|
||||||
|
GST_ERROR
|
||||||
|
("Cannot update variant playlist: New playlist is not a variant playlist");
|
||||||
|
gst_m3u8_client_free (new_client);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_M3U8_CLIENT_LOCK (self);
|
||||||
|
|
||||||
|
if (!self->main->lists) {
|
||||||
|
GST_ERROR
|
||||||
|
("Cannot update variant playlist: Current playlist is not a variant playlist");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now see if the variant playlist still has the same lists */
|
||||||
|
unmatched_lists = g_list_copy (self->main->lists);
|
||||||
|
for (list_entry = new_client->main->lists; list_entry;
|
||||||
|
list_entry = list_entry->next) {
|
||||||
|
GList *match = g_list_find_custom (unmatched_lists, list_entry->data,
|
||||||
|
(GCompareFunc) _find_m3u8_list_match);
|
||||||
|
if (match)
|
||||||
|
unmatched_lists = g_list_remove_link (unmatched_lists, match);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unmatched_lists != NULL) {
|
||||||
|
g_list_free (unmatched_lists);
|
||||||
|
|
||||||
|
/* We should attempt to handle the case where playlists are dropped/replaced,
|
||||||
|
* and possibly switch over to a comparable (not neccessarily identical)
|
||||||
|
* playlist.
|
||||||
|
*/
|
||||||
|
GST_FIXME
|
||||||
|
("Cannot update variant playlist, unable to match all playlists");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Switch out the variant playlist */
|
||||||
|
old = self->main;
|
||||||
|
|
||||||
|
self->main = gst_m3u8_copy (new_client->main);
|
||||||
|
if (self->main->lists)
|
||||||
|
self->current = self->main->current_variant->data;
|
||||||
|
else
|
||||||
|
self->current = self->main;
|
||||||
|
|
||||||
|
gst_m3u8_free (old);
|
||||||
|
|
||||||
|
ret = TRUE;
|
||||||
|
|
||||||
|
out:
|
||||||
|
GST_M3U8_CLIENT_UNLOCK (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_m3u8_client_free (new_client);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
_find_current (GstM3U8MediaFile * file, GstM3U8Client * client)
|
_find_current (GstM3U8MediaFile * file, GstM3U8Client * client)
|
||||||
{
|
{
|
||||||
|
|
|
@ -40,6 +40,8 @@ struct _GstM3U8
|
||||||
gchar *uri; /* actually downloaded URI */
|
gchar *uri; /* actually downloaded URI */
|
||||||
gchar *base_uri; /* URI to use as base for resolving relative URIs.
|
gchar *base_uri; /* URI to use as base for resolving relative URIs.
|
||||||
* This will be different to uri in case of redirects */
|
* This will be different to uri in case of redirects */
|
||||||
|
gchar *name; /* This will be the "name" of the playlist, the original
|
||||||
|
* relative/absolute uri in a variant playlist */
|
||||||
|
|
||||||
gboolean endlist; /* if ENDLIST has been reached */
|
gboolean endlist; /* if ENDLIST has been reached */
|
||||||
gint version; /* last EXT-X-VERSION */
|
gint version; /* last EXT-X-VERSION */
|
||||||
|
@ -90,6 +92,7 @@ struct _GstM3U8Client
|
||||||
GstM3U8Client *gst_m3u8_client_new (const gchar * uri, const gchar * base_uri);
|
GstM3U8Client *gst_m3u8_client_new (const gchar * uri, const gchar * base_uri);
|
||||||
void gst_m3u8_client_free (GstM3U8Client * client);
|
void gst_m3u8_client_free (GstM3U8Client * client);
|
||||||
gboolean gst_m3u8_client_update (GstM3U8Client * client, gchar * data);
|
gboolean gst_m3u8_client_update (GstM3U8Client * client, gchar * data);
|
||||||
|
gboolean gst_m3u8_client_update_variant_playlist (GstM3U8Client * client, gchar * data, const gchar * uri, const gchar * base_uri);
|
||||||
void gst_m3u8_client_set_current (GstM3U8Client * client, GstM3U8 * m3u8);
|
void gst_m3u8_client_set_current (GstM3U8Client * client, GstM3U8 * m3u8);
|
||||||
gboolean gst_m3u8_client_get_next_fragment (GstM3U8Client * client,
|
gboolean gst_m3u8_client_get_next_fragment (GstM3U8Client * client,
|
||||||
gboolean * discontinuity, const gchar ** uri, GstClockTime * duration,
|
gboolean * discontinuity, const gchar ** uri, GstClockTime * duration,
|
||||||
|
|
Loading…
Reference in a new issue