docs: gst-launch -> gst-launch-1.0 and ffmpegcolorspace -> videoconvert
[platform/upstream/gstreamer.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-1.0 -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-1.0 -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/audio/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_dispose (GObject * object);
70 static void gst_oss4_sink_finalize (GObject * object);
71
72 static void gst_oss4_sink_get_property (GObject * object, guint prop_id,
73     GValue * value, GParamSpec * pspec);
74 static void gst_oss4_sink_set_property (GObject * object, guint prop_id,
75     const GValue * value, GParamSpec * pspec);
76
77 static GstCaps *gst_oss4_sink_getcaps (GstBaseSink * bsink, GstCaps * filter);
78 static gboolean gst_oss4_sink_open (GstAudioSink * asink,
79     gboolean silent_errors);
80 static gboolean gst_oss4_sink_open_func (GstAudioSink * asink);
81 static gboolean gst_oss4_sink_close (GstAudioSink * asink);
82 static gboolean gst_oss4_sink_prepare (GstAudioSink * asink,
83     GstAudioRingBufferSpec * spec);
84 static gboolean gst_oss4_sink_unprepare (GstAudioSink * asink);
85 static gint gst_oss4_sink_write (GstAudioSink * asink, gpointer data,
86     guint length);
87 static guint gst_oss4_sink_delay (GstAudioSink * asink);
88 static void gst_oss4_sink_reset (GstAudioSink * asink);
89
90 #define DEFAULT_DEVICE      NULL
91 #define DEFAULT_DEVICE_NAME NULL
92 #define DEFAULT_MUTE        FALSE
93 #define DEFAULT_VOLUME      1.0
94 #define MAX_VOLUME          10.0
95
96 enum
97 {
98   PROP_0,
99   PROP_DEVICE,
100   PROP_DEVICE_NAME,
101   PROP_VOLUME,
102   PROP_MUTE,
103   PROP_LAST
104 };
105
106 #define gst_oss4_sink_parent_class parent_class
107 G_DEFINE_TYPE_WITH_CODE (GstOss4Sink, gst_oss4_sink,
108     GST_TYPE_AUDIO_SINK, G_IMPLEMENT_INTERFACE (GST_TYPE_STREAM_VOLUME, NULL));
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_class_init (GstOss4SinkClass * klass)
125 {
126   GstAudioSinkClass *audiosink_class = (GstAudioSinkClass *) klass;
127   GstBaseSinkClass *basesink_class = (GstBaseSinkClass *) klass;
128   GstElementClass *gstelement_class = (GstElementClass *) klass;
129   GObjectClass *gobject_class = (GObjectClass *) klass;
130   GstPadTemplate *templ;
131
132   gobject_class->dispose = gst_oss4_sink_dispose;
133   gobject_class->finalize = gst_oss4_sink_finalize;
134   gobject_class->get_property = gst_oss4_sink_get_property;
135   gobject_class->set_property = gst_oss4_sink_set_property;
136
137   g_object_class_install_property (gobject_class, PROP_DEVICE,
138       g_param_spec_string ("device", "Device",
139           "OSS4 device (e.g. /dev/oss/hdaudio0/pcm0 or /dev/dspN) "
140           "(NULL = use first available playback device)",
141           DEFAULT_DEVICE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
142
143   g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
144       g_param_spec_string ("device-name", "Device name",
145           "Human-readable name of the sound device", DEFAULT_DEVICE_NAME,
146           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
147
148   g_object_class_install_property (gobject_class,
149       PROP_VOLUME,
150       g_param_spec_double ("volume", "Volume",
151           "Linear volume of this stream, 1.0=100%", 0.0, MAX_VOLUME,
152           DEFAULT_VOLUME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
153
154   g_object_class_install_property (gobject_class,
155       PROP_MUTE,
156       g_param_spec_boolean ("mute", "Mute",
157           "Mute state of this stream", DEFAULT_MUTE,
158           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
159
160   basesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_oss4_sink_getcaps);
161
162   audiosink_class->open = GST_DEBUG_FUNCPTR (gst_oss4_sink_open_func);
163   audiosink_class->close = GST_DEBUG_FUNCPTR (gst_oss4_sink_close);
164   audiosink_class->prepare = GST_DEBUG_FUNCPTR (gst_oss4_sink_prepare);
165   audiosink_class->unprepare = GST_DEBUG_FUNCPTR (gst_oss4_sink_unprepare);
166   audiosink_class->write = GST_DEBUG_FUNCPTR (gst_oss4_sink_write);
167   audiosink_class->delay = GST_DEBUG_FUNCPTR (gst_oss4_sink_delay);
168   audiosink_class->reset = GST_DEBUG_FUNCPTR (gst_oss4_sink_reset);
169
170   gst_element_class_set_static_metadata (gstelement_class,
171       "OSS v4 Audio Sink", "Sink/Audio",
172       "Output to a sound card via OSS version 4",
173       "Tim-Philipp Müller <tim centricular net>");
174
175   templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
176       gst_oss4_audio_get_template_caps ());
177   gst_element_class_add_pad_template (gstelement_class, templ);
178 }
179
180 static void
181 gst_oss4_sink_init (GstOss4Sink * osssink)
182 {
183   const gchar *device;
184
185   device = g_getenv ("AUDIODEV");
186   if (device == NULL)
187     device = DEFAULT_DEVICE;
188   osssink->device = g_strdup (device);
189   osssink->fd = -1;
190   osssink->bytes_per_sample = 0;
191   osssink->probed_caps = NULL;
192   osssink->device_name = NULL;
193   osssink->mute_volume = 100 | (100 << 8);
194 }
195
196 static void
197 gst_oss4_sink_finalize (GObject * object)
198 {
199   GstOss4Sink *osssink = GST_OSS4_SINK (object);
200
201   g_free (osssink->device);
202   osssink->device = NULL;
203
204   G_OBJECT_CLASS (parent_class)->finalize (object);
205 }
206
207 static void
208 gst_oss4_sink_set_volume (GstOss4Sink * oss, gdouble volume)
209 {
210   int ivol;
211
212   volume = volume * 100.0;
213   ivol = (int) volume | ((int) volume << 8);
214   GST_OBJECT_LOCK (oss);
215   if (ioctl (oss->fd, SNDCTL_DSP_SETPLAYVOL, &ivol) < 0) {
216     GST_LOG_OBJECT (oss, "SETPLAYVOL failed");
217   }
218   GST_OBJECT_UNLOCK (oss);
219 }
220
221 static gdouble
222 gst_oss4_sink_get_volume (GstOss4Sink * oss)
223 {
224   int ivol, lvol, rvol;
225   gdouble dvol = DEFAULT_VOLUME;
226
227   GST_OBJECT_LOCK (oss);
228   if (ioctl (oss->fd, SNDCTL_DSP_GETPLAYVOL, &ivol) < 0) {
229     GST_LOG_OBJECT (oss, "GETPLAYVOL failed");
230   } else {
231     /* Return the higher of the two volume channels, if different */
232     lvol = ivol & 0xff;
233     rvol = (ivol >> 8) & 0xff;
234     dvol = MAX (lvol, rvol) / 100.0;
235   }
236   GST_OBJECT_UNLOCK (oss);
237
238   return dvol;
239 }
240
241 static void
242 gst_oss4_sink_set_mute (GstOss4Sink * oss, gboolean mute)
243 {
244   int ivol;
245
246   if (mute) {
247     /*
248      * OSSv4 does not have a per-channel mute, so simulate by setting
249      * the value to 0.  Save the volume before doing a mute so we can
250      * reset the value when the user un-mutes.
251      */
252     ivol = 0;
253
254     GST_OBJECT_LOCK (oss);
255     if (ioctl (oss->fd, SNDCTL_DSP_GETPLAYVOL, &oss->mute_volume) < 0) {
256       GST_LOG_OBJECT (oss, "GETPLAYVOL failed");
257     }
258     if (ioctl (oss->fd, SNDCTL_DSP_SETPLAYVOL, &ivol) < 0) {
259       GST_LOG_OBJECT (oss, "SETPLAYVOL failed");
260     }
261     GST_OBJECT_UNLOCK (oss);
262   } else {
263     /*
264      * If the saved volume is 0, then reset it to 100.  Otherwise the mute
265      * can get stuck.  This can happen, for example, due to rounding
266      * errors in converting from the float to an integer.
267      */
268     if (oss->mute_volume == 0) {
269       oss->mute_volume = 100 | (100 << 8);
270     }
271     GST_OBJECT_LOCK (oss);
272     if (ioctl (oss->fd, SNDCTL_DSP_SETPLAYVOL, &oss->mute_volume) < 0) {
273       GST_LOG_OBJECT (oss, "SETPLAYVOL failed");
274     }
275     GST_OBJECT_UNLOCK (oss);
276   }
277 }
278
279 static gboolean
280 gst_oss4_sink_get_mute (GstOss4Sink * oss)
281 {
282   int ivol, lvol, rvol;
283
284   GST_OBJECT_LOCK (oss);
285   if (ioctl (oss->fd, SNDCTL_DSP_GETPLAYVOL, &ivol) < 0) {
286     GST_LOG_OBJECT (oss, "GETPLAYVOL failed");
287     lvol = rvol = 100;
288   } else {
289     lvol = ivol & 0xff;
290     rvol = (ivol >> 8) & 0xff;
291   }
292   GST_OBJECT_UNLOCK (oss);
293
294   return (lvol == 0 && rvol == 0);
295 }
296
297 static void
298 gst_oss4_sink_set_property (GObject * object, guint prop_id,
299     const GValue * value, GParamSpec * pspec)
300 {
301   GstOss4Sink *oss = GST_OSS4_SINK (object);
302
303   switch (prop_id) {
304     case PROP_DEVICE:
305       GST_OBJECT_LOCK (oss);
306       if (oss->fd == -1) {
307         g_free (oss->device);
308         oss->device = g_value_dup_string (value);
309         if (oss->probed_caps) {
310           gst_caps_unref (oss->probed_caps);
311           oss->probed_caps = NULL;
312         }
313         g_free (oss->device_name);
314         oss->device_name = NULL;
315       } else {
316         g_warning ("%s: can't change \"device\" property while audio sink "
317             "is open", GST_OBJECT_NAME (oss));
318       }
319       GST_OBJECT_UNLOCK (oss);
320       break;
321     case PROP_VOLUME:
322       gst_oss4_sink_set_volume (oss, g_value_get_double (value));
323       break;
324     case PROP_MUTE:
325       gst_oss4_sink_set_mute (oss, g_value_get_boolean (value));
326       break;
327     default:
328       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
329       break;
330   }
331 }
332
333 static void
334 gst_oss4_sink_get_property (GObject * object, guint prop_id,
335     GValue * value, GParamSpec * pspec)
336 {
337   GstOss4Sink *oss = GST_OSS4_SINK (object);
338
339   switch (prop_id) {
340     case PROP_DEVICE:
341       GST_OBJECT_LOCK (oss);
342       g_value_set_string (value, oss->device);
343       GST_OBJECT_UNLOCK (oss);
344       break;
345     case PROP_DEVICE_NAME:
346       GST_OBJECT_LOCK (oss);
347       if (oss->fd == -1 && oss->device != NULL) {
348         /* If device is set, try to retrieve the name even if we're not open */
349         if (gst_oss4_sink_open (GST_AUDIO_SINK (oss), TRUE)) {
350           g_value_set_string (value, oss->device_name);
351           gst_oss4_sink_close (GST_AUDIO_SINK (oss));
352         } else {
353           gchar *name = NULL;
354
355           gst_oss4_property_probe_find_device_name_nofd (GST_OBJECT (oss),
356               oss->device, &name);
357           g_value_set_string (value, name);
358           g_free (name);
359         }
360       } else {
361         g_value_set_string (value, oss->device_name);
362       }
363       GST_OBJECT_UNLOCK (oss);
364       break;
365     case PROP_VOLUME:
366       g_value_set_double (value, gst_oss4_sink_get_volume (oss));
367       break;
368     case PROP_MUTE:
369       g_value_set_boolean (value, gst_oss4_sink_get_mute (oss));
370       break;
371     default:
372       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
373       break;
374   }
375 }
376
377 static GstCaps *
378 gst_oss4_sink_getcaps (GstBaseSink * bsink, GstCaps * filter)
379 {
380   GstOss4Sink *oss;
381   GstCaps *caps;
382
383   oss = GST_OSS4_SINK (bsink);
384
385   if (oss->fd == -1) {
386     caps = gst_oss4_audio_get_template_caps ();
387   } else if (oss->probed_caps) {
388     caps = gst_caps_copy (oss->probed_caps);
389   } else {
390     caps = gst_oss4_audio_probe_caps (GST_OBJECT (oss), oss->fd);
391     if (caps != NULL && !gst_caps_is_empty (caps)) {
392       oss->probed_caps = gst_caps_copy (caps);
393     }
394   }
395
396   if (filter && caps) {
397     GstCaps *intersection;
398
399     intersection =
400         gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
401     gst_caps_unref (caps);
402     return intersection;
403   } else {
404     return caps;
405   }
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, GstAudioRingBufferSpec * 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 = GST_AUDIO_INFO_BPF (&spec->info);
593
594   return TRUE;
595 }
596
597 static gboolean
598 gst_oss4_sink_unprepare (GstAudioSink * asink)
599 {
600   /* could do a SNDCTL_DSP_HALT, but the OSS manual recommends a close/open,
601    * since HALT won't properly reset some devices, apparently */
602
603   if (!gst_oss4_sink_close (asink))
604     goto couldnt_close;
605
606   if (!gst_oss4_sink_open_func (asink))
607     goto couldnt_reopen;
608
609   return TRUE;
610
611   /* ERRORS */
612 couldnt_close:
613   {
614     GST_DEBUG_OBJECT (asink, "Couldn't close the audio device");
615     return FALSE;
616   }
617 couldnt_reopen:
618   {
619     GST_DEBUG_OBJECT (asink, "Couldn't reopen the audio device");
620     return FALSE;
621   }
622 }
623
624 static gint
625 gst_oss4_sink_write (GstAudioSink * asink, gpointer data, guint length)
626 {
627   GstOss4Sink *oss;
628   int n;
629
630   oss = GST_OSS4_SINK_CAST (asink);
631
632   n = write (oss->fd, data, length);
633   GST_LOG_OBJECT (asink, "wrote %d/%d samples, %d bytes",
634       n / oss->bytes_per_sample, length / oss->bytes_per_sample, n);
635
636   if (G_UNLIKELY (n < 0)) {
637     switch (errno) {
638       case ENOTSUP:
639       case EACCES:{
640         /* This is the most likely cause, I think */
641         GST_ELEMENT_ERROR (asink, RESOURCE, WRITE,
642             (_("Playback is not supported by this audio device.")),
643             ("write: %s (device: %s) (maybe this is an input-only device?)",
644                 g_strerror (errno), oss->open_device));
645         break;
646       }
647       default:{
648         GST_ELEMENT_ERROR (asink, RESOURCE, WRITE,
649             (_("Audio playback error.")),
650             ("write: %s (device: %s)", g_strerror (errno), oss->open_device));
651         break;
652       }
653     }
654   }
655
656   return n;
657 }
658
659 static guint
660 gst_oss4_sink_delay (GstAudioSink * asink)
661 {
662   GstOss4Sink *oss;
663   gint delay = -1;
664
665   oss = GST_OSS4_SINK_CAST (asink);
666
667   GST_OBJECT_LOCK (oss);
668   if (ioctl (oss->fd, SNDCTL_DSP_GETODELAY, &delay) < 0 || delay < 0) {
669     GST_LOG_OBJECT (oss, "GETODELAY failed");
670   }
671   GST_OBJECT_UNLOCK (oss);
672
673   if (G_UNLIKELY (delay < 0))   /* error case */
674     return 0;
675
676   return delay / oss->bytes_per_sample;
677 }
678
679 static void
680 gst_oss4_sink_reset (GstAudioSink * asink)
681 {
682   /* There's nothing we can do here really: OSS can't handle access to the
683    * same device/fd from multiple threads and might deadlock or blow up in
684    * other ways if we try an ioctl SNDCTL_DSP_HALT or similar */
685 }