next big bunch of stuff:
authorBenjamin Otte <otte@gnome.org>
Thu, 15 May 2003 16:32:22 +0000 (16:32 +0000)
committerBenjamin Otte <otte@gnome.org>
Thu, 15 May 2003 16:32:22 +0000 (16:32 +0000)
Original commit message from CVS:
next big bunch of stuff:
- proper caps setting in alsasrc
- query / conversion functions
WARNING: Alsa 0.9.2 is heavily borked wrt recording - expect segfaults

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

index 267cae9..633af04 100644 (file)
@@ -123,7 +123,7 @@ static void                 gst_alsa_src_loop               (GstElement *           element);
 static void                    gst_alsa_src_flush              (GstAlsaSrc *           src);
 static GstElementStateReturn   gst_alsa_src_change_state       (GstElement *           element);
 
-/* GStreamer functions for pads and state changing */
+/* GStreamer functions for pads, queries, conversions and state changing */
 static GstPad *                        gst_alsa_request_new_pad        (GstElement *           element, 
                                                                 GstPadTemplate *       templ,
                                                                 const gchar *          name);
@@ -135,9 +135,32 @@ static GstCaps *           gst_alsa_caps                   (snd_pcm_format_t       format,
                                                                 gint                   rate,
                                                                 gint                   channels);
 static GstElementStateReturn   gst_alsa_change_state           (GstElement *           element);
+static const GstFormat *       gst_alsa_get_formats            (GstPad *               pad);
+static gboolean                gst_alsa_convert                (GstAlsa *              this,
+                                                                GstFormat              src_format, 
+                                                                gint64                 src_value,
+                                                                GstFormat *            dest_format,
+                                                                gint64 *               dest_value);
+static gboolean                gst_alsa_pad_convert            (GstPad *               pad,
+                                                                GstFormat              src_format, 
+                                                                gint64                 src_value,
+                                                                GstFormat *            dest_format,
+                                                                gint64 *               dest_value);
+static const GstQueryType *    gst_alsa_get_query_types        (GstPad *               pad);
+static gboolean                gst_alsa_query_func             (GstElement *           element,
+                                                                GstQueryType           type, 
+                                                                GstFormat *            format,
+                                                                gint64 *               value);
+static gboolean                gst_alsa_query                  (GstElement *           element,
+                                                                GstQueryType           type, 
+                                                                GstFormat *            format,
+                                                                gint64 *               value);
+static gboolean                gst_alsa_pad_query              (GstPad *               pad,
+                                                                GstQueryType           type,
+                                                                GstFormat *            format,
+                                                                gint64 *               value);
 
 /* audio processing functions */
-static void                    gst_alsa_src_loop               (GstElement *           element);
 static void                    gst_alsa_xrun_recovery          (GstAlsa *              this);
 inline static snd_pcm_sframes_t        gst_alsa_update_avail           (GstAlsa *              this);
 inline static gboolean         gst_alsa_pcm_wait               (GstAlsa *              this);
@@ -275,10 +298,11 @@ gst_alsa_class_init (GstAlsaClass *klass)
     g_param_spec_uint64 ("max-discont", "Maximum Discontinuity", "GStreamer timeunits before the timestamp syncing starts dropping/insertting samples",
    /* rounding errors */ 1000, GST_SECOND, GST_ALSA_DEFAULT_DISCONT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
 
-  element_class->change_state    = gst_alsa_change_state;
-  element_class->request_new_pad = gst_alsa_request_new_pad;
-  element_class->set_clock      = gst_alsa_set_clock;
-  element_class->get_clock      = gst_alsa_get_clock;
+  element_class->change_state    = GST_DEBUG_FUNCPTR (gst_alsa_change_state);
+  element_class->query          = GST_DEBUG_FUNCPTR (gst_alsa_query);
+  element_class->request_new_pad = GST_DEBUG_FUNCPTR (gst_alsa_request_new_pad);
+  element_class->set_clock      = GST_DEBUG_FUNCPTR (gst_alsa_set_clock);
+  element_class->get_clock      = GST_DEBUG_FUNCPTR (gst_alsa_get_clock);
 }
 
 static void
@@ -970,35 +994,97 @@ gst_alsa_src_read (GstAlsa *this, snd_pcm_sframes_t *avail)
   }
   return err;
 }
