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