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