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