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