diff options
author | Leif Johnson <leif@ambient.2y.net> | 2003-01-19 23:23:30 +0000 |
---|---|---|
committer | Leif Johnson <leif@ambient.2y.net> | 2003-01-19 23:23:30 +0000 |
commit | 85a0755988f88183e62f42c86e266229e73514de (patch) | |
tree | ce6313b646099103cc500f545c1e6f24b7764470 | |
parent | ca810029cc72ad361162629a82203e81969f6aaf (diff) |
+ 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
-rw-r--r-- | ext/alsa/gstalsa.c | 540 |
1 files changed, 264 insertions, 276 deletions
diff --git a/ext/alsa/gstalsa.c b/ext/alsa/gstalsa.c index 665375c89..c88860498 100644 --- a/ext/alsa/gstalsa.c +++ b/ext/alsa/gstalsa.c @@ -24,7 +24,7 @@ #include "gstalsa.h" /* elementfactory information */ -static GstElementDetails gst_alsa_sink_details = { +static GstElementDetails gst_alsa_sink_details = { "Alsa Sink", "Sink/Audio", "LGPL", @@ -36,7 +36,7 @@ static GstElementDetails gst_alsa_sink_details = { }; /* elementfactory information */ -static GstElementDetails gst_alsa_src_details = { +static GstElementDetails gst_alsa_src_details = { "Alsa Src", "Source/Audio", "LGPL", @@ -57,13 +57,12 @@ static GstPadTemplate *gst_alsa_src_request_pad_factory(); static GstPadTemplate *gst_alsa_sink_pad_factory(); static GstPadTemplate *gst_alsa_sink_request_pad_factory(); -static GstPad* gst_alsa_request_new_pad (GstElement *element, GstPadTemplate *templ, const - gchar *name); +static GstPad* gst_alsa_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar *name); 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 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); @@ -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_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, - unsigned int skip_bytes); + +static void memset_interleave (char *dst, char val, unsigned int bytes, unsigned int unit_bytes, unsigned int skip_bytes); /* #define _DEBUG */ #ifdef _DEBUG @@ -107,7 +105,7 @@ enum { #define GST_TYPE_ALSA_FORMAT (gst_alsa_format_get_type()) static GType -gst_alsa_format_get_type (void) +gst_alsa_format_get_type (void) { static GType type = 0; static GEnumValue *values = NULL; @@ -116,7 +114,7 @@ gst_alsa_format_get_type (void) if (values == NULL) { /* the three: for -1, 0, and the terminating NULL */ values = g_new0 (GEnumValue, len); - + for (i=0; i<len-1; i++) { values[i].value = i-1; /* UNKNOWN is -1 */ values[i].value_name = g_strdup_printf ("%d", i-1); @@ -131,10 +129,10 @@ gst_alsa_format_get_type (void) } GType -gst_alsa_get_type (void) +gst_alsa_get_type (void) { static GType alsa_type = 0; - + if (!alsa_type) { static const GTypeInfo alsa_info = { sizeof(GstAlsaClass), @@ -153,7 +151,7 @@ gst_alsa_get_type (void) } GType -gst_alsa_sink_get_type (void) +gst_alsa_sink_get_type (void) { static GType alsa_type = 0; @@ -175,10 +173,10 @@ gst_alsa_sink_get_type (void) } GType -gst_alsa_src_get_type (void) +gst_alsa_src_get_type (void) { static GType alsa_type = 0; - + if (!alsa_type) { static const GTypeInfo alsa_info = { sizeof(GstAlsaClass), @@ -200,12 +198,12 @@ static GstPadTemplate* gst_alsa_src_pad_factory(void) { static GstPadTemplate *template = NULL; - + if (!template) - template = gst_pad_template_new("src", GST_PAD_SRC, GST_PAD_SOMETIMES, + template = gst_pad_template_new("src", GST_PAD_SRC, GST_PAD_SOMETIMES, gst_caps_new("src", "audio/raw", NULL), NULL); - + return template; } @@ -213,13 +211,16 @@ static GstPadTemplate* gst_alsa_src_request_pad_factory(void) { static GstPadTemplate *template = NULL; - + if (!template) - template = gst_pad_template_new("src%d", GST_PAD_SRC, GST_PAD_REQUEST, - gst_caps_new("src-request", "audio/raw", - gst_props_new("channels", GST_PROPS_INT(1), NULL)), + template = + gst_pad_template_new("src%d", GST_PAD_SRC, GST_PAD_REQUEST, + gst_caps_new("src-request", "audio/raw", + gst_props_new("channels", + GST_PROPS_INT(1), + NULL)), NULL); - + return template; } @@ -227,12 +228,12 @@ static GstPadTemplate* gst_alsa_sink_pad_factory(void) { static GstPadTemplate *template = NULL; - + if (!template) - template = gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_SOMETIMES, + template = gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_SOMETIMES, gst_caps_new("sink", "audio/raw", NULL), NULL); - + return template; } @@ -240,13 +241,16 @@ static GstPadTemplate* gst_alsa_sink_request_pad_factory(void) { static GstPadTemplate *template = NULL; - + if (!template) - template = gst_pad_template_new("sink%d", GST_PAD_SINK, GST_PAD_REQUEST, - gst_caps_new("sink-request", "audio/raw", - gst_props_new("channels", GST_PROPS_INT(1), NULL)), - NULL); - + template = + gst_pad_template_new("sink%d", GST_PAD_SINK, GST_PAD_REQUEST, + gst_caps_new("sink-request", "audio/raw", + gst_props_new("channels", + GST_PROPS_INT(1), + NULL)), + NULL); + return template; } @@ -255,16 +259,16 @@ gst_alsa_class_init(GstAlsaClass *klass) { GObjectClass *object_class; GstElementClass *element_class; - + object_class = (GObjectClass *)klass; element_class = (GstElementClass *)klass; - + if (parent_class == NULL) parent_class = g_type_class_ref(GST_TYPE_ELEMENT); - + object_class->get_property = gst_alsa_get_property; object_class->set_property = gst_alsa_set_property; - + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_DEVICE, g_param_spec_string("device","Device","Alsa device, as defined in an asoundrc", "default", @@ -292,9 +296,9 @@ gst_alsa_class_init(GstAlsaClass *klass) g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_DEBUG, g_param_spec_boolean("debug","Debug","Set to TRUE to output PCM state info", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); - + element_class->change_state = gst_alsa_change_state; - + element_class->request_new_pad = gst_alsa_request_new_pad; } @@ -303,9 +307,9 @@ gst_alsa_init(GstAlsa *this) { /* init values */ this->handle = NULL; - + GST_FLAG_SET(this, GST_ELEMENT_THREAD_SUGGESTED); - + if (G_OBJECT_TYPE(this) == GST_TYPE_ALSA_SRC) { this->stream = SND_PCM_STREAM_CAPTURE; this->pads = g_list_append(NULL, g_new0(GstAlsaPad, 1)); @@ -320,14 +324,14 @@ gst_alsa_init(GstAlsa *this) this->format = SND_PCM_FORMAT_UNKNOWN; /* we don't know until caps are * set */ } - + GST_ALSA_PAD(this->pads)->channel = -1; - + this->data_interleaved = TRUE; - + 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); } @@ -339,20 +343,20 @@ gst_alsa_request_new_pad (GstElement *element, GstPadTemplate *templ, const gcha gchar *newname; GList *l; GstAlsaPad *pad; - + g_return_val_if_fail ((this = GST_ALSA(element)), NULL); - + /* you can't request a pad if the non-request pad is connected */ g_return_val_if_fail (this->data_interleaved == FALSE || - this->pads == NULL || + this->pads == NULL || GST_ALSA_PAD(this->pads) == NULL || GST_ALSA_PAD(this->pads)->pad == NULL || - GST_PAD_PEER(GST_ALSA_PAD(this->pads)->pad) == NULL, + GST_PAD_PEER(GST_ALSA_PAD(this->pads)->pad) == NULL, NULL); - + if (name) { channel = atoi (name + (strchr (templ->name_template, '%') - templ->name_template)); - + l = this->pads; while (l) { if (GST_ALSA_PAD(l)->channel == channel) { @@ -372,22 +376,22 @@ gst_alsa_request_new_pad (GstElement *element, GstPadTemplate *templ, const gcha } newname = g_strdup_printf (templ->name_template, channel); } - + pad = g_new0(GstAlsaPad, 1); pad->channel = channel; pad->pad = gst_pad_new_from_template (templ, newname); 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) { gst_element_remove_pad (GST_ELEMENT (this), GST_ALSA_PAD(this->pads)->pad); g_free (GST_ALSA_PAD(this->pads)); g_list_free (this->pads); this->pads = NULL; } - + this->pads = g_list_append(this->pads, pad); - + /* FIXME: allow interleaved access (for hw:N,M access on consumer hardware) */ if (this->data_interleaved) { @@ -396,7 +400,7 @@ gst_alsa_request_new_pad (GstElement *element, GstPadTemplate *templ, const gcha } else { this->channels = MAX(this->channels, pad->channel + 1); } - + return pad->pad; } @@ -404,7 +408,7 @@ static void gst_alsa_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GstAlsa *this; - + this = (GstAlsa *)object; switch (prop_id) { case ARG_DEVICE: @@ -436,10 +440,10 @@ gst_alsa_set_property(GObject *object, guint prop_id, const GValue *value, GPara GST_DEBUG(0, "Unknown arg"); return; } - + if (GST_STATE(this) == GST_STATE_NULL) return; - + if (GST_FLAG_IS_SET(this, GST_ALSA_RUNNING)) { gst_alsa_stop_audio(this); gst_alsa_set_params(this); @@ -489,25 +493,33 @@ gst_alsa_change_state(GstElement *element) { GstAlsa *this; guint chn; - + GList *l; + g_return_val_if_fail(element != NULL, FALSE); this = GST_ALSA (element); - + switch (GST_STATE_PENDING(element)) { case GST_STATE_NULL: 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)) - gst_alsa_close_audio((GstAlsa *)element); - /* FIXME: clean up bytestreams, etc */ + gst_alsa_close_audio(this); + + l = this->pads; + while(l) { + if (GST_ALSA_PAD(l)->bs) + gst_bytestream_destroy(GST_ALSA_PAD(l)->bs); + l = l->next; + } + break; - + case GST_STATE_READY: break; - + case GST_STATE_PAUSED: 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; if (GST_FLAG_IS_SET(element, GST_ALSA_RUNNING)) { if (this->stream == SND_PCM_STREAM_PLAYBACK) { @@ -518,14 +530,14 @@ gst_alsa_change_state(GstElement *element) gst_alsa_stop_audio((GstAlsa *)element); } break; - + case GST_STATE_PLAYING: 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; break; } - + if (GST_ELEMENT_CLASS(parent_class)->change_state) return GST_ELEMENT_CLASS(parent_class)->change_state(element); @@ -542,7 +554,7 @@ gst_alsa_parse_caps (GstAlsa *this, GstCaps *caps) if (!gst_caps_get_string (caps, "format", &format_name)) return FALSE; - + if (format_name == NULL) { return FALSE; } else if (strcmp(format_name, "int")==0) { @@ -554,7 +566,7 @@ gst_alsa_parse_caps (GstAlsa *this, GstCaps *caps) "signed", &sign, NULL)) return FALSE; - + if (law == 0) { if (width == 8) { if (sign == TRUE) { @@ -617,7 +629,7 @@ gst_alsa_parse_caps (GstAlsa *this, GstCaps *caps) if (!gst_caps_get_string (caps, "layout", &layout)) return FALSE; - + if (strcmp(layout, "gfloat")==0) { format = SND_PCM_FORMAT_FLOAT; } else { @@ -627,7 +639,7 @@ gst_alsa_parse_caps (GstAlsa *this, GstCaps *caps) } else { return FALSE; } - + this->format = format; if (!gst_caps_get (caps, "rate", &this->rate, @@ -639,7 +651,7 @@ gst_alsa_parse_caps (GstAlsa *this, GstCaps *caps) this->channels = channels; else if (channels != 1) return FALSE; - + return TRUE; } @@ -650,9 +662,9 @@ gst_alsa_caps (GstAlsa *this) gint law, endianness, width, depth; gboolean sign; GstProps *props; - + g_return_val_if_fail (this != NULL && this->handle != NULL, NULL); - + if (this->format == SND_PCM_FORMAT_FLOAT) { props = gst_props_new ("format", GST_PROPS_STRING ("float"), "layout", GST_PROPS_STRING ("gfloat"), @@ -745,7 +757,7 @@ gst_alsa_caps (GstAlsa *this) "depth", GST_PROPS_INT (depth), NULL); } - + return gst_caps_new ("alsasrc", "audio/raw", props); } @@ -753,7 +765,7 @@ gst_alsa_caps (GstAlsa *this) * Negotiates the caps, "borrowed" from gstosssink.c */ GstPadLinkReturn -gst_alsa_connect(GstPad *pad, GstCaps *caps) +gst_alsa_link(GstPad *pad, GstCaps *caps) { GstAlsa *this; gboolean need_mmap; @@ -762,105 +774,81 @@ gst_alsa_connect(GstPad *pad, GstCaps *caps) g_return_val_if_fail (pad != NULL, GST_PAD_LINK_REFUSED); this = GST_ALSA(gst_pad_get_parent(pad)); - + if (GST_CAPS_IS_FIXED (caps)) { if (this->handle == NULL) if (!gst_alsa_open_audio(this)) return GST_PAD_LINK_REFUSED; - + if (gst_alsa_parse_caps(this, caps)) { need_mmap = this->mmap_open; - + /* sync the params */ if (GST_FLAG_IS_SET(this, GST_ALSA_RUNNING)) gst_alsa_stop_audio(this); - + if (GST_FLAG_IS_SET(this, GST_ALSA_OPEN)) gst_alsa_close_audio(this); - + /* FIXME send out another caps if nego fails */ - + if (!gst_alsa_open_audio(this)) return GST_PAD_LINK_REFUSED; - + if (!gst_alsa_start_audio(this)) return GST_PAD_LINK_REFUSED; - + if (need_mmap && !gst_alsa_get_channel_addresses(this)) return GST_PAD_LINK_REFUSED; - + return GST_PAD_LINK_OK; } - + return GST_PAD_LINK_REFUSED; } - + 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 gst_alsa_loop (GstElement *element) { - struct pollfd pfd; - gboolean xrun_detected; guint32 i; GstAlsa *this = GST_ALSA(element); - + 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 { - xrun_detected = FALSE; - - if (poll (&pfd, 1, 1000) < 0) { + if (snd_pcm_wait (this->handle, 1000) < 0) { if (errno == EINTR) { /* this happens mostly when run * under gdb, or when exiting due to a signal */ g_print ("EINTR\n"); - continue; + if (gst_element_interrupt (element)) + break; + else + continue; } - - g_warning("poll call failed (%s)", strerror(errno)); + + g_warning("error waiting for alsa pcm: (%d: %s)", errno, strerror(errno)); 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); 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); return; } } - - if (xrun_detected) { - gst_alsa_xrun_recovery (this); - this->avail = 0; - } /* round down to nearest period_frames avail */ this->avail -= this->avail % this->period_frames; @@ -875,7 +863,7 @@ gst_alsa_loop (GstElement *element) g_error("could not get channels"); return; } - + if (this->mute && this->stream == SND_PCM_STREAM_PLAYBACK) { for (i = 0; i < this->channels; i++) { if (this->mute & (1<<i)) { @@ -883,17 +871,20 @@ gst_alsa_loop (GstElement *element) } } } - + if (!this->process(this, this->avail)) { g_warning("alsa: something happened while processing audio"); return; } - + /* we could have released the mmap regions on a state change */ if (this->mmap_open) gst_alsa_release_channel_addresses(this); } - gst_element_yield (element); + + if (gst_element_interrupt (element)) + break; + } while (TRUE); } @@ -905,51 +896,48 @@ gst_alsa_src_process (GstAlsa *this, snd_pcm_uframes_t frames) GstAlsaPad *pad = NULL; GstCaps *caps; gint unit; -/* gint i=0; */ - + static gboolean caps_set = FALSE; - + if (!caps_set) { /* let's get on the caps-setting merry-go-round! */ caps = gst_alsa_caps(this); l = this->pads; while (l) { 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); return FALSE; } - + l = l->next; } caps_set = TRUE; } - + unit = this->sample_bytes * (this->data_interleaved ? this->channels : 1); - + while (frames) { -/* g_print ("(%d) frames to process: %d\n", i++, frames); */ l = this->pads; while (l) { pad = GST_ALSA_PAD(l); - + if (!pad->buf) { pad->buf = g_malloc(this->period_frames * unit); /* g_print ("created buffer %p of size %d\n", pad->buf, this->period_frames * unit); */ } /* g_print ("pad->buf = %p, offset = %d\n", pad->buf, pad->offset); - g_print ("about to memcpy(%p, %p, %d)\n", + g_print ("about to memcpy(%p, %p, %d)\n", pad->buf + pad->offset * unit, - pad->access_addr, + pad->access_addr, MIN(frames, this->period_frames - pad->offset) * unit); */ - memcpy(pad->buf + pad->offset * unit, - pad->access_addr, + memcpy(pad->buf + pad->offset * unit, pad->access_addr, MIN(frames, this->period_frames - pad->offset) * unit); - + pad->offset += MIN(frames, this->period_frames - pad->offset); - + if (pad->offset >= this->period_frames) { g_assert(pad->offset <= this->period_frames); buf = gst_buffer_new(); @@ -965,7 +953,7 @@ gst_alsa_src_process (GstAlsa *this, snd_pcm_uframes_t frames) frames -= MIN(frames, this->period_frames - pad->offset); /* shouldn't */ /* matter which pad, in theory (tm) */ } - + return TRUE; } @@ -973,49 +961,58 @@ static gboolean gst_alsa_sink_process (GstAlsa *this, snd_pcm_uframes_t frames) { guint8 *peeked; - guint32 len, avail; + guint32 len, avail, num_peeked; GstEvent *event = NULL; + GstAlsaPad *alsa_pad; GList *l; - + /* this is necessary because the sample_bytes will change, probably, when - * caps are set, which will occur after the first bytestream_peek. we - * underestimate the amount of data we will need by peeking 'frames' only. - * */ - - /* FIXME: if 0 < peek_bytes < len, play the peek_bytes */ + caps are set, which will occur after the first bytestream_peek. we + underestimate the amount of data we will need by peeking 1 byte only. */ if (!this->sample_bytes) { - if (!GST_ALSA_PAD(this->pads)->bs) - 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) { - 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 = GST_ALSA_PAD(this->pads); + + if (! alsa_pad->bs) + 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)); return FALSE; } - + if (!this->sample_bytes) { g_critical ("alsa plugin requires a pipeline that can adequately set caps."); return FALSE; } } - + len = frames * this->channels * this->sample_bytes; - + l = this->pads; while (l) { - if (!GST_ALSA_PAD(this->pads)->bs) - 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) { - gst_bytestream_get_status(GST_ALSA_PAD(this->pads)->bs, &avail, &event); + alsa_pad = GST_ALSA_PAD(l); + + if (! alsa_pad->bs) + 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) { g_warning("got an event on alsasink"); if (GST_EVENT_TYPE(event) == GST_EVENT_EOS) { /* really, we should just cut this pad out of the graph. let - * me know when this is needed ;) - * also, for sample accuracy etc, we should play avail - * bytes, but hey. */ + me know when this is needed ;) */ + + 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_event_unref(event); return TRUE; @@ -1026,13 +1023,18 @@ gst_alsa_sink_process (GstAlsa *this, snd_pcm_uframes_t frames) g_assert_not_reached(); } } - - memcpy(GST_ALSA_PAD(this->pads)->access_addr, peeked, len); - gst_bytestream_flush(GST_ALSA_PAD(this->pads)->bs, len); - - l=l->next; + + if (num_peeked && peeked && alsa_pad->access_addr) { + 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; } - + return TRUE; } @@ -1044,16 +1046,10 @@ gst_alsa_xrun_recovery (GstAlsa *this) 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 ((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); @@ -1061,10 +1057,10 @@ gst_alsa_xrun_recovery (GstAlsa *this) 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 @@ -1074,23 +1070,23 @@ gst_alsa_set_params (GstAlsa *this) snd_pcm_hw_params_t *hw_param; snd_pcm_access_mask_t *mask; gint ret; - + g_return_val_if_fail(this != NULL, FALSE); g_return_val_if_fail(this->handle != NULL, FALSE); - - g_print("Preparing channel: %s %dHz, %d channels\n", - snd_pcm_format_name(this->format), + + g_print("Preparing channel: %s %dHz, %d channels\n", + snd_pcm_format_name(this->format), this->rate, this->channels); - + snd_pcm_hw_params_alloca(&hw_param); snd_pcm_sw_params_alloca(&sw_param); - + ret = snd_pcm_hw_params_any(this->handle, hw_param); if (ret < 0) { g_warning("Broken configuration for this PCM: no configurations available"); return FALSE; } - + if ((ret = snd_pcm_hw_params_set_periods_integer (this->handle, hw_param)) < 0) { g_warning("cannot restrict period size to integral value."); return FALSE; @@ -1098,17 +1094,17 @@ gst_alsa_set_params (GstAlsa *this) mask = alloca(snd_pcm_access_mask_sizeof()); snd_pcm_access_mask_none(mask); - + if (this->data_interleaved) snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED); - + snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED); ret = snd_pcm_hw_params_set_access_mask(this->handle, hw_param, mask); if (ret < 0) { g_warning("the gstreamer alsa plugin does not support your hardware."); return FALSE; } - + if (this->format != SND_PCM_FORMAT_UNKNOWN) { ret = snd_pcm_hw_params_set_format(this->handle, hw_param, this->format); if (ret < 0) { @@ -1117,14 +1113,14 @@ gst_alsa_set_params (GstAlsa *this) } this->sample_bytes = snd_pcm_format_physical_width(this->format) / 8; } - + ret = snd_pcm_hw_params_set_channels(this->handle, hw_param, this->channels); if (ret < 0) { g_warning("Channels count (%d) not available: %s", this->channels, snd_strerror(ret)); return FALSE; } this->channels = snd_pcm_hw_params_get_channels(hw_param); - + if (this->rate) { ret = snd_pcm_hw_params_set_rate(this->handle, hw_param, this->rate, 0); if (ret < 0) { @@ -1132,7 +1128,7 @@ gst_alsa_set_params (GstAlsa *this) return FALSE; } } - + if (this->period_count) { ret = snd_pcm_hw_params_set_periods (this->handle, hw_param, this->period_count, 0); if (ret < 0) { @@ -1140,7 +1136,7 @@ gst_alsa_set_params (GstAlsa *this) return FALSE; } } - + if (this->period_frames) { ret = snd_pcm_hw_params_set_period_size (this->handle, hw_param, this->period_frames, 0); if (ret < 0) { @@ -1148,7 +1144,7 @@ gst_alsa_set_params (GstAlsa *this) return FALSE; } } - + if (this->buffer_frames) { ret = snd_pcm_hw_params_set_buffer_size (this->handle, hw_param, this->buffer_frames); if (ret < 0) { @@ -1156,14 +1152,14 @@ gst_alsa_set_params (GstAlsa *this) return FALSE; } } - + ret = snd_pcm_hw_params(this->handle, hw_param); if (ret < 0) { g_warning("could not set hw params: %s", snd_strerror(ret)); snd_pcm_hw_params_dump(hw_param, this->out); return FALSE; } - + if (!this->rate) this->rate = snd_pcm_hw_params_get_rate(hw_param, 0); if (!this->format) @@ -1176,7 +1172,7 @@ gst_alsa_set_params (GstAlsa *this) this->buffer_frames = snd_pcm_hw_params_get_buffer_size(hw_param); if (this->buffer_frames != this->period_count * this->period_frames) g_critical ("buffer size != period size * number of periods, unexpected things may happen!"); - + snd_pcm_sw_params_current (this->handle, sw_param); ret = snd_pcm_sw_params_set_start_threshold (this->handle, sw_param, ~0U); @@ -1184,31 +1180,31 @@ gst_alsa_set_params (GstAlsa *this) g_warning("could not set start mode: %s", snd_strerror(ret)); return FALSE; } - + 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; } - + ret = snd_pcm_sw_params_set_silence_threshold (this->handle, sw_param, 0); if (ret < 0) { g_warning("could not set silence threshold: %s", snd_strerror(ret)); return FALSE; } - + ret = snd_pcm_sw_params_set_silence_size (this->handle, sw_param, this->buffer_frames); if (ret < 0) { g_warning("could not set silence size: %s", snd_strerror(ret)); return FALSE; } - + ret = snd_pcm_sw_params_set_avail_min (this->handle, sw_param, this->period_frames); if (ret < 0) { g_warning("could not set avail min: %s", snd_strerror(ret)); return FALSE; } - + ret = snd_pcm_sw_params (this->handle, sw_param); if (ret < 0) { g_warning("could not set sw_params: %s", snd_strerror(ret)); @@ -1217,10 +1213,10 @@ gst_alsa_set_params (GstAlsa *this) if (this->debug) snd_pcm_dump(this->handle, this->out); - + this->access_interleaved = !(snd_pcm_hw_params_get_access (hw_param) == SND_PCM_ACCESS_MMAP_NONINTERLEAVED); - + if (this->access_interleaved) { this->interleave_unit = this->sample_bytes; this->interleave_skip = this->interleave_unit * this->channels; @@ -1228,11 +1224,11 @@ gst_alsa_set_params (GstAlsa *this) this->interleave_unit = 0; /* not used */ this->interleave_skip = this->sample_bytes; } - + if (this->access_addr) g_free (this->access_addr); this->access_addr = g_new0 (char*, this->channels); - + return TRUE; } @@ -1244,28 +1240,28 @@ gst_alsa_open_audio(GstAlsa *this) if (this->handle) gst_alsa_close_audio(this); - + g_print("Opening alsa device \"%s\" for %s...\n", this->device, this->stream==SND_PCM_STREAM_PLAYBACK ? "playback" : "capture"); - + ret = snd_output_stdio_attach(&this->out, stdout, 0); if (ret < 0) { g_print("error opening log output: %s\n", snd_strerror(ret)); return FALSE; } - + /* blocking i/o */ if ((ret = snd_pcm_open(&this->handle, this->device, this->stream, 0))) { - g_print("error opening pcm device %s: %s\n", + g_print("error opening pcm device %s: %s\n", this->device, snd_strerror(ret)); return FALSE; } - + if (gst_alsa_set_params(this) == FALSE) { gst_alsa_close_audio(this); return FALSE; } - + GST_FLAG_SET(this, GST_ALSA_OPEN); return TRUE; } @@ -1276,40 +1272,40 @@ gst_alsa_start_audio(GstAlsa *this) { gint err; guint32 chn; - + g_return_val_if_fail(this != NULL, FALSE); g_return_val_if_fail(this->handle != NULL, FALSE); - + if ((err = snd_pcm_prepare (this->handle)) < 0) { g_warning("channel prepare failed: %s", snd_strerror (err)); return FALSE; } - + this->avail = snd_pcm_avail_update (this->handle); - + if (this->stream == SND_PCM_STREAM_PLAYBACK && this->avail != this->buffer_frames) { g_warning ("full buffer not available at start"); return FALSE; } - + if (!gst_alsa_get_channel_addresses (this)) { return FALSE; } - + if (this->stream == SND_PCM_STREAM_PLAYBACK) { for (chn = 0; chn < this->channels; chn++) { gst_alsa_sink_silence_on_channel (this, chn, this->buffer_frames); } } - + gst_alsa_release_channel_addresses (this); - + if ((err = snd_pcm_start (this->handle)) < 0) { g_warning("could not start audio: %s", snd_strerror (err)); return FALSE; } - + GST_FLAG_SET(this, GST_ALSA_RUNNING); return TRUE; } @@ -1319,36 +1315,30 @@ gst_alsa_stop_audio(GstAlsa *this) { gint err; g_assert(this != NULL); - + g_return_if_fail(this != NULL); g_return_if_fail(this->handle != NULL); if (this->mmap_open) gst_alsa_release_channel_addresses (this); - - if (this->stream == SND_PCM_STREAM_PLAYBACK && + + if (this->stream == SND_PCM_STREAM_PLAYBACK && (err = snd_pcm_drop (this->handle)) < 0) { g_warning("channel flush failed: %s", snd_strerror (err)); return; } - + GST_FLAG_UNSET(this, GST_ALSA_RUNNING); } static void gst_alsa_close_audio(GstAlsa *this) { -/* gint err; */ g_return_if_fail(this != 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); - + this->handle = NULL; GST_FLAG_UNSET(this, GST_ALSA_OPEN); @@ -1360,18 +1350,16 @@ gst_alsa_get_channel_addresses (GstAlsa *this) guint32 err, i; const snd_pcm_channel_area_t *a; GList *l; - + g_return_val_if_fail (this->mmap_open == FALSE, FALSE); - + if ((err = snd_pcm_mmap_begin (this->handle, &this->mmap_areas, &this->offset, &this->avail)) < 0) { g_warning("gstalsa: mmap failed: %s", snd_strerror(err)); return FALSE; } - + 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; while (l) { a = &this->mmap_areas[GST_ALSA_PAD(l)->channel > 0 ? @@ -1380,14 +1368,15 @@ gst_alsa_get_channel_addresses (GstAlsa *this) this->offset) / 8); l = l->next; } - + for (i=0; i<this->channels; 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; - + return TRUE; } @@ -1396,35 +1385,35 @@ gst_alsa_release_channel_addresses (GstAlsa *this) { guint32 err, i; GList *l; - + g_return_if_fail (this->mmap_open == TRUE); - + GST_DEBUG(0, "releasing mmap'd data region: %d frames", (int)this->avail); - + if ((err = snd_pcm_mmap_commit (this->handle, this->offset, this->avail)) < 0) { g_warning("gstalsa: mmap commit failed: %s", snd_strerror(err)); return; } - + l = this->pads; while (l) { GST_ALSA_PAD(l)->access_addr = NULL; l = l->next; } - + for (i=0; i<this->channels; i++) { this->access_addr[i] = NULL; } - + this->mmap_open = FALSE; this->avail=0; } -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) { if (this->access_interleaved) { - memset_interleave + memset_interleave (this->access_addr[chn], 0, nframes * this->sample_bytes, this->interleave_unit, @@ -1432,14 +1421,13 @@ gst_alsa_sink_silence_on_channel (GstAlsa *this, guint32 chn, guint32 nframes) } else { memset (this->access_addr[chn], 0, nframes * this->sample_bytes); } -/* mark_channel_done (chn); */ } /* taken directly from paul davis' memops.cc */ static void -memset_interleave (char *dst, char val, unsigned int bytes, - unsigned int unit_bytes, - unsigned int skip_bytes) +memset_interleave (char *dst, char val, unsigned int bytes, + unsigned int unit_bytes, + unsigned int skip_bytes) { switch (unit_bytes) { case 1: @@ -1455,7 +1443,7 @@ memset_interleave (char *dst, char val, unsigned int bytes, bytes -= 2; } break; - case 4: + case 4: while (bytes) { *((int *) dst) = (int) val; dst += skip_bytes; @@ -1469,28 +1457,28 @@ static gboolean plugin_init (GModule *module, GstPlugin *plugin) { GstElementFactory *factory; - + if (!gst_library_load ("gstbytestream")) return FALSE; - - factory = gst_element_factory_new ("alsasrc", GST_TYPE_ALSA_SRC, + + factory = gst_element_factory_new ("alsasrc", GST_TYPE_ALSA_SRC, &gst_alsa_src_details); g_return_val_if_fail (factory != NULL, FALSE); gst_element_factory_add_pad_template (factory, gst_alsa_src_pad_factory ()); - gst_element_factory_add_pad_template (factory, + gst_element_factory_add_pad_template (factory, gst_alsa_src_request_pad_factory ()); gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); - - factory = gst_element_factory_new ("alsasink", GST_TYPE_ALSA_SINK, + + factory = gst_element_factory_new ("alsasink", GST_TYPE_ALSA_SINK, &gst_alsa_sink_details); g_return_val_if_fail (factory != NULL, FALSE); gst_element_factory_add_pad_template (factory, gst_alsa_sink_pad_factory()); - gst_element_factory_add_pad_template (factory, + gst_element_factory_add_pad_template (factory, gst_alsa_sink_request_pad_factory()); gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); - + gst_plugin_set_longname(plugin, "ALSA plugin library"); - + return TRUE; } |