9f2902a0458b2212159f1cd8b5237e20d3a95ea8
[platform/upstream/gstreamer.git] / sys / oss / gstosssink.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *               2000,2005 Wim Taymans <wim@fluendo.com>
4  *
5  * gstosssink.c: 
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 /**
24  * SECTION:element-osssink
25  * @short_description: output sound using OSS
26  *
27  * <refsect2>
28  * <para>
29  * This element lets you output sound using the Open Sound System (OSS).
30  * </para>
31  * <para>
32  * Note that you should almost always use generic audio conversion elements
33  * like audioconvert and audioresample in front of an audiosink to make sure
34  * your pipeline works under all circumstances (those conversion elements will
35  * act in passthrough-mode if no conversion is necessary).
36  * </para>
37  * <title>Example pipelines</title>
38  * <para>
39  * <programlisting>
40  * gst-launch -v audiotestsrc ! audioconvert ! volume volume=0.1 ! osssink
41  * </programlisting>
42  * will output a sine wave (continuous beep sound) to your sound card (with
43  * a very low volume as precaution).
44  * </para>
45  * <para>
46  * <programlisting>
47  * gst-launch -v filesrc location=music.ogg ! decodebin ! audioconvert ! audioresample ! osssink
48  * </programlisting>
49  * will play an Ogg/Vorbis audio file and output it using the Open Sound System.
50  * </para>
51  * </refsect2>
52  */
53
54 #ifdef HAVE_CONFIG_H
55 #include "config.h"
56 #endif
57 #include <sys/ioctl.h>
58 #include <fcntl.h>
59 #include <errno.h>
60 #include <unistd.h>
61 #include <string.h>
62
63 #ifdef HAVE_OSS_INCLUDE_IN_SYS
64 # include <sys/soundcard.h>
65 #else
66 # ifdef HAVE_OSS_INCLUDE_IN_ROOT
67 #  include <soundcard.h>
68 # else
69 #  ifdef HAVE_OSS_INCLUDE_IN_MACHINE
70 #   include <machine/soundcard.h>
71 #  else
72 #   error "What to include?"
73 #  endif /* HAVE_OSS_INCLUDE_IN_MACHINE */
74 # endif /* HAVE_OSS_INCLUDE_IN_ROOT */
75 #endif /* HAVE_OSS_INCLUDE_IN_SYS */
76
77 #include "common.h"
78 #include "gstosssink.h"
79
80 GST_DEBUG_CATEGORY_EXTERN (oss_debug);
81 #define GST_CAT_DEFAULT oss_debug
82
83 /* elementfactory information */
84 static const GstElementDetails gst_oss_sink_details =
85 GST_ELEMENT_DETAILS ("Audio Sink (OSS)",
86     "Sink/Audio",
87     "Output to a sound card via OSS",
88     "Erik Walthinsen <omega@cse.ogi.edu>, "
89     "Wim Taymans <wim.taymans@chello.be>");
90
91 static void gst_oss_sink_base_init (gpointer g_class);
92 static void gst_oss_sink_class_init (GstOssSinkClass * klass);
93 static void gst_oss_sink_init (GstOssSink * osssink);
94
95 static void gst_oss_sink_dispose (GObject * object);
96 static void gst_oss_sink_finalise (GObject * object);
97
98 static void gst_oss_sink_get_property (GObject * object, guint prop_id,
99     GValue * value, GParamSpec * pspec);
100 static void gst_oss_sink_set_property (GObject * object, guint prop_id,
101     const GValue * value, GParamSpec * pspec);
102
103 static GstCaps *gst_oss_sink_getcaps (GstBaseSink * bsink);
104
105 static gboolean gst_oss_sink_open (GstAudioSink * asink);
106 static gboolean gst_oss_sink_close (GstAudioSink * asink);
107 static gboolean gst_oss_sink_prepare (GstAudioSink * asink,
108     GstRingBufferSpec * spec);
109 static gboolean gst_oss_sink_unprepare (GstAudioSink * asink);
110 static guint gst_oss_sink_write (GstAudioSink * asink, gpointer data,
111     guint length);
112 static guint gst_oss_sink_delay (GstAudioSink * asink);
113 static void gst_oss_sink_reset (GstAudioSink * asink);
114
115 /* OssSink signals and args */
116 enum
117 {
118   LAST_SIGNAL
119 };
120
121 #define DEFAULT_DEVICE  "/dev/dsp"
122 enum
123 {
124   PROP_0,
125   PROP_DEVICE,
126 };
127
128 static GstStaticPadTemplate osssink_sink_factory =
129     GST_STATIC_PAD_TEMPLATE ("sink",
130     GST_PAD_SINK,
131     GST_PAD_ALWAYS,
132     GST_STATIC_CAPS ("audio/x-raw-int, "
133         "endianness = (int) { " G_STRINGIFY (G_BYTE_ORDER) " }, "
134         "signed = (boolean) { TRUE, FALSE }, "
135         "width = (int) 16, "
136         "depth = (int) 16, "
137         "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]; "
138         "audio/x-raw-int, "
139         "signed = (boolean) { TRUE, FALSE }, "
140         "width = (int) 8, "
141         "depth = (int) 8, "
142         "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]")
143     );
144
145 static GstElementClass *parent_class = NULL;
146
147 /* static guint gst_oss_sink_signals[LAST_SIGNAL] = { 0 }; */
148
149 GType
150 gst_oss_sink_get_type (void)
151 {
152   static GType osssink_type = 0;
153
154   if (!osssink_type) {
155     static const GTypeInfo osssink_info = {
156       sizeof (GstOssSinkClass),
157       gst_oss_sink_base_init,
158       NULL,
159       (GClassInitFunc) gst_oss_sink_class_init,
160       NULL,
161       NULL,
162       sizeof (GstOssSink),
163       0,
164       (GInstanceInitFunc) gst_oss_sink_init,
165     };
166
167     osssink_type =
168         g_type_register_static (GST_TYPE_AUDIO_SINK, "GstOssSink",
169         &osssink_info, 0);
170   }
171
172   return osssink_type;
173 }
174
175 static void
176 gst_oss_sink_dispose (GObject * object)
177 {
178   GstOssSink *osssink = GST_OSSSINK (object);
179
180   if (osssink->probed_caps) {
181     gst_caps_unref (osssink->probed_caps);
182     osssink->probed_caps = NULL;
183   }
184
185   G_OBJECT_CLASS (parent_class)->dispose (object);
186 }
187
188 static void
189 gst_oss_sink_base_init (gpointer g_class)
190 {
191   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
192
193   gst_element_class_set_details (element_class, &gst_oss_sink_details);
194
195   gst_element_class_add_pad_template (element_class,
196       gst_static_pad_template_get (&osssink_sink_factory));
197 }
198 static void
199 gst_oss_sink_class_init (GstOssSinkClass * klass)
200 {
201   GObjectClass *gobject_class;
202   GstElementClass *gstelement_class;
203   GstBaseSinkClass *gstbasesink_class;
204   GstBaseAudioSinkClass *gstbaseaudiosink_class;
205   GstAudioSinkClass *gstaudiosink_class;
206
207   gobject_class = (GObjectClass *) klass;
208   gstelement_class = (GstElementClass *) klass;
209   gstbasesink_class = (GstBaseSinkClass *) klass;
210   gstbaseaudiosink_class = (GstBaseAudioSinkClass *) klass;
211   gstaudiosink_class = (GstAudioSinkClass *) klass;
212
213   parent_class = g_type_class_peek_parent (klass);
214
215   gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_oss_sink_dispose);
216   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_oss_sink_finalise);
217   gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_oss_sink_get_property);
218   gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_oss_sink_set_property);
219
220   g_object_class_install_property (gobject_class, PROP_DEVICE,
221       g_param_spec_string ("device", "Device",
222           "OSS device (usually /dev/dspN)", DEFAULT_DEVICE, G_PARAM_READWRITE));
223
224   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_oss_sink_getcaps);
225
226   gstaudiosink_class->open = GST_DEBUG_FUNCPTR (gst_oss_sink_open);
227   gstaudiosink_class->close = GST_DEBUG_FUNCPTR (gst_oss_sink_close);
228   gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR (gst_oss_sink_prepare);
229   gstaudiosink_class->unprepare = GST_DEBUG_FUNCPTR (gst_oss_sink_unprepare);
230   gstaudiosink_class->write = GST_DEBUG_FUNCPTR (gst_oss_sink_write);
231   gstaudiosink_class->delay = GST_DEBUG_FUNCPTR (gst_oss_sink_delay);
232   gstaudiosink_class->reset = GST_DEBUG_FUNCPTR (gst_oss_sink_reset);
233 }
234
235 static void
236 gst_oss_sink_init (GstOssSink * osssink)
237 {
238   GST_DEBUG_OBJECT (osssink, "initializing osssink");
239
240   osssink->device = g_strdup (DEFAULT_DEVICE);
241   osssink->fd = -1;
242 }
243
244 static void
245 gst_oss_sink_finalise (GObject * object)
246 {
247   GstOssSink *osssink = GST_OSSSINK (object);
248
249   g_free (osssink->device);
250 }
251
252 static void
253 gst_oss_sink_set_property (GObject * object, guint prop_id,
254     const GValue * value, GParamSpec * pspec)
255 {
256   GstOssSink *sink;
257
258   sink = GST_OSSSINK (object);
259
260   switch (prop_id) {
261     case PROP_DEVICE:
262       g_free (sink->device);
263       sink->device = g_value_dup_string (value);
264       if (sink->probed_caps) {
265         gst_caps_unref (sink->probed_caps);
266         sink->probed_caps = NULL;
267       }
268       break;
269     default:
270       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
271       break;
272   }
273 }
274
275 static void
276 gst_oss_sink_get_property (GObject * object, guint prop_id,
277     GValue * value, GParamSpec * pspec)
278 {
279   GstOssSink *sink;
280
281   sink = GST_OSSSINK (object);
282
283   switch (prop_id) {
284     case PROP_DEVICE:
285       g_value_set_string (value, sink->device);
286       break;
287     default:
288       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
289       break;
290   }
291 }
292
293 static GstCaps *
294 gst_oss_sink_getcaps (GstBaseSink * bsink)
295 {
296   GstOssSink *osssink;
297   GstCaps *caps;
298
299   osssink = GST_OSSSINK (bsink);
300
301   if (osssink->fd == -1) {
302     caps = gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD
303             (bsink)));
304   } else if (osssink->probed_caps) {
305     caps = gst_caps_copy (osssink->probed_caps);
306   } else {
307     caps = gst_oss_helper_probe_caps (osssink->fd);
308     if (caps && !gst_caps_is_empty (caps)) {
309       osssink->probed_caps = gst_caps_copy (caps);
310     }
311   }
312
313   return caps;
314 }
315
316 static gint
317 ilog2 (gint x)
318 {
319   /* well... hacker's delight explains... */
320   x = x | (x >> 1);
321   x = x | (x >> 2);
322   x = x | (x >> 4);
323   x = x | (x >> 8);
324   x = x | (x >> 16);
325   x = x - ((x >> 1) & 0x55555555);
326   x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
327   x = (x + (x >> 4)) & 0x0f0f0f0f;
328   x = x + (x >> 8);
329   x = x + (x >> 16);
330   return (x & 0x0000003f) - 1;
331 }
332
333 static gint
334 gst_oss_sink_get_format (GstBufferFormat fmt)
335 {
336   gint result;
337
338   switch (fmt) {
339     case GST_MU_LAW:
340       result = AFMT_MU_LAW;
341       break;
342     case GST_A_LAW:
343       result = AFMT_A_LAW;
344       break;
345     case GST_IMA_ADPCM:
346       result = AFMT_IMA_ADPCM;
347       break;
348     case GST_U8:
349       result = AFMT_U8;
350       break;
351     case GST_S16_LE:
352       result = AFMT_S16_LE;
353       break;
354     case GST_S16_BE:
355       result = AFMT_S16_BE;
356       break;
357     case GST_S8:
358       result = AFMT_S8;
359       break;
360     case GST_U16_LE:
361       result = AFMT_U16_LE;
362       break;
363     case GST_U16_BE:
364       result = AFMT_U16_BE;
365       break;
366     case GST_MPEG:
367       result = AFMT_MPEG;
368       break;
369     default:
370       result = 0;
371       break;
372   }
373   return result;
374 }
375
376 static gboolean
377 gst_oss_sink_open (GstAudioSink * asink)
378 {
379   GstOssSink *oss;
380   int mode;
381
382   oss = GST_OSSSINK (asink);
383
384   mode = O_WRONLY;
385   mode |= O_NONBLOCK;
386
387   oss->fd = open (oss->device, mode, 0);
388   if (oss->fd == -1) {
389     switch (errno) {
390       case EBUSY:
391         goto busy;
392       default:
393         goto open_failed;
394     }
395   }
396
397   return TRUE;
398
399 busy:
400   {
401     GST_ELEMENT_ERROR (oss, RESOURCE, BUSY, (NULL), (NULL));
402     return FALSE;
403   }
404
405 open_failed:
406   {
407     GST_ELEMENT_ERROR (oss, RESOURCE, OPEN_WRITE, (NULL), GST_ERROR_SYSTEM);
408     return FALSE;
409   }
410 }
411
412 static gboolean
413 gst_oss_sink_close (GstAudioSink * asink)
414 {
415   close (GST_OSSSINK (asink)->fd);
416   GST_OSSSINK (asink)->fd = -1;
417   return TRUE;
418 }
419
420 static gboolean
421 gst_oss_sink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec)
422 {
423   GstOssSink *oss;
424   struct audio_buf_info info;
425   int mode;
426   int tmp;
427
428   oss = GST_OSSSINK (asink);
429
430   mode = fcntl (oss->fd, F_GETFL);
431   mode &= ~O_NONBLOCK;
432   if (fcntl (oss->fd, F_SETFL, mode) == -1)
433     goto non_block;
434
435   tmp = gst_oss_sink_get_format (spec->format);
436   if (tmp == 0)
437     goto wrong_format;
438
439   if (spec->width != 16 && spec->width != 8)
440     goto dodgy_width;
441
442   SET_PARAM (oss, SNDCTL_DSP_SETFMT, tmp, "SETFMT");
443   if (spec->channels == 2)
444     SET_PARAM (oss, SNDCTL_DSP_STEREO, 1, "STEREO");
445   SET_PARAM (oss, SNDCTL_DSP_CHANNELS, spec->channels, "CHANNELS");
446   SET_PARAM (oss, SNDCTL_DSP_SPEED, spec->rate, "SPEED");
447
448   tmp = ilog2 (spec->segsize);
449   tmp = ((spec->segtotal & 0x7fff) << 16) | tmp;
450   GST_DEBUG_OBJECT (oss, "set segsize: %d, segtotal: %d, value: %08x",
451       spec->segsize, spec->segtotal, tmp);
452
453   SET_PARAM (oss, SNDCTL_DSP_SETFRAGMENT, tmp, "SETFRAGMENT");
454   GET_PARAM (oss, SNDCTL_DSP_GETOSPACE, &info, "GETOSPACE");
455
456   spec->segsize = info.fragsize;
457   spec->segtotal = info.fragstotal;
458
459   spec->bytes_per_sample = (spec->width / 8) * spec->channels;
460   oss->bytes_per_sample = (spec->width / 8) * spec->channels;
461   memset (spec->silence_sample, 0, spec->bytes_per_sample);
462
463   GST_DEBUG_OBJECT (oss, "got segsize: %d, segtotal: %d, value: %08x",
464       spec->segsize, spec->segtotal, tmp);
465
466   return TRUE;
467
468 non_block:
469   {
470     GST_ELEMENT_ERROR (oss, RESOURCE, SETTINGS, (NULL),
471         ("Unable to set device %s in non blocking mode: %s",
472             oss->device, g_strerror (errno)));
473     return FALSE;
474   }
475 wrong_format:
476   {
477     GST_ELEMENT_ERROR (oss, RESOURCE, SETTINGS, (NULL),
478         ("Unable to get format %d", spec->format));
479     return FALSE;
480   }
481 dodgy_width:
482   {
483     GST_ELEMENT_ERROR (oss, RESOURCE, SETTINGS, (NULL),
484         ("unexpected width %d", spec->width));
485     return FALSE;
486   }
487 }
488
489 static gboolean
490 gst_oss_sink_unprepare (GstAudioSink * asink)
491 {
492   /* could do a SNDCTL_DSP_RESET, but the OSS manual recommends a close/open */
493
494   if (!gst_oss_sink_close (asink))
495     goto couldnt_close;
496
497   if (!gst_oss_sink_open (asink))
498     goto couldnt_reopen;
499
500   return TRUE;
501
502 couldnt_close:
503   {
504     GST_DEBUG_OBJECT (asink, "Could not close the audio device");
505     return FALSE;
506   }
507 couldnt_reopen:
508   {
509     GST_DEBUG_OBJECT (asink, "Could not reopen the audio device");
510     return FALSE;
511   }
512 }
513
514 static guint
515 gst_oss_sink_write (GstAudioSink * asink, gpointer data, guint length)
516 {
517   return write (GST_OSSSINK (asink)->fd, data, length);
518 }
519
520 static guint
521 gst_oss_sink_delay (GstAudioSink * asink)
522 {
523   GstOssSink *oss;
524   gint delay = 0;
525   gint ret;
526
527   oss = GST_OSSSINK (asink);
528
529 #ifdef SNDCTL_DSP_GETODELAY
530   ret = ioctl (oss->fd, SNDCTL_DSP_GETODELAY, &delay);
531 #else
532   ret = -1;
533 #endif
534   if (ret < 0) {
535     audio_buf_info info;
536
537     ret = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &info);
538
539     delay = (ret < 0 ? 0 : (info.fragstotal * info.fragsize) - info.bytes);
540   }
541   return delay / oss->bytes_per_sample;
542 }
543
544 static void
545 gst_oss_sink_reset (GstAudioSink * asink)
546 {
547 #if 0
548   GstOssSink *oss;
549   gint ret;
550
551   oss = GST_OSSSINK (asink);
552
553   /* deadlocks on my machine... */
554   ret = ioctl (oss->fd, SNDCTL_DSP_RESET, 0);
555 #endif
556 }