Commit 450030eb authored by Wim Taymans's avatar Wim Taymans
Browse files

gst-libs/gst/audio/gstbaseaudiosink.*: Store private stuff in GstBaseAudioSinkPrivate.

Original commit message from CVS:
* gst-libs/gst/audio/gstbaseaudiosink.c: (slave_method_get_type),
(gst_base_audio_sink_class_init), (gst_base_audio_sink_init),
(gst_base_audio_sink_query), (gst_base_audio_sink_get_time),
(gst_base_audio_sink_set_property),
(gst_base_audio_sink_get_property), (gst_base_audio_sink_event),
(clock_convert_external), (gst_base_audio_sink_resample_slaving),
(gst_base_audio_sink_skew_slaving),
(gst_base_audio_sink_handle_slaving), (gst_base_audio_sink_render),
(gst_base_audio_sink_async_play):
* gst-libs/gst/audio/gstbaseaudiosink.h:
Store private stuff in GstBaseAudioSinkPrivate.
Add configurable clock slaving modes property.
API:: GstBaseAudioSink::slave-method property
Some more latency reporting tweaks.
Added skew based clock slaving correction and make it the default until
the resampling method is more robust.
parent 293a9c09
2007-03-28 Wim Taymans <wim@fluendo.com>
* gst-libs/gst/audio/gstbaseaudiosink.c: (slave_method_get_type),
(gst_base_audio_sink_class_init), (gst_base_audio_sink_init),
(gst_base_audio_sink_query), (gst_base_audio_sink_get_time),
(gst_base_audio_sink_set_property),
(gst_base_audio_sink_get_property), (gst_base_audio_sink_event),
(clock_convert_external), (gst_base_audio_sink_resample_slaving),
(gst_base_audio_sink_skew_slaving),
(gst_base_audio_sink_handle_slaving), (gst_base_audio_sink_render),
(gst_base_audio_sink_async_play):
* gst-libs/gst/audio/gstbaseaudiosink.h:
Store private stuff in GstBaseAudioSinkPrivate.
Add configurable clock slaving modes property.
API:: GstBaseAudioSink::slave-method property
Some more latency reporting tweaks.
Added skew based clock slaving correction and make it the default until
the resampling method is more robust.
2007-03-27 Sebastian Dröge <slomo@circular-chaos.org> 2007-03-27 Sebastian Dröge <slomo@circular-chaos.org>
   
