From ceb88a77774971ffa4e117a7f0d1c0cc88abdb98 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 6 Jul 2005 15:27:17 +0000 Subject: [PATCH] Added audiosource base classes. Original commit message from CVS: Added audiosource base classes. Ported alsasrc, still very basic. --- ChangeLog | 51 ++ ext/alsa/Makefile.am | 6 +- ext/alsa/gstalsaplugin.c | 6 +- ext/alsa/gstalsasrc.c | 979 ++++++++++++++------------ ext/alsa/gstalsasrc.h | 66 +- gst-libs/gst/audio/Makefile.am | 4 + gst-libs/gst/audio/gstaudiosink.c | 16 +- gst-libs/gst/audio/gstaudiosrc.c | 418 +++++++++++ gst-libs/gst/audio/gstaudiosrc.h | 84 +++ gst-libs/gst/audio/gstbaseaudiosink.c | 210 +----- gst-libs/gst/audio/gstbaseaudiosrc.c | 403 +++++++++++ gst-libs/gst/audio/gstbaseaudiosrc.h | 77 ++ gst-libs/gst/audio/gstringbuffer.c | 443 ++++++++++-- gst-libs/gst/audio/gstringbuffer.h | 20 +- 14 files changed, 2041 insertions(+), 742 deletions(-) create mode 100644 gst-libs/gst/audio/gstaudiosrc.c create mode 100644 gst-libs/gst/audio/gstaudiosrc.h create mode 100644 gst-libs/gst/audio/gstbaseaudiosrc.c create mode 100644 gst-libs/gst/audio/gstbaseaudiosrc.h diff --git a/ChangeLog b/ChangeLog index 7bad61004c..74d265596c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,54 @@ +2005-07-06 Wim Taymans + + * ext/alsa/Makefile.am: + * ext/alsa/gstalsaplugin.c: (plugin_init): + * ext/alsa/gstalsasink.c: (gst_alsasink_open): + * ext/alsa/gstalsasrc.c: (gst_alsasrc_get_type), + (gst_alsasrc_dispose), (gst_alsasrc_base_init), + (gst_alsasrc_class_init), (gst_alsasrc_init), + (gst_alsasrc_getcaps), (set_hwparams), (set_swparams), + (alsasrc_parse_spec), (gst_alsasrc_open), (gst_alsasrc_close), + (xrun_recovery), (gst_alsasrc_read), (gst_alsasrc_delay), + (gst_alsasrc_reset): + * ext/alsa/gstalsasrc.h: + * gst-libs/gst/audio/Makefile.am: + * gst-libs/gst/audio/gstaudiosink.c: + (gst_audioringbuffer_get_type), (gst_audioringbuffer_class_init), + (gst_audioringbuffer_start): + * gst-libs/gst/audio/gstaudiosrc.c: (gst_audioringbuffer_get_type), + (gst_audioringbuffer_class_init), (audioringbuffer_thread_func), + (gst_audioringbuffer_init), (gst_audioringbuffer_dispose), + (gst_audioringbuffer_finalize), (gst_audioringbuffer_acquire), + (gst_audioringbuffer_release), (gst_audioringbuffer_start), + (gst_audioringbuffer_stop), (gst_audioringbuffer_delay), + (gst_audiosrc_base_init), (gst_audiosrc_class_init), + (gst_audiosrc_init), (gst_audiosrc_create_ringbuffer): + * gst-libs/gst/audio/gstaudiosrc.h: + * gst-libs/gst/audio/gstbaseaudiosink.c: + (gst_baseaudiosink_class_init), (gst_baseaudiosink_dispose), + (gst_baseaudiosink_get_time), (gst_baseaudiosink_setcaps), + (gst_baseaudiosink_preroll), (gst_baseaudiosink_render): + * gst-libs/gst/audio/gstbaseaudiosrc.c: + (gst_baseaudiosrc_base_init), (gst_baseaudiosrc_class_init), + (gst_baseaudiosrc_init), (gst_baseaudiosrc_get_clock), + (gst_baseaudiosrc_get_time), (gst_baseaudiosrc_set_property), + (gst_baseaudiosrc_get_property), (gst_baseaudiosrc_fixate), + (gst_baseaudiosrc_setcaps), (gst_baseaudiosrc_get_times), + (gst_baseaudiosrc_event), (gst_baseaudiosrc_create), + (gst_baseaudiosrc_create_ringbuffer), (gst_baseaudiosrc_callback), + (gst_baseaudiosrc_change_state): + * gst-libs/gst/audio/gstbaseaudiosrc.h: + * gst-libs/gst/audio/gstringbuffer.c: (build_linear_format), + (gst_ringbuffer_debug_spec_caps), (gst_ringbuffer_debug_spec_buff), + (gst_ringbuffer_parse_caps), (gst_ringbuffer_start), + (gst_ringbuffer_pause), (gst_ringbuffer_stop), + (gst_ringbuffer_samples_done), (gst_ringbuffer_set_sample), + (wait_segment), (gst_ringbuffer_commit), (gst_ringbuffer_read), + (gst_ringbuffer_prepare_read), (gst_ringbuffer_advance): + * gst-libs/gst/audio/gstringbuffer.h: + Added audiosource base classes. + Ported alsasrc, still very basic. + 2005-07-06 Wim Taymans * ext/theora/theoradec.c: (theora_dec_src_getcaps), diff --git a/ext/alsa/Makefile.am b/ext/alsa/Makefile.am index 0a02686614..d976b00df8 100644 --- a/ext/alsa/Makefile.am +++ b/ext/alsa/Makefile.am @@ -3,7 +3,8 @@ plugin_LTLIBRARIES = libgstalsa.la libgstalsa_la_SOURCES = \ gstalsaplugin.c \ - gstalsasink.c + gstalsasink.c \ + gstalsasrc.c # port alsa stuff then add the _SOURCES above EXTRA_DIST = \ @@ -11,8 +12,7 @@ EXTRA_DIST = \ gstalsamixertrack.c \ gstalsamixeroptions.c \ gstalsa.c \ - gstalsaclock.c \ - gstalsasrc.c + gstalsaclock.c libgstalsa_la_CFLAGS = $(GST_CFLAGS) $(ALSA_CFLAGS) libgstalsa_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) \ diff --git a/ext/alsa/gstalsaplugin.c b/ext/alsa/gstalsaplugin.c index dfa0e5bf60..5fa547c83f 100644 --- a/ext/alsa/gstalsaplugin.c +++ b/ext/alsa/gstalsaplugin.c @@ -60,10 +60,10 @@ plugin_init (GstPlugin * plugin) if (!gst_element_register (plugin, "alsamixer", GST_RANK_NONE, GST_TYPE_ALSA_MIXER)) return FALSE; - if (!gst_element_register (plugin, "alsasrc", GST_RANK_NONE, - GST_TYPE_ALSA_SRC)) - return FALSE; */ + if (!gst_element_register (plugin, "alsasrc", GST_RANK_NONE, + GST_TYPE_ALSA_SRC)) + return FALSE; if (!gst_element_register (plugin, "alsasink", GST_RANK_NONE, GST_TYPE_ALSA_SINK)) return FALSE; diff --git a/ext/alsa/gstalsasrc.c b/ext/alsa/gstalsasrc.c index 309f4eb92b..c1ed44a663 100644 --- a/ext/alsa/gstalsasrc.c +++ b/ext/alsa/gstalsasrc.c @@ -1,8 +1,7 @@ -/* - * Copyright (C) 2001 CodeFactory AB - * Copyright (C) 2001 Thomas Nyberg - * Copyright (C) 2001-2002 Andy Wingo - * Copyright (C) 2003 Benjamin Otte +/* GStreamer + * Copyright (C) 2005 Wim Taymans + * + * gstalsasrc.c: * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -11,521 +10,601 @@ * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif +#include +#include +#include +#include +#include +#include +#include + #include "gstalsasrc.h" -#include "gstalsaclock.h" /* elementfactory information */ -static GstElementDetails gst_alsa_src_details = GST_ELEMENT_DETAILS ("Alsa Src", - "Source/Audio", - "Read from a sound card via ALSA", - "Thomas Nyberg , " - "Andy Wingo , " - "Benjamin Otte "); +static GstElementDetails gst_alsasrc_details = +GST_ELEMENT_DETAILS ("Audio Src (ALSA)", + "Src/Audio", + "Output to a sound card via ALSA", + "Wim Taymans "); -static GstPadTemplate *gst_alsa_src_pad_factory (void); -static void gst_alsa_src_base_init (gpointer g_class); -static void gst_alsa_src_class_init (gpointer g_class, gpointer class_data); -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); -static GstClockTime gst_alsa_src_get_time (GstAlsa * this); +static void gst_alsasrc_base_init (gpointer g_class); +static void gst_alsasrc_class_init (GstAlsaSrcClass * klass); +static void gst_alsasrc_init (GstAlsaSrc * alsasrc); +static void gst_alsasrc_dispose (GObject * object); -static GstAlsa *src_parent_class = NULL; +static GstCaps *gst_alsasrc_getcaps (GstBaseSrc * bsrc); -static GstPadTemplate * -gst_alsa_src_pad_factory (void) +static gboolean gst_alsasrc_open (GstAudioSrc * asrc, GstRingBufferSpec * spec); +static gboolean gst_alsasrc_close (GstAudioSrc * asrc); +static guint gst_alsasrc_read (GstAudioSrc * asrc, gpointer data, guint length); +static guint gst_alsasrc_delay (GstAudioSrc * asrc); +static void gst_alsasrc_reset (GstAudioSrc * asrc); + +/* AlsaSrc signals and args */ +enum { - static GstPadTemplate *template = NULL; + LAST_SIGNAL +}; - if (!template) - template = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - gst_alsa_caps (SND_PCM_FORMAT_UNKNOWN, -1, -1)); +static GstStaticPadTemplate alsasrc_src_factory = + GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-int, " + "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " + "signed = (boolean) { TRUE, FALSE }, " + "width = (int) 16, " + "depth = (int) 16, " + "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]; " + "audio/x-raw-int, " + "signed = (boolean) { TRUE, FALSE }, " + "width = (int) 8, " + "depth = (int) 8, " + "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]") + ); - return template; -} +static GstElementClass *parent_class = NULL; + +/* static guint gst_alsasrc_signals[LAST_SIGNAL] = { 0 }; */ GType -gst_alsa_src_get_type (void) +gst_alsasrc_get_type (void) { - static GType alsa_src_type = 0; + static GType alsasrc_type = 0; - if (!alsa_src_type) { - static const GTypeInfo alsa_src_info = { + if (!alsasrc_type) { + static const GTypeInfo alsasrc_info = { sizeof (GstAlsaSrcClass), - gst_alsa_src_base_init, + gst_alsasrc_base_init, NULL, - gst_alsa_src_class_init, + (GClassInitFunc) gst_alsasrc_class_init, NULL, NULL, sizeof (GstAlsaSrc), 0, - (GInstanceInitFunc) gst_alsa_src_init, + (GInstanceInitFunc) gst_alsasrc_init, }; - alsa_src_type = - g_type_register_static (GST_TYPE_ALSA_MIXER, "GstAlsaSrc", - &alsa_src_info, 0); + alsasrc_type = + g_type_register_static (GST_TYPE_AUDIOSRC, "GstAlsaSrc", + &alsasrc_info, 0); } - return alsa_src_type; + + return alsasrc_type; } static void -gst_alsa_src_base_init (gpointer g_class) +gst_alsasrc_dispose (GObject * object) +{ + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gst_alsasrc_base_init (gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + gst_element_class_set_details (element_class, &gst_alsasrc_details); + gst_element_class_add_pad_template (element_class, - gst_alsa_src_pad_factory ()); + gst_static_pad_template_get (&alsasrc_src_factory)); +} +static void +gst_alsasrc_class_init (GstAlsaSrcClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBaseSrcClass *gstbasesrc_class; + GstBaseAudioSrcClass *gstbaseaudiosrc_class; + GstAudioSrcClass *gstaudiosrc_class; - gst_element_class_set_details (element_class, &gst_alsa_src_details); + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstbasesrc_class = (GstBaseSrcClass *) klass; + gstbaseaudiosrc_class = (GstBaseAudioSrcClass *) klass; + gstaudiosrc_class = (GstAudioSrcClass *) klass; + + parent_class = g_type_class_ref (GST_TYPE_BASEAUDIOSRC); + + gobject_class->dispose = gst_alsasrc_dispose; + + gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_alsasrc_getcaps); + + gstaudiosrc_class->open = GST_DEBUG_FUNCPTR (gst_alsasrc_open); + gstaudiosrc_class->close = GST_DEBUG_FUNCPTR (gst_alsasrc_close); + gstaudiosrc_class->read = GST_DEBUG_FUNCPTR (gst_alsasrc_read); + gstaudiosrc_class->delay = GST_DEBUG_FUNCPTR (gst_alsasrc_delay); + gstaudiosrc_class->reset = GST_DEBUG_FUNCPTR (gst_alsasrc_reset); } static void -gst_alsa_src_class_init (gpointer g_class, gpointer class_data) +gst_alsasrc_init (GstAlsaSrc * alsasrc) { - GObjectClass *object_class; - GstElementClass *element_class; - GstAlsaClass *alsa_class; - GstAlsaSrcClass *klass; + GST_DEBUG ("initializing alsasrc"); - klass = (GstAlsaSrcClass *) g_class; - object_class = (GObjectClass *) klass; - element_class = (GstElementClass *) klass; - alsa_class = (GstAlsaClass *) klass; - - if (src_parent_class == NULL) - src_parent_class = g_type_class_ref (GST_TYPE_ALSA_MIXER); - - 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; + alsasrc->device = g_strdup ("default"); } -static void -gst_alsa_src_init (GstAlsaSrc * src) +static GstCaps * +gst_alsasrc_getcaps (GstBaseSrc * bsrc) { - GstAlsa *this = GST_ALSA (src); - - this->pad[0] = gst_pad_new_from_template (gst_alsa_src_pad_factory (), "src"); - gst_pad_set_link_function (this->pad[0], gst_alsa_link); - gst_pad_set_getcaps_function (this->pad[0], gst_alsa_get_caps); - gst_element_add_pad (GST_ELEMENT (this), this->pad[0]); - - this->clock = - gst_alsa_clock_new ("alsasrcclock", gst_alsa_src_get_time, this); - /* we hold a ref to our clock until we're disposed */ - gst_object_ref (this->clock); - gst_object_sink (GST_OBJECT (this->clock)); - - src->status = NULL; - gst_element_set_loop_function (GST_ELEMENT (this), gst_alsa_src_loop); + return NULL; } -/* alsasrc provides a clock starting from the trigger tstamp - * (last play/pause/stop), and added to that the time for the currently - * processed samples, and the current fill state of the buffer */ -static GstClockTime -gst_alsa_src_get_time (GstAlsa * this) -{ - struct timeval trigger; - snd_pcm_sframes_t delay; - GstClockTime gct_trigger, gct_captured, gct_delay, retval = - GST_CLOCK_TIME_NONE; - int err; - GstAlsaSrc *src = GST_ALSA_SRC (this); - - GTimeVal now; - - g_get_current_time (&now); - return GST_TIMEVAL_TO_TIME (now); - - if (src->status == NULL) - return GST_CLOCK_TIME_NONE; - - if ((err = snd_pcm_status (this->handle, src->status)) < 0) { - GST_ERROR_OBJECT (this, "status error: %s", snd_strerror (err)); - return GST_CLOCK_TIME_NONE; - } - - /* trigger tstamp is the last time the device got started/stopped/paused */ - snd_pcm_status_get_trigger_tstamp (src->status, &trigger); - gct_trigger = GST_TIMEVAL_TO_TIME (trigger); - - /* captured is the number of samples already sent out as buffers */ - gct_captured = gst_alsa_samples_to_timestamp (this, this->captured); - - /* delay is the number of samples in the buffer not yet processed */ - delay = snd_pcm_status_get_delay (src->status); - gct_delay = gst_alsa_samples_to_timestamp (this, delay); - - retval = gct_trigger + gct_captured + gct_delay; - GST_LOG_OBJECT (src, "returning clock time of %" GST_TIME_FORMAT, - GST_TIME_ARGS (retval)); - return retval; -} +#define CHECK(call, error) \ +G_STMT_START { \ +if ((err = call) < 0) \ + goto error; \ +} G_STMT_END; static int -gst_alsa_src_mmap (GstAlsa * this, snd_pcm_sframes_t * avail) +set_hwparams (GstAlsaSrc * alsa) { - 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); + guint rrate; + gint err, dir; + snd_pcm_hw_params_t *params; - /* areas points to the memory areas that belong to gstreamer. */ - dst = g_malloc0 (this->format->channels * sizeof (snd_pcm_channel_area_t)); + snd_pcm_hw_params_alloca (¶ms); - 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; - } + /* choose all parameters */ + CHECK (snd_pcm_hw_params_any (alsa->handle, params), no_config); + /* set the interleaved read/write format */ + CHECK (snd_pcm_hw_params_set_access (alsa->handle, params, alsa->access), + wrong_access); + /* set the sample format */ + CHECK (snd_pcm_hw_params_set_format (alsa->handle, params, alsa->format), + no_sample_format); + /* set the count of channels */ + CHECK (snd_pcm_hw_params_set_channels (alsa->handle, params, alsa->channels), + no_channels); + /* set the stream rate */ + rrate = alsa->rate; + CHECK (snd_pcm_hw_params_set_rate_near (alsa->handle, params, &rrate, 0), + no_rate); + if (rrate != alsa->rate) + goto rate_match; + + if (alsa->buffer_time != -1) { + /* set the buffer time */ + CHECK (snd_pcm_hw_params_set_buffer_time_near (alsa->handle, params, + &alsa->buffer_time, &dir), buffer_time); + } + if (alsa->period_time != -1) { + /* set the period time */ + CHECK (snd_pcm_hw_params_set_period_time_near (alsa->handle, params, + &alsa->period_time, &dir), period_time); } - if ((err = snd_pcm_mmap_begin (this->handle, &src, &offset, avail)) < 0) { - GST_ERROR_OBJECT (this, "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); - GST_ERROR_OBJECT (this, "data copy failed: %s", snd_strerror (err)); - return -1; - } - if ((err = snd_pcm_mmap_commit (this->handle, offset, *avail)) < 0) { - GST_ERROR_OBJECT (this, "mmap commit failed: %s", snd_strerror (err)); - return -1; - } + /* write the parameters to device */ + CHECK (snd_pcm_hw_params (alsa->handle, params), set_hw_params); - 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); + CHECK (snd_pcm_hw_params_get_buffer_size (params, &alsa->buffer_size), + buffer_size); - 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; - } - GST_ERROR_OBJECT (this, "error on data access: %s", snd_strerror (err)); - } - return err; -} -static inline gint -gst_alsa_src_adjust_rate (gint rate, gboolean aggressive) -{ - static gint rates[] = { 96000, 48000, 44100, 22050, 8000 }; - gint i; - - if (aggressive) - return rate; - - for (i = 0; i < G_N_ELEMENTS (rates); i++) { - if (rate >= rates[i]) - return rates[i]; - } + CHECK (snd_pcm_hw_params_get_period_size (params, &alsa->period_size, &dir), + period_size); return 0; + + /* ERRORS */ +no_config: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("Broken configuration for recording: no configurations available: %s", + snd_strerror (err)), (NULL)); + return err; + } +wrong_access: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("Access type not available for recording: %s", snd_strerror (err)), + (NULL)); + return err; + } +no_sample_format: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("Sample format not available for recording: %s", snd_strerror (err)), + (NULL)); + return err; + } +no_channels: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("Channels count (%i) not available for recording: %s", + alsa->channels, snd_strerror (err)), (NULL)); + return err; + } +no_rate: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("Rate %iHz not available for recording: %s", + alsa->rate, snd_strerror (err)), (NULL)); + return err; + } +rate_match: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("Rate doesn't match (requested %iHz, get %iHz)", + alsa->rate, err), (NULL)); + return -EINVAL; + } +buffer_time: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("Unable to set buffer time %i for recording: %s", + alsa->buffer_time, snd_strerror (err)), (NULL)); + return err; + } +buffer_size: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("Unable to get buffer size for recording: %s", snd_strerror (err)), + (NULL)); + return err; + } +period_time: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("Unable to set period time %i for recording: %s", alsa->period_time, + snd_strerror (err)), (NULL)); + return err; + } +period_size: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("Unable to get period size for recording: %s", snd_strerror (err)), + (NULL)); + return err; + } +set_hw_params: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("Unable to set hw params for recording: %s", snd_strerror (err)), + (NULL)); + return err; + } +} + +static int +set_swparams (GstAlsaSrc * alsa) +{ + int err; + snd_pcm_sw_params_t *params; + + snd_pcm_sw_params_alloca (¶ms); + + /* get the current swparams */ + CHECK (snd_pcm_sw_params_current (alsa->handle, params), no_config); + /* start the transfer when the buffer is almost full: */ + /* (buffer_size / avail_min) * avail_min */ +#if 0 + CHECK (snd_pcm_sw_params_set_start_threshold (alsa->handle, params, + (alsa->buffer_size / alsa->period_size) * alsa->period_size), + start_threshold); + + /* allow the transfer when at least period_size samples can be processed */ + CHECK (snd_pcm_sw_params_set_avail_min (alsa->handle, params, + alsa->period_size), set_avail); +#endif + /* align all transfers to 1 sample */ + CHECK (snd_pcm_sw_params_set_xfer_align (alsa->handle, params, 1), set_align); + + /* write the parameters to the recording device */ + CHECK (snd_pcm_sw_params (alsa->handle, params), set_sw_params); + + return 0; + + /* ERRORS */ +no_config: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("Unable to determine current swparams for recording: %s", + snd_strerror (err)), (NULL)); + return err; + } +#if 0 +start_threshold: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("Unable to set start threshold mode for recording: %s", + snd_strerror (err)), (NULL)); + return err; + } +set_avail: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("Unable to set avail min for recording: %s", snd_strerror (err)), + (NULL)); + return err; + } +#endif +set_align: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("Unable to set transfer align for recording: %s", snd_strerror (err)), + (NULL)); + return err; + } +set_sw_params: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("Unable to set sw params for recording: %s", snd_strerror (err)), + (NULL)); + return err; + } } static gboolean -gst_alsa_src_set_caps (GstAlsaSrc * src, gboolean aggressive) +alsasrc_parse_spec (GstAlsaSrc * alsa, GstRingBufferSpec * spec) { - GstCaps *all_caps, *caps; - GstStructure *structure, *walk; - gint channels, min_channels, max_channels; - gint rate, min_rate, max_rate; - gint i, endian, width, depth; - gboolean sign; - GstAlsa *this = GST_ALSA (src); - - all_caps = gst_alsa_get_caps (this->pad[0]); - if (all_caps == NULL) - return FALSE; - /* now intersect this with all caps of the peers... */ - for (i = 0; i < GST_ELEMENT (src)->numpads; i++) { - all_caps = - gst_caps_intersect (all_caps, gst_pad_get_allowed_caps (this->pad[i])); - if (all_caps == NULL) { - GST_DEBUG ("No compatible caps found in alsasrc (%s)", - GST_ELEMENT_NAME (this)); - return FALSE; - } - } - - /* construct caps */ - caps = gst_caps_new_simple ("audio/x-raw-int", NULL); - g_assert (gst_caps_get_size (caps) == 1); - structure = gst_caps_get_structure (caps, 0); - - /* now try to find the best match */ - for (i = 0; i < gst_caps_get_size (all_caps); i++) { - walk = gst_caps_get_structure (all_caps, i); - if (!(gst_structure_get_int (walk, "signed", &sign) && - gst_structure_get_int (walk, "width", &width) && - gst_structure_get_int (walk, "depth", &depth))) { - GST_ERROR_OBJECT (src, "couldn't parse my own format. Huh?"); - continue; - } - if (!gst_structure_get_int (walk, "endianness", &endian)) { - endian = G_BYTE_ORDER; - } - gst_structure_set (structure, - "endianness", G_TYPE_INT, endian, - "width", G_TYPE_INT, width, - "depth", G_TYPE_INT, depth, "signed", G_TYPE_BOOLEAN, sign, NULL); - - min_rate = - gst_value_get_int_range_min (gst_structure_get_value (walk, "rate")); - max_rate = - gst_value_get_int_range_max (gst_structure_get_value (walk, "rate")); - min_channels = - gst_value_get_int_range_min (gst_structure_get_value (walk, - "channels")); - max_channels = - gst_value_get_int_range_max (gst_structure_get_value (walk, - "channels")); - for (rate = max_rate;; rate--) { - if ((rate = gst_alsa_src_adjust_rate (rate, aggressive)) < min_rate) - break; - gst_structure_set (structure, "rate", G_TYPE_INT, rate, NULL); - for (channels = aggressive ? max_channels : MIN (max_channels, 2); - channels >= min_channels; channels--) { - gst_structure_set (structure, "channels", G_TYPE_INT, channels, NULL); - GST_DEBUG - ("trying new caps: %ssigned, endianness: %d, width %d, depth %d, channels %d, rate %d", - sign ? "" : "un", endian, width, depth, channels, rate); - if (gst_pad_try_set_caps (this->pad[0], caps) != GST_PAD_LINK_REFUSED) - gst_alsa_link (this->pad[0], caps); - - if (this->format) { - /* try to set caps here */ - return TRUE; - } + switch (spec->type) { + case GST_BUFTYPE_LINEAR: + alsa->format = snd_pcm_build_linear_format (spec->depth, spec->width, + spec->sign ? 0 : 1, spec->bigend ? 1 : 0); + break; + case GST_BUFTYPE_FLOAT: + switch (spec->format) { + case GST_FLOAT32_LE: + alsa->format = SND_PCM_FORMAT_FLOAT_LE; + break; + case GST_FLOAT32_BE: + alsa->format = SND_PCM_FORMAT_FLOAT_BE; + break; + case GST_FLOAT64_LE: + alsa->format = SND_PCM_FORMAT_FLOAT64_LE; + break; + case GST_FLOAT64_BE: + alsa->format = SND_PCM_FORMAT_FLOAT64_BE; + break; + default: + goto error; } - } - } - - if (!aggressive) - return gst_alsa_src_set_caps (src, TRUE); - - return FALSE; -} - -inline snd_pcm_sframes_t -gst_alsa_src_update_avail (GstAlsa * this) -{ - snd_pcm_sframes_t avail = -1; - - while (avail < 0) { - avail = snd_pcm_avail_update (this->handle); - if (avail < 0) { - if (avail == -EPIPE) { - gst_alsa_xrun_recovery (this); - } else { - GST_WARNING_OBJECT (this, "unknown ALSA avail_update return value (%d)", - (int) avail); - } - } - if (snd_pcm_state (this->handle) != SND_PCM_STATE_RUNNING) { - if (!gst_alsa_start (this)) { - return 0; - } - } - } - return avail; -} - -/* we transmit buffers of period_size frames */ -static void -gst_alsa_src_loop (GstElement * element) -{ - snd_pcm_sframes_t avail, copied; - gint i; - GstAlsa *this = GST_ALSA (element); - GstAlsaSrc *src = GST_ALSA_SRC (element); - - /* set the caps on all pads */ - if (!this->format) { - if (!gst_alsa_src_set_caps (src, FALSE)) { - GST_ELEMENT_ERROR (element, CORE, NEGOTIATION, (NULL), - ("ALSA format not negotiated")); - return; - } - } - - /* the cast to long is explicitly needed; - * with avail = -32 and period_size = 100, avail < period_size is false */ - while ((avail = gst_alsa_src_update_avail (this)) < this->period_size) { - /* wait */ - if (gst_alsa_pcm_wait (this) == FALSE) - return; - } - g_assert (avail >= this->period_size); - /* make sure every pad has a buffer */ - for (i = 0; i < element->numpads; i++) { - if (src->buf[i]) - gst_data_unref (GST_DATA (src->buf[i])); - src->buf[i] = - gst_buffer_new_and_alloc (gst_alsa_samples_to_bytes (this, avail)); - } - - /* fill buffer with data */ - if ((copied = this->transmit (this, &avail)) <= 0) - return; - - { - gint outsize; - GstClockTime outtime, outdur, outreal, outideal, startalsa, outalsa; - gint64 diff, offset; - struct timeval tstamp; - int err; - - - if ((err = snd_pcm_status (this->handle, src->status)) < 0) - GST_ERROR_OBJECT (this, "status error: %s", snd_strerror (err)); - - offset = this->captured; - - /* duration of buffer is just the time of the samples */ - outdur = gst_alsa_samples_to_timestamp (this, copied); - - /* The real capture time is the time of the clock minus the duration and - * what is now in the buffer */ - outreal = gst_element_get_time (GST_ELEMENT (this)) - outdur; - /* ideal time is counting samples */ - outideal = gst_alsa_samples_to_timestamp (this, offset); - - snd_pcm_status_get_trigger_tstamp (src->status, &tstamp); - startalsa = GST_TIMEVAL_TO_TIME (tstamp) - element->base_time; - outalsa = startalsa + outideal; - - outsize = gst_alsa_samples_to_bytes (this, copied); - outtime = GST_CLOCK_TIME_NONE; - - if (GST_ELEMENT_CLOCK (this)) { - if (GST_CLOCK (GST_ALSA (this)->clock) == GST_ELEMENT_CLOCK (this)) { - outtime = outalsa; - diff = outideal - outreal; - GST_DEBUG_OBJECT (this, "ideal %lld, real %lld, diff %lld\n", outideal, - outreal, diff); - offset = gst_alsa_timestamp_to_samples (this, outtime); - } else { - outtime = outreal; - offset = gst_alsa_timestamp_to_samples (this, outtime); - } - } - - /* push the buffers out and let them have fun */ - for (i = 0; i < element->numpads; i++) { - GstBuffer *buf; - - if (!src->buf[i]) - return; - if (copied != this->period_size) - GST_BUFFER_SIZE (src->buf[i]) = outsize; - - GST_BUFFER_TIMESTAMP (src->buf[i]) = outtime; - GST_BUFFER_DURATION (src->buf[i]) = outdur; - GST_BUFFER_OFFSET (src->buf[i]) = offset; - GST_BUFFER_OFFSET_END (src->buf[i]) = offset + copied; - - buf = src->buf[i]; - src->buf[i] = NULL; - gst_pad_push (this->pad[i], GST_DATA (buf)); - } - this->captured += copied; - } -} - -static void -gst_alsa_src_flush (GstAlsaSrc * src) -{ - gint i; - - for (i = 0; i < GST_ELEMENT (src)->numpads; i++) { - if (src->buf[i]) { - gst_buffer_unref (src->buf[i]); - src->buf[i] = NULL; - } - } -} - -static GstElementStateReturn -gst_alsa_src_change_state (GstElement * element) -{ - GstAlsaSrc *src; - - g_return_val_if_fail (element != NULL, FALSE); - src = GST_ALSA_SRC (element); - - switch (GST_STATE_TRANSITION (element)) { - case GST_STATE_NULL_TO_READY: break; - case GST_STATE_READY_TO_PAUSED: - snd_pcm_status_malloc (&src->status); + case GST_BUFTYPE_A_LAW: + alsa->format = SND_PCM_FORMAT_A_LAW; break; - case GST_STATE_PAUSED_TO_PLAYING: - break; - case GST_STATE_PLAYING_TO_PAUSED: - break; - case GST_STATE_PAUSED_TO_READY: - snd_pcm_status_free (src->status); - src->status = NULL; - gst_alsa_src_flush (src); - break; - case GST_STATE_READY_TO_NULL: + case GST_BUFTYPE_MU_LAW: + alsa->format = SND_PCM_FORMAT_MU_LAW; break; default: - break; + goto error; + } + alsa->rate = spec->rate; + alsa->channels = spec->channels; + alsa->buffer_time = spec->buffer_time; + alsa->period_time = spec->latency_time; + alsa->access = SND_PCM_ACCESS_RW_INTERLEAVED; - if (GST_ELEMENT_CLASS (src_parent_class)->change_state) - return GST_ELEMENT_CLASS (src_parent_class)->change_state (element); + return TRUE; - return GST_STATE_SUCCESS; + /* ERRORS */ +error: + { + return FALSE; + } +} + +static gboolean +gst_alsasrc_open (GstAudioSrc * asrc, GstRingBufferSpec * spec) +{ + GstAlsaSrc *alsa; + gint err; + + alsa = GST_ALSA_SRC (asrc); + + if (!alsasrc_parse_spec (alsa, spec)) + goto spec_parse; + + CHECK (snd_pcm_open (&alsa->handle, alsa->device, SND_PCM_STREAM_CAPTURE, + SND_PCM_NONBLOCK), open_error); + + CHECK (snd_pcm_nonblock (alsa->handle, 0), non_block); + + CHECK (set_hwparams (alsa), hw_params_failed); + CHECK (set_swparams (alsa), sw_params_failed); + CHECK (snd_pcm_prepare (alsa->handle), prepare_failed); + + alsa->bytes_per_sample = spec->bytes_per_sample; + spec->segsize = alsa->period_size * spec->bytes_per_sample; + spec->segtotal = alsa->buffer_size / alsa->period_size; + spec->silence_sample[0] = 0; + spec->silence_sample[1] = 0; + spec->silence_sample[2] = 0; + spec->silence_sample[3] = 0; + + return TRUE; + + /* ERRORS */ +spec_parse: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("Error parsing spec"), (NULL)); + return FALSE; + } +open_error: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("Recording open error: %s", snd_strerror (err)), (NULL)); + return FALSE; + } +non_block: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("Could not set device to blocking: %s", snd_strerror (err)), (NULL)); + return FALSE; + } +hw_params_failed: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("Setting of hwparams failed: %s", snd_strerror (err)), (NULL)); + return FALSE; + } +sw_params_failed: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("Setting of swparams failed: %s", snd_strerror (err)), (NULL)); + return FALSE; + } +prepare_failed: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("Prepare failed: %s", snd_strerror (err)), (NULL)); + return FALSE; + } +} + +static gboolean +gst_alsasrc_close (GstAudioSrc * asrc) +{ + GstAlsaSrc *alsa; + + alsa = GST_ALSA_SRC (asrc); + + snd_pcm_close (alsa->handle); + + return TRUE; +} + + +/* + * Underrun and suspend recovery + */ +static gint +xrun_recovery (snd_pcm_t * handle, gint err) +{ + GST_DEBUG ("xrun recovery %d", err); + + if (err == -EPIPE) { /* under-run */ + err = snd_pcm_prepare (handle); + if (err < 0) + GST_WARNING ("Can't recovery from underrun, prepare failed: %s", + snd_strerror (err)); + return 0; + } else if (err == -ESTRPIPE) { + while ((err = snd_pcm_resume (handle)) == -EAGAIN) + g_usleep (100); /* wait until the suspend flag is released */ + + if (err < 0) { + err = snd_pcm_prepare (handle); + if (err < 0) + GST_WARNING ("Can't recovery from suspend, prepare failed: %s", + snd_strerror (err)); + } + return 0; + } + return err; +} + +static guint +gst_alsasrc_read (GstAudioSrc * asrc, gpointer data, guint length) +{ + GstAlsaSrc *alsa; + gint err; + gint cptr; + gint16 *ptr; + + alsa = GST_ALSA_SRC (asrc); + + cptr = length / alsa->bytes_per_sample; + ptr = data; + + while (cptr > 0) { + if ((err = snd_pcm_readi (alsa->handle, ptr, cptr)) < 0) { + if (err == -EAGAIN) { + GST_DEBUG ("Read error: %s", snd_strerror (err)); + continue; + } else if (xrun_recovery (alsa->handle, err) < 0) { + goto read_error; + } + continue; + } + + ptr += err * alsa->channels; + cptr -= err; + } + return length - cptr; + +read_error: + { + return length; /* skip one period */ + } +} + +static guint +gst_alsasrc_delay (GstAudioSrc * asrc) +{ + GstAlsaSrc *alsa; + snd_pcm_sframes_t delay; + + alsa = GST_ALSA_SRC (asrc); + + snd_pcm_delay (alsa->handle, &delay); + + return delay; +} + +static void +gst_alsasrc_reset (GstAudioSrc * asrc) +{ + +#if 0 + GstAlsaSrc *alsa; + gint err; + + alsa = GST_ALSA_SRC (asrc); + + CHECK (snd_pcm_drop (alsa->handle), drop_error); + CHECK (snd_pcm_prepare (alsa->handle), prepare_error); + + return; + + /* ERRORS */ +drop_error: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("alsa-reset: pcm drop error: %s", snd_strerror (err)), (NULL)); + return; + } +prepare_error: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ, + ("alsa-reset: pcm prepare error: %s", snd_strerror (err)), (NULL)); + return; + } +#endif } diff --git a/ext/alsa/gstalsasrc.h b/ext/alsa/gstalsasrc.h index d4e72e46af..55e28575e3 100644 --- a/ext/alsa/gstalsasrc.h +++ b/ext/alsa/gstalsasrc.h @@ -1,8 +1,7 @@ -/* - * Copyright (C) 2001 CodeFactory AB - * Copyright (C) 2001 Thomas Nyberg - * Copyright (C) 2001-2002 Andy Wingo - * Copyright (C) 2003 Benjamin Otte +/* GStreamer + * Copyright (C) 2005 Wim Taymans + * + * gstalsasrc.h: * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -11,45 +10,62 @@ * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. */ -#ifndef __GST_ALSA_SRC_H__ -#define __GST_ALSA_SRC_H__ -#include "gstalsamixer.h" +#ifndef __GST_ALSASRC_H__ +#define __GST_ALSASRC_H__ + + +#include +#include +#include G_BEGIN_DECLS -#define GST_ALSA_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, GST_TYPE_ALSA_SRC, GstAlsaSrc)) -#define GST_ALSA_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST(klass, GST_TYPE_ALSA_SRC, GstAlsaSrcClass)) -#define GST_IS_ALSA_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, GST_TYPE_ALSA_SRC)) -#define GST_IS_ALSA_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE(klass, GST_TYPE_ALSA_SRC)) -#define GST_TYPE_ALSA_SRC (gst_alsa_src_get_type()) +#define GST_TYPE_ALSA_SRC (gst_alsasrc_get_type()) +#define GST_ALSA_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ALSA_SRC,GstAlsaSrc)) +#define GST_ALSA_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ALSA_SRC,GstAlsaSrcClass)) +#define GST_IS_ALSA_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ALSA_SRC)) +#define GST_IS_ALSA_SRC_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ALSA_SRC)) typedef struct _GstAlsaSrc GstAlsaSrc; typedef struct _GstAlsaSrcClass GstAlsaSrcClass; struct _GstAlsaSrc { - GstAlsaMixer parent; - GstBuffer *buf[GST_ALSA_MAX_TRACKS]; - snd_pcm_status_t *status; - GstClockTime base_time; /* FIXME: move this up ? already present in element ? */ + GstAudioSrc src; + + gchar *device; + + snd_pcm_t *handle; + snd_pcm_hw_params_t *hwparams; + snd_pcm_sw_params_t *swparams; + + snd_pcm_access_t access; + snd_pcm_format_t format; + guint rate; + guint channels; + gint bytes_per_sample; + + guint buffer_time; + guint period_time; + snd_pcm_uframes_t buffer_size; + snd_pcm_uframes_t period_size; }; struct _GstAlsaSrcClass { - GstAlsaMixerClass parent_class; + GstAudioSrcClass parent_class; }; -GType gst_alsa_src_get_type (void); - -gboolean gst_alsa_src_factory_init (GstPlugin *plugin); +GType gst_alsasrc_get_type(void); G_END_DECLS -#endif /* __GST_ALSA_SRC_H__ */ +#endif /* __GST_ALSASRC_H__ */ diff --git a/gst-libs/gst/audio/Makefile.am b/gst-libs/gst/audio/Makefile.am index 8b177cd4a9..7131e7b2b4 100644 --- a/gst-libs/gst/audio/Makefile.am +++ b/gst-libs/gst/audio/Makefile.am @@ -17,7 +17,9 @@ CLEANFILES = gstaudiofilterexample.c \ libgstaudio_@GST_MAJORMINOR@_la_SOURCES = audio.c gstaudioclock.c \ multichannel.c \ gstaudiosink.c \ + gstaudiosrc.c \ gstbaseaudiosink.c \ + gstbaseaudiosrc.c \ gstringbuffer.c nodist_libgstaudio_@GST_MAJORMINOR@_la_SOURCES = $(built_sources) $(built_headers) @@ -27,7 +29,9 @@ libgstaudio_@GST_MAJORMINOR@include_HEADERS = \ gstaudioclock.h \ gstaudiofilter.h \ gstaudiosink.h \ + gstaudiosrc.h \ gstbaseaudiosink.h \ + gstbaseaudiosrc.h \ gstringbuffer.h \ multichannel.h diff --git a/gst-libs/gst/audio/gstaudiosink.c b/gst-libs/gst/audio/gstaudiosink.c index 6f44be61fd..042e03c424 100644 --- a/gst-libs/gst/audio/gstaudiosink.c +++ b/gst-libs/gst/audio/gstaudiosink.c @@ -73,12 +73,12 @@ static GstRingBufferClass *ring_parent_class = NULL; static gboolean gst_audioringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec); static gboolean gst_audioringbuffer_release (GstRingBuffer * buf); -static gboolean gst_audioringbuffer_play (GstRingBuffer * buf); +static gboolean gst_audioringbuffer_start (GstRingBuffer * buf); static gboolean gst_audioringbuffer_stop (GstRingBuffer * buf); static guint gst_audioringbuffer_delay (GstRingBuffer * buf); /* ringbuffer abstract base class */ -GType +static GType gst_audioringbuffer_get_type (void) { static GType ringbuffer_type = 0; @@ -98,7 +98,7 @@ gst_audioringbuffer_get_type (void) }; ringbuffer_type = - g_type_register_static (GST_TYPE_RINGBUFFER, "GstAudioRingBuffer", + g_type_register_static (GST_TYPE_RINGBUFFER, "GstAudioSinkRingBuffer", &ringbuffer_info, 0); } return ringbuffer_type; @@ -124,8 +124,8 @@ gst_audioringbuffer_class_init (GstAudioRingBufferClass * klass) GST_DEBUG_FUNCPTR (gst_audioringbuffer_acquire); gstringbuffer_class->release = GST_DEBUG_FUNCPTR (gst_audioringbuffer_release); - gstringbuffer_class->play = GST_DEBUG_FUNCPTR (gst_audioringbuffer_play); - gstringbuffer_class->resume = GST_DEBUG_FUNCPTR (gst_audioringbuffer_play); + gstringbuffer_class->start = GST_DEBUG_FUNCPTR (gst_audioringbuffer_start); + gstringbuffer_class->resume = GST_DEBUG_FUNCPTR (gst_audioringbuffer_start); gstringbuffer_class->stop = GST_DEBUG_FUNCPTR (gst_audioringbuffer_stop); gstringbuffer_class->delay = GST_DEBUG_FUNCPTR (gst_audioringbuffer_delay); @@ -136,7 +136,7 @@ typedef guint (*WriteFunc) (GstAudioSink * sink, gpointer data, guint length); /* this internal thread does nothing else but write samples to the audio device. * It will write each segment in the ringbuffer and will update the play * pointer. - * The play/stop methods control the thread. + * The start/stop methods control the thread. */ static void audioringbuffer_thread_func (GstRingBuffer * buf) @@ -307,13 +307,13 @@ gst_audioringbuffer_release (GstRingBuffer * buf) } static gboolean -gst_audioringbuffer_play (GstRingBuffer * buf) +gst_audioringbuffer_start (GstRingBuffer * buf) { GstAudioSink *sink; sink = GST_AUDIOSINK (GST_OBJECT_PARENT (buf)); - GST_DEBUG ("play, sending signal"); + GST_DEBUG ("start, sending signal"); GST_AUDIORINGBUFFER_SIGNAL (buf); return TRUE; diff --git a/gst-libs/gst/audio/gstaudiosrc.c b/gst-libs/gst/audio/gstaudiosrc.c new file mode 100644 index 0000000000..68bc1b39d2 --- /dev/null +++ b/gst-libs/gst/audio/gstaudiosrc.c @@ -0,0 +1,418 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen + * 2005 Wim Taymans + * + * gstaudiosrc.c: simple audio src base class + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#include "gstaudiosrc.h" + +GST_DEBUG_CATEGORY_STATIC (gst_audiosrc_debug); +#define GST_CAT_DEFAULT gst_audiosrc_debug + +#define GST_TYPE_AUDIORINGBUFFER \ + (gst_audioringbuffer_get_type()) +#define GST_AUDIORINGBUFFER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIORINGBUFFER,GstAudioRingBuffer)) +#define GST_AUDIORINGBUFFER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AUDIORINGBUFFER,GstAudioRingBufferClass)) +#define GST_AUDIORINGBUFFER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_AUDIORINGBUFFER, GstAudioRingBufferClass)) +#define GST_IS_AUDIORINGBUFFER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIORINGBUFFER)) +#define GST_IS_AUDIORINGBUFFER_CLASS(obj)\ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUDIORINGBUFFER)) + +typedef struct _GstAudioRingBuffer GstAudioRingBuffer; +typedef struct _GstAudioRingBufferClass GstAudioRingBufferClass; + +#define GST_AUDIORINGBUFFER_GET_COND(buf) (((GstAudioRingBuffer *)buf)->cond) +#define GST_AUDIORINGBUFFER_WAIT(buf) (g_cond_wait (GST_AUDIORINGBUFFER_GET_COND (buf), GST_GET_LOCK (buf))) +#define GST_AUDIORINGBUFFER_SIGNAL(buf) (g_cond_signal (GST_AUDIORINGBUFFER_GET_COND (buf))) +#define GST_AUDIORINGBUFFER_BROADCAST(buf)(g_cond_broadcast (GST_AUDIORINGBUFFER_GET_COND (buf))) + +struct _GstAudioRingBuffer +{ + GstRingBuffer object; + + gboolean running; + gint queuedseg; + + GCond *cond; +}; + +struct _GstAudioRingBufferClass +{ + GstRingBufferClass parent_class; +}; + +static void gst_audioringbuffer_class_init (GstAudioRingBufferClass * klass); +static void gst_audioringbuffer_init (GstAudioRingBuffer * ringbuffer); +static void gst_audioringbuffer_dispose (GObject * object); +static void gst_audioringbuffer_finalize (GObject * object); + +static GstRingBufferClass *ring_parent_class = NULL; + +static gboolean gst_audioringbuffer_acquire (GstRingBuffer * buf, + GstRingBufferSpec * spec); +static gboolean gst_audioringbuffer_release (GstRingBuffer * buf); +static gboolean gst_audioringbuffer_start (GstRingBuffer * buf); +static gboolean gst_audioringbuffer_stop (GstRingBuffer * buf); +static guint gst_audioringbuffer_delay (GstRingBuffer * buf); + +/* ringbuffer abstract base class */ +static GType +gst_audioringbuffer_get_type (void) +{ + static GType ringbuffer_type = 0; + + if (!ringbuffer_type) { + static const GTypeInfo ringbuffer_info = { + sizeof (GstAudioRingBufferClass), + NULL, + NULL, + (GClassInitFunc) gst_audioringbuffer_class_init, + NULL, + NULL, + sizeof (GstAudioRingBuffer), + 0, + (GInstanceInitFunc) gst_audioringbuffer_init, + NULL + }; + + ringbuffer_type = + g_type_register_static (GST_TYPE_RINGBUFFER, "GstAudioSrcRingBuffer", + &ringbuffer_info, 0); + } + return ringbuffer_type; +} + +static void +gst_audioringbuffer_class_init (GstAudioRingBufferClass * klass) +{ + GObjectClass *gobject_class; + GstObjectClass *gstobject_class; + GstRingBufferClass *gstringbuffer_class; + + gobject_class = (GObjectClass *) klass; + gstobject_class = (GstObjectClass *) klass; + gstringbuffer_class = (GstRingBufferClass *) klass; + + ring_parent_class = g_type_class_ref (GST_TYPE_RINGBUFFER); + + gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_audioringbuffer_dispose); + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_audioringbuffer_finalize); + + gstringbuffer_class->acquire = + GST_DEBUG_FUNCPTR (gst_audioringbuffer_acquire); + gstringbuffer_class->release = + GST_DEBUG_FUNCPTR (gst_audioringbuffer_release); + gstringbuffer_class->start = GST_DEBUG_FUNCPTR (gst_audioringbuffer_start); + gstringbuffer_class->resume = GST_DEBUG_FUNCPTR (gst_audioringbuffer_start); + gstringbuffer_class->stop = GST_DEBUG_FUNCPTR (gst_audioringbuffer_stop); + + gstringbuffer_class->delay = GST_DEBUG_FUNCPTR (gst_audioringbuffer_delay); +} + +typedef guint (*ReadFunc) (GstAudioSrc * src, gpointer data, guint length); + +/* this internal thread does nothing else but read samples from the audio device. + * It will read each segment in the ringbuffer and will update the play + * pointer. + * The start/stop methods control the thread. + */ +static void +audioringbuffer_thread_func (GstRingBuffer * buf) +{ + GstAudioSrc *src; + GstAudioSrcClass *csrc; + GstAudioRingBuffer *abuf = GST_AUDIORINGBUFFER (buf); + ReadFunc readfunc; + + src = GST_AUDIOSRC (GST_OBJECT_PARENT (buf)); + csrc = GST_AUDIOSRC_GET_CLASS (src); + + GST_DEBUG ("enter thread"); + + readfunc = csrc->read; + if (readfunc == NULL) + goto no_function; + + while (TRUE) { + gint left, len; + guint8 *readptr; + gint readseg; + + if (gst_ringbuffer_prepare_read (buf, &readseg, &readptr, &len)) { + gint read = 0; + + left = len; + do { + GST_DEBUG ("transfer %d bytes to segment %d", left, readseg); + read = readfunc (src, readptr + read, left); + GST_DEBUG ("transfered %d bytes", read); + if (read < 0 || read > left) { + GST_WARNING ("error reading data (reason: %s), skipping segment\n", + strerror (errno)); + break; + } + left -= read; + } while (left > 0); + + /* we read one segment */ + gst_ringbuffer_advance (buf, 1); + } else { + GST_LOCK (abuf); + if (!abuf->running) + goto stop_running; + GST_DEBUG ("signal wait"); + GST_AUDIORINGBUFFER_SIGNAL (buf); + GST_DEBUG ("wait for action"); + GST_AUDIORINGBUFFER_WAIT (buf); + GST_DEBUG ("got signal"); + if (!abuf->running) + goto stop_running; + GST_DEBUG ("continue running"); + GST_UNLOCK (abuf); + } + } + GST_DEBUG ("exit thread"); + + return; + + /* ERROR */ +no_function: + { + GST_DEBUG ("no write function, exit thread"); + return; + } +stop_running: + { + GST_UNLOCK (abuf); + GST_DEBUG ("stop running, exit thread"); + return; + } +} + +static void +gst_audioringbuffer_init (GstAudioRingBuffer * ringbuffer) +{ + ringbuffer->running = FALSE; + ringbuffer->queuedseg = 0; + + ringbuffer->cond = g_cond_new (); +} + +static void +gst_audioringbuffer_dispose (GObject * object) +{ + G_OBJECT_CLASS (ring_parent_class)->dispose (object); +} + +static void +gst_audioringbuffer_finalize (GObject * object) +{ + G_OBJECT_CLASS (ring_parent_class)->finalize (object); +} + +static gboolean +gst_audioringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) +{ + GstAudioSrc *src; + GstAudioSrcClass *csrc; + GstAudioRingBuffer *abuf; + gboolean result = FALSE; + + src = GST_AUDIOSRC (GST_OBJECT_PARENT (buf)); + csrc = GST_AUDIOSRC_GET_CLASS (src); + + if (csrc->open) + result = csrc->open (src, spec); + + if (!result) + goto could_not_open; + + /* allocate one more segment as we need some headroom */ + spec->segtotal++; + + buf->data = gst_buffer_new_and_alloc (spec->segtotal * spec->segsize); + memset (GST_BUFFER_DATA (buf->data), 0, GST_BUFFER_SIZE (buf->data)); + + abuf = GST_AUDIORINGBUFFER (buf); + abuf->running = TRUE; + + src->thread = + g_thread_create ((GThreadFunc) audioringbuffer_thread_func, buf, TRUE, + NULL); + GST_AUDIORINGBUFFER_WAIT (buf); + + return result; + +could_not_open: + { + return FALSE; + } +} + +/* function is called with LOCK */ +static gboolean +gst_audioringbuffer_release (GstRingBuffer * buf) +{ + GstAudioSrc *src; + GstAudioSrcClass *csrc; + GstAudioRingBuffer *abuf; + gboolean result = FALSE; + + src = GST_AUDIOSRC (GST_OBJECT_PARENT (buf)); + csrc = GST_AUDIOSRC_GET_CLASS (src); + abuf = GST_AUDIORINGBUFFER (buf); + + abuf->running = FALSE; + GST_AUDIORINGBUFFER_SIGNAL (buf); + GST_UNLOCK (buf); + + /* join the thread */ + g_thread_join (src->thread); + + GST_LOCK (buf); + + /* free the buffer */ + gst_buffer_unref (buf->data); + buf->data = NULL; + + if (csrc->close) + result = csrc->close (src); + + return result; +} + +static gboolean +gst_audioringbuffer_start (GstRingBuffer * buf) +{ + GstAudioSrc *src; + + src = GST_AUDIOSRC (GST_OBJECT_PARENT (buf)); + + GST_DEBUG ("start, sending signal"); + GST_AUDIORINGBUFFER_SIGNAL (buf); + + return TRUE; +} + +static gboolean +gst_audioringbuffer_stop (GstRingBuffer * buf) +{ + GstAudioSrc *src; + GstAudioSrcClass *csrc; + + src = GST_AUDIOSRC (GST_OBJECT_PARENT (buf)); + csrc = GST_AUDIOSRC_GET_CLASS (src); + + /* unblock any pending writes to the audio device */ + if (csrc->reset) { + GST_DEBUG ("reset..."); + csrc->reset (src); + GST_DEBUG ("reset done"); + } + + GST_DEBUG ("stop, waiting..."); + GST_AUDIORINGBUFFER_WAIT (buf); + GST_DEBUG ("stoped"); + + return TRUE; +} + +static guint +gst_audioringbuffer_delay (GstRingBuffer * buf) +{ + GstAudioSrc *src; + GstAudioSrcClass *csrc; + guint res = 0; + + src = GST_AUDIOSRC (GST_OBJECT_PARENT (buf)); + csrc = GST_AUDIOSRC_GET_CLASS (src); + + if (csrc->delay) + res = csrc->delay (src); + + return res; +} + +/* AudioSrc signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + ARG_0, +}; + +#define _do_init(bla) \ + GST_DEBUG_CATEGORY_INIT (gst_audiosrc_debug, "audiosrc", 0, "audiosrc element"); + +GST_BOILERPLATE_FULL (GstAudioSrc, gst_audiosrc, GstBaseAudioSrc, + GST_TYPE_BASEAUDIOSRC, _do_init); + +static GstRingBuffer *gst_audiosrc_create_ringbuffer (GstBaseAudioSrc * src); + +static void +gst_audiosrc_base_init (gpointer g_class) +{ +} + +static void +gst_audiosrc_class_init (GstAudioSrcClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBaseSrcClass *gstbasesrc_class; + GstPushSrcClass *gstpushsrc_class; + GstBaseAudioSrcClass *gstbaseaudiosrc_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstbasesrc_class = (GstBaseSrcClass *) klass; + gstpushsrc_class = (GstPushSrcClass *) klass; + gstbaseaudiosrc_class = (GstBaseAudioSrcClass *) klass; + + gstbaseaudiosrc_class->create_ringbuffer = + GST_DEBUG_FUNCPTR (gst_audiosrc_create_ringbuffer); +} + +static void +gst_audiosrc_init (GstAudioSrc * audiosrc) +{ + gst_base_src_set_live (GST_BASE_SRC (audiosrc), TRUE); +} + +static GstRingBuffer * +gst_audiosrc_create_ringbuffer (GstBaseAudioSrc * src) +{ + GstRingBuffer *buffer; + + GST_DEBUG ("creating ringbuffer"); + buffer = g_object_new (GST_TYPE_AUDIORINGBUFFER, NULL); + GST_DEBUG ("created ringbuffer @%p", buffer); + + return buffer; +} diff --git a/gst-libs/gst/audio/gstaudiosrc.h b/gst-libs/gst/audio/gstaudiosrc.h new file mode 100644 index 0000000000..784afa12a0 --- /dev/null +++ b/gst-libs/gst/audio/gstaudiosrc.h @@ -0,0 +1,84 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen + * 2005 Wim Taymans + * + * gstaudiosrc.h: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* a base class for simple audio srcs. + * + * This base class only requires subclasses to implement a set + * of simple functions. + * + * - open: open the device with the specified caps + * - read: read samples to the audio device + * - close: close the device + * - delay: the number of samples queued in the device + * - reset: unblock a read to the device and reset. + * + * All scheduling of samples and timestamps is done in this + * base class. + */ + +#ifndef __GST_AUDIOSRC_H__ +#define __GST_AUDIOSRC_H__ + +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_AUDIOSRC (gst_audiosrc_get_type()) +#define GST_AUDIOSRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIOSRC,GstAudioSrc)) +#define GST_AUDIOSRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AUDIOSRC,GstAudioSrcClass)) +#define GST_AUDIOSRC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_AUDIOSRC,GstAudioSrcClass)) +#define GST_IS_AUDIOSRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIOSRC)) +#define GST_IS_AUDIOSRC_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUDIOSRC)) + +typedef struct _GstAudioSrc GstAudioSrc; +typedef struct _GstAudioSrcClass GstAudioSrcClass; + +struct _GstAudioSrc { + GstBaseAudioSrc element; + + /*< private >*/ /* with LOCK */ + GThread *thread; +}; + +struct _GstAudioSrcClass { + GstBaseAudioSrcClass parent_class; + + /* vtable */ + + /* open the device with given specs */ + gboolean (*open) (GstAudioSrc *src, GstRingBufferSpec *spec); + /* close the device */ + gboolean (*close) (GstAudioSrc *src); + /* read samples from the device */ + guint (*read) (GstAudioSrc *src, gpointer data, guint length); + /* get number of samples queued in the device */ + guint (*delay) (GstAudioSrc *src); + /* reset the audio device, unblock from a write */ + void (*reset) (GstAudioSrc *src); +}; + +GType gst_audiosrc_get_type(void); + +G_END_DECLS + +#endif /* __GST_AUDIOSRC_H__ */ diff --git a/gst-libs/gst/audio/gstbaseaudiosink.c b/gst-libs/gst/audio/gstbaseaudiosink.c index 8590cefcc5..0a527e451c 100644 --- a/gst-libs/gst/audio/gstbaseaudiosink.c +++ b/gst-libs/gst/audio/gstbaseaudiosink.c @@ -160,7 +160,7 @@ gst_baseaudiosink_get_time (GstClock * clock, GstBaseAudioSink * sink) if (sink->ringbuffer == NULL || sink->ringbuffer->spec.rate == 0) return 0; - samples = gst_ringbuffer_played_samples (sink->ringbuffer); + samples = gst_ringbuffer_samples_done (sink->ringbuffer); result = samples * GST_SECOND / sink->ringbuffer->spec.rate; result += GST_ELEMENT (sink)->base_time; @@ -210,204 +210,31 @@ gst_baseaudiosink_get_property (GObject * object, guint prop_id, GValue * value, } } -static int linear_formats[4 * 2 * 2] = { - GST_S8, - GST_S8, - GST_U8, - GST_U8, - GST_S16_LE, - GST_S16_BE, - GST_U16_LE, - GST_U16_BE, - GST_S24_LE, - GST_S24_BE, - GST_U24_LE, - GST_U24_BE, - GST_S32_LE, - GST_S32_BE, - GST_U32_LE, - GST_U32_BE -}; - -static int linear24_formats[3 * 2 * 2] = { - GST_S24_3LE, - GST_S24_3BE, - GST_U24_3LE, - GST_U24_3BE, - GST_S20_3LE, - GST_S20_3BE, - GST_U20_3LE, - GST_U20_3BE, - GST_S18_3LE, - GST_S18_3BE, - GST_U18_3LE, - GST_U18_3BE, -}; - -static GstBufferFormat -build_linear_format (int depth, int width, int unsignd, int big_endian) -{ - if (width == 24) { - switch (depth) { - case 24: - depth = 0; - break; - case 20: - depth = 1; - break; - case 18: - depth = 2; - break; - default: - return GST_UNKNOWN; - } - return ((int (*)[2][2]) linear24_formats)[depth][!!unsignd][!!big_endian]; - } else { - switch (depth) { - case 8: - depth = 0; - break; - case 16: - depth = 1; - break; - case 24: - depth = 2; - break; - case 32: - depth = 3; - break; - default: - return GST_UNKNOWN; - } - } - return ((int (*)[2][2]) linear_formats)[depth][!!unsignd][!!big_endian]; -} - -static void -debug_spec_caps (GstBaseAudioSink * sink, GstRingBufferSpec * spec) -{ - GST_DEBUG ("spec caps: %p %" GST_PTR_FORMAT, spec->caps, spec->caps); - GST_DEBUG ("parsed caps: type: %d", spec->type); - GST_DEBUG ("parsed caps: format: %d", spec->format); - GST_DEBUG ("parsed caps: width: %d", spec->width); - GST_DEBUG ("parsed caps: depth: %d", spec->depth); - GST_DEBUG ("parsed caps: sign: %d", spec->sign); - GST_DEBUG ("parsed caps: bigend: %d", spec->bigend); - GST_DEBUG ("parsed caps: rate: %d", spec->rate); - GST_DEBUG ("parsed caps: channels: %d", spec->channels); - GST_DEBUG ("parsed caps: sample bytes: %d", spec->bytes_per_sample); -} - -static void -debug_spec_buffer (GstBaseAudioSink * sink, GstRingBufferSpec * spec) -{ - GST_DEBUG ("acquire ringbuffer: buffer time: %" G_GINT64_FORMAT " usec", - spec->buffer_time); - GST_DEBUG ("acquire ringbuffer: latency time: %" G_GINT64_FORMAT " usec", - spec->latency_time); - GST_DEBUG ("acquire ringbuffer: total segments: %d", spec->segtotal); - GST_DEBUG ("acquire ringbuffer: segment size: %d bytes = %d samples", - spec->segsize, spec->segsize / spec->bytes_per_sample); - GST_DEBUG ("acquire ringbuffer: buffer size: %d bytes = %d samples", - spec->segsize * spec->segtotal, - spec->segsize * spec->segtotal / spec->bytes_per_sample); -} - static gboolean gst_baseaudiosink_setcaps (GstBaseSink * bsink, GstCaps * caps) { GstBaseAudioSink *sink = GST_BASEAUDIOSINK (bsink); GstRingBufferSpec *spec; - const gchar *mimetype; - GstStructure *structure; spec = &sink->ringbuffer->spec; - structure = gst_caps_get_structure (caps, 0); + GST_DEBUG ("release old ringbuffer"); - /* we have to differentiate between int and float formats */ - mimetype = gst_structure_get_name (structure); + /* release old ringbuffer */ + gst_ringbuffer_release (sink->ringbuffer); - if (!strncmp (mimetype, "audio/x-raw-int", 15)) { - gint endianness; - - spec->type = GST_BUFTYPE_LINEAR; - - /* extract the needed information from the cap */ - if (!(gst_structure_get_int (structure, "width", &spec->width) && - gst_structure_get_int (structure, "depth", &spec->depth) && - gst_structure_get_boolean (structure, "signed", &spec->sign))) - goto parse_error; - - /* extract endianness if needed */ - if (spec->width > 8) { - if (!gst_structure_get_int (structure, "endianness", &endianness)) - goto parse_error; - } else { - endianness = G_BYTE_ORDER; - } - - spec->bigend = endianness == G_LITTLE_ENDIAN ? FALSE : TRUE; - - spec->format = - build_linear_format (spec->depth, spec->width, spec->sign ? 0 : 1, - spec->bigend ? 1 : 0); - - } else if (!strncmp (mimetype, "audio/x-raw-float", 17)) { - - spec->type = GST_BUFTYPE_FLOAT; - - /* get layout */ - if (!gst_structure_get_int (structure, "width", &spec->width)) - goto parse_error; - - /* match layout to format wrt to endianness */ - switch (spec->width) { - case 32: - spec->format = - G_BYTE_ORDER == G_LITTLE_ENDIAN ? GST_FLOAT32_LE : GST_FLOAT32_BE; - break; - case 64: - spec->format = - G_BYTE_ORDER == G_LITTLE_ENDIAN ? GST_FLOAT64_LE : GST_FLOAT64_BE; - break; - default: - goto parse_error; - } - } else if (!strncmp (mimetype, "audio/x-alaw", 12)) { - spec->type = GST_BUFTYPE_A_LAW; - spec->format = GST_A_LAW; - } else if (!strncmp (mimetype, "audio/x-mulaw", 13)) { - spec->type = GST_BUFTYPE_MU_LAW; - spec->format = GST_MU_LAW; - } else { - goto parse_error; - } - - /* get rate and channels */ - if (!(gst_structure_get_int (structure, "rate", &spec->rate) && - gst_structure_get_int (structure, "channels", &spec->channels))) - goto parse_error; - - spec->bytes_per_sample = (spec->width >> 3) * spec->channels; - - gst_caps_replace (&spec->caps, caps); - - debug_spec_caps (sink, spec); + GST_DEBUG ("parse caps"); spec->buffer_time = sink->buffer_time; spec->latency_time = sink->latency_time; - /* calculate suggested segsize and segtotal */ - spec->segsize = - spec->rate * spec->bytes_per_sample * spec->latency_time / GST_MSECOND; - spec->segtotal = spec->buffer_time / spec->latency_time; + /* parse new caps */ + if (!gst_ringbuffer_parse_caps (spec, caps)) + goto parse_error; - GST_DEBUG ("release old ringbuffer"); + gst_ringbuffer_debug_spec_buff (spec); - gst_ringbuffer_release (sink->ringbuffer); - - debug_spec_buffer (sink, spec); + GST_DEBUG ("acquire new ringbuffer"); if (!gst_ringbuffer_acquire (sink->ringbuffer, spec)) goto acquire_error; @@ -419,7 +246,7 @@ gst_baseaudiosink_setcaps (GstBaseSink * bsink, GstCaps * caps) spec->segtotal * spec->segsize * GST_MSECOND / (spec->rate * spec->bytes_per_sample); - debug_spec_buffer (sink, spec); + gst_ringbuffer_debug_spec_buff (spec); return TRUE; @@ -487,10 +314,23 @@ gst_baseaudiosink_event (GstBaseSink * bsink, GstEvent * event) static GstFlowReturn gst_baseaudiosink_preroll (GstBaseSink * bsink, GstBuffer * buffer) { + GstBaseAudioSink *sink = GST_BASEAUDIOSINK (bsink); + + if (!gst_ringbuffer_is_acquired (sink->ringbuffer)) + goto wrong_state; + /* we don't really do anything when prerolling. We could make a * property to play this buffer to have some sort of scrubbing * support. */ return GST_FLOW_OK; + +wrong_state: + { + GST_DEBUG ("ringbuffer in wrong state"); + GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND, + ("sink not negotiated."), (NULL)); + return GST_FLOW_ERROR; + } } static GstFlowReturn @@ -514,6 +354,8 @@ gst_baseaudiosink_render (GstBaseSink * bsink, GstBuffer * buf) wrong_state: { GST_DEBUG ("ringbuffer in wrong state"); + GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND, + ("sink not negotiated."), (NULL)); return GST_FLOW_ERROR; } } diff --git a/gst-libs/gst/audio/gstbaseaudiosrc.c b/gst-libs/gst/audio/gstbaseaudiosrc.c new file mode 100644 index 0000000000..98f8d33869 --- /dev/null +++ b/gst-libs/gst/audio/gstbaseaudiosrc.c @@ -0,0 +1,403 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen + * 2005 Wim Taymans + * + * gstbaseaudiosrc.c: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#include "gstbaseaudiosrc.h" + +GST_DEBUG_CATEGORY_STATIC (gst_baseaudiosrc_debug); +#define GST_CAT_DEFAULT gst_baseaudiosrc_debug + +/* BaseAudioSrc signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +#define DEFAULT_BUFFER_TIME 500 * GST_USECOND +#define DEFAULT_LATENCY_TIME 10 * GST_USECOND +enum +{ + PROP_0, + PROP_BUFFER_TIME, + PROP_LATENCY_TIME, +}; + +#define _do_init(bla) \ + GST_DEBUG_CATEGORY_INIT (gst_baseaudiosrc_debug, "baseaudiosrc", 0, "baseaudiosrc element"); + +GST_BOILERPLATE_FULL (GstBaseAudioSrc, gst_baseaudiosrc, GstPushSrc, + GST_TYPE_PUSHSRC, _do_init); + +static void gst_baseaudiosrc_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_baseaudiosrc_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static void gst_baseaudiosrc_fixate (GstPad * pad, GstCaps * caps); + +static GstElementStateReturn gst_baseaudiosrc_change_state (GstElement * + element); + +static GstClock *gst_baseaudiosrc_get_clock (GstElement * elem); +static GstClockTime gst_baseaudiosrc_get_time (GstClock * clock, + GstBaseAudioSrc * src); + +static GstFlowReturn gst_baseaudiosrc_create (GstPushSrc * psrc, + GstBuffer ** buf); + +static gboolean gst_baseaudiosrc_event (GstBaseSrc * bsrc, GstEvent * event); +static void gst_baseaudiosrc_get_times (GstBaseSrc * bsrc, + GstBuffer * buffer, GstClockTime * start, GstClockTime * end); +static gboolean gst_baseaudiosrc_setcaps (GstBaseSrc * bsrc, GstCaps * caps); + +//static guint gst_baseaudiosrc_signals[LAST_SIGNAL] = { 0 }; + +static void +gst_baseaudiosrc_base_init (gpointer g_class) +{ +} + +static void +gst_baseaudiosrc_class_init (GstBaseAudioSrcClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBaseSrcClass *gstbasesrc_class; + GstPushSrcClass *gstpushsrc_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstbasesrc_class = (GstBaseSrcClass *) klass; + gstpushsrc_class = (GstPushSrcClass *) klass; + + gobject_class->set_property = + GST_DEBUG_FUNCPTR (gst_baseaudiosrc_set_property); + gobject_class->get_property = + GST_DEBUG_FUNCPTR (gst_baseaudiosrc_get_property); + + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BUFFER_TIME, + g_param_spec_int64 ("buffer-time", "Buffer Time", + "Size of audio buffer in milliseconds (-1 = default)", + -1, G_MAXINT64, DEFAULT_BUFFER_TIME, G_PARAM_READWRITE)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LATENCY_TIME, + g_param_spec_int64 ("latency-time", "Latency Time", + "Audio latency in milliseconds (-1 = default)", + -1, G_MAXINT64, DEFAULT_LATENCY_TIME, G_PARAM_READWRITE)); + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_baseaudiosrc_change_state); + gstelement_class->get_clock = GST_DEBUG_FUNCPTR (gst_baseaudiosrc_get_clock); + + gstbasesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_baseaudiosrc_setcaps); + gstbasesrc_class->event = GST_DEBUG_FUNCPTR (gst_baseaudiosrc_event); + gstbasesrc_class->get_times = GST_DEBUG_FUNCPTR (gst_baseaudiosrc_get_times); + + gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_baseaudiosrc_create); +} + +static void +gst_baseaudiosrc_init (GstBaseAudioSrc * baseaudiosrc) +{ + baseaudiosrc->buffer_time = DEFAULT_BUFFER_TIME; + baseaudiosrc->latency_time = DEFAULT_LATENCY_TIME; + + baseaudiosrc->clock = gst_audio_clock_new ("clock", + (GstAudioClockGetTimeFunc) gst_baseaudiosrc_get_time, baseaudiosrc); + + gst_pad_set_fixatecaps_function (GST_BASE_SRC_PAD (baseaudiosrc), + gst_baseaudiosrc_fixate); +} + +static GstClock * +gst_baseaudiosrc_get_clock (GstElement * elem) +{ + GstBaseAudioSrc *src; + + src = GST_BASEAUDIOSRC (elem); + + return GST_CLOCK (gst_object_ref (GST_OBJECT (src->clock))); +} + +static GstClockTime +gst_baseaudiosrc_get_time (GstClock * clock, GstBaseAudioSrc * src) +{ + guint64 samples; + GstClockTime result; + + if (src->ringbuffer == NULL || src->ringbuffer->spec.rate == 0) + return 0; + + samples = gst_ringbuffer_samples_done (src->ringbuffer); + + result = samples * GST_SECOND / src->ringbuffer->spec.rate; + result += GST_ELEMENT (src)->base_time; + + return result; +} + +static void +gst_baseaudiosrc_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstBaseAudioSrc *src; + + src = GST_BASEAUDIOSRC (object); + + switch (prop_id) { + case PROP_BUFFER_TIME: + src->buffer_time = g_value_get_int64 (value); + break; + case PROP_LATENCY_TIME: + src->latency_time = g_value_get_int64 (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_baseaudiosrc_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstBaseAudioSrc *src; + + src = GST_BASEAUDIOSRC (object); + + switch (prop_id) { + case PROP_BUFFER_TIME: + g_value_set_int64 (value, src->buffer_time); + break; + case PROP_LATENCY_TIME: + g_value_set_int64 (value, src->latency_time); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_baseaudiosrc_fixate (GstPad * pad, GstCaps * caps) +{ + GstStructure *s; + + s = gst_caps_get_structure (caps, 0); + + gst_caps_structure_fixate_field_nearest_int (s, "rate", 44100); + gst_caps_structure_fixate_field_nearest_int (s, "channels", 2); + gst_caps_structure_fixate_field_nearest_int (s, "depth", 16); + gst_caps_structure_fixate_field_nearest_int (s, "width", 16); + gst_structure_set (s, "signed", G_TYPE_BOOLEAN, TRUE, NULL); + gst_caps_structure_fixate_field_nearest_int (s, "endianness", G_BYTE_ORDER); +} + +static gboolean +gst_baseaudiosrc_setcaps (GstBaseSrc * bsrc, GstCaps * caps) +{ + GstBaseAudioSrc *src = GST_BASEAUDIOSRC (bsrc); + GstRingBufferSpec *spec; + + spec = &src->ringbuffer->spec; + + spec->buffer_time = src->buffer_time; + spec->latency_time = src->latency_time; + + if (!gst_ringbuffer_parse_caps (spec, caps)) + goto parse_error; + + /* calculate suggested segsize and segtotal */ + spec->segsize = + spec->rate * spec->bytes_per_sample * spec->latency_time / GST_MSECOND; + spec->segtotal = spec->buffer_time / spec->latency_time; + + GST_DEBUG ("release old ringbuffer"); + + gst_ringbuffer_release (src->ringbuffer); + + gst_ringbuffer_debug_spec_buff (spec); + + GST_DEBUG ("acquire new ringbuffer"); + + if (!gst_ringbuffer_acquire (src->ringbuffer, spec)) + goto acquire_error; + + /* calculate actual latency and buffer times */ + spec->latency_time = + spec->segsize * GST_MSECOND / (spec->rate * spec->bytes_per_sample); + spec->buffer_time = + spec->segtotal * spec->segsize * GST_MSECOND / (spec->rate * + spec->bytes_per_sample); + + gst_ringbuffer_debug_spec_buff (spec); + + return TRUE; + + /* ERRORS */ +parse_error: + { + GST_DEBUG ("could not parse caps"); + return FALSE; + } +acquire_error: + { + GST_DEBUG ("could not acquire ringbuffer"); + return FALSE; + } +} + +static void +gst_baseaudiosrc_get_times (GstBaseSrc * bsrc, GstBuffer * buffer, + GstClockTime * start, GstClockTime * end) +{ + /* ne need to sync to a clock here, we schedule the samples based + * on our own clock for the moment. FIXME, implement this when + * we are not using our own clock */ + *start = GST_CLOCK_TIME_NONE; + *end = GST_CLOCK_TIME_NONE; +} + +static gboolean +gst_baseaudiosrc_event (GstBaseSrc * bsrc, GstEvent * event) +{ + GstBaseAudioSrc *src = GST_BASEAUDIOSRC (bsrc); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH: + if (GST_EVENT_FLUSH_DONE (event)) { + } else { + gst_ringbuffer_pause (src->ringbuffer); + } + break; + default: + break; + } + return TRUE; +} + +static GstFlowReturn +gst_baseaudiosrc_create (GstPushSrc * psrc, GstBuffer ** outbuf) +{ + GstBaseAudioSrc *src = GST_BASEAUDIOSRC (psrc); + GstBuffer *buf; + guchar *data; + guint len; + guint res; + + if (!gst_ringbuffer_is_acquired (src->ringbuffer)) + goto wrong_state; + + buf = gst_buffer_new_and_alloc (src->ringbuffer->spec.segsize); + + data = GST_BUFFER_DATA (buf); + len = GST_BUFFER_SIZE (buf); + + res = gst_ringbuffer_read (src->ringbuffer, -1, data, len); + if (res == -1) + goto stopped; + + gst_buffer_set_caps (buf, GST_PAD_CAPS (GST_BASE_SRC_PAD (psrc))); + + *outbuf = buf; + + return GST_FLOW_OK; + +wrong_state: + { + GST_DEBUG ("ringbuffer in wrong state"); + return GST_FLOW_WRONG_STATE; + } +stopped: + { + gst_buffer_unref (buf); + GST_DEBUG ("ringbuffer stopped"); + return GST_FLOW_WRONG_STATE; + } +} + +GstRingBuffer * +gst_baseaudiosrc_create_ringbuffer (GstBaseAudioSrc * src) +{ + GstBaseAudioSrcClass *bclass; + GstRingBuffer *buffer = NULL; + + bclass = GST_BASEAUDIOSRC_GET_CLASS (src); + if (bclass->create_ringbuffer) + buffer = bclass->create_ringbuffer (src); + + if (buffer) { + gst_object_set_parent (GST_OBJECT (buffer), GST_OBJECT (src)); + } + + return buffer; +} + +void +gst_baseaudiosrc_callback (GstRingBuffer * rbuf, guint8 * data, guint len, + gpointer user_data) +{ + //GstBaseAudioSrc *src = GST_BASEAUDIOSRC (data); +} + +static GstElementStateReturn +gst_baseaudiosrc_change_state (GstElement * element) +{ + GstElementStateReturn ret = GST_STATE_SUCCESS; + GstBaseAudioSrc *src = GST_BASEAUDIOSRC (element); + GstElementState transition = GST_STATE_TRANSITION (element); + + switch (transition) { + case GST_STATE_NULL_TO_READY: + break; + case GST_STATE_READY_TO_PAUSED: + src->ringbuffer = gst_baseaudiosrc_create_ringbuffer (src); + gst_ringbuffer_set_callback (src->ringbuffer, gst_baseaudiosrc_callback, + src); + break; + case GST_STATE_PAUSED_TO_PLAYING: + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element); + + switch (transition) { + case GST_STATE_PLAYING_TO_PAUSED: + gst_ringbuffer_pause (src->ringbuffer); + break; + case GST_STATE_PAUSED_TO_READY: + gst_ringbuffer_stop (src->ringbuffer); + gst_ringbuffer_release (src->ringbuffer); + gst_object_unref (GST_OBJECT (src->ringbuffer)); + src->ringbuffer = NULL; + break; + case GST_STATE_READY_TO_NULL: + break; + default: + break; + } + + return ret; +} diff --git a/gst-libs/gst/audio/gstbaseaudiosrc.h b/gst-libs/gst/audio/gstbaseaudiosrc.h new file mode 100644 index 0000000000..9dcb9f1cab --- /dev/null +++ b/gst-libs/gst/audio/gstbaseaudiosrc.h @@ -0,0 +1,77 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen + * 2005 Wim Taymans + * + * gstbaseaudiosrc.h: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* a base class for audio sources. + */ + +#ifndef __GST_BASEAUDIOSRC_H__ +#define __GST_BASEAUDIOSRC_H__ + +#include +#include +#include "gstringbuffer.h" +#include "gstaudioclock.h" + +G_BEGIN_DECLS + +#define GST_TYPE_BASEAUDIOSRC (gst_baseaudiosrc_get_type()) +#define GST_BASEAUDIOSRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_BASEAUDIOSRC,GstBaseAudioSrc)) +#define GST_BASEAUDIOSRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_BASEAUDIOSRC,GstBaseAudioSrcClass)) +#define GST_BASEAUDIOSRC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_BASEAUDIOSRC, GstBaseAudioSrcClass)) +#define GST_IS_BASEAUDIOSRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_BASEAUDIOSRC)) +#define GST_IS_BASEAUDIOSRC_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BASEAUDIOSRC)) + +#define GST_BASEAUDIOSRC_CLOCK(obj) (GST_BASEAUDIOSRC (obj)->clock) +#define GST_BASEAUDIOSRC_PAD(obj) (GST_BASESRC (obj)->srcpad) + +typedef struct _GstBaseAudioSrc GstBaseAudioSrc; +typedef struct _GstBaseAudioSrcClass GstBaseAudioSrcClass; + +struct _GstBaseAudioSrc { + GstPushSrc element; + + /*< protected >*/ /* with LOCK */ + /* our ringbuffer */ + GstRingBuffer *ringbuffer; + + /* required buffer and latency */ + GstClockTime buffer_time; + GstClockTime latency_time; + + /* clock */ + GstClock *clock; +}; + +struct _GstBaseAudioSrcClass { + GstPushSrcClass parent_class; + + /* subclass ringbuffer allocation */ + GstRingBuffer* (*create_ringbuffer) (GstBaseAudioSrc *src); +}; + +GType gst_baseaudiosrc_get_type(void); + +GstRingBuffer *gst_baseaudiosrc_create_ringbuffer (GstBaseAudioSrc *src); + +G_END_DECLS + +#endif /* __GST_BASEAUDIOSRC_H__ */ diff --git a/gst-libs/gst/audio/gstringbuffer.c b/gst-libs/gst/audio/gstringbuffer.c index 04adc9b685..2dc8f0b140 100644 --- a/gst-libs/gst/audio/gstringbuffer.c +++ b/gst-libs/gst/audio/gstringbuffer.c @@ -106,6 +106,204 @@ gst_ringbuffer_finalize (GObject * object) G_OBJECT_CLASS (parent_class)->finalize (G_OBJECT (ringbuffer)); } +static int linear_formats[4 * 2 * 2] = { + GST_S8, + GST_S8, + GST_U8, + GST_U8, + GST_S16_LE, + GST_S16_BE, + GST_U16_LE, + GST_U16_BE, + GST_S24_LE, + GST_S24_BE, + GST_U24_LE, + GST_U24_BE, + GST_S32_LE, + GST_S32_BE, + GST_U32_LE, + GST_U32_BE +}; + +static int linear24_formats[3 * 2 * 2] = { + GST_S24_3LE, + GST_S24_3BE, + GST_U24_3LE, + GST_U24_3BE, + GST_S20_3LE, + GST_S20_3BE, + GST_U20_3LE, + GST_U20_3BE, + GST_S18_3LE, + GST_S18_3BE, + GST_U18_3LE, + GST_U18_3BE, +}; + +static GstBufferFormat +build_linear_format (int depth, int width, int unsignd, int big_endian) +{ + if (width == 24) { + switch (depth) { + case 24: + depth = 0; + break; + case 20: + depth = 1; + break; + case 18: + depth = 2; + break; + default: + return GST_UNKNOWN; + } + return ((int (*)[2][2]) linear24_formats)[depth][!!unsignd][!!big_endian]; + } else { + switch (depth) { + case 8: + depth = 0; + break; + case 16: + depth = 1; + break; + case 24: + depth = 2; + break; + case 32: + depth = 3; + break; + default: + return GST_UNKNOWN; + } + } + return ((int (*)[2][2]) linear_formats)[depth][!!unsignd][!!big_endian]; +} + +void +gst_ringbuffer_debug_spec_caps (GstRingBufferSpec * spec) +{ + GST_DEBUG ("spec caps: %p %" GST_PTR_FORMAT, spec->caps, spec->caps); + GST_DEBUG ("parsed caps: type: %d", spec->type); + GST_DEBUG ("parsed caps: format: %d", spec->format); + GST_DEBUG ("parsed caps: width: %d", spec->width); + GST_DEBUG ("parsed caps: depth: %d", spec->depth); + GST_DEBUG ("parsed caps: sign: %d", spec->sign); + GST_DEBUG ("parsed caps: bigend: %d", spec->bigend); + GST_DEBUG ("parsed caps: rate: %d", spec->rate); + GST_DEBUG ("parsed caps: channels: %d", spec->channels); + GST_DEBUG ("parsed caps: sample bytes: %d", spec->bytes_per_sample); +} + +void +gst_ringbuffer_debug_spec_buff (GstRingBufferSpec * spec) +{ + GST_DEBUG ("acquire ringbuffer: buffer time: %" G_GINT64_FORMAT " usec", + spec->buffer_time); + GST_DEBUG ("acquire ringbuffer: latency time: %" G_GINT64_FORMAT " usec", + spec->latency_time); + GST_DEBUG ("acquire ringbuffer: total segments: %d", spec->segtotal); + GST_DEBUG ("acquire ringbuffer: segment size: %d bytes = %d samples", + spec->segsize, spec->segsize / spec->bytes_per_sample); + GST_DEBUG ("acquire ringbuffer: buffer size: %d bytes = %d samples", + spec->segsize * spec->segtotal, + spec->segsize * spec->segtotal / spec->bytes_per_sample); +} + +gboolean +gst_ringbuffer_parse_caps (GstRingBufferSpec * spec, GstCaps * caps) +{ + const gchar *mimetype; + GstStructure *structure; + + structure = gst_caps_get_structure (caps, 0); + + /* we have to differentiate between int and float formats */ + mimetype = gst_structure_get_name (structure); + + if (!strncmp (mimetype, "audio/x-raw-int", 15)) { + gint endianness; + + spec->type = GST_BUFTYPE_LINEAR; + + /* extract the needed information from the cap */ + if (!(gst_structure_get_int (structure, "width", &spec->width) && + gst_structure_get_int (structure, "depth", &spec->depth) && + gst_structure_get_boolean (structure, "signed", &spec->sign))) + goto parse_error; + + /* extract endianness if needed */ + if (spec->width > 8) { + if (!gst_structure_get_int (structure, "endianness", &endianness)) + goto parse_error; + } else { + endianness = G_BYTE_ORDER; + } + + spec->bigend = endianness == G_LITTLE_ENDIAN ? FALSE : TRUE; + + spec->format = + build_linear_format (spec->depth, spec->width, spec->sign ? 0 : 1, + spec->bigend ? 1 : 0); + } else if (!strncmp (mimetype, "audio/x-raw-float", 17)) { + + spec->type = GST_BUFTYPE_FLOAT; + + /* get layout */ + if (!gst_structure_get_int (structure, "width", &spec->width)) + goto parse_error; + + /* match layout to format wrt to endianness */ + switch (spec->width) { + case 32: + spec->format = + G_BYTE_ORDER == G_LITTLE_ENDIAN ? GST_FLOAT32_LE : GST_FLOAT32_BE; + break; + case 64: + spec->format = + G_BYTE_ORDER == G_LITTLE_ENDIAN ? GST_FLOAT64_LE : GST_FLOAT64_BE; + break; + default: + goto parse_error; + } + } else if (!strncmp (mimetype, "audio/x-alaw", 12)) { + spec->type = GST_BUFTYPE_A_LAW; + spec->format = GST_A_LAW; + } else if (!strncmp (mimetype, "audio/x-mulaw", 13)) { + spec->type = GST_BUFTYPE_MU_LAW; + spec->format = GST_MU_LAW; + } else { + goto parse_error; + } + + /* get rate and channels */ + if (!(gst_structure_get_int (structure, "rate", &spec->rate) && + gst_structure_get_int (structure, "channels", &spec->channels))) + goto parse_error; + + spec->bytes_per_sample = (spec->width >> 3) * spec->channels; + + gst_caps_replace (&spec->caps, caps); + + g_return_val_if_fail (spec->latency_time != 0, FALSE); + + /* calculate suggested segsize and segtotal */ + spec->segsize = + spec->rate * spec->bytes_per_sample * spec->latency_time / GST_MSECOND; + spec->segtotal = spec->buffer_time / spec->latency_time; + + gst_ringbuffer_debug_spec_caps (spec); + gst_ringbuffer_debug_spec_buff (spec); + + return TRUE; + + /* ERRORS */ +parse_error: + { + GST_DEBUG ("could not parse caps"); + return FALSE; + } +} + /** * gst_ringbuffer_set_callback: * @buf: the #GstRingBuffer to set the callback on @@ -264,17 +462,17 @@ gst_ringbuffer_is_acquired (GstRingBuffer * buf) /** - * gst_ringbuffer_play: - * @buf: the #GstRingBuffer to play + * gst_ringbuffer_start: + * @buf: the #GstRingBuffer to start * - * Start playing samples from the ringbuffer. + * Start processing samples from the ringbuffer. * * Returns: TRUE if the device could be started, FALSE on error. * * MT safe. */ gboolean -gst_ringbuffer_play (GstRingBuffer * buf) +gst_ringbuffer_start (GstRingBuffer * buf) { gboolean res = FALSE; GstRingBufferClass *rclass; @@ -283,16 +481,16 @@ gst_ringbuffer_play (GstRingBuffer * buf) g_return_val_if_fail (buf != NULL, FALSE); GST_LOCK (buf); - /* if paused, set to playing */ + /* if stopped, set to started */ res = g_atomic_int_compare_and_exchange (&buf->state, - GST_RINGBUFFER_STATE_STOPPED, GST_RINGBUFFER_STATE_PLAYING); + GST_RINGBUFFER_STATE_STOPPED, GST_RINGBUFFER_STATE_STARTED); if (!res) { /* was not stopped, try from paused */ res = g_atomic_int_compare_and_exchange (&buf->state, - GST_RINGBUFFER_STATE_PAUSED, GST_RINGBUFFER_STATE_PLAYING); + GST_RINGBUFFER_STATE_PAUSED, GST_RINGBUFFER_STATE_STARTED); if (!res) { - /* was not paused either, must be playing then */ + /* was not paused either, must be started then */ res = TRUE; goto done; } @@ -304,8 +502,8 @@ gst_ringbuffer_play (GstRingBuffer * buf) if (rclass->resume) res = rclass->resume (buf); } else { - if (rclass->play) - res = rclass->play (buf); + if (rclass->start) + res = rclass->start (buf); } if (!res) { @@ -322,7 +520,7 @@ done: * gst_ringbuffer_pause: * @buf: the #GstRingBuffer to pause * - * Pause playing samples from the ringbuffer. + * Pause processing samples from the ringbuffer. * * Returns: TRUE if the device could be paused, FALSE on error. * @@ -337,12 +535,12 @@ gst_ringbuffer_pause (GstRingBuffer * buf) g_return_val_if_fail (buf != NULL, FALSE); GST_LOCK (buf); - /* if playing, set to paused */ + /* if started, set to paused */ res = g_atomic_int_compare_and_exchange (&buf->state, - GST_RINGBUFFER_STATE_PLAYING, GST_RINGBUFFER_STATE_PAUSED); + GST_RINGBUFFER_STATE_STARTED, GST_RINGBUFFER_STATE_PAUSED); if (!res) { - /* was not playing */ + /* was not started */ res = TRUE; goto done; } @@ -355,7 +553,7 @@ gst_ringbuffer_pause (GstRingBuffer * buf) res = rclass->pause (buf); if (!res) { - buf->state = GST_RINGBUFFER_STATE_PLAYING; + buf->state = GST_RINGBUFFER_STATE_STARTED; } done: GST_UNLOCK (buf); @@ -367,7 +565,7 @@ done: * gst_ringbuffer_stop: * @buf: the #GstRingBuffer to stop * - * Stop playing samples from the ringbuffer. + * Stop processing samples from the ringbuffer. * * Returns: TRUE if the device could be stopped, FALSE on error. * @@ -382,12 +580,12 @@ gst_ringbuffer_stop (GstRingBuffer * buf) g_return_val_if_fail (buf != NULL, FALSE); GST_LOCK (buf); - /* if playing, set to stopped */ + /* if started, set to stopped */ res = g_atomic_int_compare_and_exchange (&buf->state, - GST_RINGBUFFER_STATE_PLAYING, GST_RINGBUFFER_STATE_STOPPED); + GST_RINGBUFFER_STATE_STARTED, GST_RINGBUFFER_STATE_STOPPED); if (!res) { - /* was not playing, must be stopped then */ + /* was not started, must be stopped then */ res = TRUE; goto done; } @@ -400,7 +598,7 @@ gst_ringbuffer_stop (GstRingBuffer * buf) res = rclass->stop (buf); if (!res) { - buf->state = GST_RINGBUFFER_STATE_PLAYING; + buf->state = GST_RINGBUFFER_STATE_STARTED; } else { gst_ringbuffer_set_sample (buf, 0); } @@ -442,38 +640,38 @@ gst_ringbuffer_delay (GstRingBuffer * buf) } /** - * gst_ringbuffer_played_samples: + * gst_ringbuffer_samples_done: * @buf: the #GstRingBuffer to query * - * Get the number of samples that were played by the ringbuffer + * Get the number of samples that were processed by the ringbuffer * since it was last started. * - * Returns: The number of samples played by the ringbuffer. + * Returns: The number of samples processed by the ringbuffer. * * MT safe. */ guint64 -gst_ringbuffer_played_samples (GstRingBuffer * buf) +gst_ringbuffer_samples_done (GstRingBuffer * buf) { - gint segplayed; + gint segdone; guint64 raw, samples; guint delay; g_return_val_if_fail (buf != NULL, 0); - /* get the amount of segments we played */ - segplayed = g_atomic_int_get (&buf->segplayed); + /* get the amount of segments we processed */ + segdone = g_atomic_int_get (&buf->segdone); - /* and the number of samples not yet played */ + /* and the number of samples not yet processed */ delay = gst_ringbuffer_delay (buf); - samples = (segplayed * buf->samples_per_seg); + samples = (segdone * buf->samples_per_seg); raw = samples; if (samples >= delay) samples -= delay; - GST_DEBUG ("played samples: raw %llu, delay %u, real %llu", raw, delay, + GST_DEBUG ("processed samples: raw %llu, delay %u, real %llu", raw, delay, samples); return samples; @@ -505,47 +703,47 @@ gst_ringbuffer_set_sample (GstRingBuffer * buf, guint64 sample) /* FIXME, we assume the ringbuffer can restart at a random * position, round down to the beginning and keep track of - * offset when calculating the played samples. */ - buf->segplayed = sample / buf->samples_per_seg; + * offset when calculating the processed samples. */ + buf->segdone = sample / buf->samples_per_seg; buf->next_sample = sample; for (i = 0; i < buf->spec.segtotal; i++) { gst_ringbuffer_clear (buf, i); } - GST_DEBUG ("setting sample to %llu, segplayed %d", sample, buf->segplayed); + GST_DEBUG ("setting sample to %llu, segdone %d", sample, buf->segdone); } static gboolean wait_segment (GstRingBuffer * buf) { - /* buffer must be playing now or we deadlock since nobody is reading */ - if (g_atomic_int_get (&buf->state) != GST_RINGBUFFER_STATE_PLAYING) { - GST_DEBUG ("play!"); - gst_ringbuffer_play (buf); + /* buffer must be started now or we deadlock since nobody is reading */ + if (g_atomic_int_get (&buf->state) != GST_RINGBUFFER_STATE_STARTED) { + GST_DEBUG ("start!"); + gst_ringbuffer_start (buf); } /* take lock first, then update our waiting flag */ GST_LOCK (buf); if (g_atomic_int_compare_and_exchange (&buf->waiting, 0, 1)) { GST_DEBUG ("waiting.."); - if (g_atomic_int_get (&buf->state) != GST_RINGBUFFER_STATE_PLAYING) - goto not_playing; + if (g_atomic_int_get (&buf->state) != GST_RINGBUFFER_STATE_STARTED) + goto not_started; GST_RINGBUFFER_WAIT (buf); - if (g_atomic_int_get (&buf->state) != GST_RINGBUFFER_STATE_PLAYING) - goto not_playing; + if (g_atomic_int_get (&buf->state) != GST_RINGBUFFER_STATE_STARTED) + goto not_started; } GST_UNLOCK (buf); return TRUE; /* ERROR */ -not_playing: +not_started: { GST_UNLOCK (buf); - GST_DEBUG ("stopped playing"); + GST_DEBUG ("stopped processing"); return FALSE; } } @@ -573,7 +771,7 @@ guint gst_ringbuffer_commit (GstRingBuffer * buf, guint64 sample, guchar * data, guint len) { - gint segplayed; + gint segdone; gint segsize, segtotal, bps, sps; guint8 *dest; @@ -582,7 +780,7 @@ gst_ringbuffer_commit (GstRingBuffer * buf, guint64 sample, guchar * data, g_return_val_if_fail (data != NULL, -1); if (sample == -1) { - /* play aligned with last sample */ + /* process aligned with last sample */ sample = buf->next_sample; } else { if (sample != buf->next_sample) { @@ -614,17 +812,17 @@ gst_ringbuffer_commit (GstRingBuffer * buf, guint64 sample, guchar * data, while (TRUE) { gint diff; - /* get the currently playing segment */ - segplayed = g_atomic_int_get (&buf->segplayed); + /* get the currently processed segment */ + segdone = g_atomic_int_get (&buf->segdone); /* see how far away it is from the write segment */ - diff = writeseg - segplayed; + diff = writeseg - segdone; GST_DEBUG ("pointer at %d, sample %llu, write to %d-%d, len %d, diff %d, segtotal %d, segsize %d", - segplayed, sample, writeseg, writeoff, len, diff, segtotal, segsize); + segdone, sample, writeseg, writeoff, len, diff, segtotal, segsize); - /* play segment too far ahead, we need to drop */ + /* segment too far ahead, we need to drop */ if (diff < 0) { /* we need to drop one segment at a time, pretend we wrote a * segment. */ @@ -639,7 +837,7 @@ gst_ringbuffer_commit (GstRingBuffer * buf, guint64 sample, guchar * data, /* else we need to wait for the segment to become writable. */ if (!wait_segment (buf)) - goto not_playing; + goto not_started; } /* we can write now */ @@ -660,9 +858,127 @@ gst_ringbuffer_commit (GstRingBuffer * buf, guint64 sample, guchar * data, return len; /* ERRORS */ -not_playing: +not_started: { - GST_DEBUG ("stopped playing"); + GST_DEBUG ("stopped processing"); + return -1; + } +} + +/** + * gst_ringbuffer_read: + * @buf: the #GstRingBuffer to read from + * @sample: the sample position of the data + * @data: where the data should be read + * @len: the length of the data to read + * + * Read @length samples from the ringbuffer into the memory pointed + * to by @data. + * The first sample should be read from position @sample in + * the ringbuffer. + * + * @len should not be a multiple of the segment size of the ringbuffer + * although it is recommended. + * + * Returns: The number of samples read from the ringbuffer or -1 on + * error. + * + * MT safe. + */ +guint +gst_ringbuffer_read (GstRingBuffer * buf, guint64 sample, guchar * data, + guint len) +{ + gint segdone; + gint segsize, segtotal, bps, sps; + guint8 *dest; + + g_return_val_if_fail (buf != NULL, -1); + g_return_val_if_fail (buf->data != NULL, -1); + g_return_val_if_fail (data != NULL, -1); + + if (sample == -1) { + /* process aligned with last sample */ + sample = buf->next_sample; + } else { + if (sample != buf->next_sample) { + GST_WARNING ("discontinuity found got %" G_GUINT64_FORMAT + ", expected %" G_GUINT64_FORMAT, sample, buf->next_sample); + } + } + + dest = GST_BUFFER_DATA (buf->data); + segsize = buf->spec.segsize; + segtotal = buf->spec.segtotal; + bps = buf->spec.bytes_per_sample; + sps = buf->samples_per_seg; + + /* we assume the complete buffer will be consumed and the next sample + * should be written after this */ + buf->next_sample = sample + len / bps; + + /* read enough bytes */ + while (len > 0) { + gint readlen; + gint readseg, readoff; + + /* figure out the segment and the offset inside the segment where + * the sample should be written. */ + readseg = sample / sps; + readoff = (sample % sps) * bps; + + while (TRUE) { + gint diff; + + /* get the currently processed segment */ + segdone = g_atomic_int_get (&buf->segdone); + + /* see how far away it is from the read segment */ + diff = segdone - readseg; + + GST_DEBUG + ("pointer at %d, sample %llu, read from %d-%d, len %d, diff %d, segtotal %d, segsize %d", + segdone, sample, readseg, readoff, len, diff, segtotal, segsize); + + /* segment too far ahead, we need to drop */ + if (diff < 0) { + /* we need to drop one segment at a time, pretend we wrote a + * segment. */ + readlen = MIN (segsize, len); + goto next; + } + + /* read segment is within readable range, we can break the loop and + * start reading the data. */ + if (diff > 0 && diff < segtotal) + break; + + /* else we need to wait for the segment to become readable. */ + if (!wait_segment (buf)) + goto not_started; + } + + /* we can read now */ + readseg = readseg % segtotal; + readlen = MIN (segsize - readoff, len); + + GST_DEBUG ("read @%p seg %d, off %d, len %d", + dest + readseg * segsize, readseg, readoff, readlen); + + memcpy (data, dest + readseg * segsize + readoff, readlen); + + next: + len -= readlen; + data += readlen; + sample += readlen / bps; + } + + return len; + + /* ERRORS */ +not_started: + { + GST_DEBUG ("stopped processing"); return -1; } } @@ -677,7 +993,7 @@ not_playing: * Returns a pointer to memory where the data from segment @segment * can be found. This function is used by subclasses. * - * Returns: FALSE if the buffer is not playing. + * Returns: FALSE if the buffer is not started. * * MT safe. */ @@ -686,23 +1002,24 @@ gst_ringbuffer_prepare_read (GstRingBuffer * buf, gint * segment, guint8 ** readptr, gint * len) { guint8 *data; - gint segplayed; + gint segdone; - /* buffer must be playing */ - if (g_atomic_int_get (&buf->state) != GST_RINGBUFFER_STATE_PLAYING) + /* buffer must be started */ + if (g_atomic_int_get (&buf->state) != GST_RINGBUFFER_STATE_STARTED) return FALSE; g_return_val_if_fail (buf != NULL, FALSE); g_return_val_if_fail (buf->data != NULL, FALSE); + g_return_val_if_fail (segment != NULL, FALSE); g_return_val_if_fail (readptr != NULL, FALSE); g_return_val_if_fail (len != NULL, FALSE); data = GST_BUFFER_DATA (buf->data); - /* get the position of the play pointer */ - segplayed = g_atomic_int_get (&buf->segplayed); + /* get the position of the pointer */ + segdone = g_atomic_int_get (&buf->segdone); - *segment = segplayed % buf->spec.segtotal; + *segment = segdone % buf->spec.segtotal; *len = buf->spec.segsize; *readptr = data + *segment * *len; @@ -711,7 +1028,7 @@ gst_ringbuffer_prepare_read (GstRingBuffer * buf, gint * segment, buf->callback (buf, *readptr, *len, buf->cb_data); GST_DEBUG ("prepare read from segment %d (real %d) @%p", - *segment, segplayed, *readptr); + *segment, segdone, *readptr); return TRUE; } @@ -722,7 +1039,7 @@ gst_ringbuffer_prepare_read (GstRingBuffer * buf, gint * segment, * @advance: the number of segments written * * Subclasses should call this function to notify the fact that - * @advance segments are now played by the device. + * @advance segments are now processed by the device. * * MT safe. */ @@ -732,7 +1049,7 @@ gst_ringbuffer_advance (GstRingBuffer * buf, guint advance) g_return_if_fail (buf != NULL); /* update counter */ - g_atomic_int_add (&buf->segplayed, advance); + g_atomic_int_add (&buf->segdone, advance); /* the lock is already taken when the waiting flag is set, * we grab the lock as well to make sure the waiter is actually diff --git a/gst-libs/gst/audio/gstringbuffer.h b/gst-libs/gst/audio/gstringbuffer.h index 38079c5dbd..3f478f2a08 100644 --- a/gst-libs/gst/audio/gstringbuffer.h +++ b/gst-libs/gst/audio/gstringbuffer.h @@ -44,7 +44,7 @@ typedef void (*GstRingBufferCallback) (GstRingBuffer *rbuf, guint8* data, guint typedef enum { GST_RINGBUFFER_STATE_STOPPED, GST_RINGBUFFER_STATE_PAUSED, - GST_RINGBUFFER_STATE_PLAYING, + GST_RINGBUFFER_STATE_STARTED, } GstRingBufferState; typedef enum { @@ -160,11 +160,11 @@ struct _GstRingBuffer { /*< public >*/ /* ATOMIC */ gint state; /* state of the buffer */ - gint segplayed; /* number of segments played since last start */ + gint segdone; /* number of segments processed since last start */ gint waiting; /* when waiting for a segment to be freed */ /*< private >*/ - guint64 next_sample; /* the next sample we need to write */ + guint64 next_sample; /* the next sample we need to process */ GstRingBufferCallback callback; gpointer cb_data; }; @@ -179,7 +179,7 @@ struct _GstRingBufferClass { gboolean (*release) (GstRingBuffer *buf); /* playback control */ - gboolean (*play) (GstRingBuffer *buf); + gboolean (*start) (GstRingBuffer *buf); gboolean (*pause) (GstRingBuffer *buf); gboolean (*resume) (GstRingBuffer *buf); gboolean (*stop) (GstRingBuffer *buf); @@ -194,6 +194,10 @@ GType gst_ringbuffer_get_type(void); void gst_ringbuffer_set_callback (GstRingBuffer *buf, GstRingBufferCallback cb, gpointer user_data); +gboolean gst_ringbuffer_parse_caps (GstRingBufferSpec *spec, GstCaps *caps); +void gst_ringbuffer_debug_spec_caps (GstRingBufferSpec *spec); +void gst_ringbuffer_debug_spec_buff (GstRingBufferSpec *spec); + /* allocate resources */ gboolean gst_ringbuffer_acquire (GstRingBuffer *buf, GstRingBufferSpec *spec); gboolean gst_ringbuffer_release (GstRingBuffer *buf); @@ -201,21 +205,25 @@ gboolean gst_ringbuffer_release (GstRingBuffer *buf); gboolean gst_ringbuffer_is_acquired (GstRingBuffer *buf); /* playback/pause */ -gboolean gst_ringbuffer_play (GstRingBuffer *buf); +gboolean gst_ringbuffer_start (GstRingBuffer *buf); gboolean gst_ringbuffer_pause (GstRingBuffer *buf); gboolean gst_ringbuffer_stop (GstRingBuffer *buf); /* get status */ guint gst_ringbuffer_delay (GstRingBuffer *buf); -guint64 gst_ringbuffer_played_samples (GstRingBuffer *buf); +guint64 gst_ringbuffer_samples_done (GstRingBuffer *buf); void gst_ringbuffer_set_sample (GstRingBuffer *buf, guint64 sample); /* commit samples */ guint gst_ringbuffer_commit (GstRingBuffer *buf, guint64 sample, guchar *data, guint len); +/* read samples */ +guint gst_ringbuffer_read (GstRingBuffer *buf, guint64 sample, + guchar *data, guint len); /* mostly protected */ +gboolean gst_ringbuffer_prepare_write (GstRingBuffer *buf, gint *segment, guint8 **writeptr, gint *len); gboolean gst_ringbuffer_prepare_read (GstRingBuffer *buf, gint *segment, guint8 **readptr, gint *len); void gst_ringbuffer_clear (GstRingBuffer *buf, gint segment); void gst_ringbuffer_advance (GstRingBuffer *buf, guint advance);