]> git.sesse.net Git - vlc/blobdiff - modules/audio_output/oss.c
OSS: build fix
[vlc] / modules / audio_output / oss.c
index fcd3ca0ea208abc5ce2a38db892c544fb188b988..d201df56516a32037f27351f8893d2211a569458 100644 (file)
 # include "config.h"
 #endif
 
-#include <errno.h>                                                 /* ENOMEM */
 #include <fcntl.h>                                       /* open(), O_WRONLY */
 #include <sys/ioctl.h>                                            /* ioctl() */
 #include <unistd.h>                                      /* write(), close() */
 
 #include <vlc_common.h>
 #include <vlc_plugin.h>
-
-#ifdef HAVE_ALLOCA_H
-#   include <alloca.h>
-#endif
+#include <vlc_fs.h>
 
 #include <vlc_aout.h>
 
@@ -51,8 +47,6 @@
 #   include <soundcard.h>
 #elif defined( HAVE_SYS_SOUNDCARD_H )
 #   include <sys/soundcard.h>
-#elif defined( HAVE_MACHINE_SOUNDCARD_H )
-#   include <machine/soundcard.h>
 #endif
 
 /* Patches for ignorant OSS versions */
  *****************************************************************************/
 struct aout_sys_t
 {
+    aout_packet_t packet;
     int i_fd;
-    int b_workaround_buggy_driver;
     int i_fragstotal;
     mtime_t max_buffer_duration;
+    vlc_thread_t thread;
 };
 
 /* This must be a power of 2. */
@@ -92,41 +87,34 @@ struct aout_sys_t
 static int  Open         ( vlc_object_t * );
 static void Close        ( vlc_object_t * );
 
-static void Play         ( aout_instance_t * );
-static int  OSSThread    ( aout_instance_t * );
+static void Play         ( audio_output_t *, block_t * );
+static void* OSSThread   ( void * );
 
-static mtime_t BufferDuration( aout_instance_t * p_aout );
+static mtime_t BufferDuration( audio_output_t * p_aout );
 
 /*****************************************************************************
  * Module descriptor
  *****************************************************************************/
-#define BUGGY_TEXT N_("Try to work around buggy OSS drivers")
-#define BUGGY_LONGTEXT N_( \
-    "Some buggy OSS drivers just don't like when their internal buffers " \
-    "are completely filled (the sound gets heavily hashed). If you have one " \
-    "of these drivers, then you need to enable this option." )
-
-vlc_module_begin();
-    set_shortname( "OSS" );
-    set_description( N_("UNIX OSS audio output") );
-
-    set_category( CAT_AUDIO );
-    set_subcategory( SUBCAT_AUDIO_AOUT );
-    add_file( "dspdev", "/dev/dsp", aout_FindAndRestart,
-              N_("OSS DSP device"), NULL, false );
-    add_bool( "oss-buggy", 0, NULL, BUGGY_TEXT, BUGGY_LONGTEXT, true );
-
-    set_capability( "audio output", 100 );
-    add_shortcut( "oss" );
-    set_callbacks( Open, Close );
-vlc_module_end();
+vlc_module_begin ()
+    set_shortname( "OSS" )
+    set_description( N_("Open Sound System") )
+
+    set_category( CAT_AUDIO )
+    set_subcategory( SUBCAT_AUDIO_AOUT )
+    add_loadfile( "oss-audio-device", "/dev/dsp",
+                  N_("OSS DSP device"), NULL, false )
+
+    set_capability( "audio output", 100 )
+    add_shortcut( "oss" )
+    set_callbacks( Open, Close )
+vlc_module_end ()
 
 /*****************************************************************************
  * Probe: probe the audio device for available formats and channels
  *****************************************************************************/
