"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,
#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
{
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 =
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",
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;
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)
{
/* 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)
/* 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)
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;
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;
gboolean force_variable_rate;
gboolean updating_caps;
guint64 max_duplication_time;
+ guint64 max_closing_segment_duplication_duration;
/* segment handling */
GstSegment segment;
'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',
--- /dev/null
+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
--- /dev/null
+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)
--- /dev/null
+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)
--- /dev/null
+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
+
+
--- /dev/null
+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)
--- /dev/null
+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)
--- /dev/null
+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
+
+
+
--- /dev/null
+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)
--- /dev/null
+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)