2005-07-06 Wim Taymans <wim@fluendo.com>
+ * 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 <wim@fluendo.com>
+
* ext/theora/theoradec.c: (theora_dec_src_getcaps),
(theora_dec_push), (theora_handle_data_packet):
Prepare for better timestamp fix later.
libgstalsa_la_SOURCES = \
gstalsaplugin.c \
- gstalsasink.c
+ gstalsasink.c \
+ gstalsasrc.c
# port alsa stuff then add the _SOURCES above
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) \
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;
-/*
- * Copyright (C) 2001 CodeFactory AB
- * Copyright (C) 2001 Thomas Nyberg <thomas@codefactory.se>
- * Copyright (C) 2001-2002 Andy Wingo <apwingo@eos.ncsu.edu>
- * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+/* GStreamer
+ * Copyright (C) 2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstalsasrc.c:
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
*
* 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 <sys/ioctl.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <getopt.h>
+#include <alsa/asoundlib.h>
+
#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 <thomas@codefactory.se>, "
- "Andy Wingo <apwingo@eos.ncsu.edu>, "
- "Benjamin Otte <in7y118@public.uni-hamburg.de>");
-
-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 GstAlsa *src_parent_class = NULL;
-
-static GstPadTemplate *
-gst_alsa_src_pad_factory (void)
+static GstElementDetails gst_alsasrc_details =
+GST_ELEMENT_DETAILS ("Audio Src (ALSA)",
+ "Src/Audio",
+ "Output to a sound card via ALSA",
+ "Wim Taymans <wim@fluendo.com>");
+
+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 GstCaps *gst_alsasrc_getcaps (GstBaseSrc * bsrc);
+
+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;
-
- if (!template)
- template = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
- gst_alsa_caps (SND_PCM_FORMAT_UNKNOWN, -1, -1));
-
- return template;
-}
+ LAST_SIGNAL
+};
+
+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 ]")
+ );
+
+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_alsasrc_dispose (GObject * object)
+{
+ G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
-gst_alsa_src_base_init (gpointer g_class)
+gst_alsasrc_base_init (gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
- gst_element_class_add_pad_template (element_class,
- gst_alsa_src_pad_factory ());
+ gst_element_class_set_details (element_class, &gst_alsasrc_details);
- gst_element_class_set_details (element_class, &gst_alsa_src_details);
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&alsasrc_src_factory));
}
-
static void
-gst_alsa_src_class_init (gpointer g_class, gpointer class_data)
+gst_alsasrc_class_init (GstAlsaSrcClass * klass)
{
- GObjectClass *object_class;
- GstElementClass *element_class;
- GstAlsaClass *alsa_class;
- GstAlsaSrcClass *klass;
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+ GstBaseSrcClass *gstbasesrc_class;
+ GstBaseAudioSrcClass *gstbaseaudiosrc_class;
+ GstAudioSrcClass *gstaudiosrc_class;
- klass = (GstAlsaSrcClass *) g_class;
- object_class = (GObjectClass *) klass;
- element_class = (GstElementClass *) klass;
- alsa_class = (GstAlsaClass *) klass;
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+ gstbasesrc_class = (GstBaseSrcClass *) klass;
+ gstbaseaudiosrc_class = (GstBaseAudioSrcClass *) klass;
+ gstaudiosrc_class = (GstAudioSrcClass *) klass;
- if (src_parent_class == NULL)
- src_parent_class = g_type_class_ref (GST_TYPE_ALSA_MIXER);
+ parent_class = g_type_class_ref (GST_TYPE_BASEAUDIOSRC);
- alsa_class->stream = SND_PCM_STREAM_CAPTURE;
- alsa_class->transmit_mmap = gst_alsa_src_mmap;
- alsa_class->transmit_rw = gst_alsa_src_read;
+ gobject_class->dispose = gst_alsasrc_dispose;
- element_class->change_state = gst_alsa_src_change_state;
+ 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_init (GstAlsaSrc * src)
+gst_alsasrc_init (GstAlsaSrc * alsasrc)
{
- 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]);
+ GST_DEBUG ("initializing alsasrc");
- 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);
+ alsasrc->device = g_strdup ("default");
}
-/* 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)
+static GstCaps *
+gst_alsasrc_getcaps (GstBaseSrc * bsrc)
{
- 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);
+ return NULL;
+}
- if (src->status == NULL)
- return GST_CLOCK_TIME_NONE;
+#define CHECK(call, error) \
+G_STMT_START { \
+if ((err = call) < 0) \
+ goto error; \
+} G_STMT_END;
- 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;
+static int
+set_hwparams (GstAlsaSrc * alsa)
+{
+ guint rrate;
+ gint err, dir;
+ snd_pcm_hw_params_t *params;
+
+ snd_pcm_hw_params_alloca (¶ms);
+
+ /* 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);
}
- /* 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);
+ /* write the parameters to device */
+ CHECK (snd_pcm_hw_params (alsa->handle, params), set_hw_params);
- /* captured is the number of samples already sent out as buffers */
- gct_captured = gst_alsa_samples_to_timestamp (this, this->captured);
+ CHECK (snd_pcm_hw_params_get_buffer_size (params, &alsa->buffer_size),
+ buffer_size);
- /* 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);
+ CHECK (snd_pcm_hw_params_get_period_size (params, &alsa->period_size, &dir),
+ period_size);
- retval = gct_trigger + gct_captured + gct_delay;
- GST_LOG_OBJECT (src, "returning clock time of %" GST_TIME_FORMAT,
- GST_TIME_ARGS (retval));
- return retval;
-}
+ return 0;
-static int
-gst_alsa_src_mmap (GstAlsa * this, snd_pcm_sframes_t * avail)
-{
- snd_pcm_uframes_t offset;
- snd_pcm_channel_area_t *dst;
- const snd_pcm_channel_area_t *src;
- int i, err, width = snd_pcm_format_physical_width (this->format->format);
- GstAlsaSrc *alsa_src = GST_ALSA_SRC (this);
-
- /* areas points to the memory areas that belong to gstreamer. */
- dst = g_malloc0 (this->format->channels * sizeof (snd_pcm_channel_area_t));
-
- if (((GstElement *) this)->numpads == 1) {
- /* interleaved */
- for (i = 0; i < this->format->channels; i++) {
- dst[i].addr = alsa_src->buf[0]->data;
- dst[i].first = i * width;
- dst[i].step = this->format->channels * width;
- }
- } else {
- /* noninterleaved */
- for (i = 0; i < this->format->channels; i++) {
- dst[i].addr = alsa_src->buf[i]->data;
- dst[i].first = 0;
- dst[i].step = width;
- }
+ /* ERRORS */
+no_config:
+ {
+ GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_READ,
+ ("Broken configuration for recording: no configurations available: %s",
+ snd_strerror (err)), (NULL));
+ return err;
}
-
- if ((err = snd_pcm_mmap_begin (this->handle, &src, &offset, avail)) < 0) {
- GST_ERROR_OBJECT (this, "mmap failed: %s", snd_strerror (err));
- return -1;
+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;
}
- 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;
+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;
}
- if ((err = snd_pcm_mmap_commit (this->handle, offset, *avail)) < 0) {
- GST_ERROR_OBJECT (this, "mmap commit failed: %s", snd_strerror (err));
- return -1;
+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;
}
-
- return err;
}
+
static int
-gst_alsa_src_read (GstAlsa * this, snd_pcm_sframes_t * avail)
+set_swparams (GstAlsaSrc * alsa)
{
- void *channels[this->format->channels];
- int err, i;
- GstAlsaSrc *src = GST_ALSA_SRC (this);
-
- if (((GstElement *) this)->numpads == 1) {
- /* interleaved */
- err = snd_pcm_readi (this->handle, src->buf[0]->data, *avail);
- } else {
- /* noninterleaved */
- for (i = 0; i < this->format->channels; i++) {
- channels[i] = src->buf[i]->data;
- }
- err = snd_pcm_readn (this->handle, channels, *avail);
+ 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;
}
- /* 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));
+#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;
}
- 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;
+static gboolean
+alsasrc_parse_spec (GstAlsaSrc * alsa, GstRingBufferSpec * spec)
+{
+ 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;
+ }
+ break;
+ case GST_BUFTYPE_A_LAW:
+ alsa->format = SND_PCM_FORMAT_A_LAW;
+ break;
+ case GST_BUFTYPE_MU_LAW:
+ alsa->format = SND_PCM_FORMAT_MU_LAW;
+ break;
+ default:
+ goto error;
- for (i = 0; i < G_N_ELEMENTS (rates); i++) {
- if (rate >= rates[i])
- return rates[i];
}
+ 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;
- return 0;
+ return TRUE;
+
+ /* ERRORS */
+error:
+ {
+ return FALSE;
+ }
}
static gboolean
-gst_alsa_src_set_caps (GstAlsaSrc * src, gboolean aggressive)
+gst_alsasrc_open (GstAudioSrc * asrc, 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)
+ 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;
- /* 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;
- }
}
+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;
+ }
+}
- /* 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);
+static gboolean
+gst_alsasrc_close (GstAudioSrc * asrc)
+{
+ GstAlsaSrc *alsa;
- /* 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;
- }
- }
- }
- }
+ alsa = GST_ALSA_SRC (asrc);
- if (!aggressive)
- return gst_alsa_src_set_caps (src, TRUE);
+ snd_pcm_close (alsa->handle);
- return FALSE;
+ return TRUE;
}
-inline snd_pcm_sframes_t
-gst_alsa_src_update_avail (GstAlsa * this)
+
+/*
+ * Underrun and suspend recovery
+ */
+static gint
+xrun_recovery (snd_pcm_t * handle, gint err)
{
- 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;
- }
+ 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 avail;
+ return err;
}
-/* we transmit buffers of period_size frames */
-static void
-gst_alsa_src_loop (GstElement * element)
+static guint
+gst_alsasrc_read (GstAudioSrc * asrc, gpointer data, guint length)
{
- 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;
+ 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;
}
- }
- /* 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;
+ ptr += err * alsa->channels;
+ cptr -= err;
}
- 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;
+ return length - cptr;
+read_error:
{
- 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);
- }
- }
+ return length; /* skip one period */
+ }
+}
- /* push the buffers out and let them have fun */
- for (i = 0; i < element->numpads; i++) {
- GstBuffer *buf;
+static guint
+gst_alsasrc_delay (GstAudioSrc * asrc)
+{
+ GstAlsaSrc *alsa;
+ snd_pcm_sframes_t delay;
- if (!src->buf[i])
- return;
- if (copied != this->period_size)
- GST_BUFFER_SIZE (src->buf[i]) = outsize;
+ alsa = GST_ALSA_SRC (asrc);
- 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;
+ snd_pcm_delay (alsa->handle, &delay);
- buf = src->buf[i];
- src->buf[i] = NULL;
- gst_pad_push (this->pad[i], GST_DATA (buf));
- }
- this->captured += copied;
- }
+ return delay;
}
static void
-gst_alsa_src_flush (GstAlsaSrc * src)
+gst_alsasrc_reset (GstAudioSrc * asrc)
{
- 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;
+#if 0
+ GstAlsaSrc *alsa;
+ gint err;
- g_return_val_if_fail (element != NULL, FALSE);
- src = GST_ALSA_SRC (element);
+ alsa = GST_ALSA_SRC (asrc);
- switch (GST_STATE_TRANSITION (element)) {
- case GST_STATE_NULL_TO_READY:
- break;
- case GST_STATE_READY_TO_PAUSED:
- snd_pcm_status_malloc (&src->status);
- 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:
- break;
- default:
- break;
- }
+ CHECK (snd_pcm_drop (alsa->handle), drop_error);
+ CHECK (snd_pcm_prepare (alsa->handle), prepare_error);
- if (GST_ELEMENT_CLASS (src_parent_class)->change_state)
- return GST_ELEMENT_CLASS (src_parent_class)->change_state (element);
+ return;
- return GST_STATE_SUCCESS;
+ /* 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
}
-/*
- * Copyright (C) 2001 CodeFactory AB
- * Copyright (C) 2001 Thomas Nyberg <thomas@codefactory.se>
- * Copyright (C) 2001-2002 Andy Wingo <apwingo@eos.ncsu.edu>
- * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+/* GStreamer
+ * Copyright (C) 2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstalsasrc.h:
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
*
* 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 <gst/gst.h>
+#include <gst/audio/gstaudiosrc.h>
+#include <alsa/asoundlib.h>
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__ */
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)
gstaudioclock.h \
gstaudiofilter.h \
gstaudiosink.h \
+ gstaudiosrc.h \
gstbaseaudiosink.h \
+ gstbaseaudiosrc.h \
gstringbuffer.h \
multichannel.h
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;
};
ringbuffer_type =
- g_type_register_static (GST_TYPE_RINGBUFFER, "GstAudioRingBuffer",
+ g_type_register_static (GST_TYPE_RINGBUFFER, "GstAudioSinkRingBuffer",
&ringbuffer_info, 0);
}
return ringbuffer_type;
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);
/* 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)
}
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;
--- /dev/null
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2005 Wim Taymans <wim@fluendo.com>
+ *
+ * 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 <string.h>
+
+#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;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2005 Wim Taymans <wim@fluendo.com>
+ *
+ * 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 <gst/gst.h>
+#include <gst/audio/gstbaseaudiosrc.h>
+
+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__ */
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;
}
}
-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);
-
- /* 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_DEBUG ("release old ringbuffer");
- gst_caps_replace (&spec->caps, caps);
+ /* release old ringbuffer */
+ gst_ringbuffer_release (sink->ringbuffer);
- 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;
-
- GST_DEBUG ("release old ringbuffer");
+ /* parse new caps */
+ if (!gst_ringbuffer_parse_caps (spec, caps))
+ goto parse_error;
- gst_ringbuffer_release (sink->ringbuffer);
+ gst_ringbuffer_debug_spec_buff (spec);
- debug_spec_buffer (sink, spec);
+ GST_DEBUG ("acquire new ringbuffer");
if (!gst_ringbuffer_acquire (sink->ringbuffer, spec))
goto acquire_error;
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;
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
wrong_state:
{
GST_DEBUG ("ringbuffer in wrong state");
+ GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND,
+ ("sink not negotiated."), (NULL));
return GST_FLOW_ERROR;
}
}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2005 Wim Taymans <wim@fluendo.com>
+ *
+ * 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 <string.h>
+
+#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;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2005 Wim Taymans <wim@fluendo.com>
+ *
+ * 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 <gst/gst.h>
+#include <gst/base/gstpushsrc.h>
+#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__ */
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
/**
- * 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;
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;
}
if (rclass->resume)
res = rclass->resume (buf);
} else {
- if (rclass->play)
- res = rclass->play (buf);
+ if (rclass->start)
+ res = rclass->start (buf);
}
if (!res) {
* 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.
*
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;
}
res = rclass->pause (buf);
if (!res) {
- buf->state = GST_RINGBUFFER_STATE_PLAYING;
+ buf->state = GST_RINGBUFFER_STATE_STARTED;
}
done:
GST_UNLOCK (buf);
* 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.
*
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;
}
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);
}
}
/**
- * 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;
/* 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;
}
}
gst_ringbuffer_commit (GstRingBuffer * buf, guint64 sample, guchar * data,
guint len)
{
- gint segplayed;
+ gint segdone;
gint segsize, segtotal, bps, sps;
guint8 *dest;
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) {
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. */
/* 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 */
return len;
/* ERRORS */
-not_playing:
+not_started:
+ {
+ 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 playing");
+ GST_DEBUG ("stopped processing");
return -1;
}
}
* 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.
*/
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;
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;
}
* @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.
*/
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
typedef enum {
GST_RINGBUFFER_STATE_STOPPED,
GST_RINGBUFFER_STATE_PAUSED,
- GST_RINGBUFFER_STATE_PLAYING,
+ GST_RINGBUFFER_STATE_STARTED,
} GstRingBufferState;
typedef enum {
/*< 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;
};
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);
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);
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);