/* 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;
+ }
}
-