/*****************************************************************************
* oss.c: Open Sound System audio output plugin for VLC
*****************************************************************************
- * Copyright (C) 2000-2002 the VideoLAN team
+ * Copyright (C) 2000-2002 VLC authors and VideoLAN
* Copyright (C) 2007-2012 RĂ©mi Denis-Courmont
*
* Authors: Michel Kaempf <maxx@via.ecp.fr>
* Sam Hocevar <sam@zoy.org>
* Christophe Massiot <massiot@via.ecp.fr>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
#include <stdlib.h>
#include <math.h>
+#include <errno.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/ioctl.h>
struct aout_sys_t
{
int fd;
- uint8_t level;
+ audio_sample_format_t format;
+ bool starting;
+
bool mute;
+ uint8_t level;
+ char *device;
};
static int Open (vlc_object_t *);
add_string ("oss-audio-device", "",
AUDIO_DEV_TEXT, AUDIO_DEV_LONGTEXT, false)
set_capability( "audio output", 100 )
- set_callbacks( Open, Close )
+ set_callbacks (Open, Close)
vlc_module_end ()
+static int TimeGet (audio_output_t *, mtime_t *);
static void Play (audio_output_t *, block_t *);
static void Pause (audio_output_t *, bool, mtime_t);
static void Flush (audio_output_t *, bool);
static int VolumeSync (audio_output_t *);
-static int VolumeSet (audio_output_t *, float);
-static int MuteSet (audio_output_t *, bool);
-static int Open (vlc_object_t *obj)
+static int Start (audio_output_t *aout, audio_sample_format_t *restrict fmt)
{
- audio_output_t *aout = (audio_output_t *)obj;
+ aout_sys_t* sys = aout->sys;
/* Open the device */
- const char *device;
- char *devicebuf = var_InheritString (aout, "oss-audio-device");
- device = devicebuf;
+ const char *device = sys->device;
if (device == NULL)
device = getenv ("OSS_AUDIODEV");
if (device == NULL)
device = "/dev/dsp";
- msg_Dbg (aout, "using OSS device: %s", device);
-
int fd = vlc_open (device, O_WRONLY);
- free (devicebuf);
if (fd == -1)
{
- msg_Err (aout, "cannot open OSS device: %m");
+ msg_Err (aout, "cannot open OSS device %s: %s", device,
+ vlc_strerror_c(errno));
return VLC_EGENERIC;
}
-
- aout_sys_t *sys = malloc (sizeof (*sys));
- if (unlikely(sys == NULL))
- goto error;
- aout->sys = sys;
sys->fd = fd;
+ msg_Dbg (aout, "using OSS device: %s", device);
/* Select audio format */
int format;
- vlc_fourcc_t fourcc = aout->format.i_format;
bool spdif = false;
- switch (fourcc)
+ switch (fmt->i_format)
{
#ifdef AFMT_FLOAT
- case VLC_CODEC_F64B:
- case VLC_CODEC_F64L:
- case VLC_CODEC_F32B:
- case VLC_CODEC_F32L:
+ case VLC_CODEC_FL64:
+ case VLC_CODEC_FL32:
format = AFMT_FLOAT;
break;
#endif
- case VLC_CODEC_S32B:
- format = AFMT_S32_BE;
- break;
- case VLC_CODEC_S32L:
- format = AFMT_S32_LE;
+ case VLC_CODEC_S32N:
+ format = AFMT_S32_NE;
break;
- case VLC_CODEC_S16B:
- format = AFMT_S16_BE;
+ case VLC_CODEC_S16N:
+ format = AFMT_S16_NE;
break;
- case VLC_CODEC_S16L:
- format = AFMT_S16_LE;
- break;
- case VLC_CODEC_S8:
case VLC_CODEC_U8:
format = AFMT_U8;
break;
default:
- if (AOUT_FMT_SPDIF(&aout->format))
+ if (AOUT_FMT_SPDIF(fmt))
spdif = var_InheritBool (aout, "spdif");
if (spdif)
format = AFMT_AC3;
if (ioctl (fd, SNDCTL_DSP_SETFMT, &format) < 0)
{
- msg_Err (aout, "cannot set audio format 0x%X: %m", format);
+ msg_Err (aout, "cannot set audio format 0x%X: %s", format,
+ vlc_strerror_c(errno));
goto error;
}
switch (format)
{
- case AFMT_S8: fourcc = VLC_CODEC_S8; break;
- case AFMT_U8: fourcc = VLC_CODEC_U8; break;
- case AFMT_S16_BE: fourcc = VLC_CODEC_S16B; break;
- case AFMT_S16_LE: fourcc = VLC_CODEC_S16L; break;
- //case AFMT_S24_BE:
- //case AFMT_S24_LE:
- case AFMT_S32_BE: fourcc = VLC_CODEC_S32B; break;
- case AFMT_S32_LE: fourcc = VLC_CODEC_S32L; break;
+ case AFMT_U8: fmt->i_format = VLC_CODEC_U8; break;
+ case AFMT_S16_NE: fmt->i_format = VLC_CODEC_S16N; break;
+ case AFMT_S32_NE: fmt->i_format = VLC_CODEC_S32N; break;
#ifdef AFMT_FLOAT
- case AFMT_FLOAT: fourcc = VLC_CODEC_FL32; break;
+ case AFMT_FLOAT: fmt->i_format = VLC_CODEC_FL32; break;
#endif
case AFMT_AC3:
if (spdif)
{
- fourcc = VLC_CODEC_SPDIFL;
+ fmt->i_format = VLC_CODEC_SPDIFL;
break;
}
default:
}
/* Select channels count */
- int channels = spdif ? 2 : aout_FormatNbChannels (&aout->format);
+ int channels = spdif ? 2 : aout_FormatNbChannels (fmt);
if (ioctl (fd, SNDCTL_DSP_CHANNELS, &channels) < 0)
{
- msg_Err (aout, "cannot set %d channels: %m", channels);
+ msg_Err (aout, "cannot set %d channels: %s", channels,
+ vlc_strerror_c(errno));
goto error;
}
}
/* Select sample rate */
- int rate = spdif ? 48000 : aout->format.i_rate;
+ int rate = spdif ? 48000 : fmt->i_rate;
if (ioctl (fd, SNDCTL_DSP_SPEED, &rate) < 0)
{
- msg_Err (aout, "cannot set %d Hz sample rate: %m", rate);
+ msg_Err (aout, "cannot set %d Hz sample rate: %s", rate,
+ vlc_strerror_c(errno));
goto error;
}
/* Setup audio_output_t */
- aout->format.i_format = fourcc;
- aout->pf_play = Play;
- aout->pf_pause = Pause;
- aout->pf_flush = Flush;
- aout->volume_set = NULL;
- aout->mute_set = NULL;
+ aout->time_get = TimeGet;
+ aout->play = Play;
+ aout->pause = Pause;
+ aout->flush = Flush;
if (spdif)
{
- aout->format.i_bytes_per_frame = AOUT_SPDIF_SIZE;
- aout->format.i_frame_length = A52_FRAME_NB;
+ fmt->i_bytes_per_frame = AOUT_SPDIF_SIZE;
+ fmt->i_frame_length = A52_FRAME_NB;
}
else
{
- aout->format.i_rate = rate;
- aout->format.i_original_channels =
- aout->format.i_physical_channels = channels;
-
- sys->level = 100;
- sys->mute = false;
- if (VolumeSync (aout) == 0)
- {
- aout->volume_set = VolumeSet;
- aout->mute_set = MuteSet;
- }
+ fmt->i_rate = rate;
+ fmt->i_original_channels =
+ fmt->i_physical_channels = channels;
}
- return 0;
+ aout_FormatPrepare (fmt);
+ VolumeSync (aout);
+ sys->starting = true;
+ sys->format = *fmt;
+ return VLC_SUCCESS;
error:
close (fd);
- free (sys);
return VLC_EGENERIC;
}
-/**
- * Releases the audio output.
- */
-static void Close (vlc_object_t *obj)
+static int TimeGet (audio_output_t *aout, mtime_t *restrict pts)
{
- audio_output_t *aout = (audio_output_t *)obj;
aout_sys_t *sys = aout->sys;
- int fd = sys->fd;
+ int delay;
-#if 0
- /* FIXME: ugly hack so selected OSS device survives restart */
- char *device = var_InheritString (obj, "audio-device");
- if (device != NULL)
+ if (ioctl (sys->fd, SNDCTL_DSP_GETODELAY, &delay) < 0)
{
- if (!var_Type (obj, "oss-audio-device"))
- var_Create (obj, "oss-audio-device", VLC_VAR_STRING);
- var_SetString (obj, "oss-audio-device", device);
- free (device);
+ msg_Warn (aout, "cannot get delay: %s", vlc_strerror_c(errno));
+ return -1;
}
- var_DelCallback (obj, "audio-device", aout_ChannelsRestart, NULL);
- var_Destroy (obj, "audio-device");
-#endif
-
- ioctl (fd, SNDCTL_DSP_HALT, NULL);
- close (fd);
- free (sys);
+ *pts = (delay * CLOCK_FREQ * sys->format.i_frame_length)
+ / (sys->format.i_rate * sys->format.i_bytes_per_frame);
+ return 0;
}
/**
aout_sys_t *sys = aout->sys;
int fd = sys->fd;
- int delay;
- if (ioctl (sys->fd, SNDCTL_DSP_GETODELAY, &delay) >= 0)
- {
- mtime_t latency = (delay * CLOCK_FREQ * aout->format.i_frame_length)
- / (aout->format.i_rate * aout->format.i_bytes_per_frame);
- /* TODO: insert zeroes when starting playback */
- aout_TimeReport (aout, block->i_pts - latency);
- }
- else
- msg_Warn (aout, "cannot get delay: %m");
-
while (block->i_buffer > 0)
{
ssize_t bytes = write (fd, block->p_buffer, block->i_buffer);
block->i_buffer -= bytes;
}
else
- msg_Err (aout, "cannot write samples: %m");
+ msg_Err (aout, "cannot write samples: %s", vlc_strerror_c(errno));
}
block_Release (block);
if (wait)
return; /* drain is implicit with OSS */
- ioctl (fd, SNDCTL_DSP_HALT_OUTPUT, NULL);
+ ioctl (fd, SNDCTL_DSP_HALT, NULL);
}
static int VolumeSync (audio_output_t *aout)
return 0;
}
+/**
+ * Releases the audio output device.
+ */
+static void Stop (audio_output_t *aout)
+{
+ aout_sys_t *sys = aout->sys;
+ int fd = sys->fd;
+
+ ioctl (fd, SNDCTL_DSP_HALT, NULL);
+ close (fd);
+ sys->fd = -1;
+}
+
static int VolumeSet (audio_output_t *aout, float vol)
{
aout_sys_t *sys = aout->sys;
int fd = sys->fd;
+ if (fd == -1)
+ return -1;
int level = lroundf (vol * 100.f);
if (level > 0xFF)
level |= level << 8;
if (!sys->mute && ioctl (fd, SNDCTL_DSP_SETPLAYVOL, &level) < 0)
{
- msg_Err (aout, "cannot set volume: %m");
+ msg_Err (aout, "cannot set volume: %s", vlc_strerror_c(errno));
return -1;
}
{
aout_sys_t *sys = aout->sys;
int fd = sys->fd;
+ if (fd == -1)
+ return -1;
int level = mute ? 0 : (sys->level | (sys->level << 8));
if (ioctl (fd, SNDCTL_DSP_SETPLAYVOL, &level) < 0)
{
- msg_Err (aout, "cannot mute: %m");
+ msg_Err (aout, "cannot mute: %s", vlc_strerror_c(errno));
return -1;
}
aout_MuteReport (aout, mute);
return 0;
}
+
+static int DevicesEnum (audio_output_t *aout)
+{
+ int fd = vlc_open ("/dev/dsp", O_WRONLY);
+ if (fd == -1)
+ return -1;
+
+ oss_sysinfo si;
+ int n = -1;
+
+ if (ioctl (fd, SNDCTL_SYSINFO, &si) < 0)
+ {
+ msg_Err (aout, "cannot get system infos: %s", vlc_strerror(errno));
+ goto out;
+ }
+
+ msg_Dbg (aout, "using %s version %s (0x%06X) under %s", si.product,
+ si.version, si.versionnum, si.license);
+
+ for (int i = 0; i < si.numaudios; i++)
+ {
+ oss_audioinfo ai = { .dev = i };
+
+ if (ioctl (fd, SNDCTL_AUDIOINFO, &ai) < 0)
+ {
+ msg_Warn (aout, "cannot get device %d infos: %s", i,
+ vlc_strerror_c(errno));
+ continue;
+ }
+ if (ai.caps & (PCM_CAP_HIDDEN|PCM_CAP_MODEM))
+ continue;
+ if (!(ai.caps & PCM_CAP_OUTPUT))
+ continue;
+ if (!ai.enabled)
+ continue;
+
+ aout_HotplugReport (aout, ai.devnode, ai.name);
+ n++;
+ }
+out:
+ close (fd);
+ return n;
+}
+
+static int DeviceSelect (audio_output_t *aout, const char *id)
+{
+ aout_sys_t *sys = aout->sys;
+ char *path = NULL;
+
+ if (id != NULL)
+ {
+ path = strdup (id);
+ if (unlikely(path == NULL))
+ return -1;
+ }
+
+ free (sys->device);
+ sys->device = path;
+ aout_DeviceReport (aout, path);
+ aout_RestartRequest (aout, AOUT_RESTART_OUTPUT);
+ return 0;
+}
+
+static int Open (vlc_object_t *obj)
+{
+ audio_output_t *aout = (audio_output_t *)obj;
+
+ aout_sys_t *sys = malloc (sizeof (*sys));
+ if(unlikely( sys == NULL ))
+ return VLC_ENOMEM;
+
+ sys->fd = -1;
+
+ sys->level = 100;
+ sys->mute = false;
+ sys->device = var_InheritString (aout, "oss-audio-device");
+
+ aout->sys = sys;
+ aout->start = Start;
+ aout->stop = Stop;
+ aout->volume_set = VolumeSet;
+ aout->mute_set = MuteSet;
+ aout->device_select = DeviceSelect;
+
+ DevicesEnum (aout);
+ return VLC_SUCCESS;
+}
+
+static void Close (vlc_object_t *obj)
+{
+ audio_output_t *aout = (audio_output_t *)obj;
+ aout_sys_t *sys = aout->sys;
+
+ free (sys->device);
+ free (sys);
+}