Commit 730ff798 authored by Benjamin Otte's avatar Benjamin Otte
Browse files

fixing alsa step 2: complete rewrite of data transfer. The whole stuff is...

fixing alsa step 2: complete rewrite of data transfer. The whole stuff is clean enough to go from there now.

Original commit message from CVS:
fixing alsa step 2: complete rewrite of data transfer. The whole stuff is clean enough to go from there now.
License change to LGPL, since no copied code is left now.
Missing:
- alsasrc
- resetting format
- corner cases
- testsuite
parent 2534ded8
......@@ -3,7 +3,8 @@
This plugin was originally written by Thomas Nyberg <thomas@codefactory.se> for
ALSA 0.5.x. It was updated and changed quite a bit in September 2001 by Andy
Wingo for use with ALSA 0.9.x.
Wingo for use with ALSA 0.9.x. It was rewritten in great part again by Benjamin
Otte in January/February 2003.
1.0 Introduction
----------------
......@@ -44,21 +45,18 @@ My particular card has 4 channel out and 2 channel in. As you can see, each of
these devices are stereo. The alsasink and alsasrc elements correspond to the
default alsa devices.
3.0 License
-----------
ALSA 0.9.x isn't very well documented, so I had to rely on a number of different
sources for guidance. One of the main sources of inspiration was Paul Davis'
audioengine (available in quasimodo's cvs tree,
http://quasimodo.sourceforge.net/), from which I took a large amount of code.
Since that project is licensed under the GPL, it was only right that I license
the updated version of this plugin under the GPL as well. What, in my
understanding, this means for the end user is that you cannot use the ALSA
plugin with a non-GPL program. If in doubt, please refer to the COPYING file
located in this directory.
3.0 Where to look for other ALSA drivers
----------------------------------------
ALSA 0.9 isn't very well documented, so we had to rely on a number of different
sources for guidance. Sources of inspiration include JACK's alsa driver
(available in jack's cvs tree, http://jack.sourceforge.net/), the alsa2 driver
of mplayer (http://www.mplayerhq.hu) and the pcm.c example from the ALSA library
itself.
(http://www.alsa-project.org/alsa-doc/alsa-lib/_2test_2pcm_8c-example.html)
4.0 License
-----------
This plugin is licensed under the Lesser General Public License (LGPL). See the
file COPYING in the top source directory.
......@@ -2,6 +2,7 @@
* 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 General Public
......@@ -22,6 +23,33 @@
#include <sys/time.h>
#include "gstalsa.h"
/* error checking for standard alsa functions */
#ifdef G_HAVE_ISO_VARARGS
#define ERROR_CHECK(value, ...) G_STMT_START{ \
int err = (value); \
if (err < 0) { \
g_warning ( __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) { \
g_warning ( ## args, snd_strerror (err)); \
return FALSE; \
} \
}G_STMT_END
#else
#define ERROR_CHECK(value, args...) G_STMT_START{ \
int err = (value); \
if (err < 0) { \
g_warning (snd_strerror (err)); \
return FALSE; \
} \
}G_STMT_END
#endif
/* elementfactory information */
static GstElementDetails gst_alsa_sink_details = {
"Alsa Sink",
......@@ -30,8 +58,9 @@ static GstElementDetails gst_alsa_sink_details = {
"Output to a sound card via ALSA",
VERSION,
"Thomas Nyberg <thomas@codefactory.se>, "
"Andy Wingo <apwingo@eos.ncsu.edu>",
"(C) 2001 "
"Andy Wingo <apwingo@eos.ncsu.edu>"
"Benjamin Otte <in7y118@public.uni-hamburg.de>",
"(C) 2001-2003"
};
/* elementfactory information */
......@@ -42,8 +71,9 @@ static GstElementDetails gst_alsa_src_details = {
"Read from a sound card via ALSA",
VERSION,
"Thomas Nyberg <thomas@codefactory.se>, "
"Andy Wingo <apwingo@eos.ncsu.edu>",
"(C) 2001"
"Andy Wingo <apwingo@eos.ncsu.edu>"
"Benjamin Otte <in7y118@public.uni-hamburg.de>",
"(C) 2001-2003"
};
/* GObject functions */
......@@ -65,30 +95,20 @@ static GstCaps *gst_alsa_caps (snd_pcm_format_t format, gint rate, gint
static GstElementStateReturn gst_alsa_change_state (GstElement *element);
/* audio processing functions */
static void gst_alsa_loop (GstElement *element);
static void gst_alsa_sink_loop (GstElement *element);
static void gst_alsa_xrun_recovery (GstAlsa *this);
static gboolean gst_alsa_src_process (GstAlsa *this, snd_pcm_uframes_t frames);
static gboolean gst_alsa_sink_process (GstAlsa *this, snd_pcm_uframes_t frames);
static void gst_alsa_sink_check_event (GstAlsa *this, GstAlsaPad *pad);
static gboolean gst_alsa_sink_check_event (GstAlsa *this, gint pad_nr);
/* alsa setup / start / stop functions */
static gboolean gst_alsa_set_params (GstAlsa *this);
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 void gst_alsa_stop_audio (GstAlsa *this);
static void gst_alsa_close_audio (GstAlsa *this);
static gboolean gst_alsa_get_channel_addresses (GstAlsa *this);
static void gst_alsa_release_channel_addresses (GstAlsa *this);
/* #define _DEBUG */
#ifdef _DEBUG
#define DEBUG(text, args...) g_message(text, ##args)
#else
#define DEBUG(text, args...)
#endif
static gboolean gst_alsa_drain_audio (GstAlsa *this);
static gboolean gst_alsa_stop_audio (GstAlsa *this);
static gboolean gst_alsa_close_audio (GstAlsa *this);
/*** TYPE FUNCTIONS ***********************************************************/
......@@ -195,7 +215,8 @@ enum
ARG_CHANNELS,
ARG_RATE,
ARG_PERIODCOUNT,
ARG_PERIODFRAMES,
ARG_PERIODSIZE,
ARG_BUFFERSIZE,
ARG_DEBUG,
ARG_AUTORECOVER
};
......@@ -220,26 +241,27 @@ gst_alsa_class_init (GstAlsaClass *klass)
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DEVICE,
g_param_spec_string ("device", "Device", "Alsa device, as defined in an asoundrc",
"default", G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
/* The next 3 should only be settable on srcs, should it? */
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FORMAT,
g_param_spec_enum ("format", "Format", "PCM audio format",
GST_TYPE_ALSA_FORMAT, -1, G_PARAM_READWRITE));
GST_TYPE_ALSA_FORMAT, -1, G_PARAM_READABLE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_CHANNELS,
g_param_spec_int ("channels", "Channels", "Number of channels",
1, 64, 2, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
1, GST_ALSA_MAX_CHANNELS, 2, G_PARAM_READABLE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_RATE,
g_param_spec_int ("rate", "Rate", "Sample rate, in Hz",
8000, 192000, 44100, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
8000, 192000, 44100, G_PARAM_READABLE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PERIODCOUNT,
g_param_spec_int ("period-count", "Period count", "Number of hardware buffers to use",
2, 64, 2, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PERIODFRAMES,
g_param_spec_int ("period-frames", "Period frames", "Number of frames (samples on each channel) in one hardware period",
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PERIODSIZE,
g_param_spec_int ("period-size", "Period size", "Number of frames (samples on each channel) in one hardware period",
64, 8192, 8192, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DEBUG,
g_param_spec_boolean ("debug", "Debug", "Set to TRUE to output PCM state info",
FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BUFFERSIZE,
g_param_spec_int ("buffer-size", "Buffer size", "Number of frames the hardware buffer can hold",
128, 65536, 16384, G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_AUTORECOVER,
g_param_spec_boolean ("autorecover", "Automatic xrun recovery", "Set to TRUE to increase the period count on xruns",
g_param_spec_boolean ("autorecover", "Automatic xrun recovery", "When TRUE tries to reduce processor load on xruns",
TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
element_class->change_state = gst_alsa_change_state;
......@@ -249,40 +271,31 @@ gst_alsa_class_init (GstAlsaClass *klass)
static void
gst_alsa_init (GstAlsa *this)
{
gint i;
/* init values */
this->handle = NULL;
this->channels = 1;
GST_FLAG_SET (this, GST_ELEMENT_THREAD_SUGGESTED);
if (G_OBJECT_TYPE (this) == GST_TYPE_ALSA_SRC) {
this->stream = SND_PCM_STREAM_CAPTURE;
this->format = SND_PCM_FORMAT_S16; /* native endian */
this->process = gst_alsa_src_process;
this->pads = g_list_append (NULL, g_new0 (GstAlsaPad, 1));
GST_ALSA_PAD (this->pads)->pad =
gst_pad_new_from_template (gst_alsa_src_pad_factory (), "src");
this->pads[0].pad = gst_pad_new_from_template (gst_alsa_sink_pad_factory (), "src");
} else if (G_OBJECT_TYPE (this) == GST_TYPE_ALSA_SINK) {
this->stream = SND_PCM_STREAM_PLAYBACK;
this->format = SND_PCM_FORMAT_UNKNOWN; /* we don't know until caps are set */
this->process = gst_alsa_sink_process;
this->pads = g_list_append (NULL, g_new0 (GstAlsaPad, 1));
GST_ALSA_PAD (this->pads)->pad =
gst_pad_new_from_template (gst_alsa_sink_pad_factory (), "sink");
gst_element_set_loop_function (GST_ELEMENT (this), gst_alsa_sink_loop);
this->pads[0].pad = gst_pad_new_from_template (gst_alsa_sink_pad_factory (), "sink");
this->pads[0].bs = gst_bytestream_new (this->pads[0].pad);
}
GST_ALSA_PAD (this->pads)->channel = -1;
/* data is interleaved by default, because there's only one default pad */
this->data_interleaved = TRUE;
this->rate = 0;
this->channels = 0;
gst_element_add_pad (GST_ELEMENT (this), GST_ALSA_PAD (this->pads)->pad);
gst_element_add_pad (GST_ELEMENT (this), this->pads[0].pad);
for (i = 1; i < GST_ALSA_MAX_CHANNELS; i++) {
this->pads[i].pad = NULL;
}
gst_pad_set_link_function (GST_ALSA_PAD (this->pads)->pad, gst_alsa_link);
gst_element_set_loop_function (GST_ELEMENT (this), gst_alsa_loop);
gst_pad_set_link_function (this->pads[0].pad, gst_alsa_link);
}
static void
......@@ -290,6 +303,7 @@ 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) {
......@@ -298,7 +312,7 @@ gst_alsa_set_property (GObject *object, guint prop_id, const GValue *value,
g_free (this->device);
this->device = g_strdup (g_value_get_string (value));
break;
case ARG_FORMAT:
/* case ARG_FORMAT:
this->format = g_value_get_enum (value);
break;
case ARG_CHANNELS:
......@@ -306,18 +320,20 @@ gst_alsa_set_property (GObject *object, guint prop_id, const GValue *value,
break;
case ARG_RATE:
this->rate = g_value_get_int (value);
break;
break;*/
case ARG_PERIODCOUNT:
g_return_if_fail (!GST_FLAG_IS_SET (this, GST_ALSA_RUNNING));
this->period_count = g_value_get_int (value);
this->buffer_frames = this->period_count * this->period_frames;
break;
case ARG_PERIODFRAMES:
this->period_frames = g_value_get_int (value);
this->buffer_frames = this->period_count * this->period_frames;
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_DEBUG:
this->debug = g_value_get_boolean (value);
return;
case ARG_AUTORECOVER:
this->autorecover = g_value_get_boolean (value);
return;
......@@ -331,10 +347,7 @@ gst_alsa_set_property (GObject *object, guint prop_id, const GValue *value,
if (GST_FLAG_IS_SET (this, GST_ALSA_RUNNING)) {
gst_alsa_stop_audio (this);
gst_alsa_set_params (this);
gst_alsa_start_audio (this);
} else {
gst_alsa_set_params (this);
}
}
......@@ -350,7 +363,7 @@ gst_alsa_get_property (GObject *object, guint prop_id, GValue *value,
case ARG_DEVICE:
g_value_set_string (value, this->device);
break;
case ARG_FORMAT:
/* case ARG_FORMAT:
g_value_set_enum (value, this->format);
break;
case ARG_CHANNELS:
......@@ -359,14 +372,14 @@ gst_alsa_get_property (GObject *object, guint prop_id, GValue *value,
case ARG_RATE:
g_value_set_int (value, this->rate);
break;
case ARG_PERIODCOUNT:
*/ case ARG_PERIODCOUNT:
g_value_set_int (value, this->period_count);
break;
case ARG_PERIODFRAMES:
g_value_set_int (value, this->period_frames);
case ARG_PERIODSIZE:
g_value_set_int (value, this->period_size);
break;
case ARG_DEBUG:
g_value_set_boolean (value, this->debug);
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);
......@@ -437,80 +450,49 @@ gst_alsa_request_new_pad (GstElement *element, GstPadTemplate *templ,
const gchar *name)
{
GstAlsa *this;
GstAlsaPad *pad;
gint channel;
gchar *newname;
GList *l;
gint channel = 0;
g_return_val_if_fail ((this = GST_ALSA (element)), NULL);
g_return_val_if_fail (GST_FLAG_IS_SET (element, GST_ALSA_RUNNING), NULL);
/* you can't request a pad if the non-request pad is connected */
g_return_val_if_fail (this->data_interleaved == FALSE ||
this->pads == NULL ||
GST_ALSA_PAD (this->pads) == NULL ||
GST_ALSA_PAD (this->pads)->pad == NULL ||
GST_PAD_PEER (GST_ALSA_PAD (this->pads)->pad) == NULL,
NULL);
/* you can't request a pad if the non-request pad already has more than 1 channel */
g_return_val_if_fail (this->channels > GST_ELEMENT(this)->numpads, NULL);
if (name) {
/* locate the channel number in the requested pad name. to do so look at
where the % (which begins the %d) is in the template name. */
channel = atoi (name + (strchr (templ->name_template, '%') -
templ->name_template));
channel = (gint) strtol (name + (strchr (templ->name_template, '%') -
templ->name_template), NULL, 0);
if (channel < 1 || channel >= GST_ALSA_MAX_CHANNELS) {
g_warning ("invalid channel requested. (%d)", channel);
return NULL;
}
}
/* make sure the requested channel is free. */
l = this->pads;
while (l) {
if (GST_ALSA_PAD (l)->channel == channel) {
if (channel > 0 || this->pads[channel].pad != NULL) {
g_warning ("requested channel %d already in use.", channel);
return NULL;
}
l = l->next;
}
newname = g_strdup (name);
} else {
channel = 0;
/* if the user doesn't care which channel, find the lowest channel number
that's free. */
l = this->pads;
while (l) {
if (GST_ALSA_PAD (l)->channel >= channel)
channel = GST_ALSA_PAD (l)->channel + 1;
l = l->next;
if (channel == 0) {
for (channel = 1; channel < GST_ALSA_MAX_CHANNELS; channel++) {
if (this->pads[channel].pad != NULL)
goto found_channel;
}
newname = g_strdup_printf (templ->name_template, channel);
}
/* set up a new GstAlsaPad struct to hold this channel's info. */
pad = g_new0 (GstAlsaPad, 1);
pad->channel = channel;
pad->pad = gst_pad_new_from_template (templ, newname);
gst_element_add_pad (GST_ELEMENT (this), pad->pad);
gst_pad_set_link_function (pad->pad, gst_alsa_link);
/* if the only pad is the default (nonrequest) pad, then remove the current
pad (we know it's free from the above g_return_val_if_fail) */
if (this->data_interleaved && this->pads) {
gst_element_remove_pad (GST_ELEMENT (this), GST_ALSA_PAD (this->pads)->pad);
g_free (GST_ALSA_PAD (this->pads));
g_list_free (this->pads);
this->pads = NULL;
return NULL;
}
this->pads = g_list_append (this->pads, pad);
found_channel:
this->pads[channel].pad = gst_pad_new_from_template (templ, name);
gst_pad_set_link_function (this->pads[channel].pad, gst_alsa_link);
gst_element_add_pad (GST_ELEMENT (this), this->pads[channel].pad);
if (G_OBJECT_TYPE (this) == GST_TYPE_ALSA_SINK)
this->pads[channel].bs = gst_bytestream_new (this->pads[channel].pad);
/* all request pads are mono (non-interleaved). FIXME: allow interleaved
access (for hw:N,M access on consumer hardware) */
if (this->data_interleaved) {
this->channels = pad->channel + 1;
this->data_interleaved = FALSE;
} else {
this->channels = MAX (this->channels, pad->channel + 1);
}
return pad->pad;
return this->pads[channel].pad;
}
/* gets the matching alsa format or SND_PCM_FORMAT_UNKNOWN if none matches */
......@@ -615,7 +597,7 @@ static inline void add_channels (GstProps *props, gint rate, gint channels) {
gst_props_add_entry (props, gst_props_entry_new ("rate", GST_PROPS_INT (rate)));
}
if (channels < 0) {
gst_props_add_entry (props, gst_props_entry_new ("channels", GST_PROPS_INT_RANGE (1, 64)));
gst_props_add_entry (props, gst_props_entry_new ("channels", GST_PROPS_INT_RANGE (1, GST_ALSA_MAX_CHANNELS)));
} else {
gst_props_add_entry (props, gst_props_entry_new ("channels", GST_PROPS_INT (channels)));
}
......@@ -624,7 +606,7 @@ static inline void add_channels (GstProps *props, gint rate, gint channels) {
* 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 channel number
* @channels: all allowed values for channels if < 0, else desired channels
*/
static GstCaps *
gst_alsa_caps (snd_pcm_format_t format, gint rate, gint channels)
......@@ -657,14 +639,13 @@ gst_alsa_caps (snd_pcm_format_t format, gint rate, gint channels)
return ret_caps;
}
/* Negotiates the caps, "borrowed" from gstosssink.c */
/* Negotiates the caps */
GstPadLinkReturn
gst_alsa_link (GstPad *pad, GstCaps *caps)
{
GstAlsa *this;
snd_pcm_format_t format;
gint rate, channels;
gboolean need_mmap;
g_return_val_if_fail (caps != NULL, GST_PAD_LINK_REFUSED);
g_return_val_if_fail (pad != NULL, GST_PAD_LINK_REFUSED);
......@@ -676,9 +657,10 @@ gst_alsa_link (GstPad *pad, GstCaps *caps)
if (!gst_alsa_open_audio (this))
return GST_PAD_LINK_REFUSED;
/* FIXME: allow changing the format here */
/* FIXME: allow changing the format here, even by retrying caps on other pads */
format = gst_alsa_get_format (caps);
DEBUG ("found format %s\n", snd_pcm_format_name (format));
GST_DEBUG (GST_CAT_CAPS, "found format %s\n", snd_pcm_format_name (format));
if (this->format != SND_PCM_FORMAT_UNKNOWN && this->format != format)
return GST_PAD_LINK_REFUSED;
if (!gst_caps_get (caps, "rate", &rate,
......@@ -698,20 +680,13 @@ gst_alsa_link (GstPad *pad, GstCaps *caps)
this->format = format;
this->rate = rate;
need_mmap = this->mmap_open;
/* sync the params */
if (GST_FLAG_IS_SET (this, GST_ALSA_RUNNING)) gst_alsa_stop_audio (this);
if (GST_FLAG_IS_SET (this, GST_ALSA_OPEN)) gst_alsa_close_audio (this);
/* FIXME send out another caps if nego fails */
if (!gst_alsa_open_audio (this)) return GST_PAD_LINK_REFUSED;
if (!gst_alsa_start_audio (this)) return GST_PAD_LINK_REFUSED;
if (need_mmap && !gst_alsa_get_channel_addresses (this))
return GST_PAD_LINK_REFUSED;
return GST_PAD_LINK_OK;
}
......@@ -722,45 +697,36 @@ static GstElementStateReturn
gst_alsa_change_state (GstElement *element)
{
GstAlsa *this;
GList *l;
g_return_val_if_fail (element != NULL, FALSE);
this = GST_ALSA (element);
switch (GST_STATE_PENDING (element)) {
case GST_STATE_NULL:
if (GST_FLAG_IS_SET (element, GST_ALSA_RUNNING)) gst_alsa_stop_audio (this);
if (GST_FLAG_IS_SET (element, GST_ALSA_OPEN)) gst_alsa_close_audio (this);
/* clear out bytestreams as well. */
l = this->pads;
while (l) {
if (GST_ALSA_PAD (l)->bs)
gst_bytestream_destroy (GST_ALSA_PAD (l)->bs);
l = l->next;
}
switch (GST_STATE_TRANSITION (element)) {
case GST_STATE_NULL_TO_READY:
if (GST_FLAG_IS_SET (element, GST_ALSA_OPEN) == FALSE)
if (!gst_alsa_open_audio (this))
return GST_STATE_FAILURE;
break;
case GST_STATE_READY:
case GST_STATE_READY_TO_PAUSED:
break;
case GST_STATE_PAUSED:
case GST_STATE_PAUSED_TO_PLAYING:
if (GST_FLAG_IS_SET (element, GST_ALSA_OPEN) == FALSE)
if (!gst_alsa_open_audio (this))
return GST_STATE_FAILURE;
case GST_STATE_PLAYING_TO_PAUSED:
if (GST_FLAG_IS_SET (element, GST_ALSA_RUNNING))
gst_alsa_stop_audio (this);
gst_alsa_drain_audio (this);
break;
case GST_STATE_PLAYING:
if (GST_FLAG_IS_SET (element, GST_ALSA_RUNNING) == FALSE)
if (!gst_alsa_start_audio (this))
return GST_STATE_FAILURE;
case GST_STATE_PAUSED_TO_READY:
break;
case GST_STATE_READY_TO_NULL:
if (GST_FLAG_IS_SET (element, GST_ALSA_OPEN))
gst_alsa_close_audio (this);
break;
default:
g_assert_not_reached();
}
if (GST_ELEMENT_CLASS (parent_class)->change_state)
......@@ -771,69 +737,129 @@ gst_alsa_change_state (GstElement *element)
/*** AUDIO PROCESSING *********************************************************/
/* shamelessly stolen from pbd's audioengine and jack alsa_driver. thanks,
paul! */
static void
gst_alsa_loop (GstElement *element)
gst_alsa_sink_loop (GstElement *element)
{
snd_pcm_sframes_t avail;
gint i;
gint bytes, num_bytes; /* per channel */
GstAlsa *this = GST_ALSA (element);
g_return_if_fail (this != NULL);
while (1) {
if (snd_pcm_wait (this->handle, 1000) < 0) {
if (errno == EINTR) {
/* this happens mostly when run
* under gdb, or when exiting due to a signal */
g_print ("EINTR\n");
if (gst_element_interrupt (element))
break;
else
continue;
}
g_warning ("error waiting for alsa pcm: (%d: %s)", errno, strerror (errno));
/* caps nego: fetch 1 byte from every pad */
if (this->format == SND_PCM_FORMAT_UNKNOWN) {
GST_DEBUG (GST_CAT_NEGOTIATION, "starting caps negotiation");
for (i = 0; i < element->numpads; i++) {
g_assert (this->pads[i].pad != NULL);
do {
num_bytes = gst_bytestream_peek_bytes (this->pads[i].bs, &this->pads[i].data, 1);
} while (num_bytes == 0 && gst_alsa_sink_check_event (this, i));
if (num_bytes == 0)
return;
}
if (this->format == SND_PCM_FORMAT_UNKNOWN) {
gst_element_error (GST_ELEMENT (this), "alsasink: No caps available");
}
}
this->avail = snd_pcm_avail_update (this->handle);
DEBUG ("snd_pcm_avail_update() = %d", (int) this->avail);
while (1) {
if (this->avail < 0) {
if (this->avail == -EPIPE) {
avail = snd_pcm_avail_update (this->handle);
if (avail < 0) {
if (avail == -EPIPE) {
gst_alsa_xrun_recovery (this);
this->avail = 0;
continue;
} else {
g_warning ("unknown ALSA avail_update return value (%d)", (int) this->avail);
g_warning ("unknown ALSA avail_update return value (%d)", (int) avail);
return;
}
}
/* round down to nearest period_frames avail */
this->avail -= this->avail % this->period_frames;
if (avail > 0) {
/* check how many bytes we still have in all our bytestreams */
bytes = avail * ( snd_pcm_format_physical_width (this->format) / 8 ) * (element->numpads == 1 ? this->channels : 1);
for (i = 0; i < element->numpads; i++) {
g_assert (this->pads[i].pad != NULL);