+2004-06-23 Wim Taymans <wim@fluendo.com>
+
+ * ext/alsa/gstalsa.c: (gst_alsa_change_state), (gst_alsa_start),
+ (gst_alsa_xrun_recovery):
+ * ext/alsa/gstalsa.h:
+ * ext/alsa/gstalsasink.c: (gst_alsa_sink_check_event),
+ (gst_alsa_sink_loop), (gst_alsa_sink_get_time):
+ * ext/alsa/gstalsasrc.c: (gst_alsa_src_init),
+ (gst_alsa_src_get_time), (gst_alsa_src_update_avail),
+ (gst_alsa_src_loop):
+ Add clock to alsasrc. Take new capture timestamp when
+ restarting after an overrun. Split up some functions between
+ alsasrc ans alsasink.
+
2004-06-23 Thomas Vander Stichele <thomas at apestaart dot org>
* ext/alsa/gstalsa.c: (gst_alsa_init), (gst_alsa_dispose),
if (!(GST_FLAG_IS_SET (element, GST_ALSA_RUNNING) ||
gst_alsa_start_audio (this)))
return GST_STATE_FAILURE;
- this->transmitted = 0;
+ this->played = 0;
+ this->captured = 0;
break;
case GST_STATE_PAUSED_TO_PLAYING:
if (snd_pcm_state (this->handle) == SND_PCM_STATE_PAUSED) {
inline gboolean
gst_alsa_start (GstAlsa * this)
{
+ GstClockTime elemnow;
+
GST_DEBUG ("Setting state to RUNNING");
switch (snd_pcm_state (this->handle)) {
ERROR_CHECK (snd_pcm_prepare (this->handle), "error preparing: %s");
case SND_PCM_STATE_SUSPENDED:
case SND_PCM_STATE_PREPARED:
+ /* The strategy to recover the timestamps from the xrun is to take the
+ * current element time and pretend we just sent all the samples up to
+ * that time. This will result in an offset discontinuity in the next
+ * buffer along with the correct timestamp on that buffer, we only
+ * update the capture timestamps */
+ elemnow = gst_element_get_time (GST_ELEMENT (this));
+ this->captured = gst_alsa_timestamp_to_samples (this, elemnow);
ERROR_CHECK (snd_pcm_start (this->handle), "error starting playback: %s");
break;
case SND_PCM_STATE_PAUSED:
GST_ERROR_OBJECT (this, "status error: %s", snd_strerror (err));
if (snd_pcm_status_get_state (status) == SND_PCM_STATE_XRUN) {
- struct timeval now, diff, tstamp;
-
- gettimeofday (&now, 0);
- snd_pcm_status_get_trigger_tstamp (status, &tstamp);
- timersub (&now, &tstamp, &diff);
- GST_INFO_OBJECT (this, "alsa: xrun of at least %.3f msecs",
- diff.tv_sec * 1000 + diff.tv_usec / 1000.0);
/* if we're allowed to recover, ... */
if (this->autorecover) {
this->period_count *= 2;
}
}
- }
- if (!(gst_alsa_stop_audio (this) && gst_alsa_start_audio (this))) {
- GST_ELEMENT_ERROR (this, RESOURCE, FAILED, (NULL),
- ("Error restarting audio after xrun"));
- return FALSE;
+ /* prepare the device again */
+ if ((err = snd_pcm_prepare (this->handle)) < 0) {
+ GST_ERROR_OBJECT (this, "prepare error: %s", snd_strerror (err));
+ return FALSE;
+ }
+ if (!gst_alsa_start (this)) {
+ GST_ELEMENT_ERROR (this, RESOURCE, FAILED, (NULL),
+ ("Error starting audio after xrun"));
+ return FALSE;
+ }
+ GST_DEBUG_OBJECT (this, "XRun!!!! pretending we captured %lld samples",
+ this->captured);
+ } else {
+ if (!(gst_alsa_stop_audio (this) && gst_alsa_start_audio (this))) {
+ GST_ELEMENT_ERROR (this, RESOURCE, FAILED, (NULL),
+ ("Error restarting audio after xrun"));
+ return FALSE;
+ }
}
return TRUE;
/* clocking */
GstAlsaClock * clock; /* our provided clock */
- snd_pcm_uframes_t transmitted; /* samples transmitted since last sync
+ GstClockTime clock_base;
+ snd_pcm_uframes_t played; /* samples transmitted since last sync
This thing actually is our master clock.
We will event insert silent samples or
drop some to sync to incoming timestamps.
*/
+ snd_pcm_uframes_t captured;
GstClockTime max_discont; /* max difference between current
playback timestamp and buffers timestamps
*/
break;
}
delay = (this->format == NULL) ? 0 :
- GST_SECOND * this->transmitted / this->format->rate -
+ GST_SECOND * this->played / this->format->rate -
gst_alsa_sink_get_time (this);
if (gst_event_discont_get_value (event, GST_FORMAT_TIME, &value)) {
gst_element_set_time_delay (GST_ELEMENT (this), MIN (value, delay),
* better way to get this info */
if (element->base_time > this->clock->start_time) {
expected =
- this->transmitted - gst_alsa_timestamp_to_samples (this,
+ this->played - gst_alsa_timestamp_to_samples (this,
element->base_time - this->clock->start_time);
} else {
expected =
- this->transmitted + gst_alsa_timestamp_to_samples (this,
+ this->played + gst_alsa_timestamp_to_samples (this,
this->clock->start_time - element->base_time);
}
} else {
if ((copied = this->transmit (this, &avail)) < 0)
return;
/* update our clock */
- this->transmitted += copied;
+ this->played += copied;
/* flush the data */
bytes = gst_alsa_samples_to_bytes (this, copied);
for (i = 0; i < element->numpads; i++) {
if (!this->format)
return 0;
if (snd_pcm_delay (this->handle, &delay) != 0) {
- return this->transmitted / this->format->rate;
+ return this->played / this->format->rate;
}
- if (this->transmitted <= delay) {
+ if (this->played <= delay) {
return 0;
}
- return GST_SECOND * (this->transmitted - delay) / this->format->rate;
+ return GST_SECOND * (this->played - delay) / this->format->rate;
}
static void gst_alsa_src_loop (GstElement * element);
static void gst_alsa_src_flush (GstAlsaSrc * src);
static GstElementStateReturn gst_alsa_src_change_state (GstElement * element);
+static GstClockTime gst_alsa_src_get_time (GstAlsa * this);
static GstAlsa *src_parent_class = NULL;
gst_pad_set_getcaps_function (this->pad[0], gst_alsa_get_caps);
gst_element_add_pad (GST_ELEMENT (this), this->pad[0]);
+ this->clock =
+ gst_alsa_clock_new ("alsasrcclock", gst_alsa_src_get_time, this);
+ /* we hold a ref to our clock until we're disposed */
+ gst_object_ref (GST_OBJECT (this->clock));
+ gst_object_sink (GST_OBJECT (this->clock));
+
gst_element_set_loop_function (GST_ELEMENT (this), gst_alsa_src_loop);
}
+static GstClockTime
+gst_alsa_src_get_time (GstAlsa * this)
+{
+ GTimeVal now;
+
+ g_get_current_time (&now);
+
+ return GST_TIMEVAL_TO_TIME (now);
+}
+
static int
gst_alsa_src_mmap (GstAlsa * this, snd_pcm_sframes_t * avail)
{
return FALSE;
}
+inline snd_pcm_sframes_t
+gst_alsa_src_update_avail (GstAlsa * this)
+{
+ snd_pcm_sframes_t avail = -1;
+
+ while (avail < 0) {
+ avail = snd_pcm_avail_update (this->handle);
+ if (avail < 0) {
+ if (avail == -EPIPE) {
+ gst_alsa_xrun_recovery (this);
+ } else {
+ GST_WARNING_OBJECT (this, "unknown ALSA avail_update return value (%d)",
+ (int) avail);
+ }
+ }
+ if (snd_pcm_state (this->handle) != SND_PCM_STATE_RUNNING) {
+ if (!gst_alsa_start (this)) {
+ return 0;
+ }
+ }
+ }
+ return avail;
+}
+
/* we transmit buffers of period_size frames */
static void
gst_alsa_src_loop (GstElement * element)
GstClockTime now;
now = gst_element_get_time (element);
- this->clock_base = gst_alsa_get_time (this);
- this->transmitted = gst_alsa_timestamp_to_samples (this, now);
+ this->clock_base = gst_alsa_src_get_time (this);
+ this->captured = gst_alsa_timestamp_to_samples (this, now);
}
/* the cast to long is explicitly needed;
* with avail = -32 and period_size = 100, avail < period_size is false */
- while ((long) (avail =
- gst_alsa_update_avail (this)) < (long) this->period_size) {
+ while ((avail = gst_alsa_src_update_avail (this)) < this->period_size) {
/* wait */
if (gst_alsa_pcm_wait (this) == FALSE)
return;
* what is now in the buffer */
outreal = gst_element_get_time (GST_ELEMENT (this)) - outdur;
/* ideal time is counting samples */
- outideal = gst_alsa_samples_to_timestamp (this, this->transmitted);
+ outideal = gst_alsa_samples_to_timestamp (this, this->captured);
outsize = gst_alsa_samples_to_bytes (this, copied);
outtime = GST_CLOCK_TIME_NONE;
if (GST_ELEMENT_CLOCK (this)) {
if (GST_CLOCK (GST_ALSA (this)->clock) == GST_ELEMENT_CLOCK (this)) {
outtime = outideal;
-
diff = outideal - outreal;
GST_DEBUG_OBJECT (this, "ideal %lld, real %lld, diff %lld\n", outideal,
outreal, diff);
- gst_alsa_clock_update (this, outideal);
} else {
outtime = outreal;
}
GST_BUFFER_TIMESTAMP (src->buf[i]) = outtime;
GST_BUFFER_DURATION (src->buf[i]) = outdur;
- GST_BUFFER_OFFSET (src->buf[i]) = this->transmitted;
- GST_BUFFER_OFFSET_END (src->buf[i]) = this->transmitted + copied;
+ GST_BUFFER_OFFSET (src->buf[i]) = this->captured;
+ GST_BUFFER_OFFSET_END (src->buf[i]) = this->captured + copied;
buf = src->buf[i];
src->buf[i] = NULL;
gst_pad_push (this->pad[i], GST_DATA (buf));
}
- this->transmitted += copied;
+ this->captured += copied;
}
}