hlsdemux: Re-use streams if possible
When switching variants, try to re-use existing streams/pads instead of creating new ones. When dealing with urisourcebin and decodebin3 this is not only the expected way but also avoids a lot of buffering/hang issues. Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/1757>
This commit is contained in:
parent
f1fdbfc5bd
commit
1246b448ee
@ -241,6 +241,10 @@ gst_hls_demux_get_bitrate (GstHLSDemux * hlsdemux)
|
|||||||
{
|
{
|
||||||
GstAdaptiveDemux *demux = GST_ADAPTIVE_DEMUX_CAST (hlsdemux);
|
GstAdaptiveDemux *demux = GST_ADAPTIVE_DEMUX_CAST (hlsdemux);
|
||||||
|
|
||||||
|
/* FIXME !!!
|
||||||
|
*
|
||||||
|
* No, there isn't a single output :D */
|
||||||
|
|
||||||
/* Valid because hlsdemux only has a single output */
|
/* Valid because hlsdemux only has a single output */
|
||||||
if (demux->streams) {
|
if (demux->streams) {
|
||||||
GstAdaptiveDemuxStream *stream = demux->streams->data;
|
GstAdaptiveDemuxStream *stream = demux->streams->data;
|
||||||
@ -494,6 +498,10 @@ create_stream_for_playlist (GstAdaptiveDemux * demux, GstM3U8 * playlist,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (demux,
|
||||||
|
"is_primary_playlist:%d selected:%d playlist name '%s'",
|
||||||
|
is_primary_playlist, selected, playlist->name);
|
||||||
|
|
||||||
stream = gst_adaptive_demux_stream_new (demux,
|
stream = gst_adaptive_demux_stream_new (demux,
|
||||||
gst_hls_demux_create_pad (hlsdemux));
|
gst_hls_demux_create_pad (hlsdemux));
|
||||||
|
|
||||||
@ -508,6 +516,79 @@ create_stream_for_playlist (GstAdaptiveDemux * demux, GstM3U8 * playlist,
|
|||||||
hlsdemux_stream->reset_pts = TRUE;
|
hlsdemux_stream->reset_pts = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GstHLSDemuxStream *
|
||||||
|
find_adaptive_stream_for_playlist (GstAdaptiveDemux * demux, GstM3U8 * playlist)
|
||||||
|
{
|
||||||
|
GList *tmp;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (demux, "Looking for existing stream for '%s' %s",
|
||||||
|
playlist->name, playlist->uri);
|
||||||
|
|
||||||
|
for (tmp = demux->streams; tmp; tmp = tmp->next) {
|
||||||
|
GstHLSDemuxStream *hlsstream = (GstHLSDemuxStream *) tmp->data;
|
||||||
|
if (hlsstream->playlist == playlist)
|
||||||
|
return hlsstream;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns TRUE if the previous and current (to switch to) variant are compatible.
|
||||||
|
*
|
||||||
|
* That is:
|
||||||
|
* * They have the same number of streams
|
||||||
|
* * The streams are of the same type
|
||||||
|
*/
|
||||||
|
static gboolean
|
||||||
|
new_variant_is_compatible (GstAdaptiveDemux * demux)
|
||||||
|
{
|
||||||
|
GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
|
||||||
|
GstHLSVariantStream *previous = hlsdemux->previous_variant;
|
||||||
|
GstHLSVariantStream *current = hlsdemux->current_variant;
|
||||||
|
gint i;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (demux,
|
||||||
|
"Checking whether new variant is compatible with previous");
|
||||||
|
|
||||||
|
for (i = 0; i < GST_HLS_N_MEDIA_TYPES; ++i) {
|
||||||
|
GList *mlist = current->media[i];
|
||||||
|
if (g_list_length (previous->media[i]) != g_list_length (current->media[i])) {
|
||||||
|
GST_LOG_OBJECT (demux, "Number of medias for type %s don't match",
|
||||||
|
gst_hls_media_type_get_name (i));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if all new media were present in previous (if not there are new ones) */
|
||||||
|
while (mlist != NULL) {
|
||||||
|
GstHLSMedia *media = mlist->data;
|
||||||
|
if (!gst_hls_variant_find_matching_media (previous, media)) {
|
||||||
|
GST_LOG_OBJECT (demux,
|
||||||
|
"New stream of type %s present. Variant not compatible",
|
||||||
|
gst_hls_media_type_get_name (i));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
mlist = mlist->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if all old media are present in current (if not some have gone) */
|
||||||
|
mlist = previous->media[i];
|
||||||
|
while (mlist != NULL) {
|
||||||
|
GstHLSMedia *media = mlist->data;
|
||||||
|
if (!gst_hls_variant_find_matching_media (current, media)) {
|
||||||
|
GST_LOG_OBJECT (demux,
|
||||||
|
"Old stream of type %s gone. Variant not compatible",
|
||||||
|
gst_hls_media_type_get_name (i));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
mlist = mlist->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (demux, "Variants are compatible");
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_hls_demux_setup_streams (GstAdaptiveDemux * demux)
|
gst_hls_demux_setup_streams (GstAdaptiveDemux * demux)
|
||||||
{
|
{
|
||||||
@ -520,6 +601,59 @@ gst_hls_demux_setup_streams (GstAdaptiveDemux * demux)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (demux, "Setting up streams");
|
||||||
|
if (hlsdemux->streams_aware && hlsdemux->previous_variant &&
|
||||||
|
new_variant_is_compatible (demux)) {
|
||||||
|
GstHLSDemuxStream *hlsstream;
|
||||||
|
GST_DEBUG_OBJECT (demux, "Have a previous variant, Re-using streams");
|
||||||
|
|
||||||
|
/* Carry over the main playlist */
|
||||||
|
hlsstream =
|
||||||
|
find_adaptive_stream_for_playlist (demux,
|
||||||
|
hlsdemux->previous_variant->m3u8);
|
||||||
|
if (G_UNLIKELY (hlsstream == NULL))
|
||||||
|
goto no_match_error;
|
||||||
|
|
||||||
|
gst_m3u8_unref (hlsstream->playlist);
|
||||||
|
hlsstream->playlist = gst_m3u8_ref (playlist->m3u8);
|
||||||
|
|
||||||
|
for (i = 0; i < GST_HLS_N_MEDIA_TYPES; ++i) {
|
||||||
|
GList *mlist = playlist->media[i];
|
||||||
|
while (mlist != NULL) {
|
||||||
|
GstHLSMedia *media = mlist->data;
|
||||||
|
GstHLSMedia *old_media =
|
||||||
|
gst_hls_variant_find_matching_media (hlsdemux->previous_variant,
|
||||||
|
media);
|
||||||
|
|
||||||
|
if (G_UNLIKELY (old_media == NULL)) {
|
||||||
|
GST_FIXME_OBJECT (demux, "Handle new stream !");
|
||||||
|
goto no_match_error;
|
||||||
|
}
|
||||||
|
if (!g_strcmp0 (media->uri, old_media->uri))
|
||||||
|
GST_DEBUG_OBJECT (demux, "Identical stream !");
|
||||||
|
if (media->mtype == GST_HLS_MEDIA_TYPE_AUDIO ||
|
||||||
|
media->mtype == GST_HLS_MEDIA_TYPE_VIDEO) {
|
||||||
|
hlsstream =
|
||||||
|
find_adaptive_stream_for_playlist (demux, old_media->playlist);
|
||||||
|
if (!hlsstream)
|
||||||
|
goto no_match_error;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (demux, "Found matching stream");
|
||||||
|
gst_m3u8_unref (hlsstream->playlist);
|
||||||
|
hlsstream->playlist = gst_m3u8_ref (media->playlist);
|
||||||
|
} else {
|
||||||
|
GST_DEBUG_OBJECT (demux, "Skipping stream of type %s",
|
||||||
|
gst_hls_media_type_get_name (media->mtype));
|
||||||
|
}
|
||||||
|
|
||||||
|
mlist = mlist->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME : This seems wrong and assumes there's only one stream :( */
|
||||||
gst_hls_demux_clear_all_pending_data (hlsdemux);
|
gst_hls_demux_clear_all_pending_data (hlsdemux);
|
||||||
|
|
||||||
/* 1 output for the main playlist */
|
/* 1 output for the main playlist */
|
||||||
@ -533,22 +667,29 @@ gst_hls_demux_setup_streams (GstAdaptiveDemux * demux)
|
|||||||
if (media->uri == NULL /* || media->mtype != GST_HLS_MEDIA_TYPE_AUDIO */ ) {
|
if (media->uri == NULL /* || media->mtype != GST_HLS_MEDIA_TYPE_AUDIO */ ) {
|
||||||
/* No uri means this is a placeholder for a stream
|
/* No uri means this is a placeholder for a stream
|
||||||
* contained in another mux */
|
* contained in another mux */
|
||||||
GST_LOG_OBJECT (demux, "Skipping stream %s type %d with no URI",
|
GST_LOG_OBJECT (demux, "Skipping stream %s type %s with no URI",
|
||||||
media->name, media->mtype);
|
media->name, gst_hls_media_type_get_name (media->mtype));
|
||||||
mlist = mlist->next;
|
mlist = mlist->next;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
GST_LOG_OBJECT (demux, "media of type %d - %s, uri: %s", i,
|
GST_LOG_OBJECT (demux, "media of type %s - %s, uri: %s",
|
||||||
media->name, media->uri);
|
gst_hls_media_type_get_name (i), media->name, media->uri);
|
||||||
create_stream_for_playlist (demux, media->playlist, FALSE,
|
create_stream_for_playlist (demux, media->playlist, FALSE,
|
||||||
(media->mtype == GST_HLS_MEDIA_TYPE_VIDEO ||
|
(media->mtype == GST_HLS_MEDIA_TYPE_VIDEO
|
||||||
media->mtype == GST_HLS_MEDIA_TYPE_AUDIO));
|
|| media->mtype == GST_HLS_MEDIA_TYPE_AUDIO));
|
||||||
|
|
||||||
mlist = mlist->next;
|
mlist = mlist->next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
|
no_match_error:
|
||||||
|
{
|
||||||
|
/* POST ERROR MESSAGE */
|
||||||
|
GST_ERROR_OBJECT (demux, "Should not happen ! Could not find old stream");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const gchar *
|
static const gchar *
|
||||||
@ -588,15 +729,27 @@ gst_hls_demux_set_current_variant (GstHLSDemux * hlsdemux,
|
|||||||
gst_hls_variant_find_matching_media (variant, old_media);
|
gst_hls_variant_find_matching_media (variant, old_media);
|
||||||
|
|
||||||
if (new_media) {
|
if (new_media) {
|
||||||
|
GST_LOG_OBJECT (hlsdemux, "Found matching GstHLSMedia");
|
||||||
|
GST_LOG_OBJECT (hlsdemux, "old_media '%s' '%s'", old_media->name,
|
||||||
|
old_media->uri);
|
||||||
|
GST_LOG_OBJECT (hlsdemux, "new_media '%s' '%s'", new_media->name,
|
||||||
|
new_media->uri);
|
||||||
new_media->playlist->sequence = old_media->playlist->sequence;
|
new_media->playlist->sequence = old_media->playlist->sequence;
|
||||||
new_media->playlist->sequence_position =
|
new_media->playlist->sequence_position =
|
||||||
old_media->playlist->sequence_position;
|
old_media->playlist->sequence_position;
|
||||||
|
} else {
|
||||||
|
GST_LOG_OBJECT (hlsdemux,
|
||||||
|
"Didn't find a matching variant for '%s' '%s'", old_media->name,
|
||||||
|
old_media->uri);
|
||||||
}
|
}
|
||||||
mlist = mlist->next;
|
mlist = mlist->next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gst_hls_variant_stream_unref (hlsdemux->current_variant);
|
if (hlsdemux->previous_variant)
|
||||||
|
gst_hls_variant_stream_unref (hlsdemux->previous_variant);
|
||||||
|
/* Steal the reference */
|
||||||
|
hlsdemux->previous_variant = hlsdemux->current_variant;
|
||||||
}
|
}
|
||||||
|
|
||||||
hlsdemux->current_variant = gst_hls_variant_stream_ref (variant);
|
hlsdemux->current_variant = gst_hls_variant_stream_ref (variant);
|
||||||
@ -855,8 +1008,8 @@ gst_hls_demux_handle_buffer (GstAdaptiveDemux * demux,
|
|||||||
return GST_FLOW_OK;
|
return GST_FLOW_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (hlsdemux, "Typefind result: %" GST_PTR_FORMAT " prob:%d",
|
GST_DEBUG_OBJECT (stream->pad,
|
||||||
caps, prob);
|
"Typefind result: %" GST_PTR_FORMAT " prob:%d", caps, prob);
|
||||||
|
|
||||||
hls_stream->stream_type = caps_to_reader (caps);
|
hls_stream->stream_type = caps_to_reader (caps);
|
||||||
gst_hlsdemux_tsreader_set_type (&hls_stream->tsreader,
|
gst_hlsdemux_tsreader_set_type (&hls_stream->tsreader,
|
||||||
@ -1192,7 +1345,15 @@ gst_hls_demux_reset (GstAdaptiveDemux * ademux)
|
|||||||
gst_hls_variant_stream_unref (demux->current_variant);
|
gst_hls_variant_stream_unref (demux->current_variant);
|
||||||
demux->current_variant = NULL;
|
demux->current_variant = NULL;
|
||||||
}
|
}
|
||||||
|
if (demux->previous_variant != NULL) {
|
||||||
|
gst_hls_variant_stream_unref (demux->previous_variant);
|
||||||
|
demux->previous_variant = NULL;
|
||||||
|
}
|
||||||
demux->srcpad_counter = 0;
|
demux->srcpad_counter = 0;
|
||||||
|
demux->streams_aware = GST_OBJECT_PARENT (demux)
|
||||||
|
&& GST_OBJECT_FLAG_IS_SET (GST_OBJECT_PARENT (demux),
|
||||||
|
GST_BIN_FLAG_STREAMS_AWARE);
|
||||||
|
GST_DEBUG_OBJECT (demux, "Streams aware : %d", demux->streams_aware);
|
||||||
|
|
||||||
gst_hls_demux_clear_all_pending_data (demux);
|
gst_hls_demux_clear_all_pending_data (demux);
|
||||||
GST_M3U8_CLIENT_UNLOCK (hlsdemux->client);
|
GST_M3U8_CLIENT_UNLOCK (hlsdemux->client);
|
||||||
|
@ -147,6 +147,10 @@ struct _GstHLSDemux
|
|||||||
GstHLSMasterPlaylist *master;
|
GstHLSMasterPlaylist *master;
|
||||||
|
|
||||||
GstHLSVariantStream *current_variant;
|
GstHLSVariantStream *current_variant;
|
||||||
|
/* The previous variant, used to transition streams over */
|
||||||
|
GstHLSVariantStream *previous_variant;
|
||||||
|
|
||||||
|
gboolean streams_aware;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstHLSDemuxClass
|
struct _GstHLSDemuxClass
|
||||||
|
Loading…
x
Reference in New Issue
Block a user