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.")
72 #define SPDIF_TEXT N_("Use S/PDIF when available")
73 #define SPDIF_LONGTEXT N_( \
74 "S/PDIF can be used by default when " \
75 "your hardware supports it as well as the audio stream being played.")
78 set_shortname( "OSS" )
79 set_description (N_("Open Sound System audio output"))
80 set_category( CAT_AUDIO
)
81 set_subcategory( SUBCAT_AUDIO_AOUT
)
82 add_string ("oss-audio-device", "",
83 AUDIO_DEV_TEXT
, AUDIO_DEV_LONGTEXT
, false)
84 add_bool("oss-spdif", false, SPDIF_TEXT
, SPDIF_LONGTEXT
, true)
86 set_capability( "audio output", 100 )
87 set_callbacks (Open
, Close
)
90 static int TimeGet (audio_output_t
*, vlc_tick_t
*);
91 static void Play(audio_output_t
*, block_t
*, vlc_tick_t
);
92 static void Pause (audio_output_t
*, bool, vlc_tick_t
);
93 static void Flush (audio_output_t
*, bool);
95 static int Start (audio_output_t
*aout
, audio_sample_format_t
*restrict fmt
)
97 aout_sys_t
* sys
= aout
->sys
;
99 if (aout_FormatNbChannels(fmt
) == 0)
102 /* Open the device */
103 const char *device
= sys
->device
;
105 device
= getenv ("OSS_AUDIODEV");
109 int fd
= vlc_open (device
, O_WRONLY
);
112 msg_Err (aout
, "cannot open OSS device %s: %s", device
,
113 vlc_strerror_c(errno
));
116 msg_Dbg (aout
, "using OSS device: %s", device
);
118 /* Select audio format */
122 switch (fmt
->i_format
)
131 format
= AFMT_S32_NE
;
134 format
= AFMT_S16_NE
;
140 if (AOUT_FMT_SPDIF(fmt
))
141 spdif
= var_InheritBool(aout
, "oss-spdif");
149 format
= AFMT_S16_NE
;
152 if (ioctl (fd
, SNDCTL_DSP_SETFMT
, &format
) < 0)
154 msg_Err (aout
, "cannot set audio format 0x%X: %s", format
,
155 vlc_strerror_c(errno
));
161 case AFMT_U8
: fmt
->i_format
= VLC_CODEC_U8
; break;
162 case AFMT_S16_NE
: fmt
->i_format
= VLC_CODEC_S16N
; break;
163 case AFMT_S32_NE
: fmt
->i_format
= VLC_CODEC_S32N
; break;
165 case AFMT_FLOAT
: fmt
->i_format
= VLC_CODEC_FL32
; break;
170 fmt
->i_format
= VLC_CODEC_SPDIFL
;
174 msg_Err (aout
, "unsupported audio format 0x%X", format
);
178 /* Select channels count */
179 int channels
= spdif
? 2 : aout_FormatNbChannels (fmt
);
180 if (ioctl (fd
, SNDCTL_DSP_CHANNELS
, &channels
) < 0)
182 msg_Err (aout
, "cannot set %d channels: %s", channels
,
183 vlc_strerror_c(errno
));
189 case 1: channels
= AOUT_CHAN_CENTER
; break;
190 case 2: channels
= AOUT_CHANS_STEREO
; break;
191 case 4: channels
= AOUT_CHANS_4_0
; break;
192 case 6: channels
= AOUT_CHANS_5_1
; break;
193 case 8: channels
= AOUT_CHANS_7_1
; break;
195 msg_Err (aout
, "unsupported channels count %d", channels
);
199 /* Select sample rate */
200 int rate
= spdif
? 48000 : fmt
->i_rate
;
201 if (ioctl (fd
, SNDCTL_DSP_SPEED
, &rate
) < 0)
203 msg_Err (aout
, "cannot set %d Hz sample rate: %s", rate
,
204 vlc_strerror_c(errno
));
208 /* Setup audio_output_t */
209 aout
->time_get
= TimeGet
;
216 fmt
->i_bytes_per_frame
= AOUT_SPDIF_SIZE
;
217 fmt
->i_frame_length
= A52_FRAME_NB
;
222 fmt
->i_physical_channels
= channels
;
224 fmt
->channel_type
= AUDIO_CHANNEL_TYPE_BITMAP
;
225 aout_FormatPrepare (fmt
);
230 bytes
= AOUT_SPDIF_SIZE
;
232 bytes
= fmt
->i_rate
/ (CLOCK_FREQ
/ AOUT_MIN_PREPARE_TIME
)
233 * fmt
->i_bytes_per_frame
;
234 if (unlikely(bytes
< 16))
237 int frag
= (AOUT_MAX_ADVANCE_TIME
/ AOUT_MIN_PREPARE_TIME
) << 16
238 | (32 - clz(bytes
- 1));
239 if (ioctl (fd
, SNDCTL_DSP_SETFRAGMENT
, &frag
) < 0)
240 msg_Err (aout
, "cannot set 0x%08x fragment: %s", frag
,
241 vlc_strerror_c(errno
));
244 aout_SoftVolumeStart (aout
);
245 sys
->starting
= true;
253 static int TimeGet (audio_output_t
*aout
, vlc_tick_t
*restrict pts
)
255 aout_sys_t
*sys
= aout
->sys
;
258 if (ioctl (sys
->fd
, SNDCTL_DSP_GETODELAY
, &delay
) < 0)
260 msg_Warn (aout
, "cannot get delay: %s", vlc_strerror_c(errno
));
264 *pts
= (delay
* CLOCK_FREQ
* sys
->format
.i_frame_length
)
265 / (sys
->format
.i_rate
* sys
->format
.i_bytes_per_frame
);
270 * Queues one audio buffer to the hardware.
272 static void Play(audio_output_t
*aout
, block_t
*block
, vlc_tick_t date
)
274 aout_sys_t
*sys
= aout
->sys
;
277 while (block
->i_buffer
> 0)
279 ssize_t bytes
= write (fd
, block
->p_buffer
, block
->i_buffer
);
282 block
->p_buffer
+= bytes
;
283 block
->i_buffer
-= bytes
;
286 msg_Err (aout
, "cannot write samples: %s", vlc_strerror_c(errno
));
288 block_Release (block
);
293 * Pauses/resumes the audio playback.
295 static void Pause (audio_output_t
*aout
, bool pause
, vlc_tick_t date
)
297 aout_sys_t
*sys
= aout
->sys
;
301 ioctl (fd
, pause
? SNDCTL_DSP_SILENCE
: SNDCTL_DSP_SKIP
, NULL
);
305 * Flushes/drains the audio playback buffer.
307 static void Flush (audio_output_t
*aout
, bool wait
)
309 aout_sys_t
*sys
= aout
->sys
;
313 return; /* drain is implicit with OSS */
314 ioctl (fd
, SNDCTL_DSP_HALT
, NULL
);
318 * Releases the audio output device.
320 static void Stop (audio_output_t
*aout
)
322 aout_sys_t
*sys
= aout
->sys
;
325 ioctl (fd
, SNDCTL_DSP_HALT
, NULL
);
330 static int DevicesEnum (audio_output_t
*aout
)
332 int fd
= vlc_open ("/dev/dsp", O_WRONLY
);
339 if (ioctl (fd
, SNDCTL_SYSINFO
, &si
) < 0)
341 msg_Err (aout
, "cannot get system infos: %s", vlc_strerror(errno
));
345 msg_Dbg (aout
, "using %s version %s (0x%06X) under %s", si
.product
,
346 si
.version
, si
.versionnum
, si
.license
);
348 for (int i
= 0; i
< si
.numaudios
; i
++)
350 oss_audioinfo ai
= { .dev
= i
};
352 if (ioctl (fd
, SNDCTL_AUDIOINFO
, &ai
) < 0)
354 msg_Warn (aout
, "cannot get device %d infos: %s", i
,
355 vlc_strerror_c(errno
));
358 if (ai
.caps
& (PCM_CAP_HIDDEN
|PCM_CAP_MODEM
))
360 if (!(ai
.caps
& PCM_CAP_OUTPUT
))
365 aout_HotplugReport (aout
, ai
.devnode
, ai
.name
);
373 static int DeviceSelect (audio_output_t
*aout
, const char *id
)
375 aout_sys_t
*sys
= aout
->sys
;
381 if (unlikely(path
== NULL
))
387 aout_DeviceReport (aout
, path
);
388 aout_RestartRequest (aout
, AOUT_RESTART_OUTPUT
);
392 static int Open (vlc_object_t
*obj
)
394 audio_output_t
*aout
= (audio_output_t
*)obj
;
396 aout_sys_t
*sys
= malloc (sizeof (*sys
));
397 if(unlikely( sys
== NULL
))
401 sys
->device
= var_InheritString (aout
, "oss-audio-device");
406 aout
->device_select
= DeviceSelect
;
407 aout_DeviceReport (aout
, sys
->device
);
408 aout_SoftVolumeInit (aout
);
414 static void Close (vlc_object_t
*obj
)
416 audio_output_t
*aout
= (audio_output_t
*)obj
;
417 aout_sys_t
*sys
= aout
->sys
;