* ask ALSA for current time using htstamp
* FIXME: This is not very accurate, should use alsa timers instead.
* 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
gst_alsa_get_time (GstAlsa * this)
{
int err;
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) {
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, ×tamp);
/* 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_TIME_ARGS (time));
return time;
}
+void
+gst_alsa_clock_update (GstAlsa * this, GstClockTime ideal)
+{
+
+}
+
static const GList *
gst_alsa_probe_get_properties (GstPropertyProbe * probe)
{
gst_alsa_start_audio (this)))
return GST_STATE_FAILURE;
this->transmitted = 0;
+ this->clock_base = GST_CLOCK_TIME_NONE;
break;
case GST_STATE_PAUSED_TO_PLAYING:
if (snd_pcm_state (this->handle) == SND_PCM_STATE_PAUSED) {
inline snd_pcm_sframes_t
gst_alsa_update_avail (GstAlsa * this)
{
- snd_pcm_sframes_t avail = snd_pcm_avail_update (this->handle);
+ snd_pcm_sframes_t avail = -1;
- 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);
+ 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;
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);
-
- /* start new timestamps from the current time */
- this->transmitted = gst_alsa_timestamp_to_samples (this,
- gst_element_get_time (GST_ELEMENT (this)));
+ GstClockTime elemnow;
/* 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;
+ 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))) {
+ GST_ELEMENT_ERROR (this, RESOURCE, FAILED, (NULL),
+ ("Error restarting audio after xrun"));
+ return FALSE;
+ }
}
return TRUE;
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;
* with avail = -32 and period_size = 100, avail < period_size is false */
- while ((avail = gst_alsa_update_avail (this)) < (long) this->period_size) {
- if (avail == -EPIPE) {
- 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;
- };
+ while ((long) (avail =
+ gst_alsa_update_avail (this)) < (long) this->period_size) {
/* wait */
if (gst_alsa_pcm_wait (this) == FALSE)
return;
{
gint outsize;
- GstClockTime outtime, outdur;
+ GstClockTime outtime, outdur, outreal, outideal;
+ gint64 diff;
- outsize = gst_alsa_samples_to_bytes (this, copied);
+ /* 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);
outtime = GST_CLOCK_TIME_NONE;
if (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 {
- outtime = gst_element_get_time (GST_ELEMENT (this));
+ outtime = outreal;
}
}