-static void Probe( aout_instance_t * p_aout )
+static void Probe( audio_output_t * p_aout )
 {
-    struct aout_sys_t * p_sys = p_aout->output.p_sys;
+    struct aout_sys_t * p_sys = p_aout->sys;
     vlc_value_t val, text;
     int i_format, i_nb_channels;
 
@@ -136,7 +124,7 @@ static void Probe( aout_instance_t * p_aout )
 
     /* Test for multi-channel. */
 #ifdef SNDCTL_DSP_GETCHANNELMASK
-    if ( aout_FormatNbChannels( &p_aout->output.output ) > 2 )
+    if ( aout_FormatNbChannels( &p_aout->format ) > 2 )
     {
         /* Check that the device supports this. */
 
@@ -163,24 +151,24 @@ static void Probe( aout_instance_t * p_aout )
             }
 
             if ( (i_chanmask & (DSP_BIND_SURR | DSP_BIND_CENTER_LFE))
-                  && (p_aout->output.output.i_physical_channels ==
+                  && (p_aout->format.i_physical_channels ==
                        (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
                          | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
                          | AOUT_CHAN_LFE)) )
             {
                 val.i_int = AOUT_VAR_5_1;
-                text.psz_string = "5.1";
+                text.psz_string = (char*) "5.1";
                 var_Change( p_aout, "audio-device",
                             VLC_VAR_ADDCHOICE, &val, &text );
             }
 
             if ( (i_chanmask & DSP_BIND_SURR)
-                  && (p_aout->output.output.i_physical_channels &
+                  && (p_aout->format.i_physical_channels &
                        (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
                          | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT)) )
             {
                 val.i_int = AOUT_VAR_2F2R;
-                text.psz_string = N_("2 Front 2 Rear");
+                text.psz_string = _("2 Front 2 Rear");
                 var_Change( p_aout, "audio-device",
                             VLC_VAR_ADDCHOICE, &val, &text );
             }
@@ -204,7 +192,7 @@ static void Probe( aout_instance_t * p_aout )
          && i_nb_channels == 2 )
     {
         val.i_int = AOUT_VAR_STEREO;
-        text.psz_string = N_("Stereo");
+        text.psz_string = _("Stereo");
         var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
     }
 
@@ -224,9 +212,9 @@ static void Probe( aout_instance_t * p_aout )
          && i_nb_channels == 1 )
     {
         val.i_int = AOUT_VAR_MONO;
-        text.psz_string = N_("Mono");
+        text.psz_string = _("Mono");
         var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
-        if ( p_aout->output.output.i_physical_channels == AOUT_CHAN_CENTER )
+        if ( p_aout->format.i_physical_channels == AOUT_CHAN_CENTER )
         {
             var_Set( p_aout, "audio-device", val );
         }
@@ -240,7 +228,7 @@ static void Probe( aout_instance_t * p_aout )
     }
 
     /* Test for spdif. */
-    if ( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
+    if ( AOUT_FMT_SPDIF( &p_aout->format ) )
     {
         i_format = AFMT_AC3;
 
@@ -248,20 +236,17 @@ static void Probe( aout_instance_t * p_aout )
              && i_format == AFMT_AC3 )
         {
             val.i_int = AOUT_VAR_SPDIF;
-            text.psz_string = N_("A/52 over S/PDIF");
+            text.psz_string = _("A/52 over S/PDIF");
             var_Change( p_aout, "audio-device",
                         VLC_VAR_ADDCHOICE, &val, &text );
-            if( config_GetInt( p_aout, "spdif" ) )
+            if( var_InheritBool( p_aout, "spdif" ) )
                 var_Set( p_aout, "audio-device", val );
         }
-        else if( config_GetInt( p_aout, "spdif" ) )
+        else if( var_InheritBool( p_aout, "spdif" ) )
         {
             msg_Warn( p_aout, "S/PDIF not supported by card" );
         }
     }
-
-    var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart,
-                     NULL );
 }
 
 /*****************************************************************************
@@ -272,18 +257,18 @@ static void Probe( aout_instance_t * p_aout )
  *****************************************************************************/
 static int Open( vlc_object_t *p_this )
 {
-    aout_instance_t * p_aout = (aout_instance_t *)p_this;
+    audio_output_t * p_aout = (audio_output_t *)p_this;
     struct aout_sys_t * p_sys;
     char * psz_device;
     vlc_value_t val;
 
     /* Allocate structure */
-    p_aout->output.p_sys = p_sys = malloc( sizeof( aout_sys_t ) );
+    p_aout->sys = p_sys = malloc( sizeof( aout_sys_t ) );
     if( p_sys == NULL )
         return VLC_ENOMEM;
 
     /* Get device name */
-    if( (psz_device = config_GetPsz( p_aout, "dspdev" )) == NULL )
+    if( (psz_device = var_InheritString( p_aout, "oss-audio-device" )) == NULL )
     {
         msg_Err( p_aout, "no audio device specified (maybe /dev/dsp?)" );
         free( p_sys );
@@ -295,10 +280,11 @@ static int Open( vlc_object_t *p_this )
      * wait forever until the device is available. Since this breaks the
      * OSS spec, we immediately put it back to blocking mode if the
      * operation was successful. */
-    p_sys->i_fd = open( psz_device, O_WRONLY | O_NDELAY );
+    p_sys->i_fd = vlc_open( psz_device, O_WRONLY | O_NDELAY );
     if( p_sys->i_fd < 0 )
     {
         msg_Err( p_aout, "cannot open audio device (%s)", psz_device );
+        free( psz_device );
         free( p_sys );
         return VLC_EGENERIC;
     }
@@ -309,72 +295,67 @@ static int Open( vlc_object_t *p_this )
 
     free( psz_device );
 
-    p_aout->output.pf_play = Play;
+    p_aout->pf_play = aout_PacketPlay;
+    p_aout->pf_pause = aout_PacketPause;
+    p_aout->pf_flush = aout_PacketFlush;
 
     if ( var_Type( p_aout, "audio-device" ) == 0 )
-    {
         Probe( p_aout );
-    }
+    var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
 
     if ( var_Get( p_aout, "audio-device", &val ) < 0 )
-    {
         /* Probe() has failed. */
-        free( p_sys );
-        return VLC_EGENERIC;
-    }
+        goto error;
 
     if ( val.i_int == AOUT_VAR_SPDIF )
     {
-        p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
+        p_aout->format.i_format = VLC_CODEC_SPDIFL;
     }
     else if ( val.i_int == AOUT_VAR_5_1 )
     {
-        p_aout->output.output.i_format = AOUT_FMT_S16_NE;
-        p_aout->output.output.i_physical_channels
+        p_aout->format.i_format = VLC_CODEC_S16N;
+        p_aout->format.i_physical_channels
             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
                | AOUT_CHAN_LFE;
     }
     else if ( val.i_int == AOUT_VAR_2F2R )
     {
-        p_aout->output.output.i_format = AOUT_FMT_S16_NE;
-        p_aout->output.output.i_physical_channels
+        p_aout->format.i_format = VLC_CODEC_S16N;
+        p_aout->format.i_physical_channels
             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
     }
     else if ( val.i_int == AOUT_VAR_STEREO )
     {
-        p_aout->output.output.i_format = AOUT_FMT_S16_NE;
-        p_aout->output.output.i_physical_channels
+        p_aout->format.i_format = VLC_CODEC_S16N;
+        p_aout->format.i_physical_channels
             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
     }
     else if ( val.i_int == AOUT_VAR_MONO )
     {
-        p_aout->output.output.i_format = AOUT_FMT_S16_NE;
-        p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
+        p_aout->format.i_format = VLC_CODEC_S16N;
+        p_aout->format.i_physical_channels = AOUT_CHAN_CENTER;
     }
     else
     {
         /* This should not happen ! */
-        msg_Err( p_aout, "internal: can't find audio-device (%i)", val.i_int );
-        free( p_sys );
-        return VLC_EGENERIC;
+        msg_Err( p_aout, "internal: can't find audio-device (%"PRId64")",
+                 val.i_int );
+        goto error;
     }
 
-    val.b_bool = true;
-    var_Set( p_aout, "intf-change", val );
+    var_TriggerCallback( p_aout, "intf-change" );
 
     /* Reset the DSP device */
     if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 )
     {
         msg_Err( p_aout, "cannot reset OSS audio device" );
-        close( p_sys->i_fd );
-        free( p_sys );
-        return VLC_EGENERIC;
+        goto error;
     }
 
     /* Set the output format */
-    if ( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
+    if ( AOUT_FMT_SPDIF( &p_aout->format ) )
     {
         int i_format = AFMT_AC3;
 
@@ -382,20 +363,17 @@ static int Open( vlc_object_t *p_this )
              || i_format != AFMT_AC3 )
         {
             msg_Err( p_aout, "cannot reset OSS audio device" );
-            close( p_sys->i_fd );
-            free( p_sys );
-            return VLC_EGENERIC;
+            goto error;
         }
 
-        p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
-        p_aout->output.i_nb_samples = A52_FRAME_NB;
-        p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
-        p_aout->output.output.i_frame_length = A52_FRAME_NB;
+        p_aout->format.i_format = VLC_CODEC_SPDIFL;
+        p_aout->format.i_bytes_per_frame = AOUT_SPDIF_SIZE;
+        p_aout->format.i_frame_length = A52_FRAME_NB;
 
+        aout_PacketInit( p_aout, &p_sys->packet, A52_FRAME_NB );
         aout_VolumeNoneInit( p_aout );
     }
-
-    if ( !AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
+    else
     {
         unsigned int i_format = AFMT_S16_NE;
         unsigned int i_frame_size, i_fragments;
@@ -406,74 +384,66 @@ static int Open( vlc_object_t *p_this )
         if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 )
         {
             msg_Err( p_aout, "cannot set audio output format" );
-            close( p_sys->i_fd );
-            free( p_sys );
-            return VLC_EGENERIC;
+            goto error;
         }
 
         switch ( i_format )
         {
         case AFMT_U8:
-            p_aout->output.output.i_format = VLC_FOURCC('u','8',' ',' ');
+            p_aout->format.i_format = VLC_CODEC_U8;
             break;
         case AFMT_S8:
-            p_aout->output.output.i_format = VLC_FOURCC('s','8',' ',' ');
+            p_aout->format.i_format = VLC_CODEC_S8;
             break;
         case AFMT_U16_LE:
-            p_aout->output.output.i_format = VLC_FOURCC('u','1','6','l');
+            p_aout->format.i_format = VLC_CODEC_U16L;
             break;
         case AFMT_S16_LE:
-            p_aout->output.output.i_format = VLC_FOURCC('s','1','6','l');
+            p_aout->format.i_format = VLC_CODEC_S16L;
             break;
         case AFMT_U16_BE:
-            p_aout->output.output.i_format = VLC_FOURCC('u','1','6','b');
+            p_aout->format.i_format = VLC_CODEC_U16B;
             break;
         case AFMT_S16_BE:
-            p_aout->output.output.i_format = VLC_FOURCC('s','1','6','b');
+            p_aout->format.i_format = VLC_CODEC_S16B;
             break;
         default:
             msg_Err( p_aout, "OSS fell back to an unknown format (%d)",
                      i_format );
-            close( p_sys->i_fd );
-            free( p_sys );
-            return VLC_EGENERIC;
+            goto error;
         }
 
-        i_nb_channels = aout_FormatNbChannels( &p_aout->output.output );
+        i_nb_channels = aout_FormatNbChannels( &p_aout->format );
 
         /* Set the number of channels */
         if( ioctl( p_sys->i_fd, SNDCTL_DSP_CHANNELS, &i_nb_channels ) < 0 ||
-            i_nb_channels != aout_FormatNbChannels( &p_aout->output.output ) )
+            i_nb_channels != aout_FormatNbChannels( &p_aout->format ) )
         {
             msg_Err( p_aout, "cannot set number of audio channels (%s)",
-                     aout_FormatPrintChannels( &p_aout->output.output) );
-            close( p_sys->i_fd );
-            free( p_sys );
-            return VLC_EGENERIC;
+                     aout_FormatPrintChannels( &p_aout->format) );
+            goto error;
         }
 
         /* Set the output rate */
