decodebin3: Handle streams without CAPS or TIME segment
authorEdward Hervey <edward@centricular.com>
Wed, 26 Apr 2023 10:20:25 +0000 (12:20 +0200)
committerTim-Philipp Müller <tim@centricular.com>
Thu, 27 Apr 2023 13:48:09 +0000 (14:48 +0100)
decodebin3 will do its best to figure out whether a parsebin is required to
process the incoming stream.

The problem is that for push-based stream it could happen that the stream would
not provide any caps, resulting in nothing being linked internally.

Furthermore, there is the possibility that a stream *with* caps would not be
using a TIME segment, which is required for multiqueue to properly work.

In order to fix those two issues, we force the usage of parsebin on push-based
streams:
* When the pad is linked, if upstream can't provide any caps
* When we get a non-TIME segment

Fixes #2521

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4499>

subprojects/gst-plugins-base/gst/playback/gstdecodebin3.c

index f96d44c..0da1f68 100644 (file)
@@ -912,6 +912,7 @@ gst_decodebin3_input_pad_link (GstPad * pad, GstObject * parent, GstPad * peer)
   GstDecodebin3 *dbin = (GstDecodebin3 *) parent;
   GstQuery *query;
   gboolean pull_mode = FALSE;
+  gboolean has_caps = TRUE;
   GstPadLinkReturn res = GST_PAD_LINK_OK;
   DecodebinInput *input = g_object_get_data (G_OBJECT (pad), "decodebin.input");
 
@@ -928,12 +929,26 @@ gst_decodebin3_input_pad_link (GstPad * pad, GstObject * parent, GstPad * peer)
 
   GST_DEBUG_OBJECT (dbin, "Upstream can do pull-based : %d", pull_mode);
 
-  /* If upstream *can* do pull-based, we always use a parsebin. If not, we will
-   * delay that decision to a later stage (caps/stream/collection event
-   * processing) to figure out if one is really needed or whether an identity
-   * element will be enough */
+  if (!pull_mode) {
+    /* If push-based, query if it will provide some caps */
+    query = gst_query_new_caps (NULL);
+    if (gst_pad_query (peer, query)) {
+      GstCaps *rescaps = NULL;
+      gst_query_parse_caps_result (query, &rescaps);
+      if (!rescaps || gst_caps_is_any (rescaps) || gst_caps_is_empty (rescaps)) {
+        GST_DEBUG_OBJECT (dbin, "Upstream can't provide caps");
+        has_caps = FALSE;
+      }
+    }
+    gst_query_unref (query);
+  }
+
+  /* If upstream *can* do pull-based OR it doesn't have any caps, we always use
+   * a parsebin. If not, we will delay that decision to a later stage
+   * (caps/stream/collection event processing) to figure out if one is really
+   * needed or whether an identity element will be enough */
   INPUT_LOCK (dbin);
-  if (pull_mode) {
+  if (pull_mode || !has_caps) {
     if (!ensure_input_parsebin (dbin, input))
       res = GST_PAD_LINK_REFUSED;
     else if (input->identity) {
@@ -1410,6 +1425,21 @@ sink_event_function (GstPad * sinkpad, GstDecodebin3 * dbin, GstEvent * event)
       }
       break;
     }
+    case GST_EVENT_SEGMENT:
+    {
+      const GstSegment *segment = NULL;
+      gst_event_parse_segment (event, &segment);
+
+      /* All data reaching multiqueue must be in time format. If it's not, we
+       * need to use a parsebin on the incoming stream.
+       */
+      if (segment && segment->format != GST_FORMAT_TIME && !input->parsebin) {
+        GST_DEBUG_OBJECT (sinkpad,
+            "Got a non-time segment, forcing parsebin handling");
+        ensure_input_parsebin (dbin, input);
+      }
+      break;
+    }
     default:
       break;
   }