+ fixing 100 % cpu usage bug (bug #103658) + cleaning up some of the FIXMEs, mostly...
authorLeif Johnson <leif@ambient.2y.net>
Sun, 19 Jan 2003 23:23:30 +0000 (23:23 +0000)
committerLeif Johnson <leif@ambient.2y.net>
Sun, 19 Jan 2003 23:23:30 +0000 (23:23 +0000)
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

ext/alsa/gstalsa.c

index 665375c..c888604 100644 (file)
@@ -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;
 }