-        i_rate = p_aout->output.output.i_rate;
+        i_rate = p_aout->format.i_rate;
         if( ioctl( p_sys->i_fd, SNDCTL_DSP_SPEED, &i_rate ) < 0 )
         {
             msg_Err( p_aout, "cannot set audio output rate (%i)",
-                             p_aout->output.output.i_rate );
-            close( p_sys->i_fd );
-            free( p_sys );
-            return VLC_EGENERIC;
+                             p_aout->format.i_rate );
+            goto error;
         }
 
-        if( i_rate != p_aout->output.output.i_rate )
+        if( i_rate != p_aout->format.i_rate )
         {
-            p_aout->output.output.i_rate = i_rate;
+            p_aout->format.i_rate = i_rate;
         }
 
         /* Set the fragment size */
-        aout_FormatPrepare( &p_aout->output.output );
+        aout_FormatPrepare( &p_aout->format );
 
         /* i_fragment = xxxxyyyy where: xxxx        is fragtotal
          *                              1 << yyyy   is fragsize */
-        i_frame_size = ((uint64_t)p_aout->output.output.i_bytes_per_frame * p_aout->output.output.i_rate * 65536) / (48000 * 2 * 2) / FRAME_COUNT;
+        i_frame_size = ((uint64_t)p_aout->format.i_bytes_per_frame * p_aout->format.i_rate * 65536) / (48000 * 2 * 2) / FRAME_COUNT;
         i_fragments = 4;
         while( i_fragments < 12 && (1U << i_fragments) < i_frame_size )
         {
@@ -488,51 +458,40 @@ static int Open( vlc_object_t *p_this )
         if( ioctl( p_sys->i_fd, SNDCTL_DSP_GETOSPACE, &audio_buf ) < 0 )
         {
             msg_Err( p_aout, "cannot get fragment size" );
-            close( p_sys->i_fd );
-            free( p_sys );
-            return VLC_EGENERIC;
+            goto error;
         }
-        else
-        {
-            /* Number of fragments actually allocated */
-            p_aout->output.p_sys->i_fragstotal = audio_buf.fragstotal;
 
-            /* Maximum duration the soundcard's buffer can hold */
-            p_aout->output.p_sys->max_buffer_duration =
-                (mtime_t)audio_buf.fragstotal * audio_buf.fragsize * 1000000
-                / p_aout->output.output.i_bytes_per_frame
-                / p_aout->output.output.i_rate
-                * p_aout->output.output.i_frame_length;
+        /* Number of fragments actually allocated */
+        p_aout->sys->i_fragstotal = audio_buf.fragstotal;
 
-            p_aout->output.i_nb_samples = audio_buf.fragsize /
-                p_aout->output.output.i_bytes_per_frame;
-        }
+        /* Maximum duration the soundcard's buffer can hold */
+        p_aout->sys->max_buffer_duration =
+                (mtime_t)audio_buf.fragstotal * audio_buf.fragsize * 1000000
+                / p_aout->format.i_bytes_per_frame
+                / p_aout->format.i_rate
+                * p_aout->format.i_frame_length;
 
+        aout_PacketInit( p_aout, &p_sys->packet,
+                         audio_buf.fragsize/p_aout->format.i_bytes_per_frame );
         aout_VolumeSoftInit( p_aout );
     }
 
