sys/oss/gstosselement.c: Add code to handle rate probing (bug #120883)
authorDavid Schleef <ds@schleef.org>
Thu, 25 Mar 2004 02:43:48 +0000 (02:43 +0000)
committerDavid Schleef <ds@schleef.org>
Thu, 25 Mar 2004 02:43:48 +0000 (02:43 +0000)
Original commit message from CVS:
* sys/oss/gstosselement.c: (gst_osselement_sync_parms),
(gst_osselement_close_audio), (gst_osselement_probe_caps),
(gst_osselement_get_format_structure),
(gst_osselement_rate_probe_check), (gst_osselement_rate_add_range),
(gst_osselement_rate_check_rate), (gst_osselement_rate_add_rate),
(gst_osselement_rate_int_compare): Add code to handle rate probing
(bug #120883)
* sys/oss/gstosselement.h: same
* sys/oss/gstosssink.c: (gst_osssink_init), (gst_osssink_getcaps):
Use rate probing provided by osselement.
* sys/oss/gstosssrc.c: (gst_osssrc_init), (gst_osssrc_getcaps): same

ChangeLog
sys/oss/gstosselement.c
sys/oss/gstosselement.h
sys/oss/gstosssink.c
sys/oss/gstosssrc.c

index 4cf6f8e..dda4d5b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2004-03-24  David Schleef  <ds@schleef.org>
+
+       * sys/oss/gstosselement.c: (gst_osselement_sync_parms),
+       (gst_osselement_close_audio), (gst_osselement_probe_caps),
+       (gst_osselement_get_format_structure),
+       (gst_osselement_rate_probe_check), (gst_osselement_rate_add_range),
+       (gst_osselement_rate_check_rate), (gst_osselement_rate_add_rate),
+       (gst_osselement_rate_int_compare): Add code to handle rate probing
+       (bug #120883)
+       * sys/oss/gstosselement.h: same
+       * sys/oss/gstosssink.c: (gst_osssink_init), (gst_osssink_getcaps):
+       Use rate probing provided by osselement.
+       * sys/oss/gstosssrc.c: (gst_osssrc_init), (gst_osssrc_getcaps): same
+
 2004-03-24  Ronald Bultje  <rbultje@ronald.bitfreak.net>
 
        * ext/xvid/gstxvidenc.c: (gst_xvidenc_set_property),
index ad609af..78215d5 100644 (file)
@@ -642,7 +642,7 @@ gst_osselement_sync_parms (GstOssElement * oss)
       g_warning
           ("couldn't set the right number of channels (wanted %d, got %d), enjoy the tone difference",
           target_channels, oss->channels);
-    if (target_rate != oss->rate)
+    if (target_rate < oss->rate - 1 || target_rate > oss->rate + 1)
       g_warning
           ("couldn't set the right sample rate (wanted %d, got %d), enjoy the speed difference",
           target_rate, oss->rate);
@@ -806,6 +806,10 @@ static void
 gst_osselement_close_audio (GstOssElement * oss)
 {
   gst_ossmixer_free_list (oss);
+  if (oss->probed_caps) {
+    gst_caps_free (oss->probed_caps);
+    oss->probed_caps = NULL;
+  }
 
   if (oss->fd < 0)
     return;
@@ -967,3 +971,336 @@ gst_osselement_change_state (GstElement * element)
 
   return GST_STATE_SUCCESS;
 }
+
+
+/* rate probing code */
+
+
+#if 0
+#include <sys/soundcard.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <glib.h>
+#endif
+
+typedef struct _GstOssProbe GstOssProbe;
+struct _GstOssProbe
+{
+  int fd;
+  int format;
+  int n_channels;
+  GArray *rates;
+  int min;
+  int max;
+};
+
+typedef struct _GstOssRange GstOssRange;
+struct _GstOssRange
+{
+  int min;
+  int max;
+};
+
+static GstStructure *gst_osselement_get_format_structure (unsigned int
+    format_bit);
+static gboolean gst_osselement_rate_probe_check (GstOssProbe * probe);
+static int gst_osselement_rate_check_rate (GstOssProbe * probe, int irate);
+static void gst_osselement_rate_add_range (GQueue * queue, int min, int max);
+static void gst_osselement_rate_add_rate (GArray * array, int rate);
+static int gst_osselement_rate_int_compare (gconstpointer a, gconstpointer b);
+
+void
+gst_osselement_probe_caps (GstOssElement * oss)
+{
+  GstOssProbe *probe;
+  int i;
+  gboolean ret;
+  GstStructure *structure;
+  unsigned int format_bit;
+  unsigned int format_mask;
+  GstCaps *caps;
+
+  if (oss->probed_caps != NULL)
+    return;
+  if (oss->fd == -1)
+    return;
+
+  /* FIXME test make sure we're not currently playing */
+  /* FIXME test both mono and stereo */
+
+  format_mask = AFMT_U8 | AFMT_S16_LE | AFMT_S16_BE | AFMT_S8 |
+      AFMT_U16_LE | AFMT_U16_BE;
+  format_mask &= oss->caps;
+
+  caps = gst_caps_new_empty ();
+
+  /* assume that the most significant bit of format_mask is 0 */
+  for (format_bit = 1; format_bit < format_mask; format_bit <<= 1) {
+    if (format_bit & format_mask) {
+      GValue rate_value = { 0 };
+
+      probe = g_new0 (GstOssProbe, 1);
+      probe->fd = oss->fd;
+      probe->format = format_bit;
+      probe->n_channels = 2;
+
+      ret = gst_osselement_rate_probe_check (probe);
+      if (probe->min == -1 || probe->max == -1) {
+        g_array_free (probe->rates, TRUE);
+        g_free (probe);
+        continue;
+      }
+
+      if (ret) {
+        GValue value = { 0 };
+
+        g_array_sort (probe->rates, gst_osselement_rate_int_compare);
+
+        g_value_init (&rate_value, GST_TYPE_LIST);
+        g_value_init (&value, G_TYPE_INT);
+
+        for (i = 0; i < probe->rates->len; i++) {
+          g_value_set_int (&value, g_array_index (probe->rates, int, i));
+
+          gst_value_list_append_value (&rate_value, &value);
+        }
+
+        g_value_unset (&value);
+      } else {
+        /* one big range */
+        g_value_init (&rate_value, GST_TYPE_INT_RANGE);
+        gst_value_set_int_range (&rate_value, probe->min, probe->max);
+      }
+
+      g_array_free (probe->rates, TRUE);
+      g_free (probe);
+
+      structure = gst_osselement_get_format_structure (format_bit);
+      gst_structure_set (structure, "channels", GST_TYPE_INT_RANGE, 1, 2, NULL);
+      gst_structure_set_value (structure, "rate", &rate_value);
+      g_value_unset (&rate_value);
+
+      gst_caps_append_structure (caps, structure);
+    }
+  }
+
+  GST_DEBUG ("probed caps: %" GST_PTR_FORMAT, caps);
+  oss->probed_caps = caps;
+}
+
+static GstStructure *
+gst_osselement_get_format_structure (unsigned int format_bit)
+{
+  GstStructure *structure;
+  int endianness;
+  gboolean sign;
+  int width;
+
+  switch (format_bit) {
+    case AFMT_U8:
+      endianness = 0;
+      sign = FALSE;
+      width = 8;
+      break;
+    case AFMT_S16_LE:
+      endianness = G_LITTLE_ENDIAN;
+      sign = TRUE;
+      width = 16;
+      break;
+    case AFMT_S16_BE:
+      endianness = G_BIG_ENDIAN;
+      sign = TRUE;
+      width = 16;
+      break;
+    case AFMT_S8:
+      endianness = 0;
+      sign = TRUE;
+      width = 8;
+      break;
+    case AFMT_U16_LE:
+      endianness = G_LITTLE_ENDIAN;
+      sign = FALSE;
+      width = 16;
+      break;
+    case AFMT_U16_BE:
+      endianness = G_BIG_ENDIAN;
+      sign = FALSE;
+      width = 16;
+      break;
+    default:
+      g_assert_not_reached ();
+      return NULL;
+  }
+
+  structure = gst_structure_new ("audio/x-raw-int",
+      "width", G_TYPE_INT, width,
+      "depth", G_TYPE_INT, width, "signed", G_TYPE_BOOLEAN, sign, NULL);
+
+  if (endianness) {
+    gst_structure_set (structure, "endianness", G_TYPE_INT, endianness, NULL);
+  }
+
+  return structure;
+}
+
+static gboolean
+gst_osselement_rate_probe_check (GstOssProbe * probe)
+{
+  GstOssRange *range;
+  GQueue *ranges;
+  int exact_rates = 0;
+  gboolean checking_exact_rates = TRUE;
+  int n_checks = 0;
+  gboolean result = TRUE;
+
+  ranges = g_queue_new ();
+
+  probe->rates = g_array_new (FALSE, FALSE, sizeof (int));
+
+  probe->min = gst_osselement_rate_check_rate (probe, 1000);
+  n_checks++;
+  probe->max = gst_osselement_rate_check_rate (probe, 100000);
+  n_checks++;
+  if (probe->min == -1 || probe->max == -1) {
+    GST_DEBUG ("unexpected check_rate error");
+    return FALSE;
+  }
+  gst_osselement_rate_add_range (ranges, probe->min + 1, probe->max - 1);
+
+  while ((range = g_queue_pop_head (ranges))) {
+    int min1;
+    int max1;
+    int mid;
+    int mid_ret;
+
+    GST_DEBUG ("checking [%d,%d]\n", range->min, range->max);
+
+    mid = (range->min + range->max) / 2;
+    mid_ret = gst_osselement_rate_check_rate (probe, mid);
+    if (mid_ret == -1) {
+      /* FIXME ioctl returned an error.  do something */
+      GST_DEBUG ("unexpected check_rate error");
+    }
+    n_checks++;
+
+    if (mid == mid_ret && checking_exact_rates) {
+      int max_exact_matches = 20;
+
+      exact_rates++;
+      if (exact_rates > max_exact_matches) {
+        GST_DEBUG ("got %d exact rates, assuming all are exact\n",
+            max_exact_matches);
+        result = FALSE;
+        g_free (range);
+        break;
+      }
+    } else {
+      checking_exact_rates = FALSE;
+    }
+
+    /* Assume that the rate is arithmetically rounded to the nearest
+     * supported rate. */
+    if (mid == mid_ret) {
+      min1 = mid - 1;
+      max1 = mid + 1;
+    } else {
+      if (mid < mid_ret) {
+        min1 = mid - (mid_ret - mid);
+        max1 = mid_ret + 1;
+      } else {
+        min1 = mid_ret - 1;
+        max1 = mid + (mid - mid_ret);
+      }
+    }
+
+    gst_osselement_rate_add_range (ranges, range->min, min1);
+    gst_osselement_rate_add_range (ranges, max1, range->max);
+
+    g_free (range);
+  }
+
+  while ((range = g_queue_pop_head (ranges))) {
+    g_free (range);
+  }
+  g_queue_free (ranges);
+
+  return result;
+}
+
+static void
+gst_osselement_rate_add_range (GQueue * queue, int min, int max)
+{
+  if (min <= max) {
+    GstOssRange *range = g_new0 (GstOssRange, 1);
+
+    range->min = min;
+    range->max = max;
+
+    g_queue_push_tail (queue, range);
+    /* push_head also works, but has different probing behavior */
+    /*g_queue_push_head (queue, range); */
+  }
+}
+
+static int
+gst_osselement_rate_check_rate (GstOssProbe * probe, int irate)
+{
+  int rate;
+  int format;
+  int n_channels;
+  int ret;
+
+  rate = irate;
+  format = probe->format;
+  n_channels = probe->n_channels;
+
+  ret = ioctl (probe->fd, SNDCTL_DSP_SETFMT, &format);
+  if (ret < 0)
+    return -1;
+  ret = ioctl (probe->fd, SNDCTL_DSP_CHANNELS, &n_channels);
+  if (ret < 0)
+    return -1;
+  ret = ioctl (probe->fd, SNDCTL_DSP_SPEED, &rate);
+  if (ret < 0)
+    return -1;
+
+  GST_DEBUG ("rate %d -> %d\n", irate, rate);
+
+  if (rate == irate - 1 || rate == irate + 1) {
+    rate = irate;
+  }
+  gst_osselement_rate_add_rate (probe->rates, rate);
+  return rate;
+}
+
+static void
+gst_osselement_rate_add_rate (GArray * array, int rate)
+{
+  int i;
+  int val;
+
+  for (i = 0; i < array->len; i++) {
+    val = g_array_index (array, int, i);
+
+    if (val == rate)
+      return;
+  }
+  GST_DEBUG ("supported rate: %d\n", rate);
+  g_array_append_val (array, rate);
+}
+
+static int
+gst_osselement_rate_int_compare (gconstpointer a, gconstpointer b)
+{
+  const int *va = (const int *) a;
+  const int *vb = (const int *) b;
+
+  if (*va < *vb)
+    return -1;
+  if (*va > *vb)
+    return 1;
+  return 0;
+}
index 3ce84a2..9c30187 100644 (file)
@@ -73,6 +73,7 @@ struct _GstOssElement
   guint64       fragment_time;
   gint          fragment_size;
   GstOssOpenMode mode;
+  GstCaps       *probed_caps;
 
   /* stats bytes per *second* */
   guint                 bps;
@@ -118,6 +119,7 @@ gboolean    gst_osselement_convert          (GstOssElement *oss,
                                                 gint64         src_value,
                                                 GstFormat     *dest_format,
                                                 gint64        *dest_value);
+void            gst_osselement_probe_caps       (GstOssElement *oss);
 
 G_END_DECLS
 
index 9d06b06..28a8c44 100644 (file)
@@ -59,6 +59,7 @@ static gboolean gst_osssink_sink_query (GstPad * pad, GstQueryType type,
     GstFormat * format, gint64 * value);
 
 static GstCaps *gst_osssink_sink_fixate (GstPad * pad, const GstCaps * caps);
+static GstCaps *gst_osssink_getcaps (GstPad * pad);
 static GstPadLinkReturn gst_osssink_sinkconnect (GstPad * pad,
     const GstCaps * caps);
 
@@ -96,7 +97,7 @@ GST_STATIC_PAD_TEMPLATE ("sink",
         "signed = (boolean) { TRUE, FALSE }, "
         "width = (int) { 8, 16 }, "
         "depth = (int) { 8, 16 }, "
-        "rate = (int) [ 1000, 48000 ], " "channels = (int) [ 1, 2 ]")
+        "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]")
     );
 
 static GstElementClass *parent_class = NULL;
@@ -202,6 +203,7 @@ gst_osssink_init (GstOssSink * osssink)
       (&osssink_sink_factory), "sink");
   gst_element_add_pad (GST_ELEMENT (osssink), osssink->sinkpad);
   gst_pad_set_link_function (osssink->sinkpad, gst_osssink_sinkconnect);
