• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  * @title: oss4sink
22  *
23  * This element lets you output sound using the Open Sound System (OSS)
24  * version 4.
25  *
26  * Note that you should almost always use generic audio conversion elements
27  * like audioconvert and audioresample in front of an audiosink to make sure
28  * your pipeline works under all circumstances (those conversion elements will
29  * act in passthrough-mode if no conversion is necessary).
30  *
31  * ## Example pipelines
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  *
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 GST_ELEMENT_REGISTER_DEFINE (oss4sink, "oss4sink", GST_RANK_SECONDARY + 1,
108     GST_TYPE_OSS4_SINK);
109 
110 static void
gst_oss4_sink_dispose(GObject * object)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
gst_oss4_sink_class_init(GstOss4SinkClass * klass)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
gst_oss4_sink_init(GstOss4Sink * osssink)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
gst_oss4_sink_finalize(GObject * object)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
gst_oss4_sink_set_volume(GstOss4Sink * oss,gdouble volume)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
gst_oss4_sink_get_volume(GstOss4Sink * oss)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
gst_oss4_sink_set_mute(GstOss4Sink * oss,gboolean mute)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
gst_oss4_sink_get_mute(GstOss4Sink * oss)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
gst_oss4_sink_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)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
gst_oss4_sink_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)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 *
gst_oss4_sink_getcaps(GstBaseSink * bsink,GstCaps * filter)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
gst_oss4_sink_open(GstAudioSink * asink,gboolean silent_errors)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
gst_oss4_sink_open_func(GstAudioSink * asink)546 gst_oss4_sink_open_func (GstAudioSink * asink)
547 {
548   if (!gst_oss4_sink_open (asink, FALSE))
549     return FALSE;
550 
551   /* the initial volume might not be the property default, so notify
552    * application to make it get a reading of the current volume */
553   g_object_notify (G_OBJECT (asink), "volume");
554   return TRUE;
555 }
556 
557 static gboolean
gst_oss4_sink_close(GstAudioSink * asink)558 gst_oss4_sink_close (GstAudioSink * asink)
559 {
560   GstOss4Sink *oss = GST_OSS4_SINK (asink);
561 
562   if (oss->fd != -1) {
563     GST_DEBUG_OBJECT (oss, "closing device");
564     close (oss->fd);
565     oss->fd = -1;
566   }
567 
568   oss->bytes_per_sample = 0;
569   /* we keep the probed caps cached, at least until the device changes */
570 
571   g_free (oss->open_device);
572   oss->open_device = NULL;
573 
574   g_free (oss->device_name);
575   oss->device_name = NULL;
576 
577   if (oss->probed_caps) {
578     gst_caps_unref (oss->probed_caps);
579     oss->probed_caps = NULL;
580   }
581 
582   return TRUE;
583 }
584 
585 static gboolean
gst_oss4_sink_prepare(GstAudioSink * asink,GstAudioRingBufferSpec * spec)586 gst_oss4_sink_prepare (GstAudioSink * asink, GstAudioRingBufferSpec * spec)
587 {
588   GstOss4Sink *oss;
589 
590   oss = GST_OSS4_SINK (asink);
591 
592   if (!gst_oss4_audio_set_format (GST_OBJECT_CAST (oss), oss->fd, spec)) {
593     GST_WARNING_OBJECT (oss, "Couldn't set requested format %" GST_PTR_FORMAT,
594         spec->caps);
595     return FALSE;
596   }
597 
598   oss->bytes_per_sample = GST_AUDIO_INFO_BPF (&spec->info);
599 
600   return TRUE;
601 }
602 
603 static gboolean
gst_oss4_sink_unprepare(GstAudioSink * asink)604 gst_oss4_sink_unprepare (GstAudioSink * asink)
605 {
606   /* could do a SNDCTL_DSP_HALT, but the OSS manual recommends a close/open,
607    * since HALT won't properly reset some devices, apparently */
608 
609   if (!gst_oss4_sink_close (asink))
610     goto couldnt_close;
611 
612   if (!gst_oss4_sink_open_func (asink))
613     goto couldnt_reopen;
614 
615   return TRUE;
616 
617   /* ERRORS */
618 couldnt_close:
619   {
620     GST_DEBUG_OBJECT (asink, "Couldn't close the audio device");
621     return FALSE;
622   }
623 couldnt_reopen:
624   {
625     GST_DEBUG_OBJECT (asink, "Couldn't reopen the audio device");
626     return FALSE;
627   }
628 }
629 
630 static gint
gst_oss4_sink_write(GstAudioSink * asink,gpointer data,guint length)631 gst_oss4_sink_write (GstAudioSink * asink, gpointer data, guint length)
632 {
633   GstOss4Sink *oss;
634   int n;
635 
636   oss = GST_OSS4_SINK_CAST (asink);
637 
638   n = write (oss->fd, data, length);
639   GST_LOG_OBJECT (asink, "wrote %d/%d samples, %d bytes",
640       n / oss->bytes_per_sample, length / oss->bytes_per_sample, n);
641 
642   if (G_UNLIKELY (n < 0)) {
643     switch (errno) {
644       case ENOTSUP:
645       case EACCES:{
646         /* This is the most likely cause, I think */
647         GST_ELEMENT_ERROR (asink, RESOURCE, WRITE,
648             (_("Playback is not supported by this audio device.")),
649             ("write: %s (device: %s) (maybe this is an input-only device?)",
650                 g_strerror (errno), oss->open_device));
651         break;
652       }
653       default:{
654         GST_ELEMENT_ERROR (asink, RESOURCE, WRITE,
655             (_("Audio playback error.")),
656             ("write: %s (device: %s)", g_strerror (errno), oss->open_device));
657         break;
658       }
659     }
660   }
661 
662   return n;
663 }
664 
665 static guint
gst_oss4_sink_delay(GstAudioSink * asink)666 gst_oss4_sink_delay (GstAudioSink * asink)
667 {
668   GstOss4Sink *oss;
669   gint delay = -1;
670 
671   oss = GST_OSS4_SINK_CAST (asink);
672 
673   GST_OBJECT_LOCK (oss);
674   if (ioctl (oss->fd, SNDCTL_DSP_GETODELAY, &delay) < 0 || delay < 0) {
675     GST_LOG_OBJECT (oss, "GETODELAY failed");
676   }
677   GST_OBJECT_UNLOCK (oss);
678 
679   if (G_UNLIKELY (delay < 0))   /* error case */
680     return 0;
681 
682   return delay / oss->bytes_per_sample;
683 }
684 
685 static void
gst_oss4_sink_reset(GstAudioSink * asink)686 gst_oss4_sink_reset (GstAudioSink * asink)
687 {
688   /* There's nothing we can do here really: OSS can't handle access to the
689    * same device/fd from multiple threads and might deadlock or blow up in
690    * other ways if we try an ioctl SNDCTL_DSP_HALT or similar */
691 }
692