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