mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-12 19:36:38 +00:00
audiorate: 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. Fixes: #4060 Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7977>
This commit is contained in:
parent
0d8bdaaf17
commit
bfc4812bbe
3 changed files with 136 additions and 24 deletions
|
@ -292,6 +292,24 @@ gst_audio_rate_fill_to_time (GstAudioRate * audiorate, GstClockTime time)
|
|||
gst_audio_rate_chain (audiorate->sinkpad, GST_OBJECT_CAST (audiorate), buf);
|
||||
}
|
||||
|
||||
/* FIXME: videorate 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_audio_rate_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
|
||||
{
|
||||
|
@ -321,30 +339,28 @@ gst_audio_rate_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
|
|||
case GST_EVENT_SEGMENT:
|
||||
{
|
||||
gst_event_copy_segment (event, &audiorate->sink_segment);
|
||||
|
||||
GST_DEBUG_OBJECT (audiorate, "handle NEWSEGMENT");
|
||||
#if 0
|
||||
/* FIXME: bad things will likely happen if rate < 0 ... */
|
||||
if (!update) {
|
||||
/* a new segment starts. We need to figure out what will be the next
|
||||
* sample offset. We mark the offsets as invalid so that the _chain
|
||||
* function will perform this calculation. */
|
||||
gst_audio_rate_fill_to_time (audiorate, audiorate->src_segment.stop);
|
||||
#endif
|
||||
audiorate->next_offset = -1;
|
||||
audiorate->next_ts = -1;
|
||||
#if 0
|
||||
} else {
|
||||
gst_audio_rate_fill_to_time (audiorate, audiorate->src_segment.start);
|
||||
}
|
||||
#endif
|
||||
|
||||
GST_DEBUG_OBJECT (audiorate, "updated segment: %" GST_SEGMENT_FORMAT,
|
||||
&audiorate->sink_segment);
|
||||
|
||||
GstSegment old_segment;
|
||||
gst_segment_copy_into (&audiorate->src_segment, &old_segment);
|
||||
|
||||
/* Copy sink_segment into src_segment and convert to TIME format. */
|
||||
gst_audio_rate_convert_segments (audiorate);
|
||||
|
||||
/* Convert next_ts to new segment. */
|
||||
audiorate->next_ts =
|
||||
convert_position (&old_segment, &audiorate->src_segment,
|
||||
audiorate->next_ts);
|
||||
if (audiorate->next_ts != -1) {
|
||||
audiorate->next_offset =
|
||||
gst_util_uint64_scale_int_round (audiorate->next_ts,
|
||||
GST_AUDIO_INFO_RATE (&audiorate->info), GST_SECOND);
|
||||
} else {
|
||||
/* Current position is outside the new segment, _chain will resync. */
|
||||
audiorate->next_offset = -1;
|
||||
}
|
||||
|
||||
/* Push updated segment */
|
||||
guint32 seqnum = gst_event_get_seqnum (event);
|
||||
gst_event_take (&event, gst_event_new_segment (&audiorate->src_segment));
|
||||
|
@ -467,14 +483,14 @@ gst_audio_rate_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
|
|||
if (bpf == 0)
|
||||
goto not_negotiated;
|
||||
|
||||
/* we have a new pending segment */
|
||||
if (audiorate->next_offset == -1) {
|
||||
gint64 pos;
|
||||
|
||||
/* first buffer, we are negotiated and we have a segment, calculate the
|
||||
* current expected offsets based on the segment.start, which is the first
|
||||
* media time of the segment and should match the media time of the first
|
||||
* buffer in that segment, which is the offset expressed in DEFAULT units.
|
||||
/* first buffer, or previous buffer's position was outside of new segment,
|
||||
* calculate the current expected offsets based on the segment.start, which
|
||||
* is the first media time of the segment and should match the media time of
|
||||
* the first buffer in that segment, which is the offset expressed in
|
||||
* DEFAULT units.
|
||||
*/
|
||||
/* convert first timestamp of segment to sample position */
|
||||
pos = gst_util_uint64_scale_int_round (audiorate->src_segment.start,
|
||||
|
|
|
@ -54,7 +54,6 @@ struct _GstAudioRate
|
|||
|
||||
gboolean discont;
|
||||
|
||||
gboolean new_segment;
|
||||
/* we accept all formats on the sink */
|
||||
GstSegment sink_segment;
|
||||
/* we output TIME format on the src */
|
||||
|
|
|
@ -564,6 +564,102 @@ GST_START_TEST (test_rate_change_down)
|
|||
|
||||
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 *audiorate;
|
||||
GstCaps *caps;
|
||||
GstPad *srcpad, *sinkpad;
|
||||
GstBuffer *buf;
|
||||
|
||||
audiorate = gst_check_setup_element ("audiorate");
|
||||
caps = gst_caps_new_simple ("audio/x-raw",
|
||||
"format", G_TYPE_STRING, GST_AUDIO_NE (F32),
|
||||
"layout", G_TYPE_STRING, "interleaved",
|
||||
"channels", G_TYPE_INT, 1, "rate", G_TYPE_INT, 44100, NULL);
|
||||
srcpad = gst_check_setup_src_pad (audiorate, &srctemplate);
|
||||
sinkpad = gst_check_setup_sink_pad (audiorate, &sinktemplate);
|
||||
|
||||
gst_pad_set_active (srcpad, TRUE);
|
||||
gst_check_setup_events (srcpad, audiorate, caps, GST_FORMAT_TIME);
|
||||
gst_pad_set_active (sinkpad, TRUE);
|
||||
fail_unless (gst_element_set_state (audiorate,
|
||||
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
|
||||
"failed to set audiorate playing");
|
||||
|
||||
/* Initial segment is [0, -1], first buffer has PTS=0 */
|
||||
GstClockTime pts = 0;
|
||||
gsize frame_size = sizeof (gfloat) * 1;
|
||||
buf = gst_buffer_new_and_alloc (frame_size);
|
||||
GST_BUFFER_TIMESTAMP (buf) = pts;
|
||||
gst_pad_push (srcpad, buf);
|
||||
fail_unless_equals_int (g_list_length (buffers), 1);
|
||||
fail_unless_equals_int64 (GST_BUFFER_PTS (buffers->data), pts);
|
||||
gst_check_drop_buffers ();
|
||||
|
||||
GList *events = NULL;
|
||||
gst_pad_add_probe (srcpad,
|
||||
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 = GST_FRAMES_TO_CLOCK_TIME (1, 44100);
|
||||
gst_pad_push_event (srcpad, gst_event_new_segment (&seg));
|
||||
fail_unless_equals_int (g_list_length (events), 1);
|
||||
g_clear_list (&events, (GDestroyNotify) gst_event_unref);
|
||||
|
||||
/* PTS=0 is correct because of the segment base time */
|
||||
pts = 0;
|
||||
buf = gst_buffer_new_and_alloc (frame_size);
|
||||
GST_BUFFER_TIMESTAMP (buf) = pts;
|
||||
gst_pad_push (srcpad, buf);
|
||||
fail_unless_equals_int (g_list_length (buffers), 1);
|
||||
fail_unless_equals_int64 (GST_BUFFER_PTS (buffers->data), pts);
|
||||
gst_check_drop_buffers ();
|
||||
|
||||
/* Push [0, -1] segment again with base time back to 0 */
|
||||
gst_segment_init (&seg, GST_FORMAT_TIME);
|
||||
gst_pad_push_event (srcpad, gst_event_new_segment (&seg));
|
||||
fail_unless_equals_int (g_list_length (events), 1);
|
||||
g_clear_list (&events, (GDestroyNotify) gst_event_unref);
|
||||
|
||||
/* PTS of 3rd frame because base time is back to 0.
|
||||
* +1 because of rounding error.
|
||||
* audiorate used to output a buffer with PTS back to segment.start instead of
|
||||
* continuing from its current position. */
|
||||
pts = GST_FRAMES_TO_CLOCK_TIME (2, 44100) + 1;
|
||||
buf = gst_buffer_new_and_alloc (frame_size);
|
||||
GST_BUFFER_TIMESTAMP (buf) = pts;
|
||||
gst_pad_push (srcpad, buf);
|
||||
fail_unless_equals_int (g_list_length (buffers), 1);
|
||||
fail_unless_equals_int64 (GST_BUFFER_PTS (buffers->data), pts);
|
||||
gst_check_drop_buffers ();
|
||||
|
||||
gst_element_set_state (audiorate, GST_STATE_NULL);
|
||||
gst_caps_unref (caps);
|
||||
|
||||
g_clear_list (&events, (GDestroyNotify) gst_event_unref);
|
||||
gst_check_drop_buffers ();
|
||||
gst_check_teardown_sink_pad (audiorate);
|
||||
gst_check_teardown_src_pad (audiorate);
|
||||
|
||||
gst_object_unref (audiorate);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static Suite *
|
||||
audiorate_suite (void)
|
||||
{
|
||||
|
@ -581,6 +677,7 @@ audiorate_suite (void)
|
|||
tcase_add_test (tc_chain, test_perfect_stream_drop45_inject25);
|
||||
tcase_add_test (tc_chain, test_large_discont);
|
||||
tcase_add_test (tc_chain, test_rate_change_down);
|
||||
tcase_add_test (tc_chain, test_segment_update);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue