mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-18 14:26:43 +00:00
Bugfixing in alsa again:
Original commit message from CVS: Bugfixing in alsa again: - Leif's commit reverted an earlier patch (stupid diff) - Some comment from Leif made me clean up his code - Moved wait() directly in front of mmap - Assorted fixes - fixed newbie bug: DON'T EVER USE STATIC VARIABLES WHEN YOU'RE NOT ABSOLUTELY SURE WHAT YOU'RE DOING, Leif *slap* ;) I hope I didn't break the src now...
This commit is contained in:
parent
c6809b3db0
commit
489eafaf72
2 changed files with 106 additions and 111 deletions
|
@ -97,7 +97,7 @@ static GstBufferPool *gst_alsa_src_get_buffer_pool (GstPad *pad);
|
||||||
static GstElementStateReturn gst_alsa_change_state (GstElement *element);
|
static GstElementStateReturn gst_alsa_change_state (GstElement *element);
|
||||||
|
|
||||||
/* audio processing functions */
|
/* audio processing functions */
|
||||||
static int gst_alsa_do_mmap (GstAlsa *this, guint numpads, snd_pcm_sframes_t *avail);
|
static int gst_alsa_do_mmap (GstAlsa *this, snd_pcm_sframes_t *avail);
|
||||||
|
|
||||||
static void gst_alsa_sink_loop (GstElement *element);
|
static void gst_alsa_sink_loop (GstElement *element);
|
||||||
static void gst_alsa_src_loop (GstElement *element);
|
static void gst_alsa_src_loop (GstElement *element);
|
||||||
|
@ -280,6 +280,7 @@ gst_alsa_init (GstAlsa *this)
|
||||||
/* init values */
|
/* init values */
|
||||||
this->handle = NULL;
|
this->handle = NULL;
|
||||||
this->channels = 1;
|
this->channels = 1;
|
||||||
|
this->caps_set = FALSE;
|
||||||
|
|
||||||
GST_FLAG_SET (this, GST_ELEMENT_THREAD_SUGGESTED);
|
GST_FLAG_SET (this, GST_ELEMENT_THREAD_SUGGESTED);
|
||||||
|
|
||||||
|
@ -816,20 +817,16 @@ gst_alsa_change_state (GstElement *element)
|
||||||
/*** AUDIO PROCESSING *********************************************************/
|
/*** AUDIO PROCESSING *********************************************************/
|
||||||
|
|
||||||
static int
|
static int
|
||||||
gst_alsa_do_mmap (GstAlsa *this, guint numpads, snd_pcm_sframes_t *avail)
|
gst_alsa_do_mmap (GstAlsa *this, snd_pcm_sframes_t *avail)
|
||||||
{
|
{
|
||||||
snd_pcm_uframes_t offset;
|
snd_pcm_uframes_t offset;
|
||||||
snd_pcm_channel_area_t *dst, *src, *areas;
|
snd_pcm_channel_area_t *dst, *src, *areas;
|
||||||
int i, err, width = snd_pcm_format_physical_width (this->format);
|
int i, err, width = snd_pcm_format_physical_width (this->format);
|
||||||
|
|
||||||
/* areas points to the memory areas that belong to gstreamer. */
|
/* areas points to the memory areas that belong to gstreamer. */
|
||||||
if (G_OBJECT_TYPE (this) == GST_TYPE_ALSA_SRC) {
|
areas = src = dst = calloc(this->channels, sizeof(snd_pcm_channel_area_t));
|
||||||
areas = dst = calloc(this->channels, sizeof(snd_pcm_channel_area_t));
|
|
||||||
} else {
|
|
||||||
areas = src = calloc(this->channels, sizeof(snd_pcm_channel_area_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (numpads == 1) {
|
if (((GstElement *) this)->numpads == 1) {
|
||||||
/* interleaved */
|
/* interleaved */
|
||||||
for (i = 0; i < this->channels; i++) {
|
for (i = 0; i < this->channels; i++) {
|
||||||
areas[i].addr = this->pads[0].data;
|
areas[i].addr = this->pads[0].data;
|
||||||
|
@ -845,18 +842,11 @@ gst_alsa_do_mmap (GstAlsa *this, guint numpads, snd_pcm_sframes_t *avail)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* might want to try to do some pointer sneakery in the first if block to get
|
if ((err = snd_pcm_mmap_begin (this->handle, (
|
||||||
rid of this second if block ... but right now i'm too sleepy */
|
const snd_pcm_channel_area_t **) (G_OBJECT_TYPE (this) == GST_TYPE_ALSA_SRC ? &src : &dst),
|
||||||
if (G_OBJECT_TYPE (this) == GST_TYPE_ALSA_SRC) {
|
&offset, avail)) < 0) {
|
||||||
if ((err = snd_pcm_mmap_begin (this->handle, (const snd_pcm_channel_area_t **) &src, &offset, avail)) < 0) {
|
g_warning ("gstalsa: mmap failed: %s", snd_strerror (err));
|
||||||
g_warning ("gstalsa: mmap failed: %s", snd_strerror (err));
|
return -1;
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ((err = snd_pcm_mmap_begin (this->handle, (const snd_pcm_channel_area_t **) &dst, &offset, avail)) < 0) {
|
|
||||||
g_warning ("gstalsa: mmap failed: %s", snd_strerror (err));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = snd_pcm_areas_copy (dst, offset, src, 0, this->channels, *avail, this->format)) < 0) {
|
if ((err = snd_pcm_areas_copy (dst, offset, src, 0, this->channels, *avail, this->format)) < 0) {
|
||||||
|
@ -879,16 +869,15 @@ gst_alsa_update_avail (GstAlsa *this)
|
||||||
if (avail < 0) {
|
if (avail < 0) {
|
||||||
if (avail == -EPIPE) {
|
if (avail == -EPIPE) {
|
||||||
gst_alsa_xrun_recovery (this);
|
gst_alsa_xrun_recovery (this);
|
||||||
return -EPIPE;
|
|
||||||
} else {
|
} else {
|
||||||
g_warning ("unknown ALSA avail_update return value (%d)", (int) avail);
|
g_warning ("unknown ALSA avail_update return value (%d)", (int) avail);
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return avail;
|
return avail;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static gint
|
/* returns TRUE, if the loop should go on */
|
||||||
|
inline static gboolean
|
||||||
gst_alsa_pcm_wait (GstAlsa *this)
|
gst_alsa_pcm_wait (GstAlsa *this)
|
||||||
{
|
{
|
||||||
if (snd_pcm_state (this->handle) == SND_PCM_STATE_RUNNING) {
|
if (snd_pcm_state (this->handle) == SND_PCM_STATE_RUNNING) {
|
||||||
|
@ -896,20 +885,23 @@ gst_alsa_pcm_wait (GstAlsa *this)
|
||||||
if (errno == EINTR) {
|
if (errno == EINTR) {
|
||||||
/* happens mostly when run under gdb, or when exiting due to a signal */
|
/* happens mostly when run under gdb, or when exiting due to a signal */
|
||||||
GST_DEBUG (GST_CAT_PLUGIN_INFO, "got interrupted while waiting");
|
GST_DEBUG (GST_CAT_PLUGIN_INFO, "got interrupted while waiting");
|
||||||
if (gst_element_interrupt (GST_ELEMENT (this)))
|
if (gst_element_interrupt (GST_ELEMENT (this))) {
|
||||||
return -1;
|
return TRUE;
|
||||||
|
} else {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
g_warning ("error waiting for alsa pcm: (%d: %s)", errno, strerror (errno));
|
g_warning ("error waiting for alsa pcm: (%d: %s)", errno, strerror (errno));
|
||||||
return -1;
|
return FALSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_alsa_sink_loop (GstElement *element)
|
gst_alsa_sink_loop (GstElement *element)
|
||||||
{
|
{
|
||||||
snd_pcm_sframes_t avail, copied;
|
snd_pcm_sframes_t avail, avail2, copied;
|
||||||
gint i;
|
gint i;
|
||||||
gint bytes, num_bytes; /* per channel */
|
gint bytes, num_bytes; /* per channel */
|
||||||
GstAlsa *this = GST_ALSA (element);
|
GstAlsa *this = GST_ALSA (element);
|
||||||
|
@ -932,50 +924,57 @@ gst_alsa_sink_loop (GstElement *element)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sink_restart:
|
sink_restart:
|
||||||
|
|
||||||
while (1) {
|
avail = gst_alsa_update_avail (this);
|
||||||
avail = gst_alsa_update_avail (this);
|
if (avail == -EPIPE) goto sink_restart;
|
||||||
if (avail == -EPIPE) goto sink_restart;
|
if (avail < 0) return;
|
||||||
if (avail == -1) break;
|
if (avail > 0) {
|
||||||
if (avail > 0) {
|
int width = snd_pcm_format_physical_width (this->format);
|
||||||
int width = snd_pcm_format_physical_width (this->format);
|
|
||||||
|
|
||||||
/* check how many bytes we still have in all our bytestreams */
|
/* Not enough space. We grab data nonetheless and sleep afterwards */
|
||||||
bytes = avail * ( width / 8 ) * (element->numpads == 1 ? this->channels : 1);
|
if (avail < this->period_size) {
|
||||||
for (i = 0; i < element->numpads; i++) {
|
avail = this->period_size;
|
||||||
g_assert (this->pads[i].pad != NULL);
|
|
||||||
do {
|
|
||||||
num_bytes = gst_bytestream_peek_bytes (this->pads[i].bs, &this->pads[i].data, bytes);
|
|
||||||
} while (num_bytes == 0 && gst_alsa_sink_check_event (this, i));
|
|
||||||
if (num_bytes == 0)
|
|
||||||
break;
|
|
||||||
bytes = MIN (bytes, num_bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FIXME: lotsa stuff can have happened while fetching data. Do we need to check something? */
|
|
||||||
|
|
||||||
/* put this data into alsa */
|
|
||||||
avail = bytes / (width / 8 ) / (element->numpads == 1 ? this->channels : 1);
|
|
||||||
if ((copied = gst_alsa_do_mmap (this, element->numpads, &avail)) < 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* flush the data */
|
|
||||||
bytes = copied * ( width / 8 ) * (element->numpads == 1 ? this->channels : 1);
|
|
||||||
for (i = 0; i < element->numpads; i++)
|
|
||||||
gst_bytestream_flush (this->pads[i].bs, bytes);
|
|
||||||
|
|
||||||
/* BUG: we start the stream explicitly, autostart doesn't work correctly (alsa 0.9.0rc7) */
|
|
||||||
if (snd_pcm_state(this->handle) == SND_PCM_STATE_PREPARED && snd_pcm_avail_update (this->handle) == 0) {
|
|
||||||
GST_DEBUG (GST_CAT_PLUGIN_INFO, "Explicitly starting playback");
|
|
||||||
snd_pcm_start(this->handle);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* wait */
|
/* check how many bytes we still have in all our bytestreams */
|
||||||
if (gst_alsa_pcm_wait (this) < 0)
|
bytes = avail * ( width / 8 ) * (element->numpads == 1 ? this->channels : 1);
|
||||||
break;
|
for (i = 0; i < element->numpads; i++) {
|
||||||
|
g_assert (this->pads[i].pad != NULL);
|
||||||
|
do {
|
||||||
|
num_bytes = gst_bytestream_peek_bytes (this->pads[i].bs, &this->pads[i].data, bytes);
|
||||||
|
} while (num_bytes == 0 && gst_alsa_sink_check_event (this, i));
|
||||||
|
if (num_bytes == 0)
|
||||||
|
return;
|
||||||
|
bytes = MIN (bytes, num_bytes);
|
||||||
|
}
|
||||||
|
avail = bytes / (width / 8 ) / (element->numpads == 1 ? this->channels : 1);
|
||||||
|
|
||||||
|
/* FIXME: lotsa stuff can have happened while fetching data. Do we need to check something? */
|
||||||
|
|
||||||
|
/* wait until the hw buffer has enough space */
|
||||||
|
while ((avail2 = gst_alsa_update_avail (this)) < avail) {
|
||||||
|
if (avail2 == -EPIPE) goto sink_restart;
|
||||||
|
if (avail < 0) return;
|
||||||
|
if (gst_alsa_pcm_wait (this) == FALSE)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* put this data into alsa */
|
||||||
|
if ((copied = gst_alsa_do_mmap (this, &avail)) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* flush the data */
|
||||||
|
bytes = copied * ( width / 8 ) * (element->numpads == 1 ? this->channels : 1);
|
||||||
|
for (i = 0; i < element->numpads; i++)
|
||||||
|
gst_bytestream_flush (this->pads[i].bs, bytes);
|
||||||
|
|
||||||
|
/* BUG: we start the stream explicitly, autostart doesn't work correctly (alsa 0.9.0rc7) */
|
||||||
|
if (snd_pcm_state(this->handle) == SND_PCM_STATE_PREPARED && snd_pcm_avail_update (this->handle) == 0) {
|
||||||
|
GST_DEBUG (GST_CAT_PLUGIN_INFO, "Explicitly starting playback");
|
||||||
|
snd_pcm_start(this->handle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -991,11 +990,9 @@ gst_alsa_src_loop (GstElement *element)
|
||||||
|
|
||||||
g_return_if_fail (this != NULL);
|
g_return_if_fail (this != NULL);
|
||||||
|
|
||||||
static gboolean caps_set = FALSE;
|
|
||||||
|
|
||||||
/* set the caps on all pads */
|
/* set the caps on all pads */
|
||||||
if (!caps_set) {
|
if (!this->caps_set) {
|
||||||
GST_DEBUG (GST_CAT_NEGOTIATION, "starting caps negotiation");
|
GST_DEBUG (GST_CAT_NEGOTIATION, "starting caps negotiationgst_alsa_pcm_wait");
|
||||||
caps = gst_alsa_caps (this->format, this->rate, this->channels);
|
caps = gst_alsa_caps (this->format, this->rate, this->channels);
|
||||||
for (i = 0; i < element->numpads; i++) {
|
for (i = 0; i < element->numpads; i++) {
|
||||||
if (gst_pad_try_set_caps (this->pads[i].pad, caps) <= 0) {
|
if (gst_pad_try_set_caps (this->pads[i].pad, caps) <= 0) {
|
||||||
|
@ -1003,58 +1000,55 @@ gst_alsa_src_loop (GstElement *element)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
caps_set = TRUE;
|
this->caps_set = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
src_restart:
|
src_restart:
|
||||||
|
|
||||||
while (1) {
|
while ((avail = gst_alsa_update_avail (this)) < this->period_size) {
|
||||||
avail = gst_alsa_update_avail (this);
|
|
||||||
if (avail == -EPIPE) goto src_restart;
|
if (avail == -EPIPE) goto src_restart;
|
||||||
if (avail == -1) break;
|
if (avail < 0) return;
|
||||||
if (avail > 0) {
|
/* wait */
|
||||||
int width = snd_pcm_format_physical_width (this->format);
|
if (gst_alsa_pcm_wait (this) == FALSE)
|
||||||
int bytes_per_frame = ( width / 8 ) * (element->numpads == 1 ? this->channels : 1);
|
return;
|
||||||
|
}
|
||||||
|
if (avail > 0) {
|
||||||
|
int width = snd_pcm_format_physical_width (this->format);
|
||||||
|
int bytes_per_frame = ( width / 8 ) * (element->numpads == 1 ? this->channels : 1);
|
||||||
|
if ((copied = gst_alsa_do_mmap (this, &avail)) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
if ((copied = gst_alsa_do_mmap (this, element->numpads, &avail)) < 0)
|
/* we get the buffer pool once per go round */
|
||||||
break;
|
if (! pool) pool = gst_alsa_src_get_buffer_pool (this->pads[0].pad);
|
||||||
|
|
||||||
/* we get the buffer pool once per go round */
|
/* push the data to gstreamer if it's big enough to fill up a buffer. */
|
||||||
if (! pool) pool = gst_alsa_src_get_buffer_pool (this->pads[0].pad);
|
for (i = 0; i < element->numpads; i++) {
|
||||||
|
pad = &this->pads[i];
|
||||||
|
pad->offset += MIN (copied, this->period_size - pad->offset);
|
||||||
|
|
||||||
/* push the data to gstreamer if it's big enough to fill up a buffer. */
|
if (pad->offset >= this->period_size) {
|
||||||
for (i = 0; i < element->numpads; i++) {
|
g_assert (pad->offset <= this->period_size);
|
||||||
pad = &this->pads[i];
|
|
||||||
pad->offset += MIN (copied, this->period_size - pad->offset);
|
|
||||||
|
|
||||||
if (pad->offset >= this->period_size) {
|
buf = gst_buffer_new_from_pool (pool, 0, 0);
|
||||||
g_assert (pad->offset <= this->period_size);
|
|
||||||
|
|
||||||
buf = gst_buffer_new_from_pool (pool, 0, 0);
|
GST_BUFFER_DATA (buf) = pad->data;
|
||||||
|
GST_BUFFER_SIZE (buf) = this->period_size * bytes_per_frame;
|
||||||
|
GST_BUFFER_MAXSIZE (buf) = this->period_size * bytes_per_frame;
|
||||||
|
|
||||||
GST_BUFFER_DATA (buf) = pad->data;
|
gst_pad_push (pad->pad, buf);
|
||||||
GST_BUFFER_SIZE (buf) = this->period_size * bytes_per_frame;
|
|
||||||
GST_BUFFER_MAXSIZE (buf) = this->period_size * bytes_per_frame;
|
|
||||||
|
|
||||||
gst_pad_push (pad->pad, buf);
|
pad->data = NULL;
|
||||||
|
pad->offset = 0;
|
||||||
pad->data = NULL;
|
|
||||||
pad->offset = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pool = NULL;
|
|
||||||
|
|
||||||
/* BUG: we start the stream explicitly, autostart doesn't work correctly (alsa 0.9.0rc7) */
|
|
||||||
if (snd_pcm_state(this->handle) == SND_PCM_STATE_PREPARED && snd_pcm_avail_update (this->handle) == 0) {
|
|
||||||
GST_DEBUG (GST_CAT_PLUGIN_INFO, "Explicitly starting capture");
|
|
||||||
snd_pcm_start(this->handle);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* wait */
|
pool = NULL;
|
||||||
if (gst_alsa_pcm_wait (this) < 0)
|
|
||||||
break;
|
/* BUG: we start the stream explicitly, autostart doesn't work correctly (alsa 0.9.0rc7) */
|
||||||
|
if (snd_pcm_state(this->handle) == SND_PCM_STATE_PREPARED && snd_pcm_avail_update (this->handle) == 0) {
|
||||||
|
GST_DEBUG (GST_CAT_PLUGIN_INFO, "Explicitly starting capture");
|
||||||
|
snd_pcm_start(this->handle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -87,6 +87,7 @@ struct _GstAlsa {
|
||||||
snd_pcm_format_t format;
|
snd_pcm_format_t format;
|
||||||
guint rate;
|
guint rate;
|
||||||
gint channels;
|
gint channels;
|
||||||
|
gboolean caps_set;
|
||||||
|
|
||||||
/* latency / performance parameters */
|
/* latency / performance parameters */
|
||||||
snd_pcm_uframes_t period_size;
|
snd_pcm_uframes_t period_size;
|
||||||
|
|
Loading…
Reference in a new issue