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