From fa57d776d81799ddcf6fe342c3c1a9910e9ba368 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Tue, 26 Nov 2024 15:42:12 -0500 Subject: [PATCH] videorate: convert next_ts to new segment instead of restarting from 0 When receiving a new segment we should not restart PTS from the new segment' start. Instead convert current position into the new segment if possible. Part-of: --- .../gst/videorate/gstvideorate.c | 37 +++++++- .../tests/check/elements/videorate.c | 95 +++++++++++++++++++ 2 files changed, 129 insertions(+), 3 deletions(-) diff --git a/subprojects/gst-plugins-base/gst/videorate/gstvideorate.c b/subprojects/gst-plugins-base/gst/videorate/gstvideorate.c index fafaf425e3..655e299cd1 100644 --- a/subprojects/gst-plugins-base/gst/videorate/gstvideorate.c +++ b/subprojects/gst-plugins-base/gst/videorate/gstvideorate.c @@ -973,6 +973,24 @@ gst_video_rate_rollback_to_prev_caps_if_needed (GstVideoRate * videorate) return prev_caps; } +/* FIXME: audiorate has a copy, should it be public API? */ +static guint64 +convert_position (GstSegment * old_segment, GstSegment * new_segment, + guint64 position) +{ + g_return_val_if_fail (old_segment->format == new_segment->format, -1); + if (position == -1) + return -1; + position += old_segment->base; + if (position < new_segment->base) + return -1; + position -= new_segment->base; + if (position < new_segment->start || (new_segment->stop != -1 + && position > new_segment->stop)) + return -1; + return position; +} + static gboolean gst_video_rate_sink_event (GstBaseTransform * trans, GstEvent * event) { @@ -1033,13 +1051,26 @@ gst_video_rate_sink_event (GstBaseTransform * trans, GstEvent * event) gst_caps_unref (rolled_back_caps); } - if (segment.rate < 0) + + /* Convert next_ts and last_ts to new segment. */ + videorate->next_ts = + convert_position (&videorate->segment, &segment, + videorate->next_ts); + if (videorate->next_ts == -1) + videorate->last_ts = -1; + videorate->last_ts = + convert_position (&videorate->segment, &segment, + videorate->last_ts); + + if (videorate->next_ts != -1) + videorate->base_ts = videorate->next_ts; + else 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; - videorate->last_ts = GST_CLOCK_TIME_NONE; + gst_buffer_replace (&videorate->prevbuf, NULL); } diff --git a/subprojects/gst-plugins-base/tests/check/elements/videorate.c b/subprojects/gst-plugins-base/tests/check/elements/videorate.c index 08845b9c3b..8687222868 100644 --- a/subprojects/gst-plugins-base/tests/check/elements/videorate.c +++ b/subprojects/gst-plugins-base/tests/check/elements/videorate.c @@ -2068,6 +2068,100 @@ GST_START_TEST (test_segment_update_average_period) GST_END_TEST; +static GstPadProbeReturn +segment_update_probe_cb (GstPad * pad, + GstPadProbeInfo * info, gpointer user_data) +{ + GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info); + GList **events = user_data; + + *events = g_list_append (*events, gst_event_ref (event)); + return GST_PAD_PROBE_OK; +} + +GST_START_TEST (test_segment_update) +{ + GstElement *videorate; + GstCaps *caps; + GstBuffer *buf; + + 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); + + /* Note: There is 1 buffer latency, we receive 1st buffer after pushing the + * 2nd, or after pushing a new segment. + */ + + /* Initial segment is [0, -1], first buffer has PTS=0 */ + gsize frame_size = sizeof (gfloat) * 1; + buf = gst_buffer_new_and_alloc (frame_size); + GST_BUFFER_TIMESTAMP (buf) = 0; + gst_pad_push (mysrcpad, buf); + fail_unless_equals_int (g_list_length (buffers), 0); + + GList *events = NULL; + gst_pad_add_probe (mysrcpad, + GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, + (GstPadProbeCallback) segment_update_probe_cb, &events, NULL); + + /* Set segment base time to 2nd frame's PTS */ + GstSegment seg; + gst_segment_init (&seg, GST_FORMAT_TIME); + seg.base = 40 * GST_MSECOND; + gst_pad_push_event (mysrcpad, gst_event_new_segment (&seg)); + fail_unless_equals_int (g_list_length (events), 1); + g_clear_list (&events, (GDestroyNotify) gst_event_unref); + fail_unless_equals_int (g_list_length (buffers), 1); + fail_unless_equals_int64 (GST_BUFFER_PTS (buffers->data), 0); + gst_check_drop_buffers (); + + /* PTS=0 is correct because of the segment base time */ + buf = gst_buffer_new_and_alloc (frame_size); + GST_BUFFER_TIMESTAMP (buf) = 0; + gst_pad_push (mysrcpad, buf); + fail_unless_equals_int (g_list_length (buffers), 0); + + /* Push [0, -1] segment again with base time back to 0 */ + gst_segment_init (&seg, GST_FORMAT_TIME); + gst_pad_push_event (mysrcpad, gst_event_new_segment (&seg)); + fail_unless_equals_int (g_list_length (events), 1); + g_clear_list (&events, (GDestroyNotify) gst_event_unref); + fail_unless_equals_int (g_list_length (buffers), 1); + fail_unless_equals_int64 (GST_BUFFER_PTS (buffers->data), 0); + gst_check_drop_buffers (); + + /* PTS of 3rd frame because base time is back to 0. + * videorate used to output a buffer with PTS back to segment.start instead of + * continuing from its current position. */ + buf = gst_buffer_new_and_alloc (frame_size); + GST_BUFFER_TIMESTAMP (buf) = 2 * 40 * GST_MSECOND; + gst_pad_push (mysrcpad, buf); + fail_unless_equals_int (g_list_length (buffers), 0); + + /* One last buffer just to verify the one we pushed previously */ + buf = gst_buffer_new_and_alloc (frame_size); + GST_BUFFER_TIMESTAMP (buf) = 3 * 40 * GST_MSECOND; + gst_pad_push (mysrcpad, buf); + fail_unless_equals_int (g_list_length (buffers), 1); + fail_unless_equals_int64 (GST_BUFFER_PTS (buffers->data), + 2 * 40 * GST_MSECOND); + gst_check_drop_buffers (); + + gst_element_set_state (videorate, GST_STATE_NULL); + + /* cleanup */ + g_clear_list (&events, (GDestroyNotify) gst_event_unref); + cleanup_videorate (videorate); +} + +GST_END_TEST; + static Suite * videorate_suite (void) { @@ -2098,6 +2192,7 @@ videorate_suite (void) tcase_add_test (tc_chain, test_segment_update_start_advance); tcase_add_test (tc_chain, test_segment_update_same); tcase_add_test (tc_chain, test_segment_update_average_period); + tcase_add_test (tc_chain, test_segment_update); return s; }