]> git.sesse.net Git - vlc/blob - modules/audio_output/oss.c
54d4865b13f04bacc4008ae1acb79823fae573fc
[vlc] / modules / audio_output / oss.c
1 /*****************************************************************************
2  * oss.c : OSS /dev/dsp module for vlc
3  *****************************************************************************
4  * Copyright (C) 2000-2002 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Michel Kaempf <maxx@via.ecp.fr>
8  *          Sam Hocevar <sam@zoy.org>
9  *          Christophe Massiot <massiot@via.ecp.fr>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
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 General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <fcntl.h>                                       /* open(), O_WRONLY */
35 #include <sys/ioctl.h>                                            /* ioctl() */
36 #include <unistd.h>                                      /* write(), close() */
37
38 #include <vlc_common.h>
39 #include <vlc_plugin.h>
40 #include <vlc_fs.h>
41
42 #include <vlc_aout.h>
43
44 /* SNDCTL_DSP_RESET, SNDCTL_DSP_SETFMT, SNDCTL_DSP_STEREO, SNDCTL_DSP_SPEED,
45  * SNDCTL_DSP_GETOSPACE */
46 #ifdef HAVE_SOUNDCARD_H
47 #   include <soundcard.h>
48 #elif defined( HAVE_SYS_SOUNDCARD_H )
49 #   include <sys/soundcard.h>
50 #endif
51
52 /* Patches for ignorant OSS versions */
53 #ifndef AFMT_AC3
54 #   define AFMT_AC3     0x00000400        /* Dolby Digital AC3 */
55 #endif
56
57 #ifndef AFMT_S16_NE
58 #   ifdef WORDS_BIGENDIAN
59 #       define AFMT_S16_NE AFMT_S16_BE
60 #   else
61 #       define AFMT_S16_NE AFMT_S16_LE
62 #   endif
63 #endif
64
65 /*****************************************************************************
66  * aout_sys_t: OSS audio output method descriptor
67  *****************************************************************************
68  * This structure is part of the audio output thread descriptor.
69  * It describes the DSP specific properties of an audio device.
70  *****************************************************************************/
71 struct aout_sys_t
72 {
73     aout_packet_t packet;
74     int i_fd;
75     int i_fragstotal;
76     mtime_t max_buffer_duration;
77     vlc_thread_t thread;
78 };
79
80 /* This must be a power of 2. */
81 #define FRAME_SIZE 1024
82 #define FRAME_COUNT 32
83
84 /*****************************************************************************
85  * Local prototypes
86  *****************************************************************************/
87 static int  Open         ( vlc_object_t * );
88 static void Close        ( vlc_object_t * );
89
90 static void Play         ( audio_output_t *, block_t * );
91 static void* OSSThread   ( void * );
92
93 static mtime_t BufferDuration( audio_output_t * p_aout );
94
95 /*****************************************************************************
96  * Module descriptor
97  *****************************************************************************/
98 vlc_module_begin ()
99     set_shortname( "OSS" )
100     set_description( N_("Open Sound System") )
101
102     set_category( CAT_AUDIO )
103     set_subcategory( SUBCAT_AUDIO_AOUT )
104     add_loadfile( "oss-audio-device", "/dev/dsp",
105                   N_("OSS DSP device"), NULL, false )
106
107     set_capability( "audio output", 100 )
108     add_shortcut( "oss" )
109     set_callbacks( Open, Close )
110 vlc_module_end ()
111
112 /*****************************************************************************
113  * Probe: probe the audio device for available formats and channels
114  *****************************************************************************/
115 static void Probe( audio_output_t * p_aout )
116 {
117     struct aout_sys_t * p_sys = p_aout->sys;
118     vlc_value_t val, text;
119     int i_format, i_nb_channels;
120
121     var_Create( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
122     text.psz_string = _("Audio Device");
123     var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
124
125     /* Test for multi-channel. */
126 #ifdef SNDCTL_DSP_GETCHANNELMASK
127     if ( aout_FormatNbChannels( &p_aout->format ) > 2 )
128     {
129         /* Check that the device supports this. */
130
131         int i_chanmask;
132
133         /* Reset all. */
134         i_format = AFMT_S16_NE;
135         if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 ||
136             ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 )
137         {
138             msg_Err( p_aout, "cannot reset OSS audio device" );
139             var_Destroy( p_aout, "audio-device" );
140             return;
141         }
142
143         if ( ioctl( p_sys->i_fd, SNDCTL_DSP_GETCHANNELMASK,
144                     &i_chanmask ) == 0 )
145         {
146             if ( !(i_chanmask & DSP_BIND_FRONT) )
147             {
148                 msg_Err( p_aout, "no front channels! (%x)",
149                          i_chanmask );
150                 return;
151             }
152
153             if ( (i_chanmask & (DSP_BIND_SURR | DSP_BIND_CENTER_LFE))
154                   && (p_aout->format.i_physical_channels ==
155                        (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
156                          | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
157                          | AOUT_CHAN_LFE)) )
158             {
159                 val.i_int = AOUT_VAR_5_1;
160                 text.psz_string = (char*) "5.1";
161                 var_Change( p_aout, "audio-device",
162                             VLC_VAR_ADDCHOICE, &val, &text );
163             }
164
165             if ( (i_chanmask & DSP_BIND_SURR)
166                   && (p_aout->format.i_physical_channels &
167                        (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
168                          | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT)) )
169             {
170                 val.i_int = AOUT_VAR_2F2R;
171                 text.psz_string = _("2 Front 2 Rear");
172                 var_Change( p_aout, "audio-device",
173                             VLC_VAR_ADDCHOICE, &val, &text );
174             }
175         }
176     }
177 #endif
178
179     /* Reset all. */
180     i_format = AFMT_S16_NE;
181     if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 ||
182         ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 )
183     {
184         msg_Err( p_aout, "cannot reset OSS audio device" );
185         var_Destroy( p_aout, "audio-device" );
186         return;
187     }
188
189     /* Test for stereo. */
190     i_nb_channels = 2;
191     if( ioctl( p_sys->i_fd, SNDCTL_DSP_CHANNELS, &i_nb_channels ) >= 0
192          && i_nb_channels == 2 )
193     {
194         val.i_int = AOUT_VAR_STEREO;
195         text.psz_string = _("Stereo");
196         var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
197     }
198
199     /* Reset all. */
200     i_format = AFMT_S16_NE;
201     if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 ||
202         ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 )
203     {
204         msg_Err( p_aout, "cannot reset OSS audio device" );
205         var_Destroy( p_aout, "audio-device" );
206         return;
207     }
208
209     /* Test for mono. */
210     i_nb_channels = 1;
211     if( ioctl( p_sys->i_fd, SNDCTL_DSP_CHANNELS, &i_nb_channels ) >= 0
212          && i_nb_channels == 1 )
213     {
214         val.i_int = AOUT_VAR_MONO;
215         text.psz_string = _("Mono");
216         var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
217         if ( p_aout->format.i_physical_channels == AOUT_CHAN_CENTER )
218         {
219             var_Set( p_aout, "audio-device", val );
220         }
221     }
222
223     if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 )
224     {
225         msg_Err( p_aout, "cannot reset OSS audio device" );
226         var_Destroy( p_aout, "audio-device" );
227         return;
228     }
229
230     /* Test for spdif. */
231     if ( AOUT_FMT_SPDIF( &p_aout->format ) )
232     {
233         i_format = AFMT_AC3;
234
235         if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) >= 0
236              && i_format == AFMT_AC3 )
237         {
238             val.i_int = AOUT_VAR_SPDIF;
239             text.psz_string = _("A/52 over S/PDIF");
240             var_Change( p_aout, "audio-device",
241                         VLC_VAR_ADDCHOICE, &val, &text );
242             if( var_InheritBool( p_aout, "spdif" ) )
243                 var_Set( p_aout, "audio-device", val );
244         }
245         else if( var_InheritBool( p_aout, "spdif" ) )
246         {
247             msg_Warn( p_aout, "S/PDIF not supported by card" );
248         }
249     }
250
251     var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart,
252                      NULL );
253 }
254
255 /*****************************************************************************
256  * Open: open the audio device (the digital sound processor)
257  *****************************************************************************
258  * This function opens the DSP as a usual non-blocking write-only file, and
259  * modifies the p_aout->p_sys->i_fd with the file's descriptor.
260  *****************************************************************************/
261 static int Open( vlc_object_t *p_this )
262 {
263     audio_output_t * p_aout = (audio_output_t *)p_this;
264     struct aout_sys_t * p_sys;
265     char * psz_device;
266     vlc_value_t val;
267
268     /* Allocate structure */
269     p_aout->sys = p_sys = malloc( sizeof( aout_sys_t ) );
270     if( p_sys == NULL )
271         return VLC_ENOMEM;
272
273     /* Get device name */
274     if( (psz_device = var_InheritString( p_aout, "oss-audio-device" )) == NULL )
275     {
276         msg_Err( p_aout, "no audio device specified (maybe /dev/dsp?)" );
277         free( p_sys );
278         return VLC_EGENERIC;
279     }
280
281     /* Open the sound device in non-blocking mode, because ALSA's OSS
282      * emulation and some broken OSS drivers would make a blocking call
283      * wait forever until the device is available. Since this breaks the
284      * OSS spec, we immediately put it back to blocking mode if the
285      * operation was successful. */
286     p_sys->i_fd = vlc_open( psz_device, O_WRONLY | O_NDELAY );
287     if( p_sys->i_fd < 0 )
288     {
289         msg_Err( p_aout, "cannot open audio device (%s)", psz_device );
290         free( psz_device );
291         free( p_sys );
292         return VLC_EGENERIC;
293     }
294
295     /* if the opening was ok, put the device back in blocking mode */
296     fcntl( p_sys->i_fd, F_SETFL,
297             fcntl( p_sys->i_fd, F_GETFL ) &~ FNDELAY );
298
299     free( psz_device );
300
301     p_aout->pf_play = aout_PacketPlay;
302     p_aout->pf_pause = aout_PacketPause;
303     p_aout->pf_flush = aout_PacketFlush;
304
305     if ( var_Type( p_aout, "audio-device" ) == 0 )
306     {
307         Probe( p_aout );
308     }
309
310     if ( var_Get( p_aout, "audio-device", &val ) < 0 )
311     {
312         /* Probe() has failed. */
313         close( p_sys->i_fd );
314         free( p_sys );
315         return VLC_EGENERIC;
316     }
317
318     if ( val.i_int == AOUT_VAR_SPDIF )
319     {
320         p_aout->format.i_format = VLC_CODEC_SPDIFL;
321     }
322     else if ( val.i_int == AOUT_VAR_5_1 )
323     {
324         p_aout->format.i_format = VLC_CODEC_S16N;
325         p_aout->format.i_physical_channels
326             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
327                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
328                | AOUT_CHAN_LFE;
329     }
330     else if ( val.i_int == AOUT_VAR_2F2R )
331     {
332         p_aout->format.i_format = VLC_CODEC_S16N;
333         p_aout->format.i_physical_channels
334             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
335                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
336     }
337     else if ( val.i_int == AOUT_VAR_STEREO )
338     {
339         p_aout->format.i_format = VLC_CODEC_S16N;
340         p_aout->format.i_physical_channels
341             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
342     }
343     else if ( val.i_int == AOUT_VAR_MONO )
344     {
345         p_aout->format.i_format = VLC_CODEC_S16N;
346         p_aout->format.i_physical_channels = AOUT_CHAN_CENTER;
347     }
348     else
349     {
350         /* This should not happen ! */
351         msg_Err( p_aout, "internal: can't find audio-device (%"PRId64")",
352                  val.i_int );
353         close( p_sys->i_fd );
354         free( p_sys );
355         return VLC_EGENERIC;
356     }
357
358     var_TriggerCallback( p_aout, "intf-change" );
359
360     /* Reset the DSP device */
361     if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 )
362     {
363         msg_Err( p_aout, "cannot reset OSS audio device" );
364         close( p_sys->i_fd );
365         free( p_sys );
366         return VLC_EGENERIC;
367     }
368
369     /* Set the output format */
370     if ( AOUT_FMT_SPDIF( &p_aout->format ) )
371     {
372         int i_format = AFMT_AC3;
373
374         if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0
375              || i_format != AFMT_AC3 )
376         {
377             msg_Err( p_aout, "cannot reset OSS audio device" );
378             close( p_sys->i_fd );
379             free( p_sys );
380             return VLC_EGENERIC;
381         }
382
383         p_aout->format.i_format = VLC_CODEC_SPDIFL;
384         p_aout->format.i_bytes_per_frame = AOUT_SPDIF_SIZE;
385         p_aout->format.i_frame_length = A52_FRAME_NB;
386
387         aout_PacketInit( p_aout, &p_sys->packet, A52_FRAME_NB );
388         aout_VolumeNoneInit( p_aout );
389     }
390     else
391     {
392         unsigned int i_format = AFMT_S16_NE;
393         unsigned int i_frame_size, i_fragments;
394         unsigned int i_rate;
395         unsigned int i_nb_channels;
396         audio_buf_info audio_buf;
397
398         if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 )
399         {
400             msg_Err( p_aout, "cannot set audio output format" );
401             close( p_sys->i_fd );
402             free( p_sys );
403             return VLC_EGENERIC;
404         }
405
406         switch ( i_format )
407         {
408         case AFMT_U8:
409             p_aout->format.i_format = VLC_CODEC_U8;
410             break;
411         case AFMT_S8:
412             p_aout->format.i_format = VLC_CODEC_S8;
413             break;
414         case AFMT_U16_LE:
415             p_aout->format.i_format = VLC_CODEC_U16L;
416             break;
417         case AFMT_S16_LE:
418             p_aout->format.i_format = VLC_CODEC_S16L;
419             break;
420         case AFMT_U16_BE:
421             p_aout->format.i_format = VLC_CODEC_U16B;
422             break;
423         case AFMT_S16_BE:
424             p_aout->format.i_format = VLC_CODEC_S16B;
425             break;
426         default:
427             msg_Err( p_aout, "OSS fell back to an unknown format (%d)",
428                      i_format );
429             close( p_sys->i_fd );
430             free( p_sys );
431             return VLC_EGENERIC;
432         }
433
434         i_nb_channels = aout_FormatNbChannels( &p_aout->format );
435
436         /* Set the number of channels */
437         if( ioctl( p_sys->i_fd, SNDCTL_DSP_CHANNELS, &i_nb_channels ) < 0 ||
438             i_nb_channels != aout_FormatNbChannels( &p_aout->format ) )
439         {
440             msg_Err( p_aout, "cannot set number of audio channels (%s)",
441                      aout_FormatPrintChannels( &p_aout->format) );
442             close( p_sys->i_fd );
443             free( p_sys );
444             return VLC_EGENERIC;
445         }
446
447         /* Set the output rate */
448         i_rate = p_aout->format.i_rate;
449         if( ioctl( p_sys->i_fd, SNDCTL_DSP_SPEED, &i_rate ) < 0 )
450         {
451             msg_Err( p_aout, "cannot set audio output rate (%i)",
452                              p_aout->format.i_rate );
453             close( p_sys->i_fd );
454             free( p_sys );
455             return VLC_EGENERIC;
456         }
457
458         if( i_rate != p_aout->format.i_rate )
459         {
460             p_aout->format.i_rate = i_rate;
461         }
462
463         /* Set the fragment size */
464         aout_FormatPrepare( &p_aout->format );
465
466         /* i_fragment = xxxxyyyy where: xxxx        is fragtotal
467          *                              1 << yyyy   is fragsize */
468         i_frame_size = ((uint64_t)p_aout->format.i_bytes_per_frame * p_aout->format.i_rate * 65536) / (48000 * 2 * 2) / FRAME_COUNT;
469         i_fragments = 4;
470         while( i_fragments < 12 && (1U << i_fragments) < i_frame_size )
471         {
472             ++i_fragments;
473         }
474         i_fragments |= FRAME_COUNT << 16;
475         if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFRAGMENT, &i_fragments ) < 0 )
476         {
477             msg_Warn( p_aout, "cannot set fragment size (%.8x)", i_fragments );
478         }
479
480         if( ioctl( p_sys->i_fd, SNDCTL_DSP_GETOSPACE, &audio_buf ) < 0 )
481         {
482             msg_Err( p_aout, "cannot get fragment size" );
483             close( p_sys->i_fd );
484             free( p_sys );
485             return VLC_EGENERIC;
486         }
487
488         /* Number of fragments actually allocated */
489         p_aout->sys->i_fragstotal = audio_buf.fragstotal;
490
491         /* Maximum duration the soundcard's buffer can hold */
492         p_aout->sys->max_buffer_duration =
493                 (mtime_t)audio_buf.fragstotal * audio_buf.fragsize * 1000000
494                 / p_aout->format.i_bytes_per_frame
495                 / p_aout->format.i_rate
496                 * p_aout->format.i_frame_length;
497
498         aout_PacketInit( p_aout, &p_sys->packet,
499                          audio_buf.fragsize/p_aout->format.i_bytes_per_frame );
500         aout_VolumeSoftInit( p_aout );
501     }
502
503     /* Create OSS thread and wait for its readiness. */
504     if( vlc_clone( &p_sys->thread, OSSThread, p_aout,
505                    VLC_THREAD_PRIORITY_OUTPUT ) )
506     {
507         msg_Err( p_aout, "cannot create OSS thread (%m)" );
508         close( p_sys->i_fd );
509         aout_PacketDestroy( p_aout );
510         free( p_sys );
511         return VLC_ENOMEM;
512     }
513
514     return VLC_SUCCESS;
515 }
516
517 /*****************************************************************************
518  * Close: close the DSP audio device
519  *****************************************************************************/
520 static void Close( vlc_object_t * p_this )
521 {
522     audio_output_t *p_aout = (audio_output_t *)p_this;
523     struct aout_sys_t * p_sys = p_aout->sys;
524
525     vlc_cancel( p_sys->thread );
526     vlc_join( p_sys->thread, NULL );
527     p_aout->b_die = false;
528
529     ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL );
530     close( p_sys->i_fd );
531
532     aout_PacketDestroy( p_aout );
533     free( p_sys );
534 }
535
536 /*****************************************************************************
537  * BufferDuration: buffer status query
538  *****************************************************************************
539  * This function returns the duration in microseconds of the current buffer.
540  *****************************************************************************/
541 static mtime_t BufferDuration( audio_output_t * p_aout )
542 {
543     struct aout_sys_t * p_sys = p_aout->sys;
544     audio_buf_info audio_buf;
545     int i_bytes;
546
547 #ifdef SNDCTL_DSP_GETODELAY
548     if ( ioctl( p_sys->i_fd, SNDCTL_DSP_GETODELAY, &i_bytes ) < 0 )
549 #endif
550     {
551         /* Fall back to GETOSPACE and approximate latency. */
552
553         /* Fill the audio_buf_info structure:
554          * - fragstotal: total number of fragments allocated
555          * - fragsize: size of a fragment in bytes
556          * - bytes: available space in bytes (includes partially used fragments)
557          * Note! 'bytes' could be more than fragments*fragsize */
558         ioctl( p_sys->i_fd, SNDCTL_DSP_GETOSPACE, &audio_buf );
559
560         /* calculate number of available fragments (not partially used ones) */
561         i_bytes = (audio_buf.fragstotal * audio_buf.fragsize) - audio_buf.bytes;
562     }
563
564     /* Return the fragment duration */
565     return (mtime_t)i_bytes * 1000000
566             / p_aout->format.i_bytes_per_frame
567             / p_aout->format.i_rate
568             * p_aout->format.i_frame_length;
569 }
570
571 typedef struct
572 {
573     aout_buffer_t *p_buffer;
574     void          *p_bytes;
575 } oss_thread_ctx_t;
576
577 static void OSSThreadCleanup( void *data )
578 {
579     oss_thread_ctx_t *p_ctx = data;
580     if( p_ctx->p_buffer )
581         aout_BufferFree( p_ctx->p_buffer );
582     else
583         free( p_ctx->p_bytes );
584 }
585
586 /*****************************************************************************
587  * OSSThread: asynchronous thread used to DMA the data to the device
588  *****************************************************************************/
589 static void* OSSThread( void *obj )
590 {
591     audio_output_t * p_aout = (audio_output_t*)obj;
592     struct aout_sys_t * p_sys = p_aout->sys;
593     mtime_t next_date = 0;
594
595     for( ;; )
596     {
597         aout_buffer_t * p_buffer = NULL;
598
599         int canc = vlc_savecancel ();
600         if ( p_aout->format.i_format != VLC_CODEC_SPDIFL )
601         {
602             mtime_t buffered = BufferDuration( p_aout );
603
604             /* Next buffer will be played at mdate() + buffered */
605             p_buffer = aout_PacketNext( p_aout, mdate() + buffered );
606
607             if( p_buffer == NULL &&
608                 buffered > ( p_aout->sys->max_buffer_duration
609                              / p_aout->sys->i_fragstotal ) )
610             {
611                 vlc_restorecancel (canc);
612                 /* If we have at least a fragment full, then we can wait a
613                  * little and retry to get a new audio buffer instead of
614                  * playing a blank sample */
615                 msleep( ( p_aout->sys->max_buffer_duration
616                           / p_aout->sys->i_fragstotal / 2 ) );
617                 continue;
618             }
619         }
620         else
621         {
622             vlc_restorecancel (canc);
623
624             /* emu10k1 driver does not report Buffer Duration correctly in
625              * passthrough mode so we have to cheat */
626             if( !next_date )
627             {
628                 next_date = mdate();
629             }
630             else
631             {
632                 mtime_t delay = next_date - mdate();
633                 if( delay > AOUT_MAX_PTS_ADVANCE )
634                 {
635                     msleep( delay / 2 );
636                 }
637             }
638
639             for( ;; )
640             {
641                 canc = vlc_savecancel ();
642                 p_buffer = aout_PacketNext( p_aout, next_date );
643                 if ( p_buffer )
644                     break;
645                 vlc_restorecancel (canc);
646
647                 msleep( VLC_HARD_MIN_SLEEP );
648                 next_date = mdate();
649             }
650         }
651
652         uint8_t * p_bytes;
653         int i_size;
654         if ( p_buffer != NULL )
655         {
656             p_bytes = p_buffer->p_buffer;
657             i_size = p_buffer->i_buffer;
658             /* This is theoretical ... we'll see next iteration whether
659              * we're drifting */
660             next_date += p_buffer->i_length;
661         }
662         else
663         {
664             i_size = FRAME_SIZE / p_aout->format.i_frame_length
665                       * p_aout->format.i_bytes_per_frame;
666             p_bytes = malloc( i_size );
667             memset( p_bytes, 0, i_size );
668             next_date = 0;
669         }
670
671         oss_thread_ctx_t ctx = {
672             .p_buffer = p_buffer,
673             .p_bytes  = p_bytes,
674         };
675
676         vlc_cleanup_push( OSSThreadCleanup, &ctx );
677         vlc_restorecancel( canc );
678
679         int i_tmp = write( p_sys->i_fd, p_bytes, i_size );
680
681         if( i_tmp < 0 )
682         {
683             msg_Err( p_aout, "write failed (%m)" );
684         }
685         vlc_cleanup_run();
686     }
687
688     return NULL;
689 }