-    p_aout->output.p_sys->b_workaround_buggy_driver =
-        config_GetInt( p_aout, "oss-buggy" );
-
     /* Create OSS thread and wait for its readiness. */
-    if( vlc_thread_create( p_aout, "aout", OSSThread,
-                           VLC_THREAD_PRIORITY_OUTPUT, false ) )
+    if( vlc_clone( &p_sys->thread, OSSThread, p_aout,
+                   VLC_THREAD_PRIORITY_OUTPUT ) )
     {
         msg_Err( p_aout, "cannot create OSS thread (%m)" );
-        close( p_sys->i_fd );
-        free( p_sys );
-        return VLC_ETHREAD;
+        aout_PacketDestroy( p_aout );
+        goto error;
     }
 
     return VLC_SUCCESS;
-}
 
-/*****************************************************************************
- * Play: nothing to do
- *****************************************************************************/
-static void Play( aout_instance_t *p_aout )
-{
-    VLC_UNUSED(p_aout);
+error:
+    var_DelCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
+    close( p_sys->i_fd );
+    free( p_sys );
+    return VLC_EGENERIC;
 }
 
 /*****************************************************************************
@@ -540,16 +499,18 @@ static void Play( aout_instance_t *p_aout )
  *****************************************************************************/
 static void Close( vlc_object_t * p_this )
 {
-    aout_instance_t *p_aout = (aout_instance_t *)p_this;
-    struct aout_sys_t * p_sys = p_aout->output.p_sys;
+    audio_output_t *p_aout = (audio_output_t *)p_this;
+    struct aout_sys_t * p_sys = p_aout->sys;
 
-    vlc_object_kill( p_aout );
-    vlc_thread_join( p_aout );
+    vlc_cancel( p_sys->thread );
+    vlc_join( p_sys->thread, NULL );
     p_aout->b_die = false;
+    var_DelCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
 
     ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL );
     close( p_sys->i_fd );
 
