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: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6031>
This commit is contained in:
Vivia Nikolaidou 2024-01-31 20:06:06 +02:00 committed by GStreamer Marge Bot
parent b6d8a55103
commit 60d9cfc954
2 changed files with 465 additions and 66 deletions

View file

@ -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;

View file

@ -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;
}