]> git.sesse.net Git - vlc/blob - modules/audio_output/oss.c
Fixed another very nasty localization bug.
[vlc] / modules / audio_output / oss.c
1 /*****************************************************************************
2  * oss.c : OSS /dev/dsp module for vlc
3  *****************************************************************************
4  * Copyright (C) 2000-2002 VideoLAN
5  * $Id: oss.c,v 1.43 2003/01/07 15:12:15 massiot Exp $
6  *
7  * Authors: Michel Kaempf <maxx@via.ecp.fr>
8  *          Samuel 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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <errno.h>                                                 /* ENOMEM */
30 #include <fcntl.h>                                       /* open(), O_WRONLY */
31 #include <sys/ioctl.h>                                            /* ioctl() */
32 #include <string.h>                                            /* strerror() */
33 #include <unistd.h>                                      /* write(), close() */
34 #include <stdlib.h>                            /* calloc(), malloc(), free() */
35
36 #include <vlc/vlc.h>
37
38 #ifdef HAVE_ALLOCA_H
39 #   include <alloca.h>
40 #endif
41
42 #include <vlc/aout.h>
43
44 #include "aout_internal.h"
45
46 /* SNDCTL_DSP_RESET, SNDCTL_DSP_SETFMT, SNDCTL_DSP_STEREO, SNDCTL_DSP_SPEED,
47  * SNDCTL_DSP_GETOSPACE */
48 #ifdef HAVE_SOUNDCARD_H
49 #   include <soundcard.h>
50 #elif defined( HAVE_SYS_SOUNDCARD_H )
51 #   include <sys/soundcard.h>
52 #elif defined( HAVE_MACHINE_SOUNDCARD_H )
53 #   include <machine/soundcard.h>
54 #endif
55
56 /*****************************************************************************
57  * aout_sys_t: OSS audio output method descriptor
58  *****************************************************************************
59  * This structure is part of the audio output thread descriptor.
60  * It describes the dsp specific properties of an audio device.
61  *****************************************************************************/
62 struct aout_sys_t
63 {
64     int i_fd;
65     int b_workaround_buggy_driver;
66     int i_fragstotal;
67     mtime_t max_buffer_duration;
68 };
69
70 /* This must be a power of 2. */
71 #define FRAME_SIZE 1024
72 #define FRAME_COUNT 4
73
74 /*****************************************************************************
75  * Local prototypes
76  *****************************************************************************/
77 static int  Open         ( vlc_object_t * );
78 static void Close        ( vlc_object_t * );
79
80 static void Play         ( aout_instance_t * );
81 static int  OSSThread    ( aout_instance_t * );
82
83 static mtime_t BufferDuration( aout_instance_t * p_aout );
84
85 /*****************************************************************************
86  * Module descriptor
87  *****************************************************************************/
88 #define BUGGY_TEXT N_("Try to work around buggy OSS drivers")
89 #define BUGGY_LONGTEXT N_( \
90     "Some buggy OSS drivers just don't like when their internal buffers " \
91     "are completely filled (the sound gets heavily hashed). If you have one " \
92     "of these drivers, then you need to enable this option." )
93
94 #define SPDIF_TEXT N_("Try to use S/PDIF output")
95 #define SPDIF_LONGTEXT N_( \
96     "Sometimes we attempt to use the S/PDIF output, even if nothing is " \
97     "connected to it. Un-checking this option disables this behaviour, " \
98     "and permanently selects analog PCM output." )
99
100 vlc_module_begin();
101     add_category_hint( N_("OSS"), NULL );
102     add_file( "dspdev", "/dev/dsp", aout_FindAndRestart,
103               N_("OSS dsp device"), NULL );
104     add_bool( "oss-buggy", 0, NULL, BUGGY_TEXT, BUGGY_LONGTEXT );
105     add_bool( "spdif", 1, NULL, SPDIF_TEXT, SPDIF_LONGTEXT );
106     set_description( _("Linux OSS /dev/dsp module") );
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( aout_instance_t * p_aout )
116 {
117     struct aout_sys_t * p_sys = p_aout->output.p_sys;
118     vlc_value_t val;
119     int i_format, i_nb_channels;
120
121     var_Create( p_aout, "audio-device", VLC_VAR_STRING | VLC_VAR_HASCHOICE );
122
123     if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 )
124     {
125         msg_Err( p_aout, "cannot reset OSS audio device" );
126         var_Destroy( p_aout, "audio-device" );
127         return;
128     }
129
130     if ( config_GetInt( p_aout, "spdif" )
131           && AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
132     {
133         i_format = AFMT_AC3;
134
135         if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) >= 0
136              && i_format == AFMT_AC3 )
137         {
138             val.psz_string = N_("A/52 over S/PDIF");
139             var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val );
140         }
141     }
142
143     /* Go to PCM mode. */
144     i_format = AFMT_S16_NE;
145     if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 ||
146         ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 )
147     {
148         return;
149     }
150
151 #ifdef SNDCTL_DSP_GETCHANNELMASK
152     if ( aout_FormatNbChannels( &p_aout->output.output ) > 2 )
153     {
154         /* Check that the device supports this. */
155
156         int i_chanmask;
157         if ( ioctl( p_sys->i_fd, SNDCTL_DSP_GETCHANNELMASK,
158                     &i_chanmask ) == 0 )
159         {
160             if ( !(i_chanmask & DSP_BIND_FRONT) )
161             {
162                 msg_Err( p_aout, "No front channels ! (%x)",
163                          i_chanmask );
164                 return;
165             }
166
167             if ( (i_chanmask & (DSP_BIND_SURR | DSP_BIND_CENTER_LFE))
168                   && (p_aout->output.output.i_physical_channels ==
169                        (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
170                          | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
171                          | AOUT_CHAN_LFE)) )
172             {
173                 val.psz_string = N_("5.1");
174                 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val );
175             }
176
177             if ( (i_chanmask & DSP_BIND_SURR)
178                   && (p_aout->output.output.i_physical_channels &
179                        (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
180                          | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT)) )
181             {
182                 val.psz_string = N_("2 Front 2 Rear");
183                 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val );
184             }
185         }
186     }
187 #endif
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.psz_string = N_("Stereo");
195         var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val );
196     }
197
198     /* Reset all. */
199     i_format = AFMT_S16_NE;
200     if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 ||
201         ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 )
202     {
203         msg_Err( p_aout, "cannot reset OSS audio device" );
204         var_Destroy( p_aout, "audio-device" );
205         return;
206     }
207
208     /* Test for mono. */
209     i_nb_channels = 1;
210     if( ioctl( p_sys->i_fd, SNDCTL_DSP_CHANNELS, &i_nb_channels ) >= 0  
211          && i_nb_channels == 1 )
212     {
213         val.psz_string = N_("Mono");
214         var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val );        
215     }
216
217     val.b_bool = VLC_TRUE;
218     var_Set( p_aout, "intf-change", val );
219 }
220
221 /*****************************************************************************
222  * Open: open the audio device (the digital sound processor)
223  *****************************************************************************
224  * This function opens the dsp as a usual non-blocking write-only file, and
225  * modifies the p_aout->p_sys->i_fd with the file's descriptor.
226  *****************************************************************************/
227 static int Open( vlc_object_t *p_this )
228 {
229     aout_instance_t * p_aout = (aout_instance_t *)p_this;
230     struct aout_sys_t * p_sys;
231     char * psz_device;
232     vlc_value_t val;
233
234     /* Allocate structure */
235     p_aout->output.p_sys = p_sys = malloc( sizeof( aout_sys_t ) );
236     if( p_sys == NULL )
237     {
238         msg_Err( p_aout, "out of memory" );
239         return VLC_ENOMEM;
240     }
241
242     /* Get device name */
243     if( (psz_device = config_GetPsz( p_aout, "dspdev" )) == NULL )
244     {
245         msg_Err( p_aout, "no audio device given (maybe /dev/dsp ?)" );
246         free( p_sys );
247         return VLC_EGENERIC;
248     }
249
250     /* Open the sound device */
251     p_sys->i_fd = open( psz_device, O_WRONLY );
252     if( p_sys->i_fd < 0 )
253     {
254         msg_Err( p_aout, "cannot open audio device (%s)", psz_device );
255         free( p_sys );
256         return VLC_EGENERIC;
257     }
258     free( psz_device );
259
260     p_aout->output.pf_play = Play;
261
262     if ( var_Type( p_aout, "audio-device" ) == 0 )
263     {
264         Probe( p_aout );
265     }
266
267     if ( var_Get( p_aout, "audio-device", &val ) < 0 )
268     {
269         /* Probe() has failed. */
270         free( p_sys );
271         return VLC_EGENERIC;
272     }
273
274     if ( !strcmp( val.psz_string, N_("A/52 over S/PDIF") ) )
275     {
276         p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
277     }
278     else if ( !strcmp( val.psz_string, N_("5.1") ) )
279     {
280         p_aout->output.output.i_format = AOUT_FMT_S16_NE;
281         p_aout->output.output.i_physical_channels
282             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
283                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARLEFT
284                | AOUT_CHAN_LFE;
285     }
286     else if ( !strcmp( val.psz_string, N_("2 Front 2 Rear") ) )
287     {
288         p_aout->output.output.i_format = AOUT_FMT_S16_NE;
289         p_aout->output.output.i_physical_channels
290             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
291                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARLEFT;
292     }
293     else if ( !strcmp( val.psz_string, N_("Stereo") ) )
294     {
295         p_aout->output.output.i_format = AOUT_FMT_S16_NE;
296         p_aout->output.output.i_physical_channels
297             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
298     }
299     else if ( !strcmp( val.psz_string, N_("Mono") ) )
300     {
301         p_aout->output.output.i_format = AOUT_FMT_S16_NE;
302         p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
303     }
304     else
305     {
306         /* This should not happen ! */
307         msg_Err( p_aout, "internal: can't find audio-device (%s)",
308                  val.psz_string );
309         free( p_sys );
310         return VLC_EGENERIC;
311     }
312     free( val.psz_string );
313
314     /* Reset the DSP device */
315     if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 )
316     {
317         msg_Err( p_aout, "cannot reset OSS audio device" );
318         close( p_sys->i_fd );
319         free( p_sys );
320         return VLC_EGENERIC;
321     }
322
323     /* Set the output format */
324     if ( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
325     {
326         int i_format = AFMT_AC3;
327
328         if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0
329              || i_format != AFMT_AC3 )
330         {
331             msg_Err( p_aout, "cannot reset OSS audio device" );
332             close( p_sys->i_fd );
333             free( p_sys );
334             return VLC_EGENERIC;
335         }
336
337         p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
338         p_aout->output.i_nb_samples = A52_FRAME_NB;
339         p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
340         p_aout->output.output.i_frame_length = A52_FRAME_NB;
341
342         aout_VolumeNoneInit( p_aout );
343     }
344
345     if ( !AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
346     {
347         unsigned int i_format = AFMT_S16_NE;
348         unsigned int i_frame_size, i_fragments;
349         unsigned int i_rate;
350         unsigned int i_nb_channels;
351         audio_buf_info audio_buf;
352
353         if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 )
354         {
355             msg_Err( p_aout, "cannot set audio output format" );
356             close( p_sys->i_fd );
357             free( p_sys );
358             return VLC_EGENERIC;
359         }
360
361         switch ( i_format )
362         {
363         case AFMT_U8:
364             p_aout->output.output.i_format = VLC_FOURCC('u','8',' ',' ');
365             break;
366         case AFMT_S8:
367             p_aout->output.output.i_format = VLC_FOURCC('s','8',' ',' ');
368             break;
369         case AFMT_U16_LE:
370             p_aout->output.output.i_format = VLC_FOURCC('u','1','6','l');
371             break;
372         case AFMT_S16_LE:
373             p_aout->output.output.i_format = VLC_FOURCC('s','1','6','l');
374             break;
375         case AFMT_U16_BE:
376             p_aout->output.output.i_format = VLC_FOURCC('u','1','6','b');
377             break;
378         case AFMT_S16_BE:
379             p_aout->output.output.i_format = VLC_FOURCC('s','1','6','b');
380             break;
381         default:
382             msg_Err( p_aout, "OSS fell back to an unknown format (%d)",
383                      i_format );
384             close( p_sys->i_fd );
385             free( p_sys );
386             return VLC_EGENERIC;
387         }
388
389         i_nb_channels = aout_FormatNbChannels( &p_aout->output.output );
390
391         /* Set the number of channels */
392         if( ioctl( p_sys->i_fd, SNDCTL_DSP_CHANNELS, &i_nb_channels ) < 0 ||
393             i_nb_channels != aout_FormatNbChannels( &p_aout->output.output ) )
394         {
395             msg_Err( p_aout, "cannot set number of audio channels (%s)",
396                      aout_FormatPrintChannels( &p_aout->output.output) );
397             close( p_sys->i_fd );
398             free( p_sys );
399             return VLC_EGENERIC;
400         }
401
402         /* Set the output rate */
403         i_rate = p_aout->output.output.i_rate;
404         if( ioctl( p_sys->i_fd, SNDCTL_DSP_SPEED, &i_rate ) < 0 )
405         {
406             msg_Err( p_aout, "cannot set audio output rate (%i)",
407                              p_aout->output.output.i_rate );
408             close( p_sys->i_fd );
409             free( p_sys );
410             return VLC_EGENERIC;
411         }
412
413         if( i_rate != p_aout->output.output.i_rate )
414         {
415             p_aout->output.output.i_rate = i_rate;
416         }
417
418         /* Set the fragment size */
419         aout_FormatPrepare( &p_aout->output.output );
420
421         /* i_fragment = xxxxyyyy where: xxxx        is fragtotal
422          *                              1 << yyyy   is fragsize */
423         i_fragments = 0;
424         i_frame_size = FRAME_SIZE * p_aout->output.output.i_bytes_per_frame;
425         while( i_frame_size >>= 1 )
426         {
427             ++i_fragments;
428         }
429         i_fragments |= FRAME_COUNT << 16;
430         if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFRAGMENT, &i_fragments ) < 0 )
431         {
432             msg_Warn( p_aout, "cannot set fragment size (%.8x)", i_fragments );
433         }
434
435         if( ioctl( p_sys->i_fd, SNDCTL_DSP_GETOSPACE, &audio_buf ) < 0 )
436         {
437             msg_Err( p_aout, "cannot get fragment size" );
438             close( p_sys->i_fd );
439             free( p_sys );
440             return VLC_EGENERIC;
441         }
442         else
443         {
444             /* Number of fragments actually allocated */
445             p_aout->output.p_sys->i_fragstotal = audio_buf.fragstotal;
446
447             /* Maximum duration the soundcard's buffer can hold */
448             p_aout->output.p_sys->max_buffer_duration =
449                 (mtime_t)audio_buf.fragstotal * audio_buf.fragsize * 1000000
450                 / p_aout->output.output.i_bytes_per_frame
451                 / p_aout->output.output.i_rate
452                 * p_aout->output.output.i_frame_length;
453
454             p_aout->output.i_nb_samples = audio_buf.fragsize /
455                 p_aout->output.output.i_bytes_per_frame;
456         }
457
458         aout_VolumeSoftInit( p_aout );
459     }
460
461     p_aout->output.p_sys->b_workaround_buggy_driver =
462         config_GetInt( p_aout, "oss-buggy" );
463
464     /* Create OSS thread and wait for its readiness. */
465     if( vlc_thread_create( p_aout, "aout", OSSThread,
466                            VLC_THREAD_PRIORITY_OUTPUT, VLC_FALSE ) )
467     {
468         msg_Err( p_aout, "cannot create OSS thread (%s)", strerror(errno) );
469         close( p_sys->i_fd );
470         free( p_sys );
471         return VLC_ETHREAD;
472     }
473
474     return VLC_SUCCESS;
475 }
476
477 /*****************************************************************************
478  * Play: nothing to do
479  *****************************************************************************/
480 static void Play( aout_instance_t *p_aout )
481 {
482 }
483
484 /*****************************************************************************
485  * Close: close the dsp audio device
486  *****************************************************************************/
487 static void Close( vlc_object_t * p_this )
488 {
489     aout_instance_t *p_aout = (aout_instance_t *)p_this;
490     struct aout_sys_t * p_sys = p_aout->output.p_sys;
491
492     p_aout->b_die = VLC_TRUE;
493     vlc_thread_join( p_aout );
494     p_aout->b_die = VLC_FALSE;
495
496     ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL );
497     close( p_sys->i_fd );
498
499     free( p_sys );
500 }
501
502 /*****************************************************************************
503  * BufferDuration: buffer status query
504  *****************************************************************************
505  * This function returns the duration in microseconds of the current buffer.
506  *****************************************************************************/
507 static mtime_t BufferDuration( aout_instance_t * p_aout )
508 {
509     struct aout_sys_t * p_sys = p_aout->output.p_sys;
510     audio_buf_info audio_buf;
511     int i_bytes;
512
513     /* Fill the audio_buf_info structure:
514      * - fragstotal: total number of fragments allocated
515      * - fragsize: size of a fragment in bytes
516      * - bytes: available space in bytes (includes partially used fragments)
517      * Note! 'bytes' could be more than fragments*fragsize */
518     ioctl( p_sys->i_fd, SNDCTL_DSP_GETOSPACE, &audio_buf );
519
520     /* calculate number of available fragments (not partially used ones) */
521     i_bytes = (audio_buf.fragstotal * audio_buf.fragsize) - audio_buf.bytes;
522
523     /* Return the fragment duration */
524     return (mtime_t)i_bytes * 1000000
525             / p_aout->output.output.i_bytes_per_frame
526             / p_aout->output.output.i_rate
527             * p_aout->output.output.i_frame_length;
528 }
529
530 /*****************************************************************************
531  * OSSThread: asynchronous thread used to DMA the data to the device
532  *****************************************************************************/
533 static int OSSThread( aout_instance_t * p_aout )
534 {
535     struct aout_sys_t * p_sys = p_aout->output.p_sys;
536     mtime_t next_date = 0;
537
538     while ( !p_aout->b_die )
539     {
540         aout_buffer_t * p_buffer = NULL;
541         int i_tmp, i_size;
542         byte_t * p_bytes;
543
544         if ( p_aout->output.output.i_format != VLC_FOURCC('s','p','d','i') )
545         {
546             mtime_t buffered = BufferDuration( p_aout );
547
548             if( p_aout->output.p_sys->b_workaround_buggy_driver )
549             {
550 #define i_fragstotal p_aout->output.p_sys->i_fragstotal
551                 /* Wait a bit - we don't want our buffer to be full */
552                 if( buffered > (p_aout->output.p_sys->max_buffer_duration
553                                 / i_fragstotal * (i_fragstotal - 1)) )
554                 {
555                     msleep((p_aout->output.p_sys->max_buffer_duration
556                                 / i_fragstotal ));
557                     buffered = BufferDuration( p_aout );
558                 }
559 #undef i_fragstotal
560             }
561
562             /* Next buffer will be played at mdate() + buffered */
563             p_buffer = aout_OutputNextBuffer( p_aout, mdate() + buffered,
564                                               VLC_FALSE );
565
566             if( p_buffer == NULL &&
567                 buffered > ( p_aout->output.p_sys->max_buffer_duration
568                              / p_aout->output.p_sys->i_fragstotal ) )
569             {
570                 /* If we have at least a fragment full, then we can wait a
571                  * little and retry to get a new audio buffer instead of
572                  * playing a blank sample */
573                 msleep( ( p_aout->output.p_sys->max_buffer_duration
574                           / p_aout->output.p_sys->i_fragstotal / 2 ) );
575                 continue;
576             }
577         }
578         else
579         {
580             /* emu10k1 driver does not report Buffer Duration correctly in
581              * passthrough mode so we have to cheat */
582             if( !next_date )
583             {
584                 next_date = mdate();
585             }
586             else
587             {
588                 mtime_t delay = next_date - mdate();
589                 if( delay > AOUT_PTS_TOLERANCE )
590                 {
591                     msleep( delay / 2 );
592                 }
593             }
594             
595             while( !p_aout->b_die && ! ( p_buffer =
596                 aout_OutputNextBuffer( p_aout, next_date, VLC_TRUE ) ) )
597             {
598                 msleep( 1000 );
599                 next_date = mdate();
600             }
601         }
602
603         if ( p_buffer != NULL )
604         {
605             p_bytes = p_buffer->p_buffer;
606             i_size = p_buffer->i_nb_bytes;
607             /* This is theoretical ... we'll see next iteration whether
608              * we're drifting */
609             next_date += p_buffer->end_date - p_buffer->start_date;
610         }
611         else
612         {
613             i_size = FRAME_SIZE / p_aout->output.output.i_frame_length
614                       * p_aout->output.output.i_bytes_per_frame;
615             p_bytes = malloc( i_size );
616             memset( p_bytes, 0, i_size );
617             next_date = 0;
618         }
619
620         i_tmp = write( p_sys->i_fd, p_bytes, i_size );
621
622         if( i_tmp < 0 )
623         {
624             msg_Err( p_aout, "write failed (%s)", strerror(errno) );
625         }
626
627         if ( p_buffer != NULL )
628         {
629             aout_BufferFree( p_buffer );
630         }
631         else
632         {
633             free( p_bytes );
634         }
635     }
636
637     return VLC_SUCCESS;
638 }