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