- modified to use _notify
[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   osssink->bps = 0;
273
274   law = gst_caps_get_int (caps, "law");
275   endianness = gst_caps_get_int (caps, "endianness");
276   sign = gst_caps_get_boolean (caps, "signed");
277
278   if (law == 0) {
279     if (width == 16) {
280       if (sign == TRUE) {
281         if (endianness == G_LITTLE_ENDIAN)
282           format = AFMT_S16_LE;
283         else if (endianness == G_BIG_ENDIAN)
284           format = AFMT_S16_BE;
285       }
286       else {
287         if (endianness == G_LITTLE_ENDIAN)
288           format = AFMT_U16_LE;
289         else if (endianness == G_BIG_ENDIAN)
290           format = AFMT_U16_BE;
291       }
292       osssink->bps = 2;
293     }
294     else if (width == 8) {
295       if (sign == TRUE) {
296         format = AFMT_S8;
297       }
298       else {
299         format = AFMT_U8;
300       }
301       osssink->bps = 1;
302     }
303   }
304
305   if (format == -1) 
306     return GST_PAD_CONNECT_REFUSED;
307
308   osssink->format = format;
309   osssink->channels = gst_caps_get_int (caps, "channels");
310   osssink->frequency = gst_caps_get_int (caps, "rate");
311
312   osssink->bps *= osssink->channels;
313   osssink->bps *= osssink->frequency;
314
315   if (!gst_osssink_sync_parms (osssink)) {
316     return GST_PAD_CONNECT_REFUSED;
317   }
318
319   return GST_PAD_CONNECT_OK;
320 }
321
322 static gboolean 
323 gst_osssink_sync_parms (GstOssSink *osssink) 
324 {
325   audio_buf_info ospace;
326   int frag;
327   gint target_format;
328   gint target_channels;
329   gint target_frequency;
330   GObject *object;
331
332   g_return_val_if_fail (osssink != NULL, FALSE);
333   g_return_val_if_fail (GST_IS_OSSSINK (osssink), FALSE);
334
335   if (osssink->fd == -1)
336     return FALSE;
337   
338   if (osssink->fragment >> 16)
339       frag = osssink->fragment;
340   else
341       frag = 0x7FFF0000 | osssink->fragment;
342   
343   GST_INFO (GST_CAT_PLUGIN_INFO, "osssink: trying to set sound card to %dHz %d bit %s (%08x fragment)",
344            osssink->frequency, osssink->format,
345            (osssink->channels == 2) ? "stereo" : "mono",frag);
346
347   ioctl (osssink->fd, SNDCTL_DSP_SETFRAGMENT, &frag);
348
349   ioctl (osssink->fd, SNDCTL_DSP_RESET, 0);
350
351   target_format = osssink->format;
352   target_channels = osssink->channels;
353   target_frequency = osssink->frequency;
354
355   ioctl (osssink->fd, SNDCTL_DSP_SETFMT, &osssink->format);
356   ioctl (osssink->fd, SNDCTL_DSP_CHANNELS, &osssink->channels);
357   ioctl (osssink->fd, SNDCTL_DSP_SPEED, &osssink->frequency);
358
359   ioctl (osssink->fd, SNDCTL_DSP_GETBLKSIZE, &osssink->fragment);
360   ioctl (osssink->fd, SNDCTL_DSP_GETOSPACE, &ospace);
361
362   GST_INFO (GST_CAT_PLUGIN_INFO, "osssink: set sound card to %dHz %d bit %s (%d bytes buffer, %08x fragment)",
363            osssink->frequency, osssink->format,
364            (osssink->channels == 2) ? "stereo" : "mono", ospace.bytes, osssink->fragment);
365
366   object = G_OBJECT (osssink);
367   g_object_freeze_notify (object);
368   g_object_notify (object, "channels");
369   g_object_notify (object, "frequency");
370   g_object_notify (object, "fragment");
371   g_object_notify (object, "format");
372   g_object_thaw_notify (object);
373
374   osssink->fragment_time = (1000000 * osssink->fragment) / osssink->bps;
375   GST_INFO (GST_CAT_PLUGIN_INFO, "fragment time %lu %llu\n", osssink->bps, osssink->fragment_time);
376
377   if (target_format != osssink->format ||
378       target_channels != osssink->channels ||
379       target_frequency != osssink->frequency) 
380   {
381     g_warning ("could not configure oss with required parameters, enjoy the noise :)");
382     /* we could eventually return FALSE here, or just do some additional tests
383      * to see that the frequencies don't differ too much etc.. */
384   }
385   return TRUE;
386 }
387
388 static void
389 gst_osssink_set_clock (GstElement *element, GstClock *clock)
390 {
391   GstOssSink *osssink;
392   
393   osssink = GST_OSSSINK (element);
394
395   osssink->clock = clock;  
396 }
397
398 static GstClock*
399 gst_osssink_get_clock (GstElement *element)
400 {
401   GstOssSink *osssink;
402   
403   osssink = GST_OSSSINK (element);
404
405   return osssink->provided_clock;
406 }
407
408 static void 
409 gst_osssink_chain (GstPad *pad, GstBuffer *buf) 
410 {
411   GstOssSink *osssink;
412   GstClockTime buftime;
413
414   /* this has to be an audio buffer */
415   osssink = GST_OSSSINK (gst_pad_get_parent (pad));
416
417   buftime = GST_BUFFER_TIMESTAMP (buf);
418
419   if (osssink->fd >= 0) {
420     if (!osssink->mute) {
421       guchar *data = GST_BUFFER_DATA (buf);
422       gint size = GST_BUFFER_SIZE (buf);
423
424       if (osssink->clock) {
425         if (osssink->clock == osssink->provided_clock) {
426           guint64 time;
427           gint granularity, granularity_time;
428           count_info optr;
429           audio_buf_info ospace;
430           gint queued;
431
432           /* FIXME, NEW_MEDIA/DISCONT?. Try to get our start point */
433           if (osssink->offset == 0LL && buftime != -1LL) {
434             /* gst_oss_clock_set_base (GST_OSS_CLOCK (osssink->clock), buftime); */
435             osssink->offset = buftime;
436           }
437
438           ioctl (osssink->fd, SNDCTL_DSP_GETOSPACE, &ospace);
439           ioctl (osssink->fd, SNDCTL_DSP_GETOPTR, &optr);
440
441           queued = (ospace.fragstotal * ospace.fragsize) - ospace.bytes;
442           time = osssink->offset + (optr.bytes) * 1000000LL / osssink->bps;
443
444           GST_DEBUG (GST_PLUGIN_INFO, "sync %llu %llu %d\n", buftime, time, queued);
445
446           granularity = ospace.fragsize;
447           /* granularity = size; */
448           granularity_time = granularity * osssink->fragment_time / ospace.fragsize;
449
450           while (size > 0) {
451             write (osssink->fd, data, MIN (size, granularity));
452             data += granularity;
453             size -= granularity;
454             time += granularity_time;
455             gst_clock_set_time (osssink->provided_clock, time);
456           }
457         }
458         else {
459           gst_element_clock_wait (GST_ELEMENT (osssink), osssink->clock, buftime);
460
461           write (osssink->fd, data, size);
462         }
463       }
464       else {
465         audio_buf_info ospace;
466
467         ioctl (osssink->fd, SNDCTL_DSP_GETOSPACE, &ospace);
468
469         if (ospace.bytes >= size) {
470           write (osssink->fd, data, size);
471         }
472       }
473     }
474   }
475   gst_buffer_unref (buf);
476 }
477
478 static void 
479 gst_osssink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) 
480 {
481   GstOssSink *osssink;
482
483   /* it's not null if we got it, but it might not be ours */
484   g_return_if_fail (GST_IS_OSSSINK (object));
485   
486   osssink = GST_OSSSINK (object);
487
488   switch (prop_id) {
489     case ARG_DEVICE:
490       /* disallow changing the device while it is opened
491          get_property("device") should return the right one */
492       if (!GST_FLAG_IS_SET (osssink, GST_OSSSINK_OPEN))
493       {
494         g_free (osssink->device);
495         osssink->device = g_strdup (g_value_get_string (value));
496         g_object_notify (object, "device");
497       }
498       break;
499     case ARG_MUTE:
500       osssink->mute = g_value_get_boolean (value);
501       g_object_notify (osssink, "mute");
502       break;
503     case ARG_FORMAT:
504       osssink->format = g_value_get_int (value);
505       gst_osssink_sync_parms (osssink);
506       break;
507     case ARG_CHANNELS:
508       osssink->channels = g_value_get_enum (value);
509       gst_osssink_sync_parms (osssink);
510       break;
511     case ARG_FREQUENCY:
512       osssink->frequency = g_value_get_int (value);
513       gst_osssink_sync_parms (osssink);
514       break;
515     case ARG_FRAGMENT:
516       osssink->fragment = g_value_get_int (value);
517       gst_osssink_sync_parms (osssink);
518       break;
519     case ARG_BUFFER_SIZE:
520       if (osssink->bufsize == g_value_get_int (value)) break;
521       osssink->bufsize = g_value_get_int (value);
522       osssink->sinkpool = gst_buffer_pool_get_default (osssink->bufsize, 6);
523       g_object_notify (object, "buffer_size");
524       break;
525     default:
526       break;
527   }
528 }
529
530 static void 
531 gst_osssink_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) 
532 {
533   GstOssSink *osssink;
534
535   /* it's not null if we got it, but it might not be ours */
536   g_return_if_fail (GST_IS_OSSSINK (object));
537   
538   osssink = GST_OSSSINK (object);
539
540   switch (prop_id) {
541     case ARG_DEVICE:
542       g_value_set_string (value, osssink->device);
543       break;
544     case ARG_MUTE:
545       g_value_set_boolean (value, osssink->mute);
546       break;
547     case ARG_FORMAT:
548       g_value_set_int (value, osssink->format);
549       break;
550     case ARG_CHANNELS:
551       g_value_set_enum (value, osssink->channels);
552       break;
553     case ARG_FREQUENCY:
554       g_value_set_int (value, osssink->frequency);
555       break;
556     case ARG_FRAGMENT:
557       g_value_set_int (value, osssink->fragment);
558       break;
559     case ARG_BUFFER_SIZE:
560       g_value_set_int (value, osssink->bufsize);
561       break;
562     default:
563       break;
564   }
565 }
566
567 static gboolean
568 gst_osssink_open_audio (GstOssSink *sink)
569 {
570   gint caps;
571   g_return_val_if_fail (sink->fd == -1, FALSE);
572
573   GST_INFO (GST_CAT_PLUGIN_INFO, "osssink: attempting to open sound device");
574
575   /* first try to open the sound card */
576   sink->fd = open(sink->device, O_WRONLY | O_NONBLOCK);
577   if (errno == EBUSY) {
578     g_warning ("osssink: unable to open the sound device (in use ?)\n");
579     return FALSE;
580   }
581
582   /* re-open the sound device in blocking mode */
583   close(sink->fd);
584   sink->fd = open(sink->device, O_WRONLY);
585
586   /* if we have it, set the default parameters and go have fun */
587   if (sink->fd >= 0) {
588     /* set card state */
589     ioctl(sink->fd, SNDCTL_DSP_GETCAPS, &caps);
590
591     GST_INFO(GST_CAT_PLUGIN_INFO, "osssink: Capabilities %08x", caps);
592
593     if (caps & DSP_CAP_DUPLEX)          GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   Full duplex");
594     if (caps & DSP_CAP_REALTIME)        GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   Realtime");
595     if (caps & DSP_CAP_BATCH)           GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   Batch");
596     if (caps & DSP_CAP_COPROC)          GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   Has coprocessor");
597     if (caps & DSP_CAP_TRIGGER)         GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   Trigger");
598     if (caps & DSP_CAP_MMAP)            GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   Direct access");
599
600 #ifdef DSP_CAP_MULTI
601     if (caps & DSP_CAP_MULTI)           GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   Multiple open");
602 #endif /* DSP_CAP_MULTI */
603
604 #ifdef DSP_CAP_BIND
605     if (caps & DSP_CAP_BIND)            GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   Channel binding");
606 #endif /* DSP_CAP_BIND */
607
608     ioctl(sink->fd, SNDCTL_DSP_GETFMTS, &caps);
609
610     GST_INFO (GST_CAT_PLUGIN_INFO, "osssink: Formats %08x", caps);
611     if (caps & AFMT_MU_LAW)             GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   MU_LAW");
612     if (caps & AFMT_A_LAW)              GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   A_LAW");
613     if (caps & AFMT_IMA_ADPCM)          GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   IMA_ADPCM");
614     if (caps & AFMT_U8)                 GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   U8");
615     if (caps & AFMT_S16_LE)             GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   S16_LE");
616     if (caps & AFMT_S16_BE)             GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   S16_BE");
617     if (caps & AFMT_S8)                 GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   S8");
618     if (caps & AFMT_U16_LE)             GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   U16_LE");
619     if (caps & AFMT_U16_BE)             GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   U16_BE");
620     if (caps & AFMT_MPEG)               GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   MPEG");
621 #ifdef AFMT_AC3
622     if (caps & AFMT_AC3)                GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   AC3");
623 #endif
624
625     GST_INFO (GST_CAT_PLUGIN_INFO, "osssink: opened audio (%s) with fd=%d", sink->device, sink->fd);
626     GST_FLAG_SET (sink, GST_OSSSINK_OPEN);
627
628     return TRUE;
629   }
630
631   return FALSE;
632 }
633
634 static void
635 gst_osssink_close_audio (GstOssSink *sink)
636 {
637   if (sink->fd < 0) return;
638
639   close(sink->fd);
640   sink->fd = -1;
641
642   GST_FLAG_UNSET (sink, GST_OSSSINK_OPEN);
643
644   GST_INFO (GST_CAT_PLUGIN_INFO, "osssink: closed sound device");
645 }
646
647 static GstElementStateReturn 
648 gst_osssink_change_state (GstElement *element) 
649 {
650   GstOssSink *osssink;
651
652   g_return_val_if_fail (GST_IS_OSSSINK (element), FALSE);
653
654   osssink = GST_OSSSINK (element);
655
656   switch (GST_STATE_TRANSITION (element)) {
657     case GST_STATE_NULL_TO_READY:
658       if (!GST_FLAG_IS_SET (element, GST_OSSSINK_OPEN)) {
659         if (!gst_osssink_open_audio (osssink)) {
660           return GST_STATE_FAILURE;
661         }
662       }
663       break;
664     case GST_STATE_READY_TO_PAUSED:
665       break;
666     case GST_STATE_PAUSED_TO_PLAYING:
667       gst_oss_clock_set_update (GST_OSS_CLOCK (osssink->provided_clock), TRUE);
668       break;
669     case GST_STATE_PLAYING_TO_PAUSED:
670     {
671       if (GST_FLAG_IS_SET (element, GST_OSSSINK_OPEN)) {
672         if (osssink->bps) {
673           GstClockTime time;
674           audio_buf_info ospace;
675           count_info optr;
676           gint queued;
677
678           ioctl (osssink->fd, SNDCTL_DSP_GETOSPACE, &ospace);
679           ioctl (osssink->fd, SNDCTL_DSP_GETOPTR, &optr);
680
681           queued = (ospace.fragstotal * ospace.fragsize) - ospace.bytes;
682           time = (optr.bytes + queued) * 1000000LL / osssink->bps;
683           ioctl (osssink->fd, SNDCTL_DSP_RESET, 0);
684
685           gst_oss_clock_set_update (GST_OSS_CLOCK (osssink->provided_clock), FALSE);
686           gst_clock_set_time (osssink->provided_clock, time);
687         }
688         else {
689           ioctl (osssink->fd, SNDCTL_DSP_RESET, 0);
690           gst_oss_clock_set_update (GST_OSS_CLOCK (osssink->provided_clock), FALSE);
691         }
692       }
693
694       break;
695     }
696     case GST_STATE_PAUSED_TO_READY:
697       if (GST_FLAG_IS_SET (element, GST_OSSSINK_OPEN))
698         ioctl (osssink->fd, SNDCTL_DSP_RESET, 0);
699       break;
700     case GST_STATE_READY_TO_NULL:
701       if (GST_FLAG_IS_SET (element, GST_OSSSINK_OPEN))
702         gst_osssink_close_audio (osssink);
703       break;
704   }
705       
706   if (GST_ELEMENT_CLASS (parent_class)->change_state)
707     return GST_ELEMENT_CLASS (parent_class)->change_state (element);
708
709   return GST_STATE_SUCCESS;
710 }
711
712 gboolean 
713 gst_osssink_factory_init (GstPlugin *plugin) 
714
715   GstElementFactory *factory;
716
717   factory = gst_elementfactory_new ("osssink", GST_TYPE_OSSSINK, &gst_osssink_details);
718   g_return_val_if_fail (factory != NULL, FALSE);
719
720   gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (osssink_sink_factory));
721
722   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
723
724   return TRUE;
725 }
726