mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-04-08 00:59:48 +00:00
hlsdemux2: Continue reworking code for async playlist updates
Everything is working again now except for corner cases: - Failing over to another playlist after a load failure - Remembering playlist redirects and using that URI directly next time. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
This commit is contained in:
parent
93d92d5ddf
commit
083538df9e
6 changed files with 242 additions and 509 deletions
|
@ -258,18 +258,21 @@ gst_hls_demux_playlist_loader_set_playlist_uri (GstHLSDemuxPlaylistLoader * pl,
|
|||
}
|
||||
|
||||
/* Check that the current playlist matches the target URI, and return
|
||||
* a ref to it if so */
|
||||
GstHLSMediaPlaylist *
|
||||
gst_hls_demux_stream_get_playlist_for_uri (GstHLSDemuxPlaylistLoader * pl,
|
||||
* TRUE if so */
|
||||
gboolean
|
||||
gst_hls_demux_playlist_loader_has_current_uri (GstHLSDemuxPlaylistLoader * pl,
|
||||
const gchar * target_playlist_uri)
|
||||
{
|
||||
GstHLSDemuxPlaylistLoaderPrivate *priv = pl->priv;
|
||||
|
||||
if (target_playlist_uri == NULL)
|
||||
target_playlist_uri = priv->target_playlist_uri;
|
||||
|
||||
if (priv->current_playlist == NULL
|
||||
|| !g_str_equal (target_playlist_uri, priv->current_playlist_uri))
|
||||
return NULL;
|
||||
return FALSE;
|
||||
|
||||
return gst_hls_media_playlist_ref (priv->current_playlist);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
enum PlaylistDownloadParamFlags
|
||||
|
@ -490,7 +493,6 @@ on_download_complete (DownloadRequest * download, DownloadRequestState state,
|
|||
if (priv->current_playlist)
|
||||
gst_hls_media_playlist_unref (priv->current_playlist);
|
||||
|
||||
/* FIXME: If there was a redirect, use that for the next update */
|
||||
priv->current_playlist_uri = g_strdup (priv->loading_playlist_uri);
|
||||
priv->current_playlist = playlist;
|
||||
|
||||
|
@ -510,6 +512,10 @@ on_download_complete (DownloadRequest * download, DownloadRequestState state,
|
|||
GstClockTime delay = get_playlist_reload_interval (pl, priv, playlist);
|
||||
schedule_next_playlist_load (pl, priv, delay);
|
||||
}
|
||||
} else {
|
||||
GST_LOG_OBJECT (pl, "Playlist is not live. Not scheduling a reload");
|
||||
/* Go back to the starting state until/if the playlist uri is updated */
|
||||
priv->state = PLAYLIST_LOADER_STATE_STARTING;
|
||||
}
|
||||
|
||||
out:
|
||||
|
@ -557,7 +563,8 @@ start_playlist_download (GstHLSDemuxPlaylistLoader * pl,
|
|||
if (orig_uri == NULL)
|
||||
return;
|
||||
|
||||
struct PlaylistDownloadParams dl_params = { 0, };
|
||||
struct PlaylistDownloadParams dl_params;
|
||||
memset (&dl_params, 0, sizeof (struct PlaylistDownloadParams));
|
||||
|
||||
GstHLSMediaPlaylist *current_playlist = priv->current_playlist;
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ void gst_hls_demux_playlist_loader_stop (GstHLSDemuxPlaylistLoader *pl);
|
|||
|
||||
void gst_hls_demux_playlist_loader_set_playlist_uri (GstHLSDemuxPlaylistLoader *pl,
|
||||
const gchar *base_uri, const gchar *current_playlist_uri);
|
||||
GstHLSMediaPlaylist *gst_hls_demux_stream_get_playlist_for_uri (GstHLSDemuxPlaylistLoader *pl,
|
||||
gboolean gst_hls_demux_playlist_loader_has_current_uri (GstHLSDemuxPlaylistLoader *pl,
|
||||
const gchar *target_playlist_uri);
|
||||
|
||||
G_END_DECLS
|
||||
|
|
|
@ -62,9 +62,6 @@ gst_hls_demux_stream_decrypt_start (GstHLSDemuxStream * stream,
|
|||
const guint8 * key_data, const guint8 * iv_data);
|
||||
static void gst_hls_demux_stream_decrypt_end (GstHLSDemuxStream * stream);
|
||||
|
||||
static GstFlowReturn
|
||||
gst_hls_demux_stream_update_rendition_playlist (GstHLSDemuxStream * stream);
|
||||
|
||||
static gboolean
|
||||
gst_hls_demux_stream_start_fragment (GstAdaptiveDemux2Stream * stream);
|
||||
static GstFlowReturn
|
||||
|
@ -212,14 +209,9 @@ gst_hls_demux_stream_seek (GstAdaptiveDemux2Stream * stream, gboolean forward,
|
|||
GST_TIME_FORMAT, hls_stream->is_variant, hls_stream->current_rendition,
|
||||
hlsdemux->current_variant, forward, GST_TIME_ARGS (ts));
|
||||
|
||||
/* If the rendition playlist needs to be updated, do it now */
|
||||
if (!hls_stream->is_variant && !hls_stream->playlist_fetched) {
|
||||
ret = gst_hls_demux_stream_update_rendition_playlist (hls_stream);
|
||||
if (ret != GST_FLOW_OK) {
|
||||
GST_WARNING_OBJECT (stream,
|
||||
"Failed to update the rendition playlist before seeking");
|
||||
return ret;
|
||||
}
|
||||
/* If this stream doesn't have a playlist yet, we can't seek on it */
|
||||
if (!hls_stream->playlist_fetched) {
|
||||
return GST_ADAPTIVE_DEMUX_FLOW_BUSY;
|
||||
}
|
||||
|
||||
/* Allow jumping to partial segments in the last 2 segments in LL-HLS */
|
||||
|
@ -1263,233 +1255,6 @@ gst_hls_demux_stream_advance_fragment (GstAdaptiveDemux2Stream * stream)
|
|||
return GST_FLOW_EOS;
|
||||
}
|
||||
|
||||
enum PlaylistDownloadParamFlags
|
||||
{
|
||||
PLAYLIST_DOWNLOAD_FLAG_SKIP_V1 = (1 << 0),
|
||||
PLAYLIST_DOWNLOAD_FLAG_SKIP_V2 = (1 << 1), /* V2 also skips date-ranges */
|
||||
PLAYLIST_DOWNLOAD_FLAG_BLOCKING_REQUEST = (1 << 2),
|
||||
};
|
||||
|
||||
struct PlaylistDownloadParams
|
||||
{
|
||||
enum PlaylistDownloadParamFlags flags;
|
||||
gint64 next_msn, next_part;
|
||||
};
|
||||
|
||||
#define HLS_SKIP_QUERY_KEY "_HLS_skip"
|
||||
#define HLS_MSN_QUERY_KEY "_HLS_msn"
|
||||
#define HLS_PART_QUERY_KEY "_HLS_part"
|
||||
|
||||
static gchar *
|
||||
apply_directives_to_uri (GstHLSDemuxStream * stream,
|
||||
const gchar * playlist_uri, struct PlaylistDownloadParams *dl_params)
|
||||
{
|
||||
GstUri *uri = gst_uri_from_string (playlist_uri);
|
||||
|
||||
if (dl_params->flags & PLAYLIST_DOWNLOAD_FLAG_SKIP_V1) {
|
||||
GST_LOG_OBJECT (stream, "Doing HLS skip (v1) request");
|
||||
gst_uri_set_query_value (uri, HLS_SKIP_QUERY_KEY, "YES");
|
||||
} else if (dl_params->flags & PLAYLIST_DOWNLOAD_FLAG_SKIP_V2) {
|
||||
GST_LOG_OBJECT (stream, "Doing HLS skip (v2) request");
|
||||
gst_uri_set_query_value (uri, HLS_SKIP_QUERY_KEY, "v2");
|
||||
} else {
|
||||
gst_uri_remove_query_key (uri, HLS_SKIP_QUERY_KEY);
|
||||
}
|
||||
|
||||
if (dl_params->flags & PLAYLIST_DOWNLOAD_FLAG_BLOCKING_REQUEST
|
||||
&& dl_params->next_msn != -1) {
|
||||
GST_LOG_OBJECT (stream,
|
||||
"Doing HLS blocking request for URI %s with MSN %" G_GINT64_FORMAT
|
||||
" part %" G_GINT64_FORMAT, playlist_uri, dl_params->next_msn,
|
||||
dl_params->next_part);
|
||||
|
||||
gchar *next_msn_str =
|
||||
g_strdup_printf ("%" G_GINT64_FORMAT, dl_params->next_msn);
|
||||
gst_uri_set_query_value (uri, HLS_MSN_QUERY_KEY, next_msn_str);
|
||||
g_free (next_msn_str);
|
||||
|
||||
if (dl_params->next_part != -1) {
|
||||
gchar *next_part_str =
|
||||
g_strdup_printf ("%" G_GINT64_FORMAT, dl_params->next_part);
|
||||
gst_uri_set_query_value (uri, HLS_PART_QUERY_KEY, next_part_str);
|
||||
g_free (next_part_str);
|
||||
} else {
|
||||
gst_uri_remove_query_key (uri, HLS_PART_QUERY_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
/* Produce the resulting URI with query arguments in UTF-8 order
|
||||
* as required by the HLS spec:
|
||||
* `Clients using Delivery Directives (Section 6.2.5) MUST ensure that
|
||||
* all query parameters appear in UTF-8 order within the URI.`
|
||||
*/
|
||||
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 GstHLSMediaPlaylist *
|
||||
download_media_playlist (GstHLSDemuxStream * stream, gchar * orig_uri,
|
||||
GError ** err, GstHLSMediaPlaylist * current)
|
||||
{
|
||||
gboolean allow_skip = TRUE;
|
||||
|
||||
GstAdaptiveDemux2Stream *base_stream = GST_ADAPTIVE_DEMUX2_STREAM (stream);
|
||||
GstAdaptiveDemux *demux = base_stream->demux;
|
||||
const gchar *main_uri = gst_adaptive_demux_get_manifest_ref_uri (demux);
|
||||
struct PlaylistDownloadParams dl_params;
|
||||
|
||||
retry:
|
||||
|
||||
memset (&dl_params, 0, sizeof (struct PlaylistDownloadParams));
|
||||
|
||||
/* If there's no previous playlist, or the URI changed this
|
||||
* is not a refresh/update but a switch to a new playlist */
|
||||
gboolean playlist_uri_change = (current == NULL
|
||||
|| g_strcmp0 (orig_uri, current->uri) != 0);
|
||||
|
||||
if (!playlist_uri_change) {
|
||||
GST_LOG_OBJECT (stream, "Updating the playlist");
|
||||
|
||||
/* See if we can do a delta playlist update (if the playlist age is less than
|
||||
* one half of the Skip Boundary */
|
||||
if (GST_CLOCK_TIME_IS_VALID (current->skip_boundary) && allow_skip) {
|
||||
GstClockTime now = gst_adaptive_demux2_get_monotonic_time (demux);
|
||||
GstClockTimeDiff playlist_age =
|
||||
GST_CLOCK_DIFF (current->playlist_ts, now);
|
||||
|
||||
if (GST_CLOCK_TIME_IS_VALID (current->playlist_ts) &&
|
||||
playlist_age <= current->skip_boundary / 2) {
|
||||
if (current->can_skip_dateranges) {
|
||||
dl_params.flags |= PLAYLIST_DOWNLOAD_FLAG_SKIP_V2;
|
||||
} else {
|
||||
dl_params.flags |= PLAYLIST_DOWNLOAD_FLAG_SKIP_V1;
|
||||
}
|
||||
}
|
||||
} else if (GST_CLOCK_TIME_IS_VALID (current->skip_boundary)) {
|
||||
GST_DEBUG_OBJECT (stream,
|
||||
"Doing full playlist update after failed delta request");
|
||||
}
|
||||
}
|
||||
|
||||
/* Blocking playlist reload check */
|
||||
if (current != NULL && current->can_block_reload) {
|
||||
if (playlist_uri_change) {
|
||||
/* FIXME: We're changing playlist, but if there's a EXT-X-RENDITION-REPORT
|
||||
* for the new playlist we might be able to use it to do a blocking request */
|
||||
} else {
|
||||
/* Get the next MSN (and/or possibly part number) for the request params */
|
||||
gst_hls_media_playlist_get_next_msn_and_part (current,
|
||||
stream->llhls_enabled, &dl_params.next_msn, &dl_params.next_part);
|
||||
dl_params.flags |= PLAYLIST_DOWNLOAD_FLAG_BLOCKING_REQUEST;
|
||||
}
|
||||
}
|
||||
|
||||
gchar *target_uri = apply_directives_to_uri (stream, orig_uri, &dl_params);
|
||||
|
||||
DownloadRequest *download = downloadhelper_fetch_uri (demux->download_helper,
|
||||
target_uri, main_uri,
|
||||
DOWNLOAD_FLAG_COMPRESS | DOWNLOAD_FLAG_FORCE_REFRESH, err);
|
||||
|
||||
g_free (target_uri);
|
||||
|
||||
if (download == NULL)
|
||||
return NULL;
|
||||
|
||||
/* If we got a permanent redirect, use that as the new
|
||||
* playlist URI, otherwise set the base URI of the playlist
|
||||
* to the redirect target if any (NULL if there was no redirect) */
|
||||
GstHLSMediaPlaylist *playlist = NULL;
|
||||
gchar *base_uri, *uri;
|
||||
|
||||
if (download->redirect_permanent && download->redirect_uri) {
|
||||
uri = g_strdup (download->redirect_uri);
|
||||
base_uri = NULL;
|
||||
} else {
|
||||
uri = g_strdup (download->uri);
|
||||
base_uri = g_strdup (download->redirect_uri);
|
||||
}
|
||||
|
||||
if (download->state == DOWNLOAD_REQUEST_STATE_ERROR) {
|
||||
GST_WARNING_OBJECT (demux,
|
||||
"Couldn't get the playlist, got HTTP status code %d",
|
||||
download->status_code);
|
||||
download_request_unref (download);
|
||||
if (err)
|
||||
g_set_error (err, GST_STREAM_ERROR, GST_STREAM_ERROR_WRONG_TYPE,
|
||||
"Couldn't download the playlist");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Calculate the newest time we know this playlist was valid to store on the HLS Media Playlist */
|
||||
GstClockTime playlist_ts =
|
||||
MAX (0, GST_CLOCK_DIFF (download_request_get_age (download),
|
||||
download->download_start_time));
|
||||
|
||||
GstBuffer *buf = download_request_take_buffer (download);
|
||||
download_request_unref (download);
|
||||
|
||||
/* there should be a buf if there wasn't an error (handled above) */
|
||||
g_assert (buf);
|
||||
|
||||
gchar *playlist_data = gst_hls_buf_to_utf8_text (buf);
|
||||
gst_buffer_unref (buf);
|
||||
|
||||
if (playlist_data == NULL) {
|
||||
GST_WARNING_OBJECT (demux, "Couldn't validate playlist encoding");
|
||||
if (err)
|
||||
g_set_error (err, GST_STREAM_ERROR, GST_STREAM_ERROR_WRONG_TYPE,
|
||||
"Couldn't validate playlist encoding");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!playlist_uri_change && current
|
||||
&& gst_hls_media_playlist_has_same_data (current, playlist_data)) {
|
||||
GST_DEBUG_OBJECT (demux, "Same playlist data");
|
||||
playlist = gst_hls_media_playlist_ref (current);
|
||||
playlist->reloaded = TRUE;
|
||||
g_free (playlist_data);
|
||||
} else {
|
||||
playlist =
|
||||
gst_hls_media_playlist_parse (playlist_data, playlist_ts, uri,
|
||||
base_uri);
|
||||
if (!playlist) {
|
||||
GST_WARNING_OBJECT (demux, "Couldn't parse playlist");
|
||||
if (err)
|
||||
g_set_error (err, GST_STREAM_ERROR, GST_STREAM_ERROR_FAILED,
|
||||
"Couldn't parse playlist");
|
||||
}
|
||||
}
|
||||
|
||||
/* Transfer over any skipped segments from the current playlist if
|
||||
* we did a delta playlist update */
|
||||
if (!playlist_uri_change && current && playlist
|
||||
&& playlist->skipped_segments > 0) {
|
||||
if (!gst_hls_media_playlist_sync_skipped_segments (playlist, current)) {
|
||||
GST_DEBUG_OBJECT (stream,
|
||||
"Could not merge delta update to playlist. Retrying with full request");
|
||||
|
||||
/* Delta playlist update failed. Load a full playlist */
|
||||
allow_skip = FALSE;
|
||||
|
||||
gst_hls_media_playlist_unref (playlist);
|
||||
g_free (uri);
|
||||
g_free (base_uri);
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
g_free (uri);
|
||||
g_free (base_uri);
|
||||
|
||||
return playlist;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_hls_demux_stream_update_preloads (GstHLSDemuxStream * hlsdemux_stream)
|
||||
{
|
||||
|
@ -1571,46 +1336,12 @@ gst_hls_demux_stream_submit_request (GstAdaptiveDemux2Stream * stream,
|
|||
(stream, download_req);
|
||||
}
|
||||
|
||||
void
|
||||
gst_hls_demux_stream_set_playlist_uri (GstHLSDemuxStream * hls_stream,
|
||||
gchar * uri)
|
||||
static void
|
||||
gst_hls_demux_stream_handle_playlist_update (GstHLSDemuxStream * stream,
|
||||
const gchar * new_playlist_uri, GstHLSMediaPlaylist * new_playlist)
|
||||
{
|
||||
GstAdaptiveDemux *demux = GST_ADAPTIVE_DEMUX2_STREAM_CAST (hls_stream)->demux;
|
||||
|
||||
if (hls_stream->playlistloader == NULL) {
|
||||
hls_stream->playlistloader =
|
||||
gst_hls_demux_playlist_loader_new (demux, demux->download_helper,
|
||||
hls_stream->llhls_enabled);
|
||||
}
|
||||
|
||||
const gchar *main_uri = gst_adaptive_demux_get_manifest_ref_uri (demux);
|
||||
gst_hls_demux_playlist_loader_set_playlist_uri (hls_stream->playlistloader,
|
||||
main_uri, uri);
|
||||
}
|
||||
|
||||
GstFlowReturn
|
||||
gst_hls_demux_stream_update_media_playlist (GstHLSDemuxStream * stream,
|
||||
gchar ** uri, GError ** err)
|
||||
{
|
||||
GstHLSMediaPlaylist *new_playlist;
|
||||
GstHLSDemux *demux = GST_HLS_DEMUX_STREAM_GET_DEMUX (stream);
|
||||
|
||||
GST_DEBUG_OBJECT (stream, "Updating %s", *uri);
|
||||
|
||||
new_playlist = download_media_playlist (stream, *uri, err, stream->playlist);
|
||||
if (new_playlist == NULL) {
|
||||
GST_WARNING_OBJECT (stream, "Could not get playlist '%s'", *uri);
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
/* Check if a redirect happened */
|
||||
if (g_strcmp0 (*uri, new_playlist->uri)) {
|
||||
GST_DEBUG_OBJECT (stream, "Playlist URI update : '%s' => '%s'", *uri,
|
||||
new_playlist->uri);
|
||||
g_free (*uri);
|
||||
*uri = g_strdup (new_playlist->uri);
|
||||
}
|
||||
|
||||
/* Synchronize playlist with previous one. If we can't update the playlist
|
||||
* timing and inform the base class that we lost sync */
|
||||
if (stream->playlist
|
||||
|
@ -1707,30 +1438,32 @@ gst_hls_demux_stream_update_media_playlist (GstHLSDemuxStream * stream,
|
|||
GST_DEBUG_OBJECT (stream, "No current segment");
|
||||
}
|
||||
|
||||
if (stream->playlist) {
|
||||
gst_hls_media_playlist_unref (stream->playlist);
|
||||
stream->playlist = new_playlist;
|
||||
} else {
|
||||
if (stream->is_variant) {
|
||||
GST_DEBUG_OBJECT (stream, "Setting up initial playlist");
|
||||
gst_hls_demux_setup_initial_playlist (demux, new_playlist);
|
||||
}
|
||||
stream->playlist = new_playlist;
|
||||
if (stream->is_variant) {
|
||||
/* Updates on the variant playlist have some special requirements to
|
||||
* set up the time mapping and initial stream config */
|
||||
gst_hls_demux_handle_variant_playlist_update (demux, new_playlist_uri,
|
||||
new_playlist);
|
||||
} else if (stream->pending_rendition) {
|
||||
/* Switching rendition configures a new playlist on the loader,
|
||||
* and we should never get a callback for a stale download URI */
|
||||
g_assert (g_str_equal (stream->pending_rendition->uri, new_playlist_uri));
|
||||
|
||||
gst_hls_rendition_stream_unref (stream->current_rendition);
|
||||
/* Stealing ref */
|
||||
stream->current_rendition = stream->pending_rendition;
|
||||
stream->pending_rendition = NULL;
|
||||
}
|
||||
|
||||
if (stream->playlist)
|
||||
gst_hls_media_playlist_unref (stream->playlist);
|
||||
stream->playlist = gst_hls_media_playlist_ref (new_playlist);
|
||||
stream->playlist_fetched = TRUE;
|
||||
|
||||
if (!GST_HLS_MEDIA_PLAYLIST_IS_LIVE (stream->playlist)) {
|
||||
/* Make sure to cancel any preloads if a playlist isn't live after reload */
|
||||
gst_hls_demux_stream_update_preloads (stream);
|
||||
}
|
||||
|
||||
if (stream->is_variant) {
|
||||
/* Update time mappings. We only use the variant stream for collecting
|
||||
* mappings since it is the reference on which rendition stream timing will
|
||||
* be based. */
|
||||
gst_hls_update_time_mappings (demux, stream->playlist);
|
||||
}
|
||||
gst_hls_media_playlist_dump (stream->playlist);
|
||||
|
||||
if (stream->current_segment) {
|
||||
GST_DEBUG_OBJECT (stream,
|
||||
"After update, current segment now sn:%" G_GINT64_FORMAT
|
||||
|
@ -1743,8 +1476,7 @@ gst_hls_demux_stream_update_media_playlist (GstHLSDemuxStream * stream,
|
|||
}
|
||||
|
||||
GST_DEBUG_OBJECT (stream, "done");
|
||||
|
||||
return GST_FLOW_OK;
|
||||
return;
|
||||
|
||||
/* ERRORS */
|
||||
lost_sync:
|
||||
|
@ -1753,88 +1485,97 @@ lost_sync:
|
|||
if (stream->playlist)
|
||||
gst_hls_media_playlist_unref (stream->playlist);
|
||||
stream->playlist = new_playlist;
|
||||
stream->playlist = gst_hls_media_playlist_ref (new_playlist);
|
||||
stream->playlist_fetched = TRUE;
|
||||
|
||||
gst_hls_demux_reset_for_lost_sync (demux);
|
||||
|
||||
return GST_ADAPTIVE_DEMUX_FLOW_LOST_SYNC;
|
||||
}
|
||||
}
|
||||
|
||||
GstClockTime
|
||||
gst_hls_demux_stream_get_playlist_reload_interval (GstHLSDemuxStream * stream)
|
||||
static void
|
||||
on_playlist_update_success (GstHLSDemuxPlaylistLoader * pl,
|
||||
const gchar * new_playlist_uri, GstHLSMediaPlaylist * new_playlist,
|
||||
gpointer userdata)
|
||||
{
|
||||
GstHLSMediaPlaylist *playlist = stream->playlist;
|
||||
GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (userdata);
|
||||
|
||||
if (playlist == NULL)
|
||||
return GST_CLOCK_TIME_NONE; /* No playlist yet */
|
||||
|
||||
if (!gst_hls_media_playlist_is_live (playlist))
|
||||
return GST_CLOCK_TIME_NONE; /* Not live playback */
|
||||
|
||||
/* Use the most recent segment (or part segment) duration, as per
|
||||
* https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis-11#section-6.3.4
|
||||
*/
|
||||
GstClockTime target_duration = GST_CLOCK_TIME_NONE;
|
||||
GstClockTime min_reload_interval = playlist->targetduration / 2;
|
||||
|
||||
if (playlist->segments->len) {
|
||||
GstM3U8MediaSegment *last_seg =
|
||||
g_ptr_array_index (playlist->segments, playlist->segments->len - 1);
|
||||
|
||||
target_duration = last_seg->duration;
|
||||
|
||||
if (stream->llhls_enabled && last_seg->partial_segments) {
|
||||
GstM3U8PartialSegment *last_part =
|
||||
g_ptr_array_index (last_seg->partial_segments,
|
||||
last_seg->partial_segments->len - 1);
|
||||
|
||||
target_duration = last_part->duration;
|
||||
if (GST_CLOCK_TIME_IS_VALID (playlist->partial_targetduration)) {
|
||||
min_reload_interval = playlist->partial_targetduration / 2;
|
||||
} else {
|
||||
min_reload_interval = target_duration / 2;
|
||||
}
|
||||
}
|
||||
} else if (stream->llhls_enabled
|
||||
&& GST_CLOCK_TIME_IS_VALID (playlist->partial_targetduration)) {
|
||||
target_duration = playlist->partial_targetduration;
|
||||
min_reload_interval = target_duration / 2;
|
||||
} else if (playlist->version > 5) {
|
||||
target_duration = playlist->targetduration;
|
||||
}
|
||||
|
||||
if (playlist->reloaded && target_duration > min_reload_interval) {
|
||||
GST_DEBUG_OBJECT (stream,
|
||||
"Playlist didn't change previously, returning lower update interval");
|
||||
target_duration = min_reload_interval;
|
||||
}
|
||||
|
||||
return target_duration;
|
||||
gst_hls_demux_stream_handle_playlist_update (hls_stream,
|
||||
new_playlist_uri, new_playlist);
|
||||
gst_adaptive_demux2_stream_mark_prepared (GST_ADAPTIVE_DEMUX2_STREAM_CAST
|
||||
(hls_stream));
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_hls_demux_stream_update_rendition_playlist (GstHLSDemuxStream * stream)
|
||||
static void
|
||||
on_playlist_update_error (GstHLSDemuxPlaylistLoader * pl,
|
||||
const gchar * playlist_uri, gpointer userdata)
|
||||
{
|
||||
GstFlowReturn ret = GST_FLOW_OK;
|
||||
GstHLSRenditionStream *target_rendition =
|
||||
stream->pending_rendition ? stream->
|
||||
pending_rendition : stream->current_rendition;
|
||||
GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (userdata);
|
||||
|
||||
ret = gst_hls_demux_stream_update_media_playlist (stream,
|
||||
&target_rendition->uri, NULL);
|
||||
if (ret != GST_FLOW_OK)
|
||||
return ret;
|
||||
/* FIXME: How to handle rendition playlist update errors */
|
||||
if (hls_stream->is_variant) {
|
||||
GstHLSDemux *demux = GST_HLS_DEMUX_STREAM_GET_DEMUX (hls_stream);
|
||||
gst_hls_demux_handle_variant_playlist_update_error (demux, playlist_uri);
|
||||
}
|
||||
}
|
||||
|
||||
if (stream->pending_rendition) {
|
||||
gst_hls_rendition_stream_unref (stream->current_rendition);
|
||||
/* Stealing ref */
|
||||
stream->current_rendition = stream->pending_rendition;
|
||||
stream->pending_rendition = NULL;
|
||||
static GstHLSDemuxPlaylistLoader *
|
||||
gst_hls_demux_stream_get_playlist_loader (GstHLSDemuxStream * hls_stream)
|
||||
{
|
||||
GstAdaptiveDemux *demux = GST_ADAPTIVE_DEMUX2_STREAM_CAST (hls_stream)->demux;
|
||||
if (hls_stream->playlistloader == NULL) {
|
||||
hls_stream->playlistloader =
|
||||
gst_hls_demux_playlist_loader_new (demux, demux->download_helper,
|
||||
hls_stream->llhls_enabled);
|
||||
gst_hls_demux_playlist_loader_set_callbacks (hls_stream->playlistloader,
|
||||
on_playlist_update_success, on_playlist_update_error, hls_stream);
|
||||
}
|
||||
|
||||
stream->playlist_fetched = TRUE;
|
||||
return hls_stream->playlistloader;
|
||||
}
|
||||
|
||||
return ret;
|
||||
void
|
||||
gst_hls_demux_stream_set_playlist_uri (GstHLSDemuxStream * hls_stream,
|
||||
gchar * uri)
|
||||
{
|
||||
GstAdaptiveDemux *demux = GST_ADAPTIVE_DEMUX2_STREAM_CAST (hls_stream)->demux;
|
||||
GstHLSDemuxPlaylistLoader *pl =
|
||||
gst_hls_demux_stream_get_playlist_loader (hls_stream);
|
||||
|
||||
const gchar *main_uri = gst_adaptive_demux_get_manifest_ref_uri (demux);
|
||||
gst_hls_demux_playlist_loader_set_playlist_uri (pl, main_uri, uri);
|
||||
}
|
||||
|
||||
void
|
||||
gst_hls_demux_stream_start_playlist_loading (GstHLSDemuxStream * hls_stream)
|
||||
{
|
||||
GstHLSDemuxPlaylistLoader *pl =
|
||||
gst_hls_demux_stream_get_playlist_loader (hls_stream);
|
||||
gst_hls_demux_playlist_loader_start (pl);
|
||||
}
|
||||
|
||||
GstFlowReturn
|
||||
gst_hls_demux_stream_check_current_playlist_uri (GstHLSDemuxStream * stream,
|
||||
gchar * uri)
|
||||
{
|
||||
GstHLSDemuxPlaylistLoader *pl =
|
||||
gst_hls_demux_stream_get_playlist_loader (stream);
|
||||
|
||||
if (!gst_hls_demux_playlist_loader_has_current_uri (pl, uri)) {
|
||||
GST_LOG_OBJECT (stream, "Playlist '%s' not available yet", uri);
|
||||
return GST_ADAPTIVE_DEMUX_FLOW_BUSY;
|
||||
}
|
||||
|
||||
return GST_FLOW_OK;
|
||||
|
||||
#if 0
|
||||
/* Check if a redirect happened */
|
||||
if (g_strcmp0 (*uri, new_playlist->uri)) {
|
||||
GST_DEBUG_OBJECT (stream, "Playlist URI update : '%s' => '%s'", *uri,
|
||||
new_playlist->uri);
|
||||
g_free (*uri);
|
||||
*uri = g_strdup (new_playlist->uri);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
|
@ -1848,11 +1589,10 @@ gst_hls_demux_stream_update_fragment_info (GstAdaptiveDemux2Stream * stream)
|
|||
GstM3U8PartialSegment *part = NULL;
|
||||
gboolean discont;
|
||||
|
||||
/* If the rendition playlist needs to be updated, do it now */
|
||||
if (!hlsdemux_stream->is_variant && !hlsdemux_stream->playlist_fetched) {
|
||||
ret = gst_hls_demux_stream_update_rendition_playlist (hlsdemux_stream);
|
||||
if (ret != GST_FLOW_OK)
|
||||
return ret;
|
||||
/* Return BUSY if the playlist isn't loaded yet */
|
||||
if (!hlsdemux_stream->playlist_fetched) {
|
||||
gst_hls_demux_stream_start_playlist_loading (hlsdemux_stream);
|
||||
return GST_ADAPTIVE_DEMUX_FLOW_BUSY;
|
||||
}
|
||||
#ifndef GST_DISABLE_GST_DEBUG
|
||||
GstClockTimeDiff live_edge_dist =
|
||||
|
@ -2089,14 +1829,7 @@ gst_hls_demux_stream_start (GstAdaptiveDemux2Stream * stream)
|
|||
/* Start the playlist loader */
|
||||
GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
|
||||
|
||||
if (hls_stream->playlistloader == NULL) {
|
||||
GstAdaptiveDemux *demux = stream->demux;
|
||||
|
||||
hls_stream->playlistloader =
|
||||
gst_hls_demux_playlist_loader_new (demux, demux->download_helper,
|
||||
hls_stream->llhls_enabled);
|
||||
}
|
||||
gst_hls_demux_playlist_loader_start (hls_stream->playlistloader);
|
||||
gst_hls_demux_stream_start_playlist_loading (hls_stream);
|
||||
|
||||
/* Chain up, to start the downloading */
|
||||
GST_ADAPTIVE_DEMUX2_STREAM_CLASS (stream_parent_class)->start (stream);
|
||||
|
@ -2107,14 +1840,20 @@ gst_hls_demux_stream_stop (GstAdaptiveDemux2Stream * stream)
|
|||
{
|
||||
GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
|
||||
|
||||
if (hls_stream->playlistloader)
|
||||
if (hls_stream->playlistloader && !hls_stream->is_variant) {
|
||||
/* Don't stop the loader for the variant stream, keep it running
|
||||
* until the scheduler itself is stopped so we keep updating
|
||||
* the live playlist timeline */
|
||||
gst_hls_demux_playlist_loader_stop (hls_stream->playlistloader);
|
||||
}
|
||||
|
||||
/* Chain up, to stop the downloading */
|
||||
GST_ADAPTIVE_DEMUX2_STREAM_CLASS (stream_parent_class)->stop (stream);
|
||||
}
|
||||
|
||||
/* Returns TRUE if the rendition stream switched group-id */
|
||||
/* Called when the variant is changed, to set a new rendition
|
||||
* for this stream to download. Returns TRUE if the rendition
|
||||
* stream switched group-id */
|
||||
static gboolean
|
||||
gst_hls_demux_update_rendition_stream (GstHLSDemux * hlsdemux,
|
||||
GstHLSDemuxStream * hls_stream, GError ** err)
|
||||
|
@ -2166,7 +1905,6 @@ gst_hls_demux_update_rendition_stream (GstHLSDemux * hlsdemux,
|
|||
|
||||
GST_DEBUG_OBJECT (hlsdemux, "Use replacement playlist %s",
|
||||
replacement_media->name);
|
||||
hls_stream->playlist_fetched = FALSE;
|
||||
if (hls_stream->pending_rendition) {
|
||||
GST_ERROR_OBJECT (hlsdemux,
|
||||
"Already had a pending rendition switch to '%s'",
|
||||
|
|
|
@ -49,7 +49,7 @@ G_BEGIN_DECLS
|
|||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_HLS_DEMUX_STREAM,GstHLSDemuxStream))
|
||||
#define GST_HLS_DEMUX_STREAM_CAST(obj) ((GstHLSDemuxStream *)obj)
|
||||
|
||||
#define GST_HLS_DEMUX_STREAM_GET_DEMUX(obj) (GST_HLS_DEMUX_CAST(GST_ADAPTIVE_DEMUX2_STREAM(stream)->demux))
|
||||
#define GST_HLS_DEMUX_STREAM_GET_DEMUX(obj) (GST_HLS_DEMUX_CAST(GST_ADAPTIVE_DEMUX2_STREAM((obj))->demux))
|
||||
|
||||
typedef struct _GstHLSDemuxStream GstHLSDemuxStream;
|
||||
typedef GstAdaptiveDemux2StreamClass GstHLSDemuxStreamClass;
|
||||
|
@ -169,11 +169,10 @@ gst_hls_demux_stream_seek (GstAdaptiveDemux2Stream * stream, gboolean forward,
|
|||
void
|
||||
gst_hls_demux_stream_set_playlist_uri (GstHLSDemuxStream * stream, gchar * uri);
|
||||
|
||||
GstFlowReturn
|
||||
gst_hls_demux_stream_update_media_playlist (GstHLSDemuxStream * stream, gchar ** uri, GError ** err);
|
||||
void
|
||||
gst_hls_demux_stream_start_playlist_loading (GstHLSDemuxStream * stream);
|
||||
|
||||
GstClockTime
|
||||
gst_hls_demux_stream_get_playlist_reload_interval (GstHLSDemuxStream * stream);
|
||||
GstFlowReturn gst_hls_demux_stream_check_current_playlist_uri (GstHLSDemuxStream * stream, gchar *uri);
|
||||
|
||||
void
|
||||
gst_hls_demux_stream_clear_pending_data (GstHLSDemuxStream * hls_stream,
|
||||
|
|
|
@ -50,6 +50,9 @@
|
|||
#include <gst/base/gsttypefindhelper.h>
|
||||
#include <gst/tag/tag.h>
|
||||
|
||||
/* FIXME: Only needed for scheduler-unlock/lock hack */
|
||||
#include <gstadaptivedemux-private.h>
|
||||
|
||||
#include "gsthlselements.h"
|
||||
#include "gstadaptivedemuxelements.h"
|
||||
#include "gsthlsdemux.h"
|
||||
|
@ -82,17 +85,13 @@ static GstStateChangeReturn
|
|||
gst_hls_demux_change_state (GstElement * element, GstStateChange transition);
|
||||
|
||||
/* GstHLSDemux */
|
||||
static GstFlowReturn gst_hls_demux_update_playlist (GstHLSDemux * demux,
|
||||
gboolean update, GError ** err);
|
||||
static GstFlowReturn
|
||||
gst_hls_demux_check_variant_playlist_loaded (GstHLSDemux * demux);
|
||||
|
||||
static gboolean gst_hls_demux_is_live (GstAdaptiveDemux * demux);
|
||||
static GstClockTime gst_hls_demux_get_duration (GstAdaptiveDemux * demux);
|
||||
static gint64 gst_hls_demux_get_manifest_update_interval (GstAdaptiveDemux *
|
||||
demux);
|
||||
static gboolean gst_hls_demux_process_manifest (GstAdaptiveDemux * demux,
|
||||
GstBuffer * buf);
|
||||
|
||||
static GstFlowReturn gst_hls_demux_update_manifest (GstAdaptiveDemux * demux);
|
||||
static gboolean gst_hls_demux_process_initial_manifest (GstAdaptiveDemux *
|
||||
demux, GstBuffer * buf);
|
||||
|
||||
static void gst_hls_prune_time_mappings (GstHLSDemux * demux);
|
||||
|
||||
|
@ -168,6 +167,16 @@ gst_hls_demux_get_property (GObject * object, guint prop_id,
|
|||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
hlsdemux_requires_periodical_playlist_update_default (GstAdaptiveDemux *
|
||||
demux G_GNUC_UNUSED)
|
||||
{
|
||||
/* We don't need the base class to update our manifest periodically, the
|
||||
* playlist loader for the main stream will do that */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gst_hls_demux2_class_init (GstHLSDemux2Class * klass)
|
||||
{
|
||||
|
@ -208,10 +217,10 @@ gst_hls_demux2_class_init (GstHLSDemux2Class * klass)
|
|||
adaptivedemux_class->is_live = gst_hls_demux_is_live;
|
||||
adaptivedemux_class->get_live_seek_range = gst_hls_demux_get_live_seek_range;
|
||||
adaptivedemux_class->get_duration = gst_hls_demux_get_duration;
|
||||
adaptivedemux_class->get_manifest_update_interval =
|
||||
gst_hls_demux_get_manifest_update_interval;
|
||||
adaptivedemux_class->process_manifest = gst_hls_demux_process_manifest;
|
||||
adaptivedemux_class->update_manifest = gst_hls_demux_update_manifest;
|
||||
adaptivedemux_class->requires_periodical_playlist_update =
|
||||
hlsdemux_requires_periodical_playlist_update_default;
|
||||
adaptivedemux_class->process_manifest =
|
||||
gst_hls_demux_process_initial_manifest;
|
||||
adaptivedemux_class->reset = gst_hls_demux_reset;
|
||||
adaptivedemux_class->seek = gst_hls_demux_seek;
|
||||
}
|
||||
|
@ -258,7 +267,10 @@ gst_hls_demux_get_bitrate (GstHLSDemux * hlsdemux)
|
|||
|
||||
/* FIXME !!!
|
||||
*
|
||||
* No, there isn't a single output :D */
|
||||
* No, there isn't a single output :D.
|
||||
* Until the download helper can do estimates,
|
||||
* use the main variant, or a video stream if the
|
||||
* main variant stream is not loading */
|
||||
|
||||
/* Valid because hlsdemux only has a single output */
|
||||
if (demux->input_period->streams) {
|
||||
|
@ -328,7 +340,7 @@ gst_hls_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek)
|
|||
gst_hls_demux_set_current_variant (hlsdemux,
|
||||
hlsdemux->master->iframe_variants->data);
|
||||
|
||||
if (gst_hls_demux_update_playlist (hlsdemux, FALSE, &err) != GST_FLOW_OK) {
|
||||
if (gst_hls_demux_check_variant_playlist_loaded (hlsdemux) != GST_FLOW_OK) {
|
||||
GST_ELEMENT_ERROR_FROM_ERROR (hlsdemux, "Could not switch playlist", err);
|
||||
return FALSE;
|
||||
}
|
||||
|
@ -342,7 +354,7 @@ gst_hls_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek)
|
|||
gst_hls_demux_set_current_variant (hlsdemux,
|
||||
hlsdemux->master->variants->data);
|
||||
|
||||
if (gst_hls_demux_update_playlist (hlsdemux, FALSE, &err) != GST_FLOW_OK) {
|
||||
if (gst_hls_demux_check_variant_playlist_loaded (hlsdemux) != GST_FLOW_OK) {
|
||||
GST_ELEMENT_ERROR_FROM_ERROR (hlsdemux, "Could not switch playlist", err);
|
||||
return FALSE;
|
||||
}
|
||||
|
@ -391,14 +403,6 @@ gst_hls_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_hls_demux_update_manifest (GstAdaptiveDemux * demux)
|
||||
{
|
||||
GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
|
||||
|
||||
return gst_hls_demux_update_playlist (hlsdemux, TRUE, NULL);
|
||||
}
|
||||
|
||||
static GstAdaptiveDemux2Stream *
|
||||
create_common_hls_stream (GstHLSDemux * demux, const gchar * name)
|
||||
{
|
||||
|
@ -423,15 +427,16 @@ create_main_variant_stream (GstHLSDemux * demux)
|
|||
stream = create_common_hls_stream (demux, "hlsstream-variant");
|
||||
demux->main_stream = hlsdemux_stream = (GstHLSDemuxStream *) stream;
|
||||
|
||||
gst_hls_demux_stream_set_playlist_uri (hlsdemux_stream,
|
||||
demux->current_variant->uri);
|
||||
|
||||
hlsdemux_stream->is_variant = TRUE;
|
||||
hlsdemux_stream->playlist_fetched = TRUE;
|
||||
|
||||
/* Due to HLS manifest information being so unreliable/inconsistent, we will
|
||||
* create the actual tracks once we have information about the streams present
|
||||
* in the variant data stream */
|
||||
stream->pending_tracks = TRUE;
|
||||
|
||||
gst_hls_demux_stream_set_playlist_uri (hlsdemux_stream,
|
||||
demux->current_variant->uri);
|
||||
gst_hls_demux_stream_start_playlist_loading (hlsdemux_stream);
|
||||
}
|
||||
|
||||
GstAdaptiveDemuxTrack *
|
||||
|
@ -590,6 +595,7 @@ gst_hls_demux_setup_streams (GstAdaptiveDemux * demux)
|
|||
if (media_stream->current_rendition)
|
||||
gst_hls_rendition_stream_unref (media_stream->current_rendition);
|
||||
media_stream->current_rendition = gst_hls_rendition_stream_ref (media);
|
||||
gst_hls_demux_stream_set_playlist_uri (media_stream, media->uri);
|
||||
}
|
||||
|
||||
if (!previous_media_stream)
|
||||
|
@ -628,12 +634,16 @@ gst_hls_demux_set_current_variant (GstHLSDemux * hlsdemux,
|
|||
}
|
||||
|
||||
if (hlsdemux->main_stream) {
|
||||
/* The variant stream exists, update the playlist we're loading */
|
||||
gst_hls_demux_stream_set_playlist_uri (hlsdemux->main_stream, variant->uri);
|
||||
}
|
||||
}
|
||||
|
||||
/* Called to process the initial multi-variant (or simple playlist)
|
||||
* received on the element's sinkpad */
|
||||
static gboolean
|
||||
gst_hls_demux_process_manifest (GstAdaptiveDemux * demux, GstBuffer * buf)
|
||||
gst_hls_demux_process_initial_manifest (GstAdaptiveDemux * demux,
|
||||
GstBuffer * buf)
|
||||
{
|
||||
GstHLSVariantStream *variant;
|
||||
GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
|
||||
|
@ -694,6 +704,8 @@ gst_hls_demux_process_manifest (GstAdaptiveDemux * demux, GstBuffer * buf)
|
|||
GST_DEBUG_OBJECT (hlsdemux, "Manifest handled, now setting up streams");
|
||||
|
||||
ret = gst_hls_demux_setup_streams (demux);
|
||||
if (!ret)
|
||||
return FALSE;
|
||||
|
||||
if (simple_media_playlist) {
|
||||
GstM3U8SeekResult seek_result;
|
||||
|
@ -711,23 +723,33 @@ gst_hls_demux_process_manifest (GstAdaptiveDemux * demux, GstBuffer * buf)
|
|||
seek_result.found_partial_segment;
|
||||
hlsdemux->main_stream->part_idx = seek_result.part_idx;
|
||||
|
||||
gst_hls_demux_setup_initial_playlist (hlsdemux, simple_media_playlist);
|
||||
gst_hls_update_time_mappings (hlsdemux, simple_media_playlist);
|
||||
gst_hls_media_playlist_dump (simple_media_playlist);
|
||||
gst_hls_demux_handle_variant_playlist_update (hlsdemux,
|
||||
simple_media_playlist->uri, simple_media_playlist);
|
||||
}
|
||||
|
||||
/* get the selected media playlist (unless the initial list was one already) */
|
||||
/* If this is a multi-variant playlist, wait for the initial variant playlist to load */
|
||||
if (!hlsdemux->master->is_simple) {
|
||||
GError *err = NULL;
|
||||
GstFlowReturn flow_ret;
|
||||
|
||||
if (gst_hls_demux_update_playlist (hlsdemux, FALSE, &err) != GST_FLOW_OK) {
|
||||
while ((flow_ret = gst_hls_demux_check_variant_playlist_loaded (hlsdemux)
|
||||
== GST_ADAPTIVE_DEMUX_FLOW_BUSY)) {
|
||||
if (!gst_adaptive_demux2_stream_wait_prepared (GST_ADAPTIVE_DEMUX2_STREAM
|
||||
(hlsdemux->main_stream))) {
|
||||
GST_DEBUG_OBJECT (demux,
|
||||
"Interrupted waiting for stream to be prepared");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (flow_ret != GST_FLOW_OK) {
|
||||
GST_ELEMENT_ERROR_FROM_ERROR (demux, "Could not fetch media playlist",
|
||||
err);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GstClockTime
|
||||
|
@ -997,21 +1019,45 @@ gst_hls_update_time_mappings (GstHLSDemux * demux,
|
|||
}
|
||||
|
||||
void
|
||||
gst_hls_demux_setup_initial_playlist (GstHLSDemux * demux,
|
||||
GstHLSMediaPlaylist * playlist)
|
||||
gst_hls_demux_handle_variant_playlist_update (GstHLSDemux * demux,
|
||||
const gchar * playlist_uri, GstHLSMediaPlaylist * playlist)
|
||||
{
|
||||
GstM3U8MediaSegment *segment;
|
||||
if (demux->main_stream == NULL || !demux->main_stream->playlist_fetched) {
|
||||
GstM3U8MediaSegment *segment;
|
||||
|
||||
GST_DEBUG_OBJECT (demux,
|
||||
"Setting up initial variant segment and time mapping");
|
||||
GST_DEBUG_OBJECT (demux,
|
||||
"Setting up initial variant segment and time mapping");
|
||||
|
||||
/* This is the initial variant playlist. We will use it to base all our timing
|
||||
* from. */
|
||||
segment = g_ptr_array_index (playlist->segments, 0);
|
||||
if (segment) {
|
||||
segment->stream_time = 0;
|
||||
gst_hls_media_playlist_recalculate_stream_time (playlist, segment);
|
||||
/* This is the initial variant playlist. We will use it to base all our timing
|
||||
* from. */
|
||||
segment = g_ptr_array_index (playlist->segments, 0);
|
||||
if (segment) {
|
||||
segment->stream_time = 0;
|
||||
gst_hls_media_playlist_recalculate_stream_time (playlist, segment);
|
||||
}
|
||||
}
|
||||
|
||||
if (demux->pending_variant) {
|
||||
g_assert (g_str_equal (demux->pending_variant->uri, playlist_uri));
|
||||
|
||||
gst_hls_variant_stream_unref (demux->current_variant);
|
||||
/* Stealing ref */
|
||||
demux->current_variant = demux->pending_variant;
|
||||
demux->pending_variant = NULL;
|
||||
}
|
||||
|
||||
/* Update time mappings. We only use the variant stream for collecting
|
||||
* mappings since it is the reference on which rendition stream timing will
|
||||
* be based. */
|
||||
gst_hls_update_time_mappings (demux, playlist);
|
||||
gst_hls_media_playlist_dump (playlist);
|
||||
}
|
||||
|
||||
void
|
||||
gst_hls_demux_handle_variant_playlist_update_error (GstHLSDemux * demux,
|
||||
const gchar * playlist_uri)
|
||||
{
|
||||
GST_FIXME ("Variant playlist update failed. Switch over to another variant");
|
||||
}
|
||||
|
||||
/* Reset hlsdemux in case of live synchronization loss (i.e. when a media
|
||||
|
@ -1106,63 +1152,14 @@ gst_hls_demux_reset (GstAdaptiveDemux * ademux)
|
|||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_hls_demux_update_variant_playlist (GstHLSDemux * demux, GError ** err)
|
||||
gst_hls_demux_check_variant_playlist_loaded (GstHLSDemux * demux)
|
||||
{
|
||||
GstFlowReturn ret = GST_FLOW_OK;
|
||||
GstHLSVariantStream *target_variant =
|
||||
demux->pending_variant ? demux->pending_variant : demux->current_variant;
|
||||
GstHLSDemuxStream *stream = demux->main_stream;
|
||||
|
||||
ret = gst_hls_demux_stream_update_media_playlist (stream,
|
||||
&target_variant->uri, err);
|
||||
if (ret != GST_FLOW_OK)
|
||||
return ret;
|
||||
|
||||
if (demux->pending_variant) {
|
||||
gst_hls_variant_stream_unref (demux->current_variant);
|
||||
/* Stealing ref */
|
||||
demux->current_variant = demux->pending_variant;
|
||||
demux->pending_variant = NULL;
|
||||
}
|
||||
|
||||
stream->playlist_fetched = TRUE;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* update: TRUE only when requested from parent class (via
|
||||
* ::demux_update_manifest() or ::change_variant_playlist() ).
|
||||
*/
|
||||
static GstFlowReturn
|
||||
gst_hls_demux_update_playlist (GstHLSDemux * demux, gboolean update,
|
||||
GError ** err)
|
||||
{
|
||||
GstFlowReturn ret = GST_FLOW_OK;
|
||||
GstAdaptiveDemux *adaptive_demux = GST_ADAPTIVE_DEMUX (demux);
|
||||
|
||||
GST_DEBUG_OBJECT (demux, "update:%d", update);
|
||||
|
||||
/* Download and update the appropriate variant playlist (pending if any, else
|
||||
* current) */
|
||||
ret = gst_hls_demux_update_variant_playlist (demux, err);
|
||||
if (ret != GST_FLOW_OK)
|
||||
return ret;
|
||||
|
||||
if (update && gst_hls_demux_is_live (adaptive_demux)) {
|
||||
GList *tmp;
|
||||
GST_DEBUG_OBJECT (demux,
|
||||
"LIVE, Marking rendition streams to be updated next");
|
||||
/* We're live, instruct all rendition medias to be updated next */
|
||||
for (tmp = adaptive_demux->input_period->streams; tmp; tmp = tmp->next) {
|
||||
GstHLSDemuxStream *hls_stream = tmp->data;
|
||||
if (!hls_stream->is_variant)
|
||||
hls_stream->playlist_fetched = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return GST_FLOW_OK;
|
||||
return gst_hls_demux_stream_check_current_playlist_uri (stream,
|
||||
target_variant->uri);
|
||||
}
|
||||
|
||||
gboolean
|
||||
|
@ -1199,7 +1196,14 @@ retry_failover_protection:
|
|||
GST_INFO_OBJECT (demux, "Client was on %dbps, max allowed is %dbps, switching"
|
||||
" to bitrate %dbps", old_bandwidth, max_bitrate, new_bandwidth);
|
||||
|
||||
if (gst_hls_demux_update_playlist (demux, TRUE, NULL) == GST_FLOW_OK) {
|
||||
GstFlowReturn flow_ret = gst_hls_demux_check_variant_playlist_loaded (demux);
|
||||
|
||||
/* If the stream is still fetching the playlist, stop */
|
||||
if (flow_ret == GST_ADAPTIVE_DEMUX_FLOW_BUSY)
|
||||
return TRUE;
|
||||
|
||||
/* FIXME: Dead code. We need a different fail and retry mechanism */
|
||||
if (flow_ret == GST_FLOW_OK) {
|
||||
const gchar *main_uri;
|
||||
gchar *uri = new_variant->uri;
|
||||
|
||||
|
@ -1257,24 +1261,6 @@ retry_failover_protection:
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static gint64
|
||||
gst_hls_demux_get_manifest_update_interval (GstAdaptiveDemux * demux)
|
||||
{
|
||||
GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
|
||||
GstClockTime target_duration = 5 * GST_SECOND;
|
||||
GstHLSDemuxStream *main_stream = hlsdemux->main_stream;
|
||||
|
||||
if (main_stream) {
|
||||
target_duration =
|
||||
gst_hls_demux_stream_get_playlist_reload_interval (main_stream);
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (demux, "Returning update interval of %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (target_duration));
|
||||
|
||||
return gst_util_uint64_scale (target_duration, G_USEC_PER_SEC, GST_SECOND);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_hls_demux_get_live_seek_range (GstAdaptiveDemux * demux, gint64 * start,
|
||||
gint64 * stop)
|
||||
|
|
|
@ -117,11 +117,14 @@ void gst_hls_demux_reset_for_lost_sync (GstHLSDemux * hlsdemux);
|
|||
const GstHLSKey *gst_hls_demux_get_key (GstHLSDemux * demux,
|
||||
const gchar * key_url, const gchar * referer, gboolean allow_cache);
|
||||
|
||||
void
|
||||
gst_hls_demux_setup_initial_playlist (GstHLSDemux * demux,
|
||||
GstHLSMediaPlaylist * playlist);
|
||||
void gst_hls_demux_handle_variant_playlist_update (GstHLSDemux * demux,
|
||||
const gchar *playlist_uri, GstHLSMediaPlaylist * playlist);
|
||||
void gst_hls_demux_handle_variant_playlist_update_error (GstHLSDemux * demux,
|
||||
const gchar *playlist_uri);
|
||||
gboolean gst_hls_demux_change_variant_playlist (GstHLSDemux * demux,
|
||||
guint max_bitrate, gboolean * changed);
|
||||
GstFlowReturn gst_hls_demux_update_variant_playlist (GstHLSDemux * demux,
|
||||
GError ** err);
|
||||
|
||||
void gst_hls_demux_add_time_mapping (GstHLSDemux * demux, gint64 dsn,
|
||||
GstClockTimeDiff stream_time, GDateTime * pdt);
|
||||
|
|
Loading…
Reference in a new issue