From 450030ebaf0b1ec429bc0841591aee3ab2b2c28e Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 28 Mar 2007 14:50:47 +0000 Subject: [PATCH] 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. --- ChangeLog | 19 ++ gst-libs/gst/audio/gstbaseaudiosink.c | 363 +++++++++++++++++++++++++--------- gst-libs/gst/audio/gstbaseaudiosink.h | 22 ++- 3 files changed, 311 insertions(+), 93 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6a05e08..8b985e8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +2007-03-28 Wim Taymans + + * 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 * gst/audioconvert/audioconvert.c: diff --git a/gst-libs/gst/audio/gstbaseaudiosink.c b/gst-libs/gst/audio/gstbaseaudiosink.c index b4688f6..adacbbc 100644 --- a/gst-libs/gst/audio/gstbaseaudiosink.c +++ b/gst-libs/gst/audio/gstbaseaudiosink.c @@ -39,6 +39,19 @@ GST_DEBUG_CATEGORY_STATIC (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 */ enum { @@ -59,6 +72,7 @@ enum #define DEFAULT_BUFFER_TIME ((200 * GST_MSECOND) / GST_USECOND) #define DEFAULT_LATENCY_TIME ((10 * GST_MSECOND) / GST_USECOND) #define DEFAULT_PROVIDE_CLOCK TRUE +#define DEFAULT_SLAVE_METHOD GST_BASE_AUDIO_SINK_SLAVE_SKEW enum { @@ -66,8 +80,29 @@ enum PROP_BUFFER_TIME, PROP_LATENCY_TIME, 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) \ 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) gstelement_class = (GstElementClass *) klass; gstbasesink_class = (GstBaseSinkClass *) klass; + g_type_class_add_private (klass, sizeof (GstBaseAudioSinkPrivate)); + gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_base_audio_sink_set_property); gobject_class->get_property = @@ -147,6 +184,11 @@ gst_base_audio_sink_class_init (GstBaseAudioSinkClass * klass) "Provide a clock to be used as the global pipeline clock", 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 = GST_DEBUG_FUNCPTR (gst_base_audio_sink_change_state); gstelement_class->provide_clock = @@ -170,9 +212,12 @@ static void gst_base_audio_sink_init (GstBaseAudioSink * baseaudiosink, GstBaseAudioSinkClass * g_class) { + baseaudiosink->priv = GST_BASE_AUDIO_SINK_GET_PRIVATE (baseaudiosink); + baseaudiosink->buffer_time = DEFAULT_BUFFER_TIME; baseaudiosink->latency_time = DEFAULT_LATENCY_TIME; baseaudiosink->provide_clock = DEFAULT_PROVIDE_CLOCK; + baseaudiosink->priv->slave_method = DEFAULT_SLAVE_METHOD; baseaudiosink->provided_clock = gst_audio_clock_new ("GstAudioSinkClock", (GstAudioClockGetTimeFunc) gst_base_audio_sink_get_time, baseaudiosink); @@ -276,14 +321,17 @@ gst_base_audio_sink_query (GstElement * element, GstQuery * query) spec = &basesink->ringbuffer->spec; + basesink->priv->us_latency = min_l; + min_latency = gst_util_uint64_scale_int (spec->segtotal * spec->segsize, 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 * amount of time. */ - max_latency = max_l; + max_latency = min_latency + (max_l == -1 ? 0 : max_l); GST_DEBUG_OBJECT (basesink, "peer min %" GST_TIME_FORMAT ", our min latency: %" @@ -314,7 +362,7 @@ gst_base_audio_sink_get_time (GstClock * clock, GstBaseAudioSink * sink) { guint64 raw, samples; guint delay; - GstClockTime result; + GstClockTime result, us_latency; if (sink->ringbuffer == NULL || sink->ringbuffer->spec.rate == 0) return GST_CLOCK_TIME_NONE; @@ -334,9 +382,15 @@ gst_base_audio_sink_get_time (GstClock * clock, GstBaseAudioSink * sink) result = gst_util_uint64_scale_int (samples, GST_SECOND, sink->ringbuffer->spec.rate); + /* latency before starting the clock */ + us_latency = sink->priv->us_latency; + + result += us_latency; + GST_DEBUG_OBJECT (sink, "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; } @@ -361,6 +415,9 @@ gst_base_audio_sink_set_property (GObject * object, guint prop_id, sink->provide_clock = g_value_get_boolean (value); GST_OBJECT_UNLOCK (sink); break; + case PROP_SLAVE_METHOD: + sink->priv->slave_method = g_value_get_enum (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -387,6 +444,9 @@ gst_base_audio_sink_get_property (GObject * object, guint prop_id, g_value_set_boolean (value, sink->provide_clock); GST_OBJECT_UNLOCK (sink); break; + case PROP_SLAVE_METHOD: + g_value_set_enum (value, sink->priv->slave_method); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -542,6 +602,7 @@ gst_base_audio_sink_event (GstBaseSink * bsink, GstEvent * event) break; case GST_EVENT_FLUSH_STOP: /* always resync on sample after a flush */ + sink->priv->avg_skew = -1; sink->next_sample = -1; if (sink->ringbuffer) gst_ring_buffer_set_flushing (sink->ringbuffer, FALSE); @@ -621,6 +682,163 @@ gst_base_audio_sink_get_offset (GstBaseAudioSink * sink) 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 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; gint bps; gint accum; - GstClockTime crate_num; - GstClockTime crate_denom; gint out_samples; - GstClockTime base_time, cinternal, cexternal, latency; + GstClockTime base_time, latency; GstClock *clock; gboolean sync, slaved; @@ -751,64 +967,21 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf) GST_DEBUG_OBJECT (sink, "compensating for latency %" GST_TIME_FORMAT, GST_TIME_ARGS (latency)); - slaved = clock != sink->provided_clock; - if (slaved) { - /* 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); - - 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; - } + /* add latency to get the timestamp to sync against the pipeline clock */ + render_start += latency; + render_stop += latency; - if (render_stop >= cexternal) { - render_stop = - gst_util_uint64_scale (render_stop - cexternal, crate_denom, - 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, + "after latency: start %" GST_TIME_FORMAT " - stop %" GST_TIME_FORMAT, + GST_TIME_ARGS (render_start), GST_TIME_ARGS (render_stop)); - GST_DEBUG_OBJECT (sink, - "after slaving: start %" GST_TIME_FORMAT " - stop %" GST_TIME_FORMAT, - GST_TIME_ARGS (render_start), GST_TIME_ARGS (render_stop)); - } else { - render_start += latency; - 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)); + slaved = clock != sink->provided_clock; + if (slaved) { + /* handle clock slaving */ + gst_base_audio_sink_handle_slaving (sink, render_start, render_stop, + &render_start, &render_stop); } - /* and bring the time to the rate corrected offset in the buffer */ render_start = gst_util_uint64_scale_int (render_start, ringbuf->spec.rate, GST_SECOND); @@ -827,6 +1000,8 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf) goto no_align; } + /* positive playback rate, first sample is render_start, negative rate, first + * sample is render_stop */ if (bsink->segment.rate >= 1.0) sample_offset = render_start; else @@ -864,8 +1039,8 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf) /* apply alignment */ render_start += align; - /* only align stop if we are not slaved */ - if (slaved) { + /* only align stop if we are not slaved to resample */ + if (slaved && sink->priv->slave_method == GST_BASE_AUDIO_SINK_SLAVE_RESAMPLE) { GST_DEBUG_OBJECT (sink, "no stop time align needed: we are slaved"); goto no_align; } @@ -876,15 +1051,15 @@ no_align: out_samples = render_stop - render_start; 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 */ if (bsink->segment.rate >= 1.0) sample_offset = render_start; else 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 */ accum = 0; do { @@ -1038,8 +1213,9 @@ static GstStateChangeReturn gst_base_audio_sink_async_play (GstBaseSink * basesink) { GstClock *clock; - GstClockTime itime, etime; GstBaseAudioSink *sink; + GstClockTime itime, etime; + GstClockTime rate_num, rate_denom; sink = GST_BASE_AUDIO_SINK (basesink); @@ -1048,34 +1224,43 @@ gst_base_audio_sink_async_play (GstBaseSink * basesink) clock = GST_ELEMENT_CLOCK (sink); 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 * calibration */ - if (clock != sink->provided_clock) { - GstClockTime rate_num, rate_denom; - - etime = gst_clock_get_time (clock); - itime = gst_clock_get_internal_time (sink->provided_clock); + /* get external and internal time to set as calibration params */ + etime = gst_clock_get_time (clock); + itime = gst_clock_get_internal_time (sink->provided_clock); - GST_DEBUG_OBJECT (sink, - "internal time: %" GST_TIME_FORMAT " external time: %" GST_TIME_FORMAT, - GST_TIME_ARGS (itime), GST_TIME_ARGS (etime)); + sink->priv->avg_skew = -1; - /* FIXME, this is not yet accurate enough for smooth playback */ - gst_clock_get_calibration (sink->provided_clock, NULL, NULL, &rate_num, - &rate_denom); - /* Does not work yet. */ - gst_clock_set_calibration (sink->provided_clock, itime, etime, - rate_num, rate_denom); + GST_DEBUG_OBJECT (sink, + "internal time: %" GST_TIME_FORMAT " external time: %" GST_TIME_FORMAT, + GST_TIME_ARGS (itime), GST_TIME_ARGS (etime)); + + gst_clock_get_calibration (sink->provided_clock, NULL, NULL, &rate_num, + &rate_denom); + gst_clock_set_calibration (sink->provided_clock, itime, etime, + rate_num, rate_denom); + + switch (sink->priv->slave_method) { + case GST_BASE_AUDIO_SINK_SLAVE_RESAMPLE: + /* only set as master if we need to resample */ + GST_DEBUG_OBJECT (sink, "Setting clock as master"); + gst_clock_set_master (sink->provided_clock, clock); + break; + default: + break; + } - gst_clock_set_master (sink->provided_clock, clock); + /* start ringbuffer so we can start slaving right away when we need to */ + gst_ring_buffer_start (sink->ringbuffer); - /* start ringbuffer so we can start slaving right away */ - gst_ring_buffer_start (sink->ringbuffer); - } -no_clock: +done: return GST_STATE_CHANGE_SUCCESS; } diff --git a/gst-libs/gst/audio/gstbaseaudiosink.h b/gst-libs/gst/audio/gstbaseaudiosink.h index 25513e6..492322b 100644 --- a/gst-libs/gst/audio/gstbaseaudiosink.h +++ b/gst-libs/gst/audio/gstbaseaudiosink.h @@ -78,8 +78,23 @@ G_BEGIN_DECLS */ #define GST_BASE_AUDIO_SINK_PAD(obj) (GST_BASE_SINK (obj)->sinkpad) +/** + * GstBaseAudioSinkSlaveMethod: + * @GST_BASE_AUDIO_SINK_SLAVE_RESAMPLE: Resample to match the master clock + * @GST_BASE_AUDIO_SINK_SLAVE_SKEW: Adjust playout pointer when master clock + * drifts too much. + * + * Different possible clock slaving algorithms + */ +typedef enum +{ + GST_BASE_AUDIO_SINK_SLAVE_RESAMPLE, + GST_BASE_AUDIO_SINK_SLAVE_SKEW, +} GstBaseAudioSinkSlaveMethod; + typedef struct _GstBaseAudioSink GstBaseAudioSink; typedef struct _GstBaseAudioSinkClass GstBaseAudioSinkClass; +typedef struct _GstBaseAudioSinkPrivate GstBaseAudioSinkPrivate; /** * GstBaseAudioSink: @@ -105,10 +120,9 @@ struct _GstBaseAudioSink { GstClock *provided_clock; /*< private >*/ - union { - /* adding + 0 to mark ABI change to be undone later */ - gpointer _gst_reserved[GST_PADDING + 0]; - } abidata; + GstBaseAudioSinkPrivate *priv; + + gpointer _gst_reserved[GST_PADDING - 1]; }; /** -- 2.7.4