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