-static gboolean
-gst_alsa_src_set_caps (GstAlsaSrc *src)
+static inline gint
+gst_alsa_adjust_rate (gint rate, gboolean aggressive)
 {
+  static gint rates[] = { 96000, 48000, 44100, 22050, 8000 };
   gint i;
-  GstCaps *caps;
+
+  if (aggressive)
+    return rate;
+  
+  for (i = 0; i < G_N_ELEMENTS (rates); i--) {
+    if (rate > rates[i])
+      return rates[i];
+  }
+
+  return 0;
+}
+static gboolean
+gst_alsa_src_set_caps (GstAlsaSrc *src, gboolean aggressive)
+{
+  GstCaps *all_caps, *caps, *walk;
+  gint channels, min_channels, max_channels;
+  gint rate, min_rate, max_rate;
+  gint i, law, endian, width, depth;
+  gboolean sign;
   GstAlsa *this = GST_ALSA (src);
 
-  if (!(this->format = g_new (GstAlsaFormat, 1)))
-    return FALSE;
-  /* FIXME: make this do proper caps nego */
-  this->format->format = SND_PCM_FORMAT_S16;
-  this->format->rate = 44100;
-  this->format->channels = GST_ELEMENT (src)->numpads;
-  GST_DEBUG (GST_CAT_NEGOTIATION, "starting caps negotiation on alsa src");
-  caps = gst_alsa_caps (this->format->format, this->format->rate, this->format->channels);
+  all_caps = gst_alsa_get_caps (this->pad[0], NULL);
+  if (all_caps == NULL) return FALSE;
+  /* now intersect this with all caps of the peers... */
   for (i = 0; i < GST_ELEMENT (src)->numpads; i++) {
-    if (gst_pad_try_set_caps (this->pad[i], caps) <= 0) {
-      GST_DEBUG (GST_CAT_NEGOTIATION, "setting caps (%p) in alsasrc (%p) on pad %d failed", caps, this, i);
+    all_caps = gst_caps_intersect (all_caps, gst_pad_get_caps (this->pad[i]));
+    if (all_caps == NULL) {
+      GST_DEBUG (GST_CAT_NEGOTIATION, "No compatible caps found in alsasrc (%s)", GST_ELEMENT_NAME (this));
       return FALSE;
     }
   }
   
-  if (GST_FLAG_IS_SET (this, GST_ALSA_RUNNING)) gst_alsa_stop_audio (this);
-  if (!gst_alsa_start_audio (this)) {
-    /* FIXME: Try the next format */
-    return FALSE;
+  /* construct caps */
+  caps = GST_CAPS_NEW ("alsasrc caps", "audio/raw",
+                       "format",      GST_PROPS_STRING ("int"),
+                      "law",         GST_PROPS_INT (0),
+                      "endianness",  GST_PROPS_INT (G_BYTE_ORDER),
+                      "signed",      GST_PROPS_BOOLEAN (TRUE),
+                      "width",       GST_PROPS_INT (16),
+                      "depth",       GST_PROPS_INT (16),
+                      "rate",        GST_PROPS_INT (44100),
+                      "channels",    GST_PROPS_INT (1));
+  gst_caps_ref (caps);
+  gst_caps_sink (caps);
+
+  /* now try to find the best match */
+  walk = all_caps;
+  while (walk) {
+    gst_caps_get (walk, "law", &law, "signed", &sign, "width", &width, "depth", &depth, NULL);
+    if (gst_caps_has_property (walk, "endianness")) {
+      gst_caps_get_int (walk, "endianness", &endian);
+    } else {
+      endian = G_BYTE_ORDER;
+    }
+    gst_caps_set (caps, "law", GST_PROPS_INT (law));
+    gst_caps_set (caps, "endianness", GST_PROPS_INT (endian));
+    gst_caps_set (caps, "width", GST_PROPS_INT (width));
+    gst_caps_set (caps, "depth", GST_PROPS_INT (depth));
+    gst_caps_set (caps, "signed", GST_PROPS_BOOLEAN (sign));
+  
+    gst_props_entry_get_int_range (gst_props_get_entry (GST_CAPS_PROPERTIES (walk), "rate"), &min_rate, &max_rate);
+    gst_props_entry_get_int_range (gst_props_get_entry (GST_CAPS_PROPERTIES (walk), "channels"), &min_channels, &max_channels);
+    for (rate = max_rate;; rate--) {
+      if ((rate = gst_alsa_adjust_rate (rate, aggressive)) < min_rate)
+       break;    
+      gst_caps_set (caps, "rate", GST_PROPS_INT (rate));
+      for (channels = aggressive ? max_channels : MIN (max_channels, 2); channels >= min_channels; channels--) {
+        gst_caps_set (caps, "channels", GST_PROPS_INT (channels));
+        g_print ("trying new caps: law %d, %ssigned, endianness: %d, width %d, depth %d, channels %d, rate %d\n",
+                 law, sign ? "" : "un", endian, width, depth, channels, rate);
+        if (gst_pad_try_set_caps (this->pad[0], caps))
+          gst_alsa_link (this->pad[0], caps);
+      
+        if (this->format) {      
+         /* try to set caps here */
+         return TRUE;
+       }
+      }
+    }
+    walk = GST_CAPS_NEXT (walk);
   }
 
-  return TRUE;
+  if (!aggressive)
+    return gst_alsa_src_set_caps (src, TRUE);
+
+  return FALSE;
 }
 /* we transmit buffers of period_size frames */
 static void
@@ -1011,7 +1097,7 @@ gst_alsa_src_loop (GstElement *element)
 
   /* set the caps on all pads */
   if (!this->format) {
-    if (!gst_alsa_src_set_caps (src)) {
+    if (!gst_alsa_src_set_caps (src, FALSE)) {
       gst_element_error (element, "Could not set caps");
       return;
     }
@@ -1102,7 +1188,7 @@ gst_alsa_src_change_state (GstElement *element)
   return GST_STATE_SUCCESS;
 }
 
-/*** GSTREAMER PAD / STATE FUNCTIONS*******************************************/
+/*** GSTREAMER PAD / QUERY / CONVERSION / STATE FUNCTIONS *********************/
 
 static GstPad *
 gst_alsa_request_new_pad (GstElement *element, GstPadTemplate *templ,
@@ -1146,7 +1232,11 @@ found_channel:
   gst_pad_set_link_function (this->pad[channel], gst_alsa_link);
   gst_pad_set_getcaps_function (this->pad[channel], gst_alsa_get_caps);
   gst_element_add_pad (GST_ELEMENT (this), this->pad[channel]);
-
+  gst_pad_set_convert_function (this->pad[channel], gst_alsa_pad_convert);
+  gst_pad_set_query_function (this->pad[channel], gst_alsa_pad_query);
+  gst_pad_set_query_type_function (this->pad[channel], gst_alsa_get_query_types);
+  gst_pad_set_formats_function (this->pad[channel], gst_alsa_get_formats);
+  
   return this->pad[channel];
 }
 
@@ -1583,6 +1673,138 @@ gst_alsa_change_state (GstElement *element)
 
   return GST_STATE_SUCCESS;
 }
+static const GstFormat *
+gst_alsa_get_formats (GstPad *pad)
+{
+  static const GstFormat formats[] = {
+    GST_FORMAT_TIME,
+    GST_FORMAT_UNITS,
+    GST_FORMAT_BYTES,
+    0
+  };
+  return formats;
+}
+static gboolean
+gst_alsa_pad_convert (GstPad *pad, GstFormat src_format, gint64 src_value,
+                     GstFormat *dest_format, gint64 *dest_value)
+{
+  return gst_alsa_convert (GST_ALSA (GST_PAD_PARENT (pad)), src_format, src_value, dest_format, dest_value);
+}
+static gboolean
+gst_alsa_convert (GstAlsa *this, GstFormat src_format, gint64 src_value,
+                 GstFormat *dest_format, gint64 *dest_value)
+{
+  gboolean res = TRUE;
+
+  if (src_format == *dest_format || (src_format == GST_FORMAT_UNITS && *dest_format == GST_FORMAT_DEFAULT)) {
+    *dest_format = GST_FORMAT_UNITS;
+    *dest_value = src_value;
+    return TRUE;
+  }
+  if (this->format == NULL)
+    return FALSE;
+
+  switch (src_format) {
+    case GST_FORMAT_BYTES:
+      switch (*dest_format) {
+        case GST_FORMAT_DEFAULT:
+          *dest_format = GST_FORMAT_UNITS;
+        case GST_FORMAT_TIME:
+         *dest_value = gst_alsa_bytes_to_timestamp (this, (guint) src_value);
+          break;
+        case GST_FORMAT_UNITS:
+         *dest_value = gst_alsa_samples_to_timestamp (this, (guint) src_value);
+          break;
+        default:
+          res = FALSE;
+         break;
+      }
+      break;
+    case GST_FORMAT_TIME:
+      switch (*dest_format) {
+        case GST_FORMAT_DEFAULT:
+          *dest_format = GST_FORMAT_UNITS;
+        case GST_FORMAT_UNITS:
+         *dest_value = gst_alsa_timestamp_to_samples (this, (GstClockTime) src_value);
+          break;
+        case GST_FORMAT_BYTES:
+         *dest_value = gst_alsa_timestamp_to_bytes (this, (GstClockTime) src_value);
+          break;
+        default:
+          res = FALSE;
+         break;
+      }
+      break;
+    case GST_FORMAT_UNITS:
+      switch (*dest_format) {    
+        case GST_FORMAT_TIME:
+         *dest_value = gst_alsa_samples_to_timestamp (this, (guint) src_value);
+          break;
+        case GST_FORMAT_BYTES:
+         *dest_value = gst_alsa_samples_to_bytes (this, (guint) src_value);
+          break;
+        case GST_FORMAT_DEFAULT:
+          g_assert_not_reached ();
+         /* fall through */
+        default:
+          res = FALSE;
+         break;
+      }
+      break;
+    default:
+      res = FALSE;
+  }
+
+  return res;
+}
+static const GstQueryType * 
+gst_alsa_get_query_types (GstPad *pad)
+{
+  static const GstQueryType query_types[] = {
+    GST_QUERY_LATENCY,
+    GST_QUERY_POSITION,
+    0,
+  };
+  return query_types;
+}
+static gboolean
+gst_alsa_query_func (GstElement *element, GstQueryType type, GstFormat *format, gint64 *value)
+{
+  gboolean res = FALSE;
+  GstAlsa *this = GST_ALSA (element);
+
+  switch (type) {
+    case GST_QUERY_LATENCY: {
+      snd_pcm_sframes_t delay;
+      ERROR_CHECK (snd_pcm_delay (this->handle, &delay), "Error getting delay: %s");
+      res = gst_alsa_convert (this, GST_FORMAT_UNITS, (gint64) delay, format, value); 
+      break;
+    }
+    case GST_QUERY_POSITION:
+      res = gst_alsa_convert (this, GST_FORMAT_UNITS, this->transmitted, format, value);
+      break;
+    default:
+      break;
+  }
+
+  return res;
+}
+static gboolean
+gst_alsa_query (GstElement *element, GstQueryType type, GstFormat *format, gint64 *value)
+{
+  return gst_alsa_pad_query (GST_ALSA (element)->pad[0], type, format, value);
+}
+static gboolean
+gst_alsa_pad_query (GstPad *pad, GstQueryType type, GstFormat *format, gint64 *value)
+{
+  if (gst_alsa_query_func (GST_PAD_PARENT (pad), type, format, value))
+    return TRUE;
+
+  if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK && gst_pad_query (gst_pad_get_peer (pad), type, format, value))
+    return TRUE;
+  
+  return FALSE;
+}
 
 /*** AUDIO PROCESSING *********************************************************/
 
@@ -1630,8 +1852,6 @@ gst_alsa_pcm_wait (GstAlsa *this)
 inline static gboolean
 gst_alsa_start (GstAlsa *this)
 {
-  //gint avail;
-
   GST_DEBUG (GST_CAT_PLUGIN_INFO, "Setting state to RUNNING");
 
   switch (snd_pcm_state(this->handle)) {
@@ -1659,9 +1879,6 @@ gst_alsa_start (GstAlsa *this)
       g_assert_not_reached ();
       break;
   }
-/*  avail = (gint) gst_alsa_update_avail (this);
-  if (avail < 0)
-    return FALSE;*/
   gst_alsa_clock_start (this->clock);
   return TRUE;
 }
@@ -1882,7 +2099,6 @@ gst_alsa_start_audio (GstAlsa *this)
   GST_FLAG_SET (this, GST_ALSA_RUNNING);
   return TRUE;
 }
-
 static gboolean
 gst_alsa_drain_audio (GstAlsa *this)
 {
index ce8f7c8..15a7399 100644 (file)
    should be big enough to allow smoothing errors on different video formats */
 #define GST_ALSA_DEFAULT_DISCONT (GST_SECOND / 10)
 
-#define GST_ALSA(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, GST_TYPE_ALSA, GstAlsa)
-#define GST_ALSA_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, GST_TYPE_ALSA, GstAlsaClass)
-#define GST_ALSA_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_ALSA, GstAlsaClass))
-#define GST_IS_ALSA(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, GST_TYPE_ALSA)
-#define GST_IS_ALSA_CLASS(klass) G_TYPE_CHECK_CLASS_TYPE(klass, GST_TYPE_ALSA)
-#define GST_TYPE_ALSA gst_alsa_get_type()
-
-#define GST_ALSA_SINK(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, GST_TYPE_ALSA_SINK, GstAlsaSink)
-#define GST_ALSA_SINK_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, GST_TYPE_ALSA_SINK, GstAlsaSinkClass)
-#define GST_IS_ALSA_SINK(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, GST_TYPE_ALSA_SINK)
-#define GST_IS_ALSA_SINK_CLASS(klass) G_TYPE_CHECK_CLASS_TYPE(klass, GST_TYPE_ALSA_SINK)
-#define GST_TYPE_ALSA_SINK gst_alsa_sink_get_type()
-
-#define GST_ALSA_SRC(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, GST_TYPE_ALSA_SRC, GstAlsaSrc)
-#define GST_ALSA_SRC_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, GST_TYPE_ALSA_SRC, GstAlsaSrcClass)
-#define GST_IS_ALSA_SRC(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, GST_TYPE_ALSA_SRC)
-#define GST_IS_ALSA_SRC_CLASS(klass) G_TYPE_CHECK_CLASS_TYPE(klass, GST_TYPE_ALSA_SRC)
-#define GST_TYPE_ALSA_SRC gst_alsa_src_get_type()
-
-#define GST_ALSA_CLOCK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ALSA_CLOCK,GstAlsaClock))
-#define GST_ALSA_CLOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ALSA_CLOCK,GstAlsaClockClass))
-#define GST_IS_ALSA_CLOCK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ALSA_CLOCK))
-#define GST_IS_ALSA_CLOCK_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ALSA_CLOCK))
-#define GST_TYPE_ALSA_CLOCK (gst_alsa_clock_get_type())
+#define GST_ALSA(obj)                  (G_TYPE_CHECK_INSTANCE_CAST(obj, GST_TYPE_ALSA, GstAlsa))
+#define GST_ALSA_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST(klass, GST_TYPE_ALSA, GstAlsaClass))
+#define GST_ALSA_GET_CLASS(obj)                (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_ALSA, GstAlsaClass))
+#define GST_IS_ALSA(obj)               (G_TYPE_CHECK_INSTANCE_TYPE(obj, GST_TYPE_ALSA))
+#define GST_IS_ALSA_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE(klass, GST_TYPE_ALSA))
+#define GST_TYPE_ALSA                  (gst_alsa_get_type())
+
+#define GST_ALSA_SINK(obj)             (G_TYPE_CHECK_INSTANCE_CAST(obj, GST_TYPE_ALSA_SINK, GstAlsaSink))
+#define GST_ALSA_SINK_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST(klass, GST_TYPE_ALSA_SINK, GstAlsaSinkClass))
+#define GST_IS_ALSA_SINK(obj)          (G_TYPE_CHECK_INSTANCE_TYPE(obj, GST_TYPE_ALSA_SINK))
+#define GST_IS_ALSA_SINK_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE(klass, GST_TYPE_ALSA_SINK))
+#define GST_TYPE_ALSA_SINK             (gst_alsa_sink_get_type())
+
+#define GST_ALSA_SRC(obj)              (G_TYPE_CHECK_INSTANCE_CAST(obj, GST_TYPE_ALSA_SRC, GstAlsaSrc))
+#define GST_ALSA_SRC_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST(klass, GST_TYPE_ALSA_SRC, GstAlsaSrcClass))
+#define GST_IS_ALSA_SRC(obj)           (G_TYPE_CHECK_INSTANCE_TYPE(obj, GST_TYPE_ALSA_SRC))
+#define GST_IS_ALSA_SRC_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE(klass, GST_TYPE_ALSA_SRC))
+#define GST_TYPE_ALSA_SRC              (gst_alsa_src_get_type())
+
+#define GST_ALSA_CLOCK(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ALSA_CLOCK,GstAlsaClock))
+#define GST_ALSA_CLOCK_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ALSA_CLOCK,GstAlsaClockClass))
+#define GST_IS_ALSA_CLOCK(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ALSA_CLOCK))
+#define GST_IS_ALSA_CLOCK_CLASS(obj)   (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ALSA_CLOCK))
+#define GST_TYPE_ALSA_CLOCK            (gst_alsa_clock_get_type())
 
 /* I would have preferred to avoid this variety of trickery, but without it i
  * can't tell whether I'm a source or a sink upon creation. */