alsa: fix supported format detection
authorTim-Philipp Müller <tim@centricular.net>
Wed, 17 Oct 2012 23:04:06 +0000 (00:04 +0100)
committerTim-Philipp Müller <tim@centricular.net>
Thu, 18 Oct 2012 10:03:07 +0000 (11:03 +0100)
The format probing code was assuming there'd be one caps
structure for each separate width/depth combination like
we did in 0.10 all over the place: for one, we'd query
unsigned/signed formats together for the same width/height,
and we'd add the entire current structure to the probed
caps when we find a format is supported. Now that we have
all raw formats in a single structure, this is all not going
to work so well any more. We added the entire structure with
all possible formats to the caps if we support just one format.

Fix probing so that we only return the list of actually
supported raw audio formats (with native endianness) from
get_caps().

ext/alsa/gstalsa.c
ext/alsa/gstalsasink.c

index d7e98ae..a6fc0c8 100644 (file)
@@ -82,71 +82,86 @@ max_rate_err:
   }
 }
 
-static const struct
+static snd_pcm_format_t
+gst_alsa_get_pcm_format (GstAudioFormat fmt)
 {
-  const int width;
-  const int depth;
-  const int sformat;
-  const int uformat;
-} pcmformats[] = {
-  {
-  8, 8, SND_PCM_FORMAT_S8, SND_PCM_FORMAT_U8}, {
-  16, 16, SND_PCM_FORMAT_S16, SND_PCM_FORMAT_U16}, {
-  32, 24, SND_PCM_FORMAT_S24, SND_PCM_FORMAT_U24}, {
-#if (G_BYTE_ORDER == G_LITTLE_ENDIAN)   /* no endian-unspecific enum available */
-  24, 24, SND_PCM_FORMAT_S24_3LE, SND_PCM_FORMAT_U24_3LE}, {
-#else
-  24, 24, SND_PCM_FORMAT_S24_3BE, SND_PCM_FORMAT_U24_3BE}, {
-#endif
-  32, 32, SND_PCM_FORMAT_S32, SND_PCM_FORMAT_U32}
-};
+  switch (fmt) {
+    case GST_AUDIO_FORMAT_S8:
+      return SND_PCM_FORMAT_S8;
+    case GST_AUDIO_FORMAT_U8:
+      return SND_PCM_FORMAT_U8;
+      /* 16 bit */
+    case GST_AUDIO_FORMAT_S16LE:
+      return SND_PCM_FORMAT_S16_LE;
+    case GST_AUDIO_FORMAT_S16BE:
+      return SND_PCM_FORMAT_S16_BE;
+    case GST_AUDIO_FORMAT_U16LE:
+      return SND_PCM_FORMAT_U16_LE;
+    case GST_AUDIO_FORMAT_U16BE:
+      return SND_PCM_FORMAT_U16_BE;
+      /* 24 bit in low 3 bytes of 32 bits */
+    case GST_AUDIO_FORMAT_S24_32LE:
+      return SND_PCM_FORMAT_S24_LE;
+    case GST_AUDIO_FORMAT_S24_32BE:
+      return SND_PCM_FORMAT_S24_BE;
+    case GST_AUDIO_FORMAT_U24_32LE:
+      return SND_PCM_FORMAT_U24_LE;
+    case GST_AUDIO_FORMAT_U24_32BE:
+      return SND_PCM_FORMAT_U24_BE;
+      /* 24 bit in 3 bytes */
+    case GST_AUDIO_FORMAT_S24LE:
+      return SND_PCM_FORMAT_S24_3LE;
+    case GST_AUDIO_FORMAT_S24BE:
+      return SND_PCM_FORMAT_S24_3BE;
+    case GST_AUDIO_FORMAT_U24LE:
+      return SND_PCM_FORMAT_U24_3LE;
+    case GST_AUDIO_FORMAT_U24BE:
+      return SND_PCM_FORMAT_U24_3BE;
+      /* 32 bit */
+    case GST_AUDIO_FORMAT_S32LE:
+      return SND_PCM_FORMAT_S32_LE;
+    case GST_AUDIO_FORMAT_S32BE:
+      return SND_PCM_FORMAT_S32_BE;
+    case GST_AUDIO_FORMAT_U32LE:
+      return SND_PCM_FORMAT_U32_LE;
+    case GST_AUDIO_FORMAT_U32BE:
+      return SND_PCM_FORMAT_U32_BE;
+    default:
+      break;
+  }
+  return SND_PCM_FORMAT_UNKNOWN;
+}
 