* gst/audioconvert/audioconvert.c: * gst/audioconvert/audioconvert.c:
...@@ -39,6 +39,19 @@ ...@@ -39,6 +39,19 @@
GST_DEBUG_CATEGORY_STATIC (gst_base_audio_sink_debug); GST_DEBUG_CATEGORY_STATIC (gst_base_audio_sink_debug);
#define GST_CAT_DEFAULT gst_base_audio_sink_debug #define GST_CAT_DEFAULT gst_base_audio_sink_debug
#define GST_BASE_AUDIO_SINK_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_BASE_AUDIO_SINK, GstBaseAudioSinkPrivate))
struct _GstBaseAudioSinkPrivate
{
/* upstream latency */
GstClockTime us_latency;
/* the clock slaving algorithm in use */
GstBaseAudioSinkSlaveMethod slave_method;
/* running average of clock skew */
GstClockTimeDiff avg_skew;
};
/* BaseAudioSink signals and args */ /* BaseAudioSink signals and args */
enum enum
{ {
...@@ -59,6 +72,7 @@ enum ...@@ -59,6 +72,7 @@ enum
#define DEFAULT_BUFFER_TIME ((200 * GST_MSECOND) / GST_USECOND) #define DEFAULT_BUFFER_TIME ((200 * GST_MSECOND) / GST_USECOND)
#define DEFAULT_LATENCY_TIME ((10 * GST_MSECOND) / GST_USECOND) #define DEFAULT_LATENCY_TIME ((10 * GST_MSECOND) / GST_USECOND)
#define DEFAULT_PROVIDE_CLOCK TRUE #define DEFAULT_PROVIDE_CLOCK TRUE
#define DEFAULT_SLAVE_METHOD GST_BASE_AUDIO_SINK_SLAVE_SKEW
enum enum
{ {
...@@ -66,8 +80,29 @@ enum ...@@ -66,8 +80,29 @@ enum
PROP_BUFFER_TIME, PROP_BUFFER_TIME,
PROP_LATENCY_TIME, PROP_LATENCY_TIME,
PROP_PROVIDE_CLOCK, PROP_PROVIDE_CLOCK,
PROP_SLAVE_METHOD
}; };
#define GST_TYPE_SLAVE_METHOD (slave_method_get_type ())
static GType
slave_method_get_type (void)
{
static GType slave_method_type = 0;
static const GEnumValue slave_method[] = {
{GST_BASE_AUDIO_SINK_SLAVE_RESAMPLE, "Resampling slaving", "resample"},
{GST_BASE_AUDIO_SINK_SLAVE_SKEW, "Skew slaving", "skew"},
{0, NULL, NULL},
};
if (!slave_method_type) {
slave_method_type =
g_enum_register_static ("GstBaseAudioSinkSlaveMethod", slave_method);
}
return slave_method_type;
}
#define _do_init(bla) \ #define _do_init(bla) \
GST_DEBUG_CATEGORY_INIT (gst_base_audio_sink_debug, "baseaudiosink", 0, "baseaudiosink element"); GST_DEBUG_CATEGORY_INIT (gst_base_audio_sink_debug, "baseaudiosink", 0, "baseaudiosink element");
...@@ -126,6 +161,8 @@ gst_base_audio_sink_class_init (GstBaseAudioSinkClass * klass) ...@@ -126,6 +161,8 @@ gst_base_audio_sink_class_init (GstBaseAudioSinkClass * klass)
gstelement_class = (GstElementClass *) klass; gstelement_class = (GstElementClass *) klass;
gstbasesink_class = (GstBaseSinkClass *) klass; gstbasesink_class = (GstBaseSinkClass *) klass;
g_type_class_add_private (klass, sizeof (GstBaseAudioSinkPrivate));
gobject_class->set_property = gobject_class->set_property =
GST_DEBUG_FUNCPTR (gst_base_audio_sink_set_property); GST_DEBUG_FUNCPTR (gst_base_audio_sink_set_property);
gobject_class->get_property = gobject_class->get_property =
...@@ -147,6 +184,11 @@ gst_base_audio_sink_class_init (GstBaseAudioSinkClass * klass) ...@@ -147,6 +184,11 @@ gst_base_audio_sink_class_init (GstBaseAudioSinkClass * klass)
"Provide a clock to be used as the global pipeline clock", "Provide a clock to be used as the global pipeline clock",
DEFAULT_PROVIDE_CLOCK, G_PARAM_READWRITE)); DEFAULT_PROVIDE_CLOCK, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_SLAVE_METHOD,
g_param_spec_enum ("slave-method", "Slave Method",
"Algorithm to use to match the rate of the masterclock",
GST_TYPE_SLAVE_METHOD, DEFAULT_SLAVE_METHOD, G_PARAM_READWRITE));
gstelement_class->change_state = gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_base_audio_sink_change_state); GST_DEBUG_FUNCPTR (gst_base_audio_sink_change_state);
gstelement_class->provide_clock = gstelement_class->provide_clock =
...@@ -170,9 +212,12 @@ static void ...@@ -170,9 +212,12 @@ static void
gst_base_audio_sink_init (GstBaseAudioSink * baseaudiosink, gst_base_audio_sink_init (GstBaseAudioSink * baseaudiosink,
GstBaseAudioSinkClass * g_class) GstBaseAudioSinkClass * g_class)
{ {
baseaudiosink->priv = GST_BASE_AUDIO_SINK_GET_PRIVATE (baseaudiosink);
baseaudiosink->buffer_time = DEFAULT_BUFFER_TIME; baseaudiosink->buffer_time = DEFAULT_BUFFER_TIME;
baseaudiosink->latency_time = DEFAULT_LATENCY_TIME; baseaudiosink->latency_time = DEFAULT_LATENCY_TIME;
baseaudiosink->provide_clock = DEFAULT_PROVIDE_CLOCK; baseaudiosink->provide_clock = DEFAULT_PROVIDE_CLOCK;
baseaudiosink->priv->slave_method = DEFAULT_SLAVE_METHOD;
baseaudiosink->provided_clock = gst_audio_clock_new ("GstAudioSinkClock", baseaudiosink->provided_clock = gst_audio_clock_new ("GstAudioSinkClock",
(GstAudioClockGetTimeFunc) gst_base_audio_sink_get_time, baseaudiosink); (GstAudioClockGetTimeFunc) gst_base_audio_sink_get_time, baseaudiosink);
...@@ -276,14 +321,17 @@ gst_base_audio_sink_query (GstElement * element, GstQuery * query) ...@@ -276,14 +321,17 @@ gst_base_audio_sink_query (GstElement * element, GstQuery * query)
spec = &basesink->ringbuffer->spec; spec = &basesink->ringbuffer->spec;
basesink->priv->us_latency = min_l;
min_latency = min_latency =
gst_util_uint64_scale_int (spec->segtotal * spec->segsize, gst_util_uint64_scale_int (spec->segtotal * spec->segsize,
GST_SECOND, spec->rate * spec->bytes_per_sample); GST_SECOND, spec->rate * spec->bytes_per_sample);
/* we cannot go lower than the buffer size */
min_latency = MAX (min_latency, min_l); /* we cannot go lower than the buffer size and the min peer latency */
min_latency = min_latency + min_l;
/* the max latency is the max of the peer, we can delay an infinite /* the max latency is the max of the peer, we can delay an infinite
* amount of time. */ * amount of time. */
max_latency = max_l; max_latency = min_latency + (max_l == -1 ? 0 : max_l);
GST_DEBUG_OBJECT (basesink, GST_DEBUG_OBJECT (basesink,
"peer min %" GST_TIME_FORMAT ", our min latency: %" "peer min %" GST_TIME_FORMAT ", our min latency: %"
...@@ -314,7 +362,7 @@ gst_base_audio_sink_get_time (GstClock * clock, GstBaseAudioSink * sink) ...@@ -314,7 +362,7 @@ gst_base_audio_sink_get_time (GstClock * clock, GstBaseAudioSink * sink)
{ {
guint64 raw, samples; guint64 raw, samples;
guint delay; guint delay;
GstClockTime result; GstClockTime result, us_latency;
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;
...@@ -334,9 +382,15 @@ gst_base_audio_sink_get_time (GstClock * clock, GstBaseAudioSink * sink) ...@@ -334,9 +382,15 @@ 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, raw, delay, samples, GST_TIME_ARGS (result)); GST_TIME_FORMAT ", upstream latency %" GST_TIME_FORMAT, raw, delay,
samples, GST_TIME_ARGS (result), GST_TIME_ARGS (us_latency));
return result; return result;
} }
...@@ -361,6 +415,9 @@ gst_base_audio_sink_set_property (GObject * object, guint prop_id, ...@@ -361,6 +415,9 @@ gst_base_audio_sink_set_property (GObject * object, guint prop_id,
sink->provide_clock = g_value_get_boolean (value); sink->provide_clock = g_value_get_boolean (value);
GST_OBJECT_UNLOCK (sink); GST_OBJECT_UNLOCK (sink);
break; break;
case PROP_SLAVE_METHOD:
sink->priv->slave_method = g_value_get_enum (value);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
...@@ -387,6 +444,9 @@ gst_base_audio_sink_get_property (GObject * object, guint prop_id, ...@@ -387,6 +444,9 @@ gst_base_audio_sink_get_property (GObject * object, guint prop_id,
g_value_set_boolean (value, sink->provide_clock); g_value_set_boolean (value, sink->provide_clock);
GST_OBJECT_UNLOCK (sink); GST_OBJECT_UNLOCK (sink);
break; break;
case PROP_SLAVE_METHOD:
g_value_set_enum (value, sink->priv->slave_method);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
...@@ -542,6 +602,7 @@ gst_base_audio_sink_event (GstBaseSink * bsink, GstEvent * event) ...@@ -542,6 +602,7 @@ gst_base_audio_sink_event (GstBaseSink * bsink, GstEvent * event)
break; break;
case GST_EVENT_FLUSH_STOP: case GST_EVENT_FLUSH_STOP:
/* always resync on sample after a flush */ /* always resync on sample after a flush */
sink->priv->avg_skew = -1;
sink->next_sample = -1; sink->next_sample = -1;
if (sink->ringbuffer) if (sink->ringbuffer)
gst_ring_buffer_set_flushing (sink->ringbuffer, FALSE); gst_ring_buffer_set_flushing (sink->ringbuffer, FALSE);
...@@ -621,6 +682,163 @@ gst_base_audio_sink_get_offset (GstBaseAudioSink * sink) ...@@ -621,6 +682,163 @@ gst_base_audio_sink_get_offset (GstBaseAudioSink * sink)
return sample; return sample;
} }
static GstClockTime
clock_convert_external (GstClockTime external, GstClockTime cinternal,
GstClockTime cexternal, GstClockTime crate_num, GstClockTime crate_denom,
GstClockTime us_latency)
{
/* adjust for rate and speed */
if (external >= cexternal) {
external =
gst_util_uint64_scale (external - cexternal, crate_denom, crate_num);
external += cinternal;
} else {
external = gst_util_uint64_scale (cexternal - external,
crate_denom, crate_num);
if (cinternal > external)
external = cinternal - external;
else
external = 0;
}
/* adjust for offset when slaving started */
if (external > us_latency)
external -= us_latency;
else
external = 0;
return external;
}
/* algorithm to calculate sample positions that will result in resampling to
* match the clock rate of the master */
static void
gst_base_audio_sink_resample_slaving (GstBaseAudioSink * sink,
GstClockTime render_start, GstClockTime render_stop,
GstClockTime * srender_start, GstClockTime * srender_stop)
{
GstClockTime cinternal, cexternal;
GstClockTime crate_num, crate_denom;
/* get calibration parameters to compensate for speed and offset differences
* when we are slaved */
gst_clock_get_calibration (sink->provided_clock, &cinternal, &cexternal,
&crate_num, &crate_denom);
GST_DEBUG_OBJECT (sink, "internal %" GST_TIME_FORMAT " external %"
GST_TIME_FORMAT " %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT " = %f",
GST_TIME_ARGS (cinternal), GST_TIME_ARGS (cexternal), crate_num,
crate_denom, gst_guint64_to_gdouble (crate_num) /
gst_guint64_to_gdouble (crate_denom));
if (crate_num == 0)
crate_denom = crate_num = 1;
/* bring external time to internal time */
render_start = clock_convert_external (render_start, cinternal, cexternal,
crate_num, crate_denom, sink->priv->us_latency);
render_stop = clock_convert_external (render_stop, cinternal, cexternal,
crate_num, crate_denom, sink->priv->us_latency);
GST_DEBUG_OBJECT (sink,
"after slaving: start %" GST_TIME_FORMAT " - stop %" GST_TIME_FORMAT,
GST_TIME_ARGS (render_start), GST_TIME_ARGS (render_stop));
*srender_start = render_start;
*srender_stop = render_stop;
}
/* algorithm to calculate sample positions that will result in changing the
* playout pointer to match the clock rate of the master */
static void
gst_base_audio_sink_skew_slaving (GstBaseAudioSink * sink,
GstClockTime render_start, GstClockTime render_stop,
GstClockTime * srender_start, GstClockTime * srender_stop)
{
GstClockTime cinternal, cexternal, crate_num, crate_denom;
GstClockTime etime, itime;
GstClockTimeDiff skew, segtime;
/* get calibration parameters to compensate for offsets */
gst_clock_get_calibration (sink->provided_clock, &cinternal, &cexternal,
&crate_num, &crate_denom);
/* sample clocks and figure out clock skew */
etime = gst_clock_get_time (GST_ELEMENT_CLOCK (sink));
itime = gst_clock_get_internal_time (sink->provided_clock);
etime -= cexternal;
itime -= cinternal;
skew = GST_CLOCK_DIFF (etime, itime);
if (sink->priv->avg_skew == -1) {
/* first observation */
sink->priv->avg_skew = skew;
} else {
/* next observations use a moving average */
sink->priv->avg_skew = (31 * sink->priv->avg_skew + skew) / 32;
}
GST_DEBUG_OBJECT (sink, "internal %" GST_TIME_FORMAT " external %"
GST_TIME_FORMAT " skew %" G_GINT64_FORMAT " avg %" G_GINT64_FORMAT,
GST_TIME_ARGS (itime), GST_TIME_ARGS (etime), skew, sink->priv->avg_skew);
/* the max drift we allow is the length of a segment */
segtime = sink->ringbuffer->spec.latency_time * 1000;
/* adjust playout pointer based on skew */
if (sink->priv->avg_skew > segtime) {
/* master is running slower */
GST_WARNING_OBJECT (sink,
"correct clock skew %" G_GINT64_FORMAT " > %" G_GINT64_FORMAT,
sink->priv->avg_skew, segtime);
cinternal += segtime;
sink->priv->avg_skew -= segtime;
sink->next_sample = -1;
gst_clock_set_calibration (sink->provided_clock, cinternal, cexternal,
crate_num, crate_denom);
} else if (sink->priv->avg_skew < -segtime) {
/* master is running faster */
GST_WARNING_OBJECT (sink,
"correct clock skew %" G_GINT64_FORMAT " < %" G_GINT64_FORMAT,
sink->priv->avg_skew, -segtime);
cinternal -= segtime;
sink->priv->avg_skew += segtime;
sink->next_sample = -1;
gst_clock_set_calibration (sink->provided_clock, cinternal, cexternal,
crate_num, crate_denom);
}
/* convert, ignoring speed */
render_start = clock_convert_external (render_start, cinternal, cexternal,
crate_num, crate_denom, sink->priv->us_latency);
render_stop = clock_convert_external (render_stop, cinternal, cexternal,
crate_num, crate_denom, sink->priv->us_latency);
*srender_start = render_start;
*srender_stop = render_stop;
}
/* converts render_start and render_stop to their slaved values */
static void
gst_base_audio_sink_handle_slaving (GstBaseAudioSink * sink,
GstClockTime render_start, GstClockTime render_stop,
GstClockTime * srender_start, GstClockTime * srender_stop)
{
switch (sink->priv->slave_method) {
case GST_BASE_AUDIO_SINK_SLAVE_RESAMPLE:
gst_base_audio_sink_resample_slaving (sink, render_start, render_stop,
srender_start, srender_stop);
break;
case GST_BASE_AUDIO_SINK_SLAVE_SKEW:
gst_base_audio_sink_skew_slaving (sink, render_start, render_stop,
srender_start, srender_stop);
break;
default:
g_warning ("unknown slaving method %d", sink->priv->slave_method);
break;
}
}
static GstFlowReturn static GstFlowReturn
gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf) gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf)
{ {
...@@ -634,10 +852,8 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf) ...@@ -634,10 +852,8 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf)
guint samples, written; guint samples, written;
gint bps; gint bps;
gint accum; gint accum;
GstClockTime crate_num;
GstClockTime crate_denom;
gint out_samples; gint out_samples;
GstClockTime base_time, cinternal, cexternal, latency; GstClockTime base_time, latency;
GstClock *clock; GstClock *clock;
gboolean sync, slaved; gboolean sync, slaved;
...@@ -751,64 +967,21 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf) ...@@ -751,64 +967,21 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf)
GST_DEBUG_OBJECT (sink, GST_DEBUG_OBJECT (sink,
"compensating for latency %" GST_TIME_FORMAT, GST_TIME_ARGS (latency)); "compensating for latency %" GST_TIME_FORMAT, GST_TIME_ARGS (latency));
slaved = clock != sink->provided_clock; /* add latency to get the timestamp to sync against the pipeline clock */
if (slaved) { render_start += latency;
/* get calibration parameters to compensate for speed and offset differences render_stop += latency;
* when we are slaved */
gst_clock_get_calibration (sink->provided_clock, &cinternal, &cexternal,
&crate_num, &crate_denom);
cinternal += latency;
GST_DEBUG_OBJECT (sink, "internal %" GST_TIME_FORMAT " external %"
GST_TIME_FORMAT " %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT " = %f",
GST_TIME_ARGS (cinternal), GST_TIME_ARGS (cexternal), crate_num,
crate_denom, gst_guint64_to_gdouble (crate_num / crate_denom));
if (crate_num == 0)
crate_denom = crate_num = 1;
/* bring to our slaved clock time */
if (render_start >= cexternal) {
render_start =
gst_util_uint64_scale (render_start - cexternal, crate_denom,
crate_num);
render_start += cinternal;
} else {
render_start = gst_util_uint64_scale (cexternal - render_start,
crate_denom, crate_num);
if (cinternal > render_start)
render_start = cinternal - render_start;
else
render_start = 0;
}
if (render_stop >= cexternal) { GST_DEBUG_OBJECT (sink,
render_stop = "after latency: start %" GST_TIME_FORMAT " - stop %" GST_TIME_FORMAT,
gst_util_uint64_scale (render_stop - cexternal, crate_denom, GST_TIME_ARGS (render_start), GST_TIME_ARGS (render_stop));
crate_num);
render_stop += cinternal;
} else {
render_stop = gst_util_uint64_scale (cexternal - render_stop,
crate_denom, crate_num);
if (cinternal > render_stop)
render_stop = cinternal - render_stop;
else
render_stop = 0;
}
GST_DEBUG_OBJECT (sink, slaved = clock != sink->provided_clock;
"after slaving: start %" GST_TIME_FORMAT " - stop %" GST_TIME_FORMAT, if (slaved) {
GST_TIME_ARGS (render_start), GST_TIME_ARGS (render_stop)); /* handle clock slaving */
} else { gst_base_audio_sink_handle_slaving (sink, render_start, render_stop,
render_start += latency; &render_start, &render_stop);
render_stop += latency;
GST_DEBUG_OBJECT (sink,
"after latency: start %" GST_TIME_FORMAT " - stop %" GST_TIME_FORMAT,
GST_TIME_ARGS (render_start), GST_TIME_ARGS (render_stop));
} }
/* and bring the time to the rate corrected offset in the buffer */ /* and bring the time to the rate corrected offset in the buffer */
render_start = gst_util_uint64_scale_int (render_start, render_start = gst_util_uint64_scale_int (render_start,
ringbuf->spec.rate, GST_SECOND); ringbuf->spec.rate, GST_SECOND);
...@@ -827,6 +1000,8 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf) ...@@ -827,6 +1000,8 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf)
goto no_align; goto no_align;
} }
/* positive playback rate, first sample is render_start, negative rate, first
* sample is render_stop */
if (bsink->segment.rate >= 1.0) if (bsink->segment.rate >= 1.0)
sample_offset = render_start; sample_offset = render_start;
else else
...@@ -864,8 +1039,8 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf) ...@@ -864,8 +1039,8 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf)
/* apply alignment */ /* apply alignment */
render_start += align; render_start += align;
/* only align stop if we are not slaved */ /* only align stop if we are not slaved to resample */
if (slaved) { if (slaved && sink->priv->slave_method == GST_BASE_AUDIO_SINK_SLAVE_RESAMPLE) {
GST_DEBUG_OBJECT (sink, "no stop time align needed: we are slaved"); GST_DEBUG_OBJECT (sink, "no stop time align needed: we are slaved");
goto no_align; goto no_align;
} }
...@@ -876,15 +1051,15 @@ no_align: ...@@ -876,15 +1051,15 @@ no_align:
out_samples = render_stop - render_start; out_samples = render_stop - render_start;
no_sync: no_sync:
GST_DEBUG_OBJECT (sink, "rendering at %" G_GUINT64_FORMAT " %d/%d",
sink->next_sample, samples, out_samples);
/* we render the first or last sample first, depending on the rate */ /* we render the first or last sample first, depending on the rate */
if (bsink->segment.rate >= 1.0) if (bsink->segment.rate >= 1.0)
sample_offset = render_start; sample_offset = render_start;
else else
sample_offset = render_stop; sample_offset = render_stop;
GST_DEBUG_OBJECT (sink, "rendering at %" G_GUINT64_FORMAT " %d/%d",
sample_offset, samples, out_samples);
/* we need to accumulate over different runs for when we get interrupted */ /* we need to accumulate over different runs for when we get interrupted */
accum = 0; accum = 0;
do { do {
...@@ -1038,8 +1213,9 @@ static GstStateChangeReturn ...@@ -1038,8 +1213,9 @@ static GstStateChangeReturn
gst_base_audio_sink_async_play (GstBaseSink * basesink) gst_base_audio_sink_async_play (GstBaseSink * basesink)
{ {
GstClock *clock; GstClock *clock;
GstClockTime itime, etime;
GstBaseAudioSink *sink; GstBaseAudioSink *sink;
GstClockTime itime, etime;
GstClockTime rate_num, rate_denom;
sink = GST_BASE_AUDIO_SINK (basesink); sink = GST_BASE_AUDIO_SINK (basesink);
...@@ -1048,34 +1224,43 @@ gst_base_audio_sink_async_play (GstBaseSink * basesink) ...@@ -1048,34 +1224,43 @@ gst_base_audio_sink_async_play (GstBaseSink * basesink)
clock = GST_ELEMENT_CLOCK (sink); clock = GST_ELEMENT_CLOCK (sink);
if (clock == NULL) if (clock == NULL)
goto no_clock; goto done;
/* we provided the global clock, don't need to do anything special */
if (clock == sink->provided_clock)
goto done;
/* FIXME, only start slaving when we really start the ringbuffer */
/* 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 */
if (clock != sink->provided_clock) { /* get external and internal time to set as calibration params */
GstClockTime rate_num, rate_denom; etime = gst_clock_get_time (clock);
itime = gst_clock_get_internal_time (sink->provided_clock);
etime = gst_clock_get_time (clock);
itime = gst_clock_get_internal_time (sink->provided_clock);
GST_DEBUG_OBJECT (sink,</