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: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7977>
This commit is contained in:
Xavier Claessens 2024-11-26 15:42:12 -05:00 committed by GStreamer Marge Bot
parent bfc4812bbe
commit fa57d776d8
2 changed files with 129 additions and 3 deletions

View file

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

View file

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