mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-06-07 16:08:51 +00:00
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.
This commit is contained in:
parent
293a9c09b8
commit
450030ebaf
3 changed files with 310 additions and 92 deletions
19
ChangeLog
19
ChangeLog
|
@ -1,3 +1,22 @@
|
||||||
|
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 @@
|
||||||
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
|
||||||
#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
|
||||||
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)
|
||||||
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)
|
||||||
"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
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
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,
|
||||||
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,
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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));
|
||||||
|
|
||||||
|
/* add latency to get the timestamp to sync against the pipeline clock */
|
||||||
|
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;
|
slaved = clock != sink->provided_clock;
|
||||||
if (slaved) {
|
if (slaved) {
|
||||||
/* get calibration parameters to compensate for speed and offset differences
|
/* handle clock slaving */
|
||||||
* when we are slaved */
|
gst_base_audio_sink_handle_slaving (sink, render_start, render_stop,
|
||||||
gst_clock_get_calibration (sink->provided_clock, &cinternal, &cexternal,
|
&render_start, &render_stop);
|
||||||
&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) {
|
|
||||||
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 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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* 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)
|
||||||
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)
|
||||||
/* 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:
|
||||||
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
|
||||||
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)
|
||||||
|
|
||||||
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);
|
sink->priv->avg_skew = -1;
|
||||||
itime = gst_clock_get_internal_time (sink->provided_clock);
|
|
||||||
|
|
||||||
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));
|
||||||
|
|
||||||
/* FIXME, this is not yet accurate enough for smooth playback */
|
gst_clock_get_calibration (sink->provided_clock, NULL, NULL, &rate_num,
|
||||||
gst_clock_get_calibration (sink->provided_clock, NULL, NULL, &rate_num,
|
&rate_denom);
|
||||||
&rate_denom);
|
gst_clock_set_calibration (sink->provided_clock, itime, etime,
|
||||||
/* Does not work yet. */
|
rate_num, rate_denom);
|
||||||
gst_clock_set_calibration (sink->provided_clock, itime, etime,
|
|
||||||
rate_num, rate_denom);
|
|
||||||
|
|
||||||
gst_clock_set_master (sink->provided_clock, clock);
|
switch (sink->priv->slave_method) {
|
||||||
|
case GST_BASE_AUDIO_SINK_SLAVE_RESAMPLE:
|
||||||
/* start ringbuffer so we can start slaving right away */
|
/* only set as master if we need to resample */
|
||||||
gst_ring_buffer_start (sink->ringbuffer);
|
GST_DEBUG_OBJECT (sink, "Setting clock as master");
|
||||||
|
gst_clock_set_master (sink->provided_clock, clock);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
no_clock:
|
|
||||||
|
/* start ringbuffer so we can start slaving right away when we need to */
|
||||||
|
gst_ring_buffer_start (sink->ringbuffer);
|
||||||
|
|
||||||
|
done:
|
||||||
return GST_STATE_CHANGE_SUCCESS;
|
return GST_STATE_CHANGE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,8 +78,23 @@ G_BEGIN_DECLS
|
||||||
*/
|
*/
|
||||||
#define GST_BASE_AUDIO_SINK_PAD(obj) (GST_BASE_SINK (obj)->sinkpad)
|
#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 _GstBaseAudioSink GstBaseAudioSink;
|
||||||
typedef struct _GstBaseAudioSinkClass GstBaseAudioSinkClass;
|
typedef struct _GstBaseAudioSinkClass GstBaseAudioSinkClass;
|
||||||
|
typedef struct _GstBaseAudioSinkPrivate GstBaseAudioSinkPrivate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GstBaseAudioSink:
|
* GstBaseAudioSink:
|
||||||
|
@ -105,10 +120,9 @@ struct _GstBaseAudioSink {
|
||||||
GstClock *provided_clock;
|
GstClock *provided_clock;
|
||||||
|
|
||||||
/*< private >*/
|
/*< private >*/
|
||||||
union {
|
GstBaseAudioSinkPrivate *priv;
|
||||||
/* adding + 0 to mark ABI change to be undone later */
|
|
||||||
gpointer _gst_reserved[GST_PADDING + 0];
|
gpointer _gst_reserved[GST_PADDING - 1];
|
||||||
} abidata;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue