ext/raw1394/: Implement GstPropertyProbe interface and add "device-name" property...
[platform/upstream/gst-plugins-good.git] / ext / esd / esdsink.c
index e719282..d46af07 100644 (file)
@@ -1,8 +1,10 @@
 /* GStreamer
+ * Copyright (C) <2005> Arwed v. Merkatz <v.merkatz@gmx.net>
+ *
+ * Roughly based on the gstreamer 0.8 esdsink plugin:
  * Copyright (C) <2001> Richard Boulton <richard-gst@tartarus.org>
  *
- * Based on example.c:
- * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ * esdsink.c: an EsounD audio sink
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
  * Boston, MA 02111-1307, USA.
  */
 
+/**
+ * SECTION:element-esdsink
+ * @see_also: #GstAlsaSink, #GstAutoAudioSink
+ *
+ * <refsect2>
+ * <para>
+ * This element outputs sound to an already-running Enlightened Sound Daemon
+ * (ESound Daemon, esd). Note that a sound daemon will never be auto-spawned
+ * through this element (regardless of the system configuration), since this
+ * is actively prevented by the element. If you must use esd, you need to
+ * make sure it is started automatically with your session or otherwise.
+ * </para>
+ * <para>
+ * TODO: insert some comments about how sucky esd is and that all the cool
+ * kids use pulseaudio or whatever these days.
+ * </para>
+ * <para>
+ * Simple example pipeline that plays an Ogg/Vorbis file via esd:
+ * <programlisting>
+ * gst-launch -v filesrc location=foo.ogg ! decodebin ! audioconvert ! audioresample ! esdsink
+ * </programlisting>
+ * </para>
+ * </refsect2>
+ */
+
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
+
 #include "esdsink.h"
 #include <esd.h>
 #include <unistd.h>
 #include <errno.h>
 
+#include <gst/gst-i18n-plugin.h>
+
+/* wtay: from my esd.h (debian unstable libesd0-dev 0.2.36-3) */
+#ifndef ESD_MAX_WRITE_SIZE
+#define ESD_MAX_WRITE_SIZE (21 * 4096)
+#endif
+
 GST_DEBUG_CATEGORY_EXTERN (esd_debug);
 #define GST_CAT_DEFAULT esd_debug
 
 /* elementfactory information */
-static GstElementDetails esdsink_details = {
-  "Esound audio sink",
-  "Sink/Audio",
-  "Plays audio to an esound server",
-  "Richard Boulton <richard-gst@tartarus.org>",
-};
+static const GstElementDetails esdsink_details =
+GST_ELEMENT_DETAILS ("Esound audio sink",
+    "Sink/Audio",
+    "Plays audio to an esound server",
+    "Arwed von Merkatz <v.merkatz@gmx.net>");
 
-/* Signals and args */
-enum {
-  /* FILL ME */
-  LAST_SIGNAL
-};
-
-enum {
-  ARG_0,
-  ARG_MUTE,
-  ARG_HOST,
-  ARG_SYNC,
-  ARG_FALLBACK,
+enum
+{
+  PROP_0,
+  PROP_HOST
 };
 
-static GstStaticPadTemplate sink_factory = 
-GST_STATIC_PAD_TEMPLATE (
-  "sink",
-  GST_PAD_SINK,
-  GST_PAD_ALWAYS,
-  GST_STATIC_CAPS (
-    "audio/x-raw-int, "
-      "endianness = (int) " G_STRINGIFY (G_BYTE_ORDER) ", "
-      "signed = (boolean) TRUE, "
-      "width = (int) 16, "
-      "depth = (int) 16, "
-      "rate = [ 8000, 96000 ], "
-      "channels = [ 1, 2 ]; "
-    "audio/x-raw-int, "
-      "signed = (boolean) FALSE, "
-      "width = (int) 8, "
-      "depth = (int) 8, "
-      "rate = [ 8000, 96000 ], "
-      "channels = [ 1, 2 ]"
-  )
-);
-
-static void                     gst_esdsink_base_init           (gpointer g_class);
-static void                    gst_esdsink_class_init          (gpointer g_class, gpointer class_data);
-static void                    gst_esdsink_init                (GTypeInstance *instance, gpointer g_class);
-
-static gboolean                        gst_esdsink_open_audio          (GstEsdsink *sink);
-static void                    gst_esdsink_close_audio         (GstEsdsink *sink);
-static GstElementStateReturn   gst_esdsink_change_state        (GstElement *element);
-static GstPadLinkReturn                gst_esdsink_link                (GstPad *pad, const GstCaps *caps);
-
-static GstClockTime            gst_esdsink_get_time            (GstClock *clock, gpointer data);
-static GstClock *              gst_esdsink_get_clock           (GstElement *element);
-static void                     gst_esdsink_set_clock           (GstElement *element, GstClock *clock);
-static void                    gst_esdsink_chain               (GstPad *pad, GstData *_data);
-
-static void                    gst_esdsink_set_property        (GObject *object, guint prop_id, 
-                                                                const GValue *value, GParamSpec *pspec);
-static void                    gst_esdsink_get_property        (GObject *object, guint prop_id, 
-                                                                GValue *value, GParamSpec *pspec);
-
-static GstElementClass *parent_class = NULL;
-/*static guint gst_esdsink_signals[LAST_SIGNAL] = { 0 }; */
-
-GType
-gst_esdsink_get_type (void)
-{
-  static GType esdsink_type = 0;
-
-  if (!esdsink_type) {
-    static const GTypeInfo esdsink_info = {
-      sizeof(GstEsdsinkClass),
-      gst_esdsink_base_init,
-      NULL,
-      gst_esdsink_class_init,
-      NULL,
-      NULL,
-      sizeof(GstEsdsink),
-      0,
-      gst_esdsink_init,
-    };
-    esdsink_type = g_type_register_static(GST_TYPE_ELEMENT, "GstEsdsink", &esdsink_info, 0);
-  }
-  return esdsink_type;
-}
+static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-raw-int, "
+        "endianness = (int) BYTE_ORDER, "
+        "signed = (boolean) TRUE, "
+        "width = (int) 16, "
+        "depth = (int) 16, "
+        "rate = (int) [ 1, MAX ], "
+        "channels = (int) [ 1, 2 ]; "
+        "audio/x-raw-int, "
+        "signed = (boolean) { true, false }, "
+        "width = (int) 8, "
+        "depth = (int) 8, "
+        "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]")
+    );
+
+static void gst_esdsink_finalize (GObject * object);
+
+static GstCaps *gst_esdsink_getcaps (GstBaseSink * bsink);
+
+static gboolean gst_esdsink_open (GstAudioSink * asink);
+static gboolean gst_esdsink_close (GstAudioSink * asink);
+static gboolean gst_esdsink_prepare (GstAudioSink * asink,
+    GstRingBufferSpec * spec);
+static gboolean gst_esdsink_unprepare (GstAudioSink * asink);
+static guint gst_esdsink_write (GstAudioSink * asink, gpointer data,
+    guint length);
+static guint gst_esdsink_delay (GstAudioSink * asink);
+static void gst_esdsink_reset (GstAudioSink * asink);
+
+static void gst_esdsink_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_esdsink_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+
+GST_BOILERPLATE (GstEsdSink, gst_esdsink, GstAudioSink, GST_TYPE_AUDIO_SINK);
 
 static void
 gst_esdsink_base_init (gpointer g_class)
 {
   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
 
-  gst_element_class_add_pad_template (element_class, 
+  gst_element_class_add_pad_template (element_class,
       gst_static_pad_template_get (&sink_factory));
   gst_element_class_set_details (element_class, &esdsink_details);
 }
 
 static void
-gst_esdsink_class_init (gpointer g_class, gpointer class_data)
+gst_esdsink_class_init (GstEsdSinkClass * klass)
 {
-  GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
-  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
-
-  parent_class = g_type_class_peek_parent (g_class);
-
-  g_object_class_install_property(gobject_class, ARG_MUTE,
-    g_param_spec_boolean("mute","mute","mute",
-                         TRUE,G_PARAM_READWRITE)); /* CHECKME */
-  g_object_class_install_property(gobject_class, ARG_HOST,
-    g_param_spec_string("host","host","host",
-                        NULL, G_PARAM_READWRITE)); /* CHECKME */
-  g_object_class_install_property(gobject_class, ARG_SYNC,
-    g_param_spec_boolean("sync","sync","Synchronize output to clock",
-                         FALSE,G_PARAM_READWRITE));
-#if 0
-  /* This option is disabled because it is dumb in GStreamer's architecture. */
-  g_object_class_install_property(gobject_class, ARG_FALLBACK,
-    g_param_spec_boolean("fallback","fallback","Fall back to using OSS if Esound daemon is not present",
-                         FALSE,G_PARAM_READWRITE));
-#endif
+  GObjectClass *gobject_class;
+  GstBaseSinkClass *gstbasesink_class;
+  GstBaseAudioSinkClass *gstbaseaudiosink_class;
+  GstAudioSinkClass *gstaudiosink_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstbasesink_class = (GstBaseSinkClass *) klass;
+  gstbaseaudiosink_class = (GstBaseAudioSinkClass *) klass;
+  gstaudiosink_class = (GstAudioSinkClass *) klass;
+
+  parent_class = g_type_class_peek_parent (klass);
+
+  gobject_class->finalize = gst_esdsink_finalize;
+
+  gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_esdsink_getcaps);
+
+  gstaudiosink_class->open = GST_DEBUG_FUNCPTR (gst_esdsink_open);
+  gstaudiosink_class->close = GST_DEBUG_FUNCPTR (gst_esdsink_close);
+  gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR (gst_esdsink_prepare);
+  gstaudiosink_class->unprepare = GST_DEBUG_FUNCPTR (gst_esdsink_unprepare);
+  gstaudiosink_class->write = GST_DEBUG_FUNCPTR (gst_esdsink_write);
+  gstaudiosink_class->delay = GST_DEBUG_FUNCPTR (gst_esdsink_delay);
+  gstaudiosink_class->reset = GST_DEBUG_FUNCPTR (gst_esdsink_reset);
 
   gobject_class->set_property = gst_esdsink_set_property;
   gobject_class->get_property = gst_esdsink_get_property;
 
-  gstelement_class->change_state = gst_esdsink_change_state;
-  gstelement_class->set_clock    = gst_esdsink_set_clock;
-  gstelement_class->get_clock    = gst_esdsink_get_clock;
+  /* default value is filled in the _init method */
+  g_object_class_install_property (gobject_class, PROP_HOST,
+      g_param_spec_string ("host", "Host",
+          "The host running the esound daemon", NULL, G_PARAM_READWRITE));
 }
 
 static void
-gst_esdsink_init(GTypeInstance *instance, gpointer g_class)
+gst_esdsink_init (GstEsdSink * esdsink, GstEsdSinkClass * klass)
 {
-  GstEsdsink *esdsink = GST_ESDSINK (instance);
-  
-  esdsink->sinkpad = gst_pad_new_from_template (
-      gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (instance), "sink"), 
-      "sink");
-  gst_element_add_pad(GST_ELEMENT(esdsink), esdsink->sinkpad);
-  gst_pad_set_chain_function(esdsink->sinkpad, GST_DEBUG_FUNCPTR(gst_esdsink_chain));
-  gst_pad_set_link_function(esdsink->sinkpad, gst_esdsink_link);
-
-  GST_FLAG_SET (esdsink, GST_ELEMENT_EVENT_AWARE);
-
-  esdsink->mute = FALSE;
   esdsink->fd = -1;
-  /* FIXME: get default from somewhere better than just putting them inline. */
-  esdsink->negotiated = FALSE;
-  esdsink->format = -1;
-  esdsink->depth = -1;
-  esdsink->channels = -1;
-  esdsink->frequency = -1;
-  esdsink->host = getenv ("ESPEAKER");
-  esdsink->provided_clock = gst_audio_clock_new("esdclock", gst_esdsink_get_time, esdsink);
-  gst_object_set_parent(GST_OBJECT(esdsink->provided_clock), GST_OBJECT(esdsink));
-  esdsink->sync = FALSE;
-  esdsink->fallback = FALSE;
+  esdsink->ctrl_fd = -1;
+  esdsink->host = g_strdup (g_getenv ("ESPEAKER"));
 }
 
-static GstPadLinkReturn
-gst_esdsink_link (GstPad *pad, const GstCaps *caps)
+static void
+gst_esdsink_finalize (GObject * object)
 {
-  GstEsdsink *esdsink;
-  GstStructure *structure;
-
-  esdsink = GST_ESDSINK (gst_pad_get_parent (pad));
-
-  structure = gst_caps_get_structure (caps, 0);
-  gst_structure_get_int (structure, "depth", &esdsink->depth);
-  gst_structure_get_int (structure, "channels", &esdsink->channels);
-  gst_structure_get_int (structure, "rate", &esdsink->frequency);
-
-  esdsink->bytes_per_sample = esdsink->channels * (esdsink->depth/8);
+  GstEsdSink *esdsink = GST_ESDSINK (object);
 
-  gst_esdsink_close_audio (esdsink);
-  if (gst_esdsink_open_audio (esdsink)) {
-    esdsink->negotiated = TRUE;
-    return GST_PAD_LINK_OK;
-  }
-  /* FIXME: is it supposed to be correct to have closed audio when caps nego 
-     failed? */
-
-  return GST_PAD_LINK_REFUSED;
-}
+  gst_caps_replace (&esdsink->cur_caps, NULL);
+  g_free (esdsink->host);
 
-static int
-gst_esdsink_get_latency (GstEsdsink *esdsink)
-{
-  /* esd_get_latency() doesn't actually work.  So we return a
-   * fake value */
-  return 44100/2;
-#if 0
-  return esd_get_latency (esdsink->fd);
-#endif
+  G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
-static GstClockTime
-gst_esdsink_get_time (GstClock *clock, gpointer data)
+static GstCaps *
+gst_esdsink_getcaps (GstBaseSink * bsink)
 {
-  GstEsdsink *esdsink = GST_ESDSINK(data);
-  GstClockTime res;
-
-  res = (esdsink->handled - gst_esdsink_get_latency(esdsink))
-    * GST_SECOND / esdsink->frequency;
+  GstEsdSink *esdsink;
 
-  return res;
-}
+  esdsink = GST_ESDSINK (bsink);
 
-static GstClock *
-gst_esdsink_get_clock (GstElement *element)
-{
-  GstEsdsink *esdsink;
+  /* no fd, we're done with the template caps */
+  if (esdsink->ctrl_fd < 0 || esdsink->cur_caps == NULL) {
+    GST_LOG_OBJECT (esdsink, "getcaps called, returning template caps");
+    return NULL;
+  }
 
-  esdsink = GST_ESDSINK (element);
+  GST_LOG_OBJECT (esdsink, "returning %" GST_PTR_FORMAT, esdsink->cur_caps);
 
-  return GST_CLOCK(esdsink->provided_clock);
+  return gst_caps_ref (esdsink->cur_caps);
 }
 
-static void
-gst_esdsink_set_clock (GstElement *element, GstClock *clock)
+static gboolean
+gst_esdsink_open (GstAudioSink * asink)
 {
-  GstEsdsink *esdsink;
-
-  esdsink = GST_ESDSINK (element);
+  esd_server_info_t *server_info;
+  GstPadTemplate *pad_template;
+  GstEsdSink *esdsink;
+  gchar *saved_env;
+  gint i;
 
-  esdsink->clock = clock;
-}
+  esdsink = GST_ESDSINK (asink);
 
-static void
-gst_esdsink_chain (GstPad *pad, GstData *_data)
-{
-  GstBuffer *buf = GST_BUFFER (_data);
-  GstEsdsink *esdsink;
+  GST_DEBUG_OBJECT (esdsink, "open");
 
-  esdsink = GST_ESDSINK (gst_pad_get_parent (pad));
+  /* ensure libesd doesn't auto-spawn a sound daemon if none is running yet */
+  saved_env = g_strdup (g_getenv ("ESD_NO_SPAWN"));
+  g_setenv ("ESD_NO_SPAWN", "1", TRUE);
 
-  if (!esdsink->negotiated) {
-    gst_element_error (esdsink, CORE, NEGOTIATION, NULL,
-                       ("element wasn't negotiated before chain function"));
-    goto done;
-  }
+  /* now try to connect to any existing/running sound daemons */
+  esdsink->ctrl_fd = esd_open_sound (esdsink->host);
 
-  if (GST_IS_EVENT(buf)){
-    GstEvent *event = GST_EVENT(buf);
-
-    switch(GST_EVENT_TYPE(event)){
-      case GST_EVENT_EOS:
-       gst_audio_clock_set_active (GST_AUDIO_CLOCK (esdsink->provided_clock),
-           FALSE);
-       gst_pad_event_default (pad, event);
-       return;
-      case GST_EVENT_DISCONTINUOUS:
-      {
-       gint64 value;
-
-       if (gst_event_discont_get_value (event, GST_FORMAT_TIME, &value)) {
-         gst_element_set_time (GST_ELEMENT (esdsink), value);
-         esdsink->handled = 0;
-       }
-       esdsink->resync = TRUE;
-       break;
-      }
-      default:
-       gst_pad_event_default(pad, event);
-       return;
-    }
-    gst_event_unref(event);
-    return;
+  /* and restore the previous state */
+  if (saved_env != NULL) {
+    g_setenv ("ESD_NO_SPAWN", saved_env, TRUE);
+  } else {
+    g_unsetenv ("ESD_NO_SPAWN");
   }
+  g_free (saved_env);
 
-  if (GST_BUFFER_DATA (buf) != NULL) {
-    if (!esdsink->mute && esdsink->fd >= 0) {
-      guchar *data = GST_BUFFER_DATA (buf);
-      gint size = GST_BUFFER_SIZE (buf);
-      gint to_write = 0;
+  if (esdsink->ctrl_fd < 0)
+    goto couldnt_connect;
 
-      if (esdsink->clock){
-       gint delay = 0;
-       gint64 queued;
+  /* get server info */
+  server_info = esd_get_server_info (esdsink->ctrl_fd);
+  if (!server_info)
+    goto no_server_info;
 
-       delay = gst_esdsink_get_latency (esdsink);
-       queued = delay * GST_SECOND / esdsink->frequency;
+  GST_INFO_OBJECT (esdsink, "got server info rate: %i", server_info->rate);
 
-       if (esdsink->resync && esdsink->sync) {
-         gst_element_wait (GST_ELEMENT (esdsink), GST_BUFFER_TIMESTAMP (buf) - queued); 
+  pad_template = gst_static_pad_template_get (&sink_factory);
+  esdsink->cur_caps = gst_caps_copy (gst_pad_template_get_caps (pad_template));
+  gst_object_unref (pad_template);
 
-       }else{
-         to_write = size;
-       }
-      }
+  for (i = 0; i < esdsink->cur_caps->structs->len; i++) {
+    GstStructure *s;
 
-      GST_DEBUG ("esdsink: fd=%d data=%p size=%d",
-                esdsink->fd, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
-      while (to_write > 0){
-       int done;
+    s = gst_caps_get_structure (esdsink->cur_caps, i);
+    gst_structure_set (s, "rate", G_TYPE_INT, server_info->rate, NULL);
+  }
 
-        done = write (esdsink->fd, data, to_write);
+  esd_free_server_info (server_info);
 
-       if(done < 0){
-         if(errno==EINTR){
-           goto done;
-         }
-         g_assert_not_reached();
-       }
+  GST_INFO_OBJECT (esdsink, "server caps: %" GST_PTR_FORMAT, esdsink->cur_caps);
 
-       to_write -= done;
-       data += done;
-       esdsink->handled += done / esdsink->bytes_per_sample;
-      }
+  return TRUE;
 
-    }
+  /* ERRORS */
+couldnt_connect:
+  {
+    GST_ELEMENT_ERROR (esdsink, RESOURCE, OPEN_WRITE,
+        (_("Could not establish connection to sound server")),
+        ("can't open connection to esound server"));
+    return FALSE;
+  }
+no_server_info:
+  {
+    GST_ELEMENT_ERROR (esdsink, RESOURCE, OPEN_WRITE,
+        (_("Failed to query sound server capabilities")),
+        ("couldn't get server info!"));
+    return FALSE;
   }
+}
+
+static gboolean
+gst_esdsink_close (GstAudioSink * asink)
+{
+  GstEsdSink *esdsink = GST_ESDSINK (asink);
+
+  GST_DEBUG_OBJECT (esdsink, "close");
 
-  gst_audio_clock_update_time ((GstAudioClock *)esdsink->provided_clock,
-      GST_BUFFER_TIMESTAMP (buf));
+  gst_caps_replace (&esdsink->cur_caps, NULL);
+  esd_close (esdsink->ctrl_fd);
+  esdsink->ctrl_fd = -1;
 
-done:
-  gst_buffer_unref (buf);
+  return TRUE;
 }
 
-static void
-gst_esdsink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+static gboolean
+gst_esdsink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec)
 {
-  GstEsdsink *esdsink;
+  GstEsdSink *esdsink = GST_ESDSINK (asink);
+  esd_format_t esdformat;
+
+  /* Name used by esound for this connection. */
+  const char connname[] = "GStreamer";
 
-  /* it's not null if we got it, but it might not be ours */
-  g_return_if_fail(GST_IS_ESDSINK(object));
-  esdsink = GST_ESDSINK(object);
+  GST_DEBUG_OBJECT (esdsink, "prepare");
 
-  switch (prop_id) {
-    case ARG_MUTE:
-      esdsink->mute = g_value_get_boolean (value);
+  /* Bitmap describing audio format. */
+  esdformat = ESD_STREAM | ESD_PLAY;
+
+  switch (spec->depth) {
+    case 8:
+      esdformat |= ESD_BITS8;
       break;
-    case ARG_HOST:
-      g_free(esdsink->host);
-      if (g_value_get_string (value) == NULL)
-         esdsink->host = NULL;
-      else
-         esdsink->host = g_strdup (g_value_get_string (value));
+    case 16:
+      esdformat |= ESD_BITS16;
       break;
-    case ARG_SYNC:
-      esdsink->sync = g_value_get_boolean (value);
+    default:
+      goto unsupported_depth;
+  }
+
+  switch (spec->channels) {
+    case 1:
+      esdformat |= ESD_MONO;
       break;
-    case ARG_FALLBACK:
-      esdsink->fallback = g_value_get_boolean (value);
+    case 2:
+      esdformat |= ESD_STEREO;
       break;
     default:
-      break;
+      goto unsupported_channels;
   }
-}
 
-static void
-gst_esdsink_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
-{
-  GstEsdsink *esdsink;
+  GST_INFO_OBJECT (esdsink,
+      "attempting to open data connection to esound server");
 
-  esdsink = GST_ESDSINK(object);
+  esdsink->fd =
+      esd_play_stream (esdformat, spec->rate, esdsink->host, connname);
 
-  switch (prop_id) {
-    case ARG_MUTE:
-      g_value_set_boolean (value, esdsink->mute);
-      break;
-    case ARG_HOST:
-      g_value_set_string (value, esdsink->host);
-      break;
-    case ARG_SYNC:
-      g_value_set_boolean (value, esdsink->sync);
-      break;
-    case ARG_FALLBACK:
-      g_value_set_boolean (value, esdsink->fallback);
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
+  if ((esdsink->fd < 0) || (esdsink->ctrl_fd < 0))
+    goto cannot_open;
+
+  esdsink->rate = spec->rate;
+
+  spec->segsize = ESD_BUF_SIZE;
+  spec->segtotal = (ESD_MAX_WRITE_SIZE / spec->segsize);
+
+  /* FIXME: this is wrong for signed ints (and the
+   * audioringbuffers should do it for us anyway) */
+  spec->silence_sample[0] = 0;
+  spec->silence_sample[1] = 0;
+  spec->silence_sample[2] = 0;
+  spec->silence_sample[3] = 0;
+
+  GST_INFO_OBJECT (esdsink, "successfully opened connection to esound server");
+
+  return TRUE;
+
+  /* ERRORS */
+unsupported_depth:
+  {
+    GST_ELEMENT_ERROR (esdsink, STREAM, WRONG_TYPE, (NULL),
+        ("can't handle sample depth of %d, only 8 or 16 supported",
+            spec->depth));
+    return FALSE;
+  }
+unsupported_channels:
+  {
+    GST_ELEMENT_ERROR (esdsink, STREAM, WRONG_TYPE, (NULL),
+        ("can't handle %d channels, only 1 or 2 supported", spec->channels));
+    return FALSE;
+  }
+cannot_open:
+  {
+    GST_ELEMENT_ERROR (esdsink, RESOURCE, OPEN_WRITE,
+        (_("Could not establish connection to sound server")),
+        ("can't open connection to esound server"));
+    return FALSE;
   }
 }
 
-gboolean
-gst_esdsink_factory_init (GstPlugin *plugin)
+static gboolean
+gst_esdsink_unprepare (GstAudioSink * asink)
 {
-  if (!gst_element_register (plugin, "esdsink", GST_RANK_NONE, GST_TYPE_ESDSINK))
-    return FALSE;
+  GstEsdSink *esdsink = GST_ESDSINK (asink);
+
+  if ((esdsink->fd < 0) && (esdsink->ctrl_fd < 0))
+    return TRUE;
+
+  close (esdsink->fd);
+  esdsink->fd = -1;
+
+  GST_INFO_OBJECT (esdsink, "closed sound device");
 
   return TRUE;
 }
 
-static gboolean
-gst_esdsink_open_audio (GstEsdsink *sink)
+
+static guint
+gst_esdsink_write (GstAudioSink * asink, gpointer data, guint length)
 {
-  /* Name used by esound for this connection. */
-  const char * connname = "GStreamer";
+  GstEsdSink *esdsink = GST_ESDSINK (asink);
+  gint to_write = 0;
 
-  /* Bitmap describing audio format. */
-  esd_format_t esdformat = ESD_STREAM | ESD_PLAY;
+  to_write = length;
 
-  g_return_val_if_fail (sink->fd == -1, FALSE);
+  while (to_write > 0) {
+    int done;
 
-  if (sink->depth == 16) esdformat |= ESD_BITS16;
-  else if (sink->depth == 8) esdformat |= ESD_BITS8;
-  else {
-    gst_element_error (sink, STREAM, FORMAT, NULL,
-                       ("invalid bit depth (%d)", sink->depth));
-    return FALSE;
-  }
+    done = write (esdsink->fd, data, to_write);
 
-  if (sink->channels == 2) esdformat |= ESD_STEREO;
-  else if (sink->channels == 1) esdformat |= ESD_MONO;
-  else {
-    gst_element_error (sink, STREAM, FORMAT, NULL,
-                       ("invalid number of channels (%d)", sink->channels));
-    return FALSE;
-  }
+    if (done < 0)
+      goto write_error;
 
-  GST_DEBUG ("esdsink: attempting to open connection to esound server");
-  if(sink->fallback){
-    sink->fd = esd_play_stream_fallback(esdformat, sink->frequency, sink->host, connname);
-  }else{
-    sink->fd = esd_play_stream(esdformat, sink->frequency, sink->host, connname);
+    to_write -= done;
+    data = (char *) data + done;
   }
-  if ( sink->fd < 0 ) {
-    gst_element_error (sink, RESOURCE, OPEN_WRITE, NULL,
-                       ("can't open connection to esound server"));
-    return FALSE;
+  return length;
+
+  /* ERRORS */
+write_error:
+  {
+    GST_ELEMENT_ERROR (esdsink, RESOURCE, WRITE,
+        ("Failed to write data to the esound daemon"), GST_ERROR_SYSTEM);
+    return 0;
   }
-
-  return TRUE;
 }
 
-static void
-gst_esdsink_close_audio (GstEsdsink *sink)
+static guint
+gst_esdsink_delay (GstAudioSink * asink)
 {
-  if (sink->fd < 0) 
-    return;
+  GstEsdSink *esdsink = GST_ESDSINK (asink);
+  guint latency;
+
+  latency = esd_get_latency (esdsink->ctrl_fd);
 
-  close(sink->fd);
-  sink->fd = -1;
+  if (latency == (guint) - 1) {
+    GST_WARNING_OBJECT (asink, "couldn't get latency");
+    return 0;
+  }
+
+  /* latency is measured in samples at a rate of 44100, this 
+   * cannot overflow. */
+  latency = latency * G_GINT64_CONSTANT (44100) / esdsink->rate;
 
-  GST_DEBUG ("esdsink: closed sound device");
+  GST_DEBUG_OBJECT (asink, "got latency: %u", latency);
+
+  return latency;
 }
 
-static GstElementStateReturn
-gst_esdsink_change_state (GstElement *element)
+static void
+gst_esdsink_reset (GstAudioSink * asink)
 {
-  GstEsdsink *esdsink;
+  GST_DEBUG_OBJECT (asink, "reset called");
+}
 
-  esdsink = GST_ESDSINK (element);
+static void
+gst_esdsink_set_property (GObject * object, guint prop_id, const GValue * value,
+    GParamSpec * pspec)
+{
+  GstEsdSink *esdsink = GST_ESDSINK (object);
 
-  switch (GST_STATE_TRANSITION (element)) {
-    case GST_STATE_NULL_TO_READY:
-      break;
-    case GST_STATE_READY_TO_PAUSED:
-      break;
-    case GST_STATE_PAUSED_TO_PLAYING:
-      break;
-    case GST_STATE_PLAYING_TO_PAUSED:
-      gst_audio_clock_set_active (GST_AUDIO_CLOCK (esdsink->provided_clock),
-         FALSE);
-      esdsink->resync = TRUE;
-      break;
-    case GST_STATE_PAUSED_TO_READY:
-      gst_esdsink_close_audio (GST_ESDSINK (element));
-      esdsink->negotiated = FALSE;
-      break;
-    case GST_STATE_READY_TO_NULL:
+  switch (prop_id) {
+    case PROP_HOST:
+      g_free (esdsink->host);
+      esdsink->host = g_value_dup_string (value);
       break;
     default:
       break;
   }
+}
 
-  if (GST_ELEMENT_CLASS (parent_class)->change_state)
-    return GST_ELEMENT_CLASS (parent_class)->change_state (element);
+static void
+gst_esdsink_get_property (GObject * object, guint prop_id, GValue * value,
+    GParamSpec * pspec)
+{
+  GstEsdSink *esdsink = GST_ESDSINK (object);
 
-  return GST_STATE_SUCCESS;
+  switch (prop_id) {
+    case PROP_HOST:
+      g_value_set_string (value, esdsink->host);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
 }
-