#include "gstalsa.h"
/* elementfactory information */
-static GstElementDetails gst_alsa_sink_details = {
+static GstElementDetails gst_alsa_sink_details = {
"Alsa Sink",
"Sink/Audio",
"LGPL",
};
/* elementfactory information */
-static GstElementDetails gst_alsa_src_details = {
+static GstElementDetails gst_alsa_src_details = {
"Alsa Src",
"Source/Audio",
"LGPL",
static GstPadTemplate *gst_alsa_sink_pad_factory();
static GstPadTemplate *gst_alsa_sink_request_pad_factory();
-static GstPad* gst_alsa_request_new_pad (GstElement *element, GstPadTemplate *templ, const
- gchar *name);
+static GstPad* gst_alsa_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar *name);
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);
static GstElementStateReturn gst_alsa_change_state(GstElement *element);
-static GstPadLinkReturn gst_alsa_connect(GstPad *pad, GstCaps *caps);
+static GstPadLinkReturn gst_alsa_link(GstPad *pad, GstCaps *caps);
static GstCaps* gst_alsa_caps (GstAlsa *this);
static void gst_alsa_release_channel_addresses (GstAlsa *this);
static void gst_alsa_sink_silence_on_channel (GstAlsa *this, guint32 chn, guint32 nframes);
-static void memset_interleave (char *dst, char val, unsigned int bytes,
- unsigned int unit_bytes,
- unsigned int skip_bytes);
+
+static void memset_interleave (char *dst, char val, unsigned int bytes, unsigned int unit_bytes, unsigned int skip_bytes);
/* #define _DEBUG */
#ifdef _DEBUG
#define GST_TYPE_ALSA_FORMAT (gst_alsa_format_get_type())
static GType
-gst_alsa_format_get_type (void)
+gst_alsa_format_get_type (void)
{
static GType type = 0;
static GEnumValue *values = NULL;
if (values == NULL) {
/* the three: for -1, 0, and the terminating NULL */
values = g_new0 (GEnumValue, len);
-
+
for (i=0; i<len-1; i++) {
values[i].value = i-1; /* UNKNOWN is -1 */
values[i].value_name = g_strdup_printf ("%d", i-1);
}
GType
-gst_alsa_get_type (void)
+gst_alsa_get_type (void)
{
static GType alsa_type = 0;
-
+
if (!alsa_type) {
static const GTypeInfo alsa_info = {
sizeof(GstAlsaClass),
}
GType
-gst_alsa_sink_get_type (void)
+gst_alsa_sink_get_type (void)
{
static GType alsa_type = 0;
}
GType
-gst_alsa_src_get_type (void)
+gst_alsa_src_get_type (void)
{
static GType alsa_type = 0;
-
+
if (!alsa_type) {
static const GTypeInfo alsa_info = {
sizeof(GstAlsaClass),
gst_alsa_src_pad_factory(void)
{
static GstPadTemplate *template = NULL;
-
+
if (!template)
- template = gst_pad_template_new("src", GST_PAD_SRC, GST_PAD_SOMETIMES,
+ template = gst_pad_template_new("src", GST_PAD_SRC, GST_PAD_SOMETIMES,
gst_caps_new("src", "audio/raw", NULL),
NULL);
-
+
return template;
}
gst_alsa_src_request_pad_factory(void)
{
static GstPadTemplate *template = NULL;
-
+
if (!template)
- template = gst_pad_template_new("src%d", GST_PAD_SRC, GST_PAD_REQUEST,
- gst_caps_new("src-request", "audio/raw",
- gst_props_new("channels", GST_PROPS_INT(1), NULL)),
+ template =
+ gst_pad_template_new("src%d", GST_PAD_SRC, GST_PAD_REQUEST,
+ gst_caps_new("src-request", "audio/raw",
+ gst_props_new("channels",
+ GST_PROPS_INT(1),
+ NULL)),
NULL);
-
+
return template;
}
gst_alsa_sink_pad_factory(void)
{
static GstPadTemplate *template = NULL;
-
+
if (!template)
- template = gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_SOMETIMES,
+ template = gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_SOMETIMES,
gst_caps_new("sink", "audio/raw", NULL),
NULL);
-
+
return template;
}
gst_alsa_sink_request_pad_factory(void)
{
static GstPadTemplate *template = NULL;
-
+
if (!template)
- template = gst_pad_template_new("sink%d", GST_PAD_SINK, GST_PAD_REQUEST,
- gst_caps_new("sink-request", "audio/raw",
- gst_props_new("channels", GST_PROPS_INT(1), NULL)),
- NULL);
-
+ template =
+ gst_pad_template_new("sink%d", GST_PAD_SINK, GST_PAD_REQUEST,
+ gst_caps_new("sink-request", "audio/raw",
+ gst_props_new("channels",
+ GST_PROPS_INT(1),
+ NULL)),
+ NULL);
+
return template;
}
{
GObjectClass *object_class;
GstElementClass *element_class;
-
+
object_class = (GObjectClass *)klass;
element_class = (GstElementClass *)klass;
-
+
if (parent_class == NULL)
parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
-
+
object_class->get_property = gst_alsa_get_property;
object_class->set_property = gst_alsa_set_property;
-
+
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_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));
-
+
element_class->change_state = gst_alsa_change_state;
-
+
element_class->request_new_pad = gst_alsa_request_new_pad;
}
{
/* init values */
this->handle = NULL;
-
+
GST_FLAG_SET(this, GST_ELEMENT_THREAD_SUGGESTED);
-
+
if (G_OBJECT_TYPE(this) == GST_TYPE_ALSA_SRC) {
this->stream = SND_PCM_STREAM_CAPTURE;
this->pads = g_list_append(NULL, g_new0(GstAlsaPad, 1));
this->format = SND_PCM_FORMAT_UNKNOWN; /* we don't know until caps are
* set */
}
-
+
GST_ALSA_PAD(this->pads)->channel = -1;
-
+
this->data_interleaved = TRUE;
-
+
gst_element_add_pad(GST_ELEMENT(this), GST_ALSA_PAD(this->pads)->pad);
-
- gst_pad_set_link_function(GST_ALSA_PAD(this->pads)->pad, gst_alsa_connect);
+
+ 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);
}
gchar *newname;
GList *l;
GstAlsaPad *pad;
-
+
g_return_val_if_fail ((this = GST_ALSA(element)), 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 ||
+ 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,
+ GST_PAD_PEER(GST_ALSA_PAD(this->pads)->pad) == NULL,
NULL);
-
+
if (name) {
channel = atoi (name + (strchr (templ->name_template, '%') - templ->name_template));
-
+
l = this->pads;
while (l) {
if (GST_ALSA_PAD(l)->channel == channel) {
}
newname = g_strdup_printf (templ->name_template, channel);
}
-
+
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_connect);
-
+ gst_pad_set_link_function(pad->pad, gst_alsa_link);
+
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;
}
-
+
this->pads = g_list_append(this->pads, pad);
-
+
/* FIXME: allow interleaved access (for hw:N,M access on consumer hardware) */
if (this->data_interleaved) {
} else {
this->channels = MAX(this->channels, pad->channel + 1);
}
-
+
return pad->pad;
}
gst_alsa_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
GstAlsa *this;
-
+
this = (GstAlsa *)object;
switch (prop_id) {
case ARG_DEVICE:
GST_DEBUG(0, "Unknown arg");
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_set_params(this);
{
GstAlsa *this;
guint chn;
-
+ 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((GstAlsa *)element);
+ gst_alsa_stop_audio(this);
if (GST_FLAG_IS_SET(element, GST_ALSA_OPEN))
- gst_alsa_close_audio((GstAlsa *)element);
- /* FIXME: clean up bytestreams, etc */
+ gst_alsa_close_audio(this);
+
+ l = this->pads;
+ while(l) {
+ if (GST_ALSA_PAD(l)->bs)
+ gst_bytestream_destroy(GST_ALSA_PAD(l)->bs);
+ l = l->next;
+ }
+
break;
-
+
case GST_STATE_READY:
break;
-
+
case GST_STATE_PAUSED:
if (GST_FLAG_IS_SET(element, GST_ALSA_OPEN) == FALSE)
- if (gst_alsa_open_audio((GstAlsa *)element) == FALSE)
+ if (gst_alsa_open_audio(this) == FALSE)
return GST_STATE_FAILURE;
if (GST_FLAG_IS_SET(element, GST_ALSA_RUNNING)) {
if (this->stream == SND_PCM_STREAM_PLAYBACK) {
gst_alsa_stop_audio((GstAlsa *)element);
}
break;
-
+
case GST_STATE_PLAYING:
if (GST_FLAG_IS_SET(element, GST_ALSA_RUNNING) == FALSE)
- if (gst_alsa_start_audio((GstAlsa *)element) == FALSE)
+ if (gst_alsa_start_audio(this) == FALSE)
return GST_STATE_FAILURE;
break;
}
-
+
if (GST_ELEMENT_CLASS(parent_class)->change_state)
return GST_ELEMENT_CLASS(parent_class)->change_state(element);
if (!gst_caps_get_string (caps, "format", &format_name))
return FALSE;
-
+
if (format_name == NULL) {
return FALSE;
} else if (strcmp(format_name, "int")==0) {
"signed", &sign,
NULL))
return FALSE;
-
+
if (law == 0) {
if (width == 8) {
if (sign == TRUE) {
if (!gst_caps_get_string (caps, "layout", &layout))
return FALSE;
-
+
if (strcmp(layout, "gfloat")==0) {
format = SND_PCM_FORMAT_FLOAT;
} else {
} else {
return FALSE;
}
-
+
this->format = format;
if (!gst_caps_get (caps,
"rate", &this->rate,
this->channels = channels;
else if (channels != 1)
return FALSE;
-
+
return TRUE;
}
gint law, endianness, width, depth;
gboolean sign;
GstProps *props;
-
+
g_return_val_if_fail (this != NULL && this->handle != NULL, NULL);
-
+
if (this->format == SND_PCM_FORMAT_FLOAT) {
props = gst_props_new ("format", GST_PROPS_STRING ("float"),
"layout", GST_PROPS_STRING ("gfloat"),
"depth", GST_PROPS_INT (depth),
NULL);
}
-
+
return gst_caps_new ("alsasrc", "audio/raw", props);
}
* Negotiates the caps, "borrowed" from gstosssink.c
*/
GstPadLinkReturn
-gst_alsa_connect(GstPad *pad, GstCaps *caps)
+gst_alsa_link(GstPad *pad, GstCaps *caps)
{
GstAlsa *this;
gboolean need_mmap;
g_return_val_if_fail (pad != NULL, GST_PAD_LINK_REFUSED);
this = GST_ALSA(gst_pad_get_parent(pad));
-
+
if (GST_CAPS_IS_FIXED (caps)) {
if (this->handle == NULL)
if (!gst_alsa_open_audio(this))
return GST_PAD_LINK_REFUSED;
-
+
if (gst_alsa_parse_caps(this, caps)) {
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;
}
-
+
return GST_PAD_LINK_REFUSED;
}
-
+
return GST_PAD_LINK_DELAYED;
}
-/* shamelessly stolen from pbd's audioengine and jack alsa_driver. thanks, paul! */
+/* shamelessly stolen from pbd's audioengine and jack alsa_driver. thanks,
+ paul! */
static void
gst_alsa_loop (GstElement *element)
{
- struct pollfd pfd;
- gboolean xrun_detected;
guint32 i;
GstAlsa *this = GST_ALSA(element);
-
+
g_return_if_fail(this != NULL);
-
- snd_pcm_poll_descriptors (this->handle, &pfd, 1);
-
- if (this->stream == SND_PCM_STREAM_PLAYBACK) {
- pfd.events = POLLOUT | POLLERR;
- } else {
- pfd.events = POLLIN | POLLERR;
- }
-
+
do {
- xrun_detected = FALSE;
-
- if (poll (&pfd, 1, 1000) < 0) {
+ 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");
- continue;
+ if (gst_element_interrupt (element))
+ break;
+ else
+ continue;
}
-
- g_warning("poll call failed (%s)", strerror(errno));
+
+ g_warning("error waiting for alsa pcm: (%d: %s)", errno, strerror(errno));
return;
}
-
- if (pfd.revents & POLLERR) {
- xrun_detected = TRUE;
- }
-
- if (pfd.revents == 0) {
- g_print ("poll on alsa %s device \"%s\" timed out\n",
- this->stream==SND_PCM_STREAM_CAPTURE ? "capture" : "playback",
- this->device);
- /* timed out, such as when the device is paused */
- continue;
- }
-
+
this->avail = snd_pcm_avail_update (this->handle);
DEBUG ("snd_pcm_avail_update() = %d", (int)this->avail);
-
+
if (this->avail < 0) {
if (this->avail == -EPIPE) {
- xrun_detected = TRUE;
+ gst_alsa_xrun_recovery (this);
+ this->avail = 0;
} else {
g_warning("unknown ALSA avail_update return value (%d)",
(int)this->avail);
return;
}
}
-
- if (xrun_detected) {
- gst_alsa_xrun_recovery (this);
- this->avail = 0;
- }
/* round down to nearest period_frames avail */
this->avail -= this->avail % this->period_frames;
g_error("could not get channels");
return;
}
-
+
if (this->mute && this->stream == SND_PCM_STREAM_PLAYBACK) {
for (i = 0; i < this->channels; i++) {
if (this->mute & (1<<i)) {
}
}
}
-
+
if (!this->process(this, this->avail)) {
g_warning("alsa: something happened while processing audio");
return;
}
-
+
/* we could have released the mmap regions on a state change */
if (this->mmap_open)
gst_alsa_release_channel_addresses(this);
}
- gst_element_yield (element);
+
+ if (gst_element_interrupt (element))
+ break;
+
} while (TRUE);
}
GstAlsaPad *pad = NULL;
GstCaps *caps;
gint unit;
-/* gint i=0; */
-
+
static gboolean caps_set = FALSE;
-
+
if (!caps_set) {
/* let's get on the caps-setting merry-go-round! */
caps = gst_alsa_caps(this);
l = this->pads;
while (l) {
if (gst_pad_try_set_caps (GST_ALSA_PAD(l)->pad, caps) <= 0) {
- g_print ("DANGER WILL ROBINSON!\n");
+ g_print ("setting caps (%p) in source (%p) failed\n", caps, this);
sleep(1);
return FALSE;
}
-
+
l = l->next;
}
caps_set = TRUE;
}
-
+
unit = this->sample_bytes * (this->data_interleaved ? this->channels : 1);
-
+
while (frames) {
-/* g_print ("(%d) frames to process: %d\n", i++, frames); */
l = this->pads;
while (l) {
pad = GST_ALSA_PAD(l);
-
+
if (!pad->buf) {
pad->buf = g_malloc(this->period_frames * unit);
/* g_print ("created buffer %p of size %d\n", pad->buf, this->period_frames * unit); */
}
/*
g_print ("pad->buf = %p, offset = %d\n", pad->buf, pad->offset);
- g_print ("about to memcpy(%p, %p, %d)\n",
+ g_print ("about to memcpy(%p, %p, %d)\n",
pad->buf + pad->offset * unit,
- pad->access_addr,
+ pad->access_addr,
MIN(frames, this->period_frames - pad->offset) * unit);
*/
- memcpy(pad->buf + pad->offset * unit,
- pad->access_addr,
+ memcpy(pad->buf + pad->offset * unit, pad->access_addr,
MIN(frames, this->period_frames - pad->offset) * unit);
-
+
pad->offset += MIN(frames, this->period_frames - pad->offset);
-
+
if (pad->offset >= this->period_frames) {
g_assert(pad->offset <= this->period_frames);
buf = gst_buffer_new();
frames -= MIN(frames, this->period_frames - pad->offset); /* shouldn't */
/* matter which pad, in theory (tm) */
}
-
+
return TRUE;
}
gst_alsa_sink_process (GstAlsa *this, snd_pcm_uframes_t frames)
{
guint8 *peeked;
- guint32 len, avail;
+ guint32 len, avail, num_peeked;
GstEvent *event = NULL;
+ GstAlsaPad *alsa_pad;
GList *l;
-
+
/* this is necessary because the sample_bytes will change, probably, when
- * caps are set, which will occur after the first bytestream_peek. we
- * underestimate the amount of data we will need by peeking 'frames' only.
- * */
-
- /* FIXME: if 0 < peek_bytes < len, play the peek_bytes */
+ caps are set, which will occur after the first bytestream_peek. we
+ underestimate the amount of data we will need by peeking 1 byte only. */
if (!this->sample_bytes) {
- if (!GST_ALSA_PAD(this->pads)->bs)
- GST_ALSA_PAD(this->pads)->bs = gst_bytestream_new(GST_ALSA_PAD(this->pads)->pad);
-
- if (gst_bytestream_peek_bytes (GST_ALSA_PAD (this->pads)->bs, &peeked, frames) != frames) {
- g_warning("could not make initial pull of %d bytes on pad %s:%s", (int)frames, GST_DEBUG_PAD_NAME(GST_ALSA_PAD(this->pads)->pad));
+ alsa_pad = GST_ALSA_PAD(this->pads);
+
+ if (! alsa_pad->bs)
+ alsa_pad->bs = gst_bytestream_new(alsa_pad->pad);
+
+ num_peeked = gst_bytestream_peek_bytes (alsa_pad->bs, &peeked, frames);
+ if (num_peeked < frames) {
+ g_warning("could not make initial pull of %d bytes on pad %s:%s",
+ (int)frames,
+ GST_DEBUG_PAD_NAME(alsa_pad->pad));
gst_element_set_eos (GST_ELEMENT(this));
return FALSE;
}
-
+
if (!this->sample_bytes) {
g_critical ("alsa plugin requires a pipeline that can adequately set caps.");
return FALSE;
}
}
-
+
len = frames * this->channels * this->sample_bytes;
-
+
l = this->pads;
while (l) {
- if (!GST_ALSA_PAD(this->pads)->bs)
- GST_ALSA_PAD(this->pads)->bs = gst_bytestream_new(GST_ALSA_PAD(this->pads)->pad);
-
- if (gst_bytestream_peek_bytes(GST_ALSA_PAD(this->pads)->bs, &peeked, len) != len) {
- gst_bytestream_get_status(GST_ALSA_PAD(this->pads)->bs, &avail, &event);
+ alsa_pad = GST_ALSA_PAD(l);
+
+ if (! alsa_pad->bs)
+ alsa_pad->bs = gst_bytestream_new(alsa_pad->pad);
+
+ num_peeked = gst_bytestream_peek_bytes(alsa_pad->bs, &peeked, len);
+ if (num_peeked == 0) {
+ gst_bytestream_get_status(alsa_pad->bs, &avail, &event);
if (event) {
g_warning("got an event on alsasink");
if (GST_EVENT_TYPE(event) == GST_EVENT_EOS) {
/* really, we should just cut this pad out of the graph. let
- * me know when this is needed ;)
- * also, for sample accuracy etc, we should play avail
- * bytes, but hey. */
+ me know when this is needed ;) */
+
+ num_peeked = gst_bytestream_peek_bytes(alsa_pad->bs, &peeked, avail);
+ if (num_peeked && peeked)
+ memcpy(alsa_pad->access_addr, peeked, avail);
+
gst_element_set_eos(GST_ELEMENT(this));
gst_event_unref(event);
return TRUE;
g_assert_not_reached();
}
}
-
- memcpy(GST_ALSA_PAD(this->pads)->access_addr, peeked, len);
- gst_bytestream_flush(GST_ALSA_PAD(this->pads)->bs, len);
-
- l=l->next;
+
+ if (num_peeked && peeked && alsa_pad->access_addr) {
+ memcpy(alsa_pad->access_addr, peeked, num_peeked);
+ gst_bytestream_flush(alsa_pad->bs, num_peeked);
+ } else {
+ g_warning ("error while writing %u bytes (peeked into %p) to %p",
+ num_peeked, peeked, alsa_pad->access_addr);
+ }
+
+ l = l->next;
}
-
+
return TRUE;
}
snd_pcm_status_alloca(&status);
- if (this->stream == SND_PCM_STREAM_CAPTURE) {
- if ((res = snd_pcm_status(this->handle, status)) < 0) {
- g_warning ("status error: %s", snd_strerror(res));
- }
- } else {
- if ((res = snd_pcm_status(this->handle, status)) < 0) {
- g_warning ("status error: %s", snd_strerror(res));
- }
+ if ((res = snd_pcm_status(this->handle, status)) < 0) {
+ g_warning ("status error: %s", snd_strerror(res));
}
-
+
if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) {
struct timeval now, diff, tstamp;
gettimeofday(&now, 0);
timersub(&now, &tstamp, &diff);
g_warning("alsa: xrun of at least %.3f msecs", diff.tv_sec * 1000 + diff.tv_usec / 1000.0);
}
-
+
gst_alsa_stop_audio (this);
gst_alsa_start_audio (this);
-}
+}
/* taken more or less from pbd's audioengine code */
static gboolean
snd_pcm_hw_params_t *hw_param;
snd_pcm_access_mask_t *mask;
gint ret;
-
+
g_return_val_if_fail(this != NULL, FALSE);
g_return_val_if_fail(this->handle != NULL, FALSE);
-
- g_print("Preparing channel: %s %dHz, %d channels\n",
- snd_pcm_format_name(this->format),
+
+ g_print("Preparing channel: %s %dHz, %d channels\n",
+ snd_pcm_format_name(this->format),
this->rate, this->channels);
-
+
snd_pcm_hw_params_alloca(&hw_param);
snd_pcm_sw_params_alloca(&sw_param);
-
+
ret = snd_pcm_hw_params_any(this->handle, hw_param);
if (ret < 0) {
g_warning("Broken configuration for this PCM: no configurations available");
return FALSE;
}
-
+
if ((ret = snd_pcm_hw_params_set_periods_integer (this->handle, hw_param)) < 0) {
g_warning("cannot restrict period size to integral value.");
return FALSE;
mask = alloca(snd_pcm_access_mask_sizeof());
snd_pcm_access_mask_none(mask);
-
+
if (this->data_interleaved)
snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
-
+
snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
ret = snd_pcm_hw_params_set_access_mask(this->handle, hw_param, mask);
if (ret < 0) {
g_warning("the gstreamer alsa plugin does not support your hardware.");
return FALSE;
}
-
+
if (this->format != SND_PCM_FORMAT_UNKNOWN) {
ret = snd_pcm_hw_params_set_format(this->handle, hw_param, this->format);
if (ret < 0) {
}
this->sample_bytes = snd_pcm_format_physical_width(this->format) / 8;
}
-
+
ret = snd_pcm_hw_params_set_channels(this->handle, hw_param, this->channels);
if (ret < 0) {
g_warning("Channels count (%d) not available: %s", this->channels, snd_strerror(ret));
return FALSE;
}
this->channels = snd_pcm_hw_params_get_channels(hw_param);
-
+
if (this->rate) {
ret = snd_pcm_hw_params_set_rate(this->handle, hw_param, this->rate, 0);
if (ret < 0) {
return FALSE;
}
}
-
+
if (this->period_count) {
ret = snd_pcm_hw_params_set_periods (this->handle, hw_param, this->period_count, 0);
if (ret < 0) {
return FALSE;
}
}
-
+
if (this->period_frames) {
ret = snd_pcm_hw_params_set_period_size (this->handle, hw_param, this->period_frames, 0);
if (ret < 0) {
return FALSE;
}
}
-
+
if (this->buffer_frames) {
ret = snd_pcm_hw_params_set_buffer_size (this->handle, hw_param, this->buffer_frames);
if (ret < 0) {
return FALSE;
}
}
-
+
ret = snd_pcm_hw_params(this->handle, hw_param);
if (ret < 0) {
g_warning("could not set hw params: %s", snd_strerror(ret));
snd_pcm_hw_params_dump(hw_param, this->out);
return FALSE;
}
-
+
if (!this->rate)
this->rate = snd_pcm_hw_params_get_rate(hw_param, 0);
if (!this->format)
this->buffer_frames = snd_pcm_hw_params_get_buffer_size(hw_param);
if (this->buffer_frames != this->period_count * this->period_frames)
g_critical ("buffer size != period size * number of periods, unexpected things may happen!");
-
+
snd_pcm_sw_params_current (this->handle, sw_param);
ret = snd_pcm_sw_params_set_start_threshold (this->handle, sw_param, ~0U);
g_warning("could not set start mode: %s", snd_strerror(ret));
return FALSE;
}
-
+
ret = snd_pcm_sw_params_set_stop_threshold (this->handle, sw_param, this->buffer_frames);
if (ret < 0) {
g_warning("could not set stop mode: %s", snd_strerror(ret));
return FALSE;
}
-
+
ret = snd_pcm_sw_params_set_silence_threshold (this->handle, sw_param, 0);
if (ret < 0) {
g_warning("could not set silence threshold: %s", snd_strerror(ret));
return FALSE;
}
-
+
ret = snd_pcm_sw_params_set_silence_size (this->handle, sw_param, this->buffer_frames);
if (ret < 0) {
g_warning("could not set silence size: %s", snd_strerror(ret));
return FALSE;
}
-
+
ret = snd_pcm_sw_params_set_avail_min (this->handle, sw_param, this->period_frames);
if (ret < 0) {
g_warning("could not set avail min: %s", snd_strerror(ret));
return FALSE;
}
-
+
ret = snd_pcm_sw_params (this->handle, sw_param);
if (ret < 0) {
g_warning("could not set sw_params: %s", snd_strerror(ret));
if (this->debug)
snd_pcm_dump(this->handle, this->out);
-
+
this->access_interleaved = !(snd_pcm_hw_params_get_access (hw_param) ==
SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
-
+
if (this->access_interleaved) {
this->interleave_unit = this->sample_bytes;
this->interleave_skip = this->interleave_unit * this->channels;
this->interleave_unit = 0; /* not used */
this->interleave_skip = this->sample_bytes;
}
-
+
if (this->access_addr)
g_free (this->access_addr);
this->access_addr = g_new0 (char*, this->channels);
-
+
return TRUE;
}
if (this->handle)
gst_alsa_close_audio(this);
-
+
g_print("Opening alsa device \"%s\" for %s...\n", this->device,
this->stream==SND_PCM_STREAM_PLAYBACK ? "playback" : "capture");
-
+
ret = snd_output_stdio_attach(&this->out, stdout, 0);
if (ret < 0) {
g_print("error opening log output: %s\n", snd_strerror(ret));
return FALSE;
}
-
+
/* blocking i/o */
if ((ret = snd_pcm_open(&this->handle, this->device, this->stream, 0))) {
- g_print("error opening pcm device %s: %s\n",
+ g_print("error opening pcm device %s: %s\n",
this->device, snd_strerror(ret));
return FALSE;
}
-
+
if (gst_alsa_set_params(this) == FALSE) {
gst_alsa_close_audio(this);
return FALSE;
}
-
+
GST_FLAG_SET(this, GST_ALSA_OPEN);
return TRUE;
}
{
gint err;
guint32 chn;
-
+
g_return_val_if_fail(this != NULL, FALSE);
g_return_val_if_fail(this->handle != NULL, FALSE);
-
+
if ((err = snd_pcm_prepare (this->handle)) < 0) {
g_warning("channel prepare failed: %s", snd_strerror (err));
return FALSE;
}
-
+
this->avail = snd_pcm_avail_update (this->handle);
-
+
if (this->stream == SND_PCM_STREAM_PLAYBACK &&
this->avail != this->buffer_frames) {
g_warning ("full buffer not available at start");
return FALSE;
}
-
+
if (!gst_alsa_get_channel_addresses (this)) {
return FALSE;
}
-
+
if (this->stream == SND_PCM_STREAM_PLAYBACK) {
for (chn = 0; chn < this->channels; chn++) {
gst_alsa_sink_silence_on_channel (this, chn, this->buffer_frames);
}
}
-
+
gst_alsa_release_channel_addresses (this);
-
+
if ((err = snd_pcm_start (this->handle)) < 0) {
g_warning("could not start audio: %s", snd_strerror (err));
return FALSE;
}
-
+
GST_FLAG_SET(this, GST_ALSA_RUNNING);
return TRUE;
}
{
gint err;
g_assert(this != NULL);
-
+
g_return_if_fail(this != NULL);
g_return_if_fail(this->handle != NULL);
if (this->mmap_open)
gst_alsa_release_channel_addresses (this);
-
- if (this->stream == SND_PCM_STREAM_PLAYBACK &&
+
+ if (this->stream == SND_PCM_STREAM_PLAYBACK &&
(err = snd_pcm_drop (this->handle)) < 0) {
g_warning("channel flush failed: %s", snd_strerror (err));
return;
}
-
+
GST_FLAG_UNSET(this, GST_ALSA_RUNNING);
}
static void
gst_alsa_close_audio(GstAlsa *this)
{
-/* gint err; */
g_return_if_fail(this != NULL);
g_return_if_fail(this->handle != NULL);
-/* if ((err = snd_pcm_drop (this->handle)) < 0) {
- g_warning("channel flush for failed: %s", snd_strerror (err));
- return;
- } */
-
snd_pcm_close(this->handle);
-
+
this->handle = NULL;
GST_FLAG_UNSET(this, GST_ALSA_OPEN);
guint32 err, i;
const snd_pcm_channel_area_t *a;
GList *l;
-
+
g_return_val_if_fail (this->mmap_open == FALSE, FALSE);
-
+
if ((err = snd_pcm_mmap_begin (this->handle, &this->mmap_areas, &this->offset, &this->avail)) < 0) {
g_warning("gstalsa: mmap failed: %s", snd_strerror(err));
return FALSE;
}
-
+
GST_DEBUG(0, "got %d mmap'd frames", (int)this->avail);
-
-/* g_print ("snd_pcm_mmap_begin() sets avail = %d\n", this->avail); */
-
+
l = this->pads;
while (l) {
a = &this->mmap_areas[GST_ALSA_PAD(l)->channel > 0 ?
this->offset) / 8);
l = l->next;
}
-
+
for (i=0; i<this->channels; i++) {
a = &this->mmap_areas[i];
- this->access_addr[i] = (char *) a->addr + ((a->first + a->step * this->offset) / 8);
+ this->access_addr[i] = (char *) a->addr + ((a->first + a->step *
+ this->offset) / 8);
}
-
+
this->mmap_open = TRUE;
-
+
return TRUE;
}
{
guint32 err, i;
GList *l;
-
+
g_return_if_fail (this->mmap_open == TRUE);
-
+
GST_DEBUG(0, "releasing mmap'd data region: %d frames", (int)this->avail);
-
+
if ((err = snd_pcm_mmap_commit (this->handle, this->offset, this->avail)) < 0) {
g_warning("gstalsa: mmap commit failed: %s", snd_strerror(err));
return;
}
-
+
l = this->pads;
while (l) {
GST_ALSA_PAD(l)->access_addr = NULL;
l = l->next;
}
-
+
for (i=0; i<this->channels; i++) {
this->access_addr[i] = NULL;
}
-
+
this->mmap_open = FALSE;
this->avail=0;
}
-static void
-gst_alsa_sink_silence_on_channel (GstAlsa *this, guint32 chn, guint32 nframes)
+static void
+gst_alsa_sink_silence_on_channel (GstAlsa *this, guint32 chn, guint32 nframes)
{
if (this->access_interleaved) {
- memset_interleave
+ memset_interleave
(this->access_addr[chn],
0, nframes * this->sample_bytes,
this->interleave_unit,
} else {
memset (this->access_addr[chn], 0, nframes * this->sample_bytes);
}
-/* mark_channel_done (chn); */
}
/* taken directly from paul davis' memops.cc */
static void
-memset_interleave (char *dst, char val, unsigned int bytes,
- unsigned int unit_bytes,
- unsigned int skip_bytes)
+memset_interleave (char *dst, char val, unsigned int bytes,
+ unsigned int unit_bytes,
+ unsigned int skip_bytes)
{
switch (unit_bytes) {
case 1:
bytes -= 2;
}
break;
- case 4:
+ case 4:
while (bytes) {
*((int *) dst) = (int) val;
dst += skip_bytes;
plugin_init (GModule *module, GstPlugin *plugin)
{
GstElementFactory *factory;
-
+
if (!gst_library_load ("gstbytestream"))
return FALSE;
-
- factory = gst_element_factory_new ("alsasrc", GST_TYPE_ALSA_SRC,
+
+ factory = gst_element_factory_new ("alsasrc", GST_TYPE_ALSA_SRC,
&gst_alsa_src_details);
g_return_val_if_fail (factory != NULL, FALSE);
gst_element_factory_add_pad_template (factory, gst_alsa_src_pad_factory ());
- gst_element_factory_add_pad_template (factory,
+ gst_element_factory_add_pad_template (factory,
gst_alsa_src_request_pad_factory ());
gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
-
- factory = gst_element_factory_new ("alsasink", GST_TYPE_ALSA_SINK,
+
+ factory = gst_element_factory_new ("alsasink", GST_TYPE_ALSA_SINK,
&gst_alsa_sink_details);
g_return_val_if_fail (factory != NULL, FALSE);
gst_element_factory_add_pad_template (factory, gst_alsa_sink_pad_factory());
- gst_element_factory_add_pad_template (factory,
+ gst_element_factory_add_pad_template (factory,
gst_alsa_sink_request_pad_factory());
gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
-
+
gst_plugin_set_longname(plugin, "ALSA plugin library");
-
+
return TRUE;
}