From 4e25c519defc61b6ce92466d2f6bab5c0258c880 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Sun, 21 Aug 2022 03:37:40 +1000 Subject: [PATCH] dashdemux: Preserve current representation on live manifest updates When updating a manifest during live playback, preserve the current representation for each stream. During update_fragment_info, if the current representation changed because it couldn't be matched, trigger a caps change and new header download. This reverts commit e0e1db212fd0df2239583b9099fc4361adeded05 and reapplies "dashdemux: Fix issue when manifest update sets slow start without passing necessary header & caps changes downstream" with changes. Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/issues/507 Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/issues/1729 Part-of: --- .../gst-plugins-bad/ext/dash/gstdashdemux.c | 84 +++++++++++++++++++++- .../gst-plugins-bad/ext/dash/gstdashdemux.h | 2 + .../gst-plugins-bad/ext/dash/gstmpdclient.c | 36 +++++++--- .../gst-plugins-bad/ext/dash/gstmpdclient.h | 2 + 4 files changed, 112 insertions(+), 12 deletions(-) diff --git a/subprojects/gst-plugins-bad/ext/dash/gstdashdemux.c b/subprojects/gst-plugins-bad/ext/dash/gstdashdemux.c index f26067d..3d91cbe 100644 --- a/subprojects/gst-plugins-bad/ext/dash/gstdashdemux.c +++ b/subprojects/gst-plugins-bad/ext/dash/gstdashdemux.c @@ -844,6 +844,14 @@ gst_dash_demux_setup_all_streams (GstDashDemux * demux) stream = (GstDashDemuxStream *) gst_adaptive_demux_stream_new (GST_ADAPTIVE_DEMUX_CAST (demux), srcpad); stream->active_stream = active_stream; + + if (active_stream->cur_representation) { + stream->last_representation_id = + g_strdup (stream->active_stream->cur_representation->id); + } else { + stream->last_representation_id = NULL; + } + s = gst_caps_get_structure (caps, 0); stream->allow_sidx = gst_mpd_client_has_isoff_ondemand_profile (demux->client); @@ -1332,6 +1340,43 @@ gst_dash_demux_stream_update_fragment_info (GstAdaptiveDemuxStream * stream) if (gst_mpd_client_get_next_fragment_timestamp (dashdemux->client, dashstream->index, &ts)) { + /* For live streams, check whether the underlying representation changed + * (due to a manifest update with no matching representation) */ + if (gst_mpd_client_is_live (dashdemux->client) + && !GST_ADAPTIVE_DEMUX_STREAM_NEED_HEADER (stream)) { + if (dashstream->active_stream + && dashstream->active_stream->cur_representation) { + /* id specifies an identifier for this Representation. The + * identifier shall be unique within a Period unless the + * Representation is functionally identically to another + * Representation in the same Period. */ + if (g_strcmp0 (dashstream->active_stream->cur_representation->id, + dashstream->last_representation_id)) { + GstCaps *caps; + stream->need_header = TRUE; + + GST_INFO_OBJECT (dashdemux, + "Representation changed from %s to %s - updating to bitrate %d", + GST_STR_NULL (dashstream->last_representation_id), + GST_STR_NULL (dashstream->active_stream->cur_representation->id), + dashstream->active_stream->cur_representation->bandwidth); + + caps = + gst_dash_demux_get_input_caps (dashdemux, + dashstream->active_stream); + gst_adaptive_demux_stream_set_caps (stream, caps); + + /* Update the stored last representation id */ + g_free (dashstream->last_representation_id); + dashstream->last_representation_id = + g_strdup (dashstream->active_stream->cur_representation->id); + } + } else { + g_free (dashstream->last_representation_id); + dashstream->last_representation_id = NULL; + } + } + if (GST_ADAPTIVE_DEMUX_STREAM_NEED_HEADER (stream)) { gst_adaptive_demux_stream_fragment_clear (&stream->fragment); gst_dash_demux_stream_update_headers_info (stream); @@ -2236,6 +2281,10 @@ gst_dash_demux_stream_select_bitrate (GstAdaptiveDemuxStream * stream, gst_adaptive_demux_stream_set_caps (stream, caps); ret = TRUE; + /* Update the stored last representation id */ + g_free (dashstream->last_representation_id); + dashstream->last_representation_id = + g_strdup (active_stream->cur_representation->id); } else { GST_WARNING_OBJECT (demux, "Can not switch representation, aborting..."); } @@ -2468,7 +2517,8 @@ gst_dash_demux_update_manifest_data (GstAdaptiveDemux * demux, streams = demux->streams; } - /* update the streams to play from the next segment */ + /* update the streams to preserve the current representation if there is one, + * and to play from the next segment */ for (iter = streams, streams_iter = new_client->active_streams; iter && streams_iter; iter = g_list_next (iter), streams_iter = g_list_next (streams_iter)) { @@ -2485,6 +2535,37 @@ gst_dash_demux_update_manifest_data (GstAdaptiveDemux * demux, return GST_FLOW_EOS; } + if (new_stream->cur_adapt_set + && demux_stream->last_representation_id != NULL) { + + GList *rep_list = new_stream->cur_adapt_set->Representations; + GstMPDRepresentationNode *rep_node = + gst_mpd_client_get_representation_with_id (rep_list, + demux_stream->last_representation_id); + if (rep_node != NULL) { + if (gst_mpd_client_setup_representation (new_client, new_stream, + rep_node)) { + GST_DEBUG_OBJECT (GST_ADAPTIVE_DEMUX_STREAM_PAD (demux_stream), + "Found and set up matching representation %s in new manifest", + demux_stream->last_representation_id); + } else { + GST_ERROR_OBJECT (GST_ADAPTIVE_DEMUX_STREAM_PAD (demux_stream), + "Failed to set up representation %s in new manifest", + demux_stream->last_representation_id); + gst_mpd_client_free (new_client); + gst_buffer_unmap (buffer, &mapinfo); + return GST_FLOW_EOS; + } + } else { + /* If we failed to find the current representation, + * then update_fragment_info() will reconfigure to the + * new settings after the current download finishes */ + GST_WARNING_OBJECT (GST_ADAPTIVE_DEMUX_STREAM_PAD (demux_stream), + "Failed to find representation %s in new manifest", + demux_stream->last_representation_id); + } + } + if (gst_mpd_client_get_next_fragment_timestamp (dashdemux->client, demux_stream->index, &ts) || gst_mpd_client_get_last_fragment_timestamp_end (dashdemux->client, @@ -3560,6 +3641,7 @@ gst_dash_demux_stream_free (GstAdaptiveDemuxStream * stream) gst_isoff_moof_box_free (dash_stream->moof); if (dash_stream->moof_sync_samples) g_array_free (dash_stream->moof_sync_samples, TRUE); + g_free (dash_stream->last_representation_id); } static GstDashDemuxClockDrift * diff --git a/subprojects/gst-plugins-bad/ext/dash/gstdashdemux.h b/subprojects/gst-plugins-bad/ext/dash/gstdashdemux.h index 88a5bfc..6374fc9 100644 --- a/subprojects/gst-plugins-bad/ext/dash/gstdashdemux.h +++ b/subprojects/gst-plugins-bad/ext/dash/gstdashdemux.h @@ -112,6 +112,8 @@ struct _GstDashDemuxStream GstClockTime target_time; /* Average skip-ahead time (only in trickmode-key-units) */ GstClockTime average_skip_size; + + gchar *last_representation_id; }; /** diff --git a/subprojects/gst-plugins-bad/ext/dash/gstmpdclient.c b/subprojects/gst-plugins-bad/ext/dash/gstmpdclient.c index fdaaa65..68ea759 100644 --- a/subprojects/gst-plugins-bad/ext/dash/gstmpdclient.c +++ b/subprojects/gst-plugins-bad/ext/dash/gstmpdclient.c @@ -79,7 +79,7 @@ gst_mpd_client_get_adaptation_set_with_id (GList * adaptation_sets, guint id) return NULL; } -static GstMPDNode * +GstMPDRepresentationNode * gst_mpd_client_get_representation_with_id (GList * representations, gchar * rep_id) { @@ -89,11 +89,24 @@ gst_mpd_client_get_representation_with_id (GList * representations, for (list = g_list_first (representations); list; list = g_list_next (list)) { representation = (GstMPDRepresentationNode *) list->data; if (!g_strcmp0 (representation->id, rep_id)) - return GST_MPD_NODE (representation); + return GST_MPD_REPRESENTATION_NODE (representation); } return NULL; } +static GstMPDNode * +gst_mpd_client_get_representation_with_id_filter (GList * representations, + gchar * rep_id) +{ + GstMPDRepresentationNode *representation = + gst_mpd_client_get_representation_with_id (representations, rep_id); + + if (representation != NULL) + return GST_MPD_NODE (representation); + + return NULL; +} + static gchar * _generate_new_string_id (GList * list, const gchar * tuple, MpdClientStringIDFilter filter) @@ -3243,8 +3256,8 @@ gst_mpd_client_set_representation_node (GstMPDClient * client, (period_node->AdaptationSets, adaptation_set_id)); g_return_val_if_fail (adap_set_node != NULL, NULL); rep_node = - GST_MPD_REPRESENTATION_NODE (gst_mpd_client_get_representation_with_id - (adap_set_node->Representations, representation_id)); + gst_mpd_client_get_representation_with_id (adap_set_node->Representations, + representation_id); if (!rep_node) { rep_node = gst_mpd_representation_node_new (); if (representation_id) @@ -3252,7 +3265,8 @@ gst_mpd_client_set_representation_node (GstMPDClient * client, else rep_node->id = _generate_new_string_id (adap_set_node->Representations, - "representation_%.2d", gst_mpd_client_get_representation_with_id); + "representation_%.2d", + gst_mpd_client_get_representation_with_id_filter); GST_DEBUG_OBJECT (client, "Add a new representation with id %s", rep_node->id); adap_set_node->Representations = @@ -3289,8 +3303,8 @@ gst_mpd_client_set_segment_list (GstMPDClient * client, g_return_val_if_fail (adaptation_set != NULL, FALSE); representation = - GST_MPD_REPRESENTATION_NODE (gst_mpd_client_get_representation_with_id - (adaptation_set->Representations, rep_id)); + gst_mpd_client_get_representation_with_id + (adaptation_set->Representations, rep_id); if (!representation->SegmentList) { representation->SegmentList = gst_mpd_segment_list_node_new (); } @@ -3326,8 +3340,8 @@ gst_mpd_client_set_segment_template (GstMPDClient * client, g_return_val_if_fail (adaptation_set != NULL, FALSE); representation = - GST_MPD_REPRESENTATION_NODE (gst_mpd_client_get_representation_with_id - (adaptation_set->Representations, rep_id)); + gst_mpd_client_get_representation_with_id + (adaptation_set->Representations, rep_id); if (!representation->SegmentTemplate) { representation->SegmentTemplate = gst_mpd_segment_template_node_new (); } @@ -3365,8 +3379,8 @@ gst_mpd_client_add_segment_url (GstMPDClient * client, g_return_val_if_fail (adaptation_set != NULL, FALSE); representation = - GST_MPD_REPRESENTATION_NODE (gst_mpd_client_get_representation_with_id - (adaptation_set->Representations, rep_id)); + gst_mpd_client_get_representation_with_id + (adaptation_set->Representations, rep_id); if (!representation->SegmentList) { representation->SegmentList = gst_mpd_segment_list_node_new (); diff --git a/subprojects/gst-plugins-bad/ext/dash/gstmpdclient.h b/subprojects/gst-plugins-bad/ext/dash/gstmpdclient.h index bc422fc..b15b5ca 100644 --- a/subprojects/gst-plugins-bad/ext/dash/gstmpdclient.h +++ b/subprojects/gst-plugins-bad/ext/dash/gstmpdclient.h @@ -99,6 +99,8 @@ gboolean gst_mpd_client_has_previous_period (GstMPDClient * client); /* Representation selection */ gint gst_mpd_client_get_rep_idx_with_max_bandwidth (GList *Representations, gint64 max_bandwidth, gint max_video_width, gint max_video_height, gint max_video_framerate_n, gint max_video_framerate_d); gint gst_mpd_client_get_rep_idx_with_min_bandwidth (GList * Representations); +GstMPDRepresentationNode* gst_mpd_client_get_representation_with_id (GList * representations, gchar * rep_id); + GstDateTime * gst_mpd_client_get_availability_start_time (GstMPDClient * client); -- 2.7.4