ext/alsa/gstalsa.c: handle min <= max correctly
authorBenjamin Otte <otte@gnome.org>
Wed, 9 Jun 2004 17:06:40 +0000 (17:06 +0000)
committerBenjamin Otte <otte@gnome.org>
Wed, 9 Jun 2004 17:06:40 +0000 (17:06 +0000)
Original commit message from CVS:
* ext/alsa/gstalsa.c: (add_channels):
handle min <= max correctly
* ext/alsa/gstalsa.c: (gst_alsa_fixate_to_mimetype),
(gst_alsa_fixate_field_nearest_int), (gst_alsa_fixate):
add fixation functions so we fixate correctly. No preferring of alaw
anymore because it's the first structure.
* ext/alsa/gstalsa.h:
* ext/alsa/gstalsa.c: (gst_alsa_sw_params_dump),
(gst_alsa_hw_params_dump):
add functions to ease debugging in alsalib
* ext/alsa/gstalsa.c: (gst_alsa_probe_hw_params),
(gst_alsa_set_hw_params), (gst_alsa_set_sw_params),
(gst_alsa_start_audio):
only specify hw params if we really setup a format (fixes #134007 -
or at least works around it)

ChangeLog
ext/alsa/gstalsa.c
ext/alsa/gstalsa.h

index dcc50c96777170cee0a3ef437b45a69c3f2c19ce..949800076c35bbbf2f6bfa8b9a35fb8696793fa7 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2004-06-09  Benjamin Otte  <otte@gnome.org>
+
+       * ext/alsa/gstalsa.c: (add_channels):
+         handle min <= max correctly
+       * ext/alsa/gstalsa.c: (gst_alsa_fixate_to_mimetype), 
+       (gst_alsa_fixate_field_nearest_int), (gst_alsa_fixate):
+         add fixation functions so we fixate correctly. No preferring of alaw
+         anymore because it's the first structure.
+       * ext/alsa/gstalsa.h:
+       * ext/alsa/gstalsa.c: (gst_alsa_sw_params_dump),
+       (gst_alsa_hw_params_dump):
+         add functions to ease debugging in alsalib
+       * ext/alsa/gstalsa.c: (gst_alsa_probe_hw_params),
+       (gst_alsa_set_hw_params), (gst_alsa_set_sw_params),
+       (gst_alsa_start_audio):
+         only specify hw params if we really setup a format (fixes #134007 -
+         or at least works around it)
+
 2004-06-09  Wim Taymans  <wim@fluendo.com>
 
        * ext/ogg/gstoggmux.c: (gst_ogg_mux_init),
index 336233c943855674291577e47016c8c3a92fdd42..96ae070fab2bdfd4235af296dfb0e637df08e629 100644 (file)
 #include "gstalsaclock.h"
 #include "gstalsamixer.h"
 
+#define ALSA_DEBUG_FLUSH(this) G_STMT_START{ \
+  gchar *__str; \
+  ssize_t __size; \
+  __size = snd_output_buffer_string (this->out, &__str); \
+  if (__size > 0) { \
+    GST_DEBUG_OBJECT (this, "%*s", __size, __str); \
+    if (snd_output_flush (this->out) != 0) \
+      GST_ERROR_OBJECT (this, "error flushing output buffer"); \
+  } \
+}G_STMT_END
+
 /* GObject functions */
 static void gst_alsa_class_init (gpointer g_class, gpointer class_data);
 static void gst_alsa_init (GstAlsa * this);
@@ -692,9 +703,20 @@ add_channels (GstStructure * structure, gint min_rate, gint max_rate,
     min_rate = GST_ALSA_MIN_RATE;
     max_rate = GST_ALSA_MAX_RATE;
   }
-  if (max_rate < 0) {
+  if (max_rate < 0 || min_rate == max_rate) {
     gst_structure_set (structure, "rate", G_TYPE_INT, min_rate, NULL);
   } else {
+    /* just to be sure */
+    if (min_rate > max_rate) {
+      gint temp;
+
+      GST_ERROR
+          ("minimum rate > maximum rate (%d > %d), please fix your soundcard drivers",
+          min_rate, max_rate);
+      temp = min_rate;
+      min_rate = max_rate;
+      max_rate = temp;
+    }
     gst_structure_set (structure, "rate", GST_TYPE_INT_RANGE, min_rate,
         max_rate, NULL);
   }
@@ -702,9 +724,20 @@ add_channels (GstStructure * structure, gint min_rate, gint max_rate,
     min_channels = 1;
     max_channels = GST_ALSA_MAX_CHANNELS;
   }
-  if (max_channels < 0) {
+  if (max_channels < 0 || min_channels == max_channels) {
     gst_structure_set (structure, "channels", G_TYPE_INT, min_channels, NULL);
   } else {
+    /* just to be sure */
+    if (min_channels > max_channels) {
+      gint temp;
+
+      GST_ERROR
+          ("minimum channels > maximum channels (%d > %d), please fix your soundcard drivers",
+          min_channels, max_channels);
+      temp = min_channels;
+      min_channels = max_channels;
+      max_channels = temp;
+    }
     gst_structure_set (structure, "channels", GST_TYPE_INT_RANGE,
         min_channels, max_channels, NULL);
   }
@@ -828,37 +861,118 @@ gst_alsa_get_caps (GstPad * pad)
   }
 }
 
