Tizen 2.0 Release
[framework/multimedia/gst-plugins-good0.10.git] / ext / esd / esdsink.c
1 /* GStreamer
2  * Copyright (C) <2005> Arwed v. Merkatz <v.merkatz@gmx.net>
3  *
4  * Roughly based on the gstreamer 0.8 esdsink plugin:
5  * Copyright (C) <2001> Richard Boulton <richard-gst@tartarus.org>
6  *
7  * esdsink.c: an EsounD audio sink
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 /**
26  * SECTION:element-esdsink
27  * @see_also: #GstAlsaSink, #GstAutoAudioSink
28  *
29  * This element outputs sound to an already-running Enlightened Sound Daemon
30  * (ESound Daemon, esd). Note that a sound daemon will never be auto-spawned
31  * through this element (regardless of the system configuration), since this
32  * is actively prevented by the element. If you must use esd, you need to
33  * make sure it is started automatically with your session or otherwise.
34  *
35  * TODO: insert some comments about how sucky esd is and that all the cool
36  * kids use pulseaudio or whatever these days.
37  *
38  * <refsect2>
39  * <title>Example launch line</title>
40  * |[
41  * gst-launch -v filesrc location=foo.ogg ! decodebin ! audioconvert ! audioresample ! esdsink
42  * ]| play an Ogg/Vorbis audio file via esd
43  * </refsect2>
44  */
45
46 #ifdef HAVE_CONFIG_H
47 #include "config.h"
48 #endif
49
50 #include "esdsink.h"
51 #include <esd.h>
52 #include <unistd.h>
53 #include <errno.h>
54
55 #include <gst/gst-i18n-plugin.h>
56
57 /* wtay: from my esd.h (debian unstable libesd0-dev 0.2.36-3) */
58 #ifndef ESD_MAX_WRITE_SIZE
59 #define ESD_MAX_WRITE_SIZE (21 * 4096)
60 #endif
61
62 GST_DEBUG_CATEGORY_EXTERN (esd_debug);
63 #define GST_CAT_DEFAULT esd_debug
64
65 enum
66 {
67   PROP_0,
68   PROP_HOST
69 };
70
71 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
72     GST_PAD_SINK,
73     GST_PAD_ALWAYS,
74     GST_STATIC_CAPS ("audio/x-raw-int, "
75         "endianness = (int) BYTE_ORDER, "
76         "signed = (boolean) TRUE, "
77         "width = (int) 16, "
78         "depth = (int) 16, "
79         "rate = (int) [ 1, MAX ], "
80         "channels = (int) [ 1, 2 ]; "
81         "audio/x-raw-int, "
82         "signed = (boolean) { true, false }, "
83         "width = (int) 8, "
84         "depth = (int) 8, "
85         "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]")
86     );
87
88 static void gst_esdsink_finalize (GObject * object);
89
90 static GstCaps *gst_esdsink_getcaps (GstBaseSink * bsink);
91
92 static gboolean gst_esdsink_open (GstAudioSink * asink);
93 static gboolean gst_esdsink_close (GstAudioSink * asink);
94 static gboolean gst_esdsink_prepare (GstAudioSink * asink,
95     GstRingBufferSpec * spec);
96 static gboolean gst_esdsink_unprepare (GstAudioSink * asink);
97 static guint gst_esdsink_write (GstAudioSink * asink, gpointer data,
98     guint length);
99 static guint gst_esdsink_delay (GstAudioSink * asink);
100 static void gst_esdsink_reset (GstAudioSink * asink);
101
102 static void gst_esdsink_set_property (GObject * object, guint prop_id,
103     const GValue * value, GParamSpec * pspec);
104 static void gst_esdsink_get_property (GObject * object, guint prop_id,
105     GValue * value, GParamSpec * pspec);
106
107 GST_BOILERPLATE (GstEsdSink, gst_esdsink, GstAudioSink, GST_TYPE_AUDIO_SINK);
108
109 static void
110 gst_esdsink_base_init (gpointer g_class)
111 {
112   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
113
114   gst_element_class_add_static_pad_template (element_class, &sink_factory);
115   gst_element_class_set_details_simple (element_class, "Esound audio sink",
116       "Sink/Audio",
117       "Plays audio to an esound server",
118       "Arwed von Merkatz <v.merkatz@gmx.net>");
119 }
120
121 static void
122 gst_esdsink_class_init (GstEsdSinkClass * klass)
123 {
124   GObjectClass *gobject_class;
125   GstBaseSinkClass *gstbasesink_class;
126   GstAudioSinkClass *gstaudiosink_class;
127
128   gobject_class = (GObjectClass *) klass;
129   gstbasesink_class = (GstBaseSinkClass *) klass;
130   gstaudiosink_class = (GstAudioSinkClass *) klass;
131
132   parent_class = g_type_class_peek_parent (klass);
133
134   gobject_class->finalize = gst_esdsink_finalize;
135
136   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_esdsink_getcaps);
137
138   gstaudiosink_class->open = GST_DEBUG_FUNCPTR (gst_esdsink_open);
139   gstaudiosink_class->close = GST_DEBUG_FUNCPTR (gst_esdsink_close);
140   gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR (gst_esdsink_prepare);
141   gstaudiosink_class->unprepare = GST_DEBUG_FUNCPTR (gst_esdsink_unprepare);
142   gstaudiosink_class->write = GST_DEBUG_FUNCPTR (gst_esdsink_write);
143   gstaudiosink_class->delay = GST_DEBUG_FUNCPTR (gst_esdsink_delay);
144   gstaudiosink_class->reset = GST_DEBUG_FUNCPTR (gst_esdsink_reset);
145
146   gobject_class->set_property = gst_esdsink_set_property;
147   gobject_class->get_property = gst_esdsink_get_property;
148
149   /* default value is filled in the _init method */
150   g_object_class_install_property (gobject_class, PROP_HOST,
151       g_param_spec_string ("host", "Host",
152           "The host running the esound daemon", NULL,
153           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
154 }
155
156 static void
157 gst_esdsink_init (GstEsdSink * esdsink, GstEsdSinkClass * klass)
158 {
159   esdsink->fd = -1;
160   esdsink->ctrl_fd = -1;
161   esdsink->host = g_strdup (g_getenv ("ESPEAKER"));
162 }
163
164 static void
165 gst_esdsink_finalize (GObject * object)
166 {
167   GstEsdSink *esdsink = GST_ESDSINK (object);
168
169   gst_caps_replace (&esdsink->cur_caps, NULL);
170   g_free (esdsink->host);
171
172   G_OBJECT_CLASS (parent_class)->finalize (object);
173 }
174
175 static GstCaps *
176 gst_esdsink_getcaps (GstBaseSink * bsink)
177 {
178   GstEsdSink *esdsink;
179
180   esdsink = GST_ESDSINK (bsink);
181
182   /* no fd, we're done with the template caps */
183   if (esdsink->ctrl_fd < 0 || esdsink->cur_caps == NULL) {
184     GST_LOG_OBJECT (esdsink, "getcaps called, returning template caps");
185     return NULL;
186   }
187
188   GST_LOG_OBJECT (esdsink, "returning %" GST_PTR_FORMAT, esdsink->cur_caps);
189
190   return gst_caps_ref (esdsink->cur_caps);
191 }
192
193 static gboolean
194 gst_esdsink_open (GstAudioSink * asink)
195 {
196   esd_server_info_t *server_info;
197   GstPadTemplate *pad_template;
198   GstEsdSink *esdsink;
199   gchar *saved_env;
200   gint i;
201
202   esdsink = GST_ESDSINK (asink);
203
204   GST_DEBUG_OBJECT (esdsink, "open");
205
206   /* ensure libesd doesn't auto-spawn a sound daemon if none is running yet */
207   saved_env = g_strdup (g_getenv ("ESD_NO_SPAWN"));
208   g_setenv ("ESD_NO_SPAWN", "1", TRUE);
209
210   /* now try to connect to any existing/running sound daemons */
211   esdsink->ctrl_fd = esd_open_sound (esdsink->host);
212
213   /* and restore the previous state */
214   if (saved_env != NULL) {
215     g_setenv ("ESD_NO_SPAWN", saved_env, TRUE);
216   } else {
217     g_unsetenv ("ESD_NO_SPAWN");
218   }
219   g_free (saved_env);
220
221   if (esdsink->ctrl_fd < 0)
222     goto couldnt_connect;
223
224   /* get server info */
225   server_info = esd_get_server_info (esdsink->ctrl_fd);
226   if (!server_info)
227     goto no_server_info;
228
229   GST_INFO_OBJECT (esdsink, "got server info rate: %i", server_info->rate);
230
231   pad_template = gst_static_pad_template_get (&sink_factory);
232   esdsink->cur_caps = gst_caps_copy (gst_pad_template_get_caps (pad_template));
233   gst_object_unref (pad_template);
234
235   for (i = 0; i < esdsink->cur_caps->structs->len; i++) {
236     GstStructure *s;
237
238     s = gst_caps_get_structure (esdsink->cur_caps, i);
239     gst_structure_set (s, "rate", G_TYPE_INT, server_info->rate, NULL);
240   }
241
242   esd_free_server_info (server_info);
243
244   GST_INFO_OBJECT (esdsink, "server caps: %" GST_PTR_FORMAT, esdsink->cur_caps);
245
246   return TRUE;
247
248   /* ERRORS */
249 couldnt_connect:
250   {
251     GST_ELEMENT_ERROR (esdsink, RESOURCE, OPEN_WRITE,
252         (_("Could not establish connection to sound server")),
253         ("can't open connection to esound server"));
254     return FALSE;
255   }
256 no_server_info:
257   {
258     GST_ELEMENT_ERROR (esdsink, RESOURCE, OPEN_WRITE,
259         (_("Failed to query sound server capabilities")),
260         ("couldn't get server info!"));
261     return FALSE;
262   }
263 }
264
265 static gboolean
266 gst_esdsink_close (GstAudioSink * asink)
267 {
268   GstEsdSink *esdsink = GST_ESDSINK (asink);
269
270   GST_DEBUG_OBJECT (esdsink, "close");
271
272   gst_caps_replace (&esdsink->cur_caps, NULL);
273   esd_close (esdsink->ctrl_fd);
274   esdsink->ctrl_fd = -1;
275
276   return TRUE;
277 }
278
279 static gboolean
280 gst_esdsink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec)
281 {
282   GstEsdSink *esdsink = GST_ESDSINK (asink);
283   esd_format_t esdformat;
284
285   /* Name used by esound for this connection. */
286   const char connname[] = "GStreamer";
287
288   GST_DEBUG_OBJECT (esdsink, "prepare");
289
290   /* Bitmap describing audio format. */
291   esdformat = ESD_STREAM | ESD_PLAY;
292
293   switch (spec->depth) {
294     case 8:
295       esdformat |= ESD_BITS8;
296       break;
297     case 16:
298       esdformat |= ESD_BITS16;
299       break;
300     default:
301       goto unsupported_depth;
302   }
303
304   switch (spec->channels) {
305     case 1:
306       esdformat |= ESD_MONO;
307       break;
308     case 2:
309       esdformat |= ESD_STEREO;
310       break;
311     default:
312       goto unsupported_channels;
313   }
314
315   GST_INFO_OBJECT (esdsink,
316       "attempting to open data connection to esound server");
317
318   esdsink->fd =
319       esd_play_stream (esdformat, spec->rate, esdsink->host, connname);
320
321   if ((esdsink->fd < 0) || (esdsink->ctrl_fd < 0))
322     goto cannot_open;
323
324   esdsink->rate = spec->rate;
325
326   spec->segsize = ESD_BUF_SIZE;
327   spec->segtotal = (ESD_MAX_WRITE_SIZE / spec->segsize);
328
329   /* FIXME: this is wrong for signed ints (and the
330    * audioringbuffers should do it for us anyway) */
331   spec->silence_sample[0] = 0;
332   spec->silence_sample[1] = 0;
333   spec->silence_sample[2] = 0;
334   spec->silence_sample[3] = 0;
335
336   GST_INFO_OBJECT (esdsink, "successfully opened connection to esound server");
337
338   return TRUE;
339
340   /* ERRORS */
341 unsupported_depth:
342   {
343     GST_ELEMENT_ERROR (esdsink, STREAM, WRONG_TYPE, (NULL),
344         ("can't handle sample depth of %d, only 8 or 16 supported",
345             spec->depth));
346     return FALSE;
347   }
348 unsupported_channels:
349   {
350     GST_ELEMENT_ERROR (esdsink, STREAM, WRONG_TYPE, (NULL),
351         ("can't handle %d channels, only 1 or 2 supported", spec->channels));
352     return FALSE;
353   }
354 cannot_open:
355   {
356     GST_ELEMENT_ERROR (esdsink, RESOURCE, OPEN_WRITE,
357         (_("Could not establish connection to sound server")),
358         ("can't open connection to esound server"));
359     return FALSE;
360   }
361 }
362
363 static gboolean
364 gst_esdsink_unprepare (GstAudioSink * asink)
365 {
366   GstEsdSink *esdsink = GST_ESDSINK (asink);
367
368   if ((esdsink->fd < 0) && (esdsink->ctrl_fd < 0))
369     return TRUE;
370
371   close (esdsink->fd);
372   esdsink->fd = -1;
373
374   GST_INFO_OBJECT (esdsink, "closed sound device");
375
376   return TRUE;
377 }
378
379
380 static guint
381 gst_esdsink_write (GstAudioSink * asink, gpointer data, guint length)
382 {
383   GstEsdSink *esdsink = GST_ESDSINK (asink);
384   gint to_write = 0;
385
386   to_write = length;
387
388   while (to_write > 0) {
389     int done;
390
391     done = write (esdsink->fd, data, to_write);
392
393     if (done < 0)
394       goto write_error;
395
396     to_write -= done;
397     data = (char *) data + done;
398   }
399   return length;
400
401   /* ERRORS */
402 write_error:
403   {
404     GST_ELEMENT_ERROR (esdsink, RESOURCE, WRITE,
405         ("Failed to write data to the esound daemon"), GST_ERROR_SYSTEM);
406     return -1;
407   }
408 }
409
410 static guint
411 gst_esdsink_delay (GstAudioSink * asink)
412 {
413   GstEsdSink *esdsink = GST_ESDSINK (asink);
414   guint latency;
415
416   latency = esd_get_latency (esdsink->ctrl_fd);
417
418   if (latency == (guint) - 1) {
419     GST_WARNING_OBJECT (asink, "couldn't get latency");
420     return 0;
421   }
422
423   /* latency is measured in samples at a rate of 44100, this 
424    * cannot overflow. */
425   latency = latency * G_GINT64_CONSTANT (44100) / esdsink->rate;
426
427   GST_DEBUG_OBJECT (asink, "got latency: %u", latency);
428
429   return latency;
430 }
431
432 static void
433 gst_esdsink_reset (GstAudioSink * asink)
434 {
435   GST_DEBUG_OBJECT (asink, "reset called");
436 }
437
438 static void
439 gst_esdsink_set_property (GObject * object, guint prop_id, const GValue * value,
440     GParamSpec * pspec)
441 {
442   GstEsdSink *esdsink = GST_ESDSINK (object);
443
444   switch (prop_id) {
445     case PROP_HOST:
446       g_free (esdsink->host);
447       esdsink->host = g_value_dup_string (value);
448       break;
449     default:
450       break;
451   }
452 }
453
454 static void
455 gst_esdsink_get_property (GObject * object, guint prop_id, GValue * value,
456     GParamSpec * pspec)
457 {
458   GstEsdSink *esdsink = GST_ESDSINK (object);
459
460   switch (prop_id) {
461     case PROP_HOST:
462       g_value_set_string (value, esdsink->host);
463       break;
464     default:
465       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
466       break;
467   }
468 }