mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-25 23:46:45 +00:00
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:
parent
c09d2389f5
commit
148c90439a
4 changed files with 109 additions and 45 deletions
10
ChangeLog
10
ChangeLog
|
@ -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),
|
||||||
|
|
|
@ -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, ×tamp);
|
snd_pcm_status_get_htstamp (this->status, ×tamp);
|
||||||
|
|
||||||
/* 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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue