[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