4672ab79f86845b78f987fa5791612c043972848
[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 GST_DEBUG_CATEGORY_EXTERN (esd_debug);
32 #define GST_CAT_DEFAULT esd_debug
33
34 /* elementfactory information */
35 static GstElementDetails esdsink_details = {
36   "Esound audio sink",
37   "Sink/Audio",
38   "Plays audio to an esound server",
39   "Richard Boulton <richard-gst@tartarus.org>",
40 };
41
42 /* Signals and args */
43 enum {
44   /* FILL ME */
45   LAST_SIGNAL
46 };
47
48 enum {
49   ARG_0,
50   ARG_MUTE,
51   ARG_HOST,
52   ARG_SYNC,
53   ARG_FALLBACK,
54 };
55
56 static GstStaticPadTemplate sink_factory = 
57 GST_STATIC_PAD_TEMPLATE (
58   "sink",
59   GST_PAD_SINK,
60   GST_PAD_ALWAYS,
61   GST_STATIC_CAPS (
62     "audio/x-raw-int, "
63       "endianness = (int) " G_STRINGIFY (G_BYTE_ORDER) ", "
64       "signed = (boolean) TRUE, "
65       "width = (int) 16, "
66       "depth = (int) 16, "
67       "rate = 44100, "
68       "channels = 2"
69   )
70 );
71
72 static void                     gst_esdsink_base_init           (gpointer g_class);
73 static void                     gst_esdsink_class_init          (gpointer g_class, gpointer class_data);
74 static void                     gst_esdsink_init                (GTypeInstance *instance, gpointer g_class);
75
76 static gboolean                 gst_esdsink_open_audio          (GstEsdsink *sink);
77 static void                     gst_esdsink_close_audio         (GstEsdsink *sink);
78 static GstElementStateReturn    gst_esdsink_change_state        (GstElement *element);
79
80 static GstClockTime             gst_esdsink_get_time            (GstClock *clock, gpointer data);
81 static GstClock *               gst_esdsink_get_clock           (GstElement *element);
82 static void                     gst_esdsink_set_clock           (GstElement *element, GstClock *clock);
83 static void                     gst_esdsink_chain               (GstPad *pad, GstData *_data);
84
85 static void                     gst_esdsink_set_property        (GObject *object, guint prop_id, 
86                                                                  const GValue *value, GParamSpec *pspec);
87 static void                     gst_esdsink_get_property        (GObject *object, guint prop_id, 
88                                                                  GValue *value, GParamSpec *pspec);
89
90 static GstElementClass *parent_class = NULL;
91 /*static guint gst_esdsink_signals[LAST_SIGNAL] = { 0 }; */
92
93 GType
94 gst_esdsink_get_type (void)
95 {
96   static GType esdsink_type = 0;
97
98   if (!esdsink_type) {
99     static const GTypeInfo esdsink_info = {
100       sizeof(GstEsdsinkClass),
101       gst_esdsink_base_init,
102       NULL,
103       gst_esdsink_class_init,
104       NULL,
105       NULL,
106       sizeof(GstEsdsink),
107       0,
108       gst_esdsink_init,
109     };
110     esdsink_type = g_type_register_static(GST_TYPE_ELEMENT, "GstEsdsink", &esdsink_info, 0);
111   }
112   return esdsink_type;
113 }
114
115 static void
116 gst_esdsink_base_init (gpointer g_class)
117 {
118   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
119
120   gst_element_class_add_pad_template (element_class, 
121       gst_static_pad_template_get (&sink_factory));
122   gst_element_class_set_details (element_class, &esdsink_details);
123 }
124
125 static void
126 gst_esdsink_class_init (gpointer g_class, gpointer class_data)
127 {
128   GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
129   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
130
131   parent_class = g_type_class_peek_parent (g_class);
132
133   g_object_class_install_property(gobject_class, ARG_MUTE,
134     g_param_spec_boolean("mute","mute","mute",
135                          TRUE,G_PARAM_READWRITE)); /* CHECKME */
136   g_object_class_install_property(gobject_class, ARG_HOST,
137     g_param_spec_string("host","host","host",
138                         NULL, G_PARAM_READWRITE)); /* CHECKME */
139   g_object_class_install_property(gobject_class, ARG_SYNC,
140     g_param_spec_boolean("sync","sync","Synchronize output to clock",
141                          TRUE,G_PARAM_READWRITE));
142 #if 0
143   /* This option is disabled because it is dumb in GStreamer's architecture. */
144   g_object_class_install_property(gobject_class, ARG_FALLBACK,
145     g_param_spec_boolean("fallback","fallback","Fall back to using OSS if Esound daemon is not present",
146                          FALSE,G_PARAM_READWRITE));
147 #endif
148
149   gobject_class->set_property = gst_esdsink_set_property;
150   gobject_class->get_property = gst_esdsink_get_property;
151
152   gstelement_class->change_state = gst_esdsink_change_state;
153   gstelement_class->set_clock    = gst_esdsink_set_clock;
154   gstelement_class->get_clock    = gst_esdsink_get_clock;
155 }
156
157 static void
158 gst_esdsink_init(GTypeInstance *instance, gpointer g_class)
159 {
160   GstEsdsink *esdsink = GST_ESDSINK (instance);
161   
162   esdsink->sinkpad = gst_pad_new_from_template (
163       gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (instance), "sink"), 
164       "sink");
165   gst_element_add_pad(GST_ELEMENT(esdsink), esdsink->sinkpad);
166   gst_pad_set_chain_function(esdsink->sinkpad, GST_DEBUG_FUNCPTR(gst_esdsink_chain));
167
168   GST_FLAG_SET (esdsink, GST_ELEMENT_EVENT_AWARE);
169
170   esdsink->mute = FALSE;
171   esdsink->fd = -1;
172   /* FIXME: get default from somewhere better than just putting them inline. */
173   /*esdsink->negotiated = FALSE;*/
174   /* we have static caps on our template, so it always is negotiated */
175   esdsink->negotiated = TRUE;
176   esdsink->format = 16;
177   esdsink->depth = 16;
178   esdsink->channels = 2;
179   esdsink->frequency = 44100;
180   esdsink->bytes_per_sample = esdsink->channels * (esdsink->depth/8);
181   esdsink->host = getenv ("ESPEAKER");
182   esdsink->provided_clock = gst_audio_clock_new("esdclock", gst_esdsink_get_time, esdsink);
183   gst_object_set_parent(GST_OBJECT(esdsink->provided_clock), GST_OBJECT(esdsink));
184   esdsink->sync = TRUE;
185   esdsink->fallback = FALSE;
186 }
187
188 #ifdef unused
189 static GstPadLinkReturn
190 gst_esdsink_link (GstPad *pad, const GstCaps *caps)
191 {
192   GstEsdsink *esdsink;
193   GstStructure *structure;
194
195   esdsink = GST_ESDSINK (gst_pad_get_parent (pad));
196
197   structure = gst_caps_get_structure (caps, 0);
198   gst_structure_get_int (structure, "depth", &esdsink->depth);
199   gst_structure_get_int (structure, "channels", &esdsink->channels);
200   gst_structure_get_int (structure, "rate", &esdsink->frequency);
201
202   esdsink->bytes_per_sample = esdsink->channels * (esdsink->depth/8);
203
204   gst_esdsink_close_audio (esdsink);
205   if (gst_esdsink_open_audio (esdsink)) {
206     esdsink->negotiated = TRUE;
207     return GST_PAD_LINK_OK;
208   }
209   /* FIXME: is it supposed to be correct to have closed audio when caps nego 
210      failed? */
211
212   GST_DEBUG ("esd link function could not negotiate, returning delayed");
213   return GST_PAD_LINK_REFUSED;
214 }
215 #endif
216
217 static GstClockTime
218 gst_esdsink_get_time (GstClock *clock, gpointer data)
219 {
220   GstEsdsink *esdsink = GST_ESDSINK(data);
221   GstClockTime res;
222
223   res = (esdsink->handled * GST_SECOND) / esdsink->frequency;
224     //- GST_SECOND * 2;
225
226   return res;
227 }
228
229 static GstClock *
230 gst_esdsink_get_clock (GstElement *element)
231 {
232   GstEsdsink *esdsink;
233
234   esdsink = GST_ESDSINK (element);
235
236   return GST_CLOCK(esdsink->provided_clock);
237 }
238
239 static void
240 gst_esdsink_set_clock (GstElement *element, GstClock *clock)
241 {
242   GstEsdsink *esdsink;
243
244   esdsink = GST_ESDSINK (element);
245
246   esdsink->clock = clock;
247 }
248
249 static void
250 gst_esdsink_chain (GstPad *pad, GstData *_data)
251 {
252   GstBuffer *buf = GST_BUFFER (_data);
253   GstEsdsink *esdsink;
254
255   esdsink = GST_ESDSINK (gst_pad_get_parent (pad));
256
257   if (!esdsink->negotiated) {
258     GST_ELEMENT_ERROR (esdsink, CORE, NEGOTIATION, (NULL),
259                        ("element wasn't negotiated before chain function"));
260     goto done;
261   }
262
263   if (GST_IS_EVENT(buf)){
264     GstEvent *event = GST_EVENT(buf);
265
266     switch(GST_EVENT_TYPE(event)){
267       case GST_EVENT_EOS:
268         gst_audio_clock_set_active (GST_AUDIO_CLOCK (esdsink->provided_clock),
269             FALSE);
270         gst_pad_event_default (pad, event);
271         return;
272       default:
273         gst_pad_event_default(pad, event);
274         return;
275     }
276     gst_event_unref(event);
277     return;
278   }
279
280   if (GST_BUFFER_DATA (buf) != NULL) {
281     if (!esdsink->mute && esdsink->fd >= 0) {
282       guchar *data = GST_BUFFER_DATA (buf);
283       gint size = GST_BUFFER_SIZE (buf);
284       gint to_write = 0;
285
286       to_write = size;
287
288       GST_LOG ("fd=%d data=%p size=%d",
289                 esdsink->fd, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
290       while (to_write > 0){
291         int done;
292
293         done = write (esdsink->fd, data, to_write);
294
295         if(done < 0){
296           if(errno==EINTR){
297             goto done;
298           }
299           g_assert_not_reached();
300         }
301
302         to_write -= done;
303         data += done;
304         esdsink->handled += done / esdsink->bytes_per_sample;
305       }
306
307     }
308   }
309
310   gst_audio_clock_update_time ((GstAudioClock *)esdsink->provided_clock,
311       gst_esdsink_get_time (esdsink->provided_clock, esdsink));
312
313 done:
314   gst_buffer_unref (buf);
315 }
316
317 static void
318 gst_esdsink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
319 {
320   GstEsdsink *esdsink;
321
322   /* it's not null if we got it, but it might not be ours */
323   g_return_if_fail(GST_IS_ESDSINK(object));
324   esdsink = GST_ESDSINK(object);
325
326   switch (prop_id) {
327     case ARG_MUTE:
328       esdsink->mute = g_value_get_boolean (value);
329       break;
330     case ARG_HOST:
331       g_free(esdsink->host);
332       if (g_value_get_string (value) == NULL)
333           esdsink->host = NULL;
334       else
335           esdsink->host = g_strdup (g_value_get_string (value));
336       break;
337     case ARG_SYNC:
338       esdsink->sync = g_value_get_boolean (value);
339       break;
340     case ARG_FALLBACK:
341       esdsink->fallback = g_value_get_boolean (value);
342       break;
343     default:
344       break;
345   }
346 }
347
348 static void
349 gst_esdsink_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
350 {
351   GstEsdsink *esdsink;
352
353   esdsink = GST_ESDSINK(object);
354
355   switch (prop_id) {
356     case ARG_MUTE:
357       g_value_set_boolean (value, esdsink->mute);
358       break;
359     case ARG_HOST:
360       g_value_set_string (value, esdsink->host);
361       break;
362     case ARG_SYNC:
363       g_value_set_boolean (value, esdsink->sync);
364       break;
365     case ARG_FALLBACK:
366       g_value_set_boolean (value, esdsink->fallback);
367       break;
368     default:
369       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
370       break;
371   }
372 }
373
374 gboolean
375 gst_esdsink_factory_init (GstPlugin *plugin)
376 {
377   if (!gst_element_register (plugin, "esdsink", GST_RANK_NONE, GST_TYPE_ESDSINK))
378     return FALSE;
379
380   return TRUE;
381 }
382
383 static gboolean
384 gst_esdsink_open_audio (GstEsdsink *sink)
385 {
386   /* Name used by esound for this connection. */
387   const char * connname = "GStreamer";
388
389   /* Bitmap describing audio format. */
390   esd_format_t esdformat = ESD_STREAM | ESD_PLAY;
391
392   g_return_val_if_fail (sink->fd == -1, FALSE);
393
394   if (sink->depth == 16) esdformat |= ESD_BITS16;
395   else if (sink->depth == 8) esdformat |= ESD_BITS8;
396   else {
397     GST_ELEMENT_ERROR (sink, STREAM, FORMAT, (NULL),
398                        ("invalid bit depth (%d)", sink->depth));
399     return FALSE;
400   }
401
402   if (sink->channels == 2) esdformat |= ESD_STEREO;
403   else if (sink->channels == 1) esdformat |= ESD_MONO;
404   else {
405     GST_ELEMENT_ERROR (sink, STREAM, FORMAT, (NULL),
406                        ("invalid number of channels (%d)", sink->channels));
407     return FALSE;
408   }
409
410   GST_INFO ("attempting to open connection to esound server");
411   if(sink->fallback){
412     sink->fd = esd_play_stream_fallback(esdformat, sink->frequency, sink->host, connname);
413   }else{
414     sink->fd = esd_play_stream(esdformat, sink->frequency, sink->host, connname);
415   }
416   if ( sink->fd < 0 ) {
417     GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (NULL),
418                        ("can't open connection to esound server"));
419     return FALSE;
420   }
421   GST_INFO ("successfully opened connection to esound server");
422
423   return TRUE;
424 }
425
426 static void
427 gst_esdsink_close_audio (GstEsdsink *sink)
428 {
429   if (sink->fd < 0) 
430     return;
431
432   close(sink->fd);
433   sink->fd = -1;
434
435   GST_INFO ("esdsink: closed sound device");
436 }
437
438 static GstElementStateReturn
439 gst_esdsink_change_state (GstElement *element)
440 {
441   GstEsdsink *esdsink;
442
443   esdsink = GST_ESDSINK (element);
444
445   switch (GST_STATE_TRANSITION (element)) {
446     case GST_STATE_NULL_TO_READY:
447       if (!gst_esdsink_open_audio (GST_ESDSINK (element))) {
448         return GST_STATE_FAILURE;
449       }
450       break;
451     case GST_STATE_READY_TO_PAUSED:
452       break;
453     case GST_STATE_PAUSED_TO_PLAYING:
454       gst_audio_clock_set_active (GST_AUDIO_CLOCK (esdsink->provided_clock),
455           TRUE);
456       break;
457     case GST_STATE_PLAYING_TO_PAUSED:
458       gst_audio_clock_set_active (GST_AUDIO_CLOCK (esdsink->provided_clock),
459           FALSE);
460       esdsink->resync = TRUE;
461       break;
462     case GST_STATE_PAUSED_TO_READY:
463       break;
464     case GST_STATE_READY_TO_NULL:
465       gst_esdsink_close_audio (GST_ESDSINK (element));
466       break;
467     default:
468       break;
469   }
470
471   if (GST_ELEMENT_CLASS (parent_class)->change_state)
472     return GST_ELEMENT_CLASS (parent_class)->change_state (element);
473
474   return GST_STATE_SUCCESS;
475 }
476