From 60d9cfc95413c3075fc52c3ff4ae628f41e86b02 Mon Sep 17 00:00:00 2001 From: Vivia Nikolaidou Date: Wed, 31 Jan 2024 20:06:06 +0200 Subject: [PATCH] videorate: Correct segment-based calculations It was adding and subtracting the segment base here and there, but it was also doing so incorrectly, leading to various calculation errors. Fixed a few bugs uncovered, related to getting a new segment: * If we reset base_ts/next_ts/out_frame_count, also reset prevbuf * Only do so if the new segment is different than the previous one Also replaced a few occurrences of GST_BUFFER_TIMESTAMP with GST_BUFFER_PTS for consistency. Integrated the tests of https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2186 , now passing. The test_segment_update_same test had to be fixed, because it was wrongly assuming that we would not fill the gap inside the new-but-same segment. Part-of: --- .../gst/videorate/gstvideorate.c | 118 +++-- .../tests/check/elements/videorate.c | 413 ++++++++++++++++++ 2 files changed, 465 insertions(+), 66 deletions(-) diff --git a/subprojects/gst-plugins-base/gst/videorate/gstvideorate.c b/subprojects/gst-plugins-base/gst/videorate/gstvideorate.c index 4ae4f69e0d..e159fd7fb1 100644 --- a/subprojects/gst-plugins-base/gst/videorate/gstvideorate.c +++ b/subprojects/gst-plugins-base/gst/videorate/gstvideorate.c @@ -723,9 +723,7 @@ gst_video_rate_push_buffer (GstVideoRate * videorate, GstBuffer * outbuf, if (videorate->segment.rate < 0.0) { if (videorate->to_rate_numerator) { /* interpolate next expected timestamp in the segment */ - GstClockTimeDiff next_ts = - videorate->segment.base + videorate->segment.stop - - videorate->base_ts - + GstClockTimeDiff next_ts = videorate->base_ts - gst_util_uint64_scale (videorate->out_frame_count + 1, videorate->to_rate_denominator * GST_SECOND, videorate->to_rate_numerator); @@ -747,9 +745,7 @@ gst_video_rate_push_buffer (GstVideoRate * videorate, GstBuffer * outbuf, } else { if (videorate->to_rate_numerator) { /* interpolate next expected timestamp in the segment */ - videorate->next_ts = - videorate->segment.base + videorate->segment.start + - videorate->base_ts + + videorate->next_ts = videorate->base_ts + gst_util_uint64_scale (videorate->out_frame_count, videorate->to_rate_denominator * GST_SECOND, videorate->to_rate_numerator); @@ -768,8 +764,7 @@ gst_video_rate_push_buffer (GstVideoRate * videorate, GstBuffer * outbuf, /* We do not need to update time in VFR (variable frame rate) mode */ if (!videorate->drop_only) { - /* adapt for looping, bring back to time in current segment. */ - GST_BUFFER_TIMESTAMP (outbuf) = push_ts - videorate->segment.base; + GST_BUFFER_PTS (outbuf) = push_ts; } GST_LOG_OBJECT (videorate, @@ -849,7 +844,7 @@ 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 next_stream_time = videorate->next_ts; GstClockTime max_closing_segment_duplication_duration = videorate->max_closing_segment_duplication_duration; @@ -1002,7 +997,6 @@ gst_video_rate_sink_event (GstBaseTransform * trans, GstEvent * event) segment.stop = (gint64) (segment.stop / videorate->rate); segment.time = (gint64) (segment.time / videorate->rate); - if (!gst_segment_is_equal (&segment, &videorate->segment)) { rolled_back_caps = gst_video_rate_rollback_to_prev_caps_if_needed (videorate); @@ -1039,19 +1033,18 @@ gst_video_rate_sink_event (GstBaseTransform * trans, GstEvent * event) gst_caps_unref (rolled_back_caps); } + if (segment.rate < 0) + videorate->base_ts = segment.stop; + else + videorate->base_ts = segment.start; + videorate->out_frame_count = 0; + videorate->next_ts = GST_CLOCK_TIME_NONE; + gst_buffer_replace (&videorate->prevbuf, NULL); } - videorate->base_ts = 0; - videorate->out_frame_count = 0; - videorate->next_ts = GST_CLOCK_TIME_NONE; - - /* We just want to update the accumulated stream_time */ - gst_segment_copy_into (&segment, &videorate->segment); GST_DEBUG_OBJECT (videorate, "updated segment: %" GST_SEGMENT_FORMAT, &videorate->segment); - - seqnum = gst_event_get_seqnum (event); gst_event_unref (event); event = gst_event_new_segment (&segment); @@ -1092,7 +1085,7 @@ gst_video_rate_sink_event (GstBaseTransform * trans, GstEvent * event) 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) + && videorate->next_ts < end_ts) || count < 1)) { res = gst_video_rate_flush_prev (videorate, count > 0, @@ -1439,7 +1432,7 @@ gst_video_rate_propose_allocation (GstBaseTransform * trans, static GstFlowReturn gst_video_rate_trans_ip_max_avg (GstVideoRate * videorate, GstBuffer * buf) { - GstClockTime ts = GST_BUFFER_TIMESTAMP (buf); + GstClockTime ts = GST_BUFFER_PTS (buf); videorate->in++; @@ -1655,9 +1648,14 @@ gst_video_rate_apply_pending_rate (GstVideoRate * videorate) goto done; ret = TRUE; - videorate->base_ts += gst_util_uint64_scale (videorate->out_frame_count, - videorate->to_rate_denominator * GST_SECOND, - videorate->to_rate_numerator); + if (videorate->segment.rate < 0) + videorate->base_ts -= gst_util_uint64_scale (videorate->out_frame_count, + videorate->to_rate_denominator * GST_SECOND, + videorate->to_rate_numerator); + else + videorate->base_ts += gst_util_uint64_scale (videorate->out_frame_count, + videorate->to_rate_denominator * GST_SECOND, + videorate->to_rate_numerator); videorate->rate = videorate->pending_rate; videorate->out_frame_count = 0; @@ -1672,7 +1670,7 @@ gst_video_rate_transform_ip (GstBaseTransform * trans, GstBuffer * buffer) { GstVideoRate *videorate; GstFlowReturn res = GST_BASE_TRANSFORM_FLOW_DROPPED; - GstClockTime intime, in_ts, in_dur, last_ts; + GstClockTime in_ts, in_dur, last_ts; gboolean skip; videorate = GST_VIDEO_RATE (trans); @@ -1712,7 +1710,7 @@ gst_video_rate_transform_ip (GstBaseTransform * trans, GstBuffer * buffer) return gst_video_rate_trans_ip_max_avg (videorate, buffer); gst_video_rate_apply_pending_rate (videorate); - in_ts = GST_BUFFER_TIMESTAMP (buffer); + in_ts = GST_BUFFER_PTS (buffer); in_dur = GST_BUFFER_DURATION (buffer); if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (in_ts))) { @@ -1735,10 +1733,6 @@ gst_video_rate_transform_ip (GstBaseTransform * trans, GstBuffer * buffer) GST_DEBUG_OBJECT (videorate, "got buffer with timestamp %" GST_TIME_FORMAT, GST_TIME_ARGS (in_ts)); - /* the input time is the time in the segment + all previously accumulated - * segments */ - intime = in_ts + videorate->segment.base; - /* we need to have two buffers to compare */ if (videorate->prevbuf == NULL || videorate->drop_only) { /* We can calculate the duration of the buffer here if not given for @@ -1752,18 +1746,14 @@ gst_video_rate_transform_ip (GstBaseTransform * trans, GstBuffer * buffer) GST_BUFFER_DURATION (buffer) = last_ts - in_ts; } - gst_video_rate_swap_prev (videorate, buffer, intime); + gst_video_rate_swap_prev (videorate, buffer, in_ts); videorate->in++; if (!GST_CLOCK_TIME_IS_VALID (videorate->next_ts)) { /* new buffer, we expect to output a buffer that matches the first * timestamp in the segment */ if (videorate->skip_to_first || skip) { - videorate->next_ts = intime; - if (videorate->segment.rate < 0.0) { - videorate->base_ts = videorate->segment.stop - in_ts; - } else { - videorate->base_ts = in_ts - videorate->segment.start; - } + videorate->base_ts = in_ts; + videorate->next_ts = in_ts; videorate->out_frame_count = 0; } else { if (videorate->segment.rate < 0.0) { @@ -1772,8 +1762,7 @@ gst_video_rate_transform_ip (GstBaseTransform * trans, GstBuffer * buffer) videorate->to_rate_denominator * GST_SECOND, videorate->to_rate_numerator); - videorate->next_ts = - videorate->segment.stop + videorate->segment.base; + videorate->next_ts = videorate->segment.stop; if (videorate->next_ts > frame_duration) videorate->next_ts = @@ -1783,11 +1772,10 @@ gst_video_rate_transform_ip (GstBaseTransform * trans, GstBuffer * buffer) videorate->next_ts = videorate->segment.start; } else { /* What else can we do? */ - videorate->next_ts = intime; + videorate->next_ts = in_ts; } } else { - videorate->next_ts = - videorate->segment.start + videorate->segment.base; + videorate->next_ts = videorate->segment.start; } } } @@ -1797,8 +1785,8 @@ gst_video_rate_transform_ip (GstBaseTransform * trans, GstBuffer * buffer) * allowed frame period. This also keeps latency down to 0 frames */ if (videorate->drop_only) { - if ((videorate->segment.rate > 0.0 && intime >= videorate->next_ts) || - (videorate->segment.rate < 0.0 && intime <= videorate->next_ts)) { + if ((videorate->segment.rate > 0.0 && in_ts >= videorate->next_ts) || + (videorate->segment.rate < 0.0 && in_ts <= videorate->next_ts)) { GstFlowReturn r; /* The buffer received from basetransform is guaranteed to be writable. @@ -1817,34 +1805,34 @@ gst_video_rate_transform_ip (GstBaseTransform * trans, GstBuffer * buffer) gst_buffer_replace (&videorate->prevbuf, NULL); } } else { - GstClockTime prevtime; + GstClockTime prev_ts; gint count = 0; gint64 diff1 = 0, diff2 = 0; - prevtime = videorate->prev_ts; + prev_ts = videorate->prev_ts; GST_LOG_OBJECT (videorate, "BEGINNING prev buf %" GST_TIME_FORMAT " new buf %" GST_TIME_FORMAT - " outgoing ts %" GST_TIME_FORMAT, GST_TIME_ARGS (prevtime), - GST_TIME_ARGS (intime), GST_TIME_ARGS (videorate->next_ts)); + " outgoing ts %" GST_TIME_FORMAT, GST_TIME_ARGS (prev_ts), + GST_TIME_ARGS (in_ts), GST_TIME_ARGS (videorate->next_ts)); videorate->in++; /* drop new buffer if it's before previous one */ - if ((videorate->segment.rate > 0.0 && intime < prevtime) || - (videorate->segment.rate < 0.0 && intime > prevtime)) { + if ((videorate->segment.rate > 0.0 && in_ts < prev_ts) || + (videorate->segment.rate < 0.0 && in_ts > prev_ts)) { GST_DEBUG_OBJECT (videorate, "The new buffer (%" GST_TIME_FORMAT ") is before the previous buffer (%" GST_TIME_FORMAT "). Dropping new buffer.", - GST_TIME_ARGS (intime), GST_TIME_ARGS (prevtime)); + GST_TIME_ARGS (in_ts), GST_TIME_ARGS (prev_ts)); videorate->drop++; if (!videorate->silent) gst_video_rate_notify_drop (videorate); goto done; } - if (!gst_video_rate_do_max_duplicate (videorate, buffer, intime, prevtime, + if (!gst_video_rate_do_max_duplicate (videorate, buffer, in_ts, prev_ts, &count)) goto done; @@ -1861,13 +1849,12 @@ gst_video_rate_transform_ip (GstBaseTransform * trans, GstBuffer * buffer) * or the code above for the very first buffer */ g_assert (GST_BUFFER_DURATION_IS_VALID (videorate->prevbuf)); if (!GST_BUFFER_DURATION_IS_VALID (buffer)) - GST_BUFFER_DURATION (buffer) = - prevtime > intime ? prevtime - intime : 0; + GST_BUFFER_DURATION (buffer) = prev_ts > in_ts ? prev_ts - in_ts : 0; } else { /* Make sure that we have a duration for previous buffer */ if (!GST_BUFFER_DURATION_IS_VALID (videorate->prevbuf)) GST_BUFFER_DURATION (videorate->prevbuf) = - intime > prevtime ? intime - prevtime : 0; + in_ts > prev_ts ? in_ts - prev_ts : 0; } #ifndef ABSDIFF @@ -1878,7 +1865,7 @@ gst_video_rate_transform_ip (GstBaseTransform * trans, GstBuffer * buffer) if (videorate->segment.rate < 0.0) { GstClockTime next_end_ts; GstClockTime prev_endtime; - GstClockTime in_endtime, base_ts_in_segment; + GstClockTime in_endtime; next_ts = videorate->next_ts; @@ -1888,8 +1875,8 @@ gst_video_rate_transform_ip (GstBaseTransform * trans, GstBuffer * buffer) break; } - prev_endtime = prevtime + GST_BUFFER_DURATION (videorate->prevbuf); - in_endtime = intime + GST_BUFFER_DURATION (buffer); + prev_endtime = prev_ts + GST_BUFFER_DURATION (videorate->prevbuf); + in_endtime = in_ts + GST_BUFFER_DURATION (buffer); if (videorate->to_rate_numerator) { GstClockTime frame_duration = gst_util_uint64_scale (1, @@ -1900,11 +1887,10 @@ gst_video_rate_transform_ip (GstBaseTransform * trans, GstBuffer * buffer) next_end_ts = next_ts + GST_BUFFER_DURATION (videorate->prevbuf); } - base_ts_in_segment = videorate->segment.stop - videorate->base_ts; - next_ts = base_ts_in_segment - ( - (base_ts_in_segment - next_ts) * videorate->rate); - next_end_ts = base_ts_in_segment - (MAX (0, - (base_ts_in_segment - next_end_ts)) * videorate->rate); + next_ts = videorate->base_ts - ( + (videorate->base_ts - next_ts) * videorate->rate); + next_end_ts = videorate->base_ts - (MAX (0, + (videorate->base_ts - next_end_ts)) * videorate->rate); diff1 = ABSDIFF (prev_endtime, next_end_ts); diff2 = ABSDIFF (in_endtime, next_end_ts); @@ -1919,8 +1905,8 @@ gst_video_rate_transform_ip (GstBaseTransform * trans, GstBuffer * buffer) videorate->base_ts + ((videorate->next_ts - videorate->base_ts) * videorate->rate); - diff1 = ABSDIFF (prevtime, next_ts); - diff2 = ABSDIFF (intime, next_ts); + diff1 = ABSDIFF (prev_ts, next_ts); + diff2 = ABSDIFF (in_ts, next_ts); GST_LOG_OBJECT (videorate, "diff with prev %" GST_TIME_FORMAT " diff with new %" @@ -1936,7 +1922,7 @@ gst_video_rate_transform_ip (GstBaseTransform * trans, GstBuffer * buffer) /* on error the _flush function posted a warning already */ if ((r = gst_video_rate_flush_prev (videorate, - count > 1, intime, FALSE)) != GST_FLOW_OK) { + count > 1, in_ts, FALSE)) != GST_FLOW_OK) { res = r; goto done; } @@ -1973,7 +1959,7 @@ gst_video_rate_transform_ip (GstBaseTransform * trans, GstBuffer * buffer) videorate->in, videorate->out, videorate->drop, videorate->dup); /* swap in new one when it's the best */ - gst_video_rate_swap_prev (videorate, buffer, intime); + gst_video_rate_swap_prev (videorate, buffer, in_ts); } done: return res; diff --git a/subprojects/gst-plugins-base/tests/check/elements/videorate.c b/subprojects/gst-plugins-base/tests/check/elements/videorate.c index 51a2bbd508..68d2bbd4e5 100644 --- a/subprojects/gst-plugins-base/tests/check/elements/videorate.c +++ b/subprojects/gst-plugins-base/tests/check/elements/videorate.c @@ -1608,6 +1608,416 @@ GST_START_TEST (test_nopts_in_middle) GST_END_TEST; +/* test segment update with start/time update */ +GST_START_TEST (test_segment_base_nonzero) +{ + GstElement *videorate; + GstBuffer *buf; + GstCaps *caps; + GstSegment segment; + /* + GList *l; + GstClockTime next_ts = 0; + guint n = 1; + */ + + videorate = setup_videorate_full (&srctemplate, &downstreamsinktemplate); + g_object_set (videorate, "skip-to-first", TRUE, NULL); + fail_unless (gst_element_set_state (videorate, + GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS, + "could not set to playing"); + + caps = gst_caps_from_string ("video/x-raw, " + "width = (int) 320, " + "height = (int) 240, " + "framerate = (fraction) 30000/1001 , " "format = (string) I420"); + gst_check_setup_events (mysrcpad, videorate, caps, GST_FORMAT_TIME); + gst_caps_unref (caps); + + /* Send first closed segment [0, -1] */ + gst_segment_init (&segment, GST_FORMAT_TIME); + segment.start = ((1000 * 60 + 2) * 60 + 20) * GST_SECOND + 560636518; + segment.time = (2 * 60 + 20) * GST_SECOND + 560636518; + segment.position = segment.start; + segment.base = segment.time; + segment.stop = GST_CLOCK_TIME_NONE; + fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment))); + + /* first buffer */ + buf = gst_buffer_new_and_alloc (4); + GST_BUFFER_TIMESTAMP (buf) = + ((1000 * 60 + 2) * 60 + 20) * GST_SECOND + 543953185; + gst_buffer_memset (buf, 0, 1, 4); + ASSERT_BUFFER_REFCOUNT (buf, "first", 1); + gst_buffer_ref (buf); + + GST_DEBUG ("pushing first buffer"); + /* pushing gives away my reference ... */ + fail_unless (gst_pad_push (mysrcpad, buf) == GST_FLOW_OK); + /* ... and a copy is now stuck inside videorate */ + ASSERT_BUFFER_REFCOUNT (buf, "first", 1); + gst_buffer_unref (buf); + fail_unless_equals_int (g_list_length (buffers), 0); + assert_videorate_stats (videorate, "first", 1, 0, 0, 0); + + /* second buffer */ + buf = gst_buffer_new_and_alloc (4); + GST_BUFFER_TIMESTAMP (buf) = + ((1000 * 60 + 2) * 60 + 20) * GST_SECOND + 577319851; + gst_buffer_memset (buf, 0, 2, 4); + ASSERT_BUFFER_REFCOUNT (buf, "second", 1); + gst_buffer_ref (buf); + + /* pushing gives away my reference ... */ + fail_unless (gst_pad_push (mysrcpad, buf) == GST_FLOW_OK); + ASSERT_BUFFER_REFCOUNT (buf, "second", 1); + gst_buffer_unref (buf); + fail_unless_equals_int (g_list_length (buffers), 1); + assert_videorate_stats (videorate, "second", 2, 1, 0, 0); + + /* push EOS to drain out all buffers */ + fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ())); + fail_unless_equals_int (g_list_length (buffers), 2); + + /* cleanup */ + cleanup_videorate (videorate); +} + +GST_END_TEST; + +/* test segment update with start/time update */ +GST_START_TEST (test_segment_update_start_advance) +{ + GstElement *videorate; + GstBuffer *buf; + GstCaps *caps; + GstSegment segment; + GList *l; + GstClockTime next_ts = 0; + guint n = 1; + + videorate = setup_videorate_full (&srctemplate, &downstreamsinktemplate); + fail_unless (gst_element_set_state (videorate, + GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS, + "could not set to playing"); + + caps = gst_caps_from_string (VIDEO_CAPS_STRING); + gst_check_setup_events (mysrcpad, videorate, caps, GST_FORMAT_TIME); + gst_caps_unref (caps); + + /* Send first closed segment [0, -1] */ + gst_segment_init (&segment, GST_FORMAT_TIME); + segment.start = 0 * GST_SECOND; + segment.time = 0 * GST_SECOND; + segment.position = 0 * GST_SECOND; + segment.stop = GST_CLOCK_TIME_NONE; + fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment))); + + /* first buffer */ + buf = gst_buffer_new_and_alloc (4); + GST_BUFFER_TIMESTAMP (buf) = 0; + gst_buffer_memset (buf, 0, 1, 4); + ASSERT_BUFFER_REFCOUNT (buf, "first", 1); + gst_buffer_ref (buf); + + GST_DEBUG ("pushing first buffer"); + /* pushing gives away my reference ... */ + fail_unless (gst_pad_push (mysrcpad, buf) == GST_FLOW_OK); + /* ... and a copy is now stuck inside videorate */ + ASSERT_BUFFER_REFCOUNT (buf, "first", 1); + gst_buffer_unref (buf); + fail_unless_equals_int (g_list_length (buffers), 0); + assert_videorate_stats (videorate, "first", 1, 0, 0, 0); + + /* second buffer */ + buf = gst_buffer_new_and_alloc (4); + GST_BUFFER_TIMESTAMP (buf) = 40 * GST_MSECOND; + gst_buffer_memset (buf, 0, 2, 4); + ASSERT_BUFFER_REFCOUNT (buf, "second", 1); + gst_buffer_ref (buf); + + /* pushing gives away my reference ... */ + fail_unless (gst_pad_push (mysrcpad, buf) == GST_FLOW_OK); + ASSERT_BUFFER_REFCOUNT (buf, "second", 1); + gst_buffer_unref (buf); + fail_unless_equals_int (g_list_length (buffers), 1); + assert_videorate_stats (videorate, "second", 2, 1, 0, 0); + + /* third buffer */ + buf = gst_buffer_new_and_alloc (4); + GST_BUFFER_TIMESTAMP (buf) = 2 * 40 * GST_MSECOND; + gst_buffer_memset (buf, 0, 3, 4); + ASSERT_BUFFER_REFCOUNT (buf, "third", 1); + gst_buffer_ref (buf); + + /* pushing gives away my reference ... */ + fail_unless (gst_pad_push (mysrcpad, buf) == GST_FLOW_OK); + ASSERT_BUFFER_REFCOUNT (buf, "third", 1); + gst_buffer_unref (buf); + fail_unless_equals_int (g_list_length (buffers), 2); + assert_videorate_stats (videorate, "second", 3, 2, 0, 0); + + /* should have the first 2 buffers here */ + for (l = buffers; l; l = l->next) { + buf = l->data; + fail_unless_equals_uint64 (GST_BUFFER_PTS (buf), next_ts); + fail_unless_equals_uint64 (GST_BUFFER_DURATION (buf), 40 * GST_MSECOND); + fail_unless_equals_int (buffer_get_byte (buf, 0), n); + + next_ts += GST_SECOND / 25; + n += 1; + } + gst_check_drop_buffers (); + fail_unless_equals_int (g_list_length (buffers), 0); + + /* Send a new segment [30, -1] that does intersect with the first */ + gst_segment_init (&segment, GST_FORMAT_TIME); + segment.start = 30 * GST_SECOND; + segment.time = 30 * GST_SECOND; + segment.position = 30 * GST_SECOND; + segment.stop = GST_CLOCK_TIME_NONE; + fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment))); + + /* fourth buffer */ + buf = gst_buffer_new_and_alloc (4); + GST_BUFFER_TIMESTAMP (buf) = 30 * GST_SECOND + 40 * GST_MSECOND; + gst_buffer_memset (buf, 0, 4, 4); + ASSERT_BUFFER_REFCOUNT (buf, "fourth", 1); + gst_buffer_ref (buf); + + /* pushing gives away my reference ... */ + fail_unless (gst_pad_push (mysrcpad, buf) == GST_FLOW_OK); + ASSERT_BUFFER_REFCOUNT (buf, "fourth", 1); + gst_buffer_unref (buf); + fail_unless_equals_int (g_list_length (buffers), 1); + assert_videorate_stats (videorate, "fourth", 4, 3, 0, 0); + + /* Should have the last buffer of the previous segment here */ + for (l = buffers; l; l = l->next) { + buf = l->data; + fail_unless_equals_uint64 (GST_BUFFER_PTS (buf), next_ts); + fail_unless_equals_uint64 (GST_BUFFER_DURATION (buf), 40 * GST_MSECOND); + fail_unless_equals_int (buffer_get_byte (buf, 0), 3); + + next_ts += GST_SECOND / 25; + n += 1; + } + gst_check_drop_buffers (); + fail_unless_equals_int (g_list_length (buffers), 0); + + /* fifth buffer */ + buf = gst_buffer_new_and_alloc (4); + GST_BUFFER_TIMESTAMP (buf) = 30 * GST_SECOND + 2 * 40 * GST_MSECOND; + gst_buffer_memset (buf, 0, 5, 4); + ASSERT_BUFFER_REFCOUNT (buf, "fifth", 1); + gst_buffer_ref (buf); + + /* pushing gives away my reference ... */ + fail_unless (gst_pad_push (mysrcpad, buf) == GST_FLOW_OK); + ASSERT_BUFFER_REFCOUNT (buf, "fifth", 1); + gst_buffer_unref (buf); + fail_unless_equals_int (g_list_length (buffers), 2); + assert_videorate_stats (videorate, "fifth", 5, 5, 0, 1); + + /* push EOS to drain out all buffers */ + fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ())); + fail_unless_equals_int (g_list_length (buffers), 3); + + assert_videorate_stats (videorate, "fifth", 5, 6, 0, 1); + + /* Should start with the new buffers of the new segment now */ + next_ts = 30 * GST_SECOND; + for (l = buffers; l; l = l->next) { + buf = l->data; + fail_unless_equals_uint64 (GST_BUFFER_PTS (buf), next_ts); + fail_unless_equals_uint64 (GST_BUFFER_DURATION (buf), 40 * GST_MSECOND); + + if (n < 6) { + fail_unless_equals_int (buffer_get_byte (buf, 0), 4); + } else { + fail_unless_equals_int (buffer_get_byte (buf, 0), 5); + } + + next_ts += GST_SECOND / 25; + n += 1; + } + fail_unless_equals_int (n, 7); + gst_check_drop_buffers (); + fail_unless_equals_int (g_list_length (buffers), 0); + + /* cleanup */ + cleanup_videorate (videorate); +} + +GST_END_TEST; + +/* test segment update with same segment */ +GST_START_TEST (test_segment_update_same) +{ + GstElement *videorate; + GstBuffer *buf; + GstCaps *caps; + GstSegment segment; + GList *l; + GstClockTime next_ts = 0; + guint n = 1; + + videorate = setup_videorate_full (&srctemplate, &downstreamsinktemplate); + fail_unless (gst_element_set_state (videorate, + GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS, + "could not set to playing"); + + caps = gst_caps_from_string (VIDEO_CAPS_STRING); + gst_check_setup_events (mysrcpad, videorate, caps, GST_FORMAT_TIME); + gst_caps_unref (caps); + + /* Send first closed segment [30, -1] */ + gst_segment_init (&segment, GST_FORMAT_TIME); + segment.start = 30 * GST_SECOND; + segment.time = 30 * GST_SECOND; + segment.position = 30 * GST_SECOND; + segment.stop = GST_CLOCK_TIME_NONE; + fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment))); + + /* first buffer */ + buf = gst_buffer_new_and_alloc (4); + GST_BUFFER_TIMESTAMP (buf) = 32 * GST_SECOND; + gst_buffer_memset (buf, 0, 1, 4); + ASSERT_BUFFER_REFCOUNT (buf, "first", 1); + gst_buffer_ref (buf); + + GST_DEBUG ("pushing first buffer"); + /* pushing gives away my reference ... */ + fail_unless (gst_pad_push (mysrcpad, buf) == GST_FLOW_OK); + /* ... and a copy is now stuck inside videorate */ + ASSERT_BUFFER_REFCOUNT (buf, "first", 1); + gst_buffer_unref (buf); + fail_unless_equals_int (g_list_length (buffers), 0); + assert_videorate_stats (videorate, "first", 1, 0, 0, 0); + + /* second buffer */ + buf = gst_buffer_new_and_alloc (4); + GST_BUFFER_TIMESTAMP (buf) = 32 * GST_SECOND + 40 * GST_MSECOND; + gst_buffer_memset (buf, 0, 2, 4); + ASSERT_BUFFER_REFCOUNT (buf, "second", 1); + gst_buffer_ref (buf); + + /* pushing gives away my reference ... */ + fail_unless (gst_pad_push (mysrcpad, buf) == GST_FLOW_OK); + ASSERT_BUFFER_REFCOUNT (buf, "second", 1); + gst_buffer_unref (buf); + fail_unless_equals_int (g_list_length (buffers), 51); + assert_videorate_stats (videorate, "second", 2, 51, 0, 50); + + /* third buffer */ + buf = gst_buffer_new_and_alloc (4); + GST_BUFFER_TIMESTAMP (buf) = 32 * GST_SECOND + 2 * 40 * GST_MSECOND; + gst_buffer_memset (buf, 0, 3, 4); + ASSERT_BUFFER_REFCOUNT (buf, "third", 1); + gst_buffer_ref (buf); + + /* pushing gives away my reference ... */ + fail_unless (gst_pad_push (mysrcpad, buf) == GST_FLOW_OK); + ASSERT_BUFFER_REFCOUNT (buf, "third", 1); + gst_buffer_unref (buf); + fail_unless_equals_int (g_list_length (buffers), 52); + assert_videorate_stats (videorate, "second", 3, 52, 0, 50); + + /* should have the first 2 buffers here */ + next_ts = 30 * GST_SECOND; + for (l = buffers; l; l = l->next) { + buf = l->data; + fail_unless_equals_uint64 (GST_BUFFER_PTS (buf), next_ts); + fail_unless_equals_uint64 (GST_BUFFER_DURATION (buf), 40 * GST_MSECOND); + + if (n < 52) { + fail_unless_equals_int (buffer_get_byte (buf, 0), 1); + } else { + fail_unless_equals_int (buffer_get_byte (buf, 0), 2); + } + + next_ts += GST_SECOND / 25; + n += 1; + } + gst_check_drop_buffers (); + fail_unless_equals_int (g_list_length (buffers), 0); + + /* Send a new segment [30, -1] that is exactly the same as before */ + gst_segment_init (&segment, GST_FORMAT_TIME); + segment.start = 30 * GST_SECOND; + segment.time = 30 * GST_SECOND; + segment.position = 30 * GST_SECOND; + segment.stop = GST_CLOCK_TIME_NONE; + fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment))); + + /* fourth buffer */ + buf = gst_buffer_new_and_alloc (4); + GST_BUFFER_TIMESTAMP (buf) = 35 * GST_SECOND + 40 * GST_MSECOND; + gst_buffer_memset (buf, 0, 4, 4); + ASSERT_BUFFER_REFCOUNT (buf, "fourth", 1); + gst_buffer_ref (buf); + + /* pushing gives away my reference ... */ + fail_unless (gst_pad_push (mysrcpad, buf) == GST_FLOW_OK); + ASSERT_BUFFER_REFCOUNT (buf, "fourth", 1); + gst_buffer_unref (buf); + fail_unless_equals_int (g_list_length (buffers), 38); + assert_videorate_stats (videorate, "fourth", 4, 90, 0, 87); + + /* Should have the last buffer of the previous segment here */ + for (l = buffers; l; l = l->next) { + buf = l->data; + fail_unless_equals_uint64 (GST_BUFFER_PTS (buf), next_ts); + fail_unless_equals_uint64 (GST_BUFFER_DURATION (buf), 40 * GST_MSECOND); + fail_unless_equals_int (buffer_get_byte (buf, 0), 3); + + next_ts += GST_SECOND / 25; + n += 1; + } + gst_check_drop_buffers (); + fail_unless_equals_int (g_list_length (buffers), 0); + + /* fifth buffer */ + buf = gst_buffer_new_and_alloc (4); + GST_BUFFER_TIMESTAMP (buf) = 35 * GST_SECOND + 2 * 40 * GST_MSECOND; + gst_buffer_memset (buf, 0, 5, 4); + ASSERT_BUFFER_REFCOUNT (buf, "fifth", 1); + gst_buffer_ref (buf); + + /* pushing gives away my reference ... */ + fail_unless (gst_pad_push (mysrcpad, buf) == GST_FLOW_OK); + ASSERT_BUFFER_REFCOUNT (buf, "fifth", 1); + gst_buffer_unref (buf); + fail_unless_equals_int (g_list_length (buffers), 37); + + /* push EOS to drain out all buffers */ + fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ())); + fail_unless_equals_int (g_list_length (buffers), 38); + + assert_videorate_stats (videorate, "fourth", 5, 128, 0, 123); + + for (l = buffers; l; l = l->next) { + buf = l->data; + fail_unless_equals_uint64 (GST_BUFFER_PTS (buf), next_ts); + fail_unless_equals_uint64 (GST_BUFFER_DURATION (buf), 40 * GST_MSECOND); + + if (n < 128) { + fail_unless_equals_int (buffer_get_byte (buf, 0), 4); + } else { + fail_unless_equals_int (buffer_get_byte (buf, 0), 5); + } + + next_ts += GST_SECOND / 25; + n += 1; + } + gst_check_drop_buffers (); + fail_unless_equals_int (g_list_length (buffers), 0); + + /* cleanup */ + cleanup_videorate (videorate); +} + +GST_END_TEST; static Suite * videorate_suite (void) { @@ -1634,6 +2044,9 @@ videorate_suite (void) tcase_add_loop_test (tc_chain, test_query_position, 0, G_N_ELEMENTS (position_tests)); tcase_add_test (tc_chain, test_nopts_in_middle); + tcase_add_test (tc_chain, test_segment_base_nonzero); + tcase_add_test (tc_chain, test_segment_update_start_advance); + tcase_add_test (tc_chain, test_segment_update_same); return s; }