[gst-cvs] CVS: gstreamer/plugins/alsa alsa.c,1.3,1.4

Andy Wingo wingo at users.sourceforge.net
Thu Oct 18 20:43:14 PDT 2001


Update of /cvsroot/gstreamer/gstreamer/plugins/alsa
In directory usw-pr-cvs1:/tmp/cvs-serv24953

Modified Files:
	alsa.c 
Log Message:
miscellaneous bug fixes. still no resolution on the alsasrc cpu-eating issue, 
although i'm working on that.


Index: alsa.c
===================================================================
RCS file: /cvsroot/gstreamer/gstreamer/plugins/alsa/alsa.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- alsa.c	2001/10/17 08:59:12	1.3
+++ alsa.c	2001/10/19 03:42:38	1.4
@@ -261,32 +261,35 @@
     /* init values */
     this->handle = NULL;
     
-    /* we're not going to set default values for things that caps takes care of
-     * because it could be that the device does not support them (eg, my hw:0,0
-     * device does not support mono operation) */
-    /* actually. after having tried that, there's no real way to wait on
-     * capsnego, i don't think, as you pull pads in the middle of the loop
-     * function, and only after that can you get caps. so if you want to use
-     * hw:N,M, you have to set all properties explicitly */
-    this->format = SND_PCM_FORMAT_S16;
+    /* i tried to trim this down to only the essentials needed for proper
+     * functioning; we'll be setting this values 'in earnest' when caps come */
+    
     this->rate = 44100;
-    this->channels = 1;
+    
+    /* i wish i could leave these to defaults, but when caps come and typically
+     * the number of channels doubles, you can't have the default count and size
+     * that alsa gives you from the pre-caps setup apply to the post-caps setup
+     * due to internal hw buffer size restrictions. these values seem to be
+     * pretty common, though, although not pushing latency limits */
+    this->period_count = 3;
+    this->period_frames = 4096;
     
     this->device = g_strdup("default");
-    this->period_count  = 4; /* 'number of fragments' in oss-speak */
-    this->period_frames = 256;
-    this->buffer_frames = this->period_count * this->period_frames;
     
-//    GST_FLAG_SET(this, GST_ELEMENT_THREAD_SUGGESTED);
+    /* is this right? */
+    GST_FLAG_SET(this, GST_ELEMENT_THREAD_SUGGESTED);
     
     if (G_OBJECT_TYPE(this) == GST_TYPE_ALSA_SRC) {
         this->stream = SND_PCM_STREAM_CAPTURE;
         this->pad = gst_pad_new_from_template(gst_alsa_src_pad_factory(), "src");
         this->process = gst_alsa_src_interleaved_process;
+        this->format = SND_PCM_FORMAT_S16; /* native endian */
     } else if (G_OBJECT_TYPE(this) == GST_TYPE_ALSA_SINK) {
         this->stream = SND_PCM_STREAM_PLAYBACK;
         this->pad = gst_pad_new_from_template(gst_alsa_sink_pad_factory(), "sink");
         this->process = gst_alsa_sink_interleaved_process;
+        this->format = SND_PCM_FORMAT_UNKNOWN; /* we don't know until caps are
+                                                * set */
     } else {
         g_print("you cannot instantiate an object of type gstalsa. use alsasink or alsasrc instead.\n");
         G_BREAKPOINT();
@@ -295,11 +298,6 @@
     
     gst_element_add_pad(GST_ELEMENT(this), this->pad);
     
-    // multichannel hw is still on the todo list:
-    //  * implement a GstAlsaProcessFunc for sink and src
-    //  * make request/non-request pads mutually exclusive
-    // also, if NONINTERLEAVED is all we got, multichannel is mandatory.
-    
     gst_pad_set_negotiate_function(this->pad, gst_alsa_negotiate);
     gst_element_set_loop_function(GST_ELEMENT(this), gst_alsa_loop);
 }
