gst-libs/gst/audio/gstaudiosink.c: Choose to allocate one less segment but require one additional segment as latency.

Original commit message from CVS:
* gst-libs/gst/audio/gstaudiosink.c: (gst_audioringbuffer_acquire):
Choose to allocate one less segment but require one additional segment
as latency.
* gst-libs/gst/audio/gstaudiosrc.c: (gst_audioringbuffer_acquire):
No need to increment the number of segments in the source.
* gst-libs/gst/audio/gstbaseaudiosink.c:
(gst_base_audio_sink_get_time), (clock_convert_external),
(gst_base_audio_sink_resample_slaving),
(gst_base_audio_sink_skew_slaving),
(gst_base_audio_sink_none_slaving), (gst_base_audio_sink_render),
(gst_base_audio_sink_async_play):
Remove adding latency when returning the internal time while subtracting
it again when we use the value a little later.
When calculating the end timestamp, we are making a rounding error
with the current algorithm. Ensure that we don't accumulate these
rounding errors when aligning samples by not resampling at all if we
don't need to. Fixes #419351.
Make the initial calibration of the clock slaving a little more
predictable and accurate. Also handle the case where we don't do
clock slaving.
This commit is contained in:
Wim Taymans 2008-05-09 16:38:10 +00:00
parent 531c6fb462
commit fc523e047c
4 changed files with 76 additions and 41 deletions

View file

@ -1,3 +1,28 @@
2008-05-09 Wim Taymans <wim.taymans@collabora.co.uk>
* gst-libs/gst/audio/gstaudiosink.c: (gst_audioringbuffer_acquire):
Choose to allocate one less segment but require one additional segment
as latency.
* gst-libs/gst/audio/gstaudiosrc.c: (gst_audioringbuffer_acquire):
No need to increment the number of segments in the source.
* gst-libs/gst/audio/gstbaseaudiosink.c:
(gst_base_audio_sink_get_time), (clock_convert_external),
(gst_base_audio_sink_resample_slaving),
(gst_base_audio_sink_skew_slaving),
(gst_base_audio_sink_none_slaving), (gst_base_audio_sink_render),
(gst_base_audio_sink_async_play):
Remove adding latency when returning the internal time while subtracting
it again when we use the value a little later.
When calculating the end timestamp, we are making a rounding error
with the current algorithm. Ensure that we don't accumulate these
rounding errors when aligning samples by not resampling at all if we
don't need to. Fixes #419351.
Make the initial calibration of the clock slaving a little more
predictable and accurate. Also handle the case where we don't do
clock slaving.
2008-05-09 Sebastian Dröge <slomo@circular-chaos.org> 2008-05-09 Sebastian Dröge <slomo@circular-chaos.org>
Based on a patch by: Based on a patch by:

View file

@ -366,8 +366,8 @@ gst_audioringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec)
if (!result) if (!result)
goto could_not_prepare; goto could_not_prepare;
/* allocate one more segment as we need some headroom */ /* set latency to one more segment as we need some headroom */
spec->segtotal++; spec->seglatency = spec->segtotal + 1;
buf->data = gst_buffer_new_and_alloc (spec->segtotal * spec->segsize); buf->data = gst_buffer_new_and_alloc (spec->segtotal * spec->segsize);
memset (GST_BUFFER_DATA (buf->data), 0, GST_BUFFER_SIZE (buf->data)); memset (GST_BUFFER_DATA (buf->data), 0, GST_BUFFER_SIZE (buf->data));

View file

@ -360,9 +360,6 @@ gst_audioringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec)
if (!result) if (!result)
goto could_not_open; goto could_not_open;
/* allocate one more segment as we need some headroom */
spec->segtotal++;
buf->data = gst_buffer_new_and_alloc (spec->segtotal * spec->segsize); buf->data = gst_buffer_new_and_alloc (spec->segtotal * spec->segsize);
memset (GST_BUFFER_DATA (buf->data), 0, GST_BUFFER_SIZE (buf->data)); memset (GST_BUFFER_DATA (buf->data), 0, GST_BUFFER_SIZE (buf->data));

View file

