hlsdemux: Re-use streams if possible 22/262822/1
authorEdward Hervey <edward@centricular.com>
Tue, 3 Nov 2020 16:48:02 +0000 (17:48 +0100)
committerGilbok Lee <gilbok.lee@samsung.com>
Fri, 20 Aug 2021 06:44:31 +0000 (15:44 +0900)
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.

Change-Id: I32cf158a0695e5af7de3a2e6001a0e9748f9e2b3
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/1757>

ext/hls/gsthlsdemux.c
ext/hls/gsthlsdemux.h

index e5aa439..b6f67ab 100644 (file)
@@ -328,6 +328,10 @@ gst_hls_demux_get_bitrate (GstHLSDemux * 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 */
   if (demux->streams) {
     GstAdaptiveDemuxStream *stream = demux->streams->data;
@@ -667,6 +671,12 @@ create_stream_for_playlist (GstAdaptiveDemux * demux, GstM3U8 * playlist,
     return;
   }
 
+#ifdef TIZEN_FEATURE_UPSTREAM
+  GST_DEBUG_OBJECT (demux,
+      "is_primary_playlist:%d selected:%d playlist name '%s'",
+      is_primary_playlist, selected, playlist->name);
+#endif
+
   stream = gst_adaptive_demux_stream_new (demux,
       gst_hls_demux_create_pad (hlsdemux));
 
@@ -697,6 +707,81 @@ create_stream_for_playlist (GstAdaptiveDemux * demux, GstM3U8 * playlist,
 #endif
 }
 #endif
+
+#ifdef TIZEN_FEATURE_UPSTREAM
+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;
+}
+#endif
 static gboolean
 gst_hls_demux_setup_streams (GstAdaptiveDemux * demux)
 {
@@ -708,7 +793,62 @@ gst_hls_demux_setup_streams (GstAdaptiveDemux * demux)
     GST_WARNING_OBJECT (demux, "Can't configure streams - no variant selected");
     return FALSE;
   }
+#ifdef TIZEN_FEATURE_UPSTREAM
+  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 ||
+            media->mtype == GST_HLS_MEDIA_TYPE_SUBTITLES) {
+          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 :( */
+#endif
   gst_hls_demux_clear_all_pending_data (hlsdemux);
 
   /* 1 output for the main playlist */
@@ -730,23 +870,34 @@ gst_hls_demux_setup_streams (GstAdaptiveDemux * demux)
       if (media->uri == NULL /* || media->mtype != GST_HLS_MEDIA_TYPE_AUDIO */ ) {
         /* No uri means this is a placeholder for a stream
          * contained in another mux */
+#ifdef TIZEN_FEATURE_UPSTREAM
+        GST_LOG_OBJECT (demux, "Skipping stream %s type %s with no URI",
+            media->name, gst_hls_media_type_get_name (media->mtype));
+#else
         GST_LOG_OBJECT (demux, "Skipping stream %s type %d with no URI",
             media->name, media->mtype);
+#endif
         mlist = mlist->next;
         continue;
       }
+#ifdef TIZEN_FEATURE_UPSTREAM
+      GST_LOG_OBJECT (demux, "media of type %s - %s, uri: %s",
+          gst_hls_media_type_get_name (i), media->name, media->uri);
+#else
       GST_LOG_OBJECT (demux, "media of type %d - %s, uri: %s", i,
           media->name, media->uri);
+#endif
+
 #ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
       create_stream_for_playlist (demux, media->playlist, FALSE, media->mtype);
 #else
       create_stream_for_playlist (demux, media->playlist, FALSE,
-          (media->mtype == GST_HLS_MEDIA_TYPE_VIDEO ||
-              media->mtype == GST_HLS_MEDIA_TYPE_AUDIO ||
+          (media->mtype == GST_HLS_MEDIA_TYPE_VIDEO
+              || media->mtype == GST_HLS_MEDIA_TYPE_AUDIO
 #ifdef TIZEN_FEATURE_HLSDEMUX_LANG_TAG
-              media->mtype == GST_HLS_MEDIA_TYPE_SUBTITLES), media);
+              || media->mtype == GST_HLS_MEDIA_TYPE_SUBTITLES), media);
 #else
-              media->mtype == GST_HLS_MEDIA_TYPE_SUBTITLES));
+              || media->mtype == GST_HLS_MEDIA_TYPE_SUBTITLES));
 #endif
 #endif
 
@@ -755,6 +906,15 @@ gst_hls_demux_setup_streams (GstAdaptiveDemux * demux)
   }
 
   return TRUE;
+
+#ifdef TIZEN_FEATURE_UPSTREAM
+no_match_error:
+  {
+    /* POST ERROR MESSAGE */
+    GST_ERROR_OBJECT (demux, "Should not happen ! Could not find old stream");
+    return FALSE;
+  }
+#endif
 }
 
 static const gchar *
@@ -796,15 +956,34 @@ gst_hls_demux_set_current_variant (GstHLSDemux * hlsdemux,
             gst_hls_variant_find_matching_media (variant, old_media);
 
         if (new_media) {
+#ifdef TIZEN_FEATURE_UPSTREAM
+          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);
+#endif
           new_media->playlist->sequence = old_media->playlist->sequence;
           new_media->playlist->sequence_position =
               old_media->playlist->sequence_position;
+#ifdef TIZEN_FEATURE_UPSTREAM
+        } else {
+          GST_LOG_OBJECT (hlsdemux,
+              "Didn't find a matching variant for '%s' '%s'", old_media->name,
+              old_media->uri);
+#endif
         }
         mlist = mlist->next;
       }
     }
-
+#ifdef TIZEN_FEATURE_UPSTREAM
+    if (hlsdemux->previous_variant)
+      gst_hls_variant_stream_unref (hlsdemux->previous_variant);
+    /* Steal the reference */
+    hlsdemux->previous_variant = hlsdemux->current_variant;
+#else
     gst_hls_variant_stream_unref (hlsdemux->current_variant);
+#endif
   }
 
   hlsdemux->current_variant = gst_hls_variant_stream_ref (variant);
@@ -1096,8 +1275,13 @@ gst_hls_demux_handle_buffer (GstAdaptiveDemux * demux,
       return GST_FLOW_OK;
     }
 
+#ifdef TIZEN_FEATURE_UPSTREAM
+    GST_DEBUG_OBJECT (stream->pad,
+        "Typefind result: %" GST_PTR_FORMAT " prob:%d", caps, prob);
+#else
     GST_DEBUG_OBJECT (hlsdemux, "Typefind result: %" GST_PTR_FORMAT " prob:%d",
         caps, prob);
+#endif
 
     hls_stream->stream_type = caps_to_reader (caps);
     gst_hlsdemux_tsreader_set_type (&hls_stream->tsreader,
@@ -1436,7 +1620,19 @@ gst_hls_demux_reset (GstAdaptiveDemux * ademux)
     gst_hls_variant_stream_unref (demux->current_variant);
     demux->current_variant = NULL;
   }
+#ifdef TIZEN_FEATURE_UPSTREAM
+  if (demux->previous_variant != NULL) {
+    gst_hls_variant_stream_unref (demux->previous_variant);
+    demux->previous_variant = NULL;
+  }
+#endif
   demux->srcpad_counter = 0;
+#ifdef TIZEN_FEATURE_UPSTREAM
+  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);
+#endif
 
   gst_hls_demux_clear_all_pending_data (demux);
   GST_M3U8_CLIENT_UNLOCK (hlsdemux->client);
@@ -2209,4 +2405,4 @@ gst_hlsdemux_set_language_tags (GstAdaptiveDemuxStream *stream, const gchar *lan
 
   return TRUE;
 }
-#endif
\ No newline at end of file
+#endif
index 670b970..548927a 100644 (file)
@@ -150,6 +150,12 @@ struct _GstHLSDemux
   GstHLSMasterPlaylist *master;
 
   GstHLSVariantStream  *current_variant;
+#ifdef TIZEN_FEATURE_UPSTREAM
+  /* The previous variant, used to transition streams over */
+  GstHLSVariantStream  *previous_variant;
+
+  gboolean streams_aware;
+#endif
 };
 
 struct _GstHLSDemuxClass