Don't call oss_clock methods on the clock
[platform/upstream/gst-plugins-good.git] / sys / oss / gstosssink.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wim.taymans@chello.be>
4  *
5  * gstosssink.c: 
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
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <sys/ioctl.h>
27 #include <fcntl.h>
28 #include <sys/soundcard.h>
29 #include <unistd.h>
30 #include <errno.h>
31
32 #include <gstosssink.h>
33
34 static GstElementDetails gst_osssink_details = {  
35   "Audio Sink (OSS)",
36   "Sink/Audio",
37   "Output to a sound card via OSS",
38   VERSION,
39   "Erik Walthinsen <omega@cse.ogi.edu>, "
40   "Wim Taymans <wim.taymans@chello.be>",
41   "(C) 1999",
42 };
43
44 static void                     gst_osssink_class_init          (GstOssSinkClass *klass);
45 static void                     gst_osssink_init                (GstOssSink *osssink);
46 static void                     gst_osssink_finalize            (GObject *object);
47
48 static gboolean                 gst_osssink_open_audio          (GstOssSink *sink);
49 static void                     gst_osssink_close_audio         (GstOssSink *sink);
50 static gboolean                 gst_osssink_sync_parms          (GstOssSink *osssink);
51 static GstElementStateReturn    gst_osssink_change_state        (GstElement *element);
52 static void                     gst_osssink_set_clock           (GstElement *element, GstClock *clock);
53 static GstClock*                gst_osssink_get_clock           (GstElement *element);
54 static GstClockTime             gst_osssink_get_time            (GstClock *clock, gpointer data);
55
56 static GstPadConnectReturn      gst_osssink_sinkconnect         (GstPad *pad, GstCaps *caps);
57
58 static void                     gst_osssink_set_property        (GObject *object, guint prop_id, const GValue *value, 
59                                                                  GParamSpec *pspec);
60 static void                     gst_osssink_get_property        (GObject *object, guint prop_id, GValue *value, 
61                                                                  GParamSpec *pspec);
62
63 static void                     gst_osssink_chain               (GstPad *pad,GstBuffer *buf);
64
65 /* OssSink signals and args */
66 enum {
67   SIGNAL_HANDOFF,
68   LAST_SIGNAL
69 };
70
71 enum {
72   ARG_0,
73   ARG_DEVICE,
74   ARG_MUTE,
75   ARG_FORMAT,
76   ARG_CHANNELS,
77   ARG_FREQUENCY,
78   ARG_FRAGMENT,
79   ARG_BUFFER_SIZE,
80   ARG_SYNC
81   /* FILL ME */
82 };
83
84 GST_PAD_TEMPLATE_FACTORY (osssink_sink_factory,
85   "sink",
86   GST_PAD_SINK,
87   GST_PAD_ALWAYS,
88   GST_CAPS_NEW (
89     "osssink_sink",
90     "audio/raw",
91       "format",     GST_PROPS_STRING ("int"),   /* hack */
92       "law",        GST_PROPS_INT (0),
93       "endianness", GST_PROPS_INT (G_BYTE_ORDER),
94       "signed",     GST_PROPS_LIST (
95                       GST_PROPS_BOOLEAN (FALSE),
96                       GST_PROPS_BOOLEAN (TRUE)
97                     ),
98       "width",      GST_PROPS_LIST (
99                       GST_PROPS_INT (8),
100                       GST_PROPS_INT (16)
101                     ),
102       "depth",      GST_PROPS_LIST (
103                       GST_PROPS_INT (8),
104                       GST_PROPS_INT (16)
105                     ),
106       "rate",       GST_PROPS_INT_RANGE (1000, 48000),
107       "channels",   GST_PROPS_INT_RANGE (1, 2)
108   )
109 );
110
111 #define GST_TYPE_OSSSINK_CHANNELS (gst_osssink_channels_get_type())
112 static GType 
113 gst_osssink_channels_get_type(void) {
114   static GType osssink_channels_type = 0;
115   static GEnumValue osssink_channels[] = {
116     {0, "0", "Silence"},
117     {1, "1", "Mono"},
118     {2, "2", "Stereo"},
119     {0, NULL, NULL},
120   };
121   if (!osssink_channels_type) {
122     osssink_channels_type = g_enum_register_static("GstAudiosinkChannels", osssink_channels);
123   }
124   return osssink_channels_type;
125 }
126
127
128 static GstElementClass *parent_class = NULL;
129 static guint gst_osssink_signals[LAST_SIGNAL] = { 0 };
130
131 GType
132 gst_osssink_get_type (void) 
133 {
134   static GType osssink_type = 0;
135
136   if (!osssink_type) {
137     static const GTypeInfo osssink_info = {
138       sizeof(GstOssSinkClass),
139       NULL,
140       NULL,
141       (GClassInitFunc)gst_osssink_class_init,
142       NULL,
143       NULL,
144       sizeof(GstOssSink),
145       0,
146       (GInstanceInitFunc)gst_osssink_init,
147     };
148     osssink_type = g_type_register_static (GST_TYPE_ELEMENT, "GstOssSink", &osssink_info, 0);
149   }
150
151   return osssink_type;
152 }
153
154 static GstBufferPool*
155 gst_osssink_get_bufferpool (GstPad *pad)
156 {
157   GstOssSink *oss;
158   
159   oss = GST_OSSSINK (gst_pad_get_parent(pad));
160
161   return oss->sinkpool;
162 }
163
164 static void
165 gst_osssink_finalize (GObject *object)
166 {
167   GstOssSink *osssink = (GstOssSink *) object;
168
169   g_free (osssink->device);
170
171   G_OBJECT_CLASS (parent_class)->finalize (object);
172 }
173
174 static void
175 gst_osssink_class_init (GstOssSinkClass *klass) 
176 {
177   GObjectClass *gobject_class;
178   GstElementClass *gstelement_class;
179
180   gobject_class = (GObjectClass*)klass;
181   gstelement_class = (GstElementClass*)klass;
182
183   parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
184
185   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_DEVICE,
186     g_param_spec_string("device","device","device",
187                         "/dev/dsp",G_PARAM_READWRITE)); /* CHECKME! */
188   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_MUTE,
189     g_param_spec_boolean("mute","mute","mute",
190                          TRUE,G_PARAM_READWRITE)); 
191   g_object_class_install_property (G_OBJECT_CLASS(klass), ARG_SYNC,
192     g_param_spec_boolean("sync","Sync","If syncing on timestamps should be enabled",
193                          TRUE, G_PARAM_READWRITE)); 
194
195   /* it would be nice to show format in symbolic form, oh well */
196   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_FORMAT,
197     g_param_spec_int ("format","format","format",
198                       0, G_MAXINT, AFMT_S16_LE, G_PARAM_READWRITE)); 
199
200   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_CHANNELS,
201     g_param_spec_enum("channels","channels","channels",
202                       GST_TYPE_OSSSINK_CHANNELS,2,G_PARAM_READWRITE)); 
203   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_FREQUENCY,
204     g_param_spec_int("frequency","frequency","frequency",
205                      0,G_MAXINT,44100,G_PARAM_READWRITE));
206   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_FRAGMENT,
207     g_param_spec_int("fragment","fragment","fragment",
208                      0,G_MAXINT,6,G_PARAM_READWRITE));
209   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_BUFFER_SIZE,
210     g_param_spec_int("buffer_size","buffer_size","buffer_size",
211                      0,G_MAXINT,4096,G_PARAM_READWRITE));
212
213   gst_osssink_signals[SIGNAL_HANDOFF] =
214     g_signal_new("handoff",G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST,
215                    G_STRUCT_OFFSET(GstOssSinkClass,handoff), NULL, NULL,
216                    g_cclosure_marshal_VOID__VOID,G_TYPE_NONE,0);
217   
218   gobject_class->set_property = gst_osssink_set_property;
219   gobject_class->get_property = gst_osssink_get_property;
220   gobject_class->finalize     = gst_osssink_finalize;
221   
222   gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_osssink_change_state);
223 }
224
225 static void 
226 gst_osssink_init (GstOssSink *osssink) 
227 {
228   osssink->sinkpad = gst_pad_new_from_template (
229                   GST_PAD_TEMPLATE_GET (osssink_sink_factory), "sink");
230   gst_element_add_pad (GST_ELEMENT (osssink), osssink->sinkpad);
231   gst_pad_set_connect_function (osssink->sinkpad, gst_osssink_sinkconnect);
232   gst_pad_set_bufferpool_function (osssink->sinkpad, gst_osssink_get_bufferpool);
233
234   gst_pad_set_chain_function (osssink->sinkpad, gst_osssink_chain);
235
236   osssink->device = g_strdup ("/dev/dsp");
237   osssink->fd = -1;
238   osssink->channels = 1;
239   osssink->frequency = 11025;
240   osssink->fragment = 6;
241 /* AFMT_*_BE not available on all OSS includes (e.g. FBSD) */
242 #ifdef WORDS_BIGENDIAN
243   osssink->format = AFMT_S16_BE;
244 #else
245   osssink->format = AFMT_S16_LE;
246 #endif /* WORDS_BIGENDIAN */  
247   osssink->bufsize = 4096;
248   osssink->bps = 0;
249   osssink->resync = FALSE;
250   osssink->sync = TRUE;
251   /* 6 buffers per chunk by default */
252   osssink->sinkpool = gst_buffer_pool_get_default (osssink->bufsize, 6);
253   osssink->provided_clock = GST_CLOCK (gst_oss_clock_new ("ossclock", gst_osssink_get_time, osssink));
254   osssink->handled = 0;
255
256   GST_ELEMENT (osssink)->setclockfunc    = gst_osssink_set_clock;
257   GST_ELEMENT (osssink)->getclockfunc    = gst_osssink_get_clock;
258   
259   GST_FLAG_SET (osssink, GST_ELEMENT_THREAD_SUGGESTED);
260   GST_FLAG_SET (osssink, GST_ELEMENT_EVENT_AWARE);
261 }
262
263
264 static GstPadConnectReturn 
265 gst_osssink_sinkconnect (GstPad *pad, GstCaps *caps) 
266 {
267   gint law, endianness, width, depth;
268   gboolean sign;
269   gint format = -1;
270   GstOssSink *osssink = GST_OSSSINK (gst_pad_get_parent (pad));
271
272   if (!GST_CAPS_IS_FIXED (caps))
273     return GST_PAD_CONNECT_DELAYED;
274   
275   gst_caps_get_int (caps, "width", &width);
276   gst_caps_get_int (caps, "depth", &depth);
277
278   if (width != depth) 
279     return GST_PAD_CONNECT_REFUSED;
280
281   /* laws 1 and 2 are 1 bps anyway */
282   osssink->bps = 1;
283
284   gst_caps_get_int (caps, "law", &law);
285   gst_caps_get_int (caps, "endianness", &endianness);
286   gst_caps_get_boolean (caps, "signed", &sign);
287
288   if (law == 0) {
289     if (width == 16) {
290       if (sign == TRUE) {
291         if (endianness == G_LITTLE_ENDIAN)
292           format = AFMT_S16_LE;
293         else if (endianness == G_BIG_ENDIAN)
294           format = AFMT_S16_BE;
295       }
296       else {
297         if (endianness == G_LITTLE_ENDIAN)
298           format = AFMT_U16_LE;
299         else if (endianness == G_BIG_ENDIAN)
300           format = AFMT_U16_BE;
301       }
302       osssink->bps = 2;
303     }
304     else if (width == 8) {
305       if (sign == TRUE) {
306         format = AFMT_S8;
307       }
308       else {
309         format = AFMT_U8;
310       }
311       osssink->bps = 1;
312     }
313   } else if (law == 1) {
314     format = AFMT_MU_LAW;
315   } else if (law == 2) {
316     format = AFMT_A_LAW;
317   } else {
318     g_critical ("unknown law");
319     return GST_PAD_CONNECT_REFUSED;
320   }
321
322   if (format == -1) 
323     return GST_PAD_CONNECT_REFUSED;
324
325   osssink->format = format;
326   gst_caps_get_int (caps, "channels", &osssink->channels);
327   gst_caps_get_int (caps, "rate", &osssink->frequency);
328
329   osssink->bps *= osssink->channels;
330   osssink->bps *= osssink->frequency;
331
332   if (!gst_osssink_sync_parms (osssink)) {
333     return GST_PAD_CONNECT_REFUSED;
334   }
335
336   return GST_PAD_CONNECT_OK;
337 }
338
339 static gboolean 
340 gst_osssink_sync_parms (GstOssSink *osssink) 
341 {
342   audio_buf_info ospace;
343   int frag;
344   gint target_format;
345   gint target_channels;
346   gint target_frequency;
347   GObject *object;
348
349   g_return_val_if_fail (osssink != NULL, FALSE);
350   g_return_val_if_fail (GST_IS_OSSSINK (osssink), FALSE);
351
352   if (osssink->fd == -1)
353     return FALSE;
354   
355   if (osssink->fragment >> 16)
356     frag = osssink->fragment;
357   else
358     frag = 0x7FFF0000 | osssink->fragment;
359   
360   GST_INFO (GST_CAT_PLUGIN_INFO, "osssink: trying to set sound card to %dHz %d bit %s (%08x fragment)",
361            osssink->frequency, osssink->format,
362            (osssink->channels == 2) ? "stereo" : "mono",frag);
363
364   ioctl (osssink->fd, SNDCTL_DSP_SETFRAGMENT, &frag);
365
366   ioctl (osssink->fd, SNDCTL_DSP_RESET, 0);
367
368   target_format = osssink->format;
369   target_channels = osssink->channels;
370   target_frequency = osssink->frequency;
371
372   ioctl (osssink->fd, SNDCTL_DSP_SETFMT, &osssink->format);
373   ioctl (osssink->fd, SNDCTL_DSP_CHANNELS, &osssink->channels);
374   ioctl (osssink->fd, SNDCTL_DSP_SPEED, &osssink->frequency);
375
376   ioctl (osssink->fd, SNDCTL_DSP_GETBLKSIZE, &osssink->fragment);
377   ioctl (osssink->fd, SNDCTL_DSP_GETOSPACE, &ospace);
378
379   GST_INFO (GST_CAT_PLUGIN_INFO, "osssink: set sound card to %dHz %d bit %s (%d bytes buffer, %08x fragment)",
380            osssink->frequency, osssink->format,
381            (osssink->channels == 2) ? "stereo" : "mono", ospace.bytes, osssink->fragment);
382
383   object = G_OBJECT (osssink);
384   g_object_freeze_notify (object);
385   g_object_notify (object, "channels");
386   g_object_notify (object, "frequency");
387   g_object_notify (object, "fragment");
388   g_object_notify (object, "format");
389   g_object_thaw_notify (object);
390
391   osssink->fragment_time = (1000000 * osssink->fragment) / osssink->bps;
392   GST_INFO (GST_CAT_PLUGIN_INFO, "fragment time %u %llu\n", osssink->bps, osssink->fragment_time);
393
394   if (target_format != osssink->format ||
395       target_channels != osssink->channels ||
396       target_frequency != osssink->frequency) 
397   {
398     g_warning ("could not configure oss with required parameters, enjoy the noise :)");
399     /* we could eventually return FALSE here, or just do some additional tests
400      * to see that the frequencies don't differ too much etc.. */
401   }
402   return TRUE;
403 }
404
405 static inline gint64 
406 gst_osssink_get_delay (GstOssSink *osssink) 
407 {
408   gint delay = 0;
409
410   if (ioctl (osssink->fd, SNDCTL_DSP_GETODELAY, &delay) < 0) {
411     audio_buf_info info;
412     if (ioctl (osssink->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
413       delay = 0;
414     }
415     else {
416       delay = (info.fragstotal * info.fragsize) - info.bytes;     
417     }
418   }
419   return delay;
420 }
421
422 static GstClockTime 
423 gst_osssink_get_time (GstClock *clock, gpointer data) 
424 {
425   GstOssSink *osssink = GST_OSSSINK (data);
426   gint delay;
427   GstClockTime res;
428
429   if (!osssink->bps)
430     return 0;
431
432   delay = gst_osssink_get_delay (osssink);
433
434   /* sometimes delay is bigger than the number of bytes sent to the device, which screws
435    * up this calculation, we assume that everything is still in the device then */
436   if (((guint64)delay) > osssink->handled) {
437     delay = osssink->handled;
438   }
439   res =  (osssink->handled - delay) * GST_SECOND / osssink->bps;
440
441   /*
442   g_print ("from osssink: %lld %d %lld %d\n", res, delay, osssink->handled, osssink->bps);
443                   */
444
445   return res;
446 }
447
448 static GstClock*
449 gst_osssink_get_clock (GstElement *element)
450 {
451   GstOssSink *osssink;
452             
453   osssink = GST_OSSSINK (element);
454
455   return GST_CLOCK (osssink->provided_clock);
456 }
457
458 static void
459 gst_osssink_set_clock (GstElement *element, GstClock *clock)
460 {
461   GstOssSink *osssink;
462   
463   osssink = GST_OSSSINK (element);
464
465   osssink->clock = clock;  
466 }
467
468 static void 
469 gst_osssink_chain (GstPad *pad, GstBuffer *buf) 
470 {
471   GstOssSink *osssink;
472   GstClockTime buftime;
473
474   /* this has to be an audio buffer */
475   osssink = GST_OSSSINK (gst_pad_get_parent (pad));
476
477   if (GST_IS_EVENT (buf)) {
478     GstEvent *event = GST_EVENT (buf);
479
480     switch (GST_EVENT_TYPE (event)) {
481       case GST_EVENT_EOS:
482         ioctl (osssink->fd, SNDCTL_DSP_SYNC);
483         gst_oss_clock_set_active (osssink->provided_clock, FALSE);
484         gst_pad_event_default (pad, event);
485         return;
486       case GST_EVENT_NEW_MEDIA:
487         g_print ("new media\n");
488         return;
489       case GST_EVENT_DISCONTINUOUS:
490       {
491         gint64 value;
492         
493         ioctl (osssink->fd, SNDCTL_DSP_RESET);
494         if (gst_event_discont_get_value (event, GST_FORMAT_TIME, &value)) {
495           if (!gst_clock_handle_discont (osssink->clock, value))
496             gst_oss_clock_set_active (osssink->provided_clock, FALSE);
497           osssink->handled = 0;
498         }
499         osssink->resync = TRUE;
500         return;
501       }
502       default:
503         gst_pad_event_default (pad, event);
504         return;
505     }
506     gst_event_free (event);
507   }
508
509   if (!osssink->bps) {
510     gst_buffer_unref (buf);
511     gst_element_error (GST_ELEMENT (osssink), "capsnego was never performed, unknown data type");
512     return;
513   }
514
515   buftime = GST_BUFFER_TIMESTAMP (buf);
516
517   if (osssink->fd >= 0) {
518     if (!osssink->mute) {
519       guchar *data = GST_BUFFER_DATA (buf);
520       gint size = GST_BUFFER_SIZE (buf);
521
522       if (osssink->clock) {
523         gint delay = 0;
524         gint64 queued;
525         GstClockTimeDiff jitter;
526     
527         delay = gst_osssink_get_delay (osssink);
528         queued = delay * GST_SECOND / osssink->bps;
529
530         if  (osssink->resync && osssink->sync) {
531           gst_element_clock_wait (GST_ELEMENT (osssink), osssink->clock, 
532                                 buftime - queued, &jitter);
533
534           if (jitter > 0) {
535             gst_clock_handle_discont (osssink->clock, buftime - queued + jitter);
536             write (osssink->fd, data, size);
537             gst_oss_clock_set_active (osssink->provided_clock, TRUE);
538             osssink->resync = FALSE;
539             osssink->handled += size;
540           }
541         }
542         else {
543           write (osssink->fd, data, size);
544           osssink->handled += size;
545         }
546       }
547       /* no clock, try to be as fast as possible */
548       else {
549         audio_buf_info ospace;
550
551         ioctl (osssink->fd, SNDCTL_DSP_GETOSPACE, &ospace);
552
553         if (ospace.bytes >= size) {
554           write (osssink->fd, data, size);
555         }
556       }
557     }
558   }
559   gst_buffer_unref (buf);
560 }
561
562 static void 
563 gst_osssink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) 
564 {
565   GstOssSink *osssink;
566
567   /* it's not null if we got it, but it might not be ours */
568   g_return_if_fail (GST_IS_OSSSINK (object));
569   
570   osssink = GST_OSSSINK (object);
571
572   switch (prop_id) {
573     case ARG_DEVICE:
574       /* disallow changing the device while it is opened
575          get_property("device") should return the right one */
576       if (!GST_FLAG_IS_SET (osssink, GST_OSSSINK_OPEN))
577       {
578         g_free (osssink->device);
579         osssink->device = g_strdup (g_value_get_string (value));
580         g_object_notify (object, "device");
581       }
582       break;
583     case ARG_MUTE:
584       osssink->mute = g_value_get_boolean (value);
585       g_object_notify (G_OBJECT (osssink), "mute");
586       break;
587     case ARG_FORMAT:
588       osssink->format = g_value_get_int (value);
589       gst_osssink_sync_parms (osssink);
590       break;
591     case ARG_CHANNELS:
592       osssink->channels = g_value_get_enum (value);
593       gst_osssink_sync_parms (osssink);
594       break;
595     case ARG_FREQUENCY:
596       osssink->frequency = g_value_get_int (value);
597       gst_osssink_sync_parms (osssink);
598       break;
599     case ARG_FRAGMENT:
600       osssink->fragment = g_value_get_int (value);
601       gst_osssink_sync_parms (osssink);
602       break;
603     case ARG_BUFFER_SIZE:
604       if (osssink->bufsize == g_value_get_int (value)) break;
605       osssink->bufsize = g_value_get_int (value);
606       osssink->sinkpool = gst_buffer_pool_get_default (osssink->bufsize, 6);
607       g_object_notify (object, "buffer_size");
608       break;
609     case ARG_SYNC:
610       osssink->sync = g_value_get_boolean (value);
611       g_object_notify (G_OBJECT (osssink), "sync");
612     default:
613       break;
614   }
615 }
616
617 static void 
618 gst_osssink_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) 
619 {
620   GstOssSink *osssink;
621
622   /* it's not null if we got it, but it might not be ours */
623   g_return_if_fail (GST_IS_OSSSINK (object));
624   
625   osssink = GST_OSSSINK (object);
626
627   switch (prop_id) {
628     case ARG_DEVICE:
629       g_value_set_string (value, osssink->device);
630       break;
631     case ARG_MUTE:
632       g_value_set_boolean (value, osssink->mute);
633       break;
634     case ARG_FORMAT:
635       g_value_set_int (value, osssink->format);
636       break;
637     case ARG_CHANNELS:
638       g_value_set_enum (value, osssink->channels);
639       break;
640     case ARG_FREQUENCY:
641       g_value_set_int (value, osssink->frequency);
642       break;
643     case ARG_FRAGMENT:
644       g_value_set_int (value, osssink->fragment);
645       break;
646     case ARG_BUFFER_SIZE:
647       g_value_set_int (value, osssink->bufsize);
648       break;
649     case ARG_SYNC:
650       g_value_set_boolean (value, osssink->sync);
651       break;
652     default:
653       break;
654   }
655 }
656
657 static gboolean
658 gst_osssink_open_audio (GstOssSink *sink)
659 {
660   gint caps;
661   g_return_val_if_fail (sink->fd == -1, FALSE);
662
663   GST_INFO (GST_CAT_PLUGIN_INFO, "osssink: attempting to open sound device");
664
665   /* first try to open the sound card */
666   sink->fd = open(sink->device, O_WRONLY | O_NONBLOCK);
667   if (errno == EBUSY) {
668     g_warning ("osssink: unable to open the sound device (in use ?)\n");
669     return FALSE;
670   }
671
672   /* re-open the sound device in blocking mode */
673   close(sink->fd);
674   sink->fd = open(sink->device, O_WRONLY);
675
676   /* if we have it, set the default parameters and go have fun */
677   if (sink->fd >= 0) {
678     /* set card state */
679     ioctl(sink->fd, SNDCTL_DSP_GETCAPS, &caps);
680
681     GST_INFO(GST_CAT_PLUGIN_INFO, "osssink: Capabilities %08x", caps);
682
683     if (caps & DSP_CAP_DUPLEX)          GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   Full duplex");
684     if (caps & DSP_CAP_REALTIME)        GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   Realtime");
685     if (caps & DSP_CAP_BATCH)           GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   Batch");
686     if (caps & DSP_CAP_COPROC)          GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   Has coprocessor");
687     if (caps & DSP_CAP_TRIGGER)         GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   Trigger");
688     if (caps & DSP_CAP_MMAP)            GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   Direct access");
689
690 #ifdef DSP_CAP_MULTI
691     if (caps & DSP_CAP_MULTI)           GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   Multiple open");
692 #endif /* DSP_CAP_MULTI */
693
694 #ifdef DSP_CAP_BIND
695     if (caps & DSP_CAP_BIND)            GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   Channel binding");
696 #endif /* DSP_CAP_BIND */
697
698     ioctl(sink->fd, SNDCTL_DSP_GETFMTS, &caps);
699
700     GST_INFO (GST_CAT_PLUGIN_INFO, "osssink: Formats %08x", caps);
701     if (caps & AFMT_MU_LAW)             GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   MU_LAW");
702     if (caps & AFMT_A_LAW)              GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   A_LAW");
703     if (caps & AFMT_IMA_ADPCM)          GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   IMA_ADPCM");
704     if (caps & AFMT_U8)                 GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   U8");
705     if (caps & AFMT_S16_LE)             GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   S16_LE");
706     if (caps & AFMT_S16_BE)             GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   S16_BE");
707     if (caps & AFMT_S8)                 GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   S8");
708     if (caps & AFMT_U16_LE)             GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   U16_LE");
709     if (caps & AFMT_U16_BE)             GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   U16_BE");
710     if (caps & AFMT_MPEG)               GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   MPEG");
711 #ifdef AFMT_AC3
712     if (caps & AFMT_AC3)                GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   AC3");
713 #endif
714
715     GST_INFO (GST_CAT_PLUGIN_INFO, "osssink: opened audio (%s) with fd=%d", sink->device, sink->fd);
716     GST_FLAG_SET (sink, GST_OSSSINK_OPEN);
717
718     return TRUE;
719   }
720
721   return FALSE;
722 }
723
724 static void
725 gst_osssink_close_audio (GstOssSink *sink)
726 {
727   if (sink->fd < 0) return;
728
729   close(sink->fd);
730   sink->fd = -1;
731
732   GST_FLAG_UNSET (sink, GST_OSSSINK_OPEN);
733
734   GST_INFO (GST_CAT_PLUGIN_INFO, "osssink: closed sound device");
735 }
736
737 static GstElementStateReturn 
738 gst_osssink_change_state (GstElement *element) 
739 {
740   GstOssSink *osssink;
741
742   g_return_val_if_fail (GST_IS_OSSSINK (element), FALSE);
743
744   osssink = GST_OSSSINK (element);
745
746   switch (GST_STATE_TRANSITION (element)) {
747     case GST_STATE_NULL_TO_READY:
748       if (!GST_FLAG_IS_SET (element, GST_OSSSINK_OPEN)) {
749         if (!gst_osssink_open_audio (osssink)) {
750           return GST_STATE_FAILURE;
751         }
752       }
753       break;
754     case GST_STATE_READY_TO_PAUSED:
755     case GST_STATE_PAUSED_TO_PLAYING:
756       osssink->resync = TRUE;
757       break;
758     case GST_STATE_PLAYING_TO_PAUSED:
759     {
760       if (GST_FLAG_IS_SET (element, GST_OSSSINK_OPEN)) 
761         ioctl (osssink->fd, SNDCTL_DSP_RESET, 0);
762       gst_oss_clock_set_active (osssink->provided_clock, FALSE);
763       osssink->resync = TRUE;
764       break;
765     }
766     case GST_STATE_PAUSED_TO_READY:
767       if (GST_FLAG_IS_SET (element, GST_OSSSINK_OPEN))
768         ioctl (osssink->fd, SNDCTL_DSP_RESET, 0);
769       break;
770     case GST_STATE_READY_TO_NULL:
771       if (GST_FLAG_IS_SET (element, GST_OSSSINK_OPEN))
772         gst_osssink_close_audio (osssink);
773       break;
774   }
775       
776   if (GST_ELEMENT_CLASS (parent_class)->change_state)
777     return GST_ELEMENT_CLASS (parent_class)->change_state (element);
778
779   return GST_STATE_SUCCESS;
780 }
781
782 gboolean 
783 gst_osssink_factory_init (GstPlugin *plugin) 
784
785   GstElementFactory *factory;
786
787   factory = gst_element_factory_new ("osssink", GST_TYPE_OSSSINK, &gst_osssink_details);
788   g_return_val_if_fail (factory != NULL, FALSE);
789
790   gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (osssink_sink_factory));
791
792   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
793
794   return TRUE;
795 }
796