summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
authorThomas Bluemel <tbluemel@control4.com>2014-05-30 16:34:18 -0600
committerSebastian Dröge <sebastian@centricular.com>2014-06-06 13:02:47 +0300
commitbabd8969f29966e471c74f6411e6a15fec80f810 (patch)
treec0ac79add540648c5bc8678133c8739ad679bba6 /ext
parente259557c5a203dbc6fb20215dacf916687bf5229 (diff)
downloadgstreamer-plugins-bad-babd8969f29966e471c74f6411e6a15fec80f810.tar.gz
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
Diffstat (limited to 'ext')
-rw-r--r--ext/hls/gsthlsdemux.c66
-rw-r--r--ext/hls/m3u8.c185
-rw-r--r--ext/hls/m3u8.h3
3 files changed, 241 insertions, 13 deletions
diff --git a/ext/hls/gsthlsdemux.c b/ext/hls/gsthlsdemux.c
index 3476e271b..5ad3646c6 100644
--- a/ext/hls/gsthlsdemux.c
+++ b/ext/hls/gsthlsdemux.c
@@ -1490,15 +1490,65 @@ 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);
+ gboolean main_checked = FALSE, updated = FALSE;
+ const gchar *uri;
+retry:
+ uri = gst_m3u8_client_get_current_uri (demux->client);
download =
gst_uri_downloader_fetch_uri (demux->downloader, uri,
demux->client->main ? demux->client->main->uri : NULL, TRUE, TRUE, TRUE,
err);
- if (download == NULL)
- return FALSE;
+ if (download == NULL) {
+ 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 */
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);
last_sequence =
- GST_M3U8_MEDIA_FILE (g_list_last (demux->client->current->files)->
- data)->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 %u",
@@ -1642,8 +1692,8 @@ retry_failover_protection:
gst_m3u8_client_set_current (demux->client, previous_variant->data);
/* Try a lower bitrate (or stop if we just tried the lowest) */
if (GST_M3U8 (previous_variant->data)->iframe && new_bandwidth ==
- GST_M3U8 (g_list_first (demux->client->main->iframe_lists)->
- data)->bandwidth)
+ GST_M3U8 (g_list_first (demux->client->main->iframe_lists)->data)->
+ bandwidth)
return FALSE;
else if (!GST_M3U8 (previous_variant->data)->iframe && new_bandwidth ==
GST_M3U8 (g_list_first (demux->client->main->lists)->data)->bandwidth)
diff --git a/ext/hls/m3u8.c b/ext/hls/m3u8.c
index d66da2d47..40ce09027 100644
--- a/ext/hls/m3u8.c
+++ b/ext/hls/m3u8.c
@@ -50,7 +50,7 @@ gst_m3u8_new (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);
@@ -59,6 +59,9 @@ gst_m3u8_set_uri (GstM3U8 * self, gchar * uri, gchar * base_uri)
g_free (self->base_uri);
self->base_uri = base_uri;
+
+ g_free (self->name);
+ self->name = name;
}
static void
@@ -68,6 +71,7 @@ gst_m3u8_free (GstM3U8 * self)
g_free (self->uri);
g_free (self->base_uri);
+ g_free (self->name);
g_free (self->codecs);
g_free (self->key);
@@ -109,6 +113,85 @@ gst_m3u8_media_file_free (GstM3U8MediaFile * 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
int_from_string (gchar * ptr, gchar ** endptr, gint * val)
{
@@ -320,6 +403,7 @@ gst_m3u8_update (GstM3U8 * self, gchar * data, gboolean * updated)
*r = '\0';
if (data[0] != '#' && data[0] != '\0') {
+ gchar *name = data;
if (duration <= 0 && list == NULL) {
GST_LOG ("%s: got line without EXTINF or EXTSTREAMINF, dropping", data);
goto next_line;
@@ -336,7 +420,7 @@ gst_m3u8_update (GstM3U8 * self, gchar * data, gboolean * updated)
gst_m3u8_free (list);
g_free (data);
} 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);
}
list = NULL;
@@ -397,6 +481,7 @@ gst_m3u8_update (GstM3U8 * self, gchar * data, gboolean * updated)
GstM3U8 *new_list;
new_list = gst_m3u8_new ();
+ new_list->parent = self;
new_list->iframe = iframe;
data = data + (iframe ? 26 : 18);
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");
}
} else if (iframe && g_str_equal (a, "URI")) {
+ gchar *name;
gchar *uri = g_strdup (v);
gchar *urip = uri;
int len = strlen (uri);
@@ -430,12 +516,15 @@ gst_m3u8_update (GstM3U8 * self, gchar * data, gboolean * updated)
if (uri[0] == '"')
uri += 1;
+ name = g_strdup (uri);
uri = uri_join (self->base_uri ? self->base_uri : self->uri, uri);
g_free (urip);
- if (uri == NULL)
+ if (uri == NULL) {
+ g_free (name);
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->update_failed_count = 0;
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;
}
@@ -695,6 +784,92 @@ out:
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
_find_current (GstM3U8MediaFile * file, GstM3U8Client * client)
{
diff --git a/ext/hls/m3u8.h b/ext/hls/m3u8.h
index d49287a06..9ae6c079d 100644
--- a/ext/hls/m3u8.h
+++ b/ext/hls/m3u8.h
@@ -40,6 +40,8 @@ struct _GstM3U8
gchar *uri; /* actually downloaded URI */
gchar *base_uri; /* URI to use as base for resolving relative URIs.
* 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 */
gint version; /* last EXT-X-VERSION */
@@ -90,6 +92,7 @@ struct _GstM3U8Client
GstM3U8Client *gst_m3u8_client_new (const gchar * uri, const gchar * base_uri);
void gst_m3u8_client_free (GstM3U8Client * client);
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);
gboolean gst_m3u8_client_get_next_fragment (GstM3U8Client * client,
gboolean * discontinuity, const gchar ** uri, GstClockTime * duration,