- use common code to write samples to the device
[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 #include <sys/ioctl.h>
24 #include <sys/soundcard.h>
25 #include <errno.h>
26
27 #include <gstosssink.h>
28
29 /* elementfactory information */
30 static GstElementDetails gst_osssink_details = {  
31   "Audio Sink (OSS)",
32   "Sink/Audio",
33   "LGPL",
34   "Output to a sound card via OSS",
35   VERSION,
36   "Erik Walthinsen <omega@cse.ogi.edu>, "
37   "Wim Taymans <wim.taymans@chello.be>",
38   "(C) 1999",
39 };
40
41 static void                     gst_osssink_class_init          (GstOssSinkClass *klass);
42 static void                     gst_osssink_init                (GstOssSink *osssink);
43 static void                     gst_osssink_dispose             (GObject *object);
44 static void                     gst_osssink_finalize            (GObject *object);
45
46 static GstElementStateReturn    gst_osssink_change_state        (GstElement *element);
47 static void                     gst_osssink_set_clock           (GstElement *element, GstClock *clock);
48 static GstClock*                gst_osssink_get_clock           (GstElement *element);
49 static GstClockTime             gst_osssink_get_time            (GstClock *clock, gpointer data);
50
51 static const GstFormat*         gst_osssink_get_formats         (GstPad *pad);
52 static gboolean                 gst_osssink_convert             (GstPad *pad, GstFormat src_format, gint64 src_value,
53                                                                  GstFormat *dest_format, gint64 *dest_value);
54 static const GstQueryType*      gst_osssink_get_query_types     (GstPad *pad);
55 static gboolean                 gst_osssink_query               (GstElement *element, GstQueryType type, 
56                                                                  GstFormat *format, gint64 *value);
57 static gboolean                 gst_osssink_sink_query          (GstPad *pad, GstQueryType type,
58                                                                  GstFormat *format, gint64 *value);
59
60 static GstPadLinkReturn gst_osssink_sinkconnect         (GstPad *pad, GstCaps *caps);
61
62 static void                     gst_osssink_set_property        (GObject *object, guint prop_id, const GValue *value, 
63                                                                  GParamSpec *pspec);
64 static void                     gst_osssink_get_property        (GObject *object, guint prop_id, GValue *value, 
65                                                                  GParamSpec *pspec);
66
67 static void                     gst_osssink_chain               (GstPad *pad,GstBuffer *buf);
68
69 /* OssSink signals and args */
70 enum {
71   SIGNAL_HANDOFF,
72   LAST_SIGNAL
73 };
74
75 enum {
76   ARG_0,
77   ARG_DEVICE,
78   ARG_MUTE,
79   ARG_FRAGMENT,
80   ARG_BUFFER_SIZE,
81   ARG_SYNC,
82   ARG_CHUNK_SIZE,
83   /* FILL ME */
84 };
85
86 GST_PAD_TEMPLATE_FACTORY (osssink_sink_factory,
87   "sink",
88   GST_PAD_SINK,
89   GST_PAD_ALWAYS,
90   GST_CAPS_NEW (
91     "osssink_sink",
92     "audio/raw",
93       "format",     GST_PROPS_STRING ("int"),   /* hack */
94       "law",        GST_PROPS_INT (0),
95       "endianness", GST_PROPS_INT (G_BYTE_ORDER),
96       "signed",     GST_PROPS_LIST (
97                       GST_PROPS_BOOLEAN (FALSE),
98                       GST_PROPS_BOOLEAN (TRUE)
99                     ),
100       "width",      GST_PROPS_LIST (
101                       GST_PROPS_INT (8),
102                       GST_PROPS_INT (16)
103                     ),
104       "depth",      GST_PROPS_LIST (
105                       GST_PROPS_INT (8),
106                       GST_PROPS_INT (16)
107                     ),
108       "rate",       GST_PROPS_INT_RANGE (1000, 48000),
109       "channels",   GST_PROPS_INT_RANGE (1, 2)
110   )
111 );
112
113 static GstElementClass *parent_class = NULL;
114 static guint gst_osssink_signals[LAST_SIGNAL] = { 0 };
115
116 GType
117 gst_osssink_get_type (void) 
118 {
119   static GType osssink_type = 0;
120
121   if (!osssink_type) {
122     static const GTypeInfo osssink_info = {
123       sizeof(GstOssSinkClass),
124       NULL,
125       NULL,
126       (GClassInitFunc)gst_osssink_class_init,
127       NULL,
128       NULL,
129       sizeof(GstOssSink),
130       0,
131       (GInstanceInitFunc)gst_osssink_init,
132     };
133     osssink_type = g_type_register_static (GST_TYPE_ELEMENT, "GstOssSink", &osssink_info, 0);
134   }
135
136   return osssink_type;
137 }
138
139 static GstBufferPool*
140 gst_osssink_get_bufferpool (GstPad *pad)
141 {
142   GstOssSink *oss;
143   
144   oss = GST_OSSSINK (gst_pad_get_parent(pad));
145
146   /* 6 buffers per chunk by default */
147   if (!oss->sinkpool)
148     oss->sinkpool = gst_buffer_pool_get_default (oss->bufsize, 6);
149
150   return oss->sinkpool;
151 }
152
153 static void
154 gst_osssink_dispose (GObject *object)
155 {
156   GstOssSink *osssink = (GstOssSink *) object;
157
158   gst_object_unparent (GST_OBJECT (osssink->provided_clock));
159
160   G_OBJECT_CLASS (parent_class)->dispose (object);
161 }
162
163 static void
164 gst_osssink_finalize (GObject *object)
165 {
166   GstOssSink *osssink = (GstOssSink *) object;
167
168   g_free (osssink->common.device);
169
170   G_OBJECT_CLASS (parent_class)->finalize (object);
171 }
172
173 static void
174 gst_osssink_class_init (GstOssSinkClass *klass) 
175 {
176   GObjectClass *gobject_class;
177   GstElementClass *gstelement_class;
178
179   gobject_class = (GObjectClass*)klass;
180   gstelement_class = (GstElementClass*)klass;
181
182   parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
183
184   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DEVICE,
185     g_param_spec_string ("device", "Device", "The device to use for output",
186                          "/dev/dsp", G_PARAM_READWRITE)); /* CHECKME! */
187   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MUTE,
188     g_param_spec_boolean ("mute", "Mute", "Mute the audio",
189                           TRUE, G_PARAM_READWRITE)); 
190   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SYNC,
191     g_param_spec_boolean ("sync", "Sync", "If syncing on timestamps should be enabled",
192                           TRUE, G_PARAM_READWRITE)); 
193   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FRAGMENT,
194     g_param_spec_int ("fragment", "Fragment", 
195                       "The fragment as 0xMMMMSSSS (MMMM = total fragments, 2^SSSS = fragment size)",
196                       0, G_MAXINT, 6, G_PARAM_READWRITE));
197   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BUFFER_SIZE,
198     g_param_spec_uint ("buffer_size", "Buffer size", "The buffer size",
199                        0, G_MAXINT, 4096, G_PARAM_READWRITE));
200   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_CHUNK_SIZE,
201     g_param_spec_uint ("chunk_size", "Chunk size", "Write data in chunk sized buffers",
202                        0, G_MAXUINT, 4096, G_PARAM_READWRITE));
203
204   gst_osssink_signals[SIGNAL_HANDOFF] =
205     g_signal_new ("handoff", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
206                   G_STRUCT_OFFSET (GstOssSinkClass, handoff), NULL, NULL,
207                   g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
208   
209   gobject_class->set_property = gst_osssink_set_property;
210   gobject_class->get_property = gst_osssink_get_property;
211   gobject_class->dispose      = gst_osssink_dispose;
212   gobject_class->finalize     = gst_osssink_finalize;
213   
214   gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_osssink_change_state);
215   gstelement_class->query        = GST_DEBUG_FUNCPTR (gst_osssink_query);
216   gstelement_class->set_clock    = gst_osssink_set_clock;
217   gstelement_class->get_clock    = gst_osssink_get_clock;
218   
219 }
220
221 static void 
222 gst_osssink_init (GstOssSink *osssink) 
223 {
224   osssink->sinkpad = gst_pad_new_from_template (
225                   GST_PAD_TEMPLATE_GET (osssink_sink_factory), "sink");
226   gst_element_add_pad (GST_ELEMENT (osssink), osssink->sinkpad);
227   gst_pad_set_link_function (osssink->sinkpad, gst_osssink_sinkconnect);
228   gst_pad_set_bufferpool_function (osssink->sinkpad, gst_osssink_get_bufferpool);
229   gst_pad_set_convert_function (osssink->sinkpad, gst_osssink_convert);
230   gst_pad_set_query_function (osssink->sinkpad, gst_osssink_sink_query);
231   gst_pad_set_query_type_function (osssink->sinkpad, gst_osssink_get_query_types);
232   gst_pad_set_formats_function (osssink->sinkpad, gst_osssink_get_formats);
233
234   gst_pad_set_chain_function (osssink->sinkpad, gst_osssink_chain);
235
236   gst_osscommon_init (&osssink->common);
237
238   osssink->bufsize = 4096;
239   osssink->chunk_size = 4096;
240   osssink->resync = FALSE;
241   osssink->sync = TRUE;
242   osssink->sinkpool = NULL;
243   osssink->provided_clock = GST_CLOCK (gst_oss_clock_new ("ossclock", gst_osssink_get_time, osssink));
244   gst_object_set_parent (GST_OBJECT (osssink->provided_clock), GST_OBJECT (osssink));
245   osssink->handled = 0;
246
247   GST_FLAG_SET (osssink, GST_ELEMENT_THREAD_SUGGESTED);
248   GST_FLAG_SET (osssink, GST_ELEMENT_EVENT_AWARE);
249 }
250
251
252 static GstPadLinkReturn 
253 gst_osssink_sinkconnect (GstPad *pad, GstCaps *caps) 
254 {
255   GstOssSink *osssink = GST_OSSSINK (gst_pad_get_parent (pad));
256
257   if (!GST_CAPS_IS_FIXED (caps))
258     return GST_PAD_LINK_DELAYED;
259
260   if (!gst_osscommon_parse_caps (&osssink->common, caps))
261     return GST_PAD_LINK_REFUSED;
262
263   if (!gst_osscommon_sync_parms (&osssink->common)) {
264     return GST_PAD_LINK_REFUSED;
265   }
266
267   return GST_PAD_LINK_OK;
268 }
269
270 static inline gint64 
271 gst_osssink_get_delay (GstOssSink *osssink) 
272 {
273   gint delay = 0;
274
275   if (osssink->common.fd == -1)
276     return 0;
277
278   if (ioctl (osssink->common.fd, SNDCTL_DSP_GETODELAY, &delay) < 0) {
279     audio_buf_info info;
280     if (ioctl (osssink->common.fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
281       delay = 0;
282     }
283     else {
284       delay = (info.fragstotal * info.fragsize) - info.bytes;     
285     }
286   }
287   return delay;
288 }
289
290 static GstClockTime 
291 gst_osssink_get_time (GstClock *clock, gpointer data) 
292 {
293   GstOssSink *osssink = GST_OSSSINK (data);
294   gint delay;
295   GstClockTime res;
296
297   if (!osssink->common.bps)
298     return 0;
299
300   delay = gst_osssink_get_delay (osssink);
301
302   /* sometimes delay is bigger than the number of bytes sent to the device, 
303    * which screws up this calculation, we assume that everything is still 
304    * in the device then */
305   if (((guint64)delay) > osssink->handled) {
306     delay = osssink->handled;
307   }
308   res =  (osssink->handled - delay) * GST_SECOND / osssink->common.bps;
309
310   return res;
311 }
312
313 static GstClock*
314 gst_osssink_get_clock (GstElement *element)
315 {
316   GstOssSink *osssink;
317             
318   osssink = GST_OSSSINK (element);
319
320   return GST_CLOCK (osssink->provided_clock);
321 }
322
323 static void
324 gst_osssink_set_clock (GstElement *element, GstClock *clock)
325 {
326   GstOssSink *osssink;
327   
328   osssink = GST_OSSSINK (element);
329
330   osssink->clock = clock;  
331 }
332
333 static void 
334 gst_osssink_chain (GstPad *pad, GstBuffer *buf) 
335 {
336   GstOssSink *osssink;
337   GstClockTime buftime;
338
339   /* this has to be an audio buffer */
340   osssink = GST_OSSSINK (gst_pad_get_parent (pad));
341
342   if (GST_IS_EVENT (buf)) {
343     GstEvent *event = GST_EVENT (buf);
344
345     switch (GST_EVENT_TYPE (event)) {
346       case GST_EVENT_EOS:
347         ioctl (osssink->common.fd, SNDCTL_DSP_SYNC);
348         gst_oss_clock_set_active (osssink->provided_clock, FALSE);
349         gst_pad_event_default (pad, event);
350         return;
351       case GST_EVENT_NEW_MEDIA:
352         g_print ("new media\n");
353         break;
354       case GST_EVENT_DISCONTINUOUS:
355       {
356         gint64 value;
357
358         ioctl (osssink->common.fd, SNDCTL_DSP_RESET);
359         if (gst_event_discont_get_value (event, GST_FORMAT_TIME, &value)) {
360           if (!gst_clock_handle_discont (osssink->clock, value))
361             gst_oss_clock_set_active (osssink->provided_clock, FALSE);
362           osssink->handled = 0;
363         }
364         osssink->resync = TRUE;
365         break;
366       }
367       default:
368         gst_pad_event_default (pad, event);
369         return;
370     }
371     gst_event_unref (event);
372     return;
373   }
374
375   if (!osssink->common.bps) {
376     gst_buffer_unref (buf);
377     gst_element_error (GST_ELEMENT (osssink), "capsnego was never performed, unknown data type");
378     return;
379   }
380
381   buftime = GST_BUFFER_TIMESTAMP (buf);
382
383   if (osssink->common.fd >= 0) {
384     if (!osssink->mute) {
385       guchar *data = GST_BUFFER_DATA (buf);
386       gint size = GST_BUFFER_SIZE (buf);
387       gint to_write = 0;
388
389       if (osssink->clock) {
390         gint delay = 0;
391         gint64 queued;
392         GstClockTimeDiff jitter;
393     
394         delay = gst_osssink_get_delay (osssink);
395         queued = delay * GST_SECOND / osssink->common.bps;
396
397         if  (osssink->resync && osssink->sync) {
398           GstClockID id = gst_clock_new_single_shot_id (osssink->clock, buftime - queued);
399
400           gst_element_clock_wait (GST_ELEMENT (osssink), id, &jitter);
401           gst_clock_id_free (id);
402
403           if (jitter >= 0) {
404             gst_clock_handle_discont (osssink->clock, buftime - queued + jitter);
405             to_write = size;
406             gst_oss_clock_set_active (osssink->provided_clock, TRUE);
407             osssink->resync = FALSE;
408           }
409         }
410         else {
411           to_write = size;
412         }
413       }
414       /* no clock, try to be as fast as possible */
415       else {
416         audio_buf_info ospace;
417
418         ioctl (osssink->common.fd, SNDCTL_DSP_GETOSPACE, &ospace);
419
420         if (ospace.bytes >= size) {
421           to_write = size;
422         }
423       }
424
425       while (to_write > 0) {
426         gint done = write (osssink->common.fd, data, 
427                            MIN (to_write, osssink->chunk_size));
428
429         if (done == -1) {
430           if (errno != EINTR)
431             break;
432         }
433         else {
434           to_write -= done;
435           data += done;
436           osssink->handled += done;
437         }
438       }
439     }
440   }
441   gst_buffer_unref (buf);
442 }
443
444 static const GstFormat*
445 gst_osssink_get_formats (GstPad *pad)
446 {
447   static const GstFormat formats[] = {
448     GST_FORMAT_TIME,
449     GST_FORMAT_DEFAULT,
450     GST_FORMAT_BYTES,
451     0
452   };
453   return formats;
454 }
455
456 static gboolean
457 gst_osssink_convert (GstPad *pad, GstFormat src_format, gint64 src_value,
458                      GstFormat *dest_format, gint64 *dest_value)
459 {
460   GstOssSink *osssink;
461
462   osssink = GST_OSSSINK (gst_pad_get_parent (pad));
463   
464   return gst_osscommon_convert (&osssink->common, src_format, src_value,
465                                 dest_format, dest_value);
466 }
467
468 static const GstQueryType*
469 gst_osssink_get_query_types (GstPad *pad)
470 {
471   static const GstQueryType query_types[] = {
472     GST_QUERY_LATENCY,
473     GST_QUERY_POSITION,
474     0,
475   };
476   return query_types;
477 }
478
479 static gboolean
480 gst_osssink_sink_query (GstPad *pad, GstQueryType type, GstFormat *format, gint64 *value) 
481 {
482   gboolean res = TRUE;
483   GstOssSink *osssink;
484
485   osssink = GST_OSSSINK (gst_pad_get_parent (pad));
486   
487   switch (type) {
488     case GST_QUERY_LATENCY:
489       if (!gst_osssink_convert (pad, 
490                                 GST_FORMAT_BYTES, gst_osssink_get_delay (osssink),
491                                 format, value)) 
492       {
493         res = FALSE;
494       }
495       break;
496     case GST_QUERY_POSITION:
497       if (!gst_osssink_convert (pad, 
498                                 GST_FORMAT_TIME, gst_clock_get_time (osssink->provided_clock),
499                                 format, value)) 
500       {
501         res = FALSE;
502       }
503       break;
504     default:
505       res = gst_pad_query (gst_pad_get_peer (osssink->sinkpad), type, format, value);
506       break;
507   }
508
509   return res;
510 }
511
512 static gboolean
513 gst_osssink_query (GstElement *element, GstQueryType type, GstFormat *format, gint64 *value) 
514 {
515   GstOssSink *osssink = GST_OSSSINK (element);
516
517   return gst_osssink_sink_query (osssink->sinkpad, type, format, value);
518 }
519
520 static void 
521 gst_osssink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) 
522 {
523   GstOssSink *osssink;
524
525   osssink = GST_OSSSINK (object);
526
527   switch (prop_id) {
528     case ARG_DEVICE:
529       /* disallow changing the device while it is opened
530          get_property("device") should return the right one */
531       if (!GST_FLAG_IS_SET (osssink, GST_OSSSINK_OPEN))
532       {
533         g_free (osssink->common.device);
534         osssink->common.device = g_strdup (g_value_get_string (value));
535         g_object_notify (object, "device");
536       }
537       break;
538     case ARG_MUTE:
539       osssink->mute = g_value_get_boolean (value);
540       g_object_notify (G_OBJECT (osssink), "mute");
541       break;
542     case ARG_FRAGMENT:
543       osssink->common.fragment = g_value_get_int (value);
544       gst_osscommon_sync_parms (&osssink->common);
545       break;
546     case ARG_BUFFER_SIZE:
547       if (osssink->bufsize == g_value_get_int (value)) break;
548       osssink->bufsize = g_value_get_uint (value);
549       osssink->sinkpool = gst_buffer_pool_get_default (osssink->bufsize, 6);
550       g_object_notify (object, "buffer_size");
551       break;
552     case ARG_SYNC:
553       osssink->sync = g_value_get_boolean (value);
554       g_object_notify (G_OBJECT (osssink), "sync");
555       break;
556     case ARG_CHUNK_SIZE:
557       osssink->chunk_size = g_value_get_uint (value);
558       break;
559     default:
560       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
561       break;
562   }
563 }
564
565 static void 
566 gst_osssink_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) 
567 {
568   GstOssSink *osssink;
569
570   osssink = GST_OSSSINK (object);
571
572   switch (prop_id) {
573     case ARG_DEVICE:
574       g_value_set_string (value, osssink->common.device);
575       break;
576     case ARG_MUTE:
577       g_value_set_boolean (value, osssink->mute);
578       break;
579     case ARG_FRAGMENT:
580       g_value_set_int (value, osssink->common.fragment);
581       break;
582     case ARG_BUFFER_SIZE:
583       g_value_set_uint (value, osssink->bufsize);
584       break;
585     case ARG_SYNC:
586       g_value_set_boolean (value, osssink->sync);
587       break;
588     case ARG_CHUNK_SIZE:
589       g_value_set_uint (value, osssink->chunk_size);
590       break;
591     default:
592       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
593       break;
594   }
595 }
596
597 static GstElementStateReturn 
598 gst_osssink_change_state (GstElement *element) 
599 {
600   GstOssSink *osssink;
601
602   osssink = GST_OSSSINK (element);
603
604   switch (GST_STATE_TRANSITION (element)) {
605     case GST_STATE_NULL_TO_READY:
606       if (!GST_FLAG_IS_SET (element, GST_OSSSINK_OPEN)) {
607         gchar *error;
608
609         if (!gst_osscommon_open_audio (&osssink->common, GST_OSSCOMMON_WRITE, &error)) {
610           gst_element_error (GST_ELEMENT (osssink), error);
611           g_free (error);
612           return GST_STATE_FAILURE;
613         }
614         GST_FLAG_SET (element, GST_OSSSINK_OPEN);
615       }
616       break;
617     case GST_STATE_READY_TO_PAUSED:
618       break;
619     case GST_STATE_PAUSED_TO_PLAYING:
620       osssink->resync = TRUE;
621       break;
622     case GST_STATE_PLAYING_TO_PAUSED:
623     {
624       if (GST_FLAG_IS_SET (element, GST_OSSSINK_OPEN)) 
625         ioctl (osssink->common.fd, SNDCTL_DSP_RESET, 0);
626       gst_oss_clock_set_active (osssink->provided_clock, FALSE);
627       osssink->resync = TRUE;
628       break;
629     }
630     case GST_STATE_PAUSED_TO_READY:
631       if (GST_FLAG_IS_SET (element, GST_OSSSINK_OPEN))
632         ioctl (osssink->common.fd, SNDCTL_DSP_RESET, 0);
633       gst_osscommon_reset (&osssink->common);
634       break;
635     case GST_STATE_READY_TO_NULL:
636       if (GST_FLAG_IS_SET (element, GST_OSSSINK_OPEN)) {
637         gst_osscommon_close_audio (&osssink->common);
638         GST_FLAG_UNSET (osssink, GST_OSSSINK_OPEN);
639
640         GST_INFO (GST_CAT_PLUGIN_INFO, "osssink: closed sound device");
641       }
642       break;
643   }
644       
645   if (GST_ELEMENT_CLASS (parent_class)->change_state)
646     return GST_ELEMENT_CLASS (parent_class)->change_state (element);
647
648   return GST_STATE_SUCCESS;
649 }
650
651 gboolean 
652 gst_osssink_factory_init (GstPlugin *plugin) 
653
654   GstElementFactory *factory;
655
656   factory = gst_element_factory_new ("osssink", GST_TYPE_OSSSINK, &gst_osssink_details);
657   g_return_val_if_fail (factory != NULL, FALSE);
658
659   gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (osssink_sink_factory));
660
661   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
662
663   return TRUE;
664 }