-GstCaps *
-gst_alsa_fixate (GstPad * pad, const GstCaps * caps)
+static GstCaps *
+gst_alsa_fixate_to_mimetype (const GstCaps * caps, const gchar * mime)
 {
-  GstCaps *newcaps;
-  GstStructure *structure;
-
-  newcaps =
-      gst_caps_new_full (gst_structure_copy (gst_caps_get_structure (caps, 0)),
-      NULL);
-  structure = gst_caps_get_structure (newcaps, 0);
+  GstCaps *try, *result;
 
-  if (gst_caps_structure_fixate_field_nearest_int (structure, "rate", 44100)) {
-    return newcaps;
+  try = gst_caps_new_simple (mime, NULL);
+  result = gst_caps_intersect (try, caps);
+  gst_caps_free (try);
+  if (gst_caps_is_empty (result)) {
+    gst_caps_free (result);
+    return NULL;
   }
-  if (gst_caps_structure_fixate_field_nearest_int (structure, "channels", 2)) {
-    return newcaps;
+  if (gst_caps_is_subset (caps, result)) {
+    /* we didn't reduce caps */
+    gst_caps_free (result);
+    return NULL;
   }
-  if (strcmp (gst_structure_get_name (structure), "audio/x-raw-int") == 0) {
-    if (gst_caps_structure_fixate_field_nearest_int (structure, "depth", 16)) {
-      return newcaps;
-    }
-    if (gst_caps_structure_fixate_field_nearest_int (structure, "width", 16)) {
-      return newcaps;
+  return result;
+}
+
+static GstCaps *
+gst_alsa_fixate_field_nearest_int (const GstCaps * caps,
+    const gchar * field_name, gint target)
+{
+  guint i;
+  GstCaps *result;
+  GstCaps *smaller = gst_caps_new_empty ();
+  GstCaps *equal = gst_caps_new_empty ();
+  GstCaps *bigger = gst_caps_new_empty ();
+
+  /* works like this: we fixate every structure and put them into one of those 
+   * caps depending on what we fixated to. We then return the best caps that is 
+   * not empty in the following order: equal, bigger, smaller 
+   * We also make sure the caps were really reduced.
+   */
+  for (i = 0; i < gst_caps_get_size (caps); i++) {
+    gint fixated_to;
+    GstStructure *copy = gst_structure_copy (gst_caps_get_structure (caps, i));
+
+    gst_caps_structure_fixate_field_nearest_int (copy, field_name, target);
+    if (gst_structure_get_int (copy, field_name, &fixated_to)) {
+      if (fixated_to == target) {
+        gst_caps_append_structure (equal, copy);
+      } else if (fixated_to > target) {
+        gst_caps_append_structure (bigger, copy);
+      } else {
+        gst_caps_append_structure (smaller, copy);
+      }
+    } else {
+      /* FIXME: what do we do here? Add to all or throw an error? */
+      g_return_val_if_reached (NULL);
     }
+  }
+  if (!gst_caps_is_empty (equal)) {
+    gst_caps_free (bigger);
+    gst_caps_free (smaller);
+    result = equal;
   } else {
-    if (gst_caps_structure_fixate_field_nearest_int (structure, "width", 32)) {
-      return newcaps;
+    gst_caps_free (equal);
+    if (!gst_caps_is_empty (bigger)) {
+      gst_caps_free (smaller);
+      result = bigger;
+    } else {
+      gst_caps_free (bigger);
+      if (gst_caps_is_empty (smaller)) {
+        gst_caps_free (smaller);
+        return NULL;
+      }
+      result = smaller;
     }
   }
+  if (gst_caps_is_subset (caps, result)) {
+    /* we didn't reduce caps */
+    gst_caps_free (result);
+    return NULL;
+  }
+  return result;
+}
 
-  gst_caps_free (newcaps);
+GstCaps *
+gst_alsa_fixate (GstPad * pad, const GstCaps * caps)
+{
+  GstCaps *result;
+  const gchar *mime;
+
+  if ((result = gst_alsa_fixate_to_mimetype (caps, "audio/x-raw-int")))
+    return result;
+  if ((result = gst_alsa_fixate_to_mimetype (caps, "audio/x-raw-float")))
+    return result;
+  if ((result = gst_alsa_fixate_to_mimetype (caps, "audio/x-alaw")))
+    return result;
+  if ((result = gst_alsa_fixate_to_mimetype (caps, "audio/x-mulaw")))
+    return result;
+
+  /* now we know there's only one mimetype in the caps */
+  /* FIXME: I should check this to be really sure I didn't mess up somewhere */
+
+  if ((result = gst_alsa_fixate_field_nearest_int (caps, "rate", 44100)))
+    return result;
+  if ((result = gst_alsa_fixate_field_nearest_int (caps, "channels", 2)))
+    return result;
+
+  mime = gst_structure_get_name (gst_caps_get_structure (caps, 0));
+  if (g_str_equal (mime, "audio/x-raw-int")) {
+    if ((result = gst_alsa_fixate_field_nearest_int (caps, "width", 16)))
+      return result;
+    if ((result = gst_alsa_fixate_field_nearest_int (caps, "depth", 16)))
+      return result;
+  } else if (g_str_equal (mime, "audio/x-raw-float")) {
+    if ((result = gst_alsa_fixate_field_nearest_int (caps, "width", 32)))
+      return result;
+  }
 
   return NULL;
 }
@@ -1229,6 +1343,20 @@ gst_alsa_open_audio (GstAlsa * this)
   return TRUE;
 }
 
+void
+gst_alsa_sw_params_dump (GstAlsa * this, snd_pcm_sw_params_t * sw_params)
+{
+  snd_pcm_sw_params_dump (sw_params, this->out);
+  ALSA_DEBUG_FLUSH (this);
+}
+
+void
+gst_alsa_hw_params_dump (GstAlsa * this, snd_pcm_hw_params_t * hw_params)
+{
+  snd_pcm_hw_params_dump (hw_params, this->out);
+  ALSA_DEBUG_FLUSH (this);
+}
+
 /* if someone finds an easy way to merge this with _set_hw_params, go ahead */
 static gboolean
 gst_alsa_probe_hw_params (GstAlsa * this, GstAlsaFormat * format)
@@ -1246,8 +1374,7 @@ gst_alsa_probe_hw_params (GstAlsa * this, GstAlsaFormat * format)
   snd_pcm_hw_params_alloca (&hw_params);
   SIMPLE_ERROR_CHECK (snd_pcm_hw_params_any (this->handle, hw_params));
 
-  snd_pcm_hw_params_dump (hw_params, this->out);
-  ALSA_DEBUG_FLUSH (this);
+  gst_alsa_hw_params_dump (this, hw_params);
 
   if (GST_ELEMENT (this)->numpads == 1) {
     SIMPLE_ERROR_CHECK (snd_pcm_hw_params_set_access (this->handle,
@@ -1291,34 +1418,27 @@ gst_alsa_set_hw_params (GstAlsa * this)
   g_return_val_if_fail (this != NULL, FALSE);
   g_return_val_if_fail (this->handle != NULL, FALSE);
 
-  if (this->format) {
-    GST_INFO ("Preparing format: %s %dHz, %d channels",
-        snd_pcm_format_name (this->format->format), this->format->rate,
-        this->format->channels);
-  } else {
-    GST_INFO ("Preparing format: (none)");
-  }
-
   snd_pcm_hw_params_alloca (&hw_params);
   ERROR_CHECK (snd_pcm_hw_params_any (this->handle, hw_params),
       "Broken configuration for this PCM: %s");
 
-  snd_pcm_hw_params_dump (hw_params, this->out);
-  ALSA_DEBUG_FLUSH (this);
+  if (this->format) {
+    GST_INFO ("Preparing format: %s %dHz, %d channels",
+        snd_pcm_format_name (this->format->format), this->format->rate,
+        this->format->channels);
 
-  if (GST_ELEMENT (this)->numpads == 1) {
-    ERROR_CHECK (snd_pcm_hw_params_set_access (this->handle, hw_params, this->
-            mmap ? SND_PCM_ACCESS_MMAP_INTERLEAVED :
-            SND_PCM_ACCESS_RW_INTERLEAVED),
-        "This plugin does not support your harware: %s");
-  } else {
-    ERROR_CHECK (snd_pcm_hw_params_set_access (this->handle, hw_params, this->
-            mmap ? SND_PCM_ACCESS_MMAP_NONINTERLEAVED :
-            SND_PCM_ACCESS_RW_NONINTERLEAVED),
-        "This plugin does not support your harware: %s");
-  }
+    if (GST_ELEMENT (this)->numpads == 1) {
+      ERROR_CHECK (snd_pcm_hw_params_set_access (this->handle, hw_params, this->
+              mmap ? SND_PCM_ACCESS_MMAP_INTERLEAVED :
+              SND_PCM_ACCESS_RW_INTERLEAVED),
+          "This plugin does not support your harware: %s");
+    } else {
+      ERROR_CHECK (snd_pcm_hw_params_set_access (this->handle, hw_params, this->
+              mmap ? SND_PCM_ACCESS_MMAP_NONINTERLEAVED :
+              SND_PCM_ACCESS_RW_NONINTERLEAVED),
+          "This plugin does not support your harware: %s");
+    }
 
-  if (this->format) {
     ERROR_CHECK (snd_pcm_hw_params_set_format (this->handle, hw_params,
             this->format->format), "Sample format (%s) not available: %s",
         snd_pcm_format_name (this->format->format));
@@ -1328,14 +1448,18 @@ gst_alsa_set_hw_params (GstAlsa * this)
     ERROR_CHECK (snd_pcm_hw_params_set_rate (this->handle, hw_params,
             this->format->rate, 0), "error setting rate (%d): %s",
         this->format->rate);
+    ERROR_CHECK (snd_pcm_hw_params_set_periods_near (this->handle, hw_params,
+            &this->period_count, 0), "error setting period count to %u: %s",
+        (guint) this->period_count);
+    ERROR_CHECK (snd_pcm_hw_params_set_period_size_near (this->handle,
+            hw_params, &this->period_size, 0),
+        "error setting period size to %u frames: %s",
+        (guint) this->period_size);
+  } else {
+    GST_INFO_OBJECT (this, "Preparing format: (none)");
   }
+  gst_alsa_hw_params_dump (this, hw_params);
 
-  ERROR_CHECK (snd_pcm_hw_params_set_periods_near (this->handle, hw_params,
-          &this->period_count, 0), "error setting period count to %u: %s",
-      (guint) this->period_count);
-  ERROR_CHECK (snd_pcm_hw_params_set_period_size_near (this->handle, hw_params,
-          &this->period_size, 0), "error setting period size to %u frames: %s",
-      (guint) this->period_size);
 
   ERROR_CHECK (snd_pcm_hw_params (this->handle, hw_params),
       "Could not set hardware parameters: %s");
@@ -1362,12 +1486,16 @@ gst_alsa_set_sw_params (GstAlsa * this)
 {
   snd_pcm_sw_params_t *sw_params;
 
+  if (!this->format) {
+    GST_LOG_OBJECT (this, "not setting sw params, we're not negotiated yet");
+    return TRUE;
+  }
+
   snd_pcm_sw_params_alloca (&sw_params);
   ERROR_CHECK (snd_pcm_sw_params_current (this->handle, sw_params),
       "Could not get current software parameters: %s");
 
-  snd_pcm_sw_params_dump (sw_params, this->out);
-  ALSA_DEBUG_FLUSH (this);
+  gst_alsa_sw_params_dump (this, sw_params);
 
   ERROR_CHECK (snd_pcm_sw_params_set_silence_size (this->handle, sw_params, 0),
       "could not set silence size: %s");
index 8d99faa28aa73f574e7a74ecdacf828d9e171963..b499b668df64f28f8f291c79d74202c150bb25df 100644 (file)
@@ -35,17 +35,6 @@ GST_DEBUG_CATEGORY_EXTERN (alsa_debug);
 #define GST_CAT_DEFAULT alsa_debug
 
 
-#define ALSA_DEBUG_FLUSH(this) G_STMT_START{ \
-  gchar *__str; \
-  ssize_t __size; \
-  __size = snd_output_buffer_string (this->out, &__str); \
-  if (__size > 0) { \
-    GST_INFO_OBJECT (this, "%*s", __size, __str); \
-    if (snd_output_flush (this->out) != 0) \
-      GST_ERROR_OBJECT (this, "error flushing output buffer"); \
-  } \
-}G_STMT_END
-
 /* error checking for standard alsa functions */
 /* NOTE: these functions require a GObject *this and can only be used in 
    functions that return TRUE on success and FALSE on error */
@@ -222,6 +211,13 @@ inline GstClockTime                gst_alsa_bytes_to_timestamp     (GstAlsa *              this,
 inline guint                   gst_alsa_timestamp_to_bytes     (GstAlsa *              this,
                                                                 GstClockTime           time);
 
+/* debugging functions (useful in gdb) - require running with --gst-debug=alsa:4 or better */
+void                           gst_alsa_sw_params_dump         (GstAlsa *              this, 
+                                                                snd_pcm_sw_params_t *  sw_params);
+void                           gst_alsa_hw_params_dump         (GstAlsa *              this, 
+                                                                snd_pcm_hw_params_t *  hw_params);
+
+
 G_END_DECLS
 
 #endif /* __GST_ALSA_H__ */