--- a/gst_plugins_base/gst-libs/gst/audio/gstbaseaudiosink.c Tue Aug 31 15:30:33 2010 +0300
+++ b/gst_plugins_base/gst-libs/gst/audio/gstbaseaudiosink.c Wed Sep 01 12:16:41 2010 +0100
@@ -36,6 +36,10 @@
#include "gstbaseaudiosink.h"
+#ifdef __SYMBIAN32__
+#include <glib_global.h>
+#endif
+
GST_DEBUG_CATEGORY_STATIC (gst_base_audio_sink_debug);
#define GST_CAT_DEFAULT gst_base_audio_sink_debug
@@ -52,10 +56,6 @@
GstClockTimeDiff avg_skew;
/* the number of samples we aligned last time */
gint64 last_align;
-
- gboolean sync_latency;
-
- GstClockTime eos_time;
};
/* BaseAudioSink signals and args */
@@ -80,25 +80,19 @@
#define DEFAULT_PROVIDE_CLOCK TRUE
#define DEFAULT_SLAVE_METHOD GST_BASE_AUDIO_SINK_SLAVE_SKEW
-/* FIXME, enable pull mode when clock slaving and trick modes are figured out */
-#define DEFAULT_CAN_ACTIVATE_PULL FALSE
-
enum
{
PROP_0,
PROP_BUFFER_TIME,
PROP_LATENCY_TIME,
PROP_PROVIDE_CLOCK,
- PROP_SLAVE_METHOD,
- PROP_CAN_ACTIVATE_PULL
+ PROP_SLAVE_METHOD
};
-#ifdef __SYMBIAN32__
-EXPORT_C
-#endif
+
+#define GST_TYPE_SLAVE_METHOD (slave_method_get_type ())
-
-GType
-gst_base_audio_sink_slave_method_get_type (void)
+static GType
+slave_method_get_type (void)
{
static GType slave_method_type = 0;
static const GEnumValue slave_method[] = {
@@ -156,9 +150,6 @@
GstCaps * caps);
static void gst_base_audio_sink_fixate (GstBaseSink * bsink, GstCaps * caps);
-static gboolean gst_base_audio_sink_query_pad (GstPad * pad, GstQuery * query);
-
-
/* static guint gst_base_audio_sink_signals[LAST_SIGNAL] = { 0 }; */
static void
@@ -188,30 +179,22 @@
g_object_class_install_property (gobject_class, PROP_BUFFER_TIME,
g_param_spec_int64 ("buffer-time", "Buffer Time",
"Size of audio buffer in microseconds", 1,
- G_MAXINT64, DEFAULT_BUFFER_TIME,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ G_MAXINT64, DEFAULT_BUFFER_TIME, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_LATENCY_TIME,
g_param_spec_int64 ("latency-time", "Latency Time",
"Audio latency in microseconds", 1,
- G_MAXINT64, DEFAULT_LATENCY_TIME,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ G_MAXINT64, DEFAULT_LATENCY_TIME, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_PROVIDE_CLOCK,
g_param_spec_boolean ("provide-clock", "Provide Clock",
"Provide a clock to be used as the global pipeline clock",
- DEFAULT_PROVIDE_CLOCK, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ 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_BASE_AUDIO_SINK_SLAVE_METHOD, DEFAULT_SLAVE_METHOD,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- g_object_class_install_property (gobject_class, PROP_CAN_ACTIVATE_PULL,
- g_param_spec_boolean ("can-activate-pull", "Allow Pull Scheduling",
- "Allow pull-based scheduling", DEFAULT_CAN_ACTIVATE_PULL,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ GST_TYPE_SLAVE_METHOD, DEFAULT_SLAVE_METHOD, G_PARAM_READWRITE));
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_base_audio_sink_change_state);
@@ -234,7 +217,6 @@
/* ref class from a thread-safe context to work around missing bit of
* thread-safety in GObject */
g_type_class_ref (GST_TYPE_AUDIO_CLOCK);
- g_type_class_ref (GST_TYPE_RING_BUFFER);
}
static void
@@ -252,11 +234,9 @@
(GstAudioClockGetTimeFunc) gst_base_audio_sink_get_time, baseaudiosink);
GST_BASE_SINK (baseaudiosink)->can_activate_push = TRUE;
- GST_BASE_SINK (baseaudiosink)->can_activate_pull = DEFAULT_CAN_ACTIVATE_PULL;
-
- /* install some custom pad_query functions */
- gst_pad_set_query_function (GST_BASE_SINK_PAD (baseaudiosink),
- GST_DEBUG_FUNCPTR (gst_base_audio_sink_query_pad));
+ /* FIXME, enable pull mode when segments, latency, state changes, negotiation
+ * and clock slaving are figured out */
+ GST_BASE_SINK (baseaudiosink)->can_activate_pull = FALSE;
}
static void
@@ -278,7 +258,6 @@
G_OBJECT_CLASS (parent_class)->dispose (object);
}
-
static GstClock *
gst_base_audio_sink_provide_clock (GstElement * elem)
{
@@ -318,47 +297,11 @@
}
static gboolean
-gst_base_audio_sink_query_pad (GstPad * pad, GstQuery * query)
-{
- gboolean res = FALSE;
- GstBaseAudioSink *basesink;
-
- basesink = GST_BASE_AUDIO_SINK (gst_pad_get_parent (pad));
-
- switch (GST_QUERY_TYPE (query)) {
- case GST_QUERY_CONVERT:
- {
- GstFormat src_fmt, dest_fmt;
- gint64 src_val, dest_val;
-
- GST_LOG_OBJECT (pad, "query convert");
-
- if (basesink->ringbuffer) {
- gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, NULL);
- res = gst_ring_buffer_convert (basesink->ringbuffer, src_fmt, src_val,
- dest_fmt, &dest_val);
- if (res) {
- gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
- }
- }
- break;
- }
- default:
- break;
- }
-
- gst_object_unref (basesink);
-
- return res;
-}
-
-static gboolean
gst_base_audio_sink_query (GstElement * element, GstQuery * query)
{
gboolean res = FALSE;
- GstBaseAudioSink *basesink;
- basesink = GST_BASE_AUDIO_SINK (element);
+ GstBaseAudioSink *basesink = GST_BASE_AUDIO_SINK (element);
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_LATENCY:
@@ -390,7 +333,7 @@
basesink->priv->us_latency = min_l;
min_latency =
- gst_util_uint64_scale_int (spec->seglatency * spec->segsize,
+ 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 and the min peer latency */
@@ -406,30 +349,13 @@
} else {
GST_DEBUG_OBJECT (basesink,
"peer or we are not live, don't care about latency");
- min_latency = min_l;
- max_latency = max_l;
+ min_latency = 0;
+ max_latency = -1;
}
gst_query_set_latency (query, live, min_latency, max_latency);
}
break;
}
- case GST_QUERY_CONVERT:
- {
- GstFormat src_fmt, dest_fmt;
- gint64 src_val, dest_val;
-
- GST_LOG_OBJECT (basesink, "query convert");
-
- if (basesink->ringbuffer) {
- gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, NULL);
- res = gst_ring_buffer_convert (basesink->ringbuffer, src_fmt, src_val,
- dest_fmt, &dest_val);
- if (res) {
- gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
- }
- }
- break;
- }
default:
res = GST_ELEMENT_CLASS (parent_class)->query (element, query);
break;
@@ -445,7 +371,7 @@
{
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;
@@ -465,10 +391,15 @@
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 %" G_GUINT64_FORMAT ", delay %u, real %"
- G_GUINT64_FORMAT ", time %" GST_TIME_FORMAT,
- raw, delay, samples, GST_TIME_ARGS (result));
+ "processed samples: raw %llu, delay %u, real %llu, time %"
+ GST_TIME_FORMAT ", upstream latency %" GST_TIME_FORMAT, raw, delay,
+ samples, GST_TIME_ARGS (result), GST_TIME_ARGS (us_latency));
return result;
}
@@ -601,9 +532,6 @@
case PROP_SLAVE_METHOD:
gst_base_audio_sink_set_slave_method (sink, g_value_get_enum (value));
break;
- case PROP_CAN_ACTIVATE_PULL:
- GST_BASE_SINK (sink)->can_activate_pull = g_value_get_boolean (value);
- break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -631,9 +559,6 @@
case PROP_SLAVE_METHOD:
g_value_set_enum (value, gst_base_audio_sink_get_slave_method (sink));
break;
- case PROP_CAN_ACTIVATE_PULL:
- g_value_set_boolean (value, GST_BASE_SINK (sink)->can_activate_pull);
- break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -654,8 +579,6 @@
GST_DEBUG_OBJECT (sink, "release old ringbuffer");
/* release old ringbuffer */
- gst_ring_buffer_pause (sink->ringbuffer);
- gst_ring_buffer_activate (sink->ringbuffer, FALSE);
gst_ring_buffer_release (sink->ringbuffer);
GST_DEBUG_OBJECT (sink, "parse caps");
@@ -669,15 +592,11 @@
gst_ring_buffer_debug_spec_buff (spec);
- GST_DEBUG_OBJECT (sink, "acquire ringbuffer");
+ GST_DEBUG_OBJECT (sink, "acquire new ringbuffer");
+
if (!gst_ring_buffer_acquire (sink->ringbuffer, spec))
goto acquire_error;
- if (bsink->pad_mode == GST_ACTIVATE_PUSH) {
- GST_DEBUG_OBJECT (sink, "activate ringbuffer");
- gst_ring_buffer_activate (sink->ringbuffer, TRUE);
- }
-
/* calculate actual latency and buffer times.
* FIXME: In 0.11, store the latency_time internally in ns */
spec->latency_time = gst_util_uint64_scale (spec->segsize,
@@ -744,6 +663,8 @@
static gboolean
gst_base_audio_sink_drain (GstBaseAudioSink * sink)
{
+ GstClockTime base_time;
+
if (!sink->ringbuffer)
return TRUE;
if (!sink->ringbuffer->spec.rate)
@@ -755,16 +676,35 @@
if (gst_ring_buffer_is_acquired (sink->ringbuffer))
gst_ring_buffer_start (sink->ringbuffer);
- if (sink->priv->eos_time != -1) {
+ if (sink->next_sample != -1) {
+ GstClockTime time;
+
+ /* convert next expected sample to time */
+ time =
+ gst_util_uint64_scale_int (sink->next_sample, GST_SECOND,
+ sink->ringbuffer->spec.rate);
+
GST_DEBUG_OBJECT (sink,
- "last sample time %" GST_TIME_FORMAT,
- GST_TIME_ARGS (sink->priv->eos_time));
+ "last sample %" G_GUINT64_FORMAT ", time %" GST_TIME_FORMAT,
+ sink->next_sample, GST_TIME_ARGS (time));
+
+ /* our time already includes the base_time, _wait_eos() wants a running_time
+ * so we have to subtract the base_time again here. FIXME, store an
+ * unadjusted EOS time so that we don't have to do this. */
+ GST_OBJECT_LOCK (sink);
+ base_time = GST_ELEMENT_CAST (sink)->base_time;
+ GST_OBJECT_UNLOCK (sink);
+
+ if (time > base_time)
+ time -= base_time;
+ else
+ time = 0;
/* wait for the EOS time to be reached, this is the time when the last
* sample is played. */
- gst_base_sink_wait_eos (GST_BASE_SINK (sink), sink->priv->eos_time, NULL);
+ gst_base_sink_wait_eos (GST_BASE_SINK (sink), time, NULL);
- GST_DEBUG_OBJECT (sink, "drained audio");
+ sink->next_sample = -1;
}
return TRUE;
}
@@ -783,7 +723,6 @@
/* always resync on sample after a flush */
sink->priv->avg_skew = -1;
sink->next_sample = -1;
- sink->priv->eos_time = -1;
if (sink->ringbuffer)
gst_ring_buffer_set_flushing (sink->ringbuffer, FALSE);
break;
@@ -864,7 +803,8 @@
static GstClockTime
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 */
if (external >= cexternal) {
@@ -872,13 +812,19 @@
gst_util_uint64_scale (external - cexternal, crate_denom, crate_num);
external += cinternal;
} else {
- external =
- gst_util_uint64_scale (cexternal - external, crate_denom, crate_num);
+ 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;
}
@@ -892,22 +838,6 @@
GstClockTime cinternal, cexternal;
GstClockTime crate_num, crate_denom;
- /* FIXME, we can sample and add observations here or use the timeouts on the
- * clock. No idea which one is better or more stable. The timeout seems more
- * arbitrary but this one seems more demanding and does not work when there is
- * no data comming in to the sink. */
-#if 0
- GstClockTime etime, itime;
- gdouble r_squared;
-
- /* sample clocks and figure out clock skew */
- etime = gst_clock_get_time (GST_ELEMENT_CLOCK (sink));
- itime = gst_audio_clock_get_time (sink->provided_clock);
-
- /* add new observation */
- gst_clock_add_observation (sink->provided_clock, itime, etime, &r_squared);
-#endif
-
/* get calibration parameters to compensate for speed and offset differences
* when we are slaved */
gst_clock_get_calibration (sink->provided_clock, &cinternal, &cexternal,
@@ -924,9 +854,9 @@
/* bring external time to internal time */
render_start = clock_convert_external (render_start, cinternal, cexternal,
- crate_num, crate_denom);
+ crate_num, crate_denom, sink->priv->us_latency);
render_stop = clock_convert_external (render_stop, cinternal, cexternal,
- crate_num, crate_denom);
+ crate_num, crate_denom, sink->priv->us_latency);
GST_DEBUG_OBJECT (sink,
"after slaving: start %" GST_TIME_FORMAT " - stop %" GST_TIME_FORMAT,
@@ -955,21 +885,11 @@
/* sample clocks and figure out clock skew */
etime = gst_clock_get_time (GST_ELEMENT_CLOCK (sink));
- itime = gst_audio_clock_get_time (sink->provided_clock);
-
- GST_DEBUG_OBJECT (sink,
- "internal %" GST_TIME_FORMAT " external %" GST_TIME_FORMAT
- " cinternal %" GST_TIME_FORMAT " cexternal %" GST_TIME_FORMAT,
- GST_TIME_ARGS (itime), GST_TIME_ARGS (etime),
- GST_TIME_ARGS (cinternal), GST_TIME_ARGS (cexternal));
+ itime = gst_clock_get_internal_time (sink->provided_clock);
- /* make sure we never go below 0 */
- etime = etime > cexternal ? etime - cexternal : 0;
- itime = itime > cinternal ? itime - cinternal : 0;
+ etime -= cexternal;
+ itime -= cinternal;
- /* do itime - etime.
- * positive value means external clock goes slower
- * negative value means external clock goes faster */
skew = GST_CLOCK_DIFF (etime, itime);
if (sink->priv->avg_skew == -1) {
/* first observation */
@@ -993,7 +913,7 @@
GST_WARNING_OBJECT (sink,
"correct clock skew %" G_GINT64_FORMAT " > %" G_GINT64_FORMAT,
sink->priv->avg_skew, segtime2);
- cexternal = cexternal > segtime ? cexternal - segtime : 0;
+ cinternal += segtime;
sink->priv->avg_skew -= segtime;
segsamples =
@@ -1040,9 +960,9 @@
/* convert, ignoring speed */
render_start = clock_convert_external (render_start, cinternal, cexternal,
- crate_num, crate_denom);
+ crate_num, crate_denom, sink->priv->us_latency);
render_stop = clock_convert_external (render_stop, cinternal, cexternal,
- crate_num, crate_denom);
+ crate_num, crate_denom, sink->priv->us_latency);
*srender_start = render_start;
*srender_stop = render_stop;
@@ -1062,9 +982,9 @@
/* convert, ignoring speed */
render_start = clock_convert_external (render_start, cinternal, cexternal,
- crate_num, crate_denom);
+ crate_num, crate_denom, sink->priv->us_latency);
render_stop = clock_convert_external (render_stop, cinternal, cexternal,
- crate_num, crate_denom);
+ crate_num, crate_denom, sink->priv->us_latency);
*srender_start = render_start;
*srender_stop = render_stop;
@@ -1095,137 +1015,11 @@
}
}
-/* must be called with LOCK */
-static GstFlowReturn
-gst_base_audio_sink_sync_latency (GstBaseSink * bsink, GstMiniObject * obj)
-{
- GstClock *clock;
- GstClockReturn status;
- GstClockTime time;
- GstFlowReturn ret;
- GstBaseAudioSink *sink;
- GstClockTime itime, etime;
- GstClockTime rate_num, rate_denom;
- GstClockTimeDiff jitter;
-
- sink = GST_BASE_AUDIO_SINK (bsink);
-
- clock = GST_ELEMENT_CLOCK (sink);
- if (G_UNLIKELY (clock == NULL))
- goto no_clock;
-
- /* we provided the global clock, don't need to do anything special */
- if (clock == sink->provided_clock)
- goto no_slaving;
-
- GST_OBJECT_UNLOCK (sink);
-
- do {
- GST_DEBUG_OBJECT (sink, "checking preroll");
-
- ret = gst_base_sink_do_preroll (bsink, obj);
- if (ret != GST_FLOW_OK)
- goto flushing;
-
- GST_OBJECT_LOCK (sink);
- time = sink->priv->us_latency;
- GST_OBJECT_UNLOCK (sink);
-
- /* preroll done, we can sync since we are in PLAYING now. */
- GST_DEBUG_OBJECT (sink, "possibly waiting for clock to reach %"
- GST_TIME_FORMAT, GST_TIME_ARGS (time));
-
- /* wait for the clock, this can be interrupted because we got shut down or
- * we PAUSED. */
- status = gst_base_sink_wait_clock (bsink, time, &jitter);
-
- GST_DEBUG_OBJECT (sink, "clock returned %d %" GST_TIME_FORMAT, status,
- GST_TIME_ARGS (jitter));
-
- /* invalid time, no clock or sync disabled, just continue then */
- if (status == GST_CLOCK_BADTIME)
- break;
-
- /* waiting could have been interrupted and we can be flushing now */
- if (G_UNLIKELY (bsink->flushing))
- goto flushing;
-
- /* retry if we got unscheduled, which means we did not reach the timeout
- * yet. if some other error occures, we continue. */
- } while (status == GST_CLOCK_UNSCHEDULED);
-
- GST_OBJECT_LOCK (sink);
- GST_DEBUG_OBJECT (sink, "latency synced");
-
- /* when we prerolled in time, we can accurately set the calibration,
- * our internal clock should exactly have been the latency (== the running
- * time of the external clock) */
- etime = GST_ELEMENT_CAST (sink)->base_time + time;
- itime = gst_audio_clock_get_time (sink->provided_clock);
-
- if (status == GST_CLOCK_EARLY) {
- /* when we prerolled late, we have to take into account the lateness */
- GST_DEBUG_OBJECT (sink, "late preroll, adding jitter");
- etime += jitter;
- }
-
- /* start ringbuffer so we can start slaving right away when we need to */
- gst_ring_buffer_start (sink->ringbuffer);
-
- GST_DEBUG_OBJECT (sink,
- "internal time: %" GST_TIME_FORMAT " external time: %" GST_TIME_FORMAT,
- GST_TIME_ARGS (itime), GST_TIME_ARGS (etime));
-
- /* copy the original calibrated rate but update the internal and external
- * times. */
- 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 when we are resampling */
- GST_DEBUG_OBJECT (sink, "Setting clock as master");
- gst_clock_set_master (sink->provided_clock, clock);
- break;
- case GST_BASE_AUDIO_SINK_SLAVE_SKEW:
- case GST_BASE_AUDIO_SINK_SLAVE_NONE:
- default:
- break;
- }
-
- sink->priv->avg_skew = -1;
- sink->next_sample = -1;
- sink->priv->eos_time = -1;
-
- return GST_FLOW_OK;
-
- /* ERRORS */
-no_clock:
- {
- GST_DEBUG_OBJECT (sink, "we have no clock");
- return GST_FLOW_OK;
- }
-no_slaving:
- {
- GST_DEBUG_OBJECT (sink, "we are not slaved");
- return GST_FLOW_OK;
- }
-flushing:
- {
- GST_DEBUG_OBJECT (sink, "we are flushing");
- GST_OBJECT_LOCK (sink);
- return GST_FLOW_WRONG_STATE;
- }
-}
-
static GstFlowReturn
gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf)
{
guint64 in_offset;
GstClockTime time, stop, render_start, render_stop, sample_offset;
- GstClockTimeDiff sync_offset, ts_offset;
GstBaseAudioSink *sink;
GstRingBuffer *ringbuf;
gint64 diff, align, ctime, cstop;
@@ -1235,11 +1029,9 @@
gint bps;
gint accum;
gint out_samples;
- GstClockTime base_time, render_delay, latency;
+ GstClockTime base_time = GST_CLOCK_TIME_NONE, latency;
GstClock *clock;
gboolean sync, slaved, align_next;
- GstFlowReturn ret;
- GstSegment clip_seg;
sink = GST_BASE_AUDIO_SINK (bsink);
@@ -1249,22 +1041,6 @@
if (G_UNLIKELY (!gst_ring_buffer_is_acquired (ringbuf)))
goto wrong_state;
- /* Wait for upstream latency before starting the ringbuffer, we do this so
- * that we can align the first sample of the ringbuffer to the base_time +
- * latency. */
- GST_OBJECT_LOCK (sink);
- base_time = GST_ELEMENT_CAST (sink)->base_time;
- if (G_UNLIKELY (sink->priv->sync_latency)) {
- ret = gst_base_audio_sink_sync_latency (bsink, GST_MINI_OBJECT_CAST (buf));
- GST_OBJECT_UNLOCK (sink);
- if (G_UNLIKELY (ret != GST_FLOW_OK))
- goto sync_latency_failed;
- /* only do this once until we are set back to PLAYING */
- sink->priv->sync_latency = FALSE;
- } else {
- GST_OBJECT_UNLOCK (sink);
- }
-
bps = ringbuf->spec.bytes_per_sample;
size = GST_BUFFER_SIZE (buf);
@@ -1276,10 +1052,12 @@
in_offset = GST_BUFFER_OFFSET (buf);
time = GST_BUFFER_TIMESTAMP (buf);
+ stop = time + gst_util_uint64_scale_int (samples, GST_SECOND,
+ ringbuf->spec.rate);
GST_DEBUG_OBJECT (sink,
- "time %" GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT ", start %"
- GST_TIME_FORMAT ", samples %u", GST_TIME_ARGS (time), in_offset,
+ "time %" GST_TIME_FORMAT ", offset %llu, start %" GST_TIME_FORMAT
+ ", samples %u", GST_TIME_ARGS (time), in_offset,
GST_TIME_ARGS (bsink->segment.start), samples);
data = GST_BUFFER_DATA (buf);
@@ -1292,48 +1070,16 @@
GST_DEBUG_OBJECT (sink,
"Buffer of size %u has no time. Using render_start=%" G_GUINT64_FORMAT,
GST_BUFFER_SIZE (buf), render_start);
- /* we don't have a start so we don't know stop either */
- stop = -1;
goto no_sync;
}
- /* let's calc stop based on the number of samples in the buffer instead
- * of trusting the DURATION */
- stop = time + gst_util_uint64_scale_int (samples, GST_SECOND,
- ringbuf->spec.rate);
-
- /* prepare the clipping segment. Since we will be subtracting ts-offset and
- * device-delay later we scale the start and stop with those values so that we
- * can correctly clip them */
- clip_seg.format = GST_FORMAT_TIME;
- clip_seg.start = bsink->segment.start;
- clip_seg.stop = bsink->segment.stop;
- clip_seg.duration = -1;
-
- /* the sync offset is the combination of ts-offset and device-delay */
- latency = gst_base_sink_get_latency (bsink);
- ts_offset = gst_base_sink_get_ts_offset (bsink);
- render_delay = gst_base_sink_get_render_delay (bsink);
- sync_offset = ts_offset - render_delay + latency;
-
- GST_DEBUG_OBJECT (sink,
- "sync-offset %" G_GINT64_FORMAT ", render-delay %" GST_TIME_FORMAT
- ", ts-offset %" G_GINT64_FORMAT, sync_offset,
- GST_TIME_ARGS (render_delay), ts_offset);
-
- /* compensate for ts-offset and device-delay when negative we need to
- * clip. */
- if (sync_offset < 0) {
- clip_seg.start += -sync_offset;
- if (clip_seg.stop != -1)
- clip_seg.stop += -sync_offset;
- }
-
/* samples should be rendered based on their timestamp. All samples
* arriving before the segment.start or after segment.stop are to be
* thrown away. All samples should also be clipped to the segment
* boundaries */
- if (!gst_segment_clip (&clip_seg, GST_FORMAT_TIME, time, stop, &ctime,
+ /* let's calc stop based on the number of samples in the buffer instead
+ * of trusting the DURATION */
+ if (!gst_segment_clip (&bsink->segment, GST_FORMAT_TIME, time, stop, &ctime,
&cstop))
goto out_of_segment;
@@ -1383,42 +1129,32 @@
"running: start %" GST_TIME_FORMAT " - stop %" GST_TIME_FORMAT,
GST_TIME_ARGS (render_start), GST_TIME_ARGS (render_stop));
- /* store the time of the last sample, we'll use this to perform sync on the
- * last sample when draining the buffer */
- if (bsink->segment.rate >= 0.0) {
- sink->priv->eos_time = render_stop;
- } else {
- sink->priv->eos_time = render_start;
- }
+ base_time = gst_element_get_base_time (GST_ELEMENT_CAST (bsink));
- /* compensate for ts-offset and delay we know this will not underflow because we
- * clipped above. */
- GST_DEBUG_OBJECT (sink,
- "compensating for sync-offset %" GST_TIME_FORMAT,
- GST_TIME_ARGS (sync_offset));
- render_start += sync_offset;
- render_stop += sync_offset;
-
- GST_DEBUG_OBJECT (sink, "adding base_time %" GST_TIME_FORMAT,
+ GST_DEBUG_OBJECT (sink, "base_time %" GST_TIME_FORMAT,
GST_TIME_ARGS (base_time));
/* add base time to sync against the clock */
render_start += base_time;
render_stop += base_time;
+ /* compensate for latency */
+ latency = gst_base_sink_get_latency (bsink);
GST_DEBUG_OBJECT (sink,
- "after compensation: start %" GST_TIME_FORMAT " - stop %" GST_TIME_FORMAT,
+ "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));
if ((slaved = clock != sink->provided_clock)) {
/* handle clock slaving */
gst_base_audio_sink_handle_slaving (sink, render_start, render_stop,
&render_start, &render_stop);
- } else {
- /* no slaving needed but we need to adapt to the clock calibration
- * parameters */
- gst_base_audio_sink_none_slaving (sink, render_start, render_stop,
- &render_start, &render_stop);
}
/* and bring the time to the rate corrected offset in the buffer */
@@ -1427,34 +1163,26 @@
render_stop = gst_util_uint64_scale_int (render_stop,
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 */
if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT))) {
GST_DEBUG_OBJECT (sink, "resync after discont");
goto no_align;
}
- /* resync when we don't know what to align the sample with */
if (G_UNLIKELY (sink->next_sample == -1)) {
GST_DEBUG_OBJECT (sink,
"no align possible: no previous sample position known");
goto no_align;
}
- /* now try to align the sample to the previous one, first see how big the
- * difference is. */
+ /* 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
+ sample_offset = render_stop;
+
+ /* now try to align the sample to the previous one */
if (sample_offset >= sink->next_sample)
diff = sample_offset - sink->next_sample;
else
@@ -1500,7 +1228,7 @@
no_sync:
/* we render the first or last sample first, depending on the rate */
- if (bsink->segment.rate >= 0.0)
+ if (bsink->segment.rate >= 1.0)
sample_offset = render_start;
else
sample_offset = render_stop;
@@ -1522,21 +1250,13 @@
break;
/* else something interrupted us and we wait for preroll. */
- if ((ret = gst_base_sink_wait_preroll (bsink)) != GST_FLOW_OK)
+ if (gst_base_sink_wait_preroll (bsink) != GST_FLOW_OK)
goto stopping;
/* if we got interrupted, we cannot assume that the next sample should
* be aligned to this one */
align_next = FALSE;
- /* update the output samples. FIXME, this will just skip them when pausing
- * during trick mode */
- if (out_samples > written) {
- out_samples -= written;
- accum = 0;
- } else
- break;
-
samples -= written;
data += written * bps;
} while (TRUE);
@@ -1582,14 +1302,8 @@
}
stopping:
{
- GST_DEBUG_OBJECT (sink, "preroll got interrupted: %d (%s)", ret,
- gst_flow_get_name (ret));
- return ret;
- }
-sync_latency_failed:
- {
- GST_DEBUG_OBJECT (sink, "failed waiting for latency");
- return ret;
+ GST_DEBUG_OBJECT (sink, "ringbuffer is stopping");
+ return GST_FLOW_WRONG_STATE;
}
}
@@ -1623,6 +1337,25 @@
return buffer;
}
+static gboolean
+gst_base_audio_sink_activate_pull (GstBaseSink * basesink, gboolean active)
+{
+ gboolean ret;
+ GstBaseAudioSink *sink = GST_BASE_AUDIO_SINK (basesink);
+
+ if (active) {
+ gst_ring_buffer_set_callback (sink->ringbuffer,
+ gst_base_audio_sink_callback, sink);
+ ret = gst_ring_buffer_start (sink->ringbuffer);
+ } else {
+ gst_ring_buffer_set_callback (sink->ringbuffer, NULL, NULL);
+ /* stop thread */
+ ret = gst_ring_buffer_release (sink->ringbuffer);
+ }
+
+ return ret;
+}
+
static void
gst_base_audio_sink_callback (GstRingBuffer * rbuf, guint8 * data, guint len,
gpointer user_data)
@@ -1635,15 +1368,11 @@
basesink = GST_BASE_SINK (user_data);
sink = GST_BASE_AUDIO_SINK (user_data);
- GST_PAD_STREAM_LOCK (basesink->sinkpad);
-
/* would be nice to arrange for pad_alloc_buffer to return data -- as it is we
will copy twice, once into data, once into DMA */
GST_LOG_OBJECT (basesink, "pulling %d bytes offset %" G_GUINT64_FORMAT
" to fill audio buffer", len, basesink->offset);
- ret =
- gst_pad_pull_range (basesink->sinkpad, basesink->segment.last_stop, len,
- &buf);
+ ret = gst_pad_pull_range (basesink->sinkpad, basesink->offset, len, &buf);
if (ret != GST_FLOW_OK) {
if (ret == GST_FLOW_UNEXPECTED)
@@ -1652,37 +1381,22 @@
goto error;
}
- GST_PAD_PREROLL_LOCK (basesink->sinkpad);
- if (basesink->flushing)
- goto flushing;
-
- /* complete preroll and wait for PLAYING */
- ret = gst_base_sink_do_preroll (basesink, GST_MINI_OBJECT_CAST (buf));
- if (ret != GST_FLOW_OK)
- goto preroll_error;
-
if (len != GST_BUFFER_SIZE (buf)) {
- GST_INFO_OBJECT (basesink,
- "got different size than requested from sink pad: %u != %u", len,
- GST_BUFFER_SIZE (buf));
+ GST_INFO_OBJECT (basesink, "short read pulling from sink pad: %d<%d",
+ len, GST_BUFFER_SIZE (buf));
len = MIN (GST_BUFFER_SIZE (buf), len);
}
- basesink->segment.last_stop += len;
+ basesink->offset += len;
memcpy (data, GST_BUFFER_DATA (buf), len);
- GST_PAD_PREROLL_UNLOCK (basesink->sinkpad);
-
- GST_PAD_STREAM_UNLOCK (basesink->sinkpad);
return;
error:
{
- GST_WARNING_OBJECT (basesink, "Got flow '%s' but can't return it: %d",
- gst_flow_get_name (ret), ret);
- gst_ring_buffer_pause (rbuf);
- GST_PAD_STREAM_UNLOCK (basesink->sinkpad);
+ GST_WARNING_OBJECT (basesink, "Got flow error but can't return it: %d",
+ ret);
return;
}
eos:
@@ -1691,72 +1405,82 @@
* the sink gets shut down; maybe we should set a flag somewhere, or
* set segment.stop and segment.duration to the last sample or so */
GST_DEBUG_OBJECT (sink, "EOS");
- gst_base_audio_sink_drain (sink);
- gst_ring_buffer_pause (rbuf);
gst_element_post_message (GST_ELEMENT_CAST (sink),
gst_message_new_eos (GST_OBJECT_CAST (sink)));
- GST_PAD_STREAM_UNLOCK (basesink->sinkpad);
- }
-flushing:
- {
- GST_DEBUG_OBJECT (sink, "we are flushing");
- gst_ring_buffer_pause (rbuf);
- GST_PAD_PREROLL_UNLOCK (basesink->sinkpad);
- GST_PAD_STREAM_UNLOCK (basesink->sinkpad);
- return;
- }
-preroll_error:
- {
- GST_DEBUG_OBJECT (sink, "error %s", gst_flow_get_name (ret));
- gst_ring_buffer_pause (rbuf);
- GST_PAD_PREROLL_UNLOCK (basesink->sinkpad);
- GST_PAD_STREAM_UNLOCK (basesink->sinkpad);
- return;
+ gst_base_audio_sink_drain (sink);
}
}
-static gboolean
-gst_base_audio_sink_activate_pull (GstBaseSink * basesink, gboolean active)
-{
- gboolean ret;
- GstBaseAudioSink *sink = GST_BASE_AUDIO_SINK (basesink);
-
- if (active) {
- GST_DEBUG_OBJECT (basesink, "activating pull");
-
- gst_ring_buffer_set_callback (sink->ringbuffer,
- gst_base_audio_sink_callback, sink);
-
- ret = gst_ring_buffer_activate (sink->ringbuffer, TRUE);
- } else {
- GST_DEBUG_OBJECT (basesink, "deactivating pull");
- gst_ring_buffer_set_callback (sink->ringbuffer, NULL, NULL);
- ret = gst_ring_buffer_activate (sink->ringbuffer, FALSE);
- }
-
- return ret;
-}
-
/* should be called with the LOCK */
static GstStateChangeReturn
gst_base_audio_sink_async_play (GstBaseSink * basesink)
{
+ GstClock *clock;
GstBaseAudioSink *sink;
+ GstClockTime itime, etime;
+ GstClockTime rate_num, rate_denom;
sink = GST_BASE_AUDIO_SINK (basesink);
GST_DEBUG_OBJECT (sink, "ringbuffer may start now");
- sink->priv->sync_latency = TRUE;
gst_ring_buffer_may_start (sink->ringbuffer, TRUE);
- if (basesink->pad_mode == GST_ACTIVATE_PULL) {
- /* we always start the ringbuffer in pull mode immediatly */
- gst_ring_buffer_start (sink->ringbuffer);
+
+ clock = GST_ELEMENT_CLOCK (sink);
+ if (clock == NULL)
+ goto done;
+
+ /* we provided the global clock, don't need to do anything special */
+ if (clock == sink->provided_clock)
+ goto done;
+
+ /* if we are slaved to a clock, we need to set the initial
+ * calibration */
+ /* 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);
+
+ sink->priv->avg_skew = -1;
+ sink->next_sample = -1;
+
+ 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;
}
+ /* 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;
}
static GstStateChangeReturn
+gst_base_audio_sink_do_play (GstBaseAudioSink * sink)
+{
+ GstStateChangeReturn ret;
+
+ GST_OBJECT_LOCK (sink);
+ ret = gst_base_audio_sink_async_play (GST_BASE_SINK_CAST (sink));
+ GST_OBJECT_UNLOCK (sink);
+
+ return ret;
+}
+
+static GstStateChangeReturn
gst_base_audio_sink_change_state (GstElement * element,
GstStateChange transition)
{
@@ -1766,7 +1490,6 @@
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
if (sink->ringbuffer == NULL) {
- gst_audio_clock_reset (GST_AUDIO_CLOCK (sink->provided_clock), 0);
sink->ringbuffer = gst_base_audio_sink_create_ringbuffer (sink);
}
if (!gst_ring_buffer_open_device (sink->ringbuffer))
@@ -1775,29 +1498,19 @@
case GST_STATE_CHANGE_READY_TO_PAUSED:
sink->next_sample = -1;
sink->priv->last_align = -1;
- sink->priv->eos_time = -1;
gst_ring_buffer_set_flushing (sink->ringbuffer, FALSE);
gst_ring_buffer_may_start (sink->ringbuffer, FALSE);
break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
- GST_OBJECT_LOCK (sink);
- GST_DEBUG_OBJECT (sink, "ringbuffer may start now");
- sink->priv->sync_latency = TRUE;
- GST_OBJECT_UNLOCK (sink);
-
- gst_ring_buffer_may_start (sink->ringbuffer, TRUE);
- if (GST_BASE_SINK_CAST (sink)->pad_mode == GST_ACTIVATE_PULL) {
- /* we always start the ringbuffer in pull mode immediatly */
- gst_ring_buffer_start (sink->ringbuffer);
- }
+ gst_base_audio_sink_do_play (sink);
break;
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+ /* need to take the lock so we don't interfere with an
+ * async play */
+ GST_OBJECT_LOCK (sink);
/* ringbuffer cannot start anymore */
gst_ring_buffer_may_start (sink->ringbuffer, FALSE);
gst_ring_buffer_pause (sink->ringbuffer);
-
- GST_OBJECT_LOCK (sink);
- sink->priv->sync_latency = FALSE;
GST_OBJECT_UNLOCK (sink);
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
@@ -1817,7 +1530,6 @@
gst_clock_set_master (sink->provided_clock, NULL);
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
- gst_ring_buffer_activate (sink->ringbuffer, FALSE);
gst_ring_buffer_release (sink->ringbuffer);
break;
case GST_STATE_CHANGE_READY_TO_NULL:
@@ -1825,13 +1537,8 @@
* caps, which happens before we commit the state to PAUSED and thus the
* PAUSED->READY state change (see above, where we release the ringbuffer)
* might not be called when we get here. */
- gst_ring_buffer_activate (sink->ringbuffer, FALSE);
gst_ring_buffer_release (sink->ringbuffer);
gst_ring_buffer_close_device (sink->ringbuffer);
- GST_OBJECT_LOCK (sink);
- gst_object_unparent (GST_OBJECT_CAST (sink->ringbuffer));
- sink->ringbuffer = NULL;
- GST_OBJECT_UNLOCK (sink);
break;
default:
break;