dashdemux: Preserve current representation on live manifest updates
authorJan Schmidt <jan@centricular.com>
Sat, 20 Aug 2022 17:37:40 +0000 (03:37 +1000)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Mon, 5 Sep 2022 16:07:00 +0000 (16:07 +0000)
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: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2920>

subprojects/gst-plugins-bad/ext/dash/gstdashdemux.c
subprojects/gst-plugins-bad/ext/dash/gstdashdemux.h
subprojects/gst-plugins-bad/ext/dash/gstmpdclient.c
subprojects/gst-plugins-bad/ext/dash/gstmpdclient.h

index f26067d..3d91cbe 100644 (file)
@@ -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 *
index 88a5bfc..6374fc9 100644 (file)
@@ -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;
 };
 
 /**
index fdaaa65..68ea759 100644 (file)
@@ -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 ();
index bc422fc..b15b5ca 100644 (file)
@@ -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);