*/
#include <stdlib.h>
+#include <sys/time.h>
#include "gstalsa.h"
static GstElementDetails gst_alsa_sink_details = {
static gboolean gst_alsa_set_params(GstAlsa *this);
static void gst_alsa_loop (GstElement *element);
+static void gst_alsa_xrun_recovery (GstAlsa *this);
static gboolean gst_alsa_sink_process (GstAlsa *this, snd_pcm_uframes_t frames);
static gboolean gst_alsa_src_process (GstAlsa *this, snd_pcm_uframes_t frames);
unsigned int unit_bytes,
unsigned int skip_bytes);
+/* #define _DEBUG */
+#ifdef _DEBUG
+#define DEBUG(text, args...) g_message(text, ##args)
+#else
+#define DEBUG(text, args...)
+#endif
+
enum {
ARG_0,
return GST_PAD_CONNECT_DELAYED;
}
-/* shamelessly stolen from pbd's audioengine. thanks, paul! */
+/* shamelessly stolen from pbd's audioengine and jack alsa_driver. thanks, paul! */
static void
gst_alsa_loop (GstElement *element)
{
xrun_detected = FALSE;
this->avail = snd_pcm_avail_update (this->handle);
-// g_print ("snd_pcm_avail_update() = %d\n", (int)this->avail);
+ DEBUG ("snd_pcm_avail_update() = %d", (int)this->avail);
if (this->avail < 0) {
if (this->avail == -EPIPE) {
- xrun_detected = TRUE;
+ gst_alsa_xrun_recovery (this);
+ this->avail = 0;
} else {
g_warning("unknown ALSA avail_update return value (%d)",
(int)this->avail);
/* round down to nearest period_frames avail */
this->avail -= this->avail % this->period_frames;
-// g_print ("snd_pcm_avail_update(), rounded down = %d\n", (int)this->avail);
+ DEBUG ("snd_pcm_avail_update(), rounded down = %d", (int)this->avail);
- /* pretty sophisticated eh? */
- if (xrun_detected)
- g_warning ("xrun detected");
-
/* we need to loop here because the available bytes might not be
* contiguous */
while (this->avail) {
return TRUE;
}
+static void
+gst_alsa_xrun_recovery (GstAlsa *this)
+{
+ snd_pcm_status_t *status;
+ int res;
+
+ snd_pcm_status_alloca(&status);
+
+ if (this->stream == SND_PCM_STREAM_CAPTURE) {
+ if ((res = snd_pcm_status(this->handle, status)) < 0) {
+ g_warning ("status error: %s", snd_strerror(res));
+ }
+ } else {
+ if ((res = snd_pcm_status(this->handle, status)) < 0) {
+ g_warning ("status error: %s", snd_strerror(res));
+ }
+ }
+
+ 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);
+ g_warning("alsa: xrun of at least %.3f msecs", diff.tv_sec * 1000 + diff.tv_usec / 1000.0);
+ }
+
+ gst_alsa_stop_audio (this);
+ gst_alsa_start_audio (this);
+}
+
/* taken more or less from pbd's audioengine code */
static gboolean
gst_alsa_set_params (GstAlsa *this)
return FALSE;
}
- ret = snd_pcm_sw_params_set_stop_threshold (this->handle, sw_param, ~0U);
+ ret = snd_pcm_sw_params_set_stop_threshold (this->handle, sw_param, this->buffer_frames);
if (ret < 0) {
g_warning("could not set stop mode: %s", snd_strerror(ret));
return FALSE;
gst_caps_get_int (caps, "endianness", &adder->endianness);
gst_caps_get_boolean (caps, "signed", &adder->is_signed);
gst_caps_get_int (caps, "channels", &adder->channels);
+ gst_caps_get_int (caps, "rate", &adder->rate);
} else if (strcmp (format, "float") == 0) {
adder->format = GST_ADDER_FORMAT_FLOAT;
gst_caps_get_string (caps, "layout", &adder->layout);
gst_caps_get_float (caps, "intercept", &adder->intercept);
gst_caps_get_float (caps, "slope", &adder->slope);
gst_caps_get_int (caps, "channels", &adder->channels);
+ gst_caps_get_int (caps, "rate", &adder->rate);
}
} else {
/* otherwise, a previously-connected pad has set all the values. we should
barf if some of the attempted new values don't match. */
if (strcmp (format, "int") == 0) {
- gint width, channels;
+ gint width, channels, rate;
gboolean is_signed;
gst_caps_get_int (caps, "width", &width);
gst_caps_get_int (caps, "channels", &channels);
gst_caps_get_boolean (caps, "signed", &is_signed);
+ gst_caps_get_int (caps, "rate", &rate);
if ((adder->format != GST_ADDER_FORMAT_INT) ||
(adder->width != width) ||
(adder->channels != channels) ||
- (adder->is_signed != is_signed)) {
+ (adder->is_signed != is_signed) ||
+ (adder->rate != rate)) {
return FALSE;
}
} else if (strcmp (format, "float") == 0) {
- gint channels;
+ gint channels, rate;
gst_caps_get_int (caps, "channels", &channels);
+ gst_caps_get_int (caps, "rate", &rate);
if ((adder->format != GST_ADDER_FORMAT_FLOAT) ||
- (adder->channels != channels)) {
+ (adder->channels != channels) ||
+ (adder->rate != rate)) {
return FALSE;
}
} else {
guint8 *raw_in;
guint32 waiting;
guint32 got_bytes;
+ gint64 timestamp = 0;
+ gint64 offset = 0;
register guint i;
g_return_if_fail (element != NULL);
inputs = g_slist_next (inputs);
}
+ GST_BUFFER_TIMESTAMP (buf_out) = timestamp;
+ if (adder->format == GST_ADDER_FORMAT_FLOAT)
+ offset += GST_BUFFER_SIZE (buf_out) / sizeof (gfloat) / adder->channels;
+ else
+ offset += GST_BUFFER_SIZE (buf_out) * 8 / adder->width / adder->channels;
+ timestamp = offset * GST_SECOND / adder->rate;
+
/* send it out */
GST_DEBUG (0, "pushing buf_out");