From b05796c9d9579cbedd280ba93e7ffb5e676be960 Mon Sep 17 00:00:00 2001 From: Andy Wingo Date: Fri, 19 Aug 2005 16:13:54 +0000 Subject: [PATCH] ext/alsa/: Port to 0.9. Original commit message from CVS: 2005-08-19 Andy Wingo * ext/alsa/gstalsamixertrack.h: * ext/alsa/gstalsamixertrack.c: * ext/alsa/gstalsamixeroptions.h: * ext/alsa/gstalsamixeroptions.c: * ext/alsa/gstalsamixer.h: * ext/alsa/gstalsamixer.c: Port to 0.9. * ext/alsa/Makefile.am: Build mixer, mixeroptions, mixertracks. Remove gstalsa.c and alsaclock. No more cruft here. --- ChangeLog | 12 + ext/alsa/Makefile.am | 12 +- ext/alsa/gstalsa.c | 2125 ---------------------------------------- ext/alsa/gstalsa.h | 208 +--- ext/alsa/gstalsaclock.c | 214 ---- ext/alsa/gstalsaclock.h | 62 -- ext/alsa/gstalsamixer.c | 198 ++-- ext/alsa/gstalsamixer.h | 24 +- ext/alsa/gstalsamixeroptions.h | 9 + ext/alsa/gstalsamixertrack.h | 7 + ext/alsa/gstalsaplugin.c | 8 +- 11 files changed, 142 insertions(+), 2737 deletions(-) delete mode 100644 ext/alsa/gstalsa.c delete mode 100644 ext/alsa/gstalsaclock.c delete mode 100644 ext/alsa/gstalsaclock.h diff --git a/ChangeLog b/ChangeLog index 293cbcc..dfcb5dd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2005-08-19 Andy Wingo + + * ext/alsa/gstalsamixertrack.h: + * ext/alsa/gstalsamixertrack.c: + * ext/alsa/gstalsamixeroptions.h: + * ext/alsa/gstalsamixeroptions.c: + * ext/alsa/gstalsamixer.h: + * ext/alsa/gstalsamixer.c: Port to 0.9. + + * ext/alsa/Makefile.am: Build mixer, mixeroptions, mixertracks. + Remove gstalsa.c and alsaclock. No more cruft here. + 2005-08-18 Wim Taymans * gst-libs/gst/rtp/gstbasertpdepayload.c: diff --git a/ext/alsa/Makefile.am b/ext/alsa/Makefile.am index 050828c..d0f533a 100644 --- a/ext/alsa/Makefile.am +++ b/ext/alsa/Makefile.am @@ -2,16 +2,11 @@ plugin_LTLIBRARIES = libgstalsa.la libgstalsa_la_SOURCES = \ gstalsaplugin.c \ - gstalsasink.c \ - gstalsasrc.c - -# port alsa stuff then add the _SOURCES above -EXTRA_DIST = \ - gstalsamixer.c \ + gstalsamixer.c \ gstalsamixertrack.c \ gstalsamixeroptions.c \ - gstalsa.c \ - gstalsaclock.c + gstalsasink.c \ + gstalsasrc.c libgstalsa_la_CFLAGS = $(GST_CFLAGS) $(ALSA_CFLAGS) libgstalsa_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) @@ -22,7 +17,6 @@ libgstalsa_la_LIBADD = \ noinst_HEADERS = \ gstalsa.h \ - gstalsaclock.h \ gstalsasink.h \ gstalsasrc.h \ gstalsamixer.h \ diff --git a/ext/alsa/gstalsa.c b/ext/alsa/gstalsa.c deleted file mode 100644 index 5d80de5..0000000 --- a/ext/alsa/gstalsa.c +++ /dev/null @@ -1,2125 +0,0 @@ -/* - * Copyright (C) 2001 CodeFactory AB - * Copyright (C) 2001 Thomas Nyberg - * Copyright (C) 2001-2002 Andy Wingo - * Copyright (C) 2003 Benjamin Otte - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include - -#include "gst/gst-i18n-plugin.h" -#include -#include "gst/propertyprobe/propertyprobe.h" -#include "gstalsa.h" -#include "gstalsaclock.h" -#include "gstalsamixer.h" - -/* all this ifdef'ed stuff causes segfaults because of alsa bug 389, see - * https://bugtrack.alsa-project.org/alsa-bug/bug_view_page.php?bug_id=0000389 - */ -#ifdef ALSA_BUG_389_FIXED -#define ALSA_DEBUG_FLUSH(this) G_STMT_START{ \ - gchar *__str; \ - size_t __size; \ - __size = snd_output_buffer_string (this->out, &__str); \ - if (__size > 0) { \ - GST_DEBUG_OBJECT (this, "%*s", __size, __str); \ - if (snd_output_flush (this->out) != 0) \ - GST_ERROR_OBJECT (this, "error flushing output buffer"); \ - } \ -}G_STMT_END -#endif - -/* GObject functions */ -static void gst_alsa_class_init (gpointer g_class, gpointer class_data); -static void gst_alsa_init (GstAlsa * this); -static void gst_alsa_dispose (GObject * object); -static void gst_alsa_finalize (GObject * object); -static void gst_alsa_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec); -static void gst_alsa_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec); - -/* interface */ -static void gst_alsa_probe_interface_init (GstPropertyProbeInterface * iface); - -/* GStreamer functions for pads and state changing */ -static GstPad *gst_alsa_request_new_pad (GstElement * element, - GstPadTemplate * templ, const gchar * name); -static GstElementStateReturn gst_alsa_change_state (GstElement * element); -static GstClock *gst_alsa_get_clock (GstElement * element); -static void gst_alsa_set_clock (GstElement * element, GstClock * clock); - -/* ALSA setup / start / stop functions */ -static gboolean gst_alsa_probe_hw_params (GstAlsa * this, - GstAlsaFormat * format); -static gboolean gst_alsa_set_hw_params (GstAlsa * this); -static gboolean gst_alsa_set_sw_params (GstAlsa * this); - -static gboolean gst_alsa_open_audio (GstAlsa * this); -static gboolean gst_alsa_start_audio (GstAlsa * this); -static gboolean gst_alsa_drain_audio (GstAlsa * this); -static gboolean gst_alsa_stop_audio (GstAlsa * this); -static gboolean gst_alsa_close_audio (GstAlsa * this); - -/* GStreamer querying, conversion, and format functions */ -static const GstFormat *gst_alsa_get_formats (GstPad * pad); -static gboolean gst_alsa_convert (GstAlsa * this, - GstFormat src_format, - gint64 src_value, GstFormat * dest_format, gint64 * dest_value); -static gboolean gst_alsa_pad_convert (GstPad * pad, - GstFormat src_format, - gint64 src_value, GstFormat * dest_format, gint64 * dest_value); -static const GstQueryType *gst_alsa_get_query_types (GstPad * pad); -static gboolean gst_alsa_query_func (GstElement * element, - GstQueryType type, GstFormat * format, gint64 * value); -static gboolean gst_alsa_query (GstElement * element, - GstQueryType type, GstFormat * format, gint64 * value); -static gboolean gst_alsa_pad_query (GstPad * pad, - GstQueryType type, GstFormat * format, gint64 * value); - -/* TYPE FUNCTIONS ***********************************************************/ - -GType -gst_alsa_get_type (void) -{ - static GType alsa_type = 0; - - if (!alsa_type) { - static const GTypeInfo alsa_info = { - sizeof (GstAlsaClass), - NULL, - NULL, - gst_alsa_class_init, - NULL, - NULL, - sizeof (GstAlsa), - 0, - (GInstanceInitFunc) gst_alsa_init, - }; - static const GInterfaceInfo alsa_probe_info = { - (GInterfaceInitFunc) gst_alsa_probe_interface_init, - NULL, - NULL - }; - - alsa_type = - g_type_register_static (GST_TYPE_ELEMENT, "GstAlsa", &alsa_info, 0); - - g_type_add_interface_static (alsa_type, - GST_TYPE_PROPERTY_PROBE, &alsa_probe_info); - } - - return alsa_type; -} - -/* GOBJECT FUNCTIONS ********************************************************/ - -enum -{ - ARG_0, - ARG_DEVICE, - ARG_DEVICE_NAME, - ARG_PERIODCOUNT, - ARG_PERIODSIZE, - ARG_BUFFERSIZE, - ARG_AUTORECOVER, - ARG_MMAP, - ARG_MAXDISCONT -}; - -static GstElement *parent_class = NULL; - -static void -gst_alsa_class_init (gpointer g_class, gpointer class_data) -{ - GObjectClass *object_class; - GstElementClass *element_class; - GstAlsaClass *klass; - - klass = (GstAlsaClass *) g_class; - object_class = (GObjectClass *) g_class; - element_class = (GstElementClass *) g_class; - - if (parent_class == NULL) - parent_class = g_type_class_ref (GST_TYPE_ELEMENT); - - object_class->dispose = gst_alsa_dispose; - object_class->finalize = gst_alsa_finalize; - object_class->get_property = gst_alsa_get_property; - object_class->set_property = gst_alsa_set_property; - - g_object_class_install_property (object_class, ARG_DEVICE, - g_param_spec_string ("device", "Device", - "ALSA device, as defined in an asoundrc", - "default", G_PARAM_READWRITE)); - g_object_class_install_property (object_class, ARG_DEVICE_NAME, - g_param_spec_string ("device_name", "Device name", - "Name of the device", NULL, G_PARAM_READABLE)); - g_object_class_install_property (object_class, ARG_PERIODCOUNT, - g_param_spec_int ("period-count", "Period count", - "Number of hardware buffers to use", - GST_ALSA_MIN_PERIOD_CNT, GST_ALSA_MAX_PERIOD_CNT, - GST_ALSA_MIN_PERIOD_CNT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); - g_object_class_install_property (object_class, ARG_PERIODSIZE, - g_param_spec_int ("period-size", "Period size", - "Number of frames (samples on each channel) in one hardware period", - GST_ALSA_MIN_PERIOD_SZ, GST_ALSA_MAX_PERIOD_SZ, - GST_ALSA_MAX_PERIOD_SZ, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); - g_object_class_install_property (object_class, ARG_BUFFERSIZE, - g_param_spec_int ("buffer-size", "Buffer size", - "Number of frames the hardware buffer can hold", - GST_ALSA_MIN_BUFFER_SZ, GST_ALSA_MAX_BUFFER_SZ, - GST_ALSA_MIN_PERIOD_CNT * GST_ALSA_MAX_PERIOD_SZ, G_PARAM_READWRITE)); - g_object_class_install_property (object_class, ARG_AUTORECOVER, - g_param_spec_boolean ("autorecover", "Automatic xrun recovery", - "When TRUE tries to reduce processor load on xruns", TRUE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); - g_object_class_install_property (object_class, ARG_MMAP, - g_param_spec_boolean ("mmap", "Use mmap'ed access", - "Wether to use mmap (faster) or standard read/write (more compatible)", - TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); - g_object_class_install_property (object_class, ARG_MAXDISCONT, - g_param_spec_uint64 ("max-discont", "Maximum Discontinuity", - "GStreamer timeunits before the timestamp syncing starts dropping/inserting samples", - /* rounding errors */ 1000, GST_SECOND, GST_ALSA_DEFAULT_DISCONT, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); - - element_class->change_state = GST_DEBUG_FUNCPTR (gst_alsa_change_state); - element_class->query = GST_DEBUG_FUNCPTR (gst_alsa_query); - element_class->request_new_pad = GST_DEBUG_FUNCPTR (gst_alsa_request_new_pad); - element_class->set_clock = GST_DEBUG_FUNCPTR (gst_alsa_set_clock); - element_class->get_clock = GST_DEBUG_FUNCPTR (gst_alsa_get_clock); -} - -static void -gst_alsa_init (GstAlsa * this) -{ - this->device = g_strdup ("default"); - this->cached_caps = NULL; -} - -static void -gst_alsa_dispose (GObject * object) -{ - GstAlsa *this = GST_ALSA (object); - - if (this->clock) { - gst_object_unparent (GST_OBJECT (this->clock)); - this->clock = NULL; - } - - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -static void -gst_alsa_finalize (GObject * object) -{ - GstAlsa *this = GST_ALSA (object); - - g_free (this->device); - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -gst_alsa_set_property (GObject * object, guint prop_id, const GValue * value, - GParamSpec * pspec) -{ - GstAlsa *this; - gint buffer_size; - - this = (GstAlsa *) object; - switch (prop_id) { - case ARG_DEVICE: - if (this->device) - g_free (this->device); - this->device = g_strdup (g_value_get_string (value)); - break; - case ARG_PERIODCOUNT: - g_return_if_fail (!GST_FLAG_IS_SET (this, GST_ALSA_RUNNING)); - this->period_count = g_value_get_int (value); - break; - case ARG_PERIODSIZE: - g_return_if_fail (!GST_FLAG_IS_SET (this, GST_ALSA_RUNNING)); - this->period_size = g_value_get_int (value); - break; - case ARG_BUFFERSIZE: - g_return_if_fail (!GST_FLAG_IS_SET (this, GST_ALSA_RUNNING)); - buffer_size = g_value_get_int (value); - this->period_count = buffer_size / this->period_size; - break; - case ARG_AUTORECOVER: - this->autorecover = g_value_get_boolean (value); - return; - case ARG_MMAP: - this->mmap = g_value_get_boolean (value); - return; - case ARG_MAXDISCONT: - this->max_discont = (GstClockTime) g_value_get_uint64 (value); - return; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - return; - } - - if (GST_STATE (this) == GST_STATE_NULL) - return; - - if (GST_FLAG_IS_SET (this, GST_ALSA_RUNNING)) { - gst_alsa_stop_audio (this); - gst_alsa_start_audio (this); - } -} - -static void -gst_alsa_get_property (GObject * object, guint prop_id, GValue * value, - GParamSpec * pspec) -{ - GstAlsa *this; - - this = (GstAlsa *) object; - - switch (prop_id) { - case ARG_DEVICE: - g_value_set_string (value, this->device); - break; - case ARG_DEVICE_NAME: - g_value_set_string (value, this->cardname); - break; - case ARG_PERIODCOUNT: - g_value_set_int (value, this->period_count); - break; - case ARG_PERIODSIZE: - g_value_set_int (value, this->period_size); - break; - case ARG_BUFFERSIZE: - g_value_set_int (value, this->period_size * this->period_count); - break; - case ARG_AUTORECOVER: - g_value_set_boolean (value, this->autorecover); - break; - case ARG_MMAP: - g_value_set_boolean (value, this->mmap); - break; - case ARG_MAXDISCONT: - g_value_set_uint64 (value, (guint64) this->max_discont); - return; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static const GList * -gst_alsa_probe_get_properties (GstPropertyProbe * probe) -{ - GObjectClass *klass = G_OBJECT_GET_CLASS (probe); - static GList *list = NULL; - - if (!list) { - list = g_list_append (NULL, g_object_class_find_property (klass, "device")); - } - - return list; -} - -static void -device_list (snd_pcm_stream_t stream, GstAlsaClass * klass) -{ - snd_ctl_t *handle; - int card, err, dev; - snd_ctl_card_info_t *info; - snd_pcm_info_t *pcminfo; - gboolean mixer = (stream == -1); - - if (stream == -1) - stream = 0; - - snd_ctl_card_info_alloca (&info); - snd_pcm_info_alloca (&pcminfo); - card = -1; - - if (snd_card_next (&card) < 0 || card < 0) { - /* no soundcard found */ - return; - } - while (card >= 0) { - char name[32]; - - sprintf (name, "hw:%d", card); - if ((err = snd_ctl_open (&handle, name, 0)) < 0) { - goto next_card; - } - if ((err = snd_ctl_card_info (handle, info)) < 0) { - snd_ctl_close (handle); - goto next_card; - } - - if (mixer) { - klass->devices = g_list_append (klass->devices, g_strdup (name)); - } else { - dev = -1; - while (1) { - gchar *gst_device; - - snd_ctl_pcm_next_device (handle, &dev); - - if (dev < 0) - break; - snd_pcm_info_set_device (pcminfo, dev); - snd_pcm_info_set_subdevice (pcminfo, 0); - snd_pcm_info_set_stream (pcminfo, stream); - if ((err = snd_ctl_pcm_info (handle, pcminfo)) < 0) { - continue; - } - - gst_device = g_strdup_printf ("hw:%d,%d", card, dev); - klass->devices = g_list_append (klass->devices, gst_device); - } - } - snd_ctl_close (handle); - next_card: - if (snd_card_next (&card) < 0) { - break; - } - } -} - -static gboolean -gst_alsa_class_probe_devices (GstAlsaClass * klass, gboolean check) -{ - static gboolean init = FALSE; - - /* I'm pretty sure ALSA has a good way to do this. However, their cool - * auto-generated documentation is pretty much useless if you try to - * do function-wise look-ups. */ - - if (!init && !check) { - snd_pcm_stream_t mode = -1; - const GList *templates; - - /* we assume one pad template at max [zero=mixer] */ - templates = - gst_element_class_get_pad_template_list (GST_ELEMENT_CLASS (klass)); - if (templates) { - if (GST_PAD_TEMPLATE_DIRECTION (templates->data) == GST_PAD_SRC) - mode = SND_PCM_STREAM_CAPTURE; - else - mode = SND_PCM_STREAM_PLAYBACK; - } - - device_list (mode, klass); - - init = TRUE; - } - - return init; -} - -static GValueArray * -gst_alsa_class_list_devices (GstAlsaClass * klass) -{ - GValueArray *array; - GValue value = { 0 }; - GList *item; - - if (!klass->devices) - return NULL; - - array = g_value_array_new (g_list_length (klass->devices)); - g_value_init (&value, G_TYPE_STRING); - for (item = klass->devices; item != NULL; item = item->next) { - g_value_set_string (&value, item->data); - g_value_array_append (array, &value); - } - g_value_unset (&value); - - return array; - -} - -static void -gst_alsa_probe_probe_property (GstPropertyProbe * probe, - guint prop_id, const GParamSpec * pspec) -{ - GstAlsaClass *klass = GST_ALSA_GET_CLASS (probe); - - switch (prop_id) { - case ARG_DEVICE: - gst_alsa_class_probe_devices (klass, FALSE); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec); - break; - } -} - -static gboolean -gst_alsa_probe_needs_probe (GstPropertyProbe * probe, - guint prop_id, const GParamSpec * pspec) -{ - GstAlsaClass *klass = GST_ALSA_GET_CLASS (probe); - gboolean ret = FALSE; - - switch (prop_id) { - case ARG_DEVICE: - ret = !gst_alsa_class_probe_devices (klass, TRUE); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec); - break; - } - - return ret; -} - -static GValueArray * -gst_alsa_probe_get_values (GstPropertyProbe * probe, - guint prop_id, const GParamSpec * pspec) -{ - GstAlsaClass *klass = GST_ALSA_GET_CLASS (probe); - GValueArray *array = NULL; - - switch (prop_id) { - case ARG_DEVICE: - array = gst_alsa_class_list_devices (klass); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec); - break; - } - - return array; -} - -static void -gst_alsa_probe_interface_init (GstPropertyProbeInterface * iface) -{ - iface->get_properties = gst_alsa_probe_get_properties; - iface->probe_property = gst_alsa_probe_probe_property; - iface->needs_probe = gst_alsa_probe_needs_probe; - iface->get_values = gst_alsa_probe_get_values; -} - -/* GSTREAMER PAD / QUERY / CONVERSION / STATE FUNCTIONS *********************/ - -static GstPad * -gst_alsa_request_new_pad (GstElement * element, GstPadTemplate * templ, - const gchar * name) -{ - GstAlsa *this; - gint track = 0; - - g_return_val_if_fail (GST_IS_ALSA (element), NULL); - g_return_val_if_fail (!GST_FLAG_IS_SET (element, GST_ALSA_RUNNING), NULL); - this = GST_ALSA (element); - - if (name) { - /* locate the track number in the requested pad name. */ - track = (gint) strtol (name + (strchr (templ->name_template, '%') - - templ->name_template), NULL, 0); - if (track < 1 || track >= GST_ALSA_MAX_TRACKS) { - GST_INFO_OBJECT (this, "invalid track requested. (%d)", track); - return NULL; - } - } - - /* make sure the requested track is free. */ - if (track > 0 || this->pad[track] != NULL) { - GST_INFO_OBJECT (this, "requested track %d already in use.", track); - return NULL; - } - - /* if the user doesn't care, use the lowest available track number */ - if (track == 0) { - for (track = 1; track < GST_ALSA_MAX_TRACKS; track++) { - if (this->pad[track] != NULL) - goto found_track; - } - return NULL; - } - -found_track: - this->pad[track] = gst_pad_new_from_template (templ, name); - - gst_pad_set_link_function (this->pad[track], gst_alsa_link); - gst_pad_set_getcaps_function (this->pad[track], gst_alsa_get_caps); - gst_pad_set_fixate_function (this->pad[track], gst_alsa_fixate); - - gst_element_add_pad (GST_ELEMENT (this), this->pad[track]); - - gst_pad_set_convert_function (this->pad[track], gst_alsa_pad_convert); - gst_pad_set_query_function (this->pad[track], gst_alsa_pad_query); - gst_pad_set_query_type_function (this->pad[track], gst_alsa_get_query_types); - gst_pad_set_formats_function (this->pad[track], gst_alsa_get_formats); - - return this->pad[track]; -} - -/* gets the matching alsa format or NULL if none matches */ -static GstAlsaFormat * -gst_alsa_get_format (const GstStructure * structure) -{ - const gchar *mimetype; - GstAlsaFormat *ret; - - if (!(ret = g_new (GstAlsaFormat, 1))) - return NULL; - - /* we have to differentiate between int and float formats */ - mimetype = gst_structure_get_name (structure); - - if (!strncmp (mimetype, "audio/x-raw-int", 15)) { - gboolean sign; - gint width, depth, endianness; - - /* extract the needed information from the cap */ - if (!(gst_structure_get_int (structure, "width", &width) && - gst_structure_get_int (structure, "depth", &depth) && - gst_structure_get_boolean (structure, "signed", &sign))) - goto error; - - /* extract endianness if needed */ - if (width > 8) { - if (!gst_structure_get_int (structure, "endianness", &endianness)) - goto error; - } else { - endianness = G_BYTE_ORDER; - } - - ret->format = - snd_pcm_build_linear_format (depth, width, sign ? 0 : 1, - endianness == G_LITTLE_ENDIAN ? 0 : 1); - - } else if (!strncmp (mimetype, "audio/x-raw-float", 17)) { - gint width; - - /* get layout */ - if (!gst_structure_get_int (structure, "width", &width)) - goto error; - - /* match layout to format wrt to endianness */ - if (width == 32) { - if (G_BYTE_ORDER == G_LITTLE_ENDIAN) { - ret->format = SND_PCM_FORMAT_FLOAT_LE; - } else if (G_BYTE_ORDER == G_BIG_ENDIAN) { - ret->format = SND_PCM_FORMAT_FLOAT_BE; - } else { - ret->format = SND_PCM_FORMAT_FLOAT; - } - } else if (width == 64) { - if (G_BYTE_ORDER == G_LITTLE_ENDIAN) { - ret->format = SND_PCM_FORMAT_FLOAT64_LE; - } else if (G_BYTE_ORDER == G_BIG_ENDIAN) { - ret->format = SND_PCM_FORMAT_FLOAT64_BE; - } else { - ret->format = SND_PCM_FORMAT_FLOAT64; - } - } else { - goto error; - } - } else if (!strncmp (mimetype, "audio/x-alaw", 12)) { - ret->format = SND_PCM_FORMAT_A_LAW; - } else if (!strncmp (mimetype, "audio/x-mulaw", 13)) { - ret->format = SND_PCM_FORMAT_MU_LAW; - } - - /* get rate and channels */ - if (!(gst_structure_get_int (structure, "rate", &ret->rate) && - gst_structure_get_int (structure, "channels", &ret->channels))) - goto error; - - return ret; - -error: - g_free (ret); - return NULL; -} - -static inline gboolean -gst_alsa_formats_match (GstAlsaFormat * one, GstAlsaFormat * two) -{ - if (one == two) - return TRUE; - if (one == NULL || two == NULL) - return FALSE; - return (one->format == two->format) && - (one->rate == two->rate) && (one->channels == two->channels); -} - -/* get props for a spec */ -static GstCaps * -gst_alsa_get_caps_internal (snd_pcm_format_t format) -{ - if (format == SND_PCM_FORMAT_A_LAW) { - return gst_caps_new_simple ("audio/x-alaw", NULL); - } else if (format == SND_PCM_FORMAT_MU_LAW) { - return gst_caps_new_simple ("audio/x-mulaw", NULL); - } else if (snd_pcm_format_linear (format)) { - /* int */ - GstStructure *structure = gst_structure_new ("audio/x-raw-int", - "width", G_TYPE_INT, (gint) snd_pcm_format_physical_width (format), - "depth", G_TYPE_INT, (gint) snd_pcm_format_width (format), - "signed", G_TYPE_BOOLEAN, - snd_pcm_format_signed (format) == 1 ? TRUE : FALSE, - NULL); - - /* endianness */ - if (snd_pcm_format_physical_width (format) > 8) { - switch (snd_pcm_format_little_endian (format)) { - case 0: - gst_structure_set (structure, "endianness", G_TYPE_INT, G_BIG_ENDIAN, - NULL); - break; - case 1: - gst_structure_set (structure, "endianness", G_TYPE_INT, - G_LITTLE_ENDIAN, NULL); - break; - default: - GST_WARNING - ("Unknown byte order in sound driver. Continuing by assuming system byte order."); - gst_structure_set (structure, "endianness", G_TYPE_INT, G_BYTE_ORDER, - NULL); - break; - } - } - return gst_caps_new_full (structure, NULL); - } else if (snd_pcm_format_float (format)) { - /* no float with non-platform endianness */ - if (!snd_pcm_format_cpu_endian (format)) - return NULL; - - return gst_caps_new_simple ("audio/x-raw-float", - "buffer-frames", GST_TYPE_INT_RANGE, 0, G_MAXINT, - "width", G_TYPE_INT, (gint) snd_pcm_format_width (format), - "endianness", G_TYPE_INT, G_BYTE_ORDER, NULL); - } - return NULL; -} - -static inline void -add_rates (GstStructure * structure, gint min_rate, gint max_rate) -{ - if (min_rate < 0) { - min_rate = GST_ALSA_MIN_RATE; - max_rate = GST_ALSA_MAX_RATE; - } - if (max_rate < 0 || min_rate == max_rate) { - gst_structure_set (structure, "rate", G_TYPE_INT, min_rate, NULL); - } else { - /* just to be sure */ - if (min_rate > max_rate) { - gint temp; - - GST_ERROR - ("minimum rate > maximum rate (%d > %d), please fix your soundcard drivers", - min_rate, max_rate); - temp = min_rate; - min_rate = max_rate; - max_rate = temp; - } - gst_structure_set (structure, "rate", GST_TYPE_INT_RANGE, min_rate, - max_rate, NULL); - } -} - -static inline void -add_channels (GstStructure * structure, gint min_channels, gint max_channels) -{ - if (min_channels < 0) { - min_channels = 1; - max_channels = GST_ALSA_MAX_CHANNELS; - } - if (max_channels < 0 || min_channels == max_channels) { - gst_structure_set (structure, "channels", G_TYPE_INT, min_channels, NULL); - } else { - /* just to be sure */ - if (min_channels > max_channels) { - gint temp; - - GST_ERROR - ("minimum channels > maximum channels (%d > %d), please fix your soundcard drivers", - min_channels, max_channels); - temp = min_channels; - min_channels = max_channels; - max_channels = temp; - } - gst_structure_set (structure, "channels", GST_TYPE_INT_RANGE, - min_channels, max_channels, NULL); - } -} - -/* - * Get all available caps. - * @format: SND_PCM_FORMAT_UNKNOWN for all formats, desired format else - * @rate: allowed rates if < 0, else desired rate - * @channels: all allowed values for channels if < 0, else desired channels - */ -GstCaps * -gst_alsa_caps (snd_pcm_format_t format, gint rate, gint channels) -{ - GstCaps *ret_caps; - - if (format != SND_PCM_FORMAT_UNKNOWN) { - /* there are some caps set already */ - ret_caps = gst_alsa_get_caps_internal (format); - - /* we can never use a format we can't set caps for */ - g_assert (ret_caps != NULL); - g_assert (gst_caps_get_size (ret_caps) == 1); - - add_rates (gst_caps_get_structure (ret_caps, 0), rate, -1); - add_channels (gst_caps_get_structure (ret_caps, 0), channels, -1); - } else { - int i; - GstCaps *temp; - - ret_caps = gst_caps_new_empty (); - for (i = 0; i <= SND_PCM_FORMAT_LAST; i++) { - temp = gst_alsa_get_caps_internal (i); - - /* can be NULL, because not all alsa formats can be specified as caps */ - if (temp != NULL) { - g_assert (gst_caps_get_size (temp) == 1); - add_rates (gst_caps_get_structure (temp, 0), rate, -1); - add_channels (gst_caps_get_structure (temp, 0), channels, -1); - gst_caps_append (ret_caps, temp); - } - } - } - - gst_caps_do_simplify (ret_caps); - return ret_caps; -} - - -static int -gst_alsa_check_sample_rates (snd_pcm_t * device_handle, - snd_pcm_hw_params_t * hw_params, unsigned int *tested_rates, - GValue * supported_rates) -{ - int i; - char init_done = 0; - - GValue value = { 0 }; - g_value_init (&value, G_TYPE_INT); - - for (i = 0; tested_rates[i] != 0; i++) { - if (!snd_pcm_hw_params_test_rate (device_handle, hw_params, tested_rates[i], - 0)) { - if (!init_done) { - /* at least one sample rate supported */ - g_value_init (supported_rates, GST_TYPE_LIST); - init_done = 1; - } - - g_value_set_int (&value, tested_rates[i]); - gst_value_list_append_value (supported_rates, &value); - } - } - -// only one -> G_TYPE_INT - - g_value_unset (&value); - - return init_done; -} - -static int -gst_alsa_rates_probe (snd_pcm_t * device_handle, - snd_pcm_hw_params_t * hw_params, GValue * supported_rates) -{ - int n; - gboolean min_found = FALSE, max_found = FALSE; - unsigned int common_rates[] = - { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 88200, - 96000, 192000, 0, 0, 0 - }; - /* this dummy sample rate should only be supported by a software device. - * you need two, because one could be dmix... */ - unsigned int uncommon_rates[] = { 12345, 45678, 0 }; - - int ret; - int dir; - unsigned int min_rate, max_rate; - - /* Get MIN/MAX supported rate */ - snd_pcm_hw_params_get_rate_min (hw_params, &min_rate, &dir); - min_rate = - min_rate < - GST_ALSA_MIN_RATE ? GST_ALSA_MIN_RATE : (min_rate + - GST_ALSA_DIR_MIN (dir)); - snd_pcm_hw_params_get_rate_max (hw_params, &max_rate, &dir); - max_rate = - max_rate > - GST_ALSA_MAX_RATE ? GST_ALSA_MAX_RATE : (max_rate + - GST_ALSA_DIR_MAX (dir)); - for (n = 0; common_rates[n] != 0; n++) { - if (common_rates[n] == min_rate) - min_found = TRUE; - if (common_rates[n] == max_rate) - max_found = TRUE; - } - if (!min_found) - common_rates[12] = min_rate; - if (!max_found && min_rate != max_rate) - common_rates[13] = max_rate; - - ret = - gst_alsa_check_sample_rates (device_handle, hw_params, uncommon_rates, - supported_rates); - if (ret) { - /* Uncommon sample rates supported, it is certainly a - * "software"/dummy device */ - g_value_unset (supported_rates); - g_value_init (supported_rates, GST_TYPE_INT_RANGE); - - if (min_rate != max_rate) { - gst_value_set_int_range (supported_rates, min_rate, max_rate); - } else { - /* Only one supported */ - g_value_unset (supported_rates); - g_value_init (supported_rates, G_TYPE_INT); - g_value_set_int (supported_rates, min_rate); - } - } else { - /* Check common sample rates for real hardware support */ - ret = - gst_alsa_check_sample_rates (device_handle, hw_params, common_rates, - supported_rates); - if (ret) { - if (gst_value_list_get_size (supported_rates) == 1) { - /* Only one supported */ - GValue *rate = (GValue *) gst_value_list_get_value (supported_rates, 0); - - min_rate = g_value_get_int (rate); - g_value_unset (supported_rates); - g_value_init (supported_rates, G_TYPE_INT); - g_value_set_int (supported_rates, min_rate); - } - } else { - GST_WARNING ("No supported samplerates found for %d-%d range", - min_rate, max_rate); - gst_value_set_int_range (supported_rates, min_rate, max_rate); - } - } - - return 0; -} - - -/* Return better caps when device is open */ -GstCaps * -gst_alsa_get_caps (GstPad * pad) -{ - GstAlsa *this; - snd_pcm_hw_params_t *hw_params; - snd_pcm_format_mask_t *mask; - int i; - GValue supported_rates = { 0 }; - unsigned int min_period_cnt, max_period_cnt; - snd_pcm_uframes_t min_period_sz, max_period_sz; - snd_pcm_uframes_t min_buffer_sz, max_buffer_sz; - gint buffer_size; - gint min_channels, max_channels; - GstCaps *ret = NULL; - - g_return_val_if_fail (pad != NULL, NULL); - - this = GST_ALSA (gst_pad_get_parent (pad)); - - if (!GST_FLAG_IS_SET (this, GST_ALSA_OPEN)) - return gst_caps_copy (GST_PAD_TEMPLATE_CAPS (GST_PAD_PAD_TEMPLATE (pad))); - if (this->cached_caps) - return gst_caps_copy (this->cached_caps); - - snd_pcm_hw_params_alloca (&hw_params); - ERROR_CHECK (snd_pcm_hw_params_any (this->handle, hw_params), - "Broken configuration for this PCM: %s"); - - if (((GstElement *) this)->numpads > 1) { - min_channels = 1; - max_channels = -1; - } else { - ERROR_CHECK (snd_pcm_hw_params_get_channels_min (hw_params, &min_channels), - "Couldn't get minimum channel count for device %s: %s", this->device); - ERROR_CHECK (snd_pcm_hw_params_get_channels_max (hw_params, &max_channels), - "Couldn't get maximum channel count for device %s: %s", this->device); - min_channels = min_channels < 1 ? 1 : min_channels; - max_channels = - max_channels > - GST_ALSA_MAX_CHANNELS ? GST_ALSA_MAX_CHANNELS : max_channels; - } - - /* Check available sample rates */ - if (!gst_alsa_rates_probe (this->handle, hw_params, &supported_rates)) { - ; - } - - /* Probe period_count and adjust default/user provided value against probed MIN/MAX */ - ERROR_CHECK (snd_pcm_hw_params_get_periods_min (hw_params, &min_period_cnt, - &i), "Couldn't get minimum period_count for device %s: %s", - this->device); - min_period_cnt = - min_period_cnt < - GST_ALSA_MIN_PERIOD_CNT ? GST_ALSA_MIN_PERIOD_CNT : (min_period_cnt + - GST_ALSA_DIR_MIN (i)); - if (this->period_count < min_period_cnt) { - this->period_count = min_period_cnt; - }; - ERROR_CHECK (snd_pcm_hw_params_get_periods_max (hw_params, &max_period_cnt, - &i), "Couldn't get maximum period_count for device %s: %s", - this->device); - max_period_cnt = - max_period_cnt > - GST_ALSA_MAX_PERIOD_CNT ? GST_ALSA_MAX_PERIOD_CNT : (max_period_cnt + - GST_ALSA_DIR_MAX (i)); - if (this->period_count > max_period_cnt) { - this->period_count = max_period_cnt; - }; - - /* Probe period_size and adjust default/user provided value against probed MIN/MAX */ - ERROR_CHECK (snd_pcm_hw_params_get_period_size_min (hw_params, &min_period_sz, - &i), "Couldn't get minimum period_size for device %s: %s", - this->device); - min_period_sz = - min_period_sz < - GST_ALSA_MIN_PERIOD_SZ ? GST_ALSA_MIN_PERIOD_SZ : (min_period_sz + - GST_ALSA_DIR_MIN (i)); - if (this->period_size < min_period_sz) { - this->period_size = min_period_sz; - }; - ERROR_CHECK (snd_pcm_hw_params_get_period_size_max (hw_params, &max_period_sz, - &i), "Couldn't get maximum period_size for device %s: %s", - this->device); - max_period_sz = - max_period_sz > - GST_ALSA_MAX_PERIOD_SZ ? GST_ALSA_MAX_PERIOD_SZ : (max_period_sz + - GST_ALSA_DIR_MAX (i)); - if (this->period_size > max_period_sz) { - this->period_size = max_period_sz; - }; - - /* Probe buffer_size MIN/MAX */ - buffer_size = this->period_count * this->period_size; - ERROR_CHECK (snd_pcm_hw_params_get_buffer_size_min (hw_params, - &min_buffer_sz), "Couldn't get minimum buffer_size for device %s: %s", - this->device); - min_buffer_sz = - min_buffer_sz < - GST_ALSA_MIN_BUFFER_SZ ? GST_ALSA_MIN_BUFFER_SZ : min_buffer_sz; - if (buffer_size < min_buffer_sz) { - buffer_size = min_buffer_sz; - this->period_size = GST_ALSA_MIN_BUFFER_SZ / this->period_count; - }; - ERROR_CHECK (snd_pcm_hw_params_get_buffer_size_max (hw_params, - &max_buffer_sz), "Couldn't get maximum buffer_size for device %s: %s", - this->device); - max_buffer_sz = - max_buffer_sz > - GST_ALSA_MAX_BUFFER_SZ ? GST_ALSA_MAX_BUFFER_SZ : max_buffer_sz; - if (buffer_size > max_buffer_sz) { - buffer_size = max_buffer_sz; - this->period_size = GST_ALSA_MAX_BUFFER_SZ / this->period_count; - }; - - - snd_pcm_format_mask_alloca (&mask); - snd_pcm_hw_params_get_format_mask (hw_params, mask); - for (i = 0; i <= SND_PCM_FORMAT_LAST; i++) { - if (snd_pcm_format_mask_test (mask, i)) { - GstCaps *caps = gst_alsa_get_caps_internal (i); - - /* we can never use a format we can't set caps for */ - if (caps != NULL) { - gint n; - - g_assert (gst_caps_get_size (caps) == 1); - - gst_structure_set_value (gst_caps_get_structure (caps, 0), "rate", - &supported_rates); - - add_channels (gst_caps_get_structure (caps, 0), min_channels, - max_channels); - - /* channel configuration */ - /* MIN used to spped up because we don't support more than 8 channels */ - for (n = min_channels; n <= MIN (8, max_channels); n++) { - if (snd_pcm_hw_params_test_channels (this->handle, hw_params, n) == 0) { - GstStructure *str; - GstAudioChannelPosition pos[8] = { - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, - GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, - GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, - GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, - GST_AUDIO_CHANNEL_POSITION_LFE, - GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, - GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT - }; - - switch (n) { - case 1: - pos[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_MONO; - break; - case 2: - case 4: - case 6: - case 8: - /* keep above */ - break; - default: - /* unsupported */ - pos[0] = GST_AUDIO_CHANNEL_POSITION_INVALID; - break; - } - - if (pos[0] != GST_AUDIO_CHANNEL_POSITION_INVALID) { - str = gst_structure_copy (gst_caps_get_structure (caps, 0)); - gst_structure_set (str, "channels", G_TYPE_INT, n, NULL); - if (n > 2) { - gst_audio_set_channel_positions (str, pos); - } - if (!ret) { - ret = gst_caps_new_empty (); - } - gst_caps_append_structure (ret, str); - } - } - } - gst_caps_unref (caps); - } - } - } - - if (ret == NULL) { - GST_WARNING_OBJECT (this, "no supported caps found, returning empty caps"); - ret = gst_caps_new_empty (); - } - gst_caps_do_simplify (ret); - GST_LOG_OBJECT (this, "get_caps returns %P", ret); - - g_value_unset (&supported_rates); - - this->cached_caps = gst_caps_copy (ret); - return ret; -} - -static GstCaps * -gst_alsa_fixate_to_mimetype (const GstCaps * caps, const gchar * mime) -{ - GstCaps *try, *result; - - try = gst_caps_new_simple (mime, NULL); - result = gst_caps_intersect (try, caps); - gst_caps_unref (try); - if (gst_caps_is_empty (result)) { - gst_caps_unref (result); - return NULL; - } - if (gst_caps_is_subset (caps, result)) { - /* we didn't reduce caps */ - gst_caps_unref (result); - return NULL; - } - return result; -} - -static GstCaps * -gst_alsa_fixate_field_nearest_int (const GstCaps * caps, - const gchar * field_name, gint target) -{ - guint i; - GstCaps *result; - GstCaps *smaller = gst_caps_new_empty (); - GstCaps *equal = gst_caps_new_empty (); - GstCaps *bigger = gst_caps_new_empty (); - - /* works like this: we fixate every structure and put them into one of those - * caps depending on what we fixated to. We then return the best caps that is - * not empty in the following order: equal, bigger, smaller - * We also make sure the caps were really reduced. - */ - for (i = 0; i < gst_caps_get_size (caps); i++) { - gint fixated_to; - GstStructure *copy = gst_structure_copy (gst_caps_get_structure (caps, i)); - - gst_caps_structure_fixate_field_nearest_int (copy, field_name, target); - if (gst_structure_get_int (copy, field_name, &fixated_to)) { - if (fixated_to == target) { - gst_caps_append_structure (equal, copy); - } else if (fixated_to > target) { - gst_caps_append_structure (bigger, copy); - } else { - gst_caps_append_structure (smaller, copy); - } - } else { - /* FIXME: what do we do here? Add to all or throw an error? */ - g_return_val_if_reached (NULL); - } - } - if (!gst_caps_is_empty (equal)) { - gst_caps_unref (bigger); - gst_caps_unref (smaller); - result = equal; - } else { - gst_caps_unref (equal); - if (!gst_caps_is_empty (bigger)) { - gst_caps_unref (smaller); - result = bigger; - } else { - gst_caps_unref (bigger); - if (gst_caps_is_empty (smaller)) { - gst_caps_unref (smaller); - return NULL; - } - result = smaller; - } - } - if (gst_caps_is_subset (caps, result)) { - /* we didn't reduce caps */ - gst_caps_unref (result); - return NULL; - } - return result; -} - -GstCaps * -gst_alsa_fixate (GstPad * pad, const GstCaps * caps) -{ - GstCaps *result; - const gchar *mime; - - if ((result = gst_alsa_fixate_to_mimetype (caps, "audio/x-raw-int"))) - return result; - if ((result = gst_alsa_fixate_to_mimetype (caps, "audio/x-raw-float"))) - return result; - if ((result = gst_alsa_fixate_to_mimetype (caps, "audio/x-alaw"))) - return result; - if ((result = gst_alsa_fixate_to_mimetype (caps, "audio/x-mulaw"))) - return result; - - /* now we know there's only one mimetype in the caps */ - /* FIXME: I should check this to be really sure I didn't mess up somewhere */ - - if ((result = gst_alsa_fixate_field_nearest_int (caps, "rate", 44100))) - return result; - if ((result = gst_alsa_fixate_field_nearest_int (caps, "channels", 2))) - return result; - - mime = gst_structure_get_name (gst_caps_get_structure (caps, 0)); - if (g_str_equal (mime, "audio/x-raw-int")) { - if ((result = gst_alsa_fixate_field_nearest_int (caps, "width", 16))) - return result; - if ((result = gst_alsa_fixate_field_nearest_int (caps, "depth", 16))) - return result; - } else if (g_str_equal (mime, "audio/x-raw-float")) { - if ((result = gst_alsa_fixate_field_nearest_int (caps, "width", 32))) - return result; - } - - return NULL; -} - -/* Negotiates the caps */ -GstPadLinkReturn -gst_alsa_link (GstPad * pad, const GstCaps * caps) -{ - GstAlsa *this; - GstAlsaFormat *format; - GstPadLinkReturn ret; - gint old_rate = 0; - - g_return_val_if_fail (caps != NULL, GST_PAD_LINK_REFUSED); - g_return_val_if_fail (pad != NULL, GST_PAD_LINK_REFUSED); - - this = GST_ALSA (gst_pad_get_parent (pad)); - - if (this->handle == NULL) - if (!gst_alsa_open_audio (this)) - return GST_PAD_LINK_REFUSED; - - format = gst_alsa_get_format (gst_caps_get_structure (caps, 0)); - if (format == NULL) - return GST_PAD_LINK_REFUSED; - - GST_DEBUG ("found format %s", snd_pcm_format_name (format->format)); - - if (!GST_FLAG_IS_SET (this, GST_ALSA_CAPS_NEGO)) { - gint i; - - GST_FLAG_SET (this, GST_ALSA_CAPS_NEGO); - - if (gst_alsa_formats_match (this->format, format)) { - ret = GST_PAD_LINK_OK; - goto out; - } - - if (!gst_alsa_probe_hw_params (this, format)) { - ret = GST_PAD_LINK_REFUSED; - goto out; - } - - for (i = 0; i < ((GstElement *) this)->numpads; i++) { - g_assert (this->pad[i] != NULL); - if (this->pad[i] == pad) - continue; - if (gst_pad_try_set_caps (this->pad[i], caps) == GST_PAD_LINK_REFUSED) { - if (this->format) { - GstCaps *old = - gst_alsa_caps (this->format->format, this->format->rate, - this->format->channels); - - for (--i; i >= 0; i--) { - if (gst_pad_try_set_caps (this->pad[i], - old) == GST_PAD_LINK_REFUSED) { - GST_ELEMENT_ERROR (this, CORE, NEGOTIATION, (NULL), - ("could not reset caps to a sane value")); - gst_caps_unref (old); - break; - } else { - /* FIXME: unset caps on pads somehow */ - } - } - gst_caps_unref (old); - ret = GST_PAD_LINK_REFUSED; - goto out; - } - } - } - - GST_FLAG_UNSET (this, GST_ALSA_CAPS_NEGO); - - /* sync the params */ - if (GST_FLAG_IS_SET (this, GST_ALSA_RUNNING)) - gst_alsa_stop_audio (this); - if (this->format) - old_rate = this->format->rate; - g_free (this->format); - this->format = format; - if (this->played && old_rate) - this->played = this->played * this->format->rate / old_rate; - if (!gst_alsa_start_audio (this)) { - GST_ELEMENT_ERROR (this, RESOURCE, SETTINGS, (NULL), (NULL)); - return GST_PAD_LINK_REFUSED; - } - - return GST_PAD_LINK_OK; - } - - return GST_PAD_LINK_DELAYED; - -out: - g_free (format); - GST_FLAG_UNSET (this, GST_ALSA_CAPS_NEGO); - return ret; -} - -static GstElementStateReturn -gst_alsa_change_state (GstElement * element) -{ - int err = 0; - GstAlsa *this; - - g_return_val_if_fail (element != NULL, FALSE); - this = GST_ALSA (element); - - switch (GST_STATE_TRANSITION (element)) { - case GST_STATE_NULL_TO_READY: - if (!(GST_FLAG_IS_SET (element, GST_ALSA_OPEN) || - gst_alsa_open_audio (this))) - return GST_STATE_FAILURE; - break; - case GST_STATE_READY_TO_PAUSED: - this->played = 0; - this->captured = 0; - break; - case GST_STATE_PAUSED_TO_PLAYING: - if (snd_pcm_state (this->handle) == SND_PCM_STATE_PAUSED) { - if ((err = snd_pcm_pause (this->handle, 0)) < 0) { - GST_ERROR_OBJECT (this, "Error unpausing sound: %s", - snd_strerror (err)); - return GST_STATE_FAILURE; - } - } - /* If we were already negotiated, but we are not running, then - * we stopped (probably because we paused), so re-start. If - * there's no format, we didn't negotiate yet so don't do - * anything because ALSA will crash (#151288, #153227, etc.). */ - else if (this->format != NULL && - !GST_FLAG_IS_SET (element, GST_ALSA_RUNNING) && - !gst_alsa_start_audio (this)) { - return GST_STATE_FAILURE; - } - gst_alsa_clock_start (this->clock); - break; - case GST_STATE_PLAYING_TO_PAUSED: - if (GST_ALSA_CAPS_IS_SET (this, GST_ALSA_CAPS_PAUSE)) { - if (snd_pcm_state (this->handle) == SND_PCM_STATE_RUNNING) { - if ((err = snd_pcm_pause (this->handle, 1)) < 0) { - GST_ERROR_OBJECT (this, "Error pausing sound: %s", - snd_strerror (err)); - GST_ALSA_CAPS_SET (this, GST_ALSA_CAPS_PAUSE, 0); - goto cant_pause; - } - } - } else { - cant_pause: - /* if device doesn't know how to pause, we just stop */ - if (GST_FLAG_IS_SET (element, GST_ALSA_RUNNING)) - gst_alsa_stop_audio (this); - } - gst_alsa_clock_stop (this->clock); - break; - case GST_STATE_PAUSED_TO_READY: - if (GST_FLAG_IS_SET (element, GST_ALSA_RUNNING)) - gst_alsa_stop_audio (this); - g_free (this->format); - this->format = NULL; - this->played = 0; - this->captured = 0; - break; - case GST_STATE_READY_TO_NULL: - if (GST_FLAG_IS_SET (element, GST_ALSA_OPEN)) - gst_alsa_close_audio (this); - break; - - default: - break; - } - - if (GST_ELEMENT_CLASS (parent_class)->change_state) - return GST_ELEMENT_CLASS (parent_class)->change_state (element); - - return GST_STATE_SUCCESS; -} - -static GstClock * -gst_alsa_get_clock (GstElement * element) -{ - return GST_CLOCK (GST_ALSA (element)->clock); -} - -static void -gst_alsa_set_clock (GstElement * element, GstClock * clock) -{ - /* we need this function just so everybody knows we use a clock */ - GST_ALSA (element)->ext_clock = clock; -} - -/* AUDIO PROCESSING *********************************************************/ - -inline snd_pcm_sframes_t -gst_alsa_update_avail (GstAlsa * this) -{ - snd_pcm_sframes_t 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); - } - } - return avail; -} - -/* returns TRUE, if the loop should go on */ -inline gboolean -gst_alsa_pcm_wait (GstAlsa * this) -{ - int err; - snd_pcm_state_t state = snd_pcm_state (this->handle); - - if (state == SND_PCM_STATE_RUNNING) { - if ((err = snd_pcm_wait (this->handle, 1000)) < 0) { - if (err == EINTR) { - /* happens mostly when run under gdb, or when exiting due to a signal */ - GST_DEBUG ("got interrupted while waiting"); - if (gst_element_interrupt (GST_ELEMENT (this))) { - return TRUE; - } else { - return FALSE; - } - } - if (!gst_alsa_xrun_recovery (this)) { - GST_ERROR_OBJECT (this, "error waiting for alsa pcm: (%d: %s)", err, - snd_strerror (err)); - return FALSE; - } - } - } else { - GST_INFO_OBJECT (this, "in state %s, not waiting", - snd_pcm_state_name (state)); - } - return TRUE; -} - -/* - * error out or make sure we're in SND_PCM_STATE_RUNNING afterwards - * return FALSE if we're not - */ -inline gboolean -gst_alsa_start (GstAlsa * this) -{ - GST_DEBUG ("Setting state to RUNNING"); - - switch (snd_pcm_state (this->handle)) { - case SND_PCM_STATE_XRUN: - gst_alsa_xrun_recovery (this); - return gst_alsa_start (this); - case SND_PCM_STATE_SETUP: - ERROR_CHECK (snd_pcm_prepare (this->handle), "error preparing: %s"); - case SND_PCM_STATE_SUSPENDED: - case SND_PCM_STATE_PREPARED: - this->captured = 0; - ERROR_CHECK (snd_pcm_start (this->handle), "error starting playback: %s"); - break; - case SND_PCM_STATE_PAUSED: - ERROR_CHECK (snd_pcm_pause (this->handle, 0), "error unpausing: %s"); - break; - case SND_PCM_STATE_RUNNING: - break; - case SND_PCM_STATE_DRAINING: - case SND_PCM_STATE_OPEN: - /* this probably happens when someone replugged a pipeline and we're in a - really weird state because our cothread wasn't busted */ - return FALSE; - default: - /* it's a bug when we get here */ - g_assert_not_reached (); - break; - } - return TRUE; -} - -gboolean -gst_alsa_xrun_recovery (GstAlsa * this) -{ - snd_pcm_status_t *status; - gint err; - - snd_pcm_status_alloca (&status); - - if ((err = snd_pcm_status (this->handle, status)) < 0) - GST_ERROR_OBJECT (this, "status error: %s", snd_strerror (err)); - - if (snd_pcm_status_get_state (status) == SND_PCM_STATE_XRUN) { - struct timeval now, diff, tstamp; - - gettimeofday (&now, 0); - snd_pcm_status_get_trigger_tstamp (status, &tstamp); - timersub (&now, &tstamp, &diff); - - /* if we're allowed to recover, ... */ - if (this->autorecover) { - /* ... then increase the period size or buffer size / period count to - prevent further xruns (at the cost of increased latency and memory - usage). */ - if (this->period_count >= 4) { - this->period_size *= 2; - this->period_count /= 2; - } else { - this->period_count *= 2; - } - } - if (!(gst_alsa_stop_audio (this) && gst_alsa_start_audio (this))) { - GST_ELEMENT_ERROR (this, RESOURCE, FAILED, (NULL), - ("Error restarting audio after xrun")); - return FALSE; - } - } - - return TRUE; -} - -/* AUDIO SETUP / START / STOP ***********************************************/ - -void -gst_alsa_set_eos (GstAlsa * this) -{ - gst_alsa_drain_audio (this); - gst_element_set_eos (GST_ELEMENT (this)); -} - -static gboolean -gst_alsa_open_audio (GstAlsa * this) -{ - snd_pcm_info_t *info; - int ret; - - g_assert (this != NULL); - g_assert (this->handle == NULL); - - /* If we have no pads, then we're apparently a mixer object, - * and that doesn't need a handle to the actual audio device. */ - if (!gst_element_get_pad_list (GST_ELEMENT (this))) - return TRUE; - - GST_INFO ("Opening alsa device \"%s\"...", this->device); - -#ifdef ALSA_BUG_389_FIXED - ERROR_CHECK (snd_output_buffer_open (&this->out), - "error opening log output: %s"); -#endif - - if ((ret = snd_pcm_open (&this->handle, this->device, - GST_ALSA_GET_CLASS (this)->stream, SND_PCM_NONBLOCK)) < 0) { - /* ALSA inverts standard errno.h error codes */ - switch (-ret) { - case EBUSY: - GST_ELEMENT_ERROR (GST_ELEMENT (this), RESOURCE, BUSY, - (_("ALSA device \"%s\" is already in use by another program."), - this->device), (NULL)); - break; - case EACCES: - case ETXTBSY: - GST_ELEMENT_ERROR (GST_ELEMENT (this), RESOURCE, OPEN_READ_WRITE, - (_("Could not access ALSA device \"%s\", check its permissions."), - this->device), GST_ERROR_SYSTEM); - break; - - case ENXIO: - case ENODEV: - case ENOENT: - GST_ELEMENT_ERROR (GST_ELEMENT (this), RESOURCE, BUSY, - (_("ALSA device \"%s\" does not exist."), this->device), (NULL)); - break; - default: - GST_ELEMENT_ERROR (GST_ELEMENT (this), RESOURCE, BUSY, - (_("ALSA device \"%s\" had an error."), - this->device), ("ALSA error %d: %s", ret, snd_strerror (ret))); - break; - } - return FALSE; - } - - snd_pcm_info_malloc (&info); - snd_pcm_info (this->handle, info); - this->cardname = g_strdup (snd_pcm_info_get_name (info)); - snd_pcm_info_free (info); - - GST_FLAG_SET (this, GST_ALSA_OPEN); - return TRUE; -} - -void -gst_alsa_sw_params_dump (GstAlsa * this, snd_pcm_sw_params_t * sw_params) -{ -#ifdef ALSA_BUG_389_FIXED - snd_pcm_sw_params_dump (sw_params, this->out); - ALSA_DEBUG_FLUSH (this); -#endif -} - -void -gst_alsa_hw_params_dump (GstAlsa * this, snd_pcm_hw_params_t * hw_params) -{ -#ifdef ALSA_BUG_389_FIXED - snd_pcm_hw_params_dump (hw_params, this->out); - ALSA_DEBUG_FLUSH (this); -#endif -} - -/* if someone finds an easy way to merge this with _set_hw_params, go ahead */ -static gboolean -gst_alsa_probe_hw_params (GstAlsa * this, GstAlsaFormat * format) -{ - snd_pcm_hw_params_t *hw_params; - snd_pcm_uframes_t period_size; - unsigned int period_count; - unsigned int rate; - - g_return_val_if_fail (this != NULL, FALSE); - g_return_val_if_fail (format != NULL, FALSE); - - GST_INFO ("Probing format: %s %dHz, %d channels", - snd_pcm_format_name (format->format), format->rate, format->channels); - - snd_pcm_hw_params_alloca (&hw_params); - SIMPLE_ERROR_CHECK (snd_pcm_hw_params_any (this->handle, hw_params)); - - gst_alsa_hw_params_dump (this, hw_params); - - if (GST_ELEMENT (this)->numpads == 1) { - SIMPLE_ERROR_CHECK (snd_pcm_hw_params_set_access (this->handle, - hw_params, this-> - mmap ? SND_PCM_ACCESS_MMAP_INTERLEAVED : - SND_PCM_ACCESS_RW_INTERLEAVED)); - } else { - SIMPLE_ERROR_CHECK (snd_pcm_hw_params_set_access (this->handle, - hw_params, this-> - mmap ? SND_PCM_ACCESS_MMAP_NONINTERLEAVED : - SND_PCM_ACCESS_RW_NONINTERLEAVED)); - } - - SIMPLE_ERROR_CHECK (snd_pcm_hw_params_set_format (this->handle, hw_params, - format->format)); - SIMPLE_ERROR_CHECK (snd_pcm_hw_params_set_channels (this->handle, hw_params, - format->channels)); - /* FIXME: We should use snd_pcm_hw_params_set_rate instead of - * snd_pcm_hw_params_set_rate_near here. Unfortunately alsa fails in that case - * far more often and seems to handle this quite well. (Example: ENS1371 - * driver on alsalib 1.0.5, kernel 2.6.6-mm5). If it sets far too wrong sample - * rates, we need to revert back to snd_pcm_hw_params_set_rate or check the - * rate that was set. - */ - rate = format->rate; - SIMPLE_ERROR_CHECK (snd_pcm_hw_params_set_rate_near (this->handle, hw_params, - &rate, 0)); - if (rate != format->rate) - GST_WARNING_OBJECT (this, "set rate (%u) differs from desired rate (%u)", - rate, format->rate); - - period_count = this->period_count; - SIMPLE_ERROR_CHECK (snd_pcm_hw_params_set_periods_near (this->handle, - hw_params, &period_count, 0)); - period_size = this->period_size; - SIMPLE_ERROR_CHECK (snd_pcm_hw_params_set_period_size_near (this->handle, - hw_params, &period_size, 0)); - - return TRUE; -} - -/* - * You must set all hw parameters at once and can't use already set params and - * change them. - * Thx ALSA for not documenting this - */ -static gboolean -gst_alsa_set_hw_params (GstAlsa * this) -{ - snd_pcm_hw_params_t *hw_params; - unsigned int rate; - - g_return_val_if_fail (this != NULL, FALSE); - g_return_val_if_fail (this->handle != NULL, FALSE); - - snd_pcm_hw_params_alloca (&hw_params); - ERROR_CHECK (snd_pcm_hw_params_any (this->handle, hw_params), - "Broken configuration for this PCM: %s"); - - if (this->format) { - GST_INFO ("Preparing format: %s %dHz, %d channels", - snd_pcm_format_name (this->format->format), this->format->rate, - this->format->channels); - - if (GST_ELEMENT (this)->numpads == 1) { - ERROR_CHECK (snd_pcm_hw_params_set_access (this->handle, hw_params, this-> - mmap ? SND_PCM_ACCESS_MMAP_INTERLEAVED : - SND_PCM_ACCESS_RW_INTERLEAVED), - "This plugin does not support your harware: %s"); - } else { - ERROR_CHECK (snd_pcm_hw_params_set_access (this->handle, hw_params, this-> - mmap ? SND_PCM_ACCESS_MMAP_NONINTERLEAVED : - SND_PCM_ACCESS_RW_NONINTERLEAVED), - "This plugin does not support your harware: %s"); - } - - ERROR_CHECK (snd_pcm_hw_params_set_format (this->handle, hw_params, - this->format->format), "Sample format (%s) not available: %s", - snd_pcm_format_name (this->format->format)); - ERROR_CHECK (snd_pcm_hw_params_set_channels (this->handle, hw_params, - this->format->channels), "Channels count (%d) not available: %s", - this->format->channels); - /* FIXME: We should use snd_pcm_hw_params_set_rate instead of - * snd_pcm_hw_params_set_rate_near here. Unfortunately alsa fails in that case - * far more often and seems to handle this quite well. (Example: ENS1371 - * driver on alsalib 1.0.5, kernel 2.6.6-mm5). If it sets far too wrong sample - * rates, we need to revert back to snd_pcm_hw_params_set_rate or check the - * rate that was set. - */ - rate = this->format->rate; - ERROR_CHECK (snd_pcm_hw_params_set_rate_near (this->handle, hw_params, - &rate, 0), "error setting rate (%d): %s", this->format->rate); - if (rate != this->format->rate) - GST_WARNING_OBJECT (this, "set rate (%u) differs from desired rate (%u)", - rate, this->format->rate); - ERROR_CHECK (snd_pcm_hw_params_set_periods_near (this->handle, hw_params, - &this->period_count, 0), "error setting period count to %u: %s", - (guint) this->period_count); - ERROR_CHECK (snd_pcm_hw_params_set_period_size_near (this->handle, - hw_params, &this->period_size, 0), - "error setting period size to %u frames: %s", - (guint) this->period_size); - } else { - GST_INFO_OBJECT (this, "Preparing format: (none)"); - } - gst_alsa_hw_params_dump (this, hw_params); - - - ERROR_CHECK (snd_pcm_hw_params (this->handle, hw_params), - "Could not set hardware parameters: %s"); - - /* now get the pcm caps */ - GST_ALSA_CAPS_SET (this, GST_ALSA_CAPS_PAUSE, - snd_pcm_hw_params_can_pause (hw_params)); - GST_ALSA_CAPS_SET (this, GST_ALSA_CAPS_RESUME, - snd_pcm_hw_params_can_resume (hw_params)); - GST_ALSA_CAPS_SET (this, GST_ALSA_CAPS_SYNC_START, - snd_pcm_hw_params_can_sync_start (hw_params)); - - if (this->mmap) { - this->transmit = GST_ALSA_GET_CLASS (this)->transmit_mmap; - } else { - this->transmit = GST_ALSA_GET_CLASS (this)->transmit_rw; - } - - return TRUE; -} - -static gboolean -gst_alsa_set_sw_params (GstAlsa * this) -{ - snd_pcm_sw_params_t *sw_params; - - if (!this->format) { - GST_LOG_OBJECT (this, "not setting sw params, we're not negotiated yet"); - return TRUE; - } - - snd_pcm_sw_params_alloca (&sw_params); - ERROR_CHECK (snd_pcm_sw_params_current (this->handle, sw_params), - "Could not get current software parameters: %s"); - - gst_alsa_sw_params_dump (this, sw_params); - - ERROR_CHECK (snd_pcm_sw_params_set_silence_size (this->handle, sw_params, 0), - "could not set silence size: %s"); - ERROR_CHECK (snd_pcm_sw_params_set_silence_threshold (this->handle, sw_params, - 0), "could not set silence threshold: %s"); - ERROR_CHECK (snd_pcm_sw_params_set_avail_min (this->handle, sw_params, - this->period_size), "could not set avail min: %s"); - /* we start explicitly */ - ERROR_CHECK (snd_pcm_sw_params_set_start_threshold (this->handle, sw_params, - this->period_size * this->period_count + 1), - "could not set start mode: %s"); - ERROR_CHECK (snd_pcm_sw_params_set_stop_threshold (this->handle, sw_params, - this->period_size * this->period_count), - "could not set stop mode: %s"); - ERROR_CHECK (snd_pcm_sw_params_set_xfer_align (this->handle, sw_params, 1), - "Unable to set transfer align for playback: %s"); - ERROR_CHECK (snd_pcm_sw_params (this->handle, sw_params), - "could not set sw_params: %s"); - return TRUE; -} - -static gboolean -gst_alsa_start_audio (GstAlsa * this) -{ - g_assert (GST_FLAG_IS_SET (this, GST_ALSA_OPEN)); - - if (!gst_alsa_set_hw_params (this)) - return FALSE; - if (!gst_alsa_set_sw_params (this)) - GST_WARNING_OBJECT (this, - "setting software parameters failed, we'll trust the defaults"); - - GST_FLAG_SET (this, GST_ALSA_RUNNING); - return TRUE; -} - -static gboolean -gst_alsa_drain_audio (GstAlsa * this) -{ - g_assert (this != NULL); - g_return_val_if_fail (this->handle != NULL, FALSE); - - GST_DEBUG ("stopping alsa"); - - switch (snd_pcm_state (this->handle)) { - case SND_PCM_STATE_XRUN: - case SND_PCM_STATE_RUNNING: - /* fall through - clock is already stopped when paused */ - case SND_PCM_STATE_PAUSED: - /* snd_pcm_drain only works in blocking mode */ - ERROR_CHECK (snd_pcm_nonblock (this->handle, 0), - "couldn't set blocking mode: %s"); - ERROR_CHECK (snd_pcm_drain (this->handle), - "couldn't stop and drain buffer: %s"); - ERROR_CHECK (snd_pcm_nonblock (this->handle, 1), - "couldn't set non-blocking mode: %s"); - break; - default: - break; - } - - GST_DEBUG ("stopped alsa"); - GST_FLAG_UNSET (this, GST_ALSA_RUNNING); - return TRUE; -} - -static gboolean -gst_alsa_stop_audio (GstAlsa * this) -{ - g_assert (this != NULL); - g_return_val_if_fail (this->handle != NULL, FALSE); - - GST_DEBUG ("stopping alsa, skipping pending frames"); - - switch (snd_pcm_state (this->handle)) { - case SND_PCM_STATE_XRUN: - case SND_PCM_STATE_RUNNING: - /* fall through - clock is already stopped when paused */ - case SND_PCM_STATE_PAUSED: - ERROR_CHECK (snd_pcm_drop (this->handle), - "couldn't stop (dropping frames): %s"); - break; - default: - break; - } - - GST_FLAG_UNSET (this, GST_ALSA_RUNNING); - return TRUE; -} - -static gboolean -gst_alsa_close_audio (GstAlsa * this) -{ -#ifdef ALSA_BUG_389_FIXED - gint err; -#endif - - /* if there's no pads, we never open. So we don't close either. */ - if (!gst_element_get_pad_list (GST_ELEMENT (this))) - return TRUE; - - g_return_val_if_fail (this != NULL, FALSE); - g_return_val_if_fail (this->handle != NULL, FALSE); - -#ifdef ALSA_BUG_389_FIXED - ALSA_DEBUG_FLUSH (this); - err = snd_output_close (this->out); - if (err != 0) - GST_ERROR_OBJECT (this, "failed to close debugging output: %s", - snd_strerror (err)); -#endif - ERROR_CHECK (snd_pcm_close (this->handle), "Error closing device: %s"); - - this->handle = NULL; - if (this->cardname) { - g_free (this->cardname); - this->cardname = NULL; - } - GST_ALSA_CAPS_SET (this, GST_ALSA_CAPS_PAUSE, 0); - GST_ALSA_CAPS_SET (this, GST_ALSA_CAPS_RESUME, 0); - GST_ALSA_CAPS_SET (this, GST_ALSA_CAPS_SYNC_START, 0); - GST_FLAG_UNSET (this, GST_ALSA_OPEN); - if (this->cached_caps) { - gst_caps_unref (this->cached_caps); - this->cached_caps = NULL; - } - - return TRUE; -} - -/* QUERYING/FORMAT/CONVERSION FUNCTIONS *************************************/ - -static const GstFormat * -gst_alsa_get_formats (GstPad * pad) -{ - static const GstFormat formats[] = { - GST_FORMAT_TIME, - GST_FORMAT_DEFAULT, - GST_FORMAT_BYTES, - 0 - }; - - return formats; -} - -static gboolean -gst_alsa_pad_convert (GstPad * pad, GstFormat src_format, gint64 src_value, - GstFormat * dest_format, gint64 * dest_value) -{ - return gst_alsa_convert (GST_ALSA (GST_PAD_PARENT (pad)), src_format, - src_value, dest_format, dest_value); -} - -static gboolean -gst_alsa_convert (GstAlsa * this, GstFormat src_format, gint64 src_value, - GstFormat * dest_format, gint64 * dest_value) -{ - gboolean res = TRUE; - - if (src_format == *dest_format) { - *dest_value = src_value; - return TRUE; - } - if (this->format == NULL) - return FALSE; - - switch (src_format) { - case GST_FORMAT_BYTES: - switch (*dest_format) { - case GST_FORMAT_DEFAULT: - *dest_value = gst_alsa_bytes_to_samples (this, (guint) src_value); - break; - case GST_FORMAT_TIME: - *dest_value = gst_alsa_bytes_to_timestamp (this, (guint) src_value); - break; - default: - res = FALSE; - break; - } - break; - case GST_FORMAT_TIME: - switch (*dest_format) { - case GST_FORMAT_DEFAULT: - *dest_value = - gst_alsa_timestamp_to_samples (this, (GstClockTime) src_value); - break; - case GST_FORMAT_BYTES: - *dest_value = - gst_alsa_timestamp_to_bytes (this, (GstClockTime) src_value); - break; - default: - res = FALSE; - break; - } - break; - case GST_FORMAT_DEFAULT: - switch (*dest_format) { - case GST_FORMAT_TIME: - *dest_value = gst_alsa_samples_to_timestamp (this, (guint) src_value); - break; - case GST_FORMAT_BYTES: - *dest_value = gst_alsa_samples_to_bytes (this, (guint) src_value); - break; - case GST_FORMAT_DEFAULT: - g_assert_not_reached (); - /* fall through */ - default: - res = FALSE; - break; - } - break; - default: - res = FALSE; - } - - return res; -} - -static const GstQueryType * -gst_alsa_get_query_types (GstPad * pad) -{ - static const GstQueryType query_types[] = { - GST_QUERY_LATENCY, - GST_QUERY_POSITION, - 0, - }; - - return query_types; -} - -static gboolean -gst_alsa_query_func (GstElement * element, GstQueryType type, - GstFormat * format, gint64 * value) -{ - gboolean res = FALSE; - GstAlsa *this = GST_ALSA (element); - - switch (type) { - case GST_QUERY_LATENCY:{ - snd_pcm_sframes_t delay; - - ERROR_CHECK (snd_pcm_delay (this->handle, &delay), - "Error getting delay: %s"); - res = - gst_alsa_convert (this, GST_FORMAT_DEFAULT, (gint64) delay, format, - value); - break; - } - case GST_QUERY_POSITION: - res = - gst_alsa_convert (this, GST_FORMAT_TIME, - gst_element_get_time (GST_ELEMENT (this)), format, value); - break; - default: - break; - } - - return res; -} - -static gboolean -gst_alsa_query (GstElement * element, GstQueryType type, GstFormat * format, - gint64 * value) -{ - return gst_alsa_pad_query (GST_ALSA (element)->pad[0], type, format, value); -} - -static gboolean -gst_alsa_pad_query (GstPad * pad, GstQueryType type, GstFormat * format, - gint64 * value) -{ - if (gst_alsa_query_func (GST_PAD_PARENT (pad), type, format, value)) - return TRUE; - - if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK - && gst_pad_query (gst_pad_get_peer (pad), type, format, value)) - return TRUE; - - return FALSE; -} - -inline snd_pcm_uframes_t -gst_alsa_timestamp_to_samples (GstAlsa * this, GstClockTime time) -{ - return (snd_pcm_uframes_t) ((time * this->format->rate + - this->format->rate / 2) / GST_SECOND); -} - -inline GstClockTime -gst_alsa_samples_to_timestamp (GstAlsa * this, snd_pcm_uframes_t samples) -{ - return ((GstClockTime) samples) * GST_SECOND / this->format->rate; -} - -inline snd_pcm_uframes_t -gst_alsa_bytes_to_samples (GstAlsa * this, guint bytes) -{ - return bytes / (snd_pcm_format_physical_width (this->format->format) / 8) / - (GST_ELEMENT (this)->numpads == 1 ? this->format->channels : 1); -} - -inline guint -gst_alsa_samples_to_bytes (GstAlsa * this, snd_pcm_uframes_t samples) -{ - return samples * snd_pcm_format_physical_width (this->format->format) / 8 * - (GST_ELEMENT (this)->numpads == 1 ? this->format->channels : 1); -} - -inline GstClockTime -gst_alsa_bytes_to_timestamp (GstAlsa * this, guint bytes) -{ - return gst_alsa_samples_to_timestamp (this, gst_alsa_bytes_to_samples (this, - bytes)); -} - -inline guint -gst_alsa_timestamp_to_bytes (GstAlsa * this, GstClockTime time) -{ - return gst_alsa_samples_to_bytes (this, gst_alsa_timestamp_to_samples (this, - time)); -} diff --git a/ext/alsa/gstalsa.h b/ext/alsa/gstalsa.h index 74dd2c8..a38f22b 100644 --- a/ext/alsa/gstalsa.h +++ b/ext/alsa/gstalsa.h @@ -19,9 +19,11 @@ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #ifndef __GST_ALSA_H__ #define __GST_ALSA_H__ + #define ALSA_PCM_NEW_HW_PARAMS_API #define ALSA_PCM_NEW_SW_PARAMS_API @@ -31,210 +33,4 @@ #include -GST_DEBUG_CATEGORY_EXTERN (alsa_debug); -#define GST_CAT_DEFAULT alsa_debug - - -/* error checking for standard alsa functions */ -/* NOTE: these functions require a GObject *this and can only be used in - functions that return TRUE on success and FALSE on error */ -#define SIMPLE_ERROR_CHECK(value) G_STMT_START{ \ - int err = (value); \ - if (err < 0) { \ - GST_WARNING_OBJECT (this, "\"" #value "\": %s", snd_strerror (err)); \ - return FALSE; \ - } \ -}G_STMT_END - - -#ifdef G_HAVE_ISO_VARARGS -#define ERROR_CHECK(value, ...) G_STMT_START{ \ - int err = (value); \ - if (err < 0) { \ - GST_WARNING_OBJECT (this, __VA_ARGS__, snd_strerror (err)); \ - return FALSE; \ - } \ -}G_STMT_END - -#elif defined(G_HAVE_GNUC_VARARGS) -#define ERROR_CHECK(value, args...) G_STMT_START{ \ - int err = (value); \ - if (err < 0) { \ - GST_WARNING_OBJECT (this, ## args, snd_strerror (err)); \ - return FALSE; \ - } \ -}G_STMT_END - -#else -#define ERROR_CHECK(value, args...) G_STMT_START{ \ - int err = (value); \ - if (err < 0) { \ - GST_WARNING_OBJECT (this, snd_strerror (err)); \ - return FALSE; \ - } \ -}G_STMT_END -#endif - - -#define GST_ALSA_MIN_RATE 8000 -#define GST_ALSA_MAX_RATE 192000 -#define GST_ALSA_MIN_PERIOD_CNT 2 -#define GST_ALSA_MAX_PERIOD_CNT 64 -#define GST_ALSA_MIN_PERIOD_SZ 2 -#define GST_ALSA_MAX_PERIOD_SZ 8192 -#define GST_ALSA_MIN_BUFFER_SZ GST_ALSA_MIN_PERIOD_CNT*GST_ALSA_MIN_PERIOD_SZ -#define GST_ALSA_MAX_BUFFER_SZ 65536 -#define GST_ALSA_MAX_TRACKS 64 /* we don't support more than 64 tracks */ -#define GST_ALSA_MAX_CHANNELS 32 /* tracks can have up to 32 channels */ - -/* a few alsa functions return an int value and a 'direction', -1, 0 or +1 - 0 = exact value - -1 = real value is up to 1 before given value - +1 = real value is up to 1 after given value -*/ -#define GST_ALSA_DIR_MIN(i) ((i == 1) ? 1 : 0) -#define GST_ALSA_DIR_MAX(i) ((i ==-1) ?-1 : 0) - -/* Mono is 1 channel ; the 5.1 standard is 6 channels. The value for - GST_ALSA_MAX_CHANNELS comes from alsa/mixer.h. */ - -/* Max allowed discontinuity in time units between timestamp and playback - pointer before killing/inserting samples. This should be big enough to allow - smoothing errors on different video formats. */ -#define GST_ALSA_DEFAULT_DISCONT (GST_SECOND / 10) - -G_BEGIN_DECLS - -#define GST_ALSA(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, GST_TYPE_ALSA, GstAlsa)) -#define GST_ALSA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST(klass, GST_TYPE_ALSA, GstAlsaClass)) -#define GST_ALSA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_ALSA, GstAlsaClass)) -#define GST_IS_ALSA(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, GST_TYPE_ALSA)) -#define GST_IS_ALSA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE(klass, GST_TYPE_ALSA)) -#define GST_TYPE_ALSA (gst_alsa_get_type()) - -enum { - GST_ALSA_OPEN = GST_ELEMENT_FLAG_LAST, - GST_ALSA_RUNNING, - GST_ALSA_CAPS_NEGO, - GST_ALSA_FLAG_LAST = GST_ELEMENT_FLAG_LAST + 3 -}; - -typedef enum { - GST_ALSA_CAPS_PAUSE = 0, - GST_ALSA_CAPS_RESUME, - GST_ALSA_CAPS_SYNC_START - /* add more */ -} GstAlsaPcmCaps; - -#define GST_ALSA_CAPS_IS_SET(obj, flag) (GST_ALSA (obj)->pcm_caps & (1<<(flag))) -#define GST_ALSA_CAPS_SET(obj, flag, set) G_STMT_START{ \ - if (set) { (GST_ALSA (obj)->pcm_caps |= (1<<(flag))); } \ - else { (GST_ALSA (obj)->pcm_caps &= ~(1<<(flag))); } \ -}G_STMT_END - -typedef struct _GstAlsaClock GstAlsaClock; -typedef struct _GstAlsaClockClass GstAlsaClockClass; - -typedef struct _GstAlsa GstAlsa; -typedef struct _GstAlsaClass GstAlsaClass; - -typedef int (*GstAlsaTransmitFunction) (GstAlsa *this, snd_pcm_sframes_t *avail); - -typedef struct { - snd_pcm_format_t format; - guint rate; - gint channels; -} GstAlsaFormat; - -struct _GstAlsa { - GstElement parent; - - /* array of GstAlsaPads */ - GstPad * pad[GST_ALSA_MAX_TRACKS]; - - gchar * device; - gchar * cardname; - snd_pcm_t * handle; - guint pcm_caps; /* capabilities of the pcm device, see GstAlsaPcmCaps */ - snd_output_t * out; - - GstAlsaFormat * format; /* NULL if undefined */ - gboolean mmap; /* use mmap transmit (fast) or read/write (sloooow) */ - GstAlsaTransmitFunction transmit; - GstCaps * cached_caps; /* we cache caps to speed up get_caps */ - - /* latency / performance parameters */ - snd_pcm_uframes_t period_size; - unsigned int period_count; - - gboolean autorecover; - - /* clocking */ - GstAlsaClock * clock; /* our provided clock */ - GstClock * ext_clock; /* externally set clock. */ - snd_pcm_uframes_t played; /* samples transmitted since last sync - This thing actually is our master clock. - We will event insert silent samples or - drop some to sync to incoming timestamps. - */ - snd_pcm_uframes_t captured; - GstClockTime max_discont; /* max difference between current - playback timestamp and buffers timestamps - */ -}; - -struct _GstAlsaClass { - GstElementClass parent_class; - - snd_pcm_stream_t stream; - - /* different transmit functions */ - GstAlsaTransmitFunction transmit_mmap; - GstAlsaTransmitFunction transmit_rw; - - /* autodetected devices available */ - GList *devices; -}; - -GType gst_alsa_get_type (void); - -void gst_alsa_set_eos (GstAlsa * this); -GstPadLinkReturn gst_alsa_link (GstPad * pad, - const GstCaps * caps); -GstCaps * gst_alsa_get_caps (GstPad * pad); -GstCaps * gst_alsa_fixate (GstPad * pad, - const GstCaps * caps); -GstCaps * gst_alsa_caps (snd_pcm_format_t format, - gint rate, - gint channels); - -/* audio processing functions */ -inline snd_pcm_sframes_t gst_alsa_update_avail (GstAlsa * this); -inline gboolean gst_alsa_pcm_wait (GstAlsa * this); -inline gboolean gst_alsa_start (GstAlsa * this); -gboolean gst_alsa_xrun_recovery (GstAlsa * this); - -/* format conversions */ -inline snd_pcm_uframes_t gst_alsa_timestamp_to_samples (GstAlsa * this, - GstClockTime time); -inline GstClockTime gst_alsa_samples_to_timestamp (GstAlsa * this, - snd_pcm_uframes_t samples); -inline snd_pcm_uframes_t gst_alsa_bytes_to_samples (GstAlsa * this, - guint bytes); -inline guint gst_alsa_samples_to_bytes (GstAlsa * this, - snd_pcm_uframes_t samples); -inline GstClockTime gst_alsa_bytes_to_timestamp (GstAlsa * this, - guint bytes); -inline guint gst_alsa_timestamp_to_bytes (GstAlsa * this, - GstClockTime time); - -/* debugging functions (useful in gdb) - require running with --gst-debug=alsa:4 or better */ -void gst_alsa_sw_params_dump (GstAlsa * this, - snd_pcm_sw_params_t * sw_params); -void gst_alsa_hw_params_dump (GstAlsa * this, - snd_pcm_hw_params_t * hw_params); - - -G_END_DECLS - #endif /* __GST_ALSA_H__ */ diff --git a/ext/alsa/gstalsaclock.c b/ext/alsa/gstalsaclock.c deleted file mode 100644 index beb502f..0000000 --- a/ext/alsa/gstalsaclock.c +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (C) 2001 CodeFactory AB - * Copyright (C) 2001 Thomas Nyberg - * Copyright (C) 2001-2002 Andy Wingo - * Copyright (C) 2003 Benjamin Otte - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstalsaclock.h" - -/* clock functions */ -static void gst_alsa_clock_class_init (gpointer g_class, gpointer class_data); -static void gst_alsa_clock_init (GstAlsaClock * clock); - -static GstClockTime gst_alsa_clock_get_internal_time (GstClock * clock); -static guint64 gst_alsa_clock_get_resolution (GstClock * clock); -static GstClockEntryStatus gst_alsa_clock_wait (GstClock * clock, - GstClockEntry * entry); -static void gst_alsa_clock_unlock (GstClock * clock, GstClockEntry * entry); - -static GstClockClass *clock_parent_class = NULL; - -/* static guint gst_alsa_clock_signals[LAST_SIGNAL] = { 0 }; */ - -GType -gst_alsa_clock_get_type (void) -{ - static GType clock_type = 0; - - if (!clock_type) { - static const GTypeInfo clock_info = { - sizeof (GstAlsaClockClass), - NULL, - NULL, - gst_alsa_clock_class_init, - NULL, - NULL, - sizeof (GstAlsaClock), - 4, - (GInstanceInitFunc) gst_alsa_clock_init, - NULL - }; - - clock_type = g_type_register_static (GST_TYPE_CLOCK, "GstAlsaClock", - &clock_info, 0); - } - return clock_type; -} -static void -gst_alsa_clock_class_init (gpointer g_class, gpointer class_data) -{ - GObjectClass *gobject_class; - GstObjectClass *gstobject_class; - GstClockClass *gstclock_class; - GstAlsaClockClass *klass; - - klass = (GstAlsaClockClass *) g_class; - gobject_class = (GObjectClass *) klass; - gstobject_class = (GstObjectClass *) klass; - gstclock_class = (GstClockClass *) klass; - - clock_parent_class = g_type_class_ref (GST_TYPE_CLOCK); - - gstclock_class->get_internal_time = gst_alsa_clock_get_internal_time; - gstclock_class->get_resolution = gst_alsa_clock_get_resolution; - gstclock_class->wait = gst_alsa_clock_wait; - gstclock_class->unlock = gst_alsa_clock_unlock; -} -static void -gst_alsa_clock_init (GstAlsaClock * clock) -{ - gst_object_set_name (GST_OBJECT (clock), "GstAlsaClock"); - - clock->start_time = GST_CLOCK_TIME_NONE; -} - -GstAlsaClock * -gst_alsa_clock_new (gchar * name, GstAlsaClockGetTimeFunc get_time, - GstAlsa * owner) -{ - GstAlsaClock *alsa_clock = - GST_ALSA_CLOCK (g_object_new (GST_TYPE_ALSA_CLOCK, NULL)); - - g_assert (alsa_clock); - - alsa_clock->get_time = get_time; - alsa_clock->owner = owner; - alsa_clock->adjust = 0; - - gst_object_set_name (GST_OBJECT (alsa_clock), name); - gst_object_set_parent (GST_OBJECT (alsa_clock), GST_OBJECT (owner)); - - return alsa_clock; -} - -void -gst_alsa_clock_start (GstAlsaClock * clock) -{ - g_assert (!GST_CLOCK_TIME_IS_VALID (clock->start_time)); - - if (clock->owner->format) { - clock->start_time = gst_clock_get_event_time (GST_CLOCK (clock)) - - clock->get_time (clock->owner); - } else { - clock->start_time = gst_clock_get_event_time (GST_CLOCK (clock)); - } -} -void -gst_alsa_clock_stop (GstAlsaClock * clock) -{ - GTimeVal timeval; - - g_get_current_time (&timeval); - - g_assert (GST_CLOCK_TIME_IS_VALID (clock->start_time)); - - clock->adjust += - GST_TIMEVAL_TO_TIME (timeval) - - gst_clock_get_event_time (GST_CLOCK (clock)); - clock->start_time = GST_CLOCK_TIME_NONE; -} - -static GstClockTime -gst_alsa_clock_get_internal_time (GstClock * clock) -{ - GstAlsaClock *alsa_clock = GST_ALSA_CLOCK (clock); - - if (GST_CLOCK_TIME_IS_VALID (alsa_clock->start_time)) { - return alsa_clock->get_time (alsa_clock->owner) + alsa_clock->start_time; - } else { - GTimeVal timeval; - - g_get_current_time (&timeval); - return GST_TIMEVAL_TO_TIME (timeval) + alsa_clock->adjust; - } -} -static guint64 -gst_alsa_clock_get_resolution (GstClock * clock) -{ - GstAlsaClock *this = GST_ALSA_CLOCK (clock); - - if (this->owner->format) { - return GST_SECOND / this->owner->format->rate; - } else { - /* FIXME: is there an "unknown" value? We just return the sysclock's time by default */ - return 1 * GST_USECOND; - } -} -static GstClockEntryStatus -gst_alsa_clock_wait (GstClock * clock, GstClockEntry * entry) -{ - GstClockTime target, entry_time, glib_start, glib_cur; - GstClockTimeDiff diff; - GstAlsaClock *this = GST_ALSA_CLOCK (clock); - GTimeVal t; - - entry_time = gst_alsa_clock_get_internal_time (clock); - diff = GST_CLOCK_ENTRY_TIME (entry) - gst_clock_get_time (clock); - - if (diff < 0) - return GST_CLOCK_ENTRY_EARLY; - - if (diff > clock->max_diff) { - GST_INFO_OBJECT (this, - "GstAlsaClock: abnormal clock request diff: %" GST_TIME_FORMAT ") >" - " %" GST_TIME_FORMAT, GST_TIME_ARGS (diff), - GST_TIME_ARGS (clock->max_diff)); - return GST_CLOCK_ENTRY_EARLY; - } - - target = entry_time + diff; - - GST_DEBUG_OBJECT (this, "real_target %" GST_TIME_FORMAT - " target %" GST_TIME_FORMAT - " now %" GST_TIME_FORMAT, - GST_TIME_ARGS (target), GST_TIME_ARGS (GST_CLOCK_ENTRY_TIME (entry)), - GST_TIME_ARGS (entry_time)); - - g_get_current_time (&t); - glib_cur = glib_start = GST_TIMEVAL_TO_TIME (t); - while (gst_alsa_clock_get_internal_time (clock) < target && - this->last_unlock < entry_time && glib_start + diff * 1.5 > glib_cur) { - g_usleep (gst_alsa_clock_get_resolution (clock) * G_USEC_PER_SEC / - GST_SECOND); - g_get_current_time (&t); - glib_cur = GST_TIMEVAL_TO_TIME (t); - } - - return entry->status; -} -static void -gst_alsa_clock_unlock (GstClock * clock, GstClockEntry * entry) -{ - GstAlsaClock *this = GST_ALSA_CLOCK (clock); - - this->last_unlock = this->get_time (this->owner); -} diff --git a/ext/alsa/gstalsaclock.h b/ext/alsa/gstalsaclock.h deleted file mode 100644 index 4629289..0000000 --- a/ext/alsa/gstalsaclock.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2001 CodeFactory AB - * Copyright (C) 2001 Thomas Nyberg - * Copyright (C) 2001-2002 Andy Wingo - * Copyright (C) 2003 Benjamin Otte - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef __GST_ALSA_CLOCK_H__ -#define __GST_ALSA_CLOCK_H__ - -#include "gstalsa.h" - -G_BEGIN_DECLS - -#define GST_ALSA_CLOCK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ALSA_CLOCK,GstAlsaClock)) -#define GST_ALSA_CLOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ALSA_CLOCK,GstAlsaClockClass)) -#define GST_IS_ALSA_CLOCK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ALSA_CLOCK)) -#define GST_IS_ALSA_CLOCK_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ALSA_CLOCK)) -#define GST_TYPE_ALSA_CLOCK (gst_alsa_clock_get_type()) - -typedef GstClockTime (*GstAlsaClockGetTimeFunc) (GstAlsa *); - -struct _GstAlsaClock { - GstSystemClock parent; - - GstAlsaClockGetTimeFunc get_time; - GstAlsa * owner; - - GstClockTimeDiff adjust; /* adjustment to real clock (recalculated when stopping) */ - GstClockTime start_time; /* time when the stream started (NONE when stopped) */ - GstClockTime last_unlock; /* time of last unlock request */ -}; - -struct _GstAlsaClockClass { - GstSystemClockClass parent_class; -}; - -GType gst_alsa_clock_get_type (void); -GstAlsaClock * gst_alsa_clock_new (gchar * name, - GstAlsaClockGetTimeFunc func, - GstAlsa * owner); - -void gst_alsa_clock_start (GstAlsaClock * clock); -void gst_alsa_clock_stop (GstAlsaClock * clock); - -G_END_DECLS - -#endif /* __GST_ALSA_CLOCK_H__ */ diff --git a/ext/alsa/gstalsamixer.c b/ext/alsa/gstalsamixer.c index d66dda1..65db1d0 100644 --- a/ext/alsa/gstalsamixer.c +++ b/ext/alsa/gstalsamixer.c @@ -17,113 +17,91 @@ * Boston, MA 02111-1307, USA. */ + #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "gstalsamixer.h" -/* elementfactory information */ + static GstElementDetails gst_alsa_mixer_details = GST_ELEMENT_DETAILS ("Alsa Mixer", "Generic/Audio", "Control sound input and output levels with ALSA", "Leif Johnson "); -static void gst_alsa_interface_init (GstImplementsInterfaceClass * klass); -static void gst_alsa_mixer_class_init (gpointer g_class, gpointer class_data); -static void gst_alsa_mixer_init (GstAlsaMixer * mixer); -static void gst_alsa_mixer_interface_init (GstMixerClass * klass); -static gboolean gst_alsa_mixer_supported (GstImplementsInterface * iface, - GType iface_type); +#define GST_BOILERPLATE_WITH_INTERFACE(type, type_as_function, parent_type, \ + parent_type_as_macro, interface_type, interface_type_as_macro, \ + interface_as_function) \ + \ +static void interface_as_function ## _interface_init (interface_type ## Class *klass); \ +static gboolean interface_as_function ## _supported (type *object, GType iface_type); \ + \ +static void \ +type_as_function ## _implements_interface_init (GstImplementsInterfaceClass *klass) \ +{ \ + klass->supported = (gpointer)interface_as_function ## _supported; \ +} \ + \ +static void \ +type_as_function ## _init_interfaces (GType type) \ +{ \ + static const GInterfaceInfo implements_iface_info = { \ + (GInterfaceInitFunc) type_as_function ## _implements_interface_init, \ + NULL, \ + NULL, \ + }; \ + static const GInterfaceInfo iface_info = { \ + (GInterfaceInitFunc) interface_as_function ## _interface_init, \ + NULL, \ + NULL, \ + }; \ + \ + g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE, \ + &implements_iface_info); \ + g_type_add_interface_static (type, interface_type_as_macro, &iface_info); \ +} \ + \ +GST_BOILERPLATE_FULL (type, type_as_function, parent_type, \ + parent_type_as_macro, type_as_function ## _init_interfaces) + +GST_BOILERPLATE_WITH_INTERFACE (GstAlsaMixer, gst_alsa_mixer, GstElement, + GST_TYPE_ELEMENT, GstMixer, GST_TYPE_MIXER, gst_alsa_mixer); -/* GStreamer stuff */ static GstElementStateReturn gst_alsa_mixer_change_state (GstElement * element); -static void gst_alsa_mixer_build_list (GstAlsaMixer * mixer); -static void gst_alsa_mixer_free_list (GstAlsaMixer * mixer); - -/* interface implementation */ +/* GstMixer */ static const GList *gst_alsa_mixer_list_tracks (GstMixer * mixer); - static void gst_alsa_mixer_set_volume (GstMixer * mixer, GstMixerTrack * track, gint * volumes); static void gst_alsa_mixer_get_volume (GstMixer * mixer, GstMixerTrack * track, gint * volumes); - static void gst_alsa_mixer_set_record (GstMixer * mixer, GstMixerTrack * track, gboolean record); static void gst_alsa_mixer_set_mute (GstMixer * mixer, GstMixerTrack * track, gboolean mute); - static void gst_alsa_mixer_set_option (GstMixer * mixer, GstMixerOptions * opts, gchar * value); static const gchar *gst_alsa_mixer_get_option (GstMixer * mixer, GstMixerOptions * opts); -/*** GOBJECT STUFF ************************************************************/ - -static GstAlsa *parent_class = NULL; - -GType -gst_alsa_mixer_get_type (void) +static void +gst_alsa_mixer_base_init (gpointer klass) { - static GType alsa_mixer_type = 0; - - if (!alsa_mixer_type) { - static const GTypeInfo alsa_mixer_info = { - sizeof (GstAlsaMixerClass), - NULL, - NULL, - gst_alsa_mixer_class_init, - NULL, - NULL, - sizeof (GstAlsaMixer), - 0, - (GInstanceInitFunc) gst_alsa_mixer_init, - }; - static const GInterfaceInfo alsa_iface_info = { - (GInterfaceInitFunc) gst_alsa_interface_init, - NULL, - NULL, - }; - static const GInterfaceInfo alsa_mixer_iface_info = { - (GInterfaceInitFunc) gst_alsa_mixer_interface_init, - NULL, - NULL, - }; - - alsa_mixer_type = - g_type_register_static (GST_TYPE_ALSA, "GstAlsaMixer", &alsa_mixer_info, - 0); - - g_type_add_interface_static (alsa_mixer_type, GST_TYPE_IMPLEMENTS_INTERFACE, - &alsa_iface_info); - g_type_add_interface_static (alsa_mixer_type, GST_TYPE_MIXER, - &alsa_mixer_iface_info); - } - - return alsa_mixer_type; + gst_element_class_set_details (GST_ELEMENT_CLASS (klass), + &gst_alsa_mixer_details); } static void -gst_alsa_mixer_class_init (gpointer g_class, gpointer class_data) +gst_alsa_mixer_class_init (GstAlsaMixerClass * klass) { - GObjectClass *object_class; GstElementClass *element_class; - GstAlsaClass *klass; - - klass = (GstAlsaClass *) g_class; - object_class = (GObjectClass *) g_class; - element_class = (GstElementClass *) g_class; - if (parent_class == NULL) - parent_class = g_type_class_ref (GST_TYPE_ALSA); + element_class = (GstElementClass *) klass; element_class->change_state = gst_alsa_mixer_change_state; - - gst_element_class_set_details (element_class, &gst_alsa_mixer_details); } static void @@ -136,10 +114,9 @@ static gboolean gst_alsa_mixer_open (GstAlsaMixer * mixer) { gint err, device; - GstAlsa *alsa = GST_ALSA (mixer); gchar *nocomma = NULL; - mixer->mixer_handle = NULL; + g_return_val_if_fail (mixer->mixer_handle == NULL, FALSE); /* open and initialize the mixer device */ err = snd_mixer_open (&mixer->mixer_handle, 0); @@ -148,6 +125,8 @@ gst_alsa_mixer_open (GstAlsaMixer * mixer) mixer->mixer_handle = NULL; return FALSE; } +#if 0 + GstAlsa *alsa = GST_ALSA (mixer); if (!strncmp (alsa->device, "hw:", 3)) nocomma = g_strdup (alsa->device); @@ -155,6 +134,9 @@ gst_alsa_mixer_open (GstAlsaMixer * mixer) nocomma = g_strdup (alsa->device + 4); else goto error; +#else + nocomma = g_strdup ("hw:0"); +#endif if (strchr (nocomma, ',')) strchr (nocomma, ',')[0] = '\0'; @@ -181,7 +163,7 @@ gst_alsa_mixer_open (GstAlsaMixer * mixer) gchar *name; if (!snd_card_get_name (device, &name)) - alsa->cardname = name; + mixer->cardname = name; } g_free (nocomma); @@ -198,49 +180,19 @@ error: static void gst_alsa_mixer_close (GstAlsaMixer * mixer) { - GstAlsa *alsa = GST_ALSA (mixer); - if (mixer->mixer_handle == NULL) return; - if (alsa->cardname) { - g_free (alsa->cardname); - alsa->cardname = NULL; + if (mixer->cardname) { + free (mixer->cardname); + mixer->cardname = NULL; } + snd_mixer_close (mixer->mixer_handle); mixer->mixer_handle = NULL; } static void -gst_alsa_interface_init (GstImplementsInterfaceClass * klass) -{ - klass->supported = gst_alsa_mixer_supported; -} - -static void -gst_alsa_mixer_interface_init (GstMixerClass * klass) -{ - GST_MIXER_TYPE (klass) = GST_MIXER_HARDWARE; - - /* set up the interface hooks */ - klass->list_tracks = gst_alsa_mixer_list_tracks; - klass->set_volume = gst_alsa_mixer_set_volume; - klass->get_volume = gst_alsa_mixer_get_volume; - klass->set_mute = gst_alsa_mixer_set_mute; - klass->set_record = gst_alsa_mixer_set_record; - klass->set_option = gst_alsa_mixer_set_option; - klass->get_option = gst_alsa_mixer_get_option; -} - -gboolean -gst_alsa_mixer_supported (GstImplementsInterface * iface, GType iface_type) -{ - g_assert (iface_type == GST_TYPE_MIXER); - - return (GST_ALSA_MIXER (iface)->mixer_handle != NULL); -} - -static void gst_alsa_mixer_build_list (GstAlsaMixer * mixer) { gint i, count; @@ -349,8 +301,6 @@ gst_alsa_mixer_free_list (GstAlsaMixer * mixer) mixer->tracklist = NULL; } -/*** GSTREAMER FUNCTIONS ******************************************************/ - static GstElementStateReturn gst_alsa_mixer_change_state (GstElement * element) { @@ -382,6 +332,29 @@ gst_alsa_mixer_change_state (GstElement * element) /*** INTERFACE IMPLEMENTATION *************************************************/ +static void +gst_alsa_mixer_interface_init (GstMixerClass * klass) +{ + GST_MIXER_TYPE (klass) = GST_MIXER_HARDWARE; + + /* set up the interface hooks */ + klass->list_tracks = gst_alsa_mixer_list_tracks; + klass->set_volume = gst_alsa_mixer_set_volume; + klass->get_volume = gst_alsa_mixer_get_volume; + klass->set_mute = gst_alsa_mixer_set_mute; + klass->set_record = gst_alsa_mixer_set_record; + klass->set_option = gst_alsa_mixer_set_option; + klass->get_option = gst_alsa_mixer_get_option; +} + +gboolean +gst_alsa_mixer_supported (GstAlsaMixer * object, GType iface_type) +{ + g_assert (iface_type == GST_TYPE_MIXER); + + return (object->mixer_handle != NULL); +} + static const GList * gst_alsa_mixer_list_tracks (GstMixer * mixer) { @@ -559,13 +532,16 @@ static const gchar * gst_alsa_mixer_get_option (GstMixer * mixer, GstMixerOptions * opts) { GstAlsaMixerOptions *alsa_opts = (GstAlsaMixerOptions *) opts; - gint idx = -1; + gint ret; + guint idx; g_return_val_if_fail (GST_ALSA_MIXER (mixer)->mixer_handle != NULL, NULL); gst_alsa_mixer_update (GST_ALSA_MIXER (mixer), NULL); - snd_mixer_selem_get_enum_item (alsa_opts->element, 0, &idx); - - return g_list_nth_data (opts->values, idx); + ret = snd_mixer_selem_get_enum_item (alsa_opts->element, 0, &idx); + if (ret == 0) + return g_list_nth_data (opts->values, idx); + else + return snd_strerror (ret); /* feeble attempt at error handling */ } diff --git a/ext/alsa/gstalsamixer.h b/ext/alsa/gstalsamixer.h index 1f98566..9c983df 100644 --- a/ext/alsa/gstalsamixer.h +++ b/ext/alsa/gstalsamixer.h @@ -16,37 +16,51 @@ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #ifndef __GST_ALSA_MIXER_H__ #define __GST_ALSA_MIXER_H__ + #include "gstalsa.h" +#include + #include "gstalsamixeroptions.h" #include "gstalsamixertrack.h" -#include + G_BEGIN_DECLS + #define GST_ALSA_MIXER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ALSA_MIXER,GstAlsaMixer)) #define GST_ALSA_MIXER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ALSA_MIXER,GstAlsaMixerClass)) #define GST_IS_ALSA_MIXER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ALSA_MIXER)) #define GST_IS_ALSA_MIXER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ALSA_MIXER)) #define GST_TYPE_ALSA_MIXER (gst_alsa_mixer_get_type()) + typedef struct _GstAlsaMixer GstAlsaMixer; typedef struct _GstAlsaMixerClass GstAlsaMixerClass; + struct _GstAlsaMixer { - GstAlsa parent; - GList * tracklist; /* list of available tracks */ - snd_mixer_t * mixer_handle; + GstElement parent; + + GList * tracklist; /* list of available tracks */ + + snd_mixer_t * mixer_handle; + + gchar * cardname; }; struct _GstAlsaMixerClass { - GstAlsaClass parent; + GstElementClass parent; }; + GType gst_alsa_mixer_get_type (void); + G_END_DECLS + #endif /* __GST_ALSA_MIXER_H__ */ diff --git a/ext/alsa/gstalsamixeroptions.h b/ext/alsa/gstalsamixeroptions.h index 0595a2c..af909df 100644 --- a/ext/alsa/gstalsamixeroptions.h +++ b/ext/alsa/gstalsamixeroptions.h @@ -16,14 +16,18 @@ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #ifndef __GST_ALSA_MIXER_OPTIONS_H__ #define __GST_ALSA_MIXER_OPTIONS_H__ + #include "gstalsa.h" #include + G_BEGIN_DECLS + #define GST_ALSA_MIXER_OPTIONS_TYPE (gst_alsa_mixer_options_get_type ()) #define GST_ALSA_MIXER_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ALSA_MIXER_OPTIONS,GstAlsaMixerOptions)) #define GST_ALSA_MIXER_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ALSA_MIXER_OPTIONS,GstAlsaMixerOptionsClass)) @@ -31,9 +35,11 @@ G_BEGIN_DECLS #define GST_IS_ALSA_MIXER_OPTIONS_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ALSA_MIXER_OPTIONS)) #define GST_TYPE_ALSA_MIXER_OPTIONS (gst_alsa_mixer_options_get_type()) + typedef struct _GstAlsaMixerOptions GstAlsaMixerOptions; typedef struct _GstAlsaMixerOptionsClass GstAlsaMixerOptionsClass; + struct _GstAlsaMixerOptions { GstMixerOptions parent; snd_mixer_elem_t *element; /* the ALSA mixer element for this track */ @@ -44,10 +50,13 @@ struct _GstAlsaMixerOptionsClass { GstMixerOptionsClass parent; }; + GType gst_alsa_mixer_options_get_type (void); GstMixerOptions *gst_alsa_mixer_options_new (snd_mixer_elem_t * element, gint track_num); + G_END_DECLS + #endif /* __GST_ALSA_MIXER_OPTIONS_H__ */ diff --git a/ext/alsa/gstalsamixertrack.h b/ext/alsa/gstalsamixertrack.h index 1bba405..ee5dff9 100644 --- a/ext/alsa/gstalsamixertrack.h +++ b/ext/alsa/gstalsamixertrack.h @@ -16,14 +16,18 @@ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #ifndef __GST_ALSA_MIXER_TRACK_H__ #define __GST_ALSA_MIXER_TRACK_H__ + #include "gstalsa.h" #include + G_BEGIN_DECLS + #define GST_ALSA_MIXER_TRACK_TYPE (gst_alsa_mixer_track_get_type ()) #define GST_ALSA_MIXER_TRACK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ALSA_MIXER_TRACK,GstAlsaMixerTrack)) #define GST_ALSA_MIXER_TRACK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ALSA_MIXER_TRACK,GstAlsaMixerTrackClass)) @@ -37,6 +41,7 @@ typedef struct _GstAlsaMixerTrackClass GstAlsaMixerTrackClass; #define GST_ALSA_MIXER_TRACK_CAPTURE (1<<0) #define GST_ALSA_MIXER_TRACK_PLAYBACK (1<<1) +#define GST_ALSA_MAX_CHANNELS 32 /* tracks can have up to 32 channels */ struct _GstAlsaMixerTrack { GstMixerTrack parent; snd_mixer_elem_t *element; /* the ALSA mixer element for this track */ @@ -57,6 +62,8 @@ GstMixerTrack * gst_alsa_mixer_track_new (snd_mixer_elem_t * element, gint flags, gint alsa_flags); + G_END_DECLS + #endif /* __GST_ALSA_MIXER_TRACK_H__ */ diff --git a/ext/alsa/gstalsaplugin.c b/ext/alsa/gstalsaplugin.c index 029ca8b..5fe1c36 100644 --- a/ext/alsa/gstalsaplugin.c +++ b/ext/alsa/gstalsaplugin.c @@ -56,11 +56,9 @@ plugin_init (GstPlugin * plugin) { int err; - /* - if (!gst_element_register (plugin, "alsamixer", GST_RANK_NONE, - GST_TYPE_ALSA_MIXER)) - return FALSE; - */ + if (!gst_element_register (plugin, "alsamixer", GST_RANK_NONE, + GST_TYPE_ALSA_MIXER)) + return FALSE; if (!gst_element_register (plugin, "alsasrc", GST_RANK_PRIMARY, GST_TYPE_ALSA_SRC)) return FALSE; -- 2.7.4