+  gst_pad_set_getcaps_function (osssink->sinkpad, gst_osssink_getcaps);
   gst_pad_set_fixate_function (osssink->sinkpad, gst_osssink_sink_fixate);
   gst_pad_set_convert_function (osssink->sinkpad, gst_osssink_convert);
   gst_pad_set_query_function (osssink->sinkpad, gst_osssink_sink_query);
@@ -256,6 +258,23 @@ gst_osssink_sink_fixate (GstPad * pad, const GstCaps * caps)
   return NULL;
 }
 
+static GstCaps *
+gst_osssink_getcaps (GstPad * pad)
+{
+  GstOssSink *osssink = GST_OSSSINK (gst_pad_get_parent (pad));
+  GstCaps *caps;
+
+  gst_osselement_probe_caps (GST_OSSELEMENT (osssink));
+
+  if (GST_OSSELEMENT (osssink)->probed_caps == NULL) {
+    caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
+  } else {
+    caps = gst_caps_copy (GST_OSSELEMENT (osssink)->probed_caps);
+  }
+
+  return caps;
+}
+
 static GstPadLinkReturn
 gst_osssink_sinkconnect (GstPad * pad, const GstCaps * caps)
 {
index b35dbfc..1ff3ee4 100644 (file)
@@ -66,7 +66,7 @@ static GstStaticPadTemplate osssrc_src_factory = GST_STATIC_PAD_TEMPLATE ("src",
         "signed = (boolean) { TRUE, FALSE }, "
         "width = (int) { 8, 16 }, "
         "depth = (int) { 8, 16 }, "
-        "rate = (int) [ 1000, 48000 ], " "channels = (int) [ 1, 2 ]")
+        "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]")
     );
 
 static void gst_osssrc_base_init (gpointer g_class);
