+ fixing 100 % cpu usage bug (bug #103658) + cleaning up some of the FIXMEs, mostly bytestream stuff + changing loop ...

Original commit message from CVS:
+ fixing 100 % cpu usage bug (bug #103658)
+ cleaning up some of the FIXMEs, mostly bytestream stuff
+ changing loop to use snd_pcm_wait instead of that poll business
This commit is contained in:
Leif Johnson 2003-01-19 23:23:30 +00:00
parent ca810029cc
commit 85a0755988

View file

@ -57,13 +57,12 @@ static GstPadTemplate *gst_alsa_src_request_pad_factory();
static GstPadTemplate *gst_alsa_sink_pad_factory(); static GstPadTemplate *gst_alsa_sink_pad_factory();
static GstPadTemplate *gst_alsa_sink_request_pad_factory(); static GstPadTemplate *gst_alsa_sink_request_pad_factory();
static GstPad* gst_alsa_request_new_pad (GstElement *element, GstPadTemplate *templ, const static GstPad* gst_alsa_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar *name);
gchar *name);
static void gst_alsa_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void gst_alsa_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
static void gst_alsa_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static void gst_alsa_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
static GstElementStateReturn gst_alsa_change_state(GstElement *element); static GstElementStateReturn gst_alsa_change_state(GstElement *element);
static GstPadLinkReturn gst_alsa_connect(GstPad *pad, GstCaps *caps); static GstPadLinkReturn gst_alsa_link(GstPad *pad, GstCaps *caps);
static GstCaps* gst_alsa_caps (GstAlsa *this); static GstCaps* gst_alsa_caps (GstAlsa *this);
@ -82,9 +81,8 @@ static gboolean gst_alsa_get_channel_addresses (GstAlsa *this);
static void gst_alsa_release_channel_addresses (GstAlsa *this); static void gst_alsa_release_channel_addresses (GstAlsa *this);
static void gst_alsa_sink_silence_on_channel (GstAlsa *this, guint32 chn, guint32 nframes); static void gst_alsa_sink_silence_on_channel (GstAlsa *this, guint32 chn, guint32 nframes);
static void memset_interleave (char *dst, char val, unsigned int bytes,
unsigned int unit_bytes, static void memset_interleave (char *dst, char val, unsigned int bytes, unsigned int unit_bytes, unsigned int skip_bytes);
unsigned int skip_bytes);
/* #define _DEBUG */ /* #define _DEBUG */
#ifdef _DEBUG #ifdef _DEBUG
@ -215,9 +213,12 @@ gst_alsa_src_request_pad_factory(void)
static GstPadTemplate *template = NULL; static GstPadTemplate *template = NULL;
if (!template) if (!template)
template = gst_pad_template_new("src%d", GST_PAD_SRC, GST_PAD_REQUEST, template =
gst_pad_template_new("src%d", GST_PAD_SRC, GST_PAD_REQUEST,
gst_caps_new("src-request", "audio/raw", gst_caps_new("src-request", "audio/raw",
gst_props_new("channels", GST_PROPS_INT(1), NULL)), gst_props_new("channels",
GST_PROPS_INT(1),
NULL)),
NULL); NULL);
return template; return template;
@ -242,9 +243,12 @@ gst_alsa_sink_request_pad_factory(void)
static GstPadTemplate *template = NULL; static GstPadTemplate *template = NULL;
if (!template) if (!template)
template = gst_pad_template_new("sink%d", GST_PAD_SINK, GST_PAD_REQUEST, template =
gst_pad_template_new("sink%d", GST_PAD_SINK, GST_PAD_REQUEST,
gst_caps_new("sink-request", "audio/raw", gst_caps_new("sink-request", "audio/raw",
gst_props_new("channels", GST_PROPS_INT(1), NULL)), gst_props_new("channels",
GST_PROPS_INT(1),
NULL)),
NULL); NULL);
return template; return template;
@ -327,7 +331,7 @@ gst_alsa_init(GstAlsa *this)
gst_element_add_pad(GST_ELEMENT(this), GST_ALSA_PAD(this->pads)->pad); gst_element_add_pad(GST_ELEMENT(this), GST_ALSA_PAD(this->pads)->pad);
gst_pad_set_link_function(GST_ALSA_PAD(this->pads)->pad, gst_alsa_connect); gst_pad_set_link_function(GST_ALSA_PAD(this->pads)->pad, gst_alsa_link);
gst_element_set_loop_function(GST_ELEMENT(this), gst_alsa_loop); gst_element_set_loop_function(GST_ELEMENT(this), gst_alsa_loop);
} }
@ -377,7 +381,7 @@ gst_alsa_request_new_pad (GstElement *element, GstPadTemplate *templ, const gcha
pad->channel = channel; pad->channel = channel;
pad->pad = gst_pad_new_from_template (templ, newname); pad->pad = gst_pad_new_from_template (templ, newname);
gst_element_add_pad (GST_ELEMENT (this), pad->pad); gst_element_add_pad (GST_ELEMENT (this), pad->pad);
gst_pad_set_link_function(pad->pad, gst_alsa_connect); gst_pad_set_link_function(pad->pad, gst_alsa_link);
if (this->data_interleaved && this->pads) { if (this->data_interleaved && this->pads) {
gst_element_remove_pad (GST_ELEMENT (this), GST_ALSA_PAD(this->pads)->pad); gst_element_remove_pad (GST_ELEMENT (this), GST_ALSA_PAD(this->pads)->pad);
@ -489,6 +493,7 @@ gst_alsa_change_state(GstElement *element)
{ {
GstAlsa *this; GstAlsa *this;
guint chn; guint chn;
GList *l;
g_return_val_if_fail(element != NULL, FALSE); g_return_val_if_fail(element != NULL, FALSE);
this = GST_ALSA (element); this = GST_ALSA (element);
@ -496,10 +501,17 @@ gst_alsa_change_state(GstElement *element)
switch (GST_STATE_PENDING(element)) { switch (GST_STATE_PENDING(element)) {
case GST_STATE_NULL: case GST_STATE_NULL:
if (GST_FLAG_IS_SET(element, GST_ALSA_RUNNING)) if (GST_FLAG_IS_SET(element, GST_ALSA_RUNNING))
gst_alsa_stop_audio((GstAlsa *)element); gst_alsa_stop_audio(this);
if (GST_FLAG_IS_SET(element, GST_ALSA_OPEN)) if (GST_FLAG_IS_SET(element, GST_ALSA_OPEN))
gst_alsa_close_audio((GstAlsa *)element); gst_alsa_close_audio(this);
/* FIXME: clean up bytestreams, etc */
l = this->pads;
while(l) {
if (GST_ALSA_PAD(l)->bs)
gst_bytestream_destroy(GST_ALSA_PAD(l)->bs);
l = l->next;
}
break; break;
case GST_STATE_READY: case GST_STATE_READY:
@ -507,7 +519,7 @@ gst_alsa_change_state(GstElement *element)
case GST_STATE_PAUSED: case GST_STATE_PAUSED:
if (GST_FLAG_IS_SET(element, GST_ALSA_OPEN) == FALSE) if (GST_FLAG_IS_SET(element, GST_ALSA_OPEN) == FALSE)
if (gst_alsa_open_audio((GstAlsa *)element) == FALSE) if (gst_alsa_open_audio(this) == FALSE)
return GST_STATE_FAILURE; return GST_STATE_FAILURE;
if (GST_FLAG_IS_SET(element, GST_ALSA_RUNNING)) { if (GST_FLAG_IS_SET(element, GST_ALSA_RUNNING)) {
if (this->stream == SND_PCM_STREAM_PLAYBACK) { if (this->stream == SND_PCM_STREAM_PLAYBACK) {
@ -521,7 +533,7 @@ gst_alsa_change_state(GstElement *element)
case GST_STATE_PLAYING: case GST_STATE_PLAYING:
if (GST_FLAG_IS_SET(element, GST_ALSA_RUNNING) == FALSE) if (GST_FLAG_IS_SET(element, GST_ALSA_RUNNING) == FALSE)
if (gst_alsa_start_audio((GstAlsa *)element) == FALSE) if (gst_alsa_start_audio(this) == FALSE)
return GST_STATE_FAILURE; return GST_STATE_FAILURE;
break; break;
} }
@ -753,7 +765,7 @@ gst_alsa_caps (GstAlsa *this)
* Negotiates the caps, "borrowed" from gstosssink.c * Negotiates the caps, "borrowed" from gstosssink.c
*/ */
GstPadLinkReturn GstPadLinkReturn
gst_alsa_connect(GstPad *pad, GstCaps *caps) gst_alsa_link(GstPad *pad, GstCaps *caps)
{ {
GstAlsa *this; GstAlsa *this;
gboolean need_mmap; gboolean need_mmap;
@ -798,58 +810,39 @@ gst_alsa_connect(GstPad *pad, GstCaps *caps)
return GST_PAD_LINK_DELAYED; return GST_PAD_LINK_DELAYED;
} }
/* shamelessly stolen from pbd's audioengine and jack alsa_driver. thanks, paul! */ /* shamelessly stolen from pbd's audioengine and jack alsa_driver. thanks,
paul! */
static void static void
gst_alsa_loop (GstElement *element) gst_alsa_loop (GstElement *element)
{ {
struct pollfd pfd;
gboolean xrun_detected;
guint32 i; guint32 i;
GstAlsa *this = GST_ALSA(element); GstAlsa *this = GST_ALSA(element);
g_return_if_fail(this != NULL); g_return_if_fail(this != NULL);
snd_pcm_poll_descriptors (this->handle, &pfd, 1);
if (this->stream == SND_PCM_STREAM_PLAYBACK) {
pfd.events = POLLOUT | POLLERR;
} else {
pfd.events = POLLIN | POLLERR;
}
do { do {
xrun_detected = FALSE; if (snd_pcm_wait (this->handle, 1000) < 0) {
if (poll (&pfd, 1, 1000) < 0) {
if (errno == EINTR) { if (errno == EINTR) {
/* this happens mostly when run /* this happens mostly when run
* under gdb, or when exiting due to a signal */ * under gdb, or when exiting due to a signal */
g_print ("EINTR\n"); g_print ("EINTR\n");
if (gst_element_interrupt (element))
break;
else
continue; continue;
} }
g_warning("poll call failed (%s)", strerror(errno)); g_warning("error waiting for alsa pcm: (%d: %s)", errno, strerror(errno));
return; return;
} }
if (pfd.revents & POLLERR) {
xrun_detected = TRUE;
}
if (pfd.revents == 0) {
g_print ("poll on alsa %s device \"%s\" timed out\n",
this->stream==SND_PCM_STREAM_CAPTURE ? "capture" : "playback",
this->device);
/* timed out, such as when the device is paused */
continue;
}
this->avail = snd_pcm_avail_update (this->handle); this->avail = snd_pcm_avail_update (this->handle);
DEBUG ("snd_pcm_avail_update() = %d", (int)this->avail); DEBUG ("snd_pcm_avail_update() = %d", (int)this->avail);
if (this->avail < 0) { if (this->avail < 0) {
if (this->avail == -EPIPE) { if (this->avail == -EPIPE) {
xrun_detected = TRUE; gst_alsa_xrun_recovery (this);
this->avail = 0;
} else { } else {
g_warning("unknown ALSA avail_update return value (%d)", g_warning("unknown ALSA avail_update return value (%d)",
(int)this->avail); (int)this->avail);
@ -857,11 +850,6 @@ gst_alsa_loop (GstElement *element)
} }
} }
if (xrun_detected) {
gst_alsa_xrun_recovery (this);
this->avail = 0;
}
/* round down to nearest period_frames avail */ /* round down to nearest period_frames avail */
this->avail -= this->avail % this->period_frames; this->avail -= this->avail % this->period_frames;
@ -893,7 +881,10 @@ gst_alsa_loop (GstElement *element)
if (this->mmap_open) if (this->mmap_open)
gst_alsa_release_channel_addresses(this); gst_alsa_release_channel_addresses(this);
} }
gst_element_yield (element);
if (gst_element_interrupt (element))
break;
} while (TRUE); } while (TRUE);
} }
@ -905,7 +896,6 @@ gst_alsa_src_process (GstAlsa *this, snd_pcm_uframes_t frames)
GstAlsaPad *pad = NULL; GstAlsaPad *pad = NULL;
GstCaps *caps; GstCaps *caps;
gint unit; gint unit;
/* gint i=0; */
static gboolean caps_set = FALSE; static gboolean caps_set = FALSE;
@ -915,7 +905,7 @@ gst_alsa_src_process (GstAlsa *this, snd_pcm_uframes_t frames)
l = this->pads; l = this->pads;
while (l) { while (l) {
if (gst_pad_try_set_caps (GST_ALSA_PAD(l)->pad, caps) <= 0) { if (gst_pad_try_set_caps (GST_ALSA_PAD(l)->pad, caps) <= 0) {
g_print ("DANGER WILL ROBINSON!\n"); g_print ("setting caps (%p) in source (%p) failed\n", caps, this);
sleep(1); sleep(1);
return FALSE; return FALSE;
} }
@ -928,7 +918,6 @@ gst_alsa_src_process (GstAlsa *this, snd_pcm_uframes_t frames)
unit = this->sample_bytes * (this->data_interleaved ? this->channels : 1); unit = this->sample_bytes * (this->data_interleaved ? this->channels : 1);
while (frames) { while (frames) {
/* g_print ("(%d) frames to process: %d\n", i++, frames); */
l = this->pads; l = this->pads;
while (l) { while (l) {
pad = GST_ALSA_PAD(l); pad = GST_ALSA_PAD(l);
@ -944,8 +933,7 @@ gst_alsa_src_process (GstAlsa *this, snd_pcm_uframes_t frames)
pad->access_addr, pad->access_addr,
MIN(frames, this->period_frames - pad->offset) * unit); MIN(frames, this->period_frames - pad->offset) * unit);
*/ */
memcpy(pad->buf + pad->offset * unit, memcpy(pad->buf + pad->offset * unit, pad->access_addr,
pad->access_addr,
MIN(frames, this->period_frames - pad->offset) * unit); MIN(frames, this->period_frames - pad->offset) * unit);
pad->offset += MIN(frames, this->period_frames - pad->offset); pad->offset += MIN(frames, this->period_frames - pad->offset);
@ -973,23 +961,26 @@ static gboolean
gst_alsa_sink_process (GstAlsa *this, snd_pcm_uframes_t frames) gst_alsa_sink_process (GstAlsa *this, snd_pcm_uframes_t frames)
{ {
guint8 *peeked; guint8 *peeked;
guint32 len, avail; guint32 len, avail, num_peeked;
GstEvent *event = NULL; GstEvent *event = NULL;
GstAlsaPad *alsa_pad;
GList *l; GList *l;
/* this is necessary because the sample_bytes will change, probably, when /* this is necessary because the sample_bytes will change, probably, when
* caps are set, which will occur after the first bytestream_peek. we caps are set, which will occur after the first bytestream_peek. we
* underestimate the amount of data we will need by peeking 'frames' only. underestimate the amount of data we will need by peeking 1 byte only. */
* */
/* FIXME: if 0 < peek_bytes < len, play the peek_bytes */
if (!this->sample_bytes) { if (!this->sample_bytes) {
if (!GST_ALSA_PAD(this->pads)->bs) alsa_pad = GST_ALSA_PAD(this->pads);
GST_ALSA_PAD(this->pads)->bs = gst_bytestream_new(GST_ALSA_PAD(this->pads)->pad);
if (gst_bytestream_peek_bytes (GST_ALSA_PAD (this->pads)->bs, &peeked, frames) != frames) { if (! alsa_pad->bs)
g_warning("could not make initial pull of %d bytes on pad %s:%s", (int)frames, GST_DEBUG_PAD_NAME(GST_ALSA_PAD(this->pads)->pad)); alsa_pad->bs = gst_bytestream_new(alsa_pad->pad);
num_peeked = gst_bytestream_peek_bytes (alsa_pad->bs, &peeked, frames);
if (num_peeked < frames) {
g_warning("could not make initial pull of %d bytes on pad %s:%s",
(int)frames,
GST_DEBUG_PAD_NAME(alsa_pad->pad));
gst_element_set_eos (GST_ELEMENT(this)); gst_element_set_eos (GST_ELEMENT(this));
return FALSE; return FALSE;
} }
@ -1004,18 +995,24 @@ gst_alsa_sink_process (GstAlsa *this, snd_pcm_uframes_t frames)
l = this->pads; l = this->pads;
while (l) { while (l) {
if (!GST_ALSA_PAD(this->pads)->bs) alsa_pad = GST_ALSA_PAD(l);
GST_ALSA_PAD(this->pads)->bs = gst_bytestream_new(GST_ALSA_PAD(this->pads)->pad);
if (gst_bytestream_peek_bytes(GST_ALSA_PAD(this->pads)->bs, &peeked, len) != len) { if (! alsa_pad->bs)
gst_bytestream_get_status(GST_ALSA_PAD(this->pads)->bs, &avail, &event); alsa_pad->bs = gst_bytestream_new(alsa_pad->pad);
num_peeked = gst_bytestream_peek_bytes(alsa_pad->bs, &peeked, len);
if (num_peeked == 0) {
gst_bytestream_get_status(alsa_pad->bs, &avail, &event);
if (event) { if (event) {
g_warning("got an event on alsasink"); g_warning("got an event on alsasink");
if (GST_EVENT_TYPE(event) == GST_EVENT_EOS) { if (GST_EVENT_TYPE(event) == GST_EVENT_EOS) {
/* really, we should just cut this pad out of the graph. let /* really, we should just cut this pad out of the graph. let
* me know when this is needed ;) me know when this is needed ;) */
* also, for sample accuracy etc, we should play avail
* bytes, but hey. */ num_peeked = gst_bytestream_peek_bytes(alsa_pad->bs, &peeked, avail);
if (num_peeked && peeked)
memcpy(alsa_pad->access_addr, peeked, avail);
gst_element_set_eos(GST_ELEMENT(this)); gst_element_set_eos(GST_ELEMENT(this));
gst_event_unref(event); gst_event_unref(event);
return TRUE; return TRUE;
@ -1027,8 +1024,13 @@ gst_alsa_sink_process (GstAlsa *this, snd_pcm_uframes_t frames)
} }
} }
memcpy(GST_ALSA_PAD(this->pads)->access_addr, peeked, len); if (num_peeked && peeked && alsa_pad->access_addr) {
gst_bytestream_flush(GST_ALSA_PAD(this->pads)->bs, len); memcpy(alsa_pad->access_addr, peeked, num_peeked);
gst_bytestream_flush(alsa_pad->bs, num_peeked);
} else {
g_warning ("error while writing %u bytes (peeked into %p) to %p",
num_peeked, peeked, alsa_pad->access_addr);
}
l = l->next; l = l->next;
} }
@ -1044,15 +1046,9 @@ gst_alsa_xrun_recovery (GstAlsa *this)
snd_pcm_status_alloca(&status); snd_pcm_status_alloca(&status);
if (this->stream == SND_PCM_STREAM_CAPTURE) {
if ((res = snd_pcm_status(this->handle, status)) < 0) { if ((res = snd_pcm_status(this->handle, status)) < 0) {
g_warning ("status error: %s", snd_strerror(res)); 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) { if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) {
struct timeval now, diff, tstamp; struct timeval now, diff, tstamp;
@ -1338,15 +1334,9 @@ gst_alsa_stop_audio(GstAlsa *this)
static void static void
gst_alsa_close_audio(GstAlsa *this) gst_alsa_close_audio(GstAlsa *this)
{ {
/* gint err; */
g_return_if_fail(this != NULL); g_return_if_fail(this != NULL);
g_return_if_fail(this->handle != NULL); g_return_if_fail(this->handle != NULL);
/* if ((err = snd_pcm_drop (this->handle)) < 0) {
g_warning("channel flush for failed: %s", snd_strerror (err));
return;
} */
snd_pcm_close(this->handle); snd_pcm_close(this->handle);
this->handle = NULL; this->handle = NULL;
@ -1370,8 +1360,6 @@ gst_alsa_get_channel_addresses (GstAlsa *this)
GST_DEBUG(0, "got %d mmap'd frames", (int)this->avail); GST_DEBUG(0, "got %d mmap'd frames", (int)this->avail);
/* g_print ("snd_pcm_mmap_begin() sets avail = %d\n", this->avail); */
l = this->pads; l = this->pads;
while (l) { while (l) {
a = &this->mmap_areas[GST_ALSA_PAD(l)->channel > 0 ? a = &this->mmap_areas[GST_ALSA_PAD(l)->channel > 0 ?
@ -1383,7 +1371,8 @@ gst_alsa_get_channel_addresses (GstAlsa *this)
for (i=0; i<this->channels; i++) { for (i=0; i<this->channels; i++) {
a = &this->mmap_areas[i]; a = &this->mmap_areas[i];
this->access_addr[i] = (char *) a->addr + ((a->first + a->step * this->offset) / 8); this->access_addr[i] = (char *) a->addr + ((a->first + a->step *
this->offset) / 8);
} }
this->mmap_open = TRUE; this->mmap_open = TRUE;
@ -1432,7 +1421,6 @@ gst_alsa_sink_silence_on_channel (GstAlsa *this, guint32 chn, guint32 nframes)
} else { } else {
memset (this->access_addr[chn], 0, nframes * this->sample_bytes); memset (this->access_addr[chn], 0, nframes * this->sample_bytes);
} }
/* mark_channel_done (chn); */
} }
/* taken directly from paul davis' memops.cc */ /* taken directly from paul davis' memops.cc */