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