videorate: Add a `max-closing-segment-duplication-duration` property
authorThibault Saunier <tsaunier@igalia.com>
Tue, 6 Sep 2022 21:25:50 +0000 (17:25 -0400)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Tue, 20 Sep 2022 13:23:02 +0000 (13:23 +0000)
This allows users to let videorate fully fill the segments when received
EOS or on new segment, removing an arbitrary limit of 25 duplicates which
might not be what the user wants (for example on low FPS stream in GES,
that sometimes leaded to broken behavior)

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

13 files changed:
subprojects/gst-plugins-base/docs/plugins/gst_plugins_cache.json
subprojects/gst-plugins-base/gst/videorate/gstvideorate.c
subprojects/gst-plugins-base/gst/videorate/gstvideorate.h
subprojects/gst-plugins-base/tests/validate/meson.build
subprojects/gst-plugins-base/tests/validate/videorate/duplicate_on_eos.validatetest [new file with mode: 0644]
subprojects/gst-plugins-base/tests/validate/videorate/duplicate_on_eos/flow-expectations/log-videorate-sink-expected [new file with mode: 0644]
subprojects/gst-plugins-base/tests/validate/videorate/duplicate_on_eos/flow-expectations/log-videorate-src-expected [new file with mode: 0644]
subprojects/gst-plugins-base/tests/validate/videorate/duplicate_on_eos_disbaled.validatetest [new file with mode: 0644]
subprojects/gst-plugins-base/tests/validate/videorate/duplicate_on_eos_disbaled/flow-expectations/log-videorate-sink-expected [new file with mode: 0644]
subprojects/gst-plugins-base/tests/validate/videorate/duplicate_on_eos_disbaled/flow-expectations/log-videorate-src-expected [new file with mode: 0644]
subprojects/gst-plugins-base/tests/validate/videorate/duplicate_on_eos_half_sec.validatetest [new file with mode: 0644]
subprojects/gst-plugins-base/tests/validate/videorate/duplicate_on_eos_half_sec/flow-expectations/log-videorate-sink-expected [new file with mode: 0644]
subprojects/gst-plugins-base/tests/validate/videorate/duplicate_on_eos_half_sec/flow-expectations/log-videorate-src-expected [new file with mode: 0644]

index 8828ace..27d27c8 100644 (file)
                         "type": "guint64",
                         "writable": false
                     },
