ext/alsa/: Add clock to alsasrc. Take new capture timestamp when restarting after an overrun. Split up some functions...
Original commit message from CVS: * 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.
This commit is contained in:
parent
96c2a15318
commit
e56c174f8d
14
ChangeLog
14
ChangeLog
@ -1,3 +1,17 @@
|
|||||||
|
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>
|
2004-06-23 Thomas Vander Stichele <thomas at apestaart dot org>
|
||||||
|
|
||||||
* ext/alsa/gstalsa.c: (gst_alsa_init), (gst_alsa_dispose),
|
* ext/alsa/gstalsa.c: (gst_alsa_init), (gst_alsa_dispose),
|
||||||
|
@ -1085,7 +1085,8 @@ gst_alsa_change_state (GstElement * element)
|
|||||||
if (!(GST_FLAG_IS_SET (element, GST_ALSA_RUNNING) ||
|
if (!(GST_FLAG_IS_SET (element, GST_ALSA_RUNNING) ||
|
||||||
gst_alsa_start_audio (this)))
|
gst_alsa_start_audio (this)))
|
||||||
return GST_STATE_FAILURE;
|
return GST_STATE_FAILURE;
|
||||||
this->transmitted = 0;
|
this->played = 0;
|
||||||
|
this->captured = 0;
|
||||||
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) {
|
||||||
@ -1202,6 +1203,8 @@ gst_alsa_pcm_wait (GstAlsa * this)
|
|||||||
inline gboolean
|
inline gboolean
|
||||||
gst_alsa_start (GstAlsa * this)
|
gst_alsa_start (GstAlsa * this)
|
||||||
{
|
{
|
||||||
|
GstClockTime elemnow;
|
||||||
|
|
||||||
GST_DEBUG ("Setting state to RUNNING");
|
GST_DEBUG ("Setting state to RUNNING");
|
||||||
|
|
||||||
switch (snd_pcm_state (this->handle)) {
|
switch (snd_pcm_state (this->handle)) {
|
||||||
@ -1212,6 +1215,13 @@ gst_alsa_start (GstAlsa * this)
|
|||||||
ERROR_CHECK (snd_pcm_prepare (this->handle), "error preparing: %s");
|
ERROR_CHECK (snd_pcm_prepare (this->handle), "error preparing: %s");
|
||||||
case SND_PCM_STATE_SUSPENDED:
|
case SND_PCM_STATE_SUSPENDED:
|
||||||
case SND_PCM_STATE_PREPARED:
|
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");
|
ERROR_CHECK (snd_pcm_start (this->handle), "error starting playback: %s");
|
||||||
break;
|
break;
|
||||||
case SND_PCM_STATE_PAUSED:
|
case SND_PCM_STATE_PAUSED:
|
||||||
@ -1244,13 +1254,6 @@ 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;
|
|
||||||
|
|
||||||
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 we're allowed to recover, ... */
|
||||||
if (this->autorecover) {
|
if (this->autorecover) {
|
||||||
@ -1264,12 +1267,25 @@ gst_alsa_xrun_recovery (GstAlsa * this)
|
|||||||
this->period_count *= 2;
|
this->period_count *= 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!(gst_alsa_stop_audio (this) && gst_alsa_start_audio (this))) {
|
/* prepare the device again */
|
||||||
GST_ELEMENT_ERROR (this, RESOURCE, FAILED, (NULL),
|
if ((err = snd_pcm_prepare (this->handle)) < 0) {
|
||||||
("Error restarting audio after xrun"));
|
GST_ERROR_OBJECT (this, "prepare error: %s", snd_strerror (err));
|
||||||
return FALSE;
|
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;
|
return TRUE;
|
||||||
|
@ -156,11 +156,13 @@ struct _GstAlsa {
|
|||||||
|
|
||||||
/* clocking */
|
/* clocking */
|
||||||
GstAlsaClock * clock; /* our provided clock */
|
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.
|
This thing actually is our master clock.
|
||||||
We will event insert silent samples or
|
We will event insert silent samples or
|
||||||
drop some to sync to incoming timestamps.
|
drop some to sync to incoming timestamps.
|
||||||
*/
|
*/
|
||||||
|
snd_pcm_uframes_t captured;
|
||||||
GstClockTime max_discont; /* max difference between current
|
GstClockTime max_discont; /* max difference between current
|
||||||
playback timestamp and buffers timestamps
|
playback timestamp and buffers timestamps
|
||||||
*/
|
*/
|
||||||
|
@ -217,7 +217,7 @@ gst_alsa_sink_check_event (GstAlsaSink * sink, gint pad_nr)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
delay = (this->format == NULL) ? 0 :
|
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);
|
gst_alsa_sink_get_time (this);
|
||||||
if (gst_event_discont_get_value (event, GST_FORMAT_TIME, &value)) {
|
if (gst_event_discont_get_value (event, GST_FORMAT_TIME, &value)) {
|
||||||
gst_element_set_time_delay (GST_ELEMENT (this), MIN (value, delay),
|
gst_element_set_time_delay (GST_ELEMENT (this), MIN (value, delay),
|
||||||
@ -386,11 +386,11 @@ sink_restart:
|
|||||||
* better way to get this info */
|
* better way to get this info */
|
||||||
if (element->base_time > this->clock->start_time) {
|
if (element->base_time > this->clock->start_time) {
|
||||||
expected =
|
expected =
|
||||||
this->transmitted - gst_alsa_timestamp_to_samples (this,
|
this->played - gst_alsa_timestamp_to_samples (this,
|
||||||
element->base_time - this->clock->start_time);
|
element->base_time - this->clock->start_time);
|
||||||
} else {
|
} else {
|
||||||
expected =
|
expected =
|
||||||
this->transmitted + gst_alsa_timestamp_to_samples (this,
|
this->played + gst_alsa_timestamp_to_samples (this,
|
||||||
this->clock->start_time - element->base_time);
|
this->clock->start_time - element->base_time);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -485,7 +485,7 @@ sink_restart:
|
|||||||
if ((copied = this->transmit (this, &avail)) < 0)
|
if ((copied = this->transmit (this, &avail)) < 0)
|
||||||
return;
|
return;
|
||||||
/* update our clock */
|
/* update our clock */
|
||||||
this->transmitted += copied;
|
this->played += copied;
|
||||||
/* flush the data */
|
/* flush the data */
|
||||||
bytes = gst_alsa_samples_to_bytes (this, copied);
|
bytes = gst_alsa_samples_to_bytes (this, copied);
|
||||||
for (i = 0; i < element->numpads; i++) {
|
for (i = 0; i < element->numpads; i++) {
|
||||||
@ -543,11 +543,11 @@ gst_alsa_sink_get_time (GstAlsa * this)
|
|||||||
if (!this->format)
|
if (!this->format)
|
||||||
return 0;
|
return 0;
|
||||||
if (snd_pcm_delay (this->handle, &delay) != 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 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GST_SECOND * (this->transmitted - delay) / this->format->rate;
|
return GST_SECOND * (this->played - delay) / this->format->rate;
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,7 @@ static int gst_alsa_src_read (GstAlsa * this, snd_pcm_sframes_t * avail);
|
|||||||
static void gst_alsa_src_loop (GstElement * element);
|
static void gst_alsa_src_loop (GstElement * element);
|
||||||
static void gst_alsa_src_flush (GstAlsaSrc * src);
|
static void gst_alsa_src_flush (GstAlsaSrc * src);
|
||||||
static GstElementStateReturn gst_alsa_src_change_state (GstElement * element);
|
static GstElementStateReturn gst_alsa_src_change_state (GstElement * element);
|
||||||
|
static GstClockTime gst_alsa_src_get_time (GstAlsa * this);
|
||||||
|
|
||||||
static GstAlsa *src_parent_class = NULL;
|
static GstAlsa *src_parent_class = NULL;
|
||||||
|
|
||||||
@ -127,9 +128,25 @@ gst_alsa_src_init (GstAlsaSrc * src)
|
|||||||
gst_pad_set_getcaps_function (this->pad[0], gst_alsa_get_caps);
|
gst_pad_set_getcaps_function (this->pad[0], gst_alsa_get_caps);
|
||||||
gst_element_add_pad (GST_ELEMENT (this), this->pad[0]);
|
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);
|
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
|
static int
|
||||||
gst_alsa_src_mmap (GstAlsa * this, snd_pcm_sframes_t * avail)
|
gst_alsa_src_mmap (GstAlsa * this, snd_pcm_sframes_t * avail)
|
||||||
{
|
{
|
||||||
@ -305,6 +322,30 @@ gst_alsa_src_set_caps (GstAlsaSrc * src, gboolean aggressive)
|
|||||||
return FALSE;
|
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 */
|
/* we transmit buffers of period_size frames */
|
||||||
static void
|
static void
|
||||||
gst_alsa_src_loop (GstElement * element)
|
gst_alsa_src_loop (GstElement * element)
|
||||||
@ -326,14 +367,13 @@ gst_alsa_src_loop (GstElement * element)
|
|||||||
GstClockTime now;
|
GstClockTime now;
|
||||||
|
|
||||||
now = gst_element_get_time (element);
|
now = gst_element_get_time (element);
|
||||||
this->clock_base = gst_alsa_get_time (this);
|
this->clock_base = gst_alsa_src_get_time (this);
|
||||||
this->transmitted = gst_alsa_timestamp_to_samples (this, now);
|
this->captured = 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 ((long) (avail =
|
while ((avail = gst_alsa_src_update_avail (this)) < this->period_size) {
|
||||||
gst_alsa_update_avail (this)) < (long) this->period_size) {
|
|
||||||
/* wait */
|
/* wait */
|
||||||
if (gst_alsa_pcm_wait (this) == FALSE)
|
if (gst_alsa_pcm_wait (this) == FALSE)
|
||||||
return;
|
return;
|
||||||
@ -363,7 +403,7 @@ gst_alsa_src_loop (GstElement * element)
|
|||||||
* what is now in the buffer */
|
* what is now in the buffer */
|
||||||
outreal = gst_element_get_time (GST_ELEMENT (this)) - outdur;
|
outreal = gst_element_get_time (GST_ELEMENT (this)) - outdur;
|
||||||
/* ideal time is counting samples */
|
/* 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);
|
outsize = gst_alsa_samples_to_bytes (this, copied);
|
||||||
outtime = GST_CLOCK_TIME_NONE;
|
outtime = GST_CLOCK_TIME_NONE;
|
||||||
@ -371,11 +411,9 @@ gst_alsa_src_loop (GstElement * element)
|
|||||||
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 = outideal;
|
outtime = outideal;
|
||||||
|
|
||||||
diff = outideal - outreal;
|
diff = outideal - outreal;
|
||||||
GST_DEBUG_OBJECT (this, "ideal %lld, real %lld, diff %lld\n", outideal,
|
GST_DEBUG_OBJECT (this, "ideal %lld, real %lld, diff %lld\n", outideal,
|
||||||
outreal, diff);
|
outreal, diff);
|
||||||
gst_alsa_clock_update (this, outideal);
|
|
||||||
} else {
|
} else {
|
||||||
outtime = outreal;
|
outtime = outreal;
|
||||||
}
|
}
|
||||||
@ -392,14 +430,14 @@ gst_alsa_src_loop (GstElement * element)
|
|||||||
|
|
||||||
GST_BUFFER_TIMESTAMP (src->buf[i]) = outtime;
|
GST_BUFFER_TIMESTAMP (src->buf[i]) = outtime;
|
||||||
GST_BUFFER_DURATION (src->buf[i]) = outdur;
|
GST_BUFFER_DURATION (src->buf[i]) = outdur;
|
||||||
GST_BUFFER_OFFSET (src->buf[i]) = this->transmitted;
|
GST_BUFFER_OFFSET (src->buf[i]) = this->captured;
|
||||||
GST_BUFFER_OFFSET_END (src->buf[i]) = this->transmitted + copied;
|
GST_BUFFER_OFFSET_END (src->buf[i]) = this->captured + copied;
|
||||||
|
|
||||||
buf = src->buf[i];
|
buf = src->buf[i];
|
||||||
src->buf[i] = NULL;
|
src->buf[i] = NULL;
|
||||||
gst_pad_push (this->pad[i], GST_DATA (buf));
|
gst_pad_push (this->pad[i], GST_DATA (buf));
|
||||||
}
|
}
|
||||||
this->transmitted += copied;
|
this->captured += copied;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user