]> git.sesse.net Git - vlc/blob - modules/audio_output/oss.c
aout: pass audio buffer explicitly to pf_play
[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     int i_fd;
74     int i_fragstotal;
75     mtime_t max_buffer_duration;
76     vlc_thread_t thread;
77 };
78
79 /* This must be a power of 2. */
80 #define FRAME_SIZE 1024
81 #define FRAME_COUNT 32
82
83 /*****************************************************************************
84  * Local prototypes
85  *****************************************************************************/
86 static int  Open         ( vlc_object_t * );
87 static void Close        ( vlc_object_t * );
88
89 static void Play         ( audio_output_t *, block_t * );
90 static void* OSSThread   ( void * );
91
92 static mtime_t BufferDuration( audio_output_t * p_aout );
93
94 /*****************************************************************************
95  * Module descriptor
96  *****************************************************************************/
97 vlc_module_begin ()
98     set_shortname( "OSS" )
99     set_description( N_("Open Sound System") )
100
101     set_category( CAT_AUDIO )
102     set_subcategory( SUBCAT_AUDIO_AOUT )
103     add_loadfile( "oss-audio-device", "/dev/dsp",
104                   N_("OSS DSP device"), NULL, false )
105         add_deprecated_alias( "dspdev" )   /* deprecated since 0.9.3 */
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_NON_LINEAR( &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 = Play;
302     p_aout->pf_pause = NULL;
303     p_aout->pf_flush = NULL;
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_NON_LINEAR( &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->i_nb_samples = A52_FRAME_NB;
385         p_aout->format.i_bytes_per_frame = AOUT_SPDIF_SIZE;
386         p_aout->format.i_frame_length = A52_FRAME_NB;
387
388         aout_VolumeNoneInit( p_aout );
389     }
390
391     if ( !AOUT_FMT_NON_LINEAR( &p_aout->format ) )
392     {
393         unsigned int i_format = AFMT_S16_NE;
394         unsigned int i_frame_size, i_fragments;
395         unsigned int i_rate;
396         unsigned int i_nb_channels;
397         audio_buf_info audio_buf;
398
399         if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 )
400         {
401             msg_Err( p_aout, "cannot set audio output format" );
402             close( p_sys->i_fd );
403             free( p_sys );
404             return VLC_EGENERIC;
405         }
406
407         switch ( i_format )
408         {
409         case AFMT_U8:
410             p_aout->format.i_format = VLC_CODEC_U8;
411             break;
412         case AFMT_S8:
413             p_aout->format.i_format = VLC_CODEC_S8;
414             break;
415         case AFMT_U16_LE:
416             p_aout->format.i_format = VLC_CODEC_U16L;
417             break;
418         case AFMT_S16_LE:
419             p_aout->format.i_format = VLC_CODEC_S16L;
420             break;
421         case AFMT_U16_BE:
422             p_aout->format.i_format = VLC_CODEC_U16B;
423             break;
424         case AFMT_S16_BE:
425             p_aout->format.i_format = VLC_CODEC_S16B;
426             break;
427         default:
428             msg_Err( p_aout, "OSS fell back to an unknown format (%d)",
429                      i_format );
430             close( p_sys->i_fd );
431             free( p_sys );
432             return VLC_EGENERIC;
433         }
434
435         i_nb_channels = aout_FormatNbChannels( &p_aout->format );
436
437         /* Set the number of channels */
438         if( ioctl( p_sys->i_fd, SNDCTL_DSP_CHANNELS, &i_nb_channels ) < 0 ||
439             i_nb_channels != aout_FormatNbChannels( &p_aout->format ) )
440         {
441             msg_Err( p_aout, "cannot set number of audio channels (%s)",
442                      aout_FormatPrintChannels( &p_aout->format) );
443             close( p_sys->i_fd );
444             free( p_sys );
445             return VLC_EGENERIC;
446         }
447
448         /* Set the output rate */
449         i_rate = p_aout->format.i_rate;
450         if( ioctl( p_sys->i_fd, SNDCTL_DSP_SPEED, &i_rate ) < 0 )
451         {
452             msg_Err( p_aout, "cannot set audio output rate (%i)",
453                              p_aout->format.i_rate );
454             close( p_sys->i_fd );
455             free( p_sys );
456             return VLC_EGENERIC;
457         }
458
459         if( i_rate != p_aout->format.i_rate )
460         {
461             p_aout->format.i_rate = i_rate;
462         }
463
464         /* Set the fragment size */
465         aout_FormatPrepare( &p_aout->format );
466
467         /* i_fragment = xxxxyyyy where: xxxx        is fragtotal
468          *                              1 << yyyy   is fragsize */
469         i_frame_size = ((uint64_t)p_aout->format.i_bytes_per_frame * p_aout->format.i_rate * 65536) / (48000 * 2 * 2) / FRAME_COUNT;
470         i_fragments = 4;
471         while( i_fragments < 12 && (1U << i_fragments) < i_frame_size )
472         {
473             ++i_fragments;
474         }
475         i_fragments |= FRAME_COUNT << 16;
476         if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFRAGMENT, &i_fragments ) < 0 )
477         {
478             msg_Warn( p_aout, "cannot set fragment size (%.8x)", i_fragments );
479         }
480
481         if( ioctl( p_sys->i_fd, SNDCTL_DSP_GETOSPACE, &audio_buf ) < 0 )
482         {
483             msg_Err( p_aout, "cannot get fragment size" );
484             close( p_sys->i_fd );
485             free( p_sys );
486             return VLC_EGENERIC;
487         }
488         else
489         {
490             /* Number of fragments actually allocated */
491             p_aout->sys->i_fragstotal = audio_buf.fragstotal;
492
493             /* Maximum duration the soundcard's buffer can hold */
494             p_aout->sys->max_buffer_duration =
495                 (mtime_t)audio_buf.fragstotal * audio_buf.fragsize * 1000000
496                 / p_aout->format.i_bytes_per_frame
497                 / p_aout->format.i_rate
498                 * p_aout->format.i_frame_length;
499
500             p_aout->i_nb_samples = audio_buf.fragsize /
501                 p_aout->format.i_bytes_per_frame;
502         }
503
504         aout_VolumeSoftInit( p_aout );
505     }
506
507     /* Create OSS thread and wait for its readiness. */
508     if( vlc_clone( &p_sys->thread, OSSThread, p_aout,
509                    VLC_THREAD_PRIORITY_OUTPUT ) )
510     {
511         msg_Err( p_aout, "cannot create OSS thread (%m)" );
512         close( p_sys->i_fd );
513         free( p_sys );
514         return VLC_ENOMEM;
515     }
516
517     return VLC_SUCCESS;
518 }
519
520 /*****************************************************************************
521  * Play: nothing to do
522  *****************************************************************************/
523 static void Play( audio_output_t *p_aout, block_t *block )
524 {
525     aout_FifoPush( &p_aout->fifo, block );
526 }
527
528 /*****************************************************************************
529  * Close: close the DSP audio device
530  *****************************************************************************/
531 static void Close( vlc_object_t * p_this )
532 {
533     audio_output_t *p_aout = (audio_output_t *)p_this;
534     struct aout_sys_t * p_sys = p_aout->sys;
535
536     vlc_cancel( p_sys->thread );
537     vlc_join( p_sys->thread, NULL );
538     p_aout->b_die = false;
539
540     ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL );
541     close( p_sys->i_fd );
542
543     free( p_sys );
544 }
545
546 /*****************************************************************************
547  * BufferDuration: buffer status query
548  *****************************************************************************
549  * This function returns the duration in microseconds of the current buffer.
550  *****************************************************************************/
551 static mtime_t BufferDuration( audio_output_t * p_aout )
552 {
553     struct aout_sys_t * p_sys = p_aout->sys;
554     audio_buf_info audio_buf;
555     int i_bytes;
556
557 #ifdef SNDCTL_DSP_GETODELAY
558     if ( ioctl( p_sys->i_fd, SNDCTL_DSP_GETODELAY, &i_bytes ) < 0 )
559 #endif
560     {
561         /* Fall back to GETOSPACE and approximate latency. */
562
563         /* Fill the audio_buf_info structure:
564          * - fragstotal: total number of fragments allocated
565          * - fragsize: size of a fragment in bytes
566          * - bytes: available space in bytes (includes partially used fragments)
567          * Note! 'bytes' could be more than fragments*fragsize */
568         ioctl( p_sys->i_fd, SNDCTL_DSP_GETOSPACE, &audio_buf );
569
570         /* calculate number of available fragments (not partially used ones) */
571         i_bytes = (audio_buf.fragstotal * audio_buf.fragsize) - audio_buf.bytes;
572     }
573
574     /* Return the fragment duration */
575     return (mtime_t)i_bytes * 1000000
576             / p_aout->format.i_bytes_per_frame
577             / p_aout->format.i_rate
578             * p_aout->format.i_frame_length;
579 }
580
581 typedef struct
582 {
583     aout_buffer_t *p_buffer;
584     void          *p_bytes;
585 } oss_thread_ctx_t;
586
587 static void OSSThreadCleanup( void *data )
588 {
589     oss_thread_ctx_t *p_ctx = data;
590     if( p_ctx->p_buffer )
591         aout_BufferFree( p_ctx->p_buffer );
592     else
593         free( p_ctx->p_bytes );
594 }
595
596 /*****************************************************************************
597  * OSSThread: asynchronous thread used to DMA the data to the device
598  *****************************************************************************/
599 static void* OSSThread( void *obj )
600 {
601     audio_output_t * p_aout = (audio_output_t*)obj;
602     struct aout_sys_t * p_sys = p_aout->sys;
603     mtime_t next_date = 0;
604
605     for( ;; )
606     {
607         aout_buffer_t * p_buffer = NULL;
608
609         int canc = vlc_savecancel ();
610         if ( p_aout->format.i_format != VLC_CODEC_SPDIFL )
611         {
612             mtime_t buffered = BufferDuration( p_aout );
613
614             /* Next buffer will be played at mdate() + buffered */
615             p_buffer = aout_OutputNextBuffer( p_aout, mdate() + buffered,
616                                               false );
617
618             if( p_buffer == NULL &&
619                 buffered > ( p_aout->sys->max_buffer_duration
620                              / p_aout->sys->i_fragstotal ) )
621             {
622                 vlc_restorecancel (canc);
623                 /* If we have at least a fragment full, then we can wait a
624                  * little and retry to get a new audio buffer instead of
625                  * playing a blank sample */
626                 msleep( ( p_aout->sys->max_buffer_duration
627                           / p_aout->sys->i_fragstotal / 2 ) );
628                 continue;
629             }
630         }
631         else
632         {
633             vlc_restorecancel (canc);
634
635             /* emu10k1 driver does not report Buffer Duration correctly in
636              * passthrough mode so we have to cheat */
637             if( !next_date )
638             {
639                 next_date = mdate();
640             }
641             else
642             {
643                 mtime_t delay = next_date - mdate();
644                 if( delay > AOUT_MAX_PTS_ADVANCE )
645                 {
646                     msleep( delay / 2 );
647                 }
648             }
649
650             for( ;; )
651             {
652                 canc = vlc_savecancel ();
653                 p_buffer = aout_OutputNextBuffer( p_aout, next_date, true );
654                 if ( p_buffer )
655                     break;
656                 vlc_restorecancel (canc);
657
658                 msleep( VLC_HARD_MIN_SLEEP );
659                 next_date = mdate();
660             }
661         }
662
663         uint8_t * p_bytes;
664         int i_size;
665         if ( p_buffer != NULL )
666         {
667             p_bytes = p_buffer->p_buffer;
668             i_size = p_buffer->i_buffer;
669             /* This is theoretical ... we'll see next iteration whether
670              * we're drifting */
671             next_date += p_buffer->i_length;
672         }
673         else
674         {
675             i_size = FRAME_SIZE / p_aout->format.i_frame_length
676                       * p_aout->format.i_bytes_per_frame;
677             p_bytes = malloc( i_size );
678             memset( p_bytes, 0, i_size );
679             next_date = 0;
680         }
681
682         oss_thread_ctx_t ctx = {
683             .p_buffer = p_buffer,
684             .p_bytes  = p_bytes,
685         };
686
687         vlc_cleanup_push( OSSThreadCleanup, &ctx );
688         vlc_restorecancel( canc );
689
690         int i_tmp = write( p_sys->i_fd, p_bytes, i_size );
691
692         if( i_tmp < 0 )
693         {
694             msg_Err( p_aout, "write failed (%m)" );
695         }
696         vlc_cleanup_run();
697     }
698
699     return NULL;
700 }