]> git.sesse.net Git - vlc/blobdiff - modules/audio_output/oss.c
Reset input titles before adding new ones.
[vlc] / modules / audio_output / oss.c
index 55642c0b7540a0785f710631ea0029f1ef23df6f..97d7c730c98998701dc04bdc682016406ff35698 100644 (file)
@@ -1,26 +1,26 @@
 /*****************************************************************************
  * 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
@@ -29,6 +29,7 @@
 
 #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 *);
@@ -71,78 +76,59 @@ vlc_module_begin ()
     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;
@@ -156,27 +142,23 @@ static int Open (vlc_object_t *obj)
 
     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:
@@ -185,10 +167,11 @@ static int Open (vlc_object_t *obj)
     }
 
     /* 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;
     }
 
@@ -205,75 +188,56 @@ static int Open (vlc_object_t *obj)
     }
 
     /* 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;
 }
 
 /**
@@ -284,17 +248,6 @@ static void Play (audio_output_t *aout, block_t *block)
     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);
@@ -304,7 +257,7 @@ static void Play (audio_output_t *aout, block_t *block)
             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);
 
@@ -334,7 +287,7 @@ static void Flush (audio_output_t *aout, bool wait)
 
     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)
@@ -354,10 +307,25 @@ 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)
@@ -366,7 +334,7 @@ static int VolumeSet (audio_output_t *aout, float vol)
         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;
     }
 
@@ -379,11 +347,13 @@ static int MuteSet (audio_output_t *aout, bool mute)
 {
     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;
     }
 
@@ -391,3 +361,99 @@ static int MuteSet (audio_output_t *aout, bool mute)
     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);
+}