sys: Don't use GST_DEBUG_FUNCPTR for GObject vfuncs
[platform/upstream/gst-plugins-good.git] / sys / oss4 / oss4-sink.c
1 /* GStreamer OSS4 audio sink
2  * Copyright (C) 2007-2008 Tim-Philipp Müller <tim centricular net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 /**
20  * SECTION:element-oss4sink
21  *
22  * This element lets you output sound using the Open Sound System (OSS)
23  * version 4.
24  * 
25  * Note that you should almost always use generic audio conversion elements
26  * like audioconvert and audioresample in front of an audiosink to make sure
27  * your pipeline works under all circumstances (those conversion elements will
28  * act in passthrough-mode if no conversion is necessary).
29  * 
30  * <refsect2>
31  * <title>Example pipelines</title>
32  * |[
33  * gst-launch -v audiotestsrc ! audioconvert ! volume volume=0.1 ! oss4sink
34  * ]| will output a sine wave (continuous beep sound) to your sound card (with
35  * a very low volume as precaution).
36  * |[
37  * gst-launch -v filesrc location=music.ogg ! decodebin ! audioconvert ! audioresample ! oss4sink
38  * ]| will play an Ogg/Vorbis audio file and output it using the Open Sound System
39  * version 4.
40  * </refsect2>
41  *
42  * Since: 0.10.7
43  */
44
45 #ifdef HAVE_CONFIG_H
46 #include "config.h"
47 #endif
48
49 #include <sys/types.h>
50 #include <sys/stat.h>
51 #include <sys/ioctl.h>
52 #include <fcntl.h>
53 #include <errno.h>
54 #include <unistd.h>
55 #include <string.h>
56
57 #include <gst/gst-i18n-plugin.h>
58 #include <gst/interfaces/streamvolume.h>
59
60 #define NO_LEGACY_MIXER
61 #include "oss4-audio.h"
62 #include "oss4-sink.h"
63 #include "oss4-property-probe.h"
64 #include "oss4-soundcard.h"
65
66 GST_DEBUG_CATEGORY_EXTERN (oss4sink_debug);
67 #define GST_CAT_DEFAULT oss4sink_debug
68
69 static void gst_oss4_sink_init_interfaces (GType type);
70 static void gst_oss4_sink_dispose (GObject * object);
71 static void gst_oss4_sink_finalize (GObject * object);
72
73 static void gst_oss4_sink_get_property (GObject * object, guint prop_id,
74     GValue * value, GParamSpec * pspec);
75 static void gst_oss4_sink_set_property (GObject * object, guint prop_id,
76     const GValue * value, GParamSpec * pspec);
77
78 static GstCaps *gst_oss4_sink_getcaps (GstBaseSink * bsink);
79 static gboolean gst_oss4_sink_open (GstAudioSink * asink,
80     gboolean silent_errors);
81 static gboolean gst_oss4_sink_open_func (GstAudioSink * asink);
82 static gboolean gst_oss4_sink_close (GstAudioSink * asink);
83 static gboolean gst_oss4_sink_prepare (GstAudioSink * asink,
84     GstRingBufferSpec * spec);
85 static gboolean gst_oss4_sink_unprepare (GstAudioSink * asink);
86 static guint gst_oss4_sink_write (GstAudioSink * asink, gpointer data,
87     guint length);
88 static guint gst_oss4_sink_delay (GstAudioSink * asink);
89 static void gst_oss4_sink_reset (GstAudioSink * asink);
90
91 #define DEFAULT_DEVICE      NULL
92 #define DEFAULT_DEVICE_NAME NULL
93 #define DEFAULT_MUTE        FALSE
94 #define DEFAULT_VOLUME      1.0
95 #define MAX_VOLUME          10.0
96
97 enum
98 {
99   PROP_0,
100   PROP_DEVICE,
101   PROP_DEVICE_NAME,
102   PROP_VOLUME,
103   PROP_MUTE,
104   PROP_LAST
105 };
106
107 GST_BOILERPLATE_FULL (GstOss4Sink, gst_oss4_sink, GstAudioSink,
108     GST_TYPE_AUDIO_SINK, gst_oss4_sink_init_interfaces);
109
110 static void
111 gst_oss4_sink_dispose (GObject * object)
112 {
113   GstOss4Sink *osssink = GST_OSS4_SINK (object);
114
115   if (osssink->probed_caps) {
116     gst_caps_unref (osssink->probed_caps);
117     osssink->probed_caps = NULL;
118   }
119
120   G_OBJECT_CLASS (parent_class)->dispose (object);
121 }
122
123 static void
124 gst_oss4_sink_base_init (gpointer g_class)
125 {
126   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
127   GstPadTemplate *templ;
128
129   gst_element_class_set_details_simple (element_class,
130       "OSS v4 Audio Sink", "Sink/Audio",
131       "Output to a sound card via OSS version 4",
132       "Tim-Philipp Müller <tim centricular net>");
133
134   templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
135       gst_oss4_audio_get_template_caps ());
136   gst_element_class_add_pad_template (element_class, templ);
137 }
138
139 static void
140 gst_oss4_sink_class_init (GstOss4SinkClass * klass)
141 {
142   GstAudioSinkClass *audiosink_class = (GstAudioSinkClass *) klass;
143   GstBaseSinkClass *basesink_class = (GstBaseSinkClass *) klass;
144   GObjectClass *gobject_class = (GObjectClass *) klass;
145
146   gobject_class->dispose = gst_oss4_sink_dispose;
147   gobject_class->finalize = gst_oss4_sink_finalize;
148   gobject_class->get_property = gst_oss4_sink_get_property;
149   gobject_class->set_property = gst_oss4_sink_set_property;
150
151   g_object_class_install_property (gobject_class, PROP_DEVICE,
152       g_param_spec_string ("device", "Device",
153           "OSS4 device (e.g. /dev/oss/hdaudio0/pcm0 or /dev/dspN) "
154           "(NULL = use first available playback device)",
155           DEFAULT_DEVICE, G_PARAM_READWRITE));
156
157   g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
158       g_param_spec_string ("device-name", "Device name",
159           "Human-readable name of the sound device", DEFAULT_DEVICE_NAME,
160           G_PARAM_READABLE));
161
162   g_object_class_install_property (gobject_class,
163       PROP_VOLUME,
164       g_param_spec_double ("volume", "Volume",
165           "Linear volume of this stream, 1.0=100%", 0.0, MAX_VOLUME,
166           DEFAULT_VOLUME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
167
168   g_object_class_install_property (gobject_class,
169       PROP_MUTE,
170       g_param_spec_boolean ("mute", "Mute",
171           "Mute state of this stream", DEFAULT_MUTE,
172           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
173
174   basesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_oss4_sink_getcaps);
175
176   audiosink_class->open = GST_DEBUG_FUNCPTR (gst_oss4_sink_open_func);
177   audiosink_class->close = GST_DEBUG_FUNCPTR (gst_oss4_sink_close);
178   audiosink_class->prepare = GST_DEBUG_FUNCPTR (gst_oss4_sink_prepare);
179   audiosink_class->unprepare = GST_DEBUG_FUNCPTR (gst_oss4_sink_unprepare);
180   audiosink_class->write = GST_DEBUG_FUNCPTR (gst_oss4_sink_write);
181   audiosink_class->delay = GST_DEBUG_FUNCPTR (gst_oss4_sink_delay);
182   audiosink_class->reset = GST_DEBUG_FUNCPTR (gst_oss4_sink_reset);
183 }
184
185 static void
186 gst_oss4_sink_init (GstOss4Sink * osssink, GstOss4SinkClass * klass)
187 {
188   const gchar *device;
189
190   device = g_getenv ("AUDIODEV");
191   if (device == NULL)
192     device = DEFAULT_DEVICE;
193   osssink->device = g_strdup (device);
194   osssink->fd = -1;
195   osssink->bytes_per_sample = 0;
196   osssink->probed_caps = NULL;
197   osssink->device_name = NULL;
198   osssink->mute_volume = 100 | (100 << 8);
199 }
200
201 static void
202 gst_oss4_sink_finalize (GObject * object)
203 {
204   GstOss4Sink *osssink = GST_OSS4_SINK (object);
205
206   g_free (osssink->device);
207   osssink->device = NULL;
208
209   g_list_free (osssink->property_probe_list);
210   osssink->property_probe_list = NULL;
211
212   G_OBJECT_CLASS (parent_class)->finalize (object);
213 }
214
215 static void
216 gst_oss4_sink_set_volume (GstOss4Sink * oss, gdouble volume)
217 {
218   int ivol;
219
220   volume = volume * 100.0;
221   ivol = (int) volume | ((int) volume << 8);
222   GST_OBJECT_LOCK (oss);
223   if (ioctl (oss->fd, SNDCTL_DSP_SETPLAYVOL, &ivol) < 0) {
224     GST_LOG_OBJECT (oss, "SETPLAYVOL failed");
225   }
226   GST_OBJECT_UNLOCK (oss);
227 }
228
229 static gdouble
230 gst_oss4_sink_get_volume (GstOss4Sink * oss)
231 {
232   int ivol, lvol, rvol;
233   gdouble dvol = DEFAULT_VOLUME;
234
235   GST_OBJECT_LOCK (oss);
236   if (ioctl (oss->fd, SNDCTL_DSP_GETPLAYVOL, &ivol) < 0) {
237     GST_LOG_OBJECT (oss, "GETPLAYVOL failed");
238   } else {
239     /* Return the higher of the two volume channels, if different */
240     lvol = ivol & 0xff;
241     rvol = (ivol >> 8) & 0xff;
242     dvol = MAX (lvol, rvol) / 100.0;
243   }
244   GST_OBJECT_UNLOCK (oss);
245
246   return dvol;
247 }
248
249 static void
250 gst_oss4_sink_set_mute (GstOss4Sink * oss, gboolean mute)
251 {
252   int ivol;
253
254   if (mute) {
255     /*
256      * OSSv4 does not have a per-channel mute, so simulate by setting
257      * the value to 0.  Save the volume before doing a mute so we can
258      * reset the value when the user un-mutes.
259      */
260     ivol = 0;
261
262     GST_OBJECT_LOCK (oss);
263     if (ioctl (oss->fd, SNDCTL_DSP_GETPLAYVOL, &oss->mute_volume) < 0) {
264       GST_LOG_OBJECT (oss, "GETPLAYVOL failed");
265     }
266     if (ioctl (oss->fd, SNDCTL_DSP_SETPLAYVOL, &ivol) < 0) {
267       GST_LOG_OBJECT (oss, "SETPLAYVOL failed");
268     }
269     GST_OBJECT_UNLOCK (oss);
270   } else {
271     /*
272      * If the saved volume is 0, then reset it to 100.  Otherwise the mute
273      * can get stuck.  This can happen, for example, due to rounding
274      * errors in converting from the float to an integer.
275      */
276     if (oss->mute_volume == 0) {
277       oss->mute_volume = 100 | (100 << 8);
278     }
279     GST_OBJECT_LOCK (oss);
280     if (ioctl (oss->fd, SNDCTL_DSP_SETPLAYVOL, &oss->mute_volume) < 0) {
281       GST_LOG_OBJECT (oss, "SETPLAYVOL failed");
282     }
283     GST_OBJECT_UNLOCK (oss);
284   }
285 }
286
287 static gboolean
288 gst_oss4_sink_get_mute (GstOss4Sink * oss)
289 {
290   int ivol, lvol, rvol;
291
292   GST_OBJECT_LOCK (oss);
293   if (ioctl (oss->fd, SNDCTL_DSP_GETPLAYVOL, &ivol) < 0) {
294     GST_LOG_OBJECT (oss, "GETPLAYVOL failed");
295     lvol = rvol = 100;
296   } else {
297     lvol = ivol & 0xff;
298     rvol = (ivol >> 8) & 0xff;
299   }
300   GST_OBJECT_UNLOCK (oss);
301
302   return (lvol == 0 && rvol == 0);
303 }
304
305 static void
306 gst_oss4_sink_set_property (GObject * object, guint prop_id,
307     const GValue * value, GParamSpec * pspec)
308 {
309   GstOss4Sink *oss = GST_OSS4_SINK (object);
310
311   switch (prop_id) {
312     case PROP_DEVICE:
313       GST_OBJECT_LOCK (oss);
314       if (oss->fd == -1) {
315         g_free (oss->device);
316         oss->device = g_value_dup_string (value);
317         if (oss->probed_caps) {
318           gst_caps_unref (oss->probed_caps);
319           oss->probed_caps = NULL;
320         }
321         g_free (oss->device_name);
322         oss->device_name = NULL;
323       } else {
324         g_warning ("%s: can't change \"device\" property while audio sink "
325             "is open", GST_OBJECT_NAME (oss));
326       }
327       GST_OBJECT_UNLOCK (oss);
328       break;
329     case PROP_VOLUME:
330       gst_oss4_sink_set_volume (oss, g_value_get_double (value));
331       break;
332     case PROP_MUTE:
333       gst_oss4_sink_set_mute (oss, g_value_get_boolean (value));
334       break;
335     default:
336       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
337       break;
338   }
339 }
340
341 static void
342 gst_oss4_sink_get_property (GObject * object, guint prop_id,
343     GValue * value, GParamSpec * pspec)
344 {
345   GstOss4Sink *oss = GST_OSS4_SINK (object);
346
347   switch (prop_id) {
348     case PROP_DEVICE:
349       GST_OBJECT_LOCK (oss);
350       g_value_set_string (value, oss->device);
351       GST_OBJECT_UNLOCK (oss);
352       break;
353     case PROP_DEVICE_NAME:
354       GST_OBJECT_LOCK (oss);
355       if (oss->fd == -1 && oss->device != NULL) {
356         /* If device is set, try to retrieve the name even if we're not open */
357         if (gst_oss4_sink_open (GST_AUDIO_SINK (oss), TRUE)) {
358           g_value_set_string (value, oss->device_name);
359           gst_oss4_sink_close (GST_AUDIO_SINK (oss));
360         } else {
361           gchar *name = NULL;
362
363           gst_oss4_property_probe_find_device_name_nofd (GST_OBJECT (oss),
364               oss->device, &name);
365           g_value_set_string (value, name);
366           g_free (name);
367         }
368       } else {
369         g_value_set_string (value, oss->device_name);
370       }
371       GST_OBJECT_UNLOCK (oss);
372       break;
373     case PROP_VOLUME:
374       g_value_set_double (value, gst_oss4_sink_get_volume (oss));
375       break;
376     case PROP_MUTE:
377       g_value_set_boolean (value, gst_oss4_sink_get_mute (oss));
378       break;
379     default:
380       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
381       break;
382   }
383 }
384
385 static GstCaps *
386 gst_oss4_sink_getcaps (GstBaseSink * bsink)
387 {
388   GstOss4Sink *oss;
389   GstCaps *caps;
390
391   oss = GST_OSS4_SINK (bsink);
392
393   if (oss->fd == -1) {
394     caps = gst_caps_copy (gst_oss4_audio_get_template_caps ());
395   } else if (oss->probed_caps) {
396     caps = gst_caps_copy (oss->probed_caps);
397   } else {
398     caps = gst_oss4_audio_probe_caps (GST_OBJECT (oss), oss->fd);
399     if (caps != NULL && !gst_caps_is_empty (caps)) {
400       oss->probed_caps = gst_caps_copy (caps);
401     }
402   }
403
404   return caps;
405 }
406
407 /* note: we must not take the object lock here unless we fix up get_property */
408 static gboolean
409 gst_oss4_sink_open (GstAudioSink * asink, gboolean silent_errors)
410 {
411   GstOss4Sink *oss;
412   gchar *device;
413   int mode;
414
415   oss = GST_OSS4_SINK (asink);
416
417   if (oss->device)
418     device = g_strdup (oss->device);
419   else
420     device = gst_oss4_audio_find_device (GST_OBJECT_CAST (oss));
421
422   /* desperate times, desperate measures */
423   if (device == NULL)
424     device = g_strdup ("/dev/dsp0");
425
426   GST_INFO_OBJECT (oss, "Trying to open OSS4 device '%s'", device);
427
428   /* we open in non-blocking mode even if we don't really want to do writes
429    * non-blocking because we can't be sure that this is really a genuine
430    * OSS4 device with well-behaved drivers etc. We really don't want to
431    * hang forever under any circumstances. */
432   oss->fd = open (device, O_WRONLY | O_NONBLOCK, 0);
433   if (oss->fd == -1) {
434     switch (errno) {
435       case EBUSY:
436         goto busy;
437       case EACCES:
438         goto no_permission;
439       default:
440         goto open_failed;
441     }
442   }
443
444   GST_INFO_OBJECT (oss, "Opened device '%s'", device);
445
446   /* Make sure it's OSS4. If it's old OSS, let osssink handle it */
447   if (!gst_oss4_audio_check_version (GST_OBJECT_CAST (oss), oss->fd))
448     goto legacy_oss;
449
450   /* now remove the non-blocking flag. */
451   mode = fcntl (oss->fd, F_GETFL);
452   mode &= ~O_NONBLOCK;
453   if (fcntl (oss->fd, F_SETFL, mode) < 0) {
454     /* some drivers do no support unsetting the non-blocking flag, try to
455      * close/open the device then. This is racy but we error out properly. */
456     GST_WARNING_OBJECT (oss, "failed to unset O_NONBLOCK (buggy driver?), "
457         "will try to re-open device now");
458     gst_oss4_sink_close (asink);
459     if ((oss->fd = open (device, O_WRONLY, 0)) == -1)
460       goto non_block;
461   }
462
463   oss->open_device = device;
464
465   /* not using ENGINEINFO here because it sometimes returns a different and
466    * less useful name than AUDIOINFO for the same device */
467   if (!gst_oss4_property_probe_find_device_name (GST_OBJECT (oss), oss->fd,
468           oss->open_device, &oss->device_name)) {
469     oss->device_name = NULL;
470   }
471
472   /* list output routings, for informational purposes only so far */
473   {
474     oss_mixer_enuminfo routings = { 0, };
475     guint i;
476
477     if (ioctl (oss->fd, SNDCTL_DSP_GET_PLAYTGT_NAMES, &routings) != -1) {
478       GST_LOG_OBJECT (oss, "%u output routings (static list: %d)",
479           routings.nvalues, !!(routings.version == 0));
480       for (i = 0; i < routings.nvalues; ++i) {
481         GST_LOG_OBJECT (oss, "  output routing %d: %s", i,
482             &routings.strings[routings.strindex[i]]);
483       }
484     }
485   }
486
487   return TRUE;
488
489   /* ERRORS */
490 busy:
491   {
492     if (!silent_errors) {
493       GST_ELEMENT_ERROR (oss, RESOURCE, BUSY,
494           (_("Could not open audio device for playback. "
495                   "Device is being used by another application.")), (NULL));
496     }
497     g_free (device);
498     return FALSE;
499   }
500 no_permission:
501   {
502     if (!silent_errors) {
503       GST_ELEMENT_ERROR (oss, RESOURCE, OPEN_WRITE,
504           (_("Could not open audio device for playback. "
505                   "You don't have permission to open the device.")),
506           GST_ERROR_SYSTEM);
507     }
508     g_free (device);
509     return FALSE;
510   }
511 open_failed:
512   {
513     if (!silent_errors) {
514       GST_ELEMENT_ERROR (oss, RESOURCE, OPEN_WRITE,
515           (_("Could not open audio device for playback.")), GST_ERROR_SYSTEM);
516     }
517     g_free (device);
518     return FALSE;
519   }
520 legacy_oss:
521   {
522     if (!silent_errors) {
523       GST_ELEMENT_ERROR (oss, RESOURCE, OPEN_WRITE,
524           (_("Could not open audio device for playback. "
525                   "This version of the Open Sound System is not supported by this "
526                   "element.")), ("Try the 'osssink' element instead"));
527     }
528     gst_oss4_sink_close (asink);
529     g_free (device);
530     return FALSE;
531   }
532 non_block:
533   {
534     if (!silent_errors) {
535       GST_ELEMENT_ERROR (oss, RESOURCE, SETTINGS, (NULL),
536           ("Unable to set device %s into non-blocking mode: %s",
537               oss->device, g_strerror (errno)));
538     }
539     g_free (device);
540     return FALSE;
541   }
542 }
543
544 static gboolean
545 gst_oss4_sink_open_func (GstAudioSink * asink)
546 {
547   return gst_oss4_sink_open (asink, FALSE);
548 }
549
550 static gboolean
551 gst_oss4_sink_close (GstAudioSink * asink)
552 {
553   GstOss4Sink *oss = GST_OSS4_SINK (asink);
554
555   if (oss->fd != -1) {
556     GST_DEBUG_OBJECT (oss, "closing device");
557     close (oss->fd);
558     oss->fd = -1;
559   }
560
561   oss->bytes_per_sample = 0;
562   /* we keep the probed caps cached, at least until the device changes */
563
564   g_free (oss->open_device);
565   oss->open_device = NULL;
566
567   g_free (oss->device_name);
568   oss->device_name = NULL;
569
570   if (oss->probed_caps) {
571     gst_caps_unref (oss->probed_caps);
572     oss->probed_caps = NULL;
573   }
574
575   return TRUE;
576 }
577
578 static gboolean
579 gst_oss4_sink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec)
580 {
581   GstOss4Sink *oss;
582
583   oss = GST_OSS4_SINK (asink);
584
585   if (!gst_oss4_audio_set_format (GST_OBJECT_CAST (oss), oss->fd, spec)) {
586     GST_WARNING_OBJECT (oss, "Couldn't set requested format %" GST_PTR_FORMAT,
587         spec->caps);
588     return FALSE;
589   }
590
591   oss->bytes_per_sample = spec->bytes_per_sample;
592   return TRUE;
593 }
594
595 static gboolean
596 gst_oss4_sink_unprepare (GstAudioSink * asink)
597 {
598   /* could do a SNDCTL_DSP_HALT, but the OSS manual recommends a close/open,
599    * since HALT won't properly reset some devices, apparently */
600
601   if (!gst_oss4_sink_close (asink))
602     goto couldnt_close;
603
604   if (!gst_oss4_sink_open_func (asink))
605     goto couldnt_reopen;
606
607   return TRUE;
608
609   /* ERRORS */
610 couldnt_close:
611   {
612     GST_DEBUG_OBJECT (asink, "Couldn't close the audio device");
613     return FALSE;
614   }
615 couldnt_reopen:
616   {
617     GST_DEBUG_OBJECT (asink, "Couldn't reopen the audio device");
618     return FALSE;
619   }
620 }
621
622 static guint
623 gst_oss4_sink_write (GstAudioSink * asink, gpointer data, guint length)
624 {
625   GstOss4Sink *oss;
626   int n;
627
628   oss = GST_OSS4_SINK_CAST (asink);
629
630   n = write (oss->fd, data, length);
631   GST_LOG_OBJECT (asink, "wrote %d/%d samples, %d bytes",
632       n / oss->bytes_per_sample, length / oss->bytes_per_sample, n);
633
634   if (G_UNLIKELY (n < 0)) {
635     switch (errno) {
636       case ENOTSUP:
637       case EACCES:{
638         /* This is the most likely cause, I think */
639         GST_ELEMENT_ERROR (asink, RESOURCE, WRITE,
640             (_("Playback is not supported by this audio device.")),
641             ("write: %s (device: %s) (maybe this is an input-only device?)",
642                 g_strerror (errno), oss->open_device));
643         break;
644       }
645       default:{
646         GST_ELEMENT_ERROR (asink, RESOURCE, WRITE,
647             (_("Audio playback error.")),
648             ("write: %s (device: %s)", g_strerror (errno), oss->open_device));
649         break;
650       }
651     }
652   }
653
654   return n;
655 }
656
657 static guint
658 gst_oss4_sink_delay (GstAudioSink * asink)
659 {
660   GstOss4Sink *oss;
661   gint delay = -1;
662
663   oss = GST_OSS4_SINK_CAST (asink);
664
665   GST_OBJECT_LOCK (oss);
666   if (ioctl (oss->fd, SNDCTL_DSP_GETODELAY, &delay) < 0 || delay < 0) {
667     GST_LOG_OBJECT (oss, "GETODELAY failed");
668   }
669   GST_OBJECT_UNLOCK (oss);
670
671   if (G_UNLIKELY (delay < 0))   /* error case */
672     return 0;
673
674   return delay / oss->bytes_per_sample;
675 }
676
677 static void
678 gst_oss4_sink_reset (GstAudioSink * asink)
679 {
680   /* There's nothing we can do here really: OSS can't handle access to the
681    * same device/fd from multiple threads and might deadlock or blow up in
682    * other ways if we try an ioctl SNDCTL_DSP_HALT or similar */
683 }
684
685 static void
686 gst_oss4_sink_init_interfaces (GType type)
687 {
688   static const GInterfaceInfo svol_iface_info = {
689     NULL, NULL, NULL
690   };
691
692   g_type_add_interface_static (type, GST_TYPE_STREAM_VOLUME, &svol_iface_info);
693
694   gst_oss4_add_property_probe_interface (type);
695 }