+    aout_PacketDestroy( p_aout );
     free( p_sys );
 }
 
@@ -558,79 +519,89 @@ static void Close( vlc_object_t * p_this )
  *****************************************************************************
  * This function returns the duration in microseconds of the current buffer.
  *****************************************************************************/
-static mtime_t BufferDuration( aout_instance_t * p_aout )
+static mtime_t BufferDuration( audio_output_t * p_aout )
 {
-    struct aout_sys_t * p_sys = p_aout->output.p_sys;
+    struct aout_sys_t * p_sys = p_aout->sys;
     audio_buf_info audio_buf;
     int i_bytes;
 
-    /* Fill the audio_buf_info structure:
-     * - fragstotal: total number of fragments allocated
-     * - fragsize: size of a fragment in bytes
-     * - bytes: available space in bytes (includes partially used fragments)
-     * Note! 'bytes' could be more than fragments*fragsize */
-    ioctl( p_sys->i_fd, SNDCTL_DSP_GETOSPACE, &audio_buf );
+#ifdef SNDCTL_DSP_GETODELAY
+    if ( ioctl( p_sys->i_fd, SNDCTL_DSP_GETODELAY, &i_bytes ) < 0 )
+#endif
+    {
+        /* Fall back to GETOSPACE and approximate latency. */
 
-    /* calculate number of available fragments (not partially used ones) */
-    i_bytes = (audio_buf.fragstotal * audio_buf.fragsize) - audio_buf.bytes;
+        /* Fill the audio_buf_info structure:
+         * - fragstotal: total number of fragments allocated
+         * - fragsize: size of a fragment in bytes
+         * - bytes: available space in bytes (includes partially used fragments)
+         * Note! 'bytes' could be more than fragments*fragsize */
+        ioctl( p_sys->i_fd, SNDCTL_DSP_GETOSPACE, &audio_buf );
+
+        /* calculate number of available fragments (not partially used ones) */
+        i_bytes = (audio_buf.fragstotal * audio_buf.fragsize) - audio_buf.bytes;
+    }
 
     /* Return the fragment duration */
     return (mtime_t)i_bytes * 1000000
-            / p_aout->output.output.i_bytes_per_frame
-            / p_aout->output.output.i_rate
-            * p_aout->output.output.i_frame_length;
+            / p_aout->format.i_bytes_per_frame
+            / p_aout->format.i_rate
+            * p_aout->format.i_frame_length;
+}
+
+typedef struct
+{
+    aout_buffer_t *p_buffer;
+    void          *p_bytes;
+} oss_thread_ctx_t;
+
+static void OSSThreadCleanup( void *data )
+{
+    oss_thread_ctx_t *p_ctx = data;
+    if( p_ctx->p_buffer )
+        aout_BufferFree( p_ctx->p_buffer );
+    else
+        free( p_ctx->p_bytes );
 }
 
 /*****************************************************************************
  * OSSThread: asynchronous thread used to DMA the data to the device
  *****************************************************************************/
