From: Benjamin Otte Date: Thu, 30 Jan 2003 18:30:30 +0000 (+0000) Subject: cleaning up alsa, step 1: cleaning up caps parsing/setting and templates X-Git-Tag: OSLOSUMMIT1-200303051~40 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=59f2a30f72249f58f63c7ecfa420ec05de9e35c0;p=platform%2Fupstream%2Fgst-plugins-base.git cleaning up alsa, step 1: cleaning up caps parsing/setting and templates Original commit message from CVS: cleaning up alsa, step 1: cleaning up caps parsing/setting and templates - gst-launch ... ! spider ! alsasink works now - alsasrc definitely does not work --- diff --git a/ext/alsa/gstalsa.c b/ext/alsa/gstalsa.c index 40982c1..d411db9 100644 --- a/ext/alsa/gstalsa.c +++ b/ext/alsa/gstalsa.c @@ -60,7 +60,7 @@ static GstPadTemplate *gst_alsa_sink_request_pad_factory (); static GstPad *gst_alsa_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar *name); static GstPadLinkReturn gst_alsa_link (GstPad *pad, GstCaps *caps); -static GstCaps *gst_alsa_caps (GstAlsa *this); +static GstCaps *gst_alsa_caps (snd_pcm_format_t format, gint rate, gint channels); static GstElementStateReturn gst_alsa_change_state (GstElement *element); @@ -98,16 +98,16 @@ gst_alsa_format_get_type (void) { static GType type = 0; static GEnumValue *values = NULL; - gint i, len = SND_PCM_FORMAT_GSM + 3; + gint i; 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); - values[i].value_nick = g_strdup (snd_pcm_format_name ((snd_pcm_format_t) i - 1)); + values = g_new0 (GEnumValue, SND_PCM_FORMAT_LAST + 1); + + for (i = -1; i <= SND_PCM_FORMAT_LAST; i++) { + values[i + 1].value = i; /* UNKNOWN is -1 */ + values[i + 1].value_name = g_strdup_printf ("%d", i); + values[i + 1].value_nick = g_strdup (snd_pcm_format_name ((snd_pcm_format_t) i)); } } @@ -116,7 +116,6 @@ gst_alsa_format_get_type (void) return type; } - GType gst_alsa_get_type (void) { @@ -277,6 +276,8 @@ gst_alsa_init (GstAlsa *this) /* 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); @@ -384,8 +385,8 @@ gst_alsa_src_pad_factory (void) static GstPadTemplate *template = NULL; if (!template) - template = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_SOMETIMES, - gst_caps_new ("src", "audio/raw", NULL), + template = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + gst_alsa_caps (SND_PCM_FORMAT_UNKNOWN, -1, -1), NULL); return template; @@ -397,11 +398,9 @@ 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)), - NULL); + template = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + gst_alsa_caps (SND_PCM_FORMAT_UNKNOWN, -1, 1), + NULL); return template; } @@ -412,8 +411,8 @@ gst_alsa_sink_pad_factory (void) static GstPadTemplate *template = NULL; if (!template) - template = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_SOMETIMES, - gst_caps_new ("sink", "audio/raw", NULL), + template = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + gst_alsa_caps (SND_PCM_FORMAT_UNKNOWN, -1, -1), NULL); return template; @@ -427,8 +426,7 @@ gst_alsa_sink_request_pad_factory (void) 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)), + gst_alsa_caps (SND_PCM_FORMAT_UNKNOWN, -1, 1), NULL); return template; @@ -515,170 +513,148 @@ gst_alsa_request_new_pad (GstElement *element, GstPadTemplate *templ, return pad->pad; } -static gboolean -gst_alsa_parse_caps (GstAlsa *this, GstCaps *caps) +/* gets the matching alsa format or SND_PCM_FORMAT_UNKNOWN if none matches */ +static snd_pcm_format_t +gst_alsa_get_format (GstCaps *caps) { - gint law, endianness, width, depth, channels; - gboolean sign; - gint format = -1; const gchar *format_name; - + /** + * We have to differentiate between int and float, so lets find out which one we have. + * Somebody fix the float caps spec please. + */ if (!gst_caps_get_string (caps, "format", &format_name)) - return FALSE; - - if (format_name == NULL) { - return FALSE; - } else if (strcmp (format_name, "int") == 0) { + return SND_PCM_FORMAT_UNKNOWN; + if (strncmp (format_name, "int", 3) == 0) { + gboolean sign; + int width, depth, endianness, law; + /* extract the needed information from the caps */ if (!gst_caps_get (caps, - "width", &width, "depth", &depth, "law", &law, - "endianness", &endianness, "signed", &sign, NULL)) - return FALSE; - - if (law == 0) { - if (width == 8) { - if (sign == TRUE) { - format = SND_PCM_FORMAT_S8; - } else { - format = SND_PCM_FORMAT_U8; - } - } else if (width == 16) { - if (sign == TRUE) { - if (endianness == G_LITTLE_ENDIAN) format = SND_PCM_FORMAT_S16_LE; - else if (endianness == G_BIG_ENDIAN) format = SND_PCM_FORMAT_S16_BE; - } else { - if (endianness == G_LITTLE_ENDIAN) format = SND_PCM_FORMAT_U16_LE; - else if (endianness == G_BIG_ENDIAN) format = SND_PCM_FORMAT_U16_BE; - } - } else if (width == 24) { - if (sign == TRUE) { - if (endianness == G_LITTLE_ENDIAN) format = SND_PCM_FORMAT_S24_LE; - else if (endianness == G_BIG_ENDIAN) format = SND_PCM_FORMAT_S24_BE; - } else { - if (endianness == G_LITTLE_ENDIAN) format = SND_PCM_FORMAT_U24_LE; - else if (endianness == G_BIG_ENDIAN) format = SND_PCM_FORMAT_U24_BE; - } - } else if (width == 32) { - if (sign == TRUE) { - if (endianness == G_LITTLE_ENDIAN) format = SND_PCM_FORMAT_S32_LE; - else if (endianness == G_BIG_ENDIAN) format = SND_PCM_FORMAT_S32_BE; - } else { - if (endianness == G_LITTLE_ENDIAN) format = SND_PCM_FORMAT_U32_LE; - else if (endianness == G_BIG_ENDIAN) format = SND_PCM_FORMAT_U32_BE; - } - } - } else if (law == 1) { /* mu law */ - if (width == depth && width == 8 && sign == FALSE) { - format = SND_PCM_FORMAT_MU_LAW; - } else { - return FALSE; - } - } else if (law == 2) { /* a law, ug. */ - if (width == depth && width == 8 && sign == FALSE) { - format = SND_PCM_FORMAT_A_LAW; - } - } else { - return FALSE; + "width", &width, + "depth", &depth, + "law", &law, + "endianness", &endianness, + "signed", &sign, + NULL)) + return SND_PCM_FORMAT_UNKNOWN; + + /* 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 1: return SND_PCM_FORMAT_A_LAW; + case 2: return SND_PCM_FORMAT_MU_LAW; + default: return SND_PCM_FORMAT_UNKNOWN; } - } else if (strcmp (format_name, "float") == 0) { - const gchar *layout; - - if (!gst_caps_get_string (caps, "layout", &layout)) - return FALSE; - - if (strcmp (layout, "gfloat") == 0) { - format = SND_PCM_FORMAT_FLOAT; + } else if (strncmp (format_name, "float", 5) == 0) { + gchar *layout; + /* get layout */ + if (!gst_caps_get_string (caps, "layout", (const gchar **) &layout)) + return SND_PCM_FORMAT_UNKNOWN; + /* match layout to format wrt to endianness */ + if (strncmp (layout, "gfloat", 6) == 0) { + return SND_PCM_FORMAT_FLOAT; + } else if (strncmp (layout, "gdouble", 7) == 0) { + return SND_PCM_FORMAT_FLOAT64; } else { - return FALSE; - /* you need doubles? jeez... */ + return SND_PCM_FORMAT_UNKNOWN; } - } else { - return FALSE; } - - this->format = format; - if (!gst_caps_get (caps, "rate", &this->rate, "channels", &channels, NULL)) - return FALSE; - - if (this->data_interleaved) - this->channels = channels; - else if (channels != 1) - return FALSE; - - return TRUE; + + return SND_PCM_FORMAT_UNKNOWN; +} +/* get props for a spec */ +static GstProps * +gst_alsa_get_props (snd_pcm_format_t format) +{ + /* int or float */ + if (snd_pcm_format_linear (format)) { + GstProps *props = gst_props_new ("format", GST_PROPS_STRING ("int"), + "width", GST_PROPS_INT(snd_pcm_format_physical_width (format)), + "depth", GST_PROPS_INT(snd_pcm_format_width (format)), + "signed", GST_PROPS_BOOLEAN (snd_pcm_format_signed (format) == 1 ? TRUE : FALSE), + NULL); + /* endianness */ + switch (snd_pcm_format_little_endian (format)) { + case 0: + gst_props_add_entry (props, gst_props_entry_new ("endianness", GST_PROPS_INT (G_BIG_ENDIAN))); + break; + case 1: + gst_props_add_entry (props, gst_props_entry_new ("endianness", GST_PROPS_INT (G_LITTLE_ENDIAN))); + break; + default: + break; + } + /* law */ + switch (format) { + case SND_PCM_FORMAT_A_LAW: + gst_props_add_entry (props, gst_props_entry_new ("law", GST_PROPS_INT (1))); + break; + case SND_PCM_FORMAT_MU_LAW: + gst_props_add_entry (props, gst_props_entry_new ("law", GST_PROPS_INT (2))); + break; + default: + gst_props_add_entry (props, gst_props_entry_new ("law", GST_PROPS_INT (0))); + break; + } + return props; + } else if (snd_pcm_format_float (format)) { + /* no float with non-platform endianness */ + if (!snd_pcm_format_cpu_endian (format)) + return NULL; + + return gst_props_new ("format", GST_PROPS_STRING ("float"), + "layout", GST_PROPS_STRING (snd_pcm_format_width (format) == 64 ? "gdouble" : "gfloat"), + NULL); + } + return NULL; } -/* caps are so painful sometimes. */ +static inline void add_channels (GstProps *props, gint rate, gint channels) { + if (rate < 0) { + gst_props_add_entry (props, gst_props_entry_new ("rate", GST_PROPS_INT_RANGE (8000, 192000))); + } else { + 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))); + } else { + gst_props_add_entry (props, gst_props_entry_new ("channels", GST_PROPS_INT (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 + */ static GstCaps * -gst_alsa_caps (GstAlsa *this) +gst_alsa_caps (snd_pcm_format_t format, gint rate, gint channels) { - 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"), - "rate", GST_PROPS_INT (this->rate), - "channels", GST_PROPS_INT ((this->data_interleaved ? this->channels : 1)), - NULL); + GstCaps *ret_caps = NULL; + + if (format != SND_PCM_FORMAT_UNKNOWN) { + /* there are some caps set already */ + GstProps *props = gst_alsa_get_props (format); + /* we can never use a format we can't set caps for */ + g_assert (props != NULL); + + add_channels (props, rate, channels); + ret_caps = gst_caps_new ("alsacaps", "audio/raw", props); } else { - /* we'll just have to assume int, i don't feel like checking */ - if (this->format == SND_PCM_FORMAT_MU_LAW) { - law = 1; - width = 8; sign = FALSE; endianness = 0; - } else if (this->format == SND_PCM_FORMAT_A_LAW) { - law = 2; - width = 8; sign = FALSE; endianness = 0; - } else { - law = 0; - if (this->format == SND_PCM_FORMAT_S8) { - width = 8; sign = TRUE; endianness = 0; - } else if (this->format == SND_PCM_FORMAT_U8) { - width = 8; sign = FALSE; endianness = 0; - } else if (this->format == SND_PCM_FORMAT_S16_LE) { - width = 16; sign = TRUE; endianness = G_LITTLE_ENDIAN; - } else if (this->format == SND_PCM_FORMAT_S16_BE) { - width = 16; sign = TRUE; endianness = G_BIG_ENDIAN; - } else if (this->format == SND_PCM_FORMAT_U16_LE) { - width = 16; sign = FALSE; endianness = G_LITTLE_ENDIAN; - } else if (this->format == SND_PCM_FORMAT_U16_BE) { - width = 16; sign = FALSE; endianness = G_BIG_ENDIAN; - } else if (this->format == SND_PCM_FORMAT_S24_LE) { - width = 24; sign = TRUE; endianness = G_LITTLE_ENDIAN; - } else if (this->format == SND_PCM_FORMAT_S24_BE) { - width = 24; sign = TRUE; endianness = G_BIG_ENDIAN; - } else if (this->format == SND_PCM_FORMAT_U24_LE) { - width = 24; sign = FALSE; endianness = G_LITTLE_ENDIAN; - } else if (this->format == SND_PCM_FORMAT_U24_BE) { - width = 24; sign = FALSE; endianness = G_BIG_ENDIAN; - } else if (this->format == SND_PCM_FORMAT_S32_LE) { - width = 32; sign = TRUE; endianness = G_LITTLE_ENDIAN; - } else if (this->format == SND_PCM_FORMAT_S32_BE) { - width = 32; sign = TRUE; endianness = G_BIG_ENDIAN; - } else if (this->format == SND_PCM_FORMAT_U32_LE) { - width = 32; sign = FALSE; endianness = G_LITTLE_ENDIAN; - } else if (this->format == SND_PCM_FORMAT_U32_BE) { - width = 32; sign = FALSE; endianness = G_BIG_ENDIAN; - } else { - g_error ("Unknown audio format %u", this->format); - return NULL; + int i; + GstProps *props; + + for (i = 0; i <= SND_PCM_FORMAT_LAST; i++) { + props = gst_alsa_get_props (i); + /* can be NULL, because not all alsa formats can be specified as caps */ + 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)); } } - depth = width; - props = gst_props_new ("format", GST_PROPS_STRING ("int"), - "rate", GST_PROPS_INT (this->rate), - "channels", GST_PROPS_INT ((this->data_interleaved ? this->channels : 1)), - "law", GST_PROPS_INT (law), - "endianness", GST_PROPS_INT (endianness), - "signed", GST_PROPS_BOOLEAN (sign), - "width", GST_PROPS_INT (width), - "depth", GST_PROPS_INT (depth), - NULL); } - return gst_caps_new ("alsasrc", "audio/raw", props); + return ret_caps; } /* Negotiates the caps, "borrowed" from gstosssink.c */ @@ -686,6 +662,8 @@ 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); @@ -698,25 +676,43 @@ gst_alsa_link (GstPad *pad, GstCaps *caps) 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: allow changing the format here */ + format = gst_alsa_get_format (caps); + DEBUG ("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, + "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++; + } + this->format = format; + this->rate = rate; + + need_mmap = this->mmap_open; - /* FIXME send out another caps if nego fails */ + /* 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; + /* FIXME send out another caps if nego fails */ - if (need_mmap && !gst_alsa_get_channel_addresses (this)) - return GST_PAD_LINK_REFUSED; + 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; - } + if (need_mmap && !gst_alsa_get_channel_addresses (this)) + return GST_PAD_LINK_REFUSED; - return GST_PAD_LINK_REFUSED; + return GST_PAD_LINK_OK; } return GST_PAD_LINK_DELAYED; @@ -874,6 +870,7 @@ gst_alsa_xrun_recovery (GstAlsa *this) gst_alsa_start_audio (this); } +/* FIXME: DO NOT RECORD WITH ALSA, IT DOESN'T WORK, IT ONLY COMPILES */ static gboolean gst_alsa_src_process (GstAlsa *this, snd_pcm_uframes_t frames) { @@ -888,11 +885,11 @@ gst_alsa_src_process (GstAlsa *this, snd_pcm_uframes_t frames) /* let's get on the caps-setting merry-go-round! */ if (!caps_set) { - caps = gst_alsa_caps (this); + caps = gst_alsa_caps (this->format, this->rate, this->channels); l = this->pads; while (l) { if (gst_pad_try_set_caps (GST_ALSA_PAD (l)->pad, caps) <= 0) { - g_print ("setting caps (%p) in source (%p) failed\n", caps, this); + DEBUG ("setting caps (%p) in source (%p) failed\n", caps, this); sleep (1); return FALSE; } @@ -1032,7 +1029,7 @@ gst_alsa_sink_check_event (GstAlsa *this, GstAlsaPad *pad) g_warning ("GstAlsaSink: got an unknown event (Type: %d)", GST_EVENT_TYPE (event)); } } else { - /* the element at the top of the chain did not emit an eos event. */ + /* the element at the top of the chain did not emit an event. */ g_assert_not_reached (); } } @@ -1051,7 +1048,7 @@ gst_alsa_set_params (GstAlsa *this) 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", + DEBUG ("Preparing channel: %s %dHz, %d channels\n", snd_pcm_format_name (this->format), this->rate, this->channels); snd_pcm_hw_params_alloca (&hw_param); @@ -1091,14 +1088,16 @@ gst_alsa_set_params (GstAlsa *this) 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; + if (this->channels > 0) { + 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); } - this->channels = snd_pcm_hw_params_get_channels (hw_param); - if (this->rate) { + if (this->rate > 0) { ret = snd_pcm_hw_params_set_rate (this->handle, hw_param, this->rate, 0); if (ret < 0) { g_warning ("error setting rate (%d): %s", this->rate, snd_strerror (ret)); @@ -1139,10 +1138,12 @@ gst_alsa_set_params (GstAlsa *this) return FALSE; } + /* FIXME: this needs to be done for a src, but not for a sink if (!this->rate) this->rate = snd_pcm_hw_params_get_rate (hw_param, 0); - if (!this->format) + if (this->format == SND_PCM_UNKNOWN) this->format = snd_pcm_hw_params_get_format (hw_param); + */ if (!this->period_count) this->period_count = snd_pcm_hw_params_get_periods (hw_param, 0); if (!this->period_frames) @@ -1217,11 +1218,12 @@ gst_alsa_open_audio (GstAlsa *this) gint ret; g_assert (this != NULL); + g_assert (this->handle == NULL); if (this->handle) gst_alsa_close_audio (this); - g_print ("Opening alsa device \"%s\" for %s...\n", this->device, + DEBUG ("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);