first batch
[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 void                     gst_osssink_sync_parms          (GstOssSink *osssink);
51 static GstElementStateReturn    gst_osssink_change_state        (GstElement *element);
52 static GstPadNegotiateReturn    gst_osssink_negotiate           (GstPad *pad, GstCaps **caps, gpointer *user_data);
53
54 static void                     gst_osssink_set_property        (GObject *object, guint prop_id, const GValue *value, 
55                                                                  GParamSpec *pspec);
56 static void                     gst_osssink_get_property        (GObject *object, guint prop_id, GValue *value, 
57                                                                  GParamSpec *pspec);
58
59 static void                     gst_osssink_chain               (GstPad *pad,GstBuffer *buf);
60
61 /* OssSink signals and args */
62 enum {
63   SIGNAL_HANDOFF,
64   LAST_SIGNAL
65 };
66
67 enum {
68   ARG_0,
69   ARG_DEVICE,
70   ARG_MUTE,
71   ARG_FORMAT,
72   ARG_CHANNELS,
73   ARG_FREQUENCY,
74   ARG_FRAGMENT,
75   ARG_BUFFER_SIZE
76   /* FILL ME */
77 };
78
79 GST_PADTEMPLATE_FACTORY (osssink_sink_factory,
80   "sink",
81   GST_PAD_SINK,
82   GST_PAD_ALWAYS,
83   GST_CAPS_NEW (
84     "osssink_sink",
85     "audio/raw",
86       "format",     GST_PROPS_STRING ("int"),   // hack
87       "law",        GST_PROPS_INT (0),
88       "endianness", GST_PROPS_INT (G_BYTE_ORDER),
89       "signed",     GST_PROPS_LIST (
90                       GST_PROPS_BOOLEAN (FALSE),
91                       GST_PROPS_BOOLEAN (TRUE)
92                     ),
93       "width",      GST_PROPS_LIST (
94                       GST_PROPS_INT (8),
95                       GST_PROPS_INT (16)
96                     ),
97       "depth",      GST_PROPS_LIST (
98                       GST_PROPS_INT (8),
99                       GST_PROPS_INT (16)
100                     ),
101       "rate",       GST_PROPS_INT_RANGE (8000, 48000),
102       "channels",   GST_PROPS_INT_RANGE (1, 2)
103   )
104 );
105
106 #define GST_TYPE_OSSSINK_CHANNELS (gst_osssink_channels_get_type())
107 static GType 
108 gst_osssink_channels_get_type(void) {
109   static GType osssink_channels_type = 0;
110   static GEnumValue osssink_channels[] = {
111     {0, "0", "Silence"},
112     {1, "1", "Mono"},
113     {2, "2", "Stereo"},
114     {0, NULL, NULL},
115   };
116   if (!osssink_channels_type) {
117     osssink_channels_type = g_enum_register_static("GstAudiosinkChannels", osssink_channels);
118   }
119   return osssink_channels_type;
120 }
121
122
123 static GstElementClass *parent_class = NULL;
124 static guint gst_osssink_signals[LAST_SIGNAL] = { 0 };
125
126 GType
127 gst_osssink_get_type (void) 
128 {
129   static GType osssink_type = 0;
130
131   if (!osssink_type) {
132     static const GTypeInfo osssink_info = {
133       sizeof(GstOssSinkClass),
134       NULL,
135       NULL,
136       (GClassInitFunc)gst_osssink_class_init,
137       NULL,
138       NULL,
139       sizeof(GstOssSink),
140       0,
141       (GInstanceInitFunc)gst_osssink_init,
142     };
143     osssink_type = g_type_register_static (GST_TYPE_ELEMENT, "GstOssSink", &osssink_info, 0);
144   }
145
146   return osssink_type;
147 }
148
149 static GstBufferPool*
150 gst_osssink_get_bufferpool (GstPad *pad)
151 {
152   GstOssSink *oss;
153   
154   oss = GST_OSSSINK (gst_pad_get_parent(pad));
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","device",
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",
185                          TRUE,G_PARAM_READWRITE)); 
186
187   // it would be nice to show format in symbolic form, oh well
188   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_FORMAT,
189     g_param_spec_int ("format","format","format",
190                       0, G_MAXINT, AFMT_S16_LE, G_PARAM_READWRITE)); 
191
192   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_CHANNELS,
193     g_param_spec_enum("channels","channels","channels",
194                       GST_TYPE_OSSSINK_CHANNELS,2,G_PARAM_READWRITE)); 
195   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_FREQUENCY,
196     g_param_spec_int("frequency","frequency","frequency",
197                      0,G_MAXINT,44100,G_PARAM_READWRITE));
198   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_FRAGMENT,
199     g_param_spec_int("fragment","fragment","fragment",
200                      0,G_MAXINT,6,G_PARAM_READWRITE));
201   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_BUFFER_SIZE,
202     g_param_spec_int("buffer_size","buffer_size","buffer_size",
203                      0,G_MAXINT,4096,G_PARAM_READWRITE));
204
205   gst_osssink_signals[SIGNAL_HANDOFF] =
206     g_signal_new("handoff",G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST,
207                    G_STRUCT_OFFSET(GstOssSinkClass,handoff), NULL, NULL,
208                    g_cclosure_marshal_VOID__VOID,G_TYPE_NONE,0);
209   
210   gobject_class->set_property = gst_osssink_set_property;
211   gobject_class->get_property = gst_osssink_get_property;
212   gobject_class->finalize     = gst_osssink_finalize;
213   
214   gstelement_class->change_state = gst_osssink_change_state;
215 }
216
217 static void 
218 gst_osssink_init (GstOssSink *osssink) 
219 {
220   osssink->sinkpad = gst_pad_new_from_template (
221                   GST_PADTEMPLATE_GET (osssink_sink_factory), "sink");
222   gst_element_add_pad (GST_ELEMENT (osssink), osssink->sinkpad);
223   gst_pad_set_negotiate_function (osssink->sinkpad, gst_osssink_negotiate);
224   gst_pad_set_bufferpool_function (osssink->sinkpad, gst_osssink_get_bufferpool);
225
226   gst_pad_set_chain_function (osssink->sinkpad, gst_osssink_chain);
227
228   osssink->device = g_strdup ("/dev/dsp");
229   osssink->fd = -1;
230   osssink->clock = gst_clock_get_system();
231   osssink->channels = 1;
232   osssink->frequency = 11025;
233   osssink->fragment = 6;
234 /* AFMT_*_BE not available on all OSS includes (e.g. FBSD) */
235 #ifdef WORDS_BIGENDIAN
236   osssink->format = AFMT_S16_BE;
237 #else
238   osssink->format = AFMT_S16_LE;
239 #endif /* WORDS_BIGENDIAN */  
240   gst_clock_register (osssink->clock, GST_OBJECT (osssink));
241   osssink->bufsize = 4096;
242   /* 6 buffers per chunk by default */
243   osssink->sinkpool = gst_buffer_pool_get_default (osssink->bufsize, 6);
244   
245   GST_FLAG_SET (osssink, GST_ELEMENT_THREAD_SUGGESTED);
246 }
247
248 static gboolean
249 gst_osssink_parse_caps (GstOssSink *osssink, GstCaps *caps)
250 {
251   gint law, endianness, width, depth;
252   gboolean sign;
253   gint format = -1;
254
255   // deal with the case where there are no props...
256   if (gst_caps_get_props(caps) == NULL) return FALSE;
257   
258   width = gst_caps_get_int (caps, "width");
259   depth = gst_caps_get_int (caps, "depth");
260
261   if (width != depth) return FALSE;
262
263   law = gst_caps_get_int (caps, "law");
264   endianness = gst_caps_get_int (caps, "endianness");
265   sign = gst_caps_get_boolean (caps, "signed");
266
267   if (law == 0) {
268     if (width == 16) {
269       if (sign == TRUE) {
270         if (endianness == G_LITTLE_ENDIAN)
271           format = AFMT_S16_LE;
272         else if (endianness == G_BIG_ENDIAN)
273           format = AFMT_S16_BE;
274       }
275       else {
276         if (endianness == G_LITTLE_ENDIAN)
277           format = AFMT_U16_LE;
278         else if (endianness == G_BIG_ENDIAN)
279           format = AFMT_U16_BE;
280       }
281     }
282     else if (width == 8) {
283       if (sign == TRUE) {
284         format = AFMT_S8;
285       }
286       else {
287         format = AFMT_U8;
288       }
289     }
290   }
291
292   if (format == -1) 
293    return FALSE;
294
295   osssink->format = format;
296   osssink->channels = gst_caps_get_int (caps, "channels");
297   osssink->frequency = gst_caps_get_int (caps, "rate");
298
299   return TRUE;
300 }
301
302 static GstPadNegotiateReturn 
303 gst_osssink_negotiate (GstPad *pad, GstCaps **caps, gpointer *user_data) 
304 {
305   GstOssSink *osssink;
306
307   g_return_val_if_fail (pad != NULL, GST_PAD_NEGOTIATE_FAIL);
308   g_return_val_if_fail (GST_IS_PAD (pad), GST_PAD_NEGOTIATE_FAIL);
309
310   osssink = GST_OSSSINK (gst_pad_get_parent (pad));
311
312   GST_INFO (GST_CAT_NEGOTIATION, "osssink: negotiate");
313   // we decide
314   if (user_data == NULL) {
315     *caps = NULL;
316     return GST_PAD_NEGOTIATE_TRY;
317   }
318   // have we got caps?
319   else if (*caps) {
320
321     if (gst_osssink_parse_caps (osssink, *caps)) {
322       gst_osssink_sync_parms (osssink);
323
324       return GST_PAD_NEGOTIATE_AGREE;
325     }
326
327     // FIXME check if the sound card was really set to these caps,
328     // else send out another caps..
329
330     return GST_PAD_NEGOTIATE_FAIL;
331   }
332   
333   return GST_PAD_NEGOTIATE_FAIL;
334 }
335
336 static void 
337 gst_osssink_sync_parms (GstOssSink *osssink) 
338 {
339   audio_buf_info ospace;
340   int frag;
341
342   g_return_if_fail (osssink != NULL);
343   g_return_if_fail (GST_IS_OSSSINK (osssink));
344
345   if (osssink->fd == -1) return;
346   
347   if (osssink->fragment >> 16)
348       frag = osssink->fragment;
349   else
350       frag = 0x7FFF0000 | osssink->fragment;
351   
352   ioctl (osssink->fd, SNDCTL_DSP_SETFRAGMENT, &frag);
353
354   ioctl (osssink->fd, SNDCTL_DSP_RESET, 0);
355
356   ioctl (osssink->fd, SNDCTL_DSP_SETFMT, &osssink->format);
357   ioctl (osssink->fd, SNDCTL_DSP_CHANNELS, &osssink->channels);
358   ioctl (osssink->fd, SNDCTL_DSP_SPEED, &osssink->frequency);
359
360   ioctl (osssink->fd, SNDCTL_DSP_GETBLKSIZE, &frag);
361   ioctl (osssink->fd, SNDCTL_DSP_GETOSPACE, &ospace);
362
363   /*
364   g_warning ("osssink: setting sound card to %dHz %d bit %s (%d bytes buffer, %d fragment)\n",
365            osssink->frequency, osssink->format,
366            (osssink->channels == 2) ? "stereo" : "mono", ospace.bytes, frag);
367            */
368   GST_INFO (GST_CAT_PLUGIN_INFO, "osssink: setting sound card to %dHz %d bit %s (%d bytes buffer, %d fragment)",
369            osssink->frequency, osssink->format,
370            (osssink->channels == 2) ? "stereo" : "mono", ospace.bytes, frag);
371
372 }
373
374 static void 
375 gst_osssink_chain (GstPad *pad, GstBuffer *buf) 
376 {
377   GstOssSink *osssink;
378   gboolean in_flush;
379   audio_buf_info ospace;
380
381   g_return_if_fail (pad != NULL);
382   g_return_if_fail (GST_IS_PAD (pad));
383   g_return_if_fail (buf != NULL);
384   
385   
386   /* this has to be an audio buffer */
387   osssink = GST_OSSSINK (gst_pad_get_parent (pad));
388 //  g_return_if_fail(GST_FLAG_IS_SET(osssink,GST_STATE_RUNNING));
389
390   if (GST_IS_EVENT (buf)) {
391     g_print ("eos on osssink\n");
392     gst_element_set_state (osssink, GST_STATE_PAUSED);
393     gst_event_free (GST_EVENT (buf));
394   }
395
396   if ((in_flush = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLUSH))) {
397     GST_DEBUG (GST_CAT_PLUGIN_INFO,"osssink: flush\n");
398     ioctl (osssink->fd, SNDCTL_DSP_RESET, 0);
399   }
400
401   g_signal_emit (G_OBJECT (osssink), gst_osssink_signals[SIGNAL_HANDOFF], 0,
402                   osssink);
403
404   if (GST_BUFFER_DATA (buf) != NULL) {
405 #ifndef GST_DISABLE_TRACE
406     gst_trace_add_entry(NULL, 0, buf, "osssink: writing to soundcard");
407 #endif // GST_DISABLE_TRACE
408     //g_print("osssink: writing to soundcard\n");
409     if (osssink->fd >= 0) {
410       if (!osssink->mute) {
411         gst_clock_wait (osssink->clock, GST_BUFFER_TIMESTAMP (buf), GST_OBJECT (osssink));
412         ioctl (osssink->fd, SNDCTL_DSP_GETOSPACE, &ospace);
413         GST_DEBUG (GST_CAT_PLUGIN_INFO,"osssink: (%d bytes buffer) %d %p %d\n", ospace.bytes, 
414                         osssink->fd, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
415         write (osssink->fd, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
416         //write(STDOUT_FILENO,GST_BUFFER_DATA(buf),GST_BUFFER_SIZE(buf));
417       }
418     }
419   }
420   gst_buffer_unref (buf);
421 }
422
423 static void 
424 gst_osssink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) 
425 {
426   GstOssSink *osssink;
427
428   /* it's not null if we got it, but it might not be ours */
429   g_return_if_fail (GST_IS_OSSSINK (object));
430   
431   osssink = GST_OSSSINK (object);
432
433   switch (prop_id) {
434     case ARG_DEVICE:
435       osssink->device = g_strdup (g_value_get_string (value));
436       break;
437     case ARG_MUTE:
438       osssink->mute = g_value_get_boolean (value);
439       break;
440     case ARG_FORMAT:
441       osssink->format = g_value_get_int (value);
442       gst_osssink_sync_parms (osssink);
443       break;
444     case ARG_CHANNELS:
445       osssink->channels = g_value_get_enum (value);
446       gst_osssink_sync_parms (osssink);
447       break;
448     case ARG_FREQUENCY:
449       osssink->frequency = g_value_get_int (value);
450       gst_osssink_sync_parms (osssink);
451       break;
452     case ARG_FRAGMENT:
453       osssink->fragment = g_value_get_int (value);
454       gst_osssink_sync_parms (osssink);
455       break;
456     case ARG_BUFFER_SIZE:
457       osssink->bufsize = g_value_get_int (value);
458       osssink->sinkpool = gst_buffer_pool_get_default (osssink->bufsize, 6);
459       break;
460     default:
461       break;
462   }
463 }
464
465 static void 
466 gst_osssink_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) 
467 {
468   GstOssSink *osssink;
469
470   /* it's not null if we got it, but it might not be ours */
471   g_return_if_fail (GST_IS_OSSSINK (object));
472   
473   osssink = GST_OSSSINK (object);
474
475   switch (prop_id) {
476     case ARG_DEVICE:
477       g_value_set_string (value, osssink->device);
478       break;
479     case ARG_MUTE:
480       g_value_set_boolean (value, osssink->mute);
481       break;
482     case ARG_FORMAT:
483       g_value_set_int (value, osssink->format);
484       break;
485     case ARG_CHANNELS:
486       g_value_set_enum (value, osssink->channels);
487       break;
488     case ARG_FREQUENCY:
489       g_value_set_int (value, osssink->frequency);
490       break;
491     case ARG_FRAGMENT:
492       g_value_set_int (value, osssink->fragment);
493       break;
494     case ARG_BUFFER_SIZE:
495       g_value_set_int (value, osssink->bufsize);
496       break;
497     default:
498       break;
499   }
500 }
501
502 static gboolean
503 gst_osssink_open_audio (GstOssSink *sink)
504 {
505   gint caps;
506   g_return_val_if_fail (sink->fd == -1, FALSE);
507
508   GST_INFO (GST_CAT_PLUGIN_INFO, "osssink: attempting to open sound device");
509
510   /* first try to open the sound card */
511   sink->fd = open(sink->device, O_WRONLY | O_NONBLOCK);
512   if (errno == EBUSY) {
513     g_warning ("osssink: unable to open the sound device (in use ?)\n");
514     return FALSE;
515   }
516
517   /* re-open the sound device in blocking mode */
518   close(sink->fd);
519   sink->fd = open(sink->device, O_WRONLY);
520
521   /* if we have it, set the default parameters and go have fun */
522   if (sink->fd >= 0) {
523     /* set card state */
524     ioctl(sink->fd, SNDCTL_DSP_GETCAPS, &caps);
525
526     GST_INFO(GST_CAT_PLUGIN_INFO, "osssink: Capabilities %08x", caps);
527
528     if (caps & DSP_CAP_DUPLEX)          GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   Full duplex");
529     if (caps & DSP_CAP_REALTIME)        GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   Realtime");
530     if (caps & DSP_CAP_BATCH)           GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   Batch");
531     if (caps & DSP_CAP_COPROC)          GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   Has coprocessor");
532     if (caps & DSP_CAP_TRIGGER)         GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   Trigger");
533     if (caps & DSP_CAP_MMAP)            GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   Direct access");
534
535 #ifdef DSP_CAP_MULTI
536     if (caps & DSP_CAP_MULTI)           GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   Multiple open");
537 #endif /* DSP_CAP_MULTI */
538
539 #ifdef DSP_CAP_BIND
540     if (caps & DSP_CAP_BIND)            GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   Channel binding");
541 #endif /* DSP_CAP_BIND */
542
543     ioctl(sink->fd, SNDCTL_DSP_GETFMTS, &caps);
544
545     GST_INFO (GST_CAT_PLUGIN_INFO, "osssink: Formats %08x", caps);
546     if (caps & AFMT_MU_LAW)             GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   MU_LAW");
547     if (caps & AFMT_A_LAW)              GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   A_LAW");
548     if (caps & AFMT_IMA_ADPCM)          GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   IMA_ADPCM");
549     if (caps & AFMT_U8)                 GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   U8");
550     if (caps & AFMT_S16_LE)             GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   S16_LE");
551     if (caps & AFMT_S16_BE)             GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   S16_BE");
552     if (caps & AFMT_S8)                 GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   S8");
553     if (caps & AFMT_U16_LE)             GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   U16_LE");
554     if (caps & AFMT_U16_BE)             GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   U16_BE");
555     if (caps & AFMT_MPEG)               GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   MPEG");
556 #ifdef AFMT_AC3
557     if (caps & AFMT_AC3)                GST_INFO (GST_CAT_PLUGIN_INFO, "osssink:   AC3");
558 #endif
559
560     GST_INFO (GST_CAT_PLUGIN_INFO, "osssink: opened audio (%s) with fd=%d", sink->device, sink->fd);
561     GST_FLAG_SET (sink, GST_OSSSINK_OPEN);
562
563     gst_osssink_sync_parms (sink);
564     return TRUE;
565   }
566
567   return FALSE;
568 }
569
570 static void
571 gst_osssink_close_audio (GstOssSink *sink)
572 {
573   if (sink->fd < 0) return;
574
575   close(sink->fd);
576   sink->fd = -1;
577
578   GST_FLAG_UNSET (sink, GST_OSSSINK_OPEN);
579
580   GST_INFO (GST_CAT_PLUGIN_INFO, "osssink: closed sound device");
581 }
582
583 static GstElementStateReturn 
584 gst_osssink_change_state (GstElement *element) 
585 {
586   GstOssSink *osssink;
587
588   g_return_val_if_fail (GST_IS_OSSSINK (element), FALSE);
589
590   osssink = GST_OSSSINK (element);
591
592   /* if going down into NULL state, close the file if it's open */ 
593   if (GST_STATE_PENDING (element) == GST_STATE_NULL) {
594     if (GST_FLAG_IS_SET (element, GST_OSSSINK_OPEN))
595       gst_osssink_close_audio (osssink);
596
597   /* otherwise (READY) we need to open the sound card */
598   } else if (GST_STATE_PENDING (element) == GST_STATE_READY) {
599     if (!GST_FLAG_IS_SET (element, GST_OSSSINK_OPEN)) {
600       if (!gst_osssink_open_audio (osssink)) {
601         return GST_STATE_FAILURE;
602       }
603     }
604   }
605       
606   if (GST_ELEMENT_CLASS (parent_class)->change_state)
607     return GST_ELEMENT_CLASS (parent_class)->change_state (element);
608
609   return GST_STATE_SUCCESS;
610 }
611
612 gboolean 
613 gst_osssink_factory_init (GstPlugin *plugin) 
614
615   GstElementFactory *factory;
616
617   factory = gst_elementfactory_new ("osssink", GST_TYPE_OSSSINK, &gst_osssink_details);
618   g_return_val_if_fail (factory != NULL, FALSE);
619
620   gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (osssink_sink_factory));
621
622   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
623
624   return TRUE;
625 }
626