@@ -313,12 +311,10 @@
     
     g_return_val_if_fail (this = GST_ALSA(element), NULL);
     
-    g_return_val_if_fail (templ->direction == GST_PAD_SINK, NULL);
-    
     /* if the "sink" pad is already connected, you can't request individual
      * channels */
-    g_return_val_if_fail (!this->pad || GST_PAD_PEER(this->pad) == NULL, NULL);
-
+    g_return_val_if_fail (this->pad == NULL || GST_PAD_PEER(this->pad) == NULL, NULL);
+    
     if (name) {
         if (sscanf(name, templ->name_template, &channel) != 1) {
             g_warning("invalid pad name %s (trying to fit with %s)", name,
@@ -347,9 +343,13 @@
     gst_element_add_pad (GST_ELEMENT (this), this->req_pads[channel]);
     gst_pad_set_negotiate_function(this->req_pads[channel], gst_alsa_negotiate);
     
-    gst_element_remove_pad (GST_ELEMENT (this), this->pad);
-    this->pad = NULL;
+    if (this->pad) {
+        gst_element_remove_pad (GST_ELEMENT (this), this->pad);
+        this->pad = NULL;
+    }
     
+    // FIXME: allow interleaved access (for hw:N,M access on consumer hardware)
+    
     if (this->stream == SND_PCM_STREAM_PLAYBACK) {
         this->process = gst_alsa_sink_noninterleaved_process;
     } else {
@@ -363,56 +363,45 @@
 gst_alsa_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
 {
     GstAlsa *this;
-    gboolean prepare_channel = FALSE;
-
+    
     this = (GstAlsa *)object;
     switch (prop_id) {
     case ARG_DEVICE:
         if (this->device)
             g_free (this->device);
         this->device = g_strdup(g_value_get_string (value));
-        prepare_channel = TRUE;
         break;
     case ARG_FORMAT:
         this->format = g_value_get_int (value);
-        prepare_channel = TRUE;
         break;
     case ARG_CHANNELS:
         this->channels = g_value_get_int (value);
-        prepare_channel = TRUE;
         break;
     case ARG_FREQUENCY:
         this->rate = g_value_get_int (value);
-        prepare_channel = TRUE;
         break;
     case ARG_PERIODCOUNT:
         this->period_count = g_value_get_int (value);
         this->buffer_frames = this->period_count * this->period_frames;
-        prepare_channel = TRUE;
         break;
     case ARG_PERIODFRAMES:
         this->period_frames = g_value_get_int (value);
         this->buffer_frames = this->period_count * this->period_frames;
-        prepare_channel = TRUE;
         break;
     default:
         GST_DEBUG(0, "Unknown arg");
-        break;
+        return;
     }
     
-    if (GST_STATE(this)==GST_STATE_NULL)
+    if (GST_STATE(this) == GST_STATE_NULL)
         return;
     
-    if (prepare_channel) {
-        /* if we are playing, we need to do some additional stuff */
-        if (GST_FLAG_IS_SET(this, GST_ALSA_RUNNING))
-            snd_pcm_drop(this->handle);
-        
+    if (GST_FLAG_IS_SET(this, GST_ALSA_RUNNING)) {
+        gst_alsa_stop_audio(this);
         gst_alsa_set_params(this);
-        
-        if (GST_FLAG_IS_SET(this, GST_ALSA_RUNNING))
-            snd_pcm_prepare(this->handle);
-        /* ready to play again */
+        gst_alsa_start_audio(this);
+    } else {
+        gst_alsa_set_params(this);
     }
 }
 
@@ -461,6 +450,7 @@
             gst_alsa_stop_audio((GstAlsa *)element);
         if (GST_FLAG_IS_SET(element, GST_ALSA_OPEN))
             gst_alsa_close_audio((GstAlsa *)element);
+        /* FIXME: clean up bytestreams, etc */
     } else if (GST_STATE_PENDING(element) == GST_STATE_PLAYING) {
         if (GST_FLAG_IS_SET(element, GST_ALSA_OPEN) == FALSE)
             if (gst_alsa_open_audio((GstAlsa *)element) == FALSE)
@@ -634,112 +624,113 @@
 gst_alsa_loop (GstElement *element)
 {
     struct pollfd pfd;
-//    int err;
-//    snd_pcm_sframes_t contiguous = 0;
-//    snd_pcm_sframes_t capture_avail = 0;
-//    snd_pcm_sframes_t playback_avail = 0;
-//    snd_pcm_uframes_t playback_offset = 0;
     gboolean xrun_detected;
-    guint32 x;
-    GstAlsa *this = (GstAlsa*) element;
+    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) {
-        snd_pcm_poll_descriptors (this->handle, &pfd, 1);
         pfd.events = POLLOUT | POLLERR;
+    } else {
+        pfd.events = POLLIN | POLLERR;
     }
     
     do {
-        if (this->stream == SND_PCM_STREAM_PLAYBACK) {
-            if (poll (&pfd, 1, 1000) < 0) {
-                if (errno == EINTR) {
-                    // this happens mostly when run
-                    // under gdb, or when exiting due to a signal
-                    continue;
-                }
-                
-                g_warning("poll call failed (%s)", strerror(errno));
-                return;
-            }
-            
-            if (pfd.revents & POLLERR) {
-                g_warning("alsa: poll reports error.");
-                return;
-            }
-            
-            if (pfd.revents == 0) {
-                // timed out, such as when the device is paused
+        if (poll (&pfd, 1, 1000) < 0) {
+            if (errno == EINTR) {
+                /* this happens mostly when run
+                 * under gdb, or when exiting due to a signal */
+                g_print ("EINTR\n");
                 continue;
             }
+            
+            g_warning("poll call failed (%s)", strerror(errno));
+            return;
+        }
+        
+        if (pfd.revents & POLLERR) {
+            g_warning("alsa: poll reports error.");
+            return;
         }
         
+        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;
+        }
+        
         xrun_detected = FALSE;
         
         this->avail = snd_pcm_avail_update (this->handle);
+//        g_print ("snd_pcm_avail_update() = %d\n", this->avail);
         
-        while (this->avail < this->period_frames) {
-            if (this->avail < 0) {
-                if (this->avail == -EPIPE) {
-                    xrun_detected = TRUE;
-                } else {
-                    g_warning("unknown ALSA avail_update return value (%d)",
-                              (int)this->avail);
-                    return;
-                }
+        if (this->avail < 0) {
+            if (this->avail == -EPIPE) {
+                xrun_detected = TRUE;
             } else {
-                snd_pcm_wait(this->handle, 1000);
+                g_warning("unknown ALSA avail_update return value (%d)",
+                          (int)this->avail);
+                return;
             }
-            this->avail = snd_pcm_avail_update (this->handle);
         }
         
+        /* pretty sophisticated eh? */
         if (xrun_detected)
-            g_warning ("xrun detected");
+            g_warning ("xrun detected"); 
         
+        /* changes this->avail, as a side effect */
         if (!gst_alsa_get_channel_addresses (this) < 0) {
             g_error("could not get channels");
             return;
         }
         
         if (this->mute && this->stream == SND_PCM_STREAM_PLAYBACK) {
-            for (x = 0; x < this->channels; x++) {
-                if (this->mute & (1<<x)) {
-                    gst_alsa_sink_silence_on_channel (this, x, this->buffer_frames);
+            for (i = 0; i < this->channels; i++) {
+                if (this->mute & (1<<i)) {
+                    gst_alsa_sink_silence_on_channel (this, i, this->buffer_frames);
                 }
             }
-//            silence_pending = 0;
         }
         
-//            channels_not_done = channel_done_bits;
-//            prefold_all_channels (contiguous);
-        
         if (!this->process(this, this->avail)) {
             g_warning("alsa: something happened while processing audio");
             return;
         }
         
-//            if (channels_not_done) {
-//                silence_untouched_channels (contiguous);
-//            }
-        
         gst_alsa_release_channel_addresses(this);
     } while (!GST_ELEMENT_IS_COTHREAD_STOPPING (element));
-    // clean up bytestreams
 }
 
 static gboolean
 gst_alsa_sink_interleaved_process (GstAlsa *this, snd_pcm_uframes_t frames)
 {
     GstBuffer *peeked;
-    gint len;
+    guint32 len;
     
     g_return_val_if_fail(this->pad != NULL, FALSE);
-    
+
     if (!this->pad_bs)
         this->pad_bs = gst_bytestream_new(this->pad);
     
-    len = frames * this->sample_bytes * this->channels;
+    /* 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.
+     * */
     
+    if (!this->sample_bytes) {
+        peeked = gst_bytestream_peek(this->pad_bs, frames);
+        if (!this->sample_bytes) {
+            g_critical ("alsa plugin requires a pipeline that can adequately set caps.");
+            return FALSE;
+        }
+    }
+    
+    len = frames * this->sample_bytes * this->channels;
     peeked = gst_bytestream_peek(this->pad_bs, len);
     memcpy(this->access_addr[0], GST_BUFFER_DATA(peeked), len);
     gst_bytestream_flush(this->pad_bs, len);
@@ -751,7 +742,7 @@
 gst_alsa_src_interleaved_process (GstAlsa *this, snd_pcm_uframes_t frames)
 {
     GstBuffer *buf;
-    gint len;
+    guint32 len;
     
     g_return_val_if_fail(this->pad != NULL, FALSE);
     
@@ -773,10 +764,26 @@
 gst_alsa_sink_noninterleaved_process (GstAlsa *this, snd_pcm_uframes_t frames)
 {
     GstBuffer *peeked;
-    gint len, i;
+    guint32 len, i;
     
-    len = frames * this->sample_bytes;
+    /* see above note. */
+    if (!this->sample_bytes) {
+        for (i=0; i<GST_ALSA_MAXCHANNELS; i++) {
+            if (this->req_pads[i]) {
+                if (!this->req_pads_bs[i])
+                    this->req_pads_bs[i] = gst_bytestream_new(this->req_pads[i]);
+                peeked = gst_bytestream_peek(this->req_pads_bs[i], frames);
+                break;
+            }
+        }
+        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;
+    
     for (i=0; i<GST_ALSA_MAXCHANNELS; i++) {
         if (this->req_pads[i]) {
             if (!this->req_pads_bs[i])
@@ -795,9 +802,7 @@
 gst_alsa_src_noninterleaved_process (GstAlsa *this, snd_pcm_uframes_t frames)
 {
     GstBuffer *buf;
-    gint len, i;
-    
-    g_return_val_if_fail(this->pad != NULL, FALSE);
+    guint32 len, i;
     
     len = frames * this->sample_bytes;
     
@@ -825,10 +830,10 @@
     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), 
             this->rate, this->channels);
@@ -860,10 +865,13 @@
         return FALSE;
     }
     
-    ret = snd_pcm_hw_params_set_format(this->handle, hw_param, this->format);
-    if (ret < 0) {
-        g_warning("Sample format (%s) not available: %s", snd_pcm_format_name(this->format), snd_strerror(ret));
-        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) {
+            g_warning("Sample format (%s) not available: %s", snd_pcm_format_name(this->format), snd_strerror(ret));
+            return FALSE;
+        }
+        this->sample_bytes = snd_pcm_format_physical_width(this->format) / 8;
     }
     
     if (!this->pad) {
@@ -873,34 +881,44 @@
             this->channels = GST_ALSA_MAXCHANNELS;
     }
     
-    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;
+    if (this->channels) {
+        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;
+        }
     }
     
-    ret = snd_pcm_hw_params_set_rate(this->handle, hw_param, this->rate, 0);
-    if (ret < 0) {
-        g_warning("error setting rate (%d): %s", this->rate, snd_strerror(ret));
-        return FALSE;
+    if (this->rate) {
+        ret = snd_pcm_hw_params_set_rate(this->handle, hw_param, this->rate, 0);
+        if (ret < 0) {
+            g_warning("error setting rate (%d): %s", this->rate, snd_strerror(ret));
+            return FALSE;
+        }
     }
     
-    ret = snd_pcm_hw_params_set_periods (this->handle, hw_param, this->period_count, 0);
-    if (ret < 0) {
-        g_warning("error setting period count minimum (%d): %s", this->period_count, snd_strerror(ret));
-        return FALSE;
+    if (this->period_count) {
+        ret = snd_pcm_hw_params_set_periods (this->handle, hw_param, this->period_count, 0);
+        if (ret < 0) {
+            g_warning("error setting period count minimum (%d): %s", this->period_count, snd_strerror(ret));
+            return FALSE;
+        }
     }
     
-    ret = snd_pcm_hw_params_set_period_size (this->handle, hw_param, this->period_frames, 0);
-    if (ret < 0) {
-        g_warning("error setting period in frames (%d): %s", this->period_frames, snd_strerror(ret));
-        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) {
+            g_warning("error setting period in frames (%d): %s", this->period_frames, snd_strerror(ret));
+            return FALSE;
+        }
     }
     
-    ret = snd_pcm_hw_params_set_buffer_size (this->handle, hw_param, this->buffer_frames);
-    if (ret < 0) {
-        g_warning("error setting buffer size (%d): %s", this->buffer_frames, snd_strerror(ret));
-        return FALSE;
+    if (this->buffer_frames) {
+        ret = snd_pcm_hw_params_set_buffer_size (this->handle, hw_param, this->buffer_frames);
+        if (ret < 0) {
+            g_warning("error setting buffer size (%d): %s", this->buffer_frames, snd_strerror(ret));
+            return FALSE;
+        }
     }
     
     ret = snd_pcm_hw_params(this->handle, hw_param);
@@ -910,6 +928,21 @@
         return FALSE;
     }
     
+    if (!this->rate)
+        this->rate = snd_pcm_hw_params_get_rate(hw_param, 0);
+    if (!this->format)
+        this->format = snd_pcm_hw_params_get_format(hw_param);
+    if (!this->channels)
+        this->channels = snd_pcm_hw_params_get_channels(hw_param);
+    if (!this->period_count)
+        this->period_count = snd_pcm_hw_params_get_periods(hw_param, 0);
+    if (!this->period_frames)
+        this->period_frames = snd_pcm_hw_params_get_period_size(hw_param, 0);
+    if (!this->buffer_frames)
+        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);
@@ -950,8 +983,6 @@
     
     snd_pcm_dump(this->handle, this->out);
     
-    this->sample_bytes = snd_pcm_format_physical_width(this->format) / 8;
-    
     this->interleaved = !(snd_pcm_hw_params_get_access (hw_param) ==
                           SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
     
@@ -1091,16 +1122,14 @@
     
     g_return_val_if_fail (this->access_addr[0] == NULL, FALSE);
     
-//    G_BREAKPOINT();
-    
-    GST_DEBUG(0, "getting mmap'd data region\n");
-    
     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 frames\n", this->avail);
+    GST_DEBUG(0, "got %d mmap'd frames\n", (int)this->avail);
+    
+//    g_print ("snd_pcm_mmap_begin() sets avail = %d\n", this->avail);
     
     for (n = 0; n < this->channels; n++) {
         a = &this->mmap_areas[n];
@@ -1117,9 +1146,7 @@
     
     g_return_if_fail (this->access_addr[0] != NULL);
     
-//    G_BREAKPOINT();
-    
-    GST_DEBUG(0, "releasing mmap'd data region: %d frames\n", this->avail);
+    GST_DEBUG(0, "releasing mmap'd data region: %d frames\n", (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));





More information about the Gstreamer-commits mailing list