Do not use short_description in section docs for elements. We extract them from eleme...
[platform/upstream/gst-plugins-good.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  * <refsect2>
23  * <para>
24  * This element lets you output sound using the Open Sound System (OSS)
25  * version 4.
26  * </para>
27  * <para>
28  * Note that you should almost always use generic audio conversion elements
29  * like audioconvert and audioresample in front of an audiosink to make sure
30  * your pipeline works under all circumstances (those conversion elements will
31  * act in passthrough-mode if no conversion is necessary).
32  * </para>
33  * <title>Example pipelines</title>
34  * <para>
35  * <programlisting>
36  * gst-launch -v audiotestsrc ! audioconvert ! volume volume=0.1 ! oss4sink
37  * </programlisting>
38  * will output a sine wave (continuous beep sound) to your sound card (with
39  * a very low volume as precaution).
40  * </para>
41  * <para>
42  * <programlisting>
43  * gst-launch -v filesrc location=music.ogg ! decodebin ! audioconvert ! audioresample ! oss4sink
44  * </programlisting>
45  * will play an Ogg/Vorbis audio file and output it using the Open Sound System
46  * version 4.
47  * </para>
48  * </refsect2>
49  *
50  * Since: 0.10.7
51  */
52
53 /* TODO: - add "volume" property for stream volume control and intercept tags
54  *         to set stream title
55  */
56
57 #ifdef HAVE_CONFIG_H
58 #include "config.h"
59 #endif
60
61 #include <sys/types.h>
62 #include <sys/stat.h>
63 #include <sys/ioctl.h>
64 #include <fcntl.h>
65 #include <errno.h>
66 #include <unistd.h>
67 #include <string.h>
68
69 #include <gst/gst-i18n-plugin.h>
70
71 #define NO_LEGACY_MIXER
72 #include "oss4-audio.h"
73 #include "oss4-sink.h"
74 #include "oss4-property-probe.h"
75 #include "oss4-soundcard.h"
76
77 GST_DEBUG_CATEGORY_EXTERN (oss4sink_debug);
78 #define GST_CAT_DEFAULT oss4sink_debug
79
80 static void gst_oss4_sink_init_interfaces (GType type);
81 static void gst_oss4_sink_dispose (GObject * object);
82 static void gst_oss4_sink_finalise (GObject * object);
83
84 static void gst_oss4_sink_get_property (GObject * object, guint prop_id,
85     GValue * value, GParamSpec * pspec);
86 static void gst_oss4_sink_set_property (GObject * object, guint prop_id,
87     const GValue * value, GParamSpec * pspec);
88
89 static GstCaps *gst_oss4_sink_getcaps (GstBaseSink * bsink);
90 static gboolean gst_oss4_sink_open (GstAudioSink * asink,
91     gboolean silent_errors);
92 static gboolean gst_oss4_sink_open_func (GstAudioSink * asink);
93 static gboolean gst_oss4_sink_close (GstAudioSink * asink);
94 static gboolean gst_oss4_sink_prepare (GstAudioSink * asink,
95     GstRingBufferSpec * spec);
96 static gboolean gst_oss4_sink_unprepare (GstAudioSink * asink);
97 static guint gst_oss4_sink_write (GstAudioSink * asink, gpointer data,
98     guint length);
99 static guint gst_oss4_sink_delay (GstAudioSink * asink);
100 static void gst_oss4_sink_reset (GstAudioSink * asink);
101
102 #define DEFAULT_DEVICE      NULL
103 #define DEFAULT_DEVICE_NAME NULL
104
105 enum
106 {
107   PROP_0,
108   PROP_DEVICE,
109   PROP_DEVICE_NAME
110 };
111
112 GST_BOILERPLATE_FULL (GstOss4Sink, gst_oss4_sink, GstAudioSink,
113     GST_TYPE_AUDIO_SINK, gst_oss4_sink_init_interfaces);
114
115 static void
116 gst_oss4_sink_dispose (GObject * object)
117 {
118   GstOss4Sink *osssink = GST_OSS4_SINK (object);
119
120   if (osssink->probed_caps) {
121     gst_caps_unref (osssink->probed_caps);
122     osssink->probed_caps = NULL;
123   }
124
125   G_OBJECT_CLASS (parent_class)->dispose (object);
126 }
127
128 static void
129 gst_oss4_sink_base_init (gpointer g_class)
130 {
131   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
132   GstPadTemplate *templ;
133
134   gst_element_class_set_details_simple (element_class,
135       "OSS v4 Audio Sink", "Sink/Audio",
136       "Output to a sound card via OSS version 4",
137       "Tim-Philipp Müller <tim centricular net>");
138
139   templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
140       gst_oss4_audio_get_template_caps ());
141   gst_element_class_add_pad_template (element_class, templ);
142 }
143
144 static void
145 gst_oss4_sink_class_init (GstOss4SinkClass * klass)
146 {
147   GstAudioSinkClass *audiosink_class = (GstAudioSinkClass *) klass;
148   GstBaseSinkClass *basesink_class = (GstBaseSinkClass *) klass;
149   GObjectClass *gobject_class = (GObjectClass *) klass;
150
151   gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_oss4_sink_dispose);
152   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_oss4_sink_finalise);
153   gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_oss4_sink_get_property);
154   gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_oss4_sink_set_property);
155
156   g_object_class_install_property (gobject_class, PROP_DEVICE,
157       g_param_spec_string ("device", "Device",
158           "OSS4 device (e.g. /dev/oss/hdaudio0/pcm0 or /dev/dspN) "
159           "(NULL = use first available playback device)",
160           DEFAULT_DEVICE, G_PARAM_READWRITE));
161
162   g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
163       g_param_spec_string ("device-name", "Device name",
164           "Human-readable name of the sound device", DEFAULT_DEVICE_NAME,
165           G_PARAM_READABLE));
166
167   basesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_oss4_sink_getcaps);
168
169   audiosink_class->open = GST_DEBUG_FUNCPTR (gst_oss4_sink_open_func);
170   audiosink_class->close = GST_DEBUG_FUNCPTR (gst_oss4_sink_close);
171   audiosink_class->prepare = GST_DEBUG_FUNCPTR (gst_oss4_sink_prepare);
172   audiosink_class->unprepare = GST_DEBUG_FUNCPTR (gst_oss4_sink_unprepare);
173   audiosink_class->write = GST_DEBUG_FUNCPTR (gst_oss4_sink_write);
174   audiosink_class->delay = GST_DEBUG_FUNCPTR (gst_oss4_sink_delay);
175   audiosink_class->reset = GST_DEBUG_FUNCPTR (gst_oss4_sink_reset);
176 }
177
178 static void
179 gst_oss4_sink_init (GstOss4Sink * osssink, GstOss4SinkClass * klass)
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 }
192
193 static void
194 gst_oss4_sink_finalise (GObject * object)
195 {
196   GstOss4Sink *osssink = GST_OSS4_SINK (object);
197
198   g_free (osssink->device);
199   osssink->device = NULL;
200
201   g_list_free (osssink->property_probe_list);
202   osssink->property_probe_list = NULL;
203
204   G_OBJECT_CLASS (parent_class)->finalize (object);
205 }
206
207 static void
208 gst_oss4_sink_set_property (GObject * object, guint prop_id,
209     const GValue * value, GParamSpec * pspec)
210 {
211   GstOss4Sink *oss = GST_OSS4_SINK (object);
212
213   switch (prop_id) {
214     case PROP_DEVICE:
215       GST_OBJECT_LOCK (oss);
216       if (oss->fd == -1) {
217         g_free (oss->device);
218         oss->device = g_value_dup_string (value);
219         if (oss->probed_caps) {
220           gst_caps_unref (oss->probed_caps);
221           oss->probed_caps = NULL;
222         }
223         g_free (oss->device_name);
224         oss->device_name = NULL;
225       } else {
226         g_warning ("%s: can't change \"device\" property while audio sink "
227             "is open", GST_OBJECT_NAME (oss));
228       }
229       GST_OBJECT_UNLOCK (oss);
230       break;
231     default:
232       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
233       break;
234   }
235 }
236
237 static void
238 gst_oss4_sink_get_property (GObject * object, guint prop_id,
239     GValue * value, GParamSpec * pspec)
240 {
241   GstOss4Sink *oss = GST_OSS4_SINK (object);
242
243   switch (prop_id) {
244     case PROP_DEVICE:
245       GST_OBJECT_LOCK (oss);
246       g_value_set_string (value, oss->device);
247       GST_OBJECT_UNLOCK (oss);
248       break;
249     case PROP_DEVICE_NAME:
250       GST_OBJECT_LOCK (oss);
251       if (oss->fd == -1 && oss->device != NULL) {
252         /* If device is set, try to retrieve the name even if we're not open */
253         if (gst_oss4_sink_open (GST_AUDIO_SINK (oss), TRUE)) {
254           g_value_set_string (value, oss->device_name);
255           gst_oss4_sink_close (GST_AUDIO_SINK (oss));
256         } else {
257           gchar *name = NULL;
258
259           gst_oss4_property_probe_find_device_name_nofd (GST_OBJECT (oss),
260               oss->device, &name);
261           g_value_set_string (value, name);
262           g_free (name);
263         }
264       } else {
265         g_value_set_string (value, oss->device_name);
266       }
267       GST_OBJECT_UNLOCK (oss);
268       break;
269     default:
270       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
271       break;
272   }
273 }
274
275 static GstCaps *
276 gst_oss4_sink_getcaps (GstBaseSink * bsink)
277 {
278   GstOss4Sink *oss;
279   GstCaps *caps;
280
281   oss = GST_OSS4_SINK (bsink);
282
283   if (oss->fd == -1) {
284     caps = gst_caps_copy (gst_oss4_audio_get_template_caps ());
285   } else if (oss->probed_caps) {
286     caps = gst_caps_copy (oss->probed_caps);
287   } else {
288     caps = gst_oss4_audio_probe_caps (GST_OBJECT (oss), oss->fd);
289     if (caps != NULL && !gst_caps_is_empty (caps)) {
290       oss->probed_caps = gst_caps_copy (caps);
291     }
292   }
293
294   return caps;
295 }
296
297 /* note: we must not take the object lock here unless we fix up get_property */
298 static gboolean
299 gst_oss4_sink_open (GstAudioSink * asink, gboolean silent_errors)
300 {
301   GstOss4Sink *oss;
302   gchar *device;
303   int mode;
304
305   oss = GST_OSS4_SINK (asink);
306
307   if (oss->device)
308     device = g_strdup (oss->device);
309   else
310     device = gst_oss4_audio_find_device (GST_OBJECT_CAST (oss));
311
312   /* desperate times, desperate measures */
313   if (device == NULL)
314     device = g_strdup ("/dev/dsp0");
315
316   GST_INFO_OBJECT (oss, "Trying to open OSS4 device '%s'", device);
317
318   /* we open in non-blocking mode even if we don't really want to do writes
319    * non-blocking because we can't be sure that this is really a genuine
320    * OSS4 device with well-behaved drivers etc. We really don't want to
321    * hang forever under any circumstances. */
322   oss->fd = open (device, O_WRONLY | O_NONBLOCK, 0);
323   if (oss->fd == -1) {
324     switch (errno) {
325       case EBUSY:
326         goto busy;
327       case EACCES:
328         goto no_permission;
329       default:
330         goto open_failed;
331     }
332   }
333
334   GST_INFO_OBJECT (oss, "Opened device '%s'", device);
335
336   /* Make sure it's OSS4. If it's old OSS, let osssink handle it */
337   if (!gst_oss4_audio_check_version (GST_OBJECT_CAST (oss), oss->fd))
338     goto legacy_oss;
339
340   /* now remove the non-blocking flag. */
341   mode = fcntl (oss->fd, F_GETFL);
342   mode &= ~O_NONBLOCK;
343   if (fcntl (oss->fd, F_SETFL, mode) < 0) {
344     /* some drivers do no support unsetting the non-blocking flag, try to
345      * close/open the device then. This is racy but we error out properly. */
346     GST_WARNING_OBJECT (oss, "failed to unset O_NONBLOCK (buggy driver?), "
347         "will try to re-open device now");
348     gst_oss4_sink_close (asink);
349     if ((oss->fd = open (device, O_WRONLY, 0)) == -1)
350       goto non_block;
351   }
352
353   oss->open_device = device;
354
355   /* not using ENGINEINFO here because it sometimes returns a different and
356    * less useful name than AUDIOINFO for the same device */
357   if (!gst_oss4_property_probe_find_device_name (GST_OBJECT (oss), oss->fd,
358           oss->open_device, &oss->device_name)) {
359     oss->device_name = NULL;
360   }
361
362   /* list output routings, for informational purposes only so far */
363   {
364     oss_mixer_enuminfo routings = { 0, };
365     guint i;
366
367     if (ioctl (oss->fd, SNDCTL_DSP_GET_PLAYTGT_NAMES, &routings) != -1) {
368       GST_LOG_OBJECT (oss, "%u output routings (static list: %d)",
369           routings.nvalues, !!(routings.version == 0));
370       for (i = 0; i < routings.nvalues; ++i) {
371         GST_LOG_OBJECT (oss, "  output routing %d: %s", i,
372             &routings.strings[routings.strindex[i]]);
373       }
374     }
375   }
376
377   return TRUE;
378
379   /* ERRORS */
380 busy:
381   {
382     if (!silent_errors) {
383       GST_ELEMENT_ERROR (oss, RESOURCE, BUSY,
384           (_("Could not open audio device for playback. "
385                   "Device is being used by another application.")), (NULL));
386     }
387     g_free (device);
388     return FALSE;
389   }
390 no_permission:
391   {
392     if (!silent_errors) {
393       GST_ELEMENT_ERROR (oss, RESOURCE, OPEN_WRITE,
394           (_("Could not open audio device for playback."
395                   "You don't have permission to open the device.")),
396           GST_ERROR_SYSTEM);
397     }
398     g_free (device);
399     return FALSE;
400   }
401 open_failed:
402   {
403     if (!silent_errors) {
404       GST_ELEMENT_ERROR (oss, RESOURCE, OPEN_WRITE,
405           (_("Could not open audio device for playback.")), GST_ERROR_SYSTEM);
406     }
407     g_free (device);
408     return FALSE;
409   }
410 legacy_oss:
411   {
412     if (!silent_errors) {
413       GST_ELEMENT_ERROR (oss, RESOURCE, OPEN_WRITE,
414           (_("Could not open audio device for playback."
415                   "This version of the Open Sound System is not supported by this "
416                   "element.")), ("Try the 'osssink' element instead"));
417     }
418     gst_oss4_sink_close (asink);
419     g_free (device);
420     return FALSE;
421   }
422 non_block:
423   {
424     if (!silent_errors) {
425       GST_ELEMENT_ERROR (oss, RESOURCE, SETTINGS, (NULL),
426           ("Unable to set device %s into non-blocking mode: %s",
427               oss->device, g_strerror (errno)));
428     }
429     g_free (device);
430     return FALSE;
431   }
432 }
433
434 static gboolean
435 gst_oss4_sink_open_func (GstAudioSink * asink)
436 {
437   return gst_oss4_sink_open (asink, FALSE);
438 }
439
440 static gboolean
441 gst_oss4_sink_close (GstAudioSink * asink)
442 {
443   GstOss4Sink *oss = GST_OSS4_SINK (asink);
444
445   if (oss->fd != -1) {
446     GST_DEBUG_OBJECT (oss, "closing device");
447     close (oss->fd);
448     oss->fd = -1;
449   }
450
451   oss->bytes_per_sample = 0;
452   /* we keep the probed caps cached, at least until the device changes */
453
454   g_free (oss->open_device);
455   oss->open_device = NULL;
456
457   g_free (oss->device_name);
458   oss->device_name = NULL;
459
460   if (oss->probed_caps) {
461     gst_caps_unref (oss->probed_caps);
462     oss->probed_caps = NULL;
463   }
464
465   return TRUE;
466 }
467
468 static gboolean
469 gst_oss4_sink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec)
470 {
471   GstOss4Sink *oss;
472
473   oss = GST_OSS4_SINK (asink);
474
475   if (!gst_oss4_audio_set_format (GST_OBJECT_CAST (oss), oss->fd, spec)) {
476     GST_WARNING_OBJECT (oss, "Couldn't set requested format %" GST_PTR_FORMAT,
477         spec->caps);
478     return FALSE;
479   }
480
481   oss->bytes_per_sample = spec->bytes_per_sample;
482   return TRUE;
483 }
484
485 static gboolean
486 gst_oss4_sink_unprepare (GstAudioSink * asink)
487 {
488   /* could do a SNDCTL_DSP_HALT, but the OSS manual recommends a close/open,
489    * since HALT won't properly reset some devices, apparently */
490
491   if (!gst_oss4_sink_close (asink))
492     goto couldnt_close;
493
494   if (!gst_oss4_sink_open_func (asink))
495     goto couldnt_reopen;
496
497   return TRUE;
498
499   /* ERRORS */
500 couldnt_close:
501   {
502     GST_DEBUG_OBJECT (asink, "Couldn't close the audio device");
503     return FALSE;
504   }
505 couldnt_reopen:
506   {
507     GST_DEBUG_OBJECT (asink, "Couldn't reopen the audio device");
508     return FALSE;
509   }
510 }
511
512 static guint
513 gst_oss4_sink_write (GstAudioSink * asink, gpointer data, guint length)
514 {
515   GstOss4Sink *oss;
516   int n;
517
518   oss = GST_OSS4_SINK_CAST (asink);
519
520   n = write (oss->fd, data, length);
521   GST_LOG_OBJECT (asink, "wrote %d/%d samples, %d bytes",
522       n / oss->bytes_per_sample, length / oss->bytes_per_sample, n);
523
524   if (G_UNLIKELY (n < 0)) {
525     switch (errno) {
526       case ENOTSUP:
527       case EACCES:{
528         /* This is the most likely cause, I think */
529         GST_ELEMENT_ERROR (asink, RESOURCE, WRITE,
530             (_("Playback is not supported by this audio device.")),
531             ("write: %s (device: %s) (maybe this is an input-only device?)",
532                 g_strerror (errno), oss->open_device));
533         break;
534       }
535       default:{
536         GST_ELEMENT_ERROR (asink, RESOURCE, WRITE,
537             (_("Audio playback error.")),
538             ("write: %s (device: %s)", g_strerror (errno), oss->open_device));
539         break;
540       }
541     }
542   }
543
544   return n;
545 }
546
547 static guint
548 gst_oss4_sink_delay (GstAudioSink * asink)
549 {
550   GstOss4Sink *oss;
551   gint delay = -1;
552
553   oss = GST_OSS4_SINK_CAST (asink);
554
555   if (ioctl (oss->fd, SNDCTL_DSP_GETODELAY, &delay) < 0 || delay < 0) {
556     GST_LOG_OBJECT (oss, "GETODELAY failed");
557     return 0;
558   }
559
560   return delay / oss->bytes_per_sample;
561 }
562
563 static void
564 gst_oss4_sink_reset (GstAudioSink * asink)
565 {
566   /* There's nothing we can do here really: OSS can't handle access to the
567    * same device/fd from multiple threads and might deadlock or blow up in
568    * other ways if we try an ioctl SNDCTL_DSP_HALT or similar */
569 }
570
571 static void
572 gst_oss4_sink_init_interfaces (GType type)
573 {
574   gst_oss4_add_property_probe_interface (type);
575 }