mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-31 20:48:56 +00:00
ALSA cleanup step 3:
Original commit message from CVS: ALSA cleanup step 3: - make caps nego work, when caps are already set - rewriting lots of caps nego while doing so - start stream explicitly now (will probably stay that way because of sync) - random bugfixes alsasrc is probably broken again. alsasink should now be stable enough to be used with gst-player or rhythmbox (seeking works)
This commit is contained in:
parent
8144288b14
commit
4b1649ad34
2 changed files with 242 additions and 129 deletions
|
@ -58,7 +58,7 @@ 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>"
|
||||
"Andy Wingo <apwingo@eos.ncsu.edu>, "
|
||||
"Benjamin Otte <in7y118@public.uni-hamburg.de>",
|
||||
"(C) 2001-2003"
|
||||
};
|
||||
|
@ -71,7 +71,7 @@ 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>"
|
||||
"Andy Wingo <apwingo@eos.ncsu.edu>, "
|
||||
"Benjamin Otte <in7y118@public.uni-hamburg.de>",
|
||||
"(C) 2001-2003"
|
||||
};
|
||||
|
@ -279,14 +279,11 @@ gst_alsa_init (GstAlsa *this)
|
|||
gint i;
|
||||
/* init values */
|
||||
this->handle = NULL;
|
||||
this->channels = 1;
|
||||
this->caps_set = FALSE;
|
||||
|
||||
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 */
|
||||
gst_element_set_loop_function (GST_ELEMENT (this), gst_alsa_src_loop);
|
||||
this->pads[0].pad = gst_pad_new_from_template (gst_alsa_src_pad_factory (), "src");
|
||||
gst_pad_set_bufferpool_function(this->pads[0].pad, gst_alsa_src_get_buffer_pool);
|
||||
|
@ -294,10 +291,8 @@ gst_alsa_init (GstAlsa *this)
|
|||
/* set the rate to a sensible value. we can't have gobject construct this
|
||||
manually since it only really makes sense on src elements. the rate can
|
||||
be changed later through the gobject set property function. */
|
||||
this->rate = 44100;
|
||||
} 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 */
|
||||
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);
|
||||
|
@ -328,17 +323,17 @@ gst_alsa_set_property (GObject *object, guint prop_id, const GValue *value,
|
|||
case ARG_FORMAT:
|
||||
/* setting this property only makes sense on sources */
|
||||
if (G_OBJECT_TYPE (this) == GST_TYPE_ALSA_SRC)
|
||||
this->format = g_value_get_enum (value);
|
||||
this->format->format = g_value_get_enum (value);
|
||||
break;
|
||||
case ARG_CHANNELS:
|
||||
/* setting this property only makes sense on sources */
|
||||
if (G_OBJECT_TYPE (this) == GST_TYPE_ALSA_SRC)
|
||||
this->channels = g_value_get_int (value);
|
||||
this->format->channels = g_value_get_int (value);
|
||||
break;
|
||||
case ARG_RATE:
|
||||
/* setting this property only makes sense on sources */
|
||||
if (G_OBJECT_TYPE (this) == GST_TYPE_ALSA_SRC)
|
||||
this->rate = g_value_get_int (value);
|
||||
this->format->rate = g_value_get_int (value);
|
||||
break;
|
||||
case ARG_PERIODCOUNT:
|
||||
g_return_if_fail (!GST_FLAG_IS_SET (this, GST_ALSA_RUNNING));
|
||||
|
@ -383,13 +378,13 @@ gst_alsa_get_property (GObject *object, guint prop_id, GValue *value,
|
|||
g_value_set_string (value, this->device);
|
||||
break;
|
||||
case ARG_FORMAT:
|
||||
g_value_set_enum (value, this->format);
|
||||
g_value_set_enum (value, this->format ? this->format->format : SND_PCM_FORMAT_UNKNOWN);
|
||||
break;
|
||||
case ARG_CHANNELS:
|
||||
g_value_set_int (value, this->channels);
|
||||
g_value_set_int (value, this->format ? this->format->channels : 2);
|
||||
break;
|
||||
case ARG_RATE:
|
||||
g_value_set_int (value, this->rate);
|
||||
g_value_set_int (value, this->format ? this->format->rate : 44100);
|
||||
break;
|
||||
case ARG_PERIODCOUNT:
|
||||
g_value_set_int (value, this->period_count);
|
||||
|
@ -474,9 +469,6 @@ gst_alsa_request_new_pad (GstElement *element, GstPadTemplate *templ,
|
|||
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 already has more than 1 channel */
|
||||
g_return_val_if_fail (this->channels <= element->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. */
|
||||
|
@ -516,15 +508,19 @@ found_channel:
|
|||
return this->pads[channel].pad;
|
||||
}
|
||||
|
||||
/* gets the matching alsa format or SND_PCM_FORMAT_UNKNOWN if none matches */
|
||||
static snd_pcm_format_t
|
||||
/* gets the matching alsa format or NULL if none matches */
|
||||
static GstAlsaFormat *
|
||||
gst_alsa_get_format (GstCaps *caps)
|
||||
{
|
||||
const gchar *format_name;
|
||||
GstAlsaFormat *ret;
|
||||
|
||||
if (!(ret = g_new (GstAlsaFormat, 1)))
|
||||
return NULL;
|
||||
|
||||
/* we have to differentiate between int and float formats */
|
||||
if (!gst_caps_get_string (caps, "format", &format_name))
|
||||
return SND_PCM_FORMAT_UNKNOWN;
|
||||
goto error;
|
||||
|
||||
if (strncmp (format_name, "int", 3) == 0) {
|
||||
gboolean sign;
|
||||
|
@ -537,36 +533,40 @@ gst_alsa_get_format (GstCaps *caps)
|
|||
"law", &law,
|
||||
"signed", &sign,
|
||||
NULL))
|
||||
return SND_PCM_FORMAT_UNKNOWN;
|
||||
goto error;
|
||||
|
||||
/* extract endianness if needed */
|
||||
if (width > 8) {
|
||||
if (!gst_caps_get (caps,
|
||||
"endianness", &endianness,
|
||||
NULL))
|
||||
return SND_PCM_FORMAT_UNKNOWN;
|
||||
} else {
|
||||
goto error;
|
||||
} else {
|
||||
endianness = G_BYTE_ORDER;
|
||||
}
|
||||
|
||||
/* find corresponding alsa format */
|
||||
switch (law) {
|
||||
case 0: return snd_pcm_build_linear_format (depth, width, sign ? 0 : 1, endianness == G_LITTLE_ENDIAN ? 0 : 1);
|
||||
case 0:
|
||||
ret->format = snd_pcm_build_linear_format (depth, width, sign ? 0 : 1, endianness == G_LITTLE_ENDIAN ? 0 : 1);
|
||||
break;
|
||||
case 1:
|
||||
if (width == 8 && depth == 8 && sign == FALSE) {
|
||||
return SND_PCM_FORMAT_MU_LAW;
|
||||
ret->format = SND_PCM_FORMAT_MU_LAW;
|
||||
break;
|
||||
} else {
|
||||
return SND_PCM_FORMAT_UNKNOWN;
|
||||
goto error;
|
||||
}
|
||||
case 2:
|
||||
if (width == 8 && depth == 8 && sign == FALSE) {
|
||||
return SND_PCM_FORMAT_A_LAW;
|
||||
ret->format = SND_PCM_FORMAT_A_LAW;
|
||||
break;
|
||||
} else {
|
||||
return SND_PCM_FORMAT_UNKNOWN;
|
||||
goto error;
|
||||
}
|
||||
default: return SND_PCM_FORMAT_UNKNOWN;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
|
||||
} else if (strncmp (format_name, "float", 5) == 0) {
|
||||
gchar *layout;
|
||||
gfloat intercept, slope;
|
||||
|
@ -576,27 +576,54 @@ gst_alsa_get_format (GstCaps *caps)
|
|||
"intercept", &intercept,
|
||||
"slope", &slope,
|
||||
NULL))
|
||||
return SND_PCM_FORMAT_UNKNOWN;
|
||||
goto error;
|
||||
if (intercept != 0.0f || slope != 1.0f) {
|
||||
return SND_PCM_FORMAT_UNKNOWN;
|
||||
goto error;
|
||||
}
|
||||
/* match layout to format wrt to endianness */
|
||||
/* match layout to format wrt to endianness */
|
||||
if (strncmp (layout, "gfloat", 6) == 0) {
|
||||
if (G_BYTE_ORDER == G_LITTLE_ENDIAN) return SND_PCM_FORMAT_FLOAT_LE;
|
||||
if (G_BYTE_ORDER == G_BIG_ENDIAN) return SND_PCM_FORMAT_FLOAT_BE;
|
||||
return SND_PCM_FORMAT_FLOAT;
|
||||
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 (strncmp (layout, "gdouble", 7) == 0) {
|
||||
if (G_BYTE_ORDER == G_LITTLE_ENDIAN) return SND_PCM_FORMAT_FLOAT64_LE;
|
||||
if (G_BYTE_ORDER == G_BIG_ENDIAN) return SND_PCM_FORMAT_FLOAT64_BE;
|
||||
return SND_PCM_FORMAT_FLOAT64;
|
||||
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 {
|
||||
return SND_PCM_FORMAT_UNKNOWN;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return SND_PCM_FORMAT_UNKNOWN;
|
||||
/* get rate and channels */
|
||||
if (!gst_caps_get (caps, "rate", &ret->rate,
|
||||
"channels", &ret->channels,
|
||||
NULL))
|
||||
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 GstProps *
|
||||
gst_alsa_get_props (snd_pcm_format_t format)
|
||||
|
@ -695,7 +722,7 @@ gst_alsa_caps (snd_pcm_format_t format, gint rate, gint channels)
|
|||
if (props != NULL) {
|
||||
add_channels (props, rate, channels);
|
||||
ret_caps = gst_caps_append (ret_caps, gst_caps_new (g_strdup (snd_pcm_format_name (i)),
|
||||
"audio/raw", props));
|
||||
"audio/raw", props));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -708,8 +735,7 @@ GstPadLinkReturn
|
|||
gst_alsa_link (GstPad *pad, GstCaps *caps)
|
||||
{
|
||||
GstAlsa *this;
|
||||
snd_pcm_format_t format;
|
||||
gint rate, channels;
|
||||
GstAlsaFormat *format;
|
||||
|
||||
g_return_val_if_fail (caps != NULL, GST_PAD_LINK_REFUSED);
|
||||
g_return_val_if_fail (pad != NULL, GST_PAD_LINK_REFUSED);
|
||||
|
@ -722,33 +748,42 @@ gst_alsa_link (GstPad *pad, GstCaps *caps)
|
|||
return GST_PAD_LINK_REFUSED;
|
||||
|
||||
format = gst_alsa_get_format (caps);
|
||||
GST_DEBUG (GST_CAT_CAPS, "found format %s\n", snd_pcm_format_name (format));
|
||||
/* FIXME: allow changing the format here, even by retrying caps on other pads */
|
||||
if (format == SND_PCM_FORMAT_UNKNOWN || (this->format != SND_PCM_FORMAT_UNKNOWN && this->format != format))
|
||||
return GST_PAD_LINK_REFUSED;
|
||||
if (!gst_caps_get (caps, "rate", &rate,
|
||||
"channels", &channels,
|
||||
NULL))
|
||||
return GST_PAD_LINK_REFUSED;
|
||||
if (this->format != SND_PCM_FORMAT_UNKNOWN && this->rate != rate)
|
||||
return GST_PAD_LINK_REFUSED;
|
||||
if (this->format != SND_PCM_FORMAT_UNKNOWN && ((this->channels > 1 || channels > 1)))
|
||||
return GST_PAD_LINK_REFUSED;
|
||||
|
||||
if (this->format == SND_PCM_FORMAT_UNKNOWN) {
|
||||
this->channels = channels;
|
||||
} else {
|
||||
this->channels++;
|
||||
if (format == NULL)
|
||||
return GST_PAD_LINK_DELAYED;
|
||||
|
||||
GST_DEBUG (GST_CAT_CAPS, "found format %s\n", snd_pcm_format_name (format->format));
|
||||
|
||||
if (gst_alsa_formats_match (this->format, format)) {
|
||||
g_free (format);
|
||||
return GST_PAD_LINK_OK;
|
||||
}
|
||||
this->format = format;
|
||||
this->rate = rate;
|
||||
|
||||
/* 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);
|
||||
if (!GST_FLAG_IS_SET (this, GST_ALSA_CAPS_NEGO)) {
|
||||
gint i;
|
||||
|
||||
if (!gst_alsa_open_audio (this)) return GST_PAD_LINK_REFUSED;
|
||||
if (!gst_alsa_start_audio (this)) return GST_PAD_LINK_REFUSED;
|
||||
GST_FLAG_SET (this, GST_ALSA_CAPS_NEGO);
|
||||
|
||||
for (i = 0; i < ((GstElement *) this)->numpads; i++) {
|
||||
g_assert (this->pads[i].pad != NULL);
|
||||
if (this->pads[i].pad == pad)
|
||||
continue;
|
||||
if (gst_pad_try_set_caps (this->pads[i].pad, gst_caps_ref (caps)) == GST_PAD_LINK_REFUSED) {
|
||||
g_free (format);
|
||||
return GST_PAD_LINK_REFUSED;
|
||||
}
|
||||
}
|
||||
|
||||
GST_FLAG_UNSET (this, GST_ALSA_CAPS_NEGO);
|
||||
g_free (this->format);
|
||||
this->format = format;
|
||||
|
||||
/* 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);
|
||||
|
||||
if (!gst_alsa_open_audio (this)) return GST_PAD_LINK_REFUSED;
|
||||
if (!gst_alsa_start_audio (this)) return GST_PAD_LINK_REFUSED;
|
||||
}
|
||||
|
||||
return GST_PAD_LINK_OK;
|
||||
}
|
||||
|
@ -763,8 +798,8 @@ gst_alsa_src_get_buffer_pool (GstPad *pad)
|
|||
|
||||
GstAlsa *this = GST_ALSA (gst_pad_get_parent (pad));
|
||||
|
||||
width = snd_pcm_format_physical_width (this->format);
|
||||
bytes_per_frame = ( width / 8 ) * (GST_ELEMENT (this)->numpads == 1 ? this->channels : 1);
|
||||
width = snd_pcm_format_physical_width (this->format->format);
|
||||
bytes_per_frame = ( width / 8 ) * (GST_ELEMENT (this)->numpads == 1 ? this->format->channels : 1);
|
||||
|
||||
/* FIXME : is this right ? constant size buffers are probably a good thing,
|
||||
but what if the size changes (e.g. during xrun autorecovery) ? */
|
||||
|
@ -782,23 +817,30 @@ gst_alsa_change_state (GstElement *element)
|
|||
|
||||
switch (GST_STATE_TRANSITION (element)) {
|
||||
case GST_STATE_NULL_TO_READY:
|
||||
if (GST_FLAG_IS_SET (element, GST_ALSA_OPEN) == FALSE)
|
||||
if (!GST_FLAG_IS_SET (element, GST_ALSA_OPEN))
|
||||
if (!gst_alsa_open_audio (this))
|
||||
return GST_STATE_FAILURE;
|
||||
break;
|
||||
case GST_STATE_READY_TO_PAUSED:
|
||||
break;
|
||||
case GST_STATE_PAUSED_TO_PLAYING:
|
||||
if (GST_FLAG_IS_SET (element, GST_ALSA_RUNNING) == FALSE)
|
||||
if (!GST_FLAG_IS_SET (element, GST_ALSA_RUNNING))
|
||||
if (!gst_alsa_start_audio (this))
|
||||
return GST_STATE_FAILURE;
|
||||
|
||||
break;
|
||||
case GST_STATE_PAUSED_TO_PLAYING:
|
||||
if (snd_pcm_state (this->handle) == SND_PCM_STATE_PAUSED)
|
||||
snd_pcm_pause (this->handle, 0);
|
||||
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)
|
||||
snd_pcm_pause (this->handle, 1);
|
||||
break;
|
||||
}
|
||||
/* if device doesn't know how to pause, we just stop */
|
||||
case GST_STATE_PAUSED_TO_READY:
|
||||
if (GST_FLAG_IS_SET (element, GST_ALSA_RUNNING))
|
||||
gst_alsa_drain_audio (this);
|
||||
break;
|
||||
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);
|
||||
|
@ -821,21 +863,21 @@ gst_alsa_do_mmap (GstAlsa *this, snd_pcm_sframes_t *avail)
|
|||
{
|
||||
snd_pcm_uframes_t offset;
|
||||
snd_pcm_channel_area_t *dst, *src, *areas;
|
||||
int i, err, width = snd_pcm_format_physical_width (this->format);
|
||||
int i, err, width = snd_pcm_format_physical_width (this->format->format);
|
||||
|
||||
/* areas points to the memory areas that belong to gstreamer. */
|
||||
areas = src = dst = calloc(this->channels, sizeof(snd_pcm_channel_area_t));
|
||||
areas = src = dst = calloc(this->format->channels, sizeof(snd_pcm_channel_area_t));
|
||||
|
||||
if (((GstElement *) this)->numpads == 1) {
|
||||
/* interleaved */
|
||||
for (i = 0; i < this->channels; i++) {
|
||||
for (i = 0; i < this->format->channels; i++) {
|
||||
areas[i].addr = this->pads[0].data;
|
||||
areas[i].first = i * width;
|
||||
areas[i].step = this->channels * width;
|
||||
areas[i].step = this->format->channels * width;
|
||||
}
|
||||
} else {
|
||||
/* noninterleaved */
|
||||
for (i = 0; i < this->channels; i++) {
|
||||
for (i = 0; i < this->format->channels; i++) {
|
||||
areas[i].addr = this->pads[i].data;
|
||||
areas[i].first = 0;
|
||||
areas[i].step = width;
|
||||
|
@ -849,7 +891,7 @@ gst_alsa_do_mmap (GstAlsa *this, snd_pcm_sframes_t *avail)
|
|||
return -1;
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_areas_copy (dst, offset, src, 0, this->channels, *avail, this->format)) < 0) {
|
||||
if ((err = snd_pcm_areas_copy (dst, offset, src, 0, this->format->channels, *avail, this->format->format)) < 0) {
|
||||
snd_pcm_mmap_commit (this->handle, offset, 0);
|
||||
g_warning ("gstalsa: data copy failed: %s", snd_strerror (err));
|
||||
return -1;
|
||||
|
@ -875,7 +917,6 @@ gst_alsa_update_avail (GstAlsa *this)
|
|||
}
|
||||
return avail;
|
||||
}
|
||||
|
||||
/* returns TRUE, if the loop should go on */
|
||||
inline static gboolean
|
||||
gst_alsa_pcm_wait (GstAlsa *this)
|
||||
|
@ -898,6 +939,34 @@ gst_alsa_pcm_wait (GstAlsa *this)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* error out or make sure we're in SND_PCM_STATE_RUNNING afterwards
|
||||
* return FALSE if we're not
|
||||
*/
|
||||
inline static gboolean
|
||||
gst_alsa_start (GstAlsa *this)
|
||||
{
|
||||
GST_DEBUG (GST_CAT_PLUGIN_INFO, "Starting playback");
|
||||
|
||||
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_PREPARED:
|
||||
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;
|
||||
default:
|
||||
/* it's a bug when we get here */
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
static void
|
||||
gst_alsa_sink_loop (GstElement *element)
|
||||
{
|
||||
|
@ -909,7 +978,7 @@ gst_alsa_sink_loop (GstElement *element)
|
|||
g_return_if_fail (this != NULL);
|
||||
|
||||
/* caps nego: fetch 1 byte from every pad */
|
||||
if (this->format == SND_PCM_FORMAT_UNKNOWN) {
|
||||
if (this->format == NULL) {
|
||||
GST_DEBUG (GST_CAT_NEGOTIATION, "starting caps negotiation");
|
||||
for (i = 0; i < element->numpads; i++) {
|
||||
g_assert (this->pads[i].pad != NULL);
|
||||
|
@ -919,7 +988,7 @@ gst_alsa_sink_loop (GstElement *element)
|
|||
if (num_bytes == 0)
|
||||
return;
|
||||
}
|
||||
if (this->format == SND_PCM_FORMAT_UNKNOWN) {
|
||||
if (this->format == NULL) {
|
||||
gst_element_error (GST_ELEMENT (this), "alsasink: No caps available");
|
||||
}
|
||||
}
|
||||
|
@ -930,7 +999,7 @@ sink_restart:
|
|||
if (avail == -EPIPE) goto sink_restart;
|
||||
if (avail < 0) return;
|
||||
if (avail > 0) {
|
||||
int width = snd_pcm_format_physical_width (this->format);
|
||||
int width = snd_pcm_format_physical_width (this->format->format);
|
||||
|
||||
/* Not enough space. We grab data nonetheless and sleep afterwards */
|
||||
if (avail < this->period_size) {
|
||||
|
@ -938,7 +1007,7 @@ sink_restart:
|
|||
}
|
||||
|
||||
/* check how many bytes we still have in all our bytestreams */
|
||||
bytes = avail * ( width / 8 ) * (element->numpads == 1 ? this->channels : 1);
|
||||
bytes = avail * ( width / 8 ) * (element->numpads == 1 ? this->format->channels : 1);
|
||||
for (i = 0; i < element->numpads; i++) {
|
||||
g_assert (this->pads[i].pad != NULL);
|
||||
do {
|
||||
|
@ -948,31 +1017,35 @@ sink_restart:
|
|||
return;
|
||||
bytes = MIN (bytes, num_bytes);
|
||||
}
|
||||
avail = bytes / (width / 8 ) / (element->numpads == 1 ? this->channels : 1);
|
||||
|
||||
/* FIXME: lotsa stuff can have happened while fetching data. Do we need to check something? */
|
||||
avail = bytes / (width / 8 ) / (element->numpads == 1 ? this->format->channels : 1);
|
||||
|
||||
/* wait until the hw buffer has enough space */
|
||||
while ((avail2 = gst_alsa_update_avail (this)) < avail) {
|
||||
if (avail2 == -EPIPE) goto sink_restart;
|
||||
if (avail < 0) return;
|
||||
while (gst_element_get_state (element) == GST_STATE_PLAYING && (avail2 = gst_alsa_update_avail (this)) < avail) {
|
||||
if (avail2 <= -EPIPE) goto sink_restart;
|
||||
if (avail2 < 0) return;
|
||||
if (avail2 < avail && snd_pcm_state(this->handle) != SND_PCM_STATE_RUNNING)
|
||||
if (!gst_alsa_start (this)) return;
|
||||
if (gst_alsa_pcm_wait (this) == FALSE)
|
||||
return;
|
||||
}
|
||||
|
||||
if (gst_element_get_state (element) != GST_STATE_PLAYING)
|
||||
return;
|
||||
/* FIXME: lotsa stuff can have happened while fetching data. Do we need to check something? */
|
||||
|
||||
|
||||
/* put this data into alsa */
|
||||
if ((copied = gst_alsa_do_mmap (this, &avail)) < 0)
|
||||
return;
|
||||
|
||||
/* flush the data */
|
||||
bytes = copied * ( width / 8 ) * (element->numpads == 1 ? this->channels : 1);
|
||||
bytes = copied * ( width / 8 ) * (element->numpads == 1 ? this->format->channels : 1);
|
||||
for (i = 0; i < element->numpads; i++)
|
||||
gst_bytestream_flush (this->pads[i].bs, bytes);
|
||||
}
|
||||
|
||||
/* BUG: we start the stream explicitly, autostart doesn't work correctly (alsa 0.9.0rc7) */
|
||||
if (snd_pcm_state(this->handle) == SND_PCM_STATE_PREPARED && snd_pcm_avail_update (this->handle) == 0) {
|
||||
GST_DEBUG (GST_CAT_PLUGIN_INFO, "Explicitly starting playback");
|
||||
snd_pcm_start(this->handle);
|
||||
}
|
||||
if (snd_pcm_state(this->handle) != SND_PCM_STATE_RUNNING && snd_pcm_avail_update (this->handle) == 0) {
|
||||
gst_alsa_start (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -991,16 +1064,22 @@ gst_alsa_src_loop (GstElement *element)
|
|||
g_return_if_fail (this != NULL);
|
||||
|
||||
/* set the caps on all pads */
|
||||
if (!this->caps_set) {
|
||||
if (!this->format) {
|
||||
if (!(this->format = g_new (GstAlsaFormat, 1))) {
|
||||
gst_element_error (element, "No more memory");
|
||||
}
|
||||
/* FIXME: make this settable */
|
||||
this->format->format = SND_PCM_FORMAT_S16;
|
||||
this->format->rate = 44100;
|
||||
this->format->channels = (element->numpads == 1) ? 2 : element->numpads;
|
||||
GST_DEBUG (GST_CAT_NEGOTIATION, "starting caps negotiationgst_alsa_pcm_wait");
|
||||
caps = gst_alsa_caps (this->format, this->rate, this->channels);
|
||||
caps = gst_alsa_caps (this->format->format, this->format->rate, this->format->channels);
|
||||
for (i = 0; i < element->numpads; i++) {
|
||||
if (gst_pad_try_set_caps (this->pads[i].pad, caps) <= 0) {
|
||||
GST_DEBUG (GST_CAT_NEGOTIATION, "setting caps (%p) in alsasrc (%p) on pad %d failed", caps, this, i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
this->caps_set = TRUE;
|
||||
}
|
||||
|
||||
src_restart:
|
||||
|
@ -1008,13 +1087,14 @@ src_restart:
|
|||
while ((avail = gst_alsa_update_avail (this)) < this->period_size) {
|
||||
if (avail == -EPIPE) goto src_restart;
|
||||
if (avail < 0) return;
|
||||
if (snd_pcm_state(this->handle) != SND_PCM_STATE_RUNNING) break;
|
||||
/* wait */
|
||||
if (gst_alsa_pcm_wait (this) == FALSE)
|
||||
return;
|
||||
}
|
||||
if (avail > 0) {
|
||||
int width = snd_pcm_format_physical_width (this->format);
|
||||
int bytes_per_frame = ( width / 8 ) * (element->numpads == 1 ? this->channels : 1);
|
||||
int width = snd_pcm_format_physical_width (this->format->format);
|
||||
int bytes_per_frame = ( width / 8 ) * (element->numpads == 1 ? this->format->channels : 1);
|
||||
if ((copied = gst_alsa_do_mmap (this, &avail)) < 0)
|
||||
return;
|
||||
|
||||
|
@ -1043,12 +1123,12 @@ src_restart:
|
|||
}
|
||||
|
||||
pool = NULL;
|
||||
}
|
||||
|
||||
/* BUG: we start the stream explicitly, autostart doesn't work correctly (alsa 0.9.0rc7) */
|
||||
if (snd_pcm_state(this->handle) == SND_PCM_STATE_PREPARED && snd_pcm_avail_update (this->handle) == 0) {
|
||||
GST_DEBUG (GST_CAT_PLUGIN_INFO, "Explicitly starting capture");
|
||||
snd_pcm_start(this->handle);
|
||||
}
|
||||
/* BUG: we start the stream explicitly, autostart doesn't work correctly (alsa 0.9.0rc7) */
|
||||
if (snd_pcm_state(this->handle) == SND_PCM_STATE_PREPARED && snd_pcm_avail_update (this->handle) == 0) {
|
||||
GST_DEBUG (GST_CAT_PLUGIN_INFO, "Explicitly starting capture");
|
||||
snd_pcm_start(this->handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1101,11 +1181,17 @@ gst_alsa_sink_check_event (GstAlsa *this, gint pad_nr)
|
|||
gst_bytestream_get_status (this->pads[pad_nr].bs, &avail, &event);
|
||||
|
||||
if (event) {
|
||||
if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
|
||||
gst_element_set_eos (GST_ELEMENT (this));
|
||||
cont = FALSE;
|
||||
} else {
|
||||
g_warning ("GstAlsaSink: got an unknown event (Type: %d)", GST_EVENT_TYPE (event));
|
||||
switch (GST_EVENT_TYPE (event)) {
|
||||
case GST_EVENT_EOS:
|
||||
gst_element_set_eos (GST_ELEMENT (this));
|
||||
cont = FALSE;
|
||||
break;
|
||||
case GST_EVENT_INTERRUPT:
|
||||
cont = FALSE;
|
||||
break;
|
||||
default:
|
||||
g_warning ("GstAlsaSink: got an unknown event (Type: %d)", GST_EVENT_TYPE (event));
|
||||
break;
|
||||
}
|
||||
gst_event_unref (event);
|
||||
} else {
|
||||
|
@ -1135,7 +1221,11 @@ gst_alsa_open_audio (GstAlsa *this)
|
|||
GST_FLAG_SET (this, GST_ALSA_OPEN);
|
||||
return TRUE;
|
||||
}
|
||||
/* you must set all hw parameters at once - thx ALSA for not documenting this */
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
|
@ -1145,13 +1235,13 @@ gst_alsa_set_hw_params (GstAlsa *this)
|
|||
unsigned int count_min, count_max;
|
||||
|
||||
/* whether to use default values when setting params */
|
||||
gboolean def = (this->format == SND_PCM_FORMAT_UNKNOWN);
|
||||
gboolean def = (this->format == NULL);
|
||||
|
||||
g_return_val_if_fail (this != NULL, FALSE);
|
||||
g_return_val_if_fail (this->handle != NULL, FALSE);
|
||||
|
||||
GST_INFO (GST_CAT_PLUGIN_INFO, "Preparing channel: %s %dHz, %d channels\n",
|
||||
snd_pcm_format_name (this->format), this->rate, this->channels);
|
||||
snd_pcm_format_name (this->format->format), this->format->rate, this->format->channels);
|
||||
|
||||
snd_pcm_hw_params_alloca (&hw_params);
|
||||
ERROR_CHECK (snd_pcm_hw_params_any (this->handle, hw_params),
|
||||
|
@ -1169,12 +1259,12 @@ gst_alsa_set_hw_params (GstAlsa *this)
|
|||
ERROR_CHECK (snd_pcm_hw_params_set_access_mask (this->handle, hw_params, mask),
|
||||
"The Gstreamer ALSA plugin does not support your hardware. Error: %s");
|
||||
|
||||
ERROR_CHECK (snd_pcm_hw_params_set_format (this->handle, hw_params, def ? SND_PCM_FORMAT_S16 : this->format),
|
||||
"Sample format (%s) not available: %s", snd_pcm_format_name (def ? SND_PCM_FORMAT_S16 : this->format));
|
||||
ERROR_CHECK (snd_pcm_hw_params_set_channels (this->handle, hw_params, this->channels),
|
||||
"Channels count (%d) not available: %s", this->channels);
|
||||
ERROR_CHECK (snd_pcm_hw_params_set_rate (this->handle, hw_params, def ? 44100 : this->rate, 0),
|
||||
"error setting rate (%d): %s", def ? 44100 : this->rate);
|
||||
ERROR_CHECK (snd_pcm_hw_params_set_format (this->handle, hw_params, def ? SND_PCM_FORMAT_S16 : this->format->format),
|
||||
"Sample format (%s) not available: %s", snd_pcm_format_name (def ? SND_PCM_FORMAT_S16 : this->format->format));
|
||||
ERROR_CHECK (snd_pcm_hw_params_set_channels (this->handle, hw_params, def ? ((GstElement *) this)->numpads : this->format->channels),
|
||||
"Channels count (%d) not available: %s", def ? ((GstElement *) this)->numpads : this->format->channels);
|
||||
ERROR_CHECK (snd_pcm_hw_params_set_rate (this->handle, hw_params, def ? 44100 : this->format->rate, 0),
|
||||
"error setting rate (%d): %s", def ? 44100 : this->format->rate);
|
||||
|
||||
if (snd_pcm_hw_params_get_period_size_min (hw_params, &size_min, 0) < 0) size_min = this->period_size;
|
||||
if (snd_pcm_hw_params_get_period_size_max (hw_params, &size_max, 0) < 0) size_max = this->period_size;
|
||||
|
@ -1194,6 +1284,11 @@ gst_alsa_set_hw_params (GstAlsa *this)
|
|||
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));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
static gboolean
|
||||
|
@ -1211,7 +1306,8 @@ gst_alsa_set_sw_params (GstAlsa *this)
|
|||
"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");
|
||||
ERROR_CHECK (snd_pcm_sw_params_set_start_threshold (this->handle, sw_params, 1),
|
||||
/* 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");
|
||||
|
|
|
@ -63,8 +63,23 @@ typedef GstAlsaClass GstAlsaSrcClass;
|
|||
enum {
|
||||
GST_ALSA_OPEN = GST_ELEMENT_FLAG_LAST,
|
||||
GST_ALSA_RUNNING,
|
||||
GST_ALSA_CAPS_NEGO,
|
||||
GST_ALSA_FLAG_LAST = GST_ELEMENT_FLAG_LAST + 3,
|
||||
};
|
||||
typedef enum {
|
||||
GST_ALSA_CAPS_PAUSE = 0,
|
||||
GST_ALSA_CAPS_RESUME,
|
||||
GST_ALSA_CAPS_SYNC_START
|
||||
/* add more */
|
||||
} GstAlsaPcmCaps;
|
||||
#define GST_ALSA_CAPS_IS_SET(obj, flag) (GST_ALSA (obj)->pcm_caps & (1<<(flag)))
|
||||
#define GST_ALSA_CAPS_SET(obj, flag, set) G_STMT_START{ \
|
||||
if (set) { \
|
||||
(GST_ALSA (obj)->pcm_caps |= (1<<(flag))); \
|
||||
} else { \
|
||||
(GST_ALSA (obj)->pcm_caps &= ~(1<<(flag))); \
|
||||
} \
|
||||
}G_STMT_END
|
||||
|
||||
typedef struct {
|
||||
GstPad *pad;
|
||||
|
@ -72,7 +87,11 @@ typedef struct {
|
|||
guint8 *data;
|
||||
guint8 offset;
|
||||
} GstAlsaPad;
|
||||
|
||||
typedef struct {
|
||||
snd_pcm_format_t format;
|
||||
guint rate;
|
||||
gint channels;
|
||||
} GstAlsaFormat;
|
||||
struct _GstAlsa {
|
||||
GstElement parent;
|
||||
|
||||
|
@ -82,12 +101,10 @@ struct _GstAlsa {
|
|||
gchar *device;
|
||||
snd_pcm_stream_t stream;
|
||||
snd_pcm_t *handle;
|
||||
guint pcm_caps;
|
||||
snd_output_t *out;
|
||||
|
||||
snd_pcm_format_t format;
|
||||
guint rate;
|
||||
gint channels;
|
||||
gboolean caps_set;
|
||||
GstAlsaFormat *format; /* NULL if undefined */
|
||||
|
||||
/* latency / performance parameters */
|
||||
snd_pcm_uframes_t period_size;
|
||||
|
|
Loading…
Reference in a new issue