+++ /dev/null
-/*
- * Copyright (C) 2001 CodeFactory AB
- * Copyright (C) 2001 Thomas Nyberg <thomas@codefactory.se>
- * Copyright (C) 2001-2002 Andy Wingo <apwingo@eos.ncsu.edu>
- * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
- *
- * 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 <sys/time.h>
-
-#include "gst/gst-i18n-plugin.h"
-#include <gst/audio/multichannel.h>
-#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));
-}