@ -371,7 +371,7 @@ gst_base_audio_sink_get_time (GstClock * clock, GstBaseAudioSink * sink)
{ {
guint64 raw, samples; guint64 raw, samples;
guint delay; guint delay;
GstClockTime result, us_latency; GstClockTime result;
if (sink->ringbuffer == NULL || sink->ringbuffer->spec.rate == 0) if (sink->ringbuffer == NULL || sink->ringbuffer->spec.rate == 0)
return GST_CLOCK_TIME_NONE; return GST_CLOCK_TIME_NONE;
@ -391,15 +391,9 @@ gst_base_audio_sink_get_time (GstClock * clock, GstBaseAudioSink * sink)
result = gst_util_uint64_scale_int (samples, GST_SECOND, result = gst_util_uint64_scale_int (samples, GST_SECOND,
sink->ringbuffer->spec.rate); sink->ringbuffer->spec.rate);
/* latency before starting the clock */
us_latency = sink->priv->us_latency;
result += us_latency;
GST_DEBUG_OBJECT (sink, GST_DEBUG_OBJECT (sink,
"processed samples: raw %llu, delay %u, real %llu, time %" "processed samples: raw %llu, delay %u, real %llu, time %"
GST_TIME_FORMAT ", upstream latency %" GST_TIME_FORMAT, raw, delay, GST_TIME_FORMAT, raw, delay, samples, GST_TIME_ARGS (result));
samples, GST_TIME_ARGS (result), GST_TIME_ARGS (us_latency));
return result; return result;
} }
@ -787,8 +781,7 @@ gst_base_audio_sink_get_offset (GstBaseAudioSink * sink)
static GstClockTime static GstClockTime
clock_convert_external (GstClockTime external, GstClockTime cinternal, clock_convert_external (GstClockTime external, GstClockTime cinternal,
GstClockTime cexternal, GstClockTime crate_num, GstClockTime crate_denom, GstClockTime cexternal, GstClockTime crate_num, GstClockTime crate_denom)
GstClockTime us_latency)
{ {
/* adjust for rate and speed */ /* adjust for rate and speed */
if (external >= cexternal) { if (external >= cexternal) {
@ -803,12 +796,6 @@ clock_convert_external (GstClockTime external, GstClockTime cinternal,
else else
external = 0; external = 0;
} }
/* adjust for offset when slaving started */
if (external > us_latency)
external -= us_latency;
else
external = 0;
return external; return external;
} }
@ -838,9 +825,9 @@ gst_base_audio_sink_resample_slaving (GstBaseAudioSink * sink,
/* bring external time to internal time */ /* bring external time to internal time */
render_start = clock_convert_external (render_start, cinternal, cexternal, render_start = clock_convert_external (render_start, cinternal, cexternal,
crate_num, crate_denom, sink->priv->us_latency); crate_num, crate_denom);
render_stop = clock_convert_external (render_stop, cinternal, cexternal, render_stop = clock_convert_external (render_stop, cinternal, cexternal,
crate_num, crate_denom, sink->priv->us_latency); crate_num, crate_denom);
GST_DEBUG_OBJECT (sink, GST_DEBUG_OBJECT (sink,
"after slaving: start %" GST_TIME_FORMAT " - stop %" GST_TIME_FORMAT, "after slaving: start %" GST_TIME_FORMAT " - stop %" GST_TIME_FORMAT,
@ -875,6 +862,9 @@ gst_base_audio_sink_skew_slaving (GstBaseAudioSink * sink,
etime = etime > cexternal ? etime - cexternal : 0; etime = etime > cexternal ? etime - cexternal : 0;
itime = itime > cinternal ? itime - cinternal : 0; itime = itime > cinternal ? itime - cinternal : 0;
/* do itime - etime.
* positive value means external clock goes slower
* negative value means external clock goes faster */
skew = GST_CLOCK_DIFF (etime, itime); skew = GST_CLOCK_DIFF (etime, itime);
if (sink->priv->avg_skew == -1) { if (sink->priv->avg_skew == -1) {
/* first observation */ /* first observation */
@ -945,9 +935,9 @@ gst_base_audio_sink_skew_slaving (GstBaseAudioSink * sink,
/* convert, ignoring speed */ /* convert, ignoring speed */
render_start = clock_convert_external (render_start, cinternal, cexternal, render_start = clock_convert_external (render_start, cinternal, cexternal,
crate_num, crate_denom, sink->priv->us_latency); crate_num, crate_denom);
render_stop = clock_convert_external (render_stop, cinternal, cexternal, render_stop = clock_convert_external (render_stop, cinternal, cexternal,
crate_num, crate_denom, sink->priv->us_latency); crate_num, crate_denom);
*srender_start = render_start; *srender_start = render_start;
*srender_stop = render_stop; *srender_stop = render_stop;
@ -967,9 +957,9 @@ gst_base_audio_sink_none_slaving (GstBaseAudioSink * sink,
/* convert, ignoring speed */ /* convert, ignoring speed */
render_start = clock_convert_external (render_start, cinternal, cexternal, render_start = clock_convert_external (render_start, cinternal, cexternal,
crate_num, crate_denom, sink->priv->us_latency); crate_num, crate_denom);
render_stop = clock_convert_external (render_stop, cinternal, cexternal, render_stop = clock_convert_external (render_stop, cinternal, cexternal,
crate_num, crate_denom, sink->priv->us_latency); crate_num, crate_denom);
*srender_start = render_start; *srender_start = render_start;
*srender_stop = render_stop; *srender_stop = render_stop;
@ -1153,26 +1143,34 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf)
render_stop = gst_util_uint64_scale_int (render_stop, render_stop = gst_util_uint64_scale_int (render_stop,
ringbuf->spec.rate, GST_SECOND); ringbuf->spec.rate, GST_SECOND);
/* positive playback rate, first sample is render_start, negative rate, first
* sample is render_stop. When no rate conversion is active, render exactly
* the amount of input samples to avoid aligning to rounding errors. */
if (bsink->segment.rate >= 0.0) {
sample_offset = render_start;
if (bsink->segment.rate == 1.0)
render_stop = sample_offset + samples;
} else {
sample_offset = render_stop;
if (bsink->segment.rate == -1.0)
render_start = sample_offset + samples;
}
/* always resync after a discont */ /* always resync after a discont */
if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT))) { if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT))) {
GST_DEBUG_OBJECT (sink, "resync after discont"); GST_DEBUG_OBJECT (sink, "resync after discont");
goto no_align; goto no_align;
} }
/* resync when we don't know what to align the sample with */
if (G_UNLIKELY (sink->next_sample == -1)) { if (G_UNLIKELY (sink->next_sample == -1)) {
GST_DEBUG_OBJECT (sink, GST_DEBUG_OBJECT (sink,
"no align possible: no previous sample position known"); "no align possible: no previous sample position known");
goto no_align; goto no_align;
} }
/* positive playback rate, first sample is render_start, negative rate, first /* now try to align the sample to the previous one, first see how big the
* sample is render_stop */ * difference is. */
if (bsink->segment.rate >= 0.0)
sample_offset = render_start;
else
sample_offset = render_stop;
/* now try to align the sample to the previous one */
if (sample_offset >= sink->next_sample) if (sample_offset >= sink->next_sample)
diff = sample_offset - sink->next_sample; diff = sample_offset - sink->next_sample;
else else
@ -1422,12 +1420,22 @@ gst_base_audio_sink_async_play (GstBaseSink * basesink)
/* if we are slaved to a clock, we need to set the initial /* if we are slaved to a clock, we need to set the initial
* calibration */ * calibration */
/* get external and internal time to set as calibration params */ /* get external and internal time to set as calibration params */
etime = gst_clock_get_time (clock); etime = GST_ELEMENT_CAST (sink)->base_time;
itime = gst_clock_get_internal_time (sink->provided_clock); itime = gst_base_audio_sink_get_time (sink->provided_clock, sink);
sink->priv->avg_skew = -1;
sink->next_sample = -1;
switch (sink->priv->slave_method) {
case GST_BASE_AUDIO_SINK_SLAVE_SKEW:
case GST_BASE_AUDIO_SINK_SLAVE_RESAMPLE:
/* adjust with upstream latency, when we are prerolled, our internal clock
* should exactly have been the time of the upstream latency */
etime += sink->priv->us_latency;
break;
case GST_BASE_AUDIO_SINK_SLAVE_NONE:
/* no slaving, base_time corresponds to our 0 time */
itime = 0;
default:
break;
}
GST_DEBUG_OBJECT (sink, GST_DEBUG_OBJECT (sink,
"internal time: %" GST_TIME_FORMAT " external time: %" GST_TIME_FORMAT, "internal time: %" GST_TIME_FORMAT " external time: %" GST_TIME_FORMAT,
GST_TIME_ARGS (itime), GST_TIME_ARGS (etime)); GST_TIME_ARGS (itime), GST_TIME_ARGS (etime));
@ -1439,14 +1447,19 @@ gst_base_audio_sink_async_play (GstBaseSink * basesink)
switch (sink->priv->slave_method) { switch (sink->priv->slave_method) {
case GST_BASE_AUDIO_SINK_SLAVE_RESAMPLE: case GST_BASE_AUDIO_SINK_SLAVE_RESAMPLE:
/* only set as master if we need to resample */ /* only set as master when we are resampling */
GST_DEBUG_OBJECT (sink, "Setting clock as master"); GST_DEBUG_OBJECT (sink, "Setting clock as master");
gst_clock_set_master (sink->provided_clock, clock); gst_clock_set_master (sink->provided_clock, clock);
break; break;
case GST_BASE_AUDIO_SINK_SLAVE_SKEW:
case GST_BASE_AUDIO_SINK_SLAVE_NONE:
default: default:
break; break;
} }
sink->priv->avg_skew = -1;
sink->next_sample = -1;
/* start ringbuffer so we can start slaving right away when we need to */ /* start ringbuffer so we can start slaving right away when we need to */
gst_ring_buffer_start (sink->ringbuffer); gst_ring_buffer_start (sink->ringbuffer);