From a3f57dfba49101315451a8f2f3f83f86388e9d66 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Thu, 24 Apr 2003 22:58:33 +0000 Subject: [PATCH] alsasrc works now, but it still uses hardcoded caps Original commit message from CVS: alsasrc works now, but it still uses hardcoded caps --- ext/alsa/gstalsa.c | 329 ++++++++++++++++++++++++++++++----------------------- ext/alsa/gstalsa.h | 3 + 2 files changed, 191 insertions(+), 141 deletions(-) diff --git a/ext/alsa/gstalsa.c b/ext/alsa/gstalsa.c index 3d41041..2520f8d 100644 --- a/ext/alsa/gstalsa.c +++ b/ext/alsa/gstalsa.c @@ -115,7 +115,13 @@ static GstPadTemplate * gst_alsa_src_pad_factory (void); static GstPadTemplate * gst_alsa_src_request_pad_factory (void); static void gst_alsa_src_class_init (GstAlsaSrcClass * klass); static void gst_alsa_src_init (GstAlsaSrc * this); +static int gst_alsa_src_mmap (GstAlsa * this, + snd_pcm_sframes_t * avail); +static int gst_alsa_src_read (GstAlsa * this, + snd_pcm_sframes_t * avail); static void gst_alsa_src_loop (GstElement * element); +static void gst_alsa_src_flush (GstAlsaSrc * src); +static GstElementStateReturn gst_alsa_src_change_state (GstElement * element); /* GStreamer functions for pads and state changing */ static GstPad * gst_alsa_request_new_pad (GstElement * element, @@ -868,7 +874,10 @@ gst_alsa_src_class_init (GstAlsaSrcClass *klass) src_parent_class = g_type_class_ref (GST_TYPE_ALSA); alsa_class->stream = SND_PCM_STREAM_CAPTURE; + alsa_class->transmit_mmap = gst_alsa_src_mmap; + alsa_class->transmit_rw = gst_alsa_src_read; + element_class->change_state = gst_alsa_src_change_state; } static void gst_alsa_src_init (GstAlsaSrc *src) @@ -887,89 +896,207 @@ gst_alsa_src_init (GstAlsaSrc *src) gst_element_set_loop_function (GST_ELEMENT (this), gst_alsa_src_loop); } +static int +gst_alsa_src_mmap (GstAlsa *this, snd_pcm_sframes_t *avail) +{ + snd_pcm_uframes_t offset; + snd_pcm_channel_area_t *dst; + const snd_pcm_channel_area_t *src; + int i, err, width = snd_pcm_format_physical_width (this->format->format); + GstAlsaSrc *alsa_src = GST_ALSA_SRC (this); + + /* areas points to the memory areas that belong to gstreamer. */ + dst = calloc(this->format->channels, sizeof(snd_pcm_channel_area_t)); + + if (((GstElement *) this)->numpads == 1) { + /* interleaved */ + for (i = 0; i < this->format->channels; i++) { + dst[i].addr = alsa_src->buf[0]->data; + dst[i].first = i * width; + dst[i].step = this->format->channels * width; + } + } else { + /* noninterleaved */ + for (i = 0; i < this->format->channels; i++) { + dst[i].addr = alsa_src->buf[i]->data; + dst[i].first = 0; + dst[i].step = width; + } + } + + if ((err = snd_pcm_mmap_begin (this->handle, &src, &offset, avail)) < 0) { + g_warning ("gstalsa: mmap failed: %s", snd_strerror (err)); + return -1; + } + if (*avail > 0 && (err = snd_pcm_areas_copy (dst, 0, src, offset, this->format->channels, *avail, this->format->format)) < 0) { + snd_pcm_mmap_commit (this->handle, offset, 0); + g_warning ("gstalsa: data copy failed: %s", snd_strerror (err)); + return -1; + } + if ((err = snd_pcm_mmap_commit (this->handle, offset, *avail)) < 0) { + g_warning ("gstalsa: mmap commit failed: %s", snd_strerror (err)); + return -1; + } + + return err; +} +static int +gst_alsa_src_read (GstAlsa *this, snd_pcm_sframes_t *avail) +{ + void *channels[this->format->channels]; + int err, i; + GstAlsaSrc *src = GST_ALSA_SRC (this); + + if (((GstElement *) this)->numpads == 1) { + /* interleaved */ + err = snd_pcm_readi (this->handle, src->buf[0]->data, *avail); + } else { + /* noninterleaved */ + for (i = 0; i < this->format->channels; i++) { + channels[i] = src->buf[i]->data; + } + err = snd_pcm_readn (this->handle, channels, *avail); + } + /* error handling */ + if (err < 0) { + if (err == -EPIPE) { + gst_alsa_xrun_recovery (this); + return 0; + } + g_warning ("error on data access: %s", snd_strerror (err)); + } + return err; +} +static gboolean +gst_alsa_src_set_caps (GstAlsaSrc *src) +{ + gint i; + GstCaps *caps; + GstAlsa *this = GST_ALSA (src); + + if (!(this->format = g_new (GstAlsaFormat, 1))) + return FALSE; + /* FIXME: make this do proper caps nego */ + this->format->format = SND_PCM_FORMAT_S16; + this->format->rate = 44100; + this->format->channels = GST_ELEMENT (src)->numpads; + GST_DEBUG (GST_CAT_NEGOTIATION, "starting caps negotiation on alsa src"); + caps = gst_alsa_caps (this->format->format, this->format->rate, this->format->channels); + for (i = 0; i < GST_ELEMENT (src)->numpads; i++) { + if (gst_pad_try_set_caps (this->pad[i], caps) <= 0) { + GST_DEBUG (GST_CAT_NEGOTIATION, "setting caps (%p) in alsasrc (%p) on pad %d failed", caps, this, i); + return FALSE; + } + } + + if (GST_FLAG_IS_SET (this, GST_ALSA_RUNNING)) gst_alsa_stop_audio (this); + if (!gst_alsa_start_audio (this)) { + /* FIXME: Try the next format */ + return FALSE; + } + + return TRUE; +} +/* we transmit buffers of period_size frames */ static void gst_alsa_src_loop (GstElement *element) { -#if 0 snd_pcm_sframes_t avail, copied; - GstBufferPool *pool = NULL; - GstBuffer *buf; - GstCaps *caps; gint i; - GstAlsaPad *pad; GstAlsa *this = GST_ALSA (element); - - g_return_if_fail (this != NULL); + GstAlsaSrc *src = GST_ALSA_SRC (element); /* set the caps on all pads */ if (!this->format) { - if (!(this->format = g_new (GstAlsaFormat, 1))) { - gst_element_error (element, "No more memory"); - } - /* FIXME: make this settable */ - this->format->format = SND_PCM_FORMAT_S16; - this->format->rate = 44100; - this->format->channels = (element->numpads == 1) ? 2 : element->numpads; - GST_DEBUG (GST_CAT_NEGOTIATION, "starting caps negotiationgst_alsa_pcm_wait"); - caps = gst_alsa_caps (this->format->format, this->format->rate, this->format->channels); - for (i = 0; i < element->numpads; i++) { - if (gst_pad_try_set_caps (this->pads[i].pad, caps) <= 0) { - GST_DEBUG (GST_CAT_NEGOTIATION, "setting caps (%p) in alsasrc (%p) on pad %d failed", caps, this, i); - return; - } + if (!gst_alsa_src_set_caps (src)) { + gst_element_error (element, "Could not set caps"); + return; } + /* get the bufferpool going */ + if (src->pool) + gst_buffer_pool_unref (src->pool); + src->pool = gst_buffer_pool_get_default (gst_alsa_samples_to_bytes (this, this->period_size), + 2 * element->numpads); } -src_restart: - while ((avail = gst_alsa_update_avail (this)) < this->period_size) { - if (avail == -EPIPE) goto src_restart; + if (avail == -EPIPE) continue; if (avail < 0) return; - if (snd_pcm_state(this->handle) != SND_PCM_STATE_RUNNING) break; + if (snd_pcm_state(this->handle) != SND_PCM_STATE_RUNNING) { + if (!gst_alsa_start (this)) + return; + continue; + }; /* wait */ if (gst_alsa_pcm_wait (this) == FALSE) return; } - if (avail > 0) { - int width = snd_pcm_format_physical_width (this->format->format); - int bytes_per_frame = ( width / 8 ) * (element->numpads == 1 ? this->format->channels : 1); - if ((copied = this->transmit (this, &avail)) < 0) + g_assert (avail >= this->period_size); + /* make sure every pad has a buffer */ + for (i = 0; i < element->numpads; i++) { + if (!src->buf[i]) { + src->buf[i] = gst_buffer_new_from_pool (src->pool, 0, 0); + } + } + /* fill buffer with data */ + if ((copied = this->transmit (this, &avail)) <= 0) + return; + /* push the buffers out and let them have fun */ + for (i = 0; i < element->numpads; i++) { + if (!src->buf[i]) return; - - /* we get the buffer pool once per go round */ - if (! pool) pool = gst_alsa_src_get_buffer_pool (this->pads[0].pad); - - /* push the data to gstreamer if it's big enough to fill up a buffer. */ - for (i = 0; i < element->numpads; i++) { - pad = &this->pads[i]; - pad->size += MIN (copied, this->period_size - pad->size); - - if (pad->size >= this->period_size) { - g_assert (pad->size <= this->period_size); - - buf = gst_buffer_new_from_pool (pool, 0, 0); - - GST_BUFFER_DATA (buf) = pad->data; - GST_BUFFER_SIZE (buf) = this->period_size * bytes_per_frame; - GST_BUFFER_MAXSIZE (buf) = this->period_size * bytes_per_frame; + if (copied != this->period_size) + GST_BUFFER_SIZE (src->buf[i]) = gst_alsa_samples_to_bytes (this, copied); + GST_BUFFER_TIMESTAMP (src->buf[i]) = gst_alsa_samples_to_timestamp (this, this->transmitted); + gst_pad_push (this->pad[i], src->buf[i]); + src->buf[i] = NULL; + } + this->transmitted += copied; +} - gst_pad_push (pad->pad, buf); +static void +gst_alsa_src_flush (GstAlsaSrc *src) +{ + gint i; - pad->data = NULL; - pad->size = 0; - } + for (i = 0; i < GST_ELEMENT (src)->numpads; i++) { + if (src->buf[i]) { + gst_buffer_unref (src->buf[i]); + src->buf[i] = NULL; } - - pool = NULL; } + if (src->pool) { + gst_buffer_pool_unref (src->pool); + src->pool = NULL; + } +} +static GstElementStateReturn +gst_alsa_src_change_state (GstElement *element) +{ + GstAlsaSrc *src; - /* BUG: we start the stream explicitly, autostart doesn't work correctly (alsa 0.9.0rc7) */ - if (snd_pcm_state(this->handle) == SND_PCM_STATE_PREPARED && snd_pcm_avail_update (this->handle) == 0) { - GST_DEBUG (GST_CAT_PLUGIN_INFO, "Explicitly starting capture"); - snd_pcm_start(this->handle); + g_return_val_if_fail (element != NULL, FALSE); + src = GST_ALSA_SRC (element); + + switch (GST_STATE_TRANSITION (element)) { + case GST_STATE_NULL_TO_READY: + case GST_STATE_READY_TO_PAUSED: + case GST_STATE_PAUSED_TO_PLAYING: + case GST_STATE_PLAYING_TO_PAUSED: + break; + case GST_STATE_PAUSED_TO_READY: + gst_alsa_src_flush (src); + break; + case GST_STATE_READY_TO_NULL: + break; + default: + g_assert_not_reached(); } -#endif + if (GST_ELEMENT_CLASS (sink_parent_class)->change_state) + return GST_ELEMENT_CLASS (sink_parent_class)->change_state (element); + + return GST_STATE_SUCCESS; } /*** GSTREAMER PAD / STATE FUNCTIONS*******************************************/ @@ -1455,87 +1582,7 @@ gst_alsa_change_state (GstElement *element) } /*** AUDIO PROCESSING *********************************************************/ -#if 0 -static int -gst_alsa_do_mmap (GstAlsa *this, snd_pcm_sframes_t *avail) -{ - snd_pcm_uframes_t offset; - snd_pcm_channel_area_t *dst, *src, *areas; - int i, err, width = snd_pcm_format_physical_width (this->format->format); - - /* areas points to the memory areas that belong to gstreamer. */ - areas = src = dst = calloc(this->format->channels, sizeof(snd_pcm_channel_area_t)); - - if (((GstElement *) this)->numpads == 1) { - /* interleaved */ - for (i = 0; i < this->format->channels; i++) { - areas[i].addr = this->pads[0].data; - areas[i].first = i * width; - areas[i].step = this->format->channels * width; - } - } else { - /* noninterleaved */ - for (i = 0; i < this->format->channels; i++) { - areas[i].addr = this->pads[i].data; - areas[i].first = 0; - areas[i].step = width; - } - } - - if ((err = snd_pcm_mmap_begin (this->handle, ( - const snd_pcm_channel_area_t **) (G_OBJECT_TYPE (this) == GST_TYPE_ALSA_SRC ? &src : &dst), - &offset, avail)) < 0) { - g_warning ("gstalsa: mmap failed: %s", snd_strerror (err)); - return -1; - } - if ((err = snd_pcm_areas_copy (dst, offset, src, 0, this->format->channels, *avail, this->format->format)) < 0) { - snd_pcm_mmap_commit (this->handle, offset, 0); - g_warning ("gstalsa: data copy failed: %s", snd_strerror (err)); - return -1; - } - if ((err = snd_pcm_mmap_commit (this->handle, offset, *avail)) < 0) { - g_warning ("gstalsa: mmap commit failed: %s", snd_strerror (err)); - return -1; - } - - return err; -} -static int -gst_alsa_do_read_write (GstAlsa *this, snd_pcm_sframes_t *avail) -{ - void *channels[this->format->channels]; - int err, i; - - if (((GstElement *) this)->numpads == 1) { - /* interleaved */ - if (G_OBJECT_TYPE (this) == GST_TYPE_ALSA_SRC) { - err = snd_pcm_readi (this->handle, this->pads[0].data, *avail); - } else { - err = snd_pcm_writei (this->handle, this->pads[0].data, *avail); - } - } else { - /* noninterleaved */ - for (i = 0; i < this->format->channels; i++) { - channels[i] = this->pads[i].data; - } - if (G_OBJECT_TYPE (this) == GST_TYPE_ALSA_SRC) { - err = snd_pcm_readn (this->handle, channels, *avail); - } else { - err = snd_pcm_writen (this->handle, channels, *avail); - } - } - /* error handling */ - if (err < 0) { - if (err == -EPIPE) { - gst_alsa_xrun_recovery (this); - return 0; - } - g_warning ("error on data access: %s", snd_strerror (err)); - } - return err; -} -#endif inline static snd_pcm_sframes_t gst_alsa_update_avail (GstAlsa *this) { @@ -1580,9 +1627,9 @@ gst_alsa_pcm_wait (GstAlsa *this) inline static gboolean gst_alsa_start (GstAlsa *this) { - gint avail; + //gint avail; - GST_DEBUG (GST_CAT_PLUGIN_INFO, "Starting playback"); + GST_DEBUG (GST_CAT_PLUGIN_INFO, "Setting state to RUNNING"); switch (snd_pcm_state(this->handle)) { case SND_PCM_STATE_XRUN: @@ -1609,9 +1656,9 @@ gst_alsa_start (GstAlsa *this) g_assert_not_reached (); break; } - avail = (gint) gst_alsa_update_avail (this); +/* avail = (gint) gst_alsa_update_avail (this); if (avail < 0) - return FALSE; + return FALSE;*/ gst_alsa_clock_start (this->clock); return TRUE; } diff --git a/ext/alsa/gstalsa.h b/ext/alsa/gstalsa.h index 35ec9d3..ce8f7c8 100644 --- a/ext/alsa/gstalsa.h +++ b/ext/alsa/gstalsa.h @@ -160,6 +160,9 @@ struct _GstAlsaSinkClass { }; struct _GstAlsaSrc { GstAlsa parent; + + GstBuffer * buf[GST_ALSA_MAX_CHANNELS]; + GstBufferPool * pool; }; struct _GstAlsaSrcClass { GstAlsaClass parent_class; -- 2.7.4