ESD Updated
[platform/upstream/gst-plugins-good.git] / ext / esd / esdsink.c
1 /* GStreamer
2  * Copyright (C) <2001> Richard Boulton <richard-gst@tartarus.org>
3  *
4  * Based on example.c:
5  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 #include "esdsink.h"
27 #include <esd.h>
28 #include <unistd.h>
29 #include <errno.h>
30
31 /* elementfactory information */
32 static GstElementDetails esdsink_details = {
33   "Esound audio sink",
34   "Sink/Audio",
35   "Plays audio to an esound server",
36   "Richard Boulton <richard-gst@tartarus.org>",
37 };
38
39 /* Signals and args */
40 enum {
41   /* FILL ME */
42   LAST_SIGNAL
43 };
44
45 enum {
46   ARG_0,
47   ARG_MUTE,
48   ARG_HOST,
49   ARG_SYNC,
50   ARG_FALLBACK,
51 };
52
53 GST_PAD_TEMPLATE_FACTORY (sink_factory,
54   "sink",                                       /* the name of the pads */
55   GST_PAD_SINK,                         /* type of the pad */
56   GST_PAD_ALWAYS,                       /* ALWAYS/SOMETIMES */
57   GST_CAPS_NEW (
58     "esdsink_sink",                             /* the name of the caps */
59     "audio/x-raw-int",                          /* the mime type of the caps */
60       /* Properties follow: */
61         "endianness", GST_PROPS_INT (G_BYTE_ORDER),
62         "signed",     GST_PROPS_LIST (
63                         GST_PROPS_BOOLEAN (TRUE),
64                         GST_PROPS_BOOLEAN (FALSE)
65                       ),
66         "width",      GST_PROPS_LIST (
67                         GST_PROPS_INT (8),
68                         GST_PROPS_INT (16)
69                       ),
70         "depth",      GST_PROPS_LIST (
71                         GST_PROPS_INT (8),
72                         GST_PROPS_INT (16)
73                       ),
74         "rate",       GST_PROPS_INT_RANGE (8000, 96000),
75         "channels",   GST_PROPS_LIST (
76                         GST_PROPS_INT (1),
77                         GST_PROPS_INT (2)
78                       )
79   )
80 );
81
82 static void                     gst_esdsink_base_init           (gpointer g_class);
83 static void                     gst_esdsink_class_init          (GstEsdsinkClass *klass);
84 static void                     gst_esdsink_init                (GstEsdsink *esdsink);
85
86 static gboolean                 gst_esdsink_open_audio          (GstEsdsink *sink);
87 static void                     gst_esdsink_close_audio         (GstEsdsink *sink);
88 static GstElementStateReturn    gst_esdsink_change_state        (GstElement *element);
89 static GstPadLinkReturn         gst_esdsink_sinkconnect         (GstPad *pad, GstCaps *caps);
90
91 static GstClockTime             gst_esdsink_get_time            (GstClock *clock, gpointer data);
92 static GstClock *               gst_esdsink_get_clock           (GstElement *element);
93 static void                     gst_esdsink_set_clock           (GstElement *element, GstClock *clock);
94 static void                     gst_esdsink_chain               (GstPad *pad, GstData *_data);
95
96 static void                     gst_esdsink_set_property        (GObject *object, guint prop_id, 
97                                                                  const GValue *value, GParamSpec *pspec);
98 static void                     gst_esdsink_get_property        (GObject *object, guint prop_id, 
99                                                                  GValue *value, GParamSpec *pspec);
100
101 static GstElementClass *parent_class = NULL;
102 /*static guint gst_esdsink_signals[LAST_SIGNAL] = { 0 }; */
103
104 GType
105 gst_esdsink_get_type (void)
106 {
107   static GType esdsink_type = 0;
108
109   if (!esdsink_type) {
110     static const GTypeInfo esdsink_info = {
111       sizeof(GstEsdsinkClass),
112       gst_esdsink_base_init,
113       NULL,
114       (GClassInitFunc)gst_esdsink_class_init,
115       NULL,
116       NULL,
117       sizeof(GstEsdsink),
118       0,
119       (GInstanceInitFunc)gst_esdsink_init,
120     };
121     esdsink_type = g_type_register_static(GST_TYPE_ELEMENT, "GstEsdsink", &esdsink_info, 0);
122   }
123   return esdsink_type;
124 }
125
126 static void
127 gst_esdsink_base_init (gpointer g_class)
128 {
129   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
130
131   gst_element_class_add_pad_template (element_class, GST_PAD_TEMPLATE_GET (sink_factory));
132   gst_element_class_set_details (element_class, &esdsink_details);
133 }
134
135 static void
136 gst_esdsink_class_init (GstEsdsinkClass *klass)
137 {
138   GObjectClass *gobject_class;
139   GstElementClass *gstelement_class;
140
141   gobject_class = (GObjectClass*)klass;
142   gstelement_class = (GstElementClass*)klass;
143
144   parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
145
146   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_MUTE,
147     g_param_spec_boolean("mute","mute","mute",
148                          TRUE,G_PARAM_READWRITE)); /* CHECKME */
149   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_HOST,
150     g_param_spec_string("host","host","host",
151                         NULL, G_PARAM_READWRITE)); /* CHECKME */
152   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_SYNC,
153     g_param_spec_boolean("sync","sync","Synchronize output to clock",
154                          FALSE,G_PARAM_READWRITE));
155   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_FALLBACK,
156     g_param_spec_boolean("fallback","fallback","Fall back to using OSS if Esound daemon is not present",
157                          FALSE,G_PARAM_READWRITE));
158
159   gobject_class->set_property = gst_esdsink_set_property;
160   gobject_class->get_property = gst_esdsink_get_property;
161
162   gstelement_class->change_state = gst_esdsink_change_state;
163   gstelement_class->set_clock    = gst_esdsink_set_clock;
164   gstelement_class->get_clock    = gst_esdsink_get_clock;
165 }
166
167 static void
168 gst_esdsink_init(GstEsdsink *esdsink)
169 {
170   esdsink->sinkpad = gst_pad_new_from_template (
171                   GST_PAD_TEMPLATE_GET (sink_factory), "sink");
172   gst_element_add_pad(GST_ELEMENT(esdsink), esdsink->sinkpad);
173   gst_pad_set_chain_function(esdsink->sinkpad, GST_DEBUG_FUNCPTR(gst_esdsink_chain));
174   gst_pad_set_link_function(esdsink->sinkpad, gst_esdsink_sinkconnect);
175
176   GST_FLAG_SET (esdsink, GST_ELEMENT_EVENT_AWARE);
177
178   esdsink->mute = FALSE;
179   esdsink->fd = -1;
180   /* FIXME: get default from somewhere better than just putting them inline. */
181   esdsink->negotiated = FALSE;
182   esdsink->format = -1;
183   esdsink->depth = -1;
184   esdsink->channels = -1;
185   esdsink->frequency = -1;
186   esdsink->host = getenv ("ESPEAKER");
187   esdsink->provided_clock = gst_audio_clock_new("esdclock", gst_esdsink_get_time, esdsink);
188   gst_object_set_parent(GST_OBJECT(esdsink->provided_clock), GST_OBJECT(esdsink));
189   esdsink->sync = FALSE;
190   esdsink->fallback = FALSE;
191 }
192
193 static GstPadLinkReturn
194 gst_esdsink_sinkconnect (GstPad *pad, GstCaps *caps)
195 {
196   GstEsdsink *esdsink;
197   gboolean sign;
198
199   esdsink = GST_ESDSINK (gst_pad_get_parent (pad));
200
201   if (!GST_CAPS_IS_FIXED (caps))
202     return GST_PAD_LINK_DELAYED;
203
204   gst_caps_get_int (caps, "depth", &esdsink->depth);
205   gst_caps_get_int (caps, "signed", &sign);
206   gst_caps_get_int (caps, "channels", &esdsink->channels);
207   gst_caps_get_int (caps, "rate", &esdsink->frequency);
208
209   esdsink->bytes_per_sample = esdsink->channels * (esdsink->depth/8);
210
211   /* only u8/s16 */
212   if ((sign == FALSE && esdsink->depth != 8) ||
213       (sign == TRUE  && esdsink->depth != 16)) {
214     return GST_PAD_LINK_REFUSED;
215   }
216
217   gst_esdsink_close_audio (esdsink);
218   if (gst_esdsink_open_audio (esdsink)) {
219     esdsink->negotiated = TRUE;
220     return GST_PAD_LINK_OK;
221   }
222
223   return GST_PAD_LINK_REFUSED;
224 }
225
226 static int
227 gst_esdsink_get_latency (GstEsdsink *esdsink)
228 {
229   /* esd_get_latency() doesn't actually work.  So we return a
230    * fake value */
231   return 44100/2;
232 #if 0
233   return esd_get_latency (esdsink->fd);
234 #endif
235 }
236
237 static GstClockTime
238 gst_esdsink_get_time (GstClock *clock, gpointer data)
239 {
240   GstEsdsink *esdsink = GST_ESDSINK(data);
241   GstClockTime res;
242
243   res = (esdsink->handled - gst_esdsink_get_latency(esdsink))
244     * GST_SECOND / esdsink->frequency;
245
246   return res;
247 }
248
249 static GstClock *
250 gst_esdsink_get_clock (GstElement *element)
251 {
252   GstEsdsink *esdsink;
253
254   esdsink = GST_ESDSINK (element);
255
256   return GST_CLOCK(esdsink->provided_clock);
257 }
258
259 static void
260 gst_esdsink_set_clock (GstElement *element, GstClock *clock)
261 {
262   GstEsdsink *esdsink;
263
264   esdsink = GST_ESDSINK (element);
265
266   esdsink->clock = clock;
267 }
268
269 static void
270 gst_esdsink_chain (GstPad *pad, GstData *_data)
271 {
272   GstBuffer *buf = GST_BUFFER (_data);
273   GstEsdsink *esdsink;
274
275   esdsink = GST_ESDSINK (gst_pad_get_parent (pad));
276
277   if (!esdsink->negotiated) {
278     gst_element_error (GST_ELEMENT (esdsink), "not negotiated");
279     goto done;
280   }
281
282   if (GST_IS_EVENT(buf)){
283     GstEvent *event = GST_EVENT(buf);
284
285     switch(GST_EVENT_TYPE(event)){
286       case GST_EVENT_EOS:
287         gst_audio_clock_set_active (GST_AUDIO_CLOCK (esdsink->provided_clock),
288             FALSE);
289         gst_pad_event_default (pad, event);
290         return;
291       case GST_EVENT_DISCONTINUOUS:
292       {
293         gint64 value;
294
295         if (gst_event_discont_get_value (event, GST_FORMAT_TIME, &value)) {
296           if (!gst_clock_handle_discont (esdsink->clock, value)){
297             gst_audio_clock_set_active (GST_AUDIO_CLOCK (esdsink->provided_clock),
298                 FALSE);
299           }
300           esdsink->handled = 0;
301         }
302         esdsink->resync = TRUE;
303         break;
304       }
305       default:
306         gst_pad_event_default(pad, event);
307         return;
308     }
309     gst_event_unref(event);
310     return;
311   }
312
313   if (GST_BUFFER_DATA (buf) != NULL) {
314     if (!esdsink->mute && esdsink->fd >= 0) {
315       guchar *data = GST_BUFFER_DATA (buf);
316       gint size = GST_BUFFER_SIZE (buf);
317       gint to_write = 0;
318
319       if (esdsink->clock){
320         gint delay = 0;
321         gint64 queued;
322         GstClockTimeDiff jitter;
323
324         delay = gst_esdsink_get_latency (esdsink);
325         queued = delay * GST_SECOND / esdsink->frequency;
326
327         if (esdsink->resync && esdsink->sync) {
328           GstClockID id = gst_clock_new_single_shot_id (esdsink->clock,
329               GST_BUFFER_TIMESTAMP (buf) - queued);
330
331           gst_element_clock_wait (GST_ELEMENT (esdsink), id, &jitter);
332           gst_clock_id_free (id);
333
334           if (jitter >= 0){
335             gst_clock_handle_discont (esdsink->clock,
336                 GST_BUFFER_TIMESTAMP (buf) - queued + jitter);
337             to_write = size;
338             gst_audio_clock_set_active ((GstAudioClock *)esdsink->provided_clock, TRUE);
339             esdsink->resync = FALSE;
340           }
341         }else{
342           to_write = size;
343         }
344       }
345
346       GST_DEBUG ("esdsink: fd=%d data=%p size=%d",
347                  esdsink->fd, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
348       while (to_write > 0){
349         int done;
350
351         done = write (esdsink->fd, data, to_write);
352
353         if(done < 0){
354           if(errno==EINTR){
355             goto done;
356           }
357           g_assert_not_reached();
358         }
359
360         to_write -= done;
361         data += done;
362         esdsink->handled += done / esdsink->bytes_per_sample;
363       }
364
365     }
366   }
367
368   gst_audio_clock_update_time ((GstAudioClock *)esdsink->provided_clock,
369       GST_BUFFER_TIMESTAMP (buf));
370
371 done:
372   gst_buffer_unref (buf);
373 }
374
375 static void
376 gst_esdsink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
377 {
378   GstEsdsink *esdsink;
379
380   /* it's not null if we got it, but it might not be ours */
381   g_return_if_fail(GST_IS_ESDSINK(object));
382   esdsink = GST_ESDSINK(object);
383
384   switch (prop_id) {
385     case ARG_MUTE:
386       esdsink->mute = g_value_get_boolean (value);
387       break;
388     case ARG_HOST:
389       g_free(esdsink->host);
390       if (g_value_get_string (value) == NULL)
391           esdsink->host = NULL;
392       else
393           esdsink->host = g_strdup (g_value_get_string (value));
394       break;
395     case ARG_SYNC:
396       esdsink->sync = g_value_get_boolean (value);
397       break;
398     case ARG_FALLBACK:
399       esdsink->fallback = g_value_get_boolean (value);
400       break;
401     default:
402       break;
403   }
404 }
405
406 static void
407 gst_esdsink_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
408 {
409   GstEsdsink *esdsink;
410
411   esdsink = GST_ESDSINK(object);
412
413   switch (prop_id) {
414     case ARG_MUTE:
415       g_value_set_boolean (value, esdsink->mute);
416       break;
417     case ARG_HOST:
418       g_value_set_string (value, esdsink->host);
419       break;
420     case ARG_SYNC:
421       g_value_set_boolean (value, esdsink->sync);
422       break;
423     case ARG_FALLBACK:
424       g_value_set_boolean (value, esdsink->fallback);
425       break;
426     default:
427       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
428       break;
429   }
430 }
431
432 gboolean
433 gst_esdsink_factory_init (GstPlugin *plugin)
434 {
435   if (!gst_element_register (plugin, "esdsink", GST_RANK_NONE, GST_TYPE_ESDSINK))
436     return FALSE;
437
438   return TRUE;
439 }
440
441 static gboolean
442 gst_esdsink_open_audio (GstEsdsink *sink)
443 {
444   /* Name used by esound for this connection. */
445   const char * connname = "GStreamer";
446
447   /* Bitmap describing audio format. */
448   esd_format_t esdformat = ESD_STREAM | ESD_PLAY;
449
450   g_return_val_if_fail (sink->fd == -1, FALSE);
451
452   if (sink->depth == 16) esdformat |= ESD_BITS16;
453   else if (sink->depth == 8) esdformat |= ESD_BITS8;
454   else {
455     GST_DEBUG ("esdsink: invalid bit depth (%d)", sink->depth);
456     return FALSE;
457   }
458
459   if (sink->channels == 2) esdformat |= ESD_STEREO;
460   else if (sink->channels == 1) esdformat |= ESD_MONO;
461   else {
462     GST_DEBUG ("esdsink: invalid number of channels (%d)", sink->channels);
463     return FALSE;
464   }
465
466   GST_DEBUG ("esdsink: attempting to open connection to esound server");
467   if(sink->fallback){
468     sink->fd = esd_play_stream_fallback(esdformat, sink->frequency, sink->host, connname);
469   }else{
470     sink->fd = esd_play_stream(esdformat, sink->frequency, sink->host, connname);
471   }
472   if ( sink->fd < 0 ) {
473     GST_DEBUG ("esdsink: can't open connection to esound server");
474     return FALSE;
475   }
476
477   return TRUE;
478 }
479
480 static void
481 gst_esdsink_close_audio (GstEsdsink *sink)
482 {
483   if (sink->fd < 0) 
484     return;
485
486   close(sink->fd);
487   sink->fd = -1;
488
489   GST_DEBUG ("esdsink: closed sound device");
490 }
491
492 static GstElementStateReturn
493 gst_esdsink_change_state (GstElement *element)
494 {
495   GstEsdsink *esdsink;
496
497   esdsink = GST_ESDSINK (element);
498
499   switch (GST_STATE_TRANSITION (element)) {
500     case GST_STATE_NULL_TO_READY:
501       break;
502     case GST_STATE_READY_TO_PAUSED:
503       break;
504     case GST_STATE_PAUSED_TO_PLAYING:
505       break;
506     case GST_STATE_PLAYING_TO_PAUSED:
507       gst_audio_clock_set_active (GST_AUDIO_CLOCK (esdsink->provided_clock),
508           FALSE);
509       esdsink->resync = TRUE;
510       break;
511     case GST_STATE_PAUSED_TO_READY:
512       gst_esdsink_close_audio (GST_ESDSINK (element));
513       esdsink->negotiated = FALSE;
514       break;
515     case GST_STATE_READY_TO_NULL:
516       break;
517     default:
518       break;
519   }
520
521   if (GST_ELEMENT_CLASS (parent_class)->change_state)
522     return GST_ELEMENT_CLASS (parent_class)->change_state (element);
523
524   return GST_STATE_SUCCESS;
525 }
526