ext/alsa/: Cleanups, take queued samples into account when reporting the time.

Original commit message from CVS:
* ext/alsa/gstalsa.c: (gst_alsa_get_time), (gst_alsa_clock_update),
(gst_alsa_change_state), (gst_alsa_update_avail),
(gst_alsa_xrun_recovery):
* ext/alsa/gstalsa.h:
* ext/alsa/gstalsasrc.c: (gst_alsa_src_loop):
Cleanups, take queued samples into account when reporting
the time.
This commit is contained in:
Wim Taymans 2004-06-22 12:01:33 +00:00
parent c09d2389f5
commit 148c90439a
4 changed files with 109 additions and 45 deletions

View file

@ -1,3 +1,13 @@
2004-06-22 Wim Taymans <wim@fluendo.com>
* ext/alsa/gstalsa.c: (gst_alsa_get_time), (gst_alsa_clock_update),
(gst_alsa_change_state), (gst_alsa_update_avail),
(gst_alsa_xrun_recovery):
* ext/alsa/gstalsa.h:
* ext/alsa/gstalsasrc.c: (gst_alsa_src_loop):
Cleanups, take queued samples into account when reporting
the time.
2004-06-22 Wim Taymans <wim@fluendo.com> 2004-06-22 Wim Taymans <wim@fluendo.com>
* gst/videorate/gstvideorate.c: (gst_videorate_class_init), * gst/videorate/gstvideorate.c: (gst_videorate_class_init),

View file

