1 /*****************************************************************************
2 * oss.c: Open Sound System audio output plugin for VLC
3 *****************************************************************************
4 * Copyright (C) 2000-2002 VLC authors and VideoLAN
5 * Copyright (C) 2007-2012 RĂ©mi Denis-Courmont
7 * Authors: Michel Kaempf <maxx@via.ecp.fr>
8 * Sam Hocevar <sam@zoy.org>
9 * Christophe Massiot <massiot@via.ecp.fr>
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation; either version 2.1 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this program; if not, write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
33 #include <sys/types.h>
35 #include <sys/ioctl.h>
36 #ifdef HAVE_SOUNDCARD_H
37 # include <soundcard.h>
39 # include <sys/soundcard.h>
42 #ifndef SNDCTL_DSP_HALT
43 # define SNDCTL_DSP_HALT SNDCTL_DSP_RESET
46 #include <vlc_common.h>
47 #include <vlc_plugin.h>
52 #define A52_FRAME_NB 1536
57 audio_sample_format_t format
;
66 static int Open (vlc_object_t
*);
67 static void Close (vlc_object_t
*);
69 #define AUDIO_DEV_TEXT N_("Audio output device")
70 #define AUDIO_DEV_LONGTEXT N_("OSS device node path.")
73 set_shortname( "OSS" )
74 set_description (N_("Open Sound System audio output"))
75 set_category( CAT_AUDIO
)
76 set_subcategory( SUBCAT_AUDIO_AOUT
)
77 add_string ("oss-audio-device", "",
78 AUDIO_DEV_TEXT
, AUDIO_DEV_LONGTEXT
, false)
80 set_capability( "audio output", 100 )
81 set_callbacks (Open
, Close
)
84 static int TimeGet (audio_output_t
*, mtime_t
*);
85 static void Play (audio_output_t
*, block_t
*);
86 static void Pause (audio_output_t
*, bool, mtime_t
);
87 static void Flush (audio_output_t
*, bool);
89 static int Start (audio_output_t
*aout
, audio_sample_format_t
*restrict fmt
)
91 aout_sys_t
* sys
= aout
->sys
;
93 if (aout_FormatNbChannels(fmt
) == 0)
97 const char *device
= sys
->device
;
99 device
= getenv ("OSS_AUDIODEV");
103 int fd
= vlc_open (device
, O_WRONLY
);
106 msg_Err (aout
, "cannot open OSS device %s: %s", device
,
107 vlc_strerror_c(errno
));
110 msg_Dbg (aout
, "using OSS device: %s", device
);
112 /* Select audio format */
116 switch (fmt
->i_format
)
125 format
= AFMT_S32_NE
;
128 format
= AFMT_S16_NE
;
134 if (AOUT_FMT_SPDIF(fmt
))
135 spdif
= var_InheritBool (aout
, "spdif");
143 format
= AFMT_S16_NE
;
146 if (ioctl (fd
, SNDCTL_DSP_SETFMT
, &format
) < 0)
148 msg_Err (aout
, "cannot set audio format 0x%X: %s", format
,
149 vlc_strerror_c(errno
));
155 case AFMT_U8
: fmt
->i_format
= VLC_CODEC_U8
; break;
156 case AFMT_S16_NE
: fmt
->i_format
= VLC_CODEC_S16N
; break;
157 case AFMT_S32_NE
: fmt
->i_format
= VLC_CODEC_S32N
; break;
159 case AFMT_FLOAT
: fmt
->i_format
= VLC_CODEC_FL32
; break;
164 fmt
->i_format
= VLC_CODEC_SPDIFL
;
168 msg_Err (aout
, "unsupported audio format 0x%X", format
);
172 /* Select channels count */
173 int channels
= spdif
? 2 : aout_FormatNbChannels (fmt
);
174 if (ioctl (fd
, SNDCTL_DSP_CHANNELS
, &channels
) < 0)
176 msg_Err (aout
, "cannot set %d channels: %s", channels
,
177 vlc_strerror_c(errno
));
183 case 1: channels
= AOUT_CHAN_CENTER
; break;
184 case 2: channels
= AOUT_CHANS_STEREO
; break;
185 case 4: channels
= AOUT_CHANS_4_0
; break;
186 case 6: channels
= AOUT_CHANS_5_1
; break;
187 case 8: channels
= AOUT_CHANS_7_1
; break;
189 msg_Err (aout
, "unsupported channels count %d", channels
);
193 /* Select sample rate */
194 int rate
= spdif
? 48000 : fmt
->i_rate
;
195 if (ioctl (fd
, SNDCTL_DSP_SPEED
, &rate
) < 0)
197 msg_Err (aout
, "cannot set %d Hz sample rate: %s", rate
,
198 vlc_strerror_c(errno
));
202 /* Setup audio_output_t */
203 aout
->time_get
= TimeGet
;
210 fmt
->i_bytes_per_frame
= AOUT_SPDIF_SIZE
;
211 fmt
->i_frame_length
= A52_FRAME_NB
;
216 fmt
->i_physical_channels
= channels
;
218 fmt
->channel_type
= AUDIO_CHANNEL_TYPE_BITMAP
;
219 aout_FormatPrepare (fmt
);
224 bytes
= AOUT_SPDIF_SIZE
;
226 bytes
= fmt
->i_rate
/ (CLOCK_FREQ
/ AOUT_MIN_PREPARE_TIME
)
227 * fmt
->i_bytes_per_frame
;
228 if (unlikely(bytes
< 16))
231 int frag
= (AOUT_MAX_ADVANCE_TIME
/ AOUT_MIN_PREPARE_TIME
) << 16
232 | (32 - clz32(bytes
- 1));
233 if (ioctl (fd
, SNDCTL_DSP_SETFRAGMENT
, &frag
) < 0)
234 msg_Err (aout
, "cannot set 0x%08x fragment: %s", frag
,
235 vlc_strerror_c(errno
));
238 aout_SoftVolumeStart (aout
);
239 sys
->starting
= true;
247 static int TimeGet (audio_output_t
*aout
, mtime_t
*restrict pts
)
249 aout_sys_t
*sys
= aout
->sys
;
252 if (ioctl (sys
->fd
, SNDCTL_DSP_GETODELAY
, &delay
) < 0)
254 msg_Warn (aout
, "cannot get delay: %s", vlc_strerror_c(errno
));
258 *pts
= (delay
* CLOCK_FREQ
* sys
->format
.i_frame_length
)
259 / (sys
->format
.i_rate
* sys
->format
.i_bytes_per_frame
);
264 * Queues one audio buffer to the hardware.
266 static void Play (audio_output_t
*aout
, block_t
*block
)
268 aout_sys_t
*sys
= aout
->sys
;
271 while (block
->i_buffer
> 0)
273 ssize_t bytes
= write (fd
, block
->p_buffer
, block
->i_buffer
);
276 block
->p_buffer
+= bytes
;
277 block
->i_buffer
-= bytes
;
280 msg_Err (aout
, "cannot write samples: %s", vlc_strerror_c(errno
));
282 block_Release (block
);
286 * Pauses/resumes the audio playback.
288 static void Pause (audio_output_t
*aout
, bool pause
, mtime_t date
)
290 aout_sys_t
*sys
= aout
->sys
;
294 ioctl (fd
, pause
? SNDCTL_DSP_SILENCE
: SNDCTL_DSP_SKIP
, NULL
);
298 * Flushes/drains the audio playback buffer.
300 static void Flush (audio_output_t
*aout
, bool wait
)
302 aout_sys_t
*sys
= aout
->sys
;
306 return; /* drain is implicit with OSS */
307 ioctl (fd
, SNDCTL_DSP_HALT
, NULL
);
311 * Releases the audio output device.
313 static void Stop (audio_output_t
*aout
)
315 aout_sys_t
*sys
= aout
->sys
;
318 ioctl (fd
, SNDCTL_DSP_HALT
, NULL
);
323 static int DevicesEnum (audio_output_t
*aout
)
325 int fd
= vlc_open ("/dev/dsp", O_WRONLY
);
332 if (ioctl (fd
, SNDCTL_SYSINFO
, &si
) < 0)
334 msg_Err (aout
, "cannot get system infos: %s", vlc_strerror(errno
));
338 msg_Dbg (aout
, "using %s version %s (0x%06X) under %s", si
.product
,
339 si
.version
, si
.versionnum
, si
.license
);
341 for (int i
= 0; i
< si
.numaudios
; i
++)
343 oss_audioinfo ai
= { .dev
= i
};
345 if (ioctl (fd
, SNDCTL_AUDIOINFO
, &ai
) < 0)
347 msg_Warn (aout
, "cannot get device %d infos: %s", i
,
348 vlc_strerror_c(errno
));
351 if (ai
.caps
& (PCM_CAP_HIDDEN
|PCM_CAP_MODEM
))
353 if (!(ai
.caps
& PCM_CAP_OUTPUT
))
358 aout_HotplugReport (aout
, ai
.devnode
, ai
.name
);
366 static int DeviceSelect (audio_output_t
*aout
, const char *id
)
368 aout_sys_t
*sys
= aout
->sys
;
374 if (unlikely(path
== NULL
))
380 aout_DeviceReport (aout
, path
);
381 aout_RestartRequest (aout
, AOUT_RESTART_OUTPUT
);
385 static int Open (vlc_object_t
*obj
)
387 audio_output_t
*aout
= (audio_output_t
*)obj
;
389 aout_sys_t
*sys
= malloc (sizeof (*sys
));
390 if(unlikely( sys
== NULL
))
394 sys
->device
= var_InheritString (aout
, "oss-audio-device");
399 aout
->device_select
= DeviceSelect
;
400 aout_DeviceReport (aout
, sys
->device
);
401 aout_SoftVolumeInit (aout
);
407 static void Close (vlc_object_t
*obj
)
409 audio_output_t
*aout
= (audio_output_t
*)obj
;
410 aout_sys_t
*sys
= aout
->sys
;