-static GstCaps *
-add_format (const gchar * str, GstStructure * s, snd_pcm_format_mask_t * mask,
-    GstCaps * caps)
+static gboolean
+format_supported (const GValue * format_val, snd_pcm_format_mask_t * mask,
+    int endianness)
 {
-  GstStructure *scopy;
-  GstAudioFormat format;
   const GstAudioFormatInfo *finfo;
-  gint w, width = 0, depth = 0;
+  snd_pcm_format_t pcm_format;
+  GstAudioFormat format;
+
+  if (!G_VALUE_HOLDS_STRING (format_val))
+    return FALSE;
 
-  format = gst_audio_format_from_string (str);
+  format = gst_audio_format_from_string (g_value_get_string (format_val));
   if (format == GST_AUDIO_FORMAT_UNKNOWN)
-    return caps;
+    return FALSE;
 
   finfo = gst_audio_format_get_info (format);
 
-  width = GST_AUDIO_FORMAT_INFO_WIDTH (finfo);
-  depth = GST_AUDIO_FORMAT_INFO_DEPTH (finfo);
+  if (GST_AUDIO_FORMAT_INFO_ENDIANNESS (finfo) != endianness)
+    return FALSE;
 
-  for (w = 0; w < G_N_ELEMENTS (pcmformats); w++)
-    if (pcmformats[w].width == width && pcmformats[w].depth == depth)
-      break;
-  if (w == G_N_ELEMENTS (pcmformats))
-    return caps;                /* Unknown format */
-
-  if (snd_pcm_format_mask_test (mask, pcmformats[w].sformat) &&
-      snd_pcm_format_mask_test (mask, pcmformats[w].uformat)) {
-    scopy = gst_structure_copy (s);
-  } else if (snd_pcm_format_mask_test (mask, pcmformats[w].sformat)) {
-    scopy = gst_structure_copy (s);
-    /* FIXME, remove unsigned version */
-  } else if (snd_pcm_format_mask_test (mask, pcmformats[w].uformat)) {
-    scopy = gst_structure_copy (s);
-    /* FIXME, remove signed version */
-  } else {
-    scopy = NULL;
-  }
-  if (scopy) {
-    caps = gst_caps_merge_structure (caps, scopy);
-  }
-  return caps;
-}
+  pcm_format = gst_alsa_get_pcm_format (format);
+  if (pcm_format == SND_PCM_FORMAT_UNKNOWN)
+    return FALSE;
 
+  return snd_pcm_format_mask_test (mask, pcm_format);
+}
 
 static GstCaps *
 gst_alsa_detect_formats (GstObject * obj, snd_pcm_hw_params_t * hw_params,
-    GstCaps * in_caps)
+    GstCaps * in_caps, int endianness)
 {
   snd_pcm_format_mask_t *mask;
   GstStructure *s;
@@ -156,10 +171,11 @@ gst_alsa_detect_formats (GstObject * obj, snd_pcm_hw_params_t * hw_params,
   snd_pcm_format_mask_malloc (&mask);
   snd_pcm_hw_params_get_format_mask (hw_params, mask);
 
-  caps = gst_caps_new_empty ();
+  caps = NULL;
 
   for (i = 0; i < gst_caps_get_size (in_caps); ++i) {
     const GValue *format;
+    GValue list = G_VALUE_INIT;
 
     s = gst_caps_get_structure (in_caps, i);
     if (!gst_structure_has_name (s, "audio/x-raw")) {
@@ -171,6 +187,8 @@ gst_alsa_detect_formats (GstObject * obj, snd_pcm_hw_params_t * hw_params,
     if (format == NULL)
       continue;
 
+    g_value_init (&list, GST_TYPE_LIST);
+
     if (GST_VALUE_HOLDS_LIST (format)) {
       gint i, len;
 
@@ -179,13 +197,31 @@ gst_alsa_detect_formats (GstObject * obj, snd_pcm_hw_params_t * hw_params,
         const GValue *val;
 
         val = gst_value_list_get_value (format, i);
-        if (G_VALUE_HOLDS_STRING (val))
-          caps = add_format (g_value_get_string (val), s, mask, caps);
+        if (format_supported (val, mask, endianness))
+          gst_value_list_append_value (&list, val);
       }
     } else if (G_VALUE_HOLDS_STRING (format)) {
-      caps = add_format (g_value_get_string (format), s, mask, caps);
-    } else
-      continue;
+      if (format_supported (format, mask, endianness))
+        gst_value_list_append_value (&list, format);
+    }
+
+    if (gst_value_list_get_size (&list) > 1) {
+      if (caps == NULL)
+        caps = gst_caps_new_empty ();
+      s = gst_structure_copy (s);
+      gst_structure_take_value (s, "format", &list);
+      gst_caps_append_structure (caps, s);
+    } else if (gst_value_list_get_size (&list) == 1) {
+      if (caps == NULL)
+        caps = gst_caps_new_empty ();
+      format = gst_value_list_get_value (&list, 0);
+      s = gst_structure_copy (s);
+      gst_structure_set_value (s, "format", format);
+      gst_caps_append_structure (caps, s);
+      g_value_unset (&list);
+    } else {
+      g_value_unset (&list);
+    }
   }
 
   snd_pcm_format_mask_free (mask);
@@ -463,7 +499,7 @@ gst_alsa_probe_supported_formats (GstObject * obj, gchar * device,
 
   caps = gst_caps_copy (template_caps);
 
-  if (!(caps = gst_alsa_detect_formats (obj, hw_params, caps)))
+  if (!(caps = gst_alsa_detect_formats (obj, hw_params, caps, G_BYTE_ORDER)))
     goto subroutine_error;
 
   if (!(caps = gst_alsa_detect_rates (obj, hw_params, caps)))
index 3a7957c..0c71ac1 100644 (file)
@@ -286,12 +286,18 @@ gst_alsasink_getcaps (GstBaseSink * bsink, GstCaps * filter)
   }
 
   if (sink->cached_caps) {
-    GST_LOG_OBJECT (sink, "Returning cached caps");
-    if (filter)
-      return gst_caps_intersect_full (filter, sink->cached_caps,
+    if (filter) {
+      caps = gst_caps_intersect_full (filter, sink->cached_caps,
           GST_CAPS_INTERSECT_FIRST);
-    else
+      GST_LOG_OBJECT (sink, "Returning cached caps %" GST_PTR_FORMAT " with "
+          "filter %" GST_PTR_FORMAT " applied: %" GST_PTR_FORMAT,
+          sink->cached_caps, filter, caps);
+      return caps;
+    } else {
+      GST_LOG_OBJECT (sink, "Returning cached caps %" GST_PTR_FORMAT,
+          sink->cached_caps);
       return gst_caps_ref (sink->cached_caps);
+    }
   }
 
   element_class = GST_ELEMENT_GET_CLASS (sink);