ges: Refactor the way we plug converters in effects
authorThibault Saunier <tsaunier@igalia.com>
Tue, 9 Jun 2020 04:03:57 +0000 (00:03 -0400)
committerThibault Saunier <tsaunier@igalia.com>
Wed, 17 Jun 2020 14:48:41 +0000 (10:48 -0400)
Stopping to do it at the bin description level but properly
plugging them where they are needed and cleanly ghosting the pads
where it makes most sense.

This introduces support for GES to request pads on the most upstream
element in case no static pad can be ghosted.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-editing-services/-/merge_requests/187>

ges/ges-command-line-formatter.c
ges/ges-effect-asset.c
ges/ges-effect-clip.c
ges/ges-effect.c
ges/ges-gerror.h
ges/ges-internal.h
tests/check/meson.build
tests/check/scenarios/complex_effect_bin_desc.validatetest [new file with mode: 0644]
tests/check/scenarios/complex_effect_bin_desc/flow-expectations/log-videosink-sink-expected [new file with mode: 0644]

index 2c46d7a07a06e513ee206a31a487344065f77a93..90e91c8d331c7e578a17b97fa712664efe21a02c 100644 (file)
@@ -157,7 +157,7 @@ static GESCommandLineOption options[] = {
       },
       {
         "inpoint", "i", GST_TYPE_CLOCK_TIME, NULL,
-        "Implies that the effect has a internal content"
+        "Implies that the effect has 'internal content'"
         "(see [ges_track_element_set_has_internal_source](ges_track_element_set_has_internal_source))",
       },
       {
index bfc4c5d2c667bdfee44c90e9c236d313e7988b73..eb85b38cd73b95e4a5a347470ff17d8b016fd94b 100644 (file)
@@ -49,7 +49,7 @@ _fill_track_type (GESAsset * asset)
   gchar *bin_desc;
   const gchar *id = ges_asset_get_id (asset);
 
-  bin_desc = ges_effect_assect_id_get_type_and_bindesc (id, &ttype, NULL);
+  bin_desc = ges_effect_asset_id_get_type_and_bindesc (id, &ttype, NULL);
 
   if (bin_desc) {
     ges_track_element_asset_set_track_type (GES_TRACK_ELEMENT_ASSET (asset),
@@ -110,35 +110,282 @@ ges_effect_asset_class_init (GESEffectAssetClass * klass)
   asset_class->extract = _extract;
 }
 
+static gboolean
+find_compatible_pads (GstElement * bin, const gchar * bin_desc,
+    GstElement * child, GstCaps * valid_caps, GstPad ** srcpad,
+    GList ** sinkpads, GList ** elems_with_reqsink,
+    GList ** elems_with_reqsrc, GError ** error)
+{
+  GList *tmp, *tmptemplate;
+
+  for (tmp = child->pads; tmp; tmp = tmp->next) {
+    GstCaps *caps;
+    GstPad *pad = tmp->data;
+
+    if (GST_PAD_PEER (pad))
+      continue;
+
+    if (GST_PAD_IS_SRC (pad) && *srcpad) {
+      g_set_error (error, GES_ERROR, GES_ERROR_INVALID_EFFECT_BIN_DESCRIPTION,
+          "More than 1 source pad in effect '%s', that is not handled",
+          bin_desc);
+      return FALSE;
+    }
+
+    caps = gst_pad_query_caps (pad, NULL);
+    if (gst_caps_can_intersect (caps, valid_caps)) {
+      if (GST_PAD_IS_SINK (pad))
+        *sinkpads = g_list_append (*sinkpads, gst_object_ref (pad));
+      else
+        *srcpad = gst_object_ref (pad);
+    } else {
+      GST_LOG_OBJECT (pad, "Can't link pad %" GST_PTR_FORMAT, caps);
+    }
+
+    gst_caps_unref (caps);
+  }
+
+  tmptemplate =
+      gst_element_class_get_pad_template_list (GST_ELEMENT_GET_CLASS (child));
+  for (; tmptemplate; tmptemplate = tmptemplate->next) {
+    GstPadTemplate *template = tmptemplate->data;
+
+    if (template->direction == GST_PAD_SINK) {
+      if (template->presence == GST_PAD_REQUEST)
+        *elems_with_reqsink = g_list_append (*elems_with_reqsink, child);
+    }
+  }
+
+  return TRUE;
+}
+
+static GstPad *
+request_pad (GstElement * element, GstPadDirection direction)
+{
+  GstPad *pad = NULL;
+  GList *templates;
+
+  templates = gst_element_class_get_pad_template_list
+      (GST_ELEMENT_GET_CLASS (element));
+
+  for (; templates; templates = templates->next) {
+    GstPadTemplate *templ = (GstPadTemplate *) templates->data;
+
+    GST_LOG_OBJECT (element, "Trying template %s",
+        GST_PAD_TEMPLATE_NAME_TEMPLATE (templ));
+
+    if ((GST_PAD_TEMPLATE_DIRECTION (templ) == direction) &&
+        (GST_PAD_TEMPLATE_PRESENCE (templ) == GST_PAD_REQUEST)) {
+      pad =
+          gst_element_get_request_pad (element,
+          GST_PAD_TEMPLATE_NAME_TEMPLATE (templ));
+      if (pad)
+        break;
+    }
+  }
+
+  return pad;
+}
+
+static GstPad *
+get_pad_from_elements_with_request_pad (GstElement * effect,
+    const gchar * bin_desc, GList * requestable, GstPadDirection direction,
+    GError ** error)
+{
+  GstElement *request_element = NULL;
+
+  if (!requestable) {
+    g_set_error (error, GES_ERROR, GES_ERROR_INVALID_EFFECT_BIN_DESCRIPTION,
+        "No %spads available for effect: %s",
+        (direction == GST_PAD_SRC) ? "src" : "sink", bin_desc);
+
+    return NULL;
+  }
+
+  request_element = requestable->data;
+  if (requestable->next) {
+    GstIterator *it = gst_bin_iterate_sorted (GST_BIN (effect));
+    GValue v;
+
+    while (gst_iterator_next (it, &v) != GST_ITERATOR_DONE) {
+      GstElement *tmpe = g_value_get_object (&v);
+
+      if (g_list_find (requestable, tmpe)) {
+        request_element = tmpe;
+        if (direction == GST_PAD_SRC) {
+          break;
+        }
+      }
+      g_value_reset (&v);
+    }
+    gst_iterator_free (it);
+  }
+
+  return request_pad (request_element, direction);
+}
+
+static gboolean
+ghost_pad (GstElement * effect, const gchar * bin_desc, GstPad * pad,
+    gint n_pad, const gchar * converter_str, GError ** error)
+{
+  gchar *name;
+  GstPad *peer, *ghosted;
+  GstPadLinkReturn lret;
+  GstElement *converter;
+
+  if (!converter_str) {
+    ghosted = pad;
+    goto ghost;
+  }
+
+  converter = gst_parse_bin_from_description_full (converter_str, TRUE, NULL,
+      GST_PARSE_FLAG_NO_SINGLE_ELEMENT_BINS | GST_PARSE_FLAG_PLACE_IN_BIN,
+      error);
+
+  if (!converter) {
+    GST_ERROR_OBJECT (effect, "Could not create converter '%s'", converter_str);
+    return FALSE;
+  }
+
+  peer =
+      GST_PAD_IS_SINK (pad) ? converter->srcpads->data : converter->sinkpads->
+      data;
+
+  gst_bin_add (GST_BIN (effect), converter);
+  lret =
+      gst_pad_link (GST_PAD_IS_SINK (pad) ? peer : pad,
+      GST_PAD_IS_SINK (pad) ? pad : peer);
+
+  if (lret != GST_PAD_LINK_OK) {
+    gst_object_unref (converter);
+    g_set_error (error, GES_ERROR, GES_ERROR_INVALID_EFFECT_BIN_DESCRIPTION,
+        "Effect %s can not link converter %s with %s", bin_desc, converter_str,
+        gst_pad_link_get_name (lret));
+    return FALSE;
+  }
+
+  ghosted =
+      GST_PAD_IS_SRC (pad) ? converter->srcpads->data : converter->sinkpads->
+      data;
+
+ghost:
+
+  if (GST_PAD_IS_SINK (pad))
+    name = g_strdup_printf ("sink_%d", n_pad);
+  else
+    name = g_strdup_printf ("src");
+
+  gst_element_add_pad (effect, gst_ghost_pad_new (name, ghosted));
+  g_free (name);
+
+  return TRUE;
+}
+
+GstElement *
+ges_effect_from_description (const gchar * bin_desc, GESTrackType type,
+    GError ** error)
+{
+
+  gint n_sink = 0;
+  GstPad *srcpad = NULL;
+  GstCaps *valid_caps = NULL;
+  const gchar *converter_str = NULL;
+  GList *tmp, *sinkpads = NULL, *elems_with_reqsink = NULL,
+      *elems_with_reqsrc = NULL;
+  GstElement *effect =
+      gst_parse_bin_from_description_full (bin_desc, FALSE, NULL,
+      GST_PARSE_FLAG_PLACE_IN_BIN | GST_PARSE_FLAG_FATAL_ERRORS, error);
+
+  if (!effect) {
+    GST_ERROR ("An error occurred while creating: %s",
+        (error && *error) ? (*error)->message : "Unknown error");
+    goto err;
+  }
+
+  if (type == GES_TRACK_TYPE_VIDEO) {
+    valid_caps = gst_caps_from_string ("video/x-raw(ANY)");
+    converter_str = "videoconvert";
+  } else if (type == GES_TRACK_TYPE_AUDIO) {
+    valid_caps = gst_caps_from_string ("audio/x-raw(ANY)");
+    converter_str = "audioconvert ! audioresample ! audioconvert";
+  } else {
+    valid_caps = gst_caps_new_any ();
+  }
+
+  for (tmp = GST_BIN_CHILDREN (effect); tmp; tmp = tmp->next) {
+    if (!find_compatible_pads (effect, bin_desc, tmp->data, valid_caps, &srcpad,
+            &sinkpads, &elems_with_reqsink, &elems_with_reqsrc, error))
+      goto err;
+  }
+
+  if (!sinkpads) {
+    GstPad *sinkpad = get_pad_from_elements_with_request_pad (effect, bin_desc,
+        elems_with_reqsink, GST_PAD_SINK, error);
+    if (!sinkpad)
+      goto err;
+    sinkpads = g_list_append (sinkpads, sinkpad);
+  }
+
+  if (!srcpad) {
+    srcpad = get_pad_from_elements_with_request_pad (effect, bin_desc,
+        elems_with_reqsrc, GST_PAD_SRC, error);
+    if (!srcpad)
+      goto err;
+  }
+
+  for (tmp = sinkpads; tmp; tmp = tmp->next) {
+    if (!ghost_pad (effect, bin_desc, tmp->data, n_sink, converter_str, error))
+      goto err;
+    n_sink++;
+  }
+
+  if (!ghost_pad (effect, bin_desc, srcpad, 0, converter_str, error))
+    goto err;
+
+done:
+  g_list_free (elems_with_reqsink);
+  g_list_free (elems_with_reqsrc);
+  g_list_free_full (sinkpads, gst_object_unref);
+  gst_clear_caps (&valid_caps);
+  gst_clear_object (&srcpad);
+
+  return effect;
+
+err:
+  gst_clear_object (&effect);
+  goto done;
+}
+
 gchar *
-ges_effect_assect_id_get_type_and_bindesc (const char *id,
+ges_effect_asset_id_get_type_and_bindesc (const char *id,
     GESTrackType * track_type, GError ** error)
 {
   GList *tmp;
   GstElement *effect;
   gchar **typebin_desc = NULL;
+  const gchar *user_bindesc;
   gchar *bindesc = NULL;
 
   *track_type = GES_TRACK_TYPE_UNKNOWN;
   typebin_desc = g_strsplit (id, " ", 2);
   if (!g_strcmp0 (typebin_desc[0], "audio")) {
     *track_type = GES_TRACK_TYPE_AUDIO;
-    bindesc = g_strdup (typebin_desc[1]);
+    user_bindesc = typebin_desc[1];
   } else if (!g_strcmp0 (typebin_desc[0], "video")) {
     *track_type = GES_TRACK_TYPE_VIDEO;
-    bindesc = g_strdup (typebin_desc[1]);
+    user_bindesc = typebin_desc[1];
   } else {
-    bindesc = g_strdup (id);
+    *track_type = GES_TRACK_TYPE_UNKNOWN;
+    user_bindesc = id;
   }
 
+  bindesc = g_strdup (user_bindesc);
   g_strfreev (typebin_desc);
 
   effect = gst_parse_bin_from_description (bindesc, TRUE, error);
   if (effect == NULL) {
+    GST_ERROR ("Could not create element from: %s", bindesc);
     g_free (bindesc);
-
-    GST_ERROR ("Could not create element from: %s", id);
-
     return NULL;
   }
 
@@ -171,7 +418,15 @@ ges_effect_assect_id_get_type_and_bindesc (const char *id,
     *track_type = GES_TRACK_TYPE_VIDEO;
     GST_ERROR ("Could not determine track type for %s, defaulting to video",
         id);
+
   }
 
+  if (!(effect = ges_effect_from_description (bindesc, *track_type, error))) {
+    g_free (bindesc);
+
+    return NULL;
+  }
+  gst_object_unref (effect);
+
   return bindesc;
 }
index e9d7a9bfd23da96274f5643ef284195d4f529672..4b89054ed5f39b0af3810a1b36ece61142b6fc6d 100644 (file)
@@ -80,7 +80,7 @@ extractable_get_parameters_from_id (const gchar * id, guint * n_params)
 
   for (i = 0; effects_desc[i] && i < 2; i++) {
     bin_desc =
-        ges_effect_assect_id_get_type_and_bindesc (effects_desc[i], &ttype,
+        ges_effect_asset_id_get_type_and_bindesc (effects_desc[i], &ttype,
         NULL);
 
     if (ttype == GES_TRACK_TYPE_AUDIO) {
index 7bba621f3a1e4abd7f953f9d48609c26d78f8c50..fb6bf782ee853ad82c9089efb26092e92743486a 100644 (file)
 /**
  * SECTION:geseffect
  * @title: GESEffect
- * @short_description: adds an effect build from a parse-launch style
- * bin description to a stream in a GESSourceClip or a GESLayer
+ * @short_description: adds an effect build from a parse-launch style bin
+ * description to a stream in a GESSourceClip or a GESLayer
  *
- * Currently we only support effects with 1 sinkpad and 1 sourcepad
- * with the exception of `gesaudiomixer` and `gescompositor` which
- * can be used as effects.
+ * Currently we only support effects with N sinkpads and one single srcpad.
+ * Apart from `gesaudiomixer` and `gescompositor` which can be used as effects
+ * and where sinkpads will be requested as needed based on the timeline topology
+ * GES will always request at most one sinkpad per effect (when required).
+ *
+ * > Note: GES always adds converters (`audioconvert ! audioresample !
+ * > audioconvert` for audio effects and `videoconvert` for video effects) to
+ * > make it simpler for end users.
  */
 #ifdef HAVE_CONFIG_H
 #include "config.h"
@@ -67,7 +72,7 @@ extractable_check_id (GType type, const gchar * id, GError ** error)
   gchar *bin_desc, *real_id;
   GESTrackType ttype;
 
-  bin_desc = ges_effect_assect_id_get_type_and_bindesc (id, &ttype, error);
+  bin_desc = ges_effect_asset_id_get_type_and_bindesc (id, &ttype, error);
 
   if (bin_desc == NULL)
     return NULL;
@@ -92,7 +97,7 @@ extractable_get_parameters_from_id (const gchar * id, guint * n_params)
   gchar *bin_desc;
   GESTrackType ttype;
 
-  bin_desc = ges_effect_assect_id_get_type_and_bindesc (id, &ttype, NULL);
+  bin_desc = ges_effect_asset_id_get_type_and_bindesc (id, &ttype, NULL);
 
   params[0].name = "bin-description";
   g_value_init (&params[0].value, G_TYPE_STRING);
@@ -221,37 +226,6 @@ ges_effect_finalize (GObject * object)
   G_OBJECT_CLASS (ges_effect_parent_class)->finalize (object);
 }
 
-static void
-ghost_compatible_pads (GstElement * bin, GstElement * child,
-    GstCaps * valid_caps, gint * n_src, gint * n_sink)
-{
-  GList *tmp;
-
-  for (tmp = child->pads; tmp; tmp = tmp->next) {
-    GstCaps *caps;
-    GstPad *pad = tmp->data;
-
-    if (GST_PAD_PEER (pad))
-      continue;
-
-    caps = gst_pad_query_caps (pad, NULL);
-
-    if (gst_caps_can_intersect (caps, valid_caps)) {
-      gchar *name =
-          g_strdup_printf ("%s_%d", GST_PAD_IS_SINK (pad) ? "sink" : "src",
-          GST_PAD_IS_SINK (pad) ? *n_sink++ : *n_src++);
-
-      GST_DEBUG_OBJECT (bin, "Ghosting pad: %" GST_PTR_FORMAT, pad);
-      gst_element_add_pad (GST_ELEMENT (bin), gst_ghost_pad_new (name, pad));
-      g_free (name);
-    } else {
-      GST_DEBUG_OBJECT (pad, "Can't ghost pad %" GST_PTR_FORMAT, caps);
-    }
-
-    gst_caps_unref (caps);
-  }
-}
-
 static gdouble
 _get_rate_factor (GESBaseEffect * effect, GHashTable * rate_values)
 {
@@ -321,14 +295,11 @@ _rate_sink_to_source (GESBaseEffect * effect, GstClockTime time,
 static GstElement *
 ges_effect_create_element (GESTrackElement * object)
 {
-  GESBaseEffect *base_effect = GES_BASE_EFFECT (object);
-  GESEffectClass *class;
   GList *tmp;
+  GESEffectClass *class;
   GstElement *effect;
-  gchar *bin_desc;
-  GstCaps *valid_caps;
-  gint n_src = 0, n_sink = 0;
   gboolean is_rate_effect = FALSE;
+  GESBaseEffect *base_effect = GES_BASE_EFFECT (object);
 
   GError *error = NULL;
   GESEffect *self = GES_EFFECT (object);
@@ -341,39 +312,15 @@ ges_effect_create_element (GESTrackElement * object)
       !g_strcmp0 (self->priv->bin_description, "gescompositor"))
     return gst_element_factory_make (self->priv->bin_description, NULL);
 
-  if (type == GES_TRACK_TYPE_VIDEO) {
-    bin_desc = g_strconcat ("videoconvert name=pre_video_convert ! ",
-        self->priv->bin_description, " ! videoconvert name=post_video_convert",
-        NULL);
-    valid_caps = gst_caps_from_string ("video/x-raw(ANY)");
-  } else if (type == GES_TRACK_TYPE_AUDIO) {
-    bin_desc =
-        g_strconcat ("audioconvert ! audioresample !",
-        self->priv->bin_description, NULL);
-    valid_caps = gst_caps_from_string ("audio/x-raw(ANY)");
-  } else {
-    g_assert_not_reached ();
-  }
-
-  effect = gst_parse_bin_from_description (bin_desc, FALSE, &error);
-  g_free (bin_desc);
+  effect =
+      ges_effect_from_description (self->priv->bin_description, type, &error);
   if (error != NULL) {
-    GST_ERROR ("An error occured while creating the GstElement: %s",
+    GST_ERROR ("An error occurred while creating the GstElement: %s",
         error->message);
     g_error_free (error);
     goto fail;
   }
 
-  for (tmp = GST_BIN_CHILDREN (effect); tmp; tmp = tmp->next) {
-    ghost_compatible_pads (effect, tmp->data, valid_caps, &n_src, &n_sink);
-
-    if (n_src > 1) {
-      GST_ERROR ("More than 1 source pad in the effect, that is not possible");
-
-      goto fail;
-    }
-  }
-
   ges_track_element_add_children_props (object, effect, NULL,
       blacklisted_factories, NULL);
 
@@ -394,7 +341,6 @@ ges_effect_create_element (GESTrackElement * object)
     GST_ERROR_OBJECT (object, "Failed to set rate translation functions");
 
 done:
-  gst_clear_caps (&valid_caps);
 
   return effect;
 
index 74721e532ee1c67fb2a4cc5a662010df581b3687..ad2782a16003b3c466c4b040c08605ebea4ff723 100644 (file)
@@ -60,6 +60,7 @@ typedef enum
   GES_ERROR_NEGATIVE_TIME,
   GES_ERROR_NOT_ENOUGH_INTERNAL_CONTENT,
   GES_ERROR_INVALID_OVERLAP_IN_TRACK,
+  GES_ERROR_INVALID_EFFECT_BIN_DESCRIPTION,
 } GESError;
 
 G_END_DECLS
index 2feb0b6ba02796f0f0589133af34bef158dcba9f..751c544f3d87836f66ee311b777f2fc152f7af77 100644 (file)
@@ -219,9 +219,9 @@ G_GNUC_INTERNAL gboolean
 ges_asset_request_id_update (GESAsset *asset, gchar **proposed_id,
     GError *error);
 G_GNUC_INTERNAL gchar *
-ges_effect_assect_id_get_type_and_bindesc (const char    *id,
-                                           GESTrackType  *track_type,
-                                           GError       **error);
+ges_effect_asset_id_get_type_and_bindesc (const char    *id,
+                                          GESTrackType  *track_type,
+                                          GError       **error);
 
 G_GNUC_INTERNAL void _ges_uri_asset_cleanup (void);
 
@@ -488,7 +488,7 @@ G_GNUC_INTERNAL GESTitleSource     * ges_title_source_new      (void);
 G_GNUC_INTERNAL GESVideoTestSource * ges_video_test_source_new (void);
 
 /****************************************************
- *                GESBaseEffect                     *
+ *                GES*Effect                     *
  ****************************************************/
 G_GNUC_INTERNAL gchar *
 ges_base_effect_get_time_property_name        (GESBaseEffect * effect,
@@ -504,6 +504,10 @@ G_GNUC_INTERNAL GstClockTime
 ges_base_effect_translate_sink_to_source_time (GESBaseEffect * effect,
                                                GstClockTime time,
                                                GHashTable * time_property_values);
+G_GNUC_INTERNAL GstElement *
+ges_effect_from_description                   (const gchar *bin_desc,
+                                               GESTrackType type,
+                                               GError **error);
 
 /****************************************************
  *              GESTimelineElement                  *
index 7c27bf35547aa80793e68d2d3aa68b1eedb47290..b377cb1d0798d3ab65d19a80c97e3c7b627a65ed 100644 (file)
@@ -82,6 +82,7 @@ if gstvalidate_dep.found()
     'seek_with_stop': true,
     'seek_with_stop.check_clock_sync': true,
     'edit_while_seeked_with_stop': true,
+    'complex_effect_bin_desc': true,
   }
 
   foreach scenario, is_validatetest: scenarios
@@ -107,7 +108,7 @@ if gstvalidate_dep.found()
 endif
 
 if build_gir
-  # Make sure to use the subproject gst-validate-launcher if avalaible.
+  # Make sure to use the subproject gst-validate-launcher if available.
   if gstvalidate_dep.found() and gstvalidate_dep.type_name() == 'internal'
     runtests = subproject('gst-devtools').get_variable('launcher')
   else
diff --git a/tests/check/scenarios/complex_effect_bin_desc.validatetest b/tests/check/scenarios/complex_effect_bin_desc.validatetest
new file mode 100644 (file)
index 0000000..d958544
--- /dev/null
@@ -0,0 +1,24 @@
+# Check that we can have effect with sources integrated where GES will request a pad on some elements
+# In that example, we are blending a green rectangle on top of a blue GESVideoTestSource using an effect
+meta,
+    tool = "ges-launch-$(gst_api_version)",
+    handles-states=true,
+    args = {
+        "--track-types", "video",
+        "--videosink", "$(videosink) name=videosink",
+        "--video-caps", "video/x-raw, format=I420, width=1280, height=720, framerate=30/1, chroma-site=jpeg, colorimetry=bt601",
+    },
+    configs = {
+        "$(validateflow), pad=videosink:sink, buffers-checksum=true, ignored-fields=\"stream-start={stream-id,group-id,stream}, segment={position,}\", ignored-event-types={gap}",
+    }
+
+
+add-clip, name=c0, asset-id=GESTestClip, layer-priority=0, type=GESTestClip, start=0, duration=0.1
+set-child-properties, element-name=c0, pattern=blue
+
+container-add-child,
+    container-name=c0,
+    asset-id="videotestsrc pattern=green ! video/x-raw,width=640,height=360 ! compositor sink_0::xpos=320 sink_0::ypos=180 sink_0::zorder=500",
+    child-type=GESEffect,
+    child-name=effect
+play
\ No newline at end of file
diff --git a/tests/check/scenarios/complex_effect_bin_desc/flow-expectations/log-videosink-sink-expected b/tests/check/scenarios/complex_effect_bin_desc/flow-expectations/log-videosink-sink-expected
new file mode 100644 (file)
index 0000000..7541dda
--- /dev/null
@@ -0,0 +1,9 @@
+event stream-start: GstEventStreamStart, flags=(GstStreamFlags)GST_STREAM_FLAG_NONE;
+event caps: video/x-raw, format=(string)I420, width=(int)1280, height=(int)720, framerate=(fraction)30/1, chroma-site=(string)jpeg, colorimetry=(string)bt601;
+event segment: format=TIME, start=0:00:00.000000000, offset=0:00:00.000000000, stop=0:00:00.100000000, flags=0x01, time=0:00:00.000000000, base=0:00:00.000000000
+buffer: checksum=d2d49287a7d0ddd7b5fadbb60c3220623119fea8, pts=0:00:00.000000000, dur=0:00:00.033333333
+buffer: checksum=d2d49287a7d0ddd7b5fadbb60c3220623119fea8, pts=0:00:00.033333333, dur=0:00:00.033333334
+buffer: checksum=d2d49287a7d0ddd7b5fadbb60c3220623119fea8, pts=0:00:00.066666667, dur=0:00:00.033333333
+event segment: format=TIME, start=0:00:00.100000000, offset=0:00:00.000000000, stop=0:00:00.100000001, flags=0x01, time=0:00:00.100000000, base=0:00:00.100000000
+buffer: checksum=b4a126ab26f314a74ef860a9af457327a28d680b, pts=0:00:00.100000000, dur=0:00:00.000000001
+event eos: (no structure)