hlsdemux2: Wait for playlist switch during seek.

When switching to/from an iframe variant to do seeking, wait for the target
playlist to load before handling the seek.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3883>
This commit is contained in:
Jan Schmidt 2022-12-28 04:40:38 +11:00 committed by GStreamer Marge Bot
parent fe41db92db
commit 454779f094
2 changed files with 66 additions and 40 deletions

View file

@ -36,6 +36,7 @@
#include <string.h>
#include <gst/base/gsttypefindhelper.h>
#include <gst/tag/tag.h>
#include <glib/gi18n-lib.h>
#include "gsthlsdemux.h"
#include "gsthlsdemux-stream.h"
@ -1511,10 +1512,16 @@ on_playlist_update_error (GstHLSDemuxPlaylistLoader * pl,
{
GstHLSDemuxStream *hls_stream = GST_HLS_DEMUX_STREAM_CAST (userdata);
/* FIXME: How to handle rendition playlist update errors */
/* FIXME: How to handle rendition playlist update errors? There's
* not much we can do about it except throw an error */
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);
} else {
GstHLSDemux *demux = GST_HLS_DEMUX_STREAM_GET_DEMUX (hls_stream);
GST_ELEMENT_ERROR (demux, STREAM, FAILED,
(_("Internal data stream error.")),
("Could not update rendition playlist"));
}
}
@ -1855,7 +1862,7 @@ gst_hls_demux_stream_stop (GstAdaptiveDemux2Stream * stream)
* for this stream to download. Returns TRUE if the rendition
* stream switched group-id */
static gboolean
gst_hls_demux_update_rendition_stream (GstHLSDemux * hlsdemux,
gst_hls_demux_update_rendition_stream_uri (GstHLSDemux * hlsdemux,
GstHLSDemuxStream * hls_stream, GError ** err)
{
gchar *current_group_id, *requested_group_id;
@ -1932,9 +1939,9 @@ gst_hls_demux_stream_select_bitrate (GstAdaptiveDemux2Stream * stream,
return FALSE;
/* Currently playing partial segments, disallow bitrate
* switches - unless exactly at the first partial
* segment in a full segment (implying we are about to play
* a partial segment but didn't yet */
* switches and rendition playlist changes - except exactly
* at the first partial segment in a full segment (implying
* we are about to play a partial segment but didn't yet) */
if (hls_stream->in_partial_segments && hls_stream->part_idx > 0)
return FALSE;
@ -1953,7 +1960,7 @@ gst_hls_demux_stream_select_bitrate (GstAdaptiveDemux2Stream * stream,
}
/* Handle rendition streams */
return gst_hls_demux_update_rendition_stream (hlsdemux, hls_stream, NULL);
return gst_hls_demux_update_rendition_stream_uri (hlsdemux, hls_stream, NULL);
}
#if defined(HAVE_OPENSSL)

View file

@ -49,6 +49,7 @@
#include <string.h>
#include <gst/base/gsttypefindhelper.h>
#include <gst/tag/tag.h>
#include <glib/gi18n-lib.h>
/* FIXME: Only needed for scheduler-unlock/lock hack */
#include <gstadaptivedemux-private.h>
@ -296,6 +297,27 @@ gst_hls_demux_clear_all_pending_data (GstHLSDemux * hlsdemux)
}
}
/* Wait until the current variant playlist finishes loading, only
* for use when called from an external thread - seeking or initial
* manifest. From the scheduler task it will just hang */
static GstFlowReturn
gst_hls_demux_wait_for_variant_playlist (GstHLSDemux * hlsdemux)
{
GstFlowReturn flow_ret;
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 (hlsdemux,
"Interrupted waiting for stream to be prepared");
return GST_FLOW_FLUSHING;
}
}
return flow_ret;
}
#define SEEK_UPDATES_PLAY_POSITION(r, start_type, stop_type) \
((r >= 0 && start_type != GST_SEEK_TYPE_NONE) || \
(r < 0 && stop_type != GST_SEEK_TYPE_NONE))
@ -334,33 +356,27 @@ gst_hls_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek)
/* Use I-frame variants for trick modes */
if (hlsdemux->master->iframe_variants != NULL
&& rate < -1.0 && old_rate >= -1.0 && old_rate <= 1.0) {
GError *err = NULL;
/* Switch to I-frame variant */
gst_hls_demux_set_current_variant (hlsdemux,
hlsdemux->master->iframe_variants->data);
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;
}
//hlsdemux->discont = TRUE;
gst_hls_demux_change_variant_playlist (hlsdemux, bitrate / ABS (rate),
NULL);
} else if (rate > -1.0 && rate <= 1.0 && (old_rate < -1.0 || old_rate > 1.0)) {
GError *err = NULL;
/* Switch to normal variant */
gst_hls_demux_set_current_variant (hlsdemux,
hlsdemux->master->variants->data);
}
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;
}
//hlsdemux->discont = TRUE;
/* TODO why not continue using the same? that was being used up to now? */
gst_hls_demux_change_variant_playlist (hlsdemux, bitrate, NULL);
gst_hls_demux_change_variant_playlist (hlsdemux, bitrate / ABS (rate), NULL);
/* Of course the playlist isn't loaded as soon as we ask - we need to wait */
GstFlowReturn flow_ret = gst_hls_demux_wait_for_variant_playlist (hlsdemux);
if (flow_ret == GST_FLOW_FLUSHING)
return FALSE;
if (flow_ret != GST_FLOW_OK) {
GST_ELEMENT_ERROR (demux, STREAM, FAILED,
(_("Internal data stream error.")), ("Could not switch playlist"));
return FALSE;
}
target_pos = rate < 0 ? stop : start;
@ -380,8 +396,20 @@ gst_hls_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek)
if (!gst_adaptive_demux2_stream_is_selected (stream))
continue;
if (gst_hls_demux_stream_seek (stream, rate >= 0, flags, target_pos,
&current_pos) != GST_FLOW_OK) {
GstFlowReturn flow_ret;
while ((flow_ret =
gst_hls_demux_stream_seek (stream, rate >= 0, flags, target_pos,
&current_pos) == GST_ADAPTIVE_DEMUX_FLOW_BUSY)) {
if (!gst_adaptive_demux2_stream_wait_prepared (GST_ADAPTIVE_DEMUX2_STREAM
(stream))) {
GST_DEBUG_OBJECT (hlsdemux,
"Interrupted waiting for stream to be prepared for seek");
return FALSE;
}
}
if (flow_ret != GST_FLOW_OK) {
GST_ERROR_OBJECT (stream, "Failed to seek on stream");
return FALSE;
}
@ -729,22 +757,13 @@ gst_hls_demux_process_initial_manifest (GstAdaptiveDemux * demux,
/* 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;
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;
}
}
GstFlowReturn flow_ret = gst_hls_demux_wait_for_variant_playlist (hlsdemux);
if (flow_ret == GST_FLOW_FLUSHING)
return FALSE;
if (flow_ret != GST_FLOW_OK) {
GST_ELEMENT_ERROR_FROM_ERROR (demux, "Could not fetch media playlist",
err);
GST_ELEMENT_ERROR (demux, STREAM, FAILED,
(_("Internal data stream error.")),
("Could not fetch media playlist"));
return FALSE;
}
}