-static int OSSThread( aout_instance_t * p_aout )
+static void* OSSThread( void *obj )
 {
-    struct aout_sys_t * p_sys = p_aout->output.p_sys;
+    audio_output_t * p_aout = (audio_output_t*)obj;
+    struct aout_sys_t * p_sys = p_aout->sys;
     mtime_t next_date = 0;
 
-    while ( !p_aout->b_die )
+    for( ;; )
     {
         aout_buffer_t * p_buffer = NULL;
-        int i_tmp, i_size;
-        uint8_t * p_bytes;
 
-        if ( p_aout->output.output.i_format != VLC_FOURCC('s','p','d','i') )
+        int canc = vlc_savecancel ();
+        if ( p_aout->format.i_format != VLC_CODEC_SPDIFL )
         {
             mtime_t buffered = BufferDuration( p_aout );
 
-            if( p_aout->output.p_sys->b_workaround_buggy_driver )
-            {
-#define i_fragstotal p_aout->output.p_sys->i_fragstotal
-                /* Wait a bit - we don't want our buffer to be full */
-                if( buffered > (p_aout->output.p_sys->max_buffer_duration
-                                / i_fragstotal * (i_fragstotal - 1)) )
-                {
-                    msleep((p_aout->output.p_sys->max_buffer_duration
-                                / i_fragstotal ));
-                    buffered = BufferDuration( p_aout );
-                }
-#undef i_fragstotal
-            }
-
             /* Next buffer will be played at mdate() + buffered */
-            p_buffer = aout_OutputNextBuffer( p_aout, mdate() + buffered,
-                                              false );
+            p_buffer = aout_PacketNext( p_aout, mdate() + buffered );
 
             if( p_buffer == NULL &&
-                buffered > ( p_aout->output.p_sys->max_buffer_duration
-                             / p_aout->output.p_sys->i_fragstotal ) )
+                buffered > ( p_aout->sys->max_buffer_duration
+                             / p_aout->sys->i_fragstotal ) )
             {
+                vlc_restorecancel (canc);
                 /* If we have at least a fragment full, then we can wait a
                  * little and retry to get a new audio buffer instead of
                  * playing a blank sample */
-                msleep( ( p_aout->output.p_sys->max_buffer_duration
-                          / p_aout->output.p_sys->i_fragstotal / 2 ) );
+                msleep( ( p_aout->sys->max_buffer_duration
+                          / p_aout->sys->i_fragstotal / 2 ) );
                 continue;
             }
         }
         else
         {
+            vlc_restorecancel (canc);
+
             /* emu10k1 driver does not report Buffer Duration correctly in
              * passthrough mode so we have to cheat */
             if( !next_date )
@@ -640,53 +611,60 @@ static int OSSThread( aout_instance_t * p_aout )
             else
             {
                 mtime_t delay = next_date - mdate();
-                if( delay > AOUT_PTS_TOLERANCE )
+                if( delay > AOUT_MAX_PTS_ADVANCE )
                 {
                     msleep( delay / 2 );
                 }
             }
 
-            while( !p_aout->b_die && ! ( p_buffer =
-                aout_OutputNextBuffer( p_aout, next_date, true ) ) )
+            for( ;; )
             {
-                msleep( 1000 );
+                canc = vlc_savecancel ();
+                p_buffer = aout_PacketNext( p_aout, next_date );
+                if ( p_buffer )
+                    break;
+                vlc_restorecancel (canc);
+
+                msleep( VLC_HARD_MIN_SLEEP );
                 next_date = mdate();
             }
         }
 
+        uint8_t * p_bytes;
+        int i_size;
         if ( p_buffer != NULL )
         {
             p_bytes = p_buffer->p_buffer;
-            i_size = p_buffer->i_nb_bytes;
+            i_size = p_buffer->i_buffer;
             /* This is theoretical ... we'll see next iteration whether
              * we're drifting */
-            next_date += p_buffer->end_date - p_buffer->start_date;
+            next_date += p_buffer->i_length;
         }
         else
         {
-            i_size = FRAME_SIZE / p_aout->output.output.i_frame_length
-                      * p_aout->output.output.i_bytes_per_frame;
+            i_size = FRAME_SIZE / p_aout->format.i_frame_length
+                      * p_aout->format.i_bytes_per_frame;
             p_bytes = malloc( i_size );
             memset( p_bytes, 0, i_size );
             next_date = 0;
         }
 
-        i_tmp = write( p_sys->i_fd, p_bytes, i_size );
+        oss_thread_ctx_t ctx = {
+            .p_buffer = p_buffer,
+            .p_bytes  = p_bytes,
+        };
+
+        vlc_cleanup_push( OSSThreadCleanup, &ctx );
+        vlc_restorecancel( canc );
+
+        int i_tmp = write( p_sys->i_fd, p_bytes, i_size );
 
         if( i_tmp < 0 )
         {
             msg_Err( p_aout, "write failed (%m)" );
         }
-
-        if ( p_buffer != NULL )
-        {
-            aout_BufferFree( p_buffer );
-        }
-        else
-        {
-            free( p_bytes );
-        }
+        vlc_cleanup_run();
     }
 
-    return VLC_SUCCESS;
+    return NULL;
 }