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