+                    "max-closing-segment-duplication-duration": {
+                        "blurb": "Maximum duration of duplicated buffers to close current segment",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "1000000000",
+                        "max": "18446744073709551615",
+                        "min": "0",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "guint64",
+                        "writable": true
+                    },
                     "max-duplication-time": {
                         "blurb": "Do not duplicate frames if the gap exceeds this period (in ns) (0 = disabled)",
                         "conditionally-available": false,
index 2fdd6d3..c347b94 100644 (file)
@@ -100,6 +100,7 @@ enum
 #define DEFAULT_MAX_RATE        G_MAXINT
 #define DEFAULT_RATE            1.0
 #define DEFAULT_MAX_DUPLICATION_TIME      0
+#define DEFAULT_MAX_CLOSING_SEGMENT_DUPLICATION_DURATION   GST_SECOND
 
 enum
 {
@@ -115,7 +116,8 @@ enum
   PROP_AVERAGE_PERIOD,
   PROP_MAX_RATE,
   PROP_RATE,
-  PROP_MAX_DUPLICATION_TIME
+  PROP_MAX_DUPLICATION_TIME,
+  PROP_MAX_CLOSING_SEGMENT_DUPLICATION_DURATION
 };
 
 static GstStaticPadTemplate gst_video_rate_src_template =
@@ -295,6 +297,27 @@ gst_video_rate_class_init (GstVideoRateClass * klass)
           0, G_MAXUINT64, DEFAULT_MAX_DUPLICATION_TIME,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+  /**
+   * GstVideoRate:max-closing-segment-duplication-duration:
+   *
+   * Limits the maximum duration for which the last buffer is duplicated when
+   * finalizing a segment or on EOS. When receiving an EOS event or a new
+   * segment, videorate duplicates the last frame to close the configured
+   * segment (copying the last buffer until its #GstSegment.stop time (or
+   * #GstSegment.start time for reverse playback) is reached), this property
+   * ensures that it won't push buffers covering a duration longer than
+   * specified.
+   *
+   * Since: 1.22
+   */
+  g_object_class_install_property (object_class,
+      PROP_MAX_CLOSING_SEGMENT_DUPLICATION_DURATION,
+      g_param_spec_uint64 ("max-closing-segment-duplication-duration",
+          "Maximum closing segment duplication duration",
+          "Maximum duration of duplicated buffers to close current segment", 0,
+          G_MAXUINT64, DEFAULT_MAX_CLOSING_SEGMENT_DUPLICATION_DURATION,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   gst_element_class_set_static_metadata (element_class,
       "Video rate adjuster", "Filter/Effect/Video",
       "Drops/duplicates/adjusts timestamps on video frames to make a perfect stream",
@@ -637,6 +660,8 @@ gst_video_rate_init (GstVideoRate * videorate)
   videorate->rate = DEFAULT_RATE;
   videorate->pending_rate = DEFAULT_RATE;
   videorate->max_duplication_time = DEFAULT_MAX_DUPLICATION_TIME;
+  videorate->max_closing_segment_duplication_duration =
+      DEFAULT_MAX_CLOSING_SEGMENT_DUPLICATION_DURATION;
 
   videorate->from_rate_numerator = 0;
   videorate->from_rate_denominator = 0;
@@ -782,7 +807,90 @@ gst_video_rate_notify_duplicate (GstVideoRate * videorate)
   g_object_notify_by_pspec ((GObject *) videorate, pspec_duplicate);
 }
 
-#define MAGIC_LIMIT  25
+static gboolean
+gst_video_rate_check_duplicate_to_close_segment (GstVideoRate * videorate,
+    GstClockTime last_input_ts, gboolean is_first)
+{
+  GstClockTime next_stream_time = videorate->next_ts - videorate->segment.base;
+  GstClockTime max_closing_segment_duplication_duration =
+      videorate->max_closing_segment_duplication_duration;
+
+  if (!GST_CLOCK_TIME_IS_VALID (videorate->next_ts))
+    return FALSE;
+
+  if (videorate->segment.rate > 0.0) {
+
+    if (!GST_CLOCK_TIME_IS_VALID (videorate->segment.stop)) {
+      /* Ensure that if no 'stop' is set, we push the last frame anyway */
+      return is_first;
+    }
+
+    if (next_stream_time >= videorate->segment.stop)
+      return FALSE;
+
+    if (GST_CLOCK_TIME_IS_VALID (max_closing_segment_duplication_duration)) {
+      if (last_input_ts > videorate->next_ts)
+        return TRUE;
+
+      return (videorate->next_ts - last_input_ts <
+          max_closing_segment_duplication_duration);
+    }
+
+    return TRUE;
+  }
+
+  /* Reverse playback */
+
+  if (!GST_CLOCK_TIME_IS_VALID (videorate->segment.start)) {
+    /* Ensure that if no 'start' is set, we push the last frame anyway */
+    return is_first;
+  }
+
+  if (next_stream_time < videorate->segment.start)
+    return FALSE;
+
+  if (GST_CLOCK_TIME_IS_VALID (max_closing_segment_duplication_duration)) {
+    if (last_input_ts < videorate->next_ts)
+      return TRUE;
+
+    return (last_input_ts - videorate->next_ts <
+        max_closing_segment_duplication_duration);
+  }
+
+  return TRUE;
+}
+
+static gint
+gst_video_rate_duplicate_to_close_segment (GstVideoRate * videorate)
+{
+  gint count = 0;
+  GstFlowReturn res;
+  GstClockTime last_input_ts = videorate->prev_ts;
+
+  if (videorate->drop_only)
+    return count;
+
+  if (!videorate->prevbuf) {
+    GST_INFO_OBJECT (videorate, "got EOS before any buffer was received");
+
+    return count;
+  }
+
+  res = GST_FLOW_OK;
+  /* fill up to the end of current segment */
+  while (res == GST_FLOW_OK
+      && gst_video_rate_check_duplicate_to_close_segment (videorate,
+          last_input_ts, count < 1)) {
+    res =
+        gst_video_rate_flush_prev (videorate, count > 0, GST_CLOCK_TIME_NONE,
+        FALSE);
+
+    count++;
+  }
+
+  return count;
+}
+
 static gboolean
 gst_video_rate_sink_event (GstBaseTransform * trans, GstEvent * event)
 {
@@ -804,30 +912,8 @@ gst_video_rate_sink_event (GstBaseTransform * trans, GstEvent * event)
 
       /* close up the previous segment, if appropriate */
       if (videorate->prevbuf) {
-        gint count = 0;
-        GstFlowReturn res;
-
-        res = GST_FLOW_OK;
-        /* fill up to the end of current segment,
-         * or only send out the stored buffer if there is no specific stop.
-         * regardless, prevent going loopy in strange cases */
-        while (res == GST_FLOW_OK && count <= MAGIC_LIMIT
-            && !videorate->drop_only
-            && ((videorate->segment.rate > 0.0
-                    && GST_CLOCK_TIME_IS_VALID (videorate->segment.stop)
-                    && GST_CLOCK_TIME_IS_VALID (videorate->next_ts)
-                    && videorate->next_ts - videorate->segment.base <
-                    videorate->segment.stop) || (videorate->segment.rate < 0.0
-                    && GST_CLOCK_TIME_IS_VALID (videorate->segment.start)
-                    && GST_CLOCK_TIME_IS_VALID (videorate->next_ts)
-                    && videorate->next_ts - videorate->segment.base >=
-                    videorate->segment.start)
-                || count < 1)) {
-          res =
-              gst_video_rate_flush_prev (videorate, count > 0,
-              GST_CLOCK_TIME_NONE, FALSE);
-          count++;
-        }
+        /* fill up to the end of current segment */
+        gint count = gst_video_rate_duplicate_to_close_segment (videorate);
         if (count > 1) {
           videorate->dup += count - 1;
           if (!videorate->silent)
@@ -871,34 +957,23 @@ gst_video_rate_sink_event (GstBaseTransform * trans, GstEvent * event)
 
       /* If the segment has a stop position, fill the segment */
       if (GST_CLOCK_TIME_IS_VALID (videorate->segment.stop)) {
-        /* fill up to the end of current segment,
-         * or only send out the stored buffer if there is no specific stop.
-         * regardless, prevent going loopy in strange cases */
-        while (res == GST_FLOW_OK && count <= MAGIC_LIMIT
-            && !videorate->drop_only
-            && ((videorate->segment.rate > 0.0
-                    && GST_CLOCK_TIME_IS_VALID (videorate->segment.stop)
-                    && GST_CLOCK_TIME_IS_VALID (videorate->next_ts)
-                    && videorate->next_ts - videorate->segment.base <
-                    videorate->segment.stop) || (videorate->segment.rate < 0.0
-                    && GST_CLOCK_TIME_IS_VALID (videorate->segment.start)
-                    && GST_CLOCK_TIME_IS_VALID (videorate->next_ts)
-                    && videorate->next_ts - videorate->segment.base >=
-                    videorate->segment.start)
-            )) {
-          res = gst_video_rate_flush_prev (videorate, count > 0,
-              GST_CLOCK_TIME_NONE, FALSE);
-          count++;
-        }
+        /* fill up to the end of current segment */
+        count = gst_video_rate_duplicate_to_close_segment (videorate);
       } else if (!videorate->drop_only && videorate->prevbuf) {
         /* Output at least one frame but if the buffer duration is valid, output
          * enough frames to use the complete buffer duration */
         if (GST_BUFFER_DURATION_IS_VALID (videorate->prevbuf)) {
-          GstClockTime end_ts =
-              videorate->next_ts + GST_BUFFER_DURATION (videorate->prevbuf);
+          GstClockTime end_ts, duration =
+              GST_BUFFER_DURATION (videorate->prevbuf);
 
-          while (res == GST_FLOW_OK && count <= MAGIC_LIMIT &&
-              ((videorate->segment.rate > 0.0
+          if (GST_CLOCK_TIME_IS_VALID
+              (videorate->max_closing_segment_duplication_duration))
+            duration =
+                MIN (videorate->max_closing_segment_duplication_duration,
+                duration);
+
+          end_ts = videorate->next_ts + duration;
+          while (res == GST_FLOW_OK && ((videorate->segment.rate > 0.0
                       && GST_CLOCK_TIME_IS_VALID (videorate->segment.stop)
                       && GST_CLOCK_TIME_IS_VALID (videorate->next_ts)
                       && videorate->next_ts - videorate->segment.base < end_ts)
@@ -1839,6 +1914,10 @@ gst_video_rate_set_property (GObject * object,
     case PROP_MAX_DUPLICATION_TIME:
       videorate->max_duplication_time = g_value_get_uint64 (value);
       break;
+    case PROP_MAX_CLOSING_SEGMENT_DUPLICATION_DURATION:
+      videorate->max_closing_segment_duplication_duration =
+          g_value_get_uint64 (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1901,6 +1980,10 @@ gst_video_rate_get_property (GObject * object,
     case PROP_MAX_DUPLICATION_TIME:
       g_value_set_uint64 (value, videorate->max_duplication_time);
       break;
+    case PROP_MAX_CLOSING_SEGMENT_DUPLICATION_DURATION:
+      g_value_set_uint64 (value,
+          videorate->max_closing_segment_duplication_duration);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
index 1dfd866..02d26e3 100644 (file)
@@ -58,6 +58,7 @@ struct _GstVideoRate
   gboolean force_variable_rate;
   gboolean updating_caps;
   guint64 max_duplication_time;
+  guint64 max_closing_segment_duplication_duration;
 
   /* segment handling */
   GstSegment segment;
index f055ffd..abcd16e 100644 (file)
@@ -17,6 +17,9 @@ tests = [
     'videorate/rate_0_5_with_decoder',
     'videorate/rate_2_0',
     'videorate/rate_2_0_with_decoder',
+    'videorate/duplicate_on_eos',
+    'videorate/duplicate_on_eos_disbaled',
+    'videorate/duplicate_on_eos_half_sec',
     'compositor/renogotiate_failing_unsupported_src_format',
     'giosrc/read-growing-file',
     'encodebin/set-encoder-properties',
diff --git a/subprojects/gst-plugins-base/tests/validate/videorate/duplicate_on_eos.validatetest b/subprojects/gst-plugins-base/tests/validate/videorate/duplicate_on_eos.validatetest
new file mode 100644 (file)
index 0000000..8162596
--- /dev/null
@@ -0,0 +1,21 @@
+meta,
+    args = {
+        "videotestsrc pattern=ball animation-mode=frames ! video/x-raw,format=I420,framerate=1/1,width=320,height=240 ! videorate name=videorate ! video/x-raw,framerate=30/1 ! fakesink sync=true qos=true",
+    },
+    configs = {
+       "$(validateflow), pad=videorate:sink, buffers-checksum=as-id, ignored-event-types={ tag }",
+       "$(validateflow), pad=videorate:src, buffers-checksum=as-id, ignored-event-types={ tag }",
+    },
+    handles-states=true,
+    ignore-eos=true
+
+pause
+
+seek, flags=flush+accurate, start=0, stop=3.0
+play
+crank-clock, repeat=91
+wait, message-type=eos
+
+# Last buffer is duplicated until the full segment is filled
+check-position, expected-position=3.0
+stop
diff --git a/subprojects/gst-plugins-base/tests/validate/videorate/duplicate_on_eos/flow-expectations/log-videorate-sink-expected b/subprojects/gst-plugins-base/tests/validate/videorate/duplicate_on_eos/flow-expectations/log-videorate-sink-expected
new file mode 100644 (file)
index 0000000..edaf641
--- /dev/null
@@ -0,0 +1,12 @@
+event stream-start: GstEventStreamStart, flags=(GstStreamFlags)GST_STREAM_FLAG_NONE, group-id=(uint)1;
+event caps: video/x-raw, format=(string)I420, framerate=(fraction)1/1, height=(int)240, interlace-mode=(string)progressive, multiview-mode=(string)mono, pixel-aspect-ratio=(fraction)1/1, width=(int)320;
+event segment: format=TIME, start=0:00:00.000000000, offset=0:00:00.000000000, stop=none, time=0:00:00.000000000, base=0:00:00.000000000, position=0:00:00.000000000
+buffer: content-id=0, pts=0:00:00.000000000, dur=0:00:01.000000000, flags=discont
+buffer: content-id=1, pts=0:00:01.000000000, dur=0:00:01.000000000
+event flush-start: (no structure)
+event flush-stop: GstEventFlushStop, reset-time=(boolean)true;
+event segment: format=TIME, start=0:00:00.000000000, offset=0:00:00.000000000, stop=0:00:03.000000000, flags=0x01, time=0:00:00.000000000, base=0:00:00.000000000, position=0:00:00.000000000
+buffer: content-id=0, pts=0:00:00.000000000, dur=0:00:01.000000000, flags=discont
+buffer: content-id=1, pts=0:00:01.000000000, dur=0:00:01.000000000
+buffer: content-id=2, pts=0:00:02.000000000, dur=0:00:01.000000000
+event eos: (no structure)
diff --git a/subprojects/gst-plugins-base/tests/validate/videorate/duplicate_on_eos/flow-expectations/log-videorate-src-expected b/subprojects/gst-plugins-base/tests/validate/videorate/duplicate_on_eos/flow-expectations/log-videorate-src-expected
new file mode 100644 (file)
index 0000000..58a15c0
--- /dev/null
@@ -0,0 +1,98 @@
+event stream-start: GstEventStreamStart, flags=(GstStreamFlags)GST_STREAM_FLAG_NONE, group-id=(uint)1;
+event caps: video/x-raw, format=(string)I420, framerate=(fraction)30/1, height=(int)240, interlace-mode=(string)progressive, multiview-mode=(string)mono, pixel-aspect-ratio=(fraction)1/1, width=(int)320;
+event segment: format=TIME, start=0:00:00.000000000, offset=0:00:00.000000000, stop=none, time=0:00:00.000000000, base=0:00:00.000000000, position=0:00:00.000000000
+buffer: content-id=0, pts=0:00:00.000000000, dur=0:00:00.033333333, flags=discont
+event flush-start: (no structure)
+event flush-stop: GstEventFlushStop, reset-time=(boolean)true;
+event segment: format=TIME, start=0:00:00.000000000, offset=0:00:00.000000000, stop=0:00:03.000000000, flags=0x01, time=0:00:00.000000000, base=0:00:00.000000000, position=0:00:00.000000000
+buffer: content-id=0, pts=0:00:00.000000000, dur=0:00:00.033333333, flags=discont
+buffer: content-id=0, pts=0:00:00.033333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=0, pts=0:00:00.066666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=0, pts=0:00:00.100000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=0, pts=0:00:00.133333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=0, pts=0:00:00.166666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=0, pts=0:00:00.200000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=0, pts=0:00:00.233333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=0, pts=0:00:00.266666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=0, pts=0:00:00.300000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=0, pts=0:00:00.333333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=0, pts=0:00:00.366666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=0, pts=0:00:00.400000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=0, pts=0:00:00.433333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=0, pts=0:00:00.466666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=0, pts=0:00:00.500000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:00.533333333, dur=0:00:00.033333333
+buffer: content-id=1, pts=0:00:00.566666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=1, pts=0:00:00.600000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:00.633333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:00.666666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=1, pts=0:00:00.700000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:00.733333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:00.766666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=1, pts=0:00:00.800000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:00.833333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:00.866666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=1, pts=0:00:00.900000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:00.933333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:00.966666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=1, pts=0:00:01.000000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:01.033333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:01.066666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=1, pts=0:00:01.100000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:01.133333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:01.166666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=1, pts=0:00:01.200000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:01.233333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:01.266666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=1, pts=0:00:01.300000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:01.333333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:01.366666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=1, pts=0:00:01.400000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:01.433333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:01.466666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=1, pts=0:00:01.500000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:01.533333333, dur=0:00:00.033333333
+buffer: content-id=2, pts=0:00:01.566666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=2, pts=0:00:01.600000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:01.633333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:01.666666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=2, pts=0:00:01.700000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:01.733333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:01.766666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=2, pts=0:00:01.800000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:01.833333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:01.866666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=2, pts=0:00:01.900000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:01.933333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:01.966666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=2, pts=0:00:02.000000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:02.033333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:02.066666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=2, pts=0:00:02.100000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:02.133333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:02.166666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=2, pts=0:00:02.200000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:02.233333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:02.266666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=2, pts=0:00:02.300000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:02.333333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:02.366666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=2, pts=0:00:02.400000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:02.433333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:02.466666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=2, pts=0:00:02.500000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:02.533333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:02.566666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=2, pts=0:00:02.600000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:02.633333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:02.666666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=2, pts=0:00:02.700000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:02.733333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:02.766666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=2, pts=0:00:02.800000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:02.833333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:02.866666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=2, pts=0:00:02.900000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:02.933333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=2, pts=0:00:02.966666666, dur=0:00:00.033333334, flags=gap
+event eos: (no structure)
diff --git a/subprojects/gst-plugins-base/tests/validate/videorate/duplicate_on_eos_disbaled.validatetest b/subprojects/gst-plugins-base/tests/validate/videorate/duplicate_on_eos_disbaled.validatetest
new file mode 100644 (file)
index 0000000..8549da4
--- /dev/null
@@ -0,0 +1,23 @@
+meta,
+    args = {
+        "videotestsrc pattern=ball animation-mode=frames ! video/x-raw,format=I420,framerate=1/1,width=320,height=240 ! videorate max-closing-segment-duplication-duration=0 name=videorate ! video/x-raw,framerate=30/1 ! fakesink sync=true qos=true",
+    },
+    configs = {
+       "$(validateflow), pad=videorate:sink, buffers-checksum=as-id, ignored-event-types={ tag }",
+       "$(validateflow), pad=videorate:src, buffers-checksum=as-id, ignored-event-types={ tag }",
+    },
+    handles-states=true,
+    ignore-eos=true
+
+pause
+
+seek, flags=flush+accurate, start=0, stop=2.0
+play
+crank-clock, repeat=31
+wait, message-type=eos
+
+# Second buffer won't be duplicated as we disabled on on EOS
+check-position, expected-position=1.0
+stop
+
+
diff --git a/subprojects/gst-plugins-base/tests/validate/videorate/duplicate_on_eos_disbaled/flow-expectations/log-videorate-sink-expected b/subprojects/gst-plugins-base/tests/validate/videorate/duplicate_on_eos_disbaled/flow-expectations/log-videorate-sink-expected
new file mode 100644 (file)
index 0000000..7ecb4e9
--- /dev/null
@@ -0,0 +1,11 @@
+event stream-start: GstEventStreamStart, flags=(GstStreamFlags)GST_STREAM_FLAG_NONE, group-id=(uint)1;
+event caps: video/x-raw, format=(string)I420, framerate=(fraction)1/1, height=(int)240, interlace-mode=(string)progressive, multiview-mode=(string)mono, pixel-aspect-ratio=(fraction)1/1, width=(int)320;
+event segment: format=TIME, start=0:00:00.000000000, offset=0:00:00.000000000, stop=none, time=0:00:00.000000000, base=0:00:00.000000000, position=0:00:00.000000000
+buffer: content-id=0, pts=0:00:00.000000000, dur=0:00:01.000000000, flags=discont
+buffer: content-id=1, pts=0:00:01.000000000, dur=0:00:01.000000000
+event flush-start: (no structure)
+event flush-stop: GstEventFlushStop, reset-time=(boolean)true;
+event segment: format=TIME, start=0:00:00.000000000, offset=0:00:00.000000000, stop=0:00:02.000000000, flags=0x01, time=0:00:00.000000000, base=0:00:00.000000000, position=0:00:00.000000000
+buffer: content-id=0, pts=0:00:00.000000000, dur=0:00:01.000000000, flags=discont
+buffer: content-id=1, pts=0:00:01.000000000, dur=0:00:01.000000000
+event eos: (no structure)
diff --git a/subprojects/gst-plugins-base/tests/validate/videorate/duplicate_on_eos_disbaled/flow-expectations/log-videorate-src-expected b/subprojects/gst-plugins-base/tests/validate/videorate/duplicate_on_eos_disbaled/flow-expectations/log-videorate-src-expected
new file mode 100644 (file)
index 0000000..c25ae88
--- /dev/null
@@ -0,0 +1,38 @@
+event stream-start: GstEventStreamStart, flags=(GstStreamFlags)GST_STREAM_FLAG_NONE, group-id=(uint)1;
+event caps: video/x-raw, format=(string)I420, framerate=(fraction)30/1, height=(int)240, interlace-mode=(string)progressive, multiview-mode=(string)mono, pixel-aspect-ratio=(fraction)1/1, width=(int)320;
+event segment: format=TIME, start=0:00:00.000000000, offset=0:00:00.000000000, stop=none, time=0:00:00.000000000, base=0:00:00.000000000, position=0:00:00.000000000
+buffer: content-id=0, pts=0:00:00.000000000, dur=0:00:00.033333333, flags=discont
+event flush-start: (no structure)
+event flush-stop: GstEventFlushStop, reset-time=(boolean)true;
+event segment: format=TIME, start=0:00:00.000000000, offset=0:00:00.000000000, stop=0:00:02.000000000, flags=0x01, time=0:00:00.000000000, base=0:00:00.000000000, position=0:00:00.000000000
+buffer: content-id=0, pts=0:00:00.000000000, dur=0:00:00.033333333, flags=discont
+buffer: content-id=0, pts=0:00:00.033333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=0, pts=0:00:00.066666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=0, pts=0:00:00.100000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=0, pts=0:00:00.133333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=0, pts=0:00:00.166666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=0, pts=0:00:00.200000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=0, pts=0:00:00.233333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=0, pts=0:00:00.266666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=0, pts=0:00:00.300000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=0, pts=0:00:00.333333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=0, pts=0:00:00.366666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=0, pts=0:00:00.400000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=0, pts=0:00:00.433333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=0, pts=0:00:00.466666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=0, pts=0:00:00.500000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:00.533333333, dur=0:00:00.033333333
+buffer: content-id=1, pts=0:00:00.566666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=1, pts=0:00:00.600000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:00.633333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:00.666666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=1, pts=0:00:00.700000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:00.733333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:00.766666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=1, pts=0:00:00.800000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:00.833333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:00.866666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=1, pts=0:00:00.900000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:00.933333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:00.966666666, dur=0:00:00.033333334, flags=gap
+event eos: (no structure)
diff --git a/subprojects/gst-plugins-base/tests/validate/videorate/duplicate_on_eos_half_sec.validatetest b/subprojects/gst-plugins-base/tests/validate/videorate/duplicate_on_eos_half_sec.validatetest
new file mode 100644 (file)
index 0000000..3d9ce96
--- /dev/null
@@ -0,0 +1,25 @@
+meta,
+    args = {
+        "videotestsrc pattern=ball animation-mode=frames ! video/x-raw,format=I420,framerate=1/1,width=320,height=240 ! videorate max-closing-segment-duplication-duration=500000000 name=videorate ! video/x-raw,framerate=30/1 ! fakesink sync=true qos=true",
+    },
+    configs = {
+       "$(validateflow), pad=videorate:sink, buffers-checksum=as-id, ignored-event-types={ tag }",
+       "$(validateflow), pad=videorate:src, buffers-checksum=as-id, ignored-event-types={ tag }",
+    },
+    handles-states=true,
+    ignore-eos=true
+
+pause
+
+seek, flags=flush+accurate, start=0, stop=2.0
+play
+crank-clock, repeat=46
+wait, message-type=eos
+
+# At most 0.5 second can be output after EOS, meaning that after the second
+# buffer (which starts at 1) videorate will duplicate the frame for 0.5 secs
+check-position, expected-position=1.5
+stop
+
+
+
diff --git a/subprojects/gst-plugins-base/tests/validate/videorate/duplicate_on_eos_half_sec/flow-expectations/log-videorate-sink-expected b/subprojects/gst-plugins-base/tests/validate/videorate/duplicate_on_eos_half_sec/flow-expectations/log-videorate-sink-expected
new file mode 100644 (file)
index 0000000..7ecb4e9
--- /dev/null
@@ -0,0 +1,11 @@
+event stream-start: GstEventStreamStart, flags=(GstStreamFlags)GST_STREAM_FLAG_NONE, group-id=(uint)1;
+event caps: video/x-raw, format=(string)I420, framerate=(fraction)1/1, height=(int)240, interlace-mode=(string)progressive, multiview-mode=(string)mono, pixel-aspect-ratio=(fraction)1/1, width=(int)320;
+event segment: format=TIME, start=0:00:00.000000000, offset=0:00:00.000000000, stop=none, time=0:00:00.000000000, base=0:00:00.000000000, position=0:00:00.000000000
+buffer: content-id=0, pts=0:00:00.000000000, dur=0:00:01.000000000, flags=discont
+buffer: content-id=1, pts=0:00:01.000000000, dur=0:00:01.000000000
+event flush-start: (no structure)
+event flush-stop: GstEventFlushStop, reset-time=(boolean)true;
+event segment: format=TIME, start=0:00:00.000000000, offset=0:00:00.000000000, stop=0:00:02.000000000, flags=0x01, time=0:00:00.000000000, base=0:00:00.000000000, position=0:00:00.000000000
+buffer: content-id=0, pts=0:00:00.000000000, dur=0:00:01.000000000, flags=discont
+buffer: content-id=1, pts=0:00:01.000000000, dur=0:00:01.000000000
+event eos: (no structure)
diff --git a/subprojects/gst-plugins-base/tests/validate/videorate/duplicate_on_eos_half_sec/flow-expectations/log-videorate-src-expected b/subprojects/gst-plugins-base/tests/validate/videorate/duplicate_on_eos_half_sec/flow-expectations/log-videorate-src-expected
new file mode 100644 (file)
index 0000000..32eb33c
--- /dev/null
@@ -0,0 +1,53 @@
+event stream-start: GstEventStreamStart, flags=(GstStreamFlags)GST_STREAM_FLAG_NONE, group-id=(uint)1;
+event caps: video/x-raw, format=(string)I420, framerate=(fraction)30/1, height=(int)240, interlace-mode=(string)progressive, multiview-mode=(string)mono, pixel-aspect-ratio=(fraction)1/1, width=(int)320;
+event segment: format=TIME, start=0:00:00.000000000, offset=0:00:00.000000000, stop=none, time=0:00:00.000000000, base=0:00:00.000000000, position=0:00:00.000000000
+buffer: content-id=0, pts=0:00:00.000000000, dur=0:00:00.033333333, flags=discont
+event flush-start: (no structure)
+event flush-stop: GstEventFlushStop, reset-time=(boolean)true;
+event segment: format=TIME, start=0:00:00.000000000, offset=0:00:00.000000000, stop=0:00:02.000000000, flags=0x01, time=0:00:00.000000000, base=0:00:00.000000000, position=0:00:00.000000000
+buffer: content-id=0, pts=0:00:00.000000000, dur=0:00:00.033333333, flags=discont
+buffer: content-id=0, pts=0:00:00.033333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=0, pts=0:00:00.066666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=0, pts=0:00:00.100000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=0, pts=0:00:00.133333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=0, pts=0:00:00.166666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=0, pts=0:00:00.200000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=0, pts=0:00:00.233333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=0, pts=0:00:00.266666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=0, pts=0:00:00.300000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=0, pts=0:00:00.333333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=0, pts=0:00:00.366666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=0, pts=0:00:00.400000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=0, pts=0:00:00.433333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=0, pts=0:00:00.466666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=0, pts=0:00:00.500000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:00.533333333, dur=0:00:00.033333333
+buffer: content-id=1, pts=0:00:00.566666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=1, pts=0:00:00.600000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:00.633333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:00.666666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=1, pts=0:00:00.700000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:00.733333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:00.766666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=1, pts=0:00:00.800000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:00.833333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:00.866666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=1, pts=0:00:00.900000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:00.933333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:00.966666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=1, pts=0:00:01.000000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:01.033333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:01.066666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=1, pts=0:00:01.100000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:01.133333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:01.166666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=1, pts=0:00:01.200000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:01.233333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:01.266666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=1, pts=0:00:01.300000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:01.333333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:01.366666666, dur=0:00:00.033333334, flags=gap
+buffer: content-id=1, pts=0:00:01.400000000, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:01.433333333, dur=0:00:00.033333333, flags=gap
+buffer: content-id=1, pts=0:00:01.466666666, dur=0:00:00.033333334, flags=gap
+event eos: (no structure)