upload tizen1.0 source
[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_pad_template (element_class,
115       gst_static_pad_template_get (&sink_factory));
116   gst_element_class_set_details_simple (element_class, "Esound audio sink",
117       "Sink/Audio",
118       "Plays audio to an esound server",
119       "Arwed von Merkatz <v.merkatz@gmx.net>");
120 }
121
122 static void
123 gst_esdsink_class_init (GstEsdSinkClass * klass)
124 {
125   GObjectClass *gobject_class;
126   GstBaseSinkClass *gstbasesink_class;
127   GstAudioSinkClass *gstaudiosink_class;
128
129   gobject_class = (GObjectClass *) klass;
130   gstbasesink_class = (GstBaseSinkClass *) klass;
131   gstaudiosink_class = (GstAudioSinkClass *) klass;
132
133   parent_class = g_type_class_peek_parent (klass);
134
135   gobject_class->finalize = gst_esdsink_finalize;
136
137   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_esdsink_getcaps);
138
139   gstaudiosink_class->open = GST_DEBUG_FUNCPTR (gst_esdsink_open);
140   gstaudiosink_class->close = GST_DEBUG_FUNCPTR (gst_esdsink_close);
141   gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR (gst_esdsink_prepare);
142   gstaudiosink_class->unprepare = GST_DEBUG_FUNCPTR (gst_esdsink_unprepare);
143   gstaudiosink_class->write = GST_DEBUG_FUNCPTR (gst_esdsink_write);
144   gstaudiosink_class->delay = GST_DEBUG_FUNCPTR (gst_esdsink_delay);
145   gstaudiosink_class->reset = GST_DEBUG_FUNCPTR (gst_esdsink_reset);
146
147   gobject_class->set_property = gst_esdsink_set_property;
148   gobject_class->get_property = gst_esdsink_get_property;
149
150   /* default value is filled in the _init method */
151   g_object_class_install_property (gobject_class, PROP_HOST,
152       g_param_spec_string ("host", "Host",
153           "The host running the esound daemon", NULL,
154           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
155 }
156
157 static void
158 gst_esdsink_init (GstEsdSink * esdsink, GstEsdSinkClass * klass)
159 {
160   esdsink->fd = -1;
161   esdsink->ctrl_fd = -1;
162   esdsink->host = g_strdup (g_getenv ("ESPEAKER"));
163 }
164
165 static void
166 gst_esdsink_finalize (GObject * object)
167 {
168   GstEsdSink *esdsink = GST_ESDSINK (object);
169
170   gst_caps_replace (&esdsink->cur_caps, NULL);
171   g_free (esdsink->host);
172
173   G_OBJECT_CLASS (parent_class)->finalize (object);
174 }
175
176 static GstCaps *
177 gst_esdsink_getcaps (GstBaseSink * bsink)
178 {
179   GstEsdSink *esdsink;
180
181   esdsink = GST_ESDSINK (bsink);
182
183   /* no fd, we're done with the template caps */
184   if (esdsink->ctrl_fd < 0 || esdsink->cur_caps == NULL) {
185     GST_LOG_OBJECT (esdsink, "getcaps called, returning template caps");
186     return NULL;
187   }
188
189   GST_LOG_OBJECT (esdsink, "returning %" GST_PTR_FORMAT, esdsink->cur_caps);
190
191   return gst_caps_ref (esdsink->cur_caps);
192 }
193
194 static gboolean
195 gst_esdsink_open (GstAudioSink * asink)
196 {
197   esd_server_info_t *server_info;
198   GstPadTemplate *pad_template;
199   GstEsdSink *esdsink;
200   gchar *saved_env;
201   gint i;
202
203   esdsink = GST_ESDSINK (asink);
204
205   GST_DEBUG_OBJECT (esdsink, "open");
206
207   /* ensure libesd doesn't auto-spawn a sound daemon if none is running yet */
208   saved_env = g_strdup (g_getenv ("ESD_NO_SPAWN"));
209   g_setenv ("ESD_NO_SPAWN", "1", TRUE);
210
211   /* now try to connect to any existing/running sound daemons */
212   esdsink->ctrl_fd = esd_open_sound (esdsink->host);
213
214   /* and restore the previous state */
215   if (saved_env != NULL) {
216     g_setenv ("ESD_NO_SPAWN", saved_env, TRUE);
217   } else {
218     g_unsetenv ("ESD_NO_SPAWN");
219   }
220   g_free (saved_env);
221
222   if (esdsink->ctrl_fd < 0)
223     goto couldnt_connect;
224
225   /* get server info */
226   server_info = esd_get_server_info (esdsink->ctrl_fd);
227   if (!server_info)
228     goto no_server_info;
229
230   GST_INFO_OBJECT (esdsink, "got server info rate: %i", server_info->rate);
231
232   pad_template = gst_static_pad_template_get (&sink_factory);
233   esdsink->cur_caps = gst_caps_copy (gst_pad_template_get_caps (pad_template));
234   gst_object_unref (pad_template);
235
236   for (i = 0; i < esdsink->cur_caps->structs->len; i++) {
237     GstStructure *s;
238
239     s = gst_caps_get_structure (esdsink->cur_caps, i);
240     gst_structure_set (s, "rate", G_TYPE_INT, server_info->rate, NULL);
241   }
242
243   esd_free_server_info (server_info);
244
245   GST_INFO_OBJECT (esdsink, "server caps: %" GST_PTR_FORMAT, esdsink->cur_caps);
246
247   return TRUE;
248
249   /* ERRORS */
250 couldnt_connect:
251   {
252     GST_ELEMENT_ERROR (esdsink, RESOURCE, OPEN_WRITE,
253         (_("Could not establish connection to sound server")),
254         ("can't open connection to esound server"));
255     return FALSE;
256   }
257 no_server_info:
258   {
259     GST_ELEMENT_ERROR (esdsink, RESOURCE, OPEN_WRITE,
260         (_("Failed to query sound server capabilities")),
261         ("couldn't get server info!"));
262     return FALSE;
263   }
264 }
265
266 static gboolean
267 gst_esdsink_close (GstAudioSink * asink)
268 {
269   GstEsdSink *esdsink = GST_ESDSINK (asink);
270
271   GST_DEBUG_OBJECT (esdsink, "close");
272
273   gst_caps_replace (&esdsink->cur_caps, NULL);
274   esd_close (esdsink->ctrl_fd);
275   esdsink->ctrl_fd = -1;
276
277   return TRUE;
278 }
279
280 static gboolean
281 gst_esdsink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec)
282 {
283   GstEsdSink *esdsink = GST_ESDSINK (asink);
284   esd_format_t esdformat;
285
286   /* Name used by esound for this connection. */
287   const char connname[] = "GStreamer";
288
289   GST_DEBUG_OBJECT (esdsink, "prepare");
290
291   /* Bitmap describing audio format. */
292   esdformat = ESD_STREAM | ESD_PLAY;
293
294   switch (spec->depth) {
295     case 8:
296       esdformat |= ESD_BITS8;
297       break;
298     case 16:
299       esdformat |= ESD_BITS16;
300       break;
301     default:
302       goto unsupported_depth;
303   }
304
305   switch (spec->channels) {
306     case 1:
307       esdformat |= ESD_MONO;
308       break;
309     case 2:
310       esdformat |= ESD_STEREO;
311       break;
312     default:
313       goto unsupported_channels;
314   }
315
316   GST_INFO_OBJECT (esdsink,
317       "attempting to open data connection to esound server");
318
319   esdsink->fd =
320       esd_play_stream (esdformat, spec->rate, esdsink->host, connname);
321
322   if ((esdsink->fd < 0) || (esdsink->ctrl_fd < 0))
323     goto cannot_open;
324
325   esdsink->rate = spec->rate;
326
327   spec->segsize = ESD_BUF_SIZE;
328   spec->segtotal = (ESD_MAX_WRITE_SIZE / spec->segsize);
329
330   /* FIXME: this is wrong for signed ints (and the
331    * audioringbuffers should do it for us anyway) */
332   spec->silence_sample[0] = 0;
333   spec->silence_sample[1] = 0;
334   spec->silence_sample[2] = 0;
335   spec->silence_sample[3] = 0;
336
337   GST_INFO_OBJECT (esdsink, "successfully opened connection to esound server");
338
339   return TRUE;
340
341   /* ERRORS */
342 unsupported_depth:
343   {
344     GST_ELEMENT_ERROR (esdsink, STREAM, WRONG_TYPE, (NULL),
345         ("can't handle sample depth of %d, only 8 or 16 supported",
346             spec->depth));
347     return FALSE;
348   }
349 unsupported_channels:
350   {
351     GST_ELEMENT_ERROR (esdsink, STREAM, WRONG_TYPE, (NULL),
352         ("can't handle %d channels, only 1 or 2 supported", spec->channels));
353     return FALSE;
354   }
355 cannot_open:
356   {
357     GST_ELEMENT_ERROR (esdsink, RESOURCE, OPEN_WRITE,
358         (_("Could not establish connection to sound server")),
359         ("can't open connection to esound server"));
360     return FALSE;
361   }
362 }
363
364 static gboolean
365 gst_esdsink_unprepare (GstAudioSink * asink)
366 {
367   GstEsdSink *esdsink = GST_ESDSINK (asink);
368
369   if ((esdsink->fd < 0) && (esdsink->ctrl_fd < 0))
370     return TRUE;
371
372   close (esdsink->fd);
373   esdsink->fd = -1;
374
375   GST_INFO_OBJECT (esdsink, "closed sound device");
376
377   return TRUE;
378 }
379
380
381 static guint
382 gst_esdsink_write (GstAudioSink * asink, gpointer data, guint length)
383 {
384   GstEsdSink *esdsink = GST_ESDSINK (asink);
385   gint to_write = 0;
386
387   to_write = length;
388
389   while (to_write > 0) {
390     int done;
391
392     done = write (esdsink->fd, data, to_write);
393
394     if (done < 0)
395       goto write_error;
396
397     to_write -= done;
398     data = (char *) data + done;
399   }
400   return length;
401
402   /* ERRORS */
403 write_error:
404   {
405     GST_ELEMENT_ERROR (esdsink, RESOURCE, WRITE,
406         ("Failed to write data to the esound daemon"), GST_ERROR_SYSTEM);
407     return -1;
408   }
409 }
410
411 static guint
412 gst_esdsink_delay (GstAudioSink * asink)
413 {
414   GstEsdSink *esdsink = GST_ESDSINK (asink);
415   guint latency;
416
417   latency = esd_get_latency (esdsink->ctrl_fd);
418
419   if (latency == (guint) - 1) {
420     GST_WARNING_OBJECT (asink, "couldn't get latency");
421     return 0;
422   }
423
424   /* latency is measured in samples at a rate of 44100, this 
425    * cannot overflow. */
426   latency = latency * G_GINT64_CONSTANT (44100) / esdsink->rate;
427
428   GST_DEBUG_OBJECT (asink, "got latency: %u", latency);
429
430   return latency;
431 }
432
433 static void
434 gst_esdsink_reset (GstAudioSink * asink)
435 {
436   GST_DEBUG_OBJECT (asink, "reset called");
437 }
438
439 static void
440 gst_esdsink_set_property (GObject * object, guint prop_id, const GValue * value,
441     GParamSpec * pspec)
442 {
443   GstEsdSink *esdsink = GST_ESDSINK (object);
444
445   switch (prop_id) {
446     case PROP_HOST:
447       g_free (esdsink->host);
448       esdsink->host = g_value_dup_string (value);
449       break;
450     default:
451       break;
452   }
453 }
454
455 static void
456 gst_esdsink_get_property (GObject * object, guint prop_id, GValue * value,
457     GParamSpec * pspec)
458 {
459   GstEsdSink *esdsink = GST_ESDSINK (object);
460
461   switch (prop_id) {
462     case PROP_HOST:
463       g_value_set_string (value, esdsink->host);
464       break;
465     default:
466       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
467       break;
468   }
469 }