@@ -76,6 +76,7 @@ static void gst_osssrc_dispose (GObject * object);
 
 static GstPadLinkReturn gst_osssrc_srcconnect (GstPad * pad,
     const GstCaps * caps);
+static GstCaps *gst_osssrc_getcaps (GstPad * pad);
 static const GstFormat *gst_osssrc_get_formats (GstPad * pad);
 static gboolean gst_osssrc_convert (GstPad * pad,
     GstFormat src_format, gint64 src_value,
@@ -176,6 +177,7 @@ gst_osssrc_init (GstOssSrc * osssrc)
       gst_pad_new_from_template (gst_static_pad_template_get
       (&osssrc_src_factory), "src");
   gst_pad_set_get_function (osssrc->srcpad, gst_osssrc_get);
+  gst_pad_set_getcaps_function (osssrc->srcpad, gst_osssrc_getcaps);
   gst_pad_set_link_function (osssrc->srcpad, gst_osssrc_srcconnect);
   gst_pad_set_convert_function (osssrc->srcpad, gst_osssrc_convert);
   gst_pad_set_formats_function (osssrc->srcpad, gst_osssrc_get_formats);
@@ -184,7 +186,6 @@ gst_osssrc_init (GstOssSrc * osssrc)
   gst_pad_set_query_function (osssrc->srcpad, gst_osssrc_src_query);
   gst_pad_set_query_type_function (osssrc->srcpad, gst_osssrc_get_query_types);
 
-
   gst_element_add_pad (GST_ELEMENT (osssrc), osssrc->srcpad);
 
   osssrc->buffersize = 4096;
@@ -208,6 +209,25 @@ gst_osssrc_dispose (GObject * object)
   G_OBJECT_CLASS (parent_class)->dispose (object);
 }
 
+static GstCaps *
+gst_osssrc_getcaps (GstPad * pad)
+{
+  GstOssSrc *src;
+  GstCaps *caps;
+
+  src = GST_OSSSRC (gst_pad_get_parent (pad));
+
+  gst_osselement_probe_caps (GST_OSSELEMENT (src));
+
+  if (GST_OSSELEMENT (src)->probed_caps == NULL) {
+    caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
+  } else {
+    caps = gst_caps_copy (GST_OSSELEMENT (src)->probed_caps);
+  }
+
+  return caps;
+}
+
 static GstPadLinkReturn
 gst_osssrc_srcconnect (GstPad * pad, const GstCaps * caps)
 {