@ -323,22 +323,46 @@ gst_alsa_get_property (GObject * object, guint prop_id, GValue * value,
* ask ALSA for current time using htstamp * ask ALSA for current time using htstamp
* FIXME: This is not very accurate, should use alsa timers instead. * FIXME: This is not very accurate, should use alsa timers instead.
* htstamp seems to use the system clock instead of the hw clock. * htstamp seems to use the system clock instead of the hw clock.
* We also use an ugly hack for now to substract the number of queued
* samples in the device from the system time, this makes sure that other
* plugins timestamp their samples with the right time but makes the
* clock a little unstable. A good way to fix this is to get the exact amount
* of samples that were produced by ALSA but I can't find a way to get that
* information.. oh well.. ALSA has enough methods, there is bound to be at
* least 1 way of getting the info...
*/ */
GstClockTime GstClockTime
gst_alsa_get_time (GstAlsa * this) gst_alsa_get_time (GstAlsa * this)
{ {
int err; int err;
snd_htimestamp_t timestamp; snd_htimestamp_t timestamp;
GstClockTime time; GstClockTime time, ideal;
GstClockTime availtime;
snd_pcm_sframes_t avail;
if ((err = snd_pcm_status (this->handle, this->status)) < 0) { if ((err = snd_pcm_status (this->handle, this->status)) < 0) {
GST_WARNING_OBJECT (this, "could not get snd_pcm_status"); GST_WARNING_OBJECT (this, "could not get snd_pcm_status");
} }
/* see how many samples are still in the buffer */
avail = snd_pcm_status_get_avail (this->status);
availtime = gst_alsa_samples_to_timestamp (this, avail);
/* get the clock time */
snd_pcm_status_get_htstamp (this->status, &timestamp); snd_pcm_status_get_htstamp (this->status, &timestamp);
/* time = GST_TIMESPEC_TO_TIME (timestamp); */ /* time = GST_TIMESPEC_TO_TIME (timestamp); */
time = timestamp.tv_sec * GST_SECOND + timestamp.tv_nsec * GST_NSECOND; /* we have to compensate the time for the number of queued samples
* in the buffer */
time =
timestamp.tv_sec * GST_SECOND + timestamp.tv_nsec * GST_NSECOND -
availtime;
ideal =
this->clock_base + gst_alsa_samples_to_timestamp (this,
this->transmitted) - availtime;
GST_DEBUG_OBJECT (this, "clock time %lld, diff to ideal %lld\n", time,
time - ideal);
GST_LOG_OBJECT (this, "ALSA reports time of %" GST_TIME_FORMAT, GST_LOG_OBJECT (this, "ALSA reports time of %" GST_TIME_FORMAT,
GST_TIME_ARGS (time)); GST_TIME_ARGS (time));
@ -346,6 +370,12 @@ gst_alsa_get_time (GstAlsa * this)
return time; return time;
} }
void
gst_alsa_clock_update (GstAlsa * this, GstClockTime ideal)
{
}
static const GList * static const GList *
gst_alsa_probe_get_properties (GstPropertyProbe * probe) gst_alsa_probe_get_properties (GstPropertyProbe * probe)
{ {
@ -1122,6 +1152,7 @@ gst_alsa_change_state (GstElement * element)
gst_alsa_start_audio (this))) gst_alsa_start_audio (this)))
return GST_STATE_FAILURE; return GST_STATE_FAILURE;
this->transmitted = 0; this->transmitted = 0;
this->clock_base = GST_CLOCK_TIME_NONE;
break; break;
case GST_STATE_PAUSED_TO_PLAYING: case GST_STATE_PAUSED_TO_PLAYING:
if (snd_pcm_state (this->handle) == SND_PCM_STATE_PAUSED) { if (snd_pcm_state (this->handle) == SND_PCM_STATE_PAUSED) {
@ -1191,8 +1222,10 @@ gst_alsa_set_clock (GstElement * element, GstClock * clock)
inline snd_pcm_sframes_t inline snd_pcm_sframes_t
gst_alsa_update_avail (GstAlsa * this) gst_alsa_update_avail (GstAlsa * this)
{ {
snd_pcm_sframes_t avail = snd_pcm_avail_update (this->handle); snd_pcm_sframes_t avail = -1;
while (avail < 0) {
avail = snd_pcm_avail_update (this->handle);
if (avail < 0) { if (avail < 0) {
if (avail == -EPIPE) { if (avail == -EPIPE) {
gst_alsa_xrun_recovery (this); gst_alsa_xrun_recovery (this);
@ -1201,6 +1234,12 @@ gst_alsa_update_avail (GstAlsa * this)
(int) avail); (int) avail);
} }
} }
if (snd_pcm_state (this->handle) != SND_PCM_STATE_RUNNING) {
if (!gst_alsa_start (this)) {
return 0;
}
}
}
return avail; return avail;
} }
@ -1280,17 +1319,7 @@ gst_alsa_xrun_recovery (GstAlsa * this)
GST_ERROR_OBJECT (this, "status error: %s", snd_strerror (err)); GST_ERROR_OBJECT (this, "status error: %s", snd_strerror (err));
if (snd_pcm_status_get_state (status) == SND_PCM_STATE_XRUN) { if (snd_pcm_status_get_state (status) == SND_PCM_STATE_XRUN) {
struct timeval now, diff, tstamp; GstClockTime elemnow;
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);
/* start new timestamps from the current time */
this->transmitted = gst_alsa_timestamp_to_samples (this,
gst_element_get_time (GST_ELEMENT (this)));
/* if we're allowed to recover, ... */ /* if we're allowed to recover, ... */
if (this->autorecover) { if (this->autorecover) {
@ -1304,13 +1333,28 @@ gst_alsa_xrun_recovery (GstAlsa * this)
this->period_count *= 2; this->period_count *= 2;
} }
} }
}
if ((err = snd_pcm_prepare (this->handle)) < 0) {
GST_ERROR_OBJECT (this, "prepare error: %s", snd_strerror (err));
return FALSE;
}
/* 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 */
elemnow = gst_element_get_time (GST_ELEMENT (this));
this->transmitted = gst_alsa_timestamp_to_samples (this, elemnow);
GST_DEBUG_OBJECT (this, "XRun!!!! pretending we transmitted %lld samples",
this->transmitted);
} else {
/* something else happened, reset the device */
if (!(gst_alsa_stop_audio (this) && gst_alsa_start_audio (this))) { if (!(gst_alsa_stop_audio (this) && gst_alsa_start_audio (this))) {
GST_ELEMENT_ERROR (this, RESOURCE, FAILED, (NULL), GST_ELEMENT_ERROR (this, RESOURCE, FAILED, (NULL),
("Error restarting audio after xrun")); ("Error restarting audio after xrun"));
return FALSE; return FALSE;
} }
}
return TRUE; return TRUE;
} }

View file

@ -156,6 +156,7 @@ struct _GstAlsa {
/* clocking */ /* clocking */
GstAlsaClock * clock; /* our provided clock */ GstAlsaClock * clock; /* our provided clock */
GstClockTime clock_base; /* adjusted clock base time */
snd_pcm_uframes_t transmitted; /* samples transmitted since last sync snd_pcm_uframes_t transmitted; /* samples transmitted since last sync
This thing actually is our master clock. This thing actually is our master clock.
We will event insert silent samples or We will event insert silent samples or
@ -193,6 +194,8 @@ GstCaps * gst_alsa_caps (snd_pcm_format_t format,
gint channels); gint channels);
GstClockTime gst_alsa_get_time (GstAlsa * this); GstClockTime gst_alsa_get_time (GstAlsa * this);
void gst_alsa_clock_update (GstAlsa * this, GstClockTime ideal);
/* audio processing functions */ /* audio processing functions */
inline snd_pcm_sframes_t gst_alsa_update_avail (GstAlsa * this); inline snd_pcm_sframes_t gst_alsa_update_avail (GstAlsa * this);

View file

@ -322,25 +322,18 @@ gst_alsa_src_loop (GstElement * element)
return; return;
} }
} }
if (this->clock_base == GST_CLOCK_TIME_NONE) {
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);
}
/* the cast to long is explicitly needed; /* the cast to long is explicitly needed;
* with avail = -32 and period_size = 100, avail < period_size is false */ * with avail = -32 and period_size = 100, avail < period_size is false */
while ((avail = gst_alsa_update_avail (this)) < (long) this->period_size) { while ((long) (avail =
if (avail == -EPIPE) { gst_alsa_update_avail (this)) < (long) this->period_size) {
GST_DEBUG_OBJECT (this, "got EPIPE when checking for available bytes");
continue;
}
if (avail < 0) {
GST_DEBUG_OBJECT (this,
"got error %s (%d) when checking for available bytes",
snd_strerror (avail));
return;
}
if (snd_pcm_state (this->handle) != SND_PCM_STATE_RUNNING) {
if (!gst_alsa_start (this))
return;
continue;
};
/* wait */ /* wait */
if (gst_alsa_pcm_wait (this) == FALSE) if (gst_alsa_pcm_wait (this) == FALSE)
return; return;
@ -360,17 +353,31 @@ gst_alsa_src_loop (GstElement * element)
{ {
gint outsize; gint outsize;
GstClockTime outtime, outdur; GstClockTime outtime, outdur, outreal, outideal;
gint64 diff;
/* duration of buffer is just the time of the samples */
outdur = gst_alsa_samples_to_timestamp (this, copied);
/* The real capture time is the time of the clock minus the duration and
* 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);
outsize = gst_alsa_samples_to_bytes (this, copied); outsize = gst_alsa_samples_to_bytes (this, copied);
outdur = gst_alsa_samples_to_timestamp (this, copied);
outtime = GST_CLOCK_TIME_NONE; outtime = GST_CLOCK_TIME_NONE;
if (GST_ELEMENT_CLOCK (this)) { if (GST_ELEMENT_CLOCK (this)) {
if (GST_CLOCK (GST_ALSA (this)->clock) == GST_ELEMENT_CLOCK (this)) { if (GST_CLOCK (GST_ALSA (this)->clock) == GST_ELEMENT_CLOCK (this)) {
outtime = gst_alsa_samples_to_timestamp (this, this->transmitted); 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 { } else {
outtime = gst_element_get_time (GST_ELEMENT (this)); outtime = outreal;
} }
} }