]> git.sesse.net Git - vlc/blob - modules/audio_output/alsa.c
auhal: restore compatibility with the 10.5 SDK
[vlc] / modules / audio_output / alsa.c
1 /*****************************************************************************
2  * alsa.c : alsa plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000-2001 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Henri Fallon <henri@videolan.org> - Original Author
8  *          Jeffrey Baker <jwbaker@acm.org> - Port to ALSA 1.0 API
9  *          John Paul Lorenti <jpl31@columbia.edu> - Device selection
10  *          Arnaud de Bossoreille de Ribou <bozo@via.ecp.fr> - S/PDIF and aout3
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <assert.h>
35
36 #include <vlc_common.h>
37 #include <vlc_plugin.h>
38
39 #include <errno.h>                                                 /* ENOMEM */
40 #include <vlc_dialog.h>
41
42 #include <vlc_aout.h>
43 #include <vlc_cpu.h>
44
45 /* ALSA part
46    Note: we use the new API which is available since 0.9.0beta10a. */
47 #define ALSA_PCM_NEW_HW_PARAMS_API
48 #define ALSA_PCM_NEW_SW_PARAMS_API
49 #include <alsa/asoundlib.h>
50 #include <alsa/version.h>
51
52 /*#define ALSA_DEBUG*/
53
54 /*****************************************************************************
55  * aout_sys_t: ALSA audio output method descriptor
56  *****************************************************************************
57  * This structure is part of the audio output thread descriptor.
58  * It describes the ALSA specific properties of an audio device.
59  *****************************************************************************/
60 struct aout_sys_t
61 {
62     snd_pcm_t         * p_snd_pcm;
63     unsigned int                 i_period_time;
64
65 #ifdef ALSA_DEBUG
66     snd_output_t      * p_snd_stderr;
67 #endif
68
69     mtime_t      start_date;
70     vlc_thread_t thread;
71     vlc_sem_t    wait;
72 };
73
74 #define A52_FRAME_NB 1536
75
76 /* These values are in frames.
77    To convert them to a number of bytes you have to multiply them by the
78    number of channel(s) (eg. 2 for stereo) and the size of a sample (eg.
79    2 for int16_t). */
80 #define ALSA_DEFAULT_PERIOD_SIZE        1024
81 #define ALSA_DEFAULT_BUFFER_SIZE        ( ALSA_DEFAULT_PERIOD_SIZE << 8 )
82 #define ALSA_SPDIF_PERIOD_SIZE          A52_FRAME_NB
83 #define ALSA_SPDIF_BUFFER_SIZE          ( ALSA_SPDIF_PERIOD_SIZE << 4 )
84 /* Why << 4 ? --Meuuh */
85 /* Why not ? --Bozo */
86 /* Right. --Meuuh */
87
88 #define DEFAULT_ALSA_DEVICE "default"
89
90 /*****************************************************************************
91  * Local prototypes
92  *****************************************************************************/
93 static int   Open         ( vlc_object_t * );
94 static void  Close        ( vlc_object_t * );
95 static void  Play         ( aout_instance_t * );
96 static void* ALSAThread   ( void * );
97 static void  ALSAFill     ( aout_instance_t * );
98 static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name,
99                                 vlc_value_t newval, vlc_value_t oldval, void *p_unused );
100 static void GetDevices( vlc_object_t *, module_config_t * );
101
102 /*****************************************************************************
103  * Module descriptor
104  *****************************************************************************/
105 static const char *const ppsz_devices[] = { "default" };
106 static const char *const ppsz_devices_text[] = { N_("Default") };
107 vlc_module_begin ()
108     set_shortname( "ALSA" )
109     set_description( N_("ALSA audio output") )
110     set_category( CAT_AUDIO )
111     set_subcategory( SUBCAT_AUDIO_AOUT )
112     add_string( "alsa-audio-device", DEFAULT_ALSA_DEVICE,
113                 N_("ALSA Device Name"), NULL, false )
114         add_deprecated_alias( "alsadev" )   /* deprecated since 0.9.3 */
115         change_string_list( ppsz_devices, ppsz_devices_text, FindDevicesCallback )
116         change_action_add( FindDevicesCallback, N_("Refresh list") )
117
118     set_capability( "audio output", 150 )
119     set_callbacks( Open, Close )
120 vlc_module_end ()
121
122 /*****************************************************************************
123  * Probe: probe the audio device for available formats and channels
124  *****************************************************************************/
125 static int Probe( aout_instance_t * p_aout,
126                   const char * psz_device, const char * psz_iec_device,
127                   int *pi_snd_pcm_format )
128 {
129     struct aout_sys_t * p_sys = p_aout->output.p_sys;
130     vlc_value_t val, text;
131     int i_ret;
132
133     var_Create ( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
134     text.psz_string = _("Audio Device");
135     var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
136
137     /* We'll open the audio device in non blocking mode so we can just exit
138      * when it is already in use, but for the real stuff we'll still use
139      * the blocking mode */
140
141     /* Now test linear PCM capabilities */
142     i_ret = snd_pcm_open( &p_sys->p_snd_pcm, psz_device,
143                           SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK );
144     if( i_ret == 0 )
145     {
146         int i_channels;
147         snd_pcm_hw_params_t * p_hw;
148         snd_pcm_hw_params_alloca (&p_hw);
149
150         if ( snd_pcm_hw_params_any( p_sys->p_snd_pcm, p_hw ) < 0 )
151         {
152             msg_Warn( p_aout, "unable to retrieve initial hardware parameters"
153                               ", disabling linear PCM audio" );
154             snd_pcm_close( p_sys->p_snd_pcm );
155             var_Destroy( p_aout, "audio-device" );
156             return VLC_EGENERIC;
157         }
158
159         if ( snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, p_hw,
160                                            *pi_snd_pcm_format ) < 0 )
161         {
162             int i_snd_rc = -1;
163
164             if( *pi_snd_pcm_format != SND_PCM_FORMAT_S16 )
165             {
166                 *pi_snd_pcm_format = SND_PCM_FORMAT_S16;
167                 i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm,
168                                                     p_hw, *pi_snd_pcm_format );
169             }
170             if ( i_snd_rc < 0 )
171             {
172                 msg_Warn( p_aout, "unable to set stream sample size and "
173                           "word order, disabling linear PCM audio" );
174                 snd_pcm_close( p_sys->p_snd_pcm );
175                 var_Destroy( p_aout, "audio-device" );
176                 return VLC_EGENERIC;
177             }
178         }
179
180         i_channels = aout_FormatNbChannels( &p_aout->output.output );
181
182         while ( i_channels > 0 )
183         {
184             if ( !snd_pcm_hw_params_test_channels( p_sys->p_snd_pcm, p_hw,
185                                                    i_channels ) )
186             {
187                 switch ( i_channels )
188                 {
189                 case 1:
190                     val.i_int = AOUT_VAR_MONO;
191                     text.psz_string = _("Mono");
192                     var_Change( p_aout, "audio-device",
193                                 VLC_VAR_ADDCHOICE, &val, &text );
194                     break;
195                 case 2:
196                     val.i_int = AOUT_VAR_STEREO;
197                     text.psz_string = _("Stereo");
198                     var_Change( p_aout, "audio-device",
199                                 VLC_VAR_ADDCHOICE, &val, &text );
200                     var_Set( p_aout, "audio-device", val );
201                     break;
202                 case 4:
203                     val.i_int = AOUT_VAR_2F2R;
204                     text.psz_string = _("2 Front 2 Rear");
205                     var_Change( p_aout, "audio-device",
206                                 VLC_VAR_ADDCHOICE, &val, &text );
207                     break;
208                 case 6:
209                     val.i_int = AOUT_VAR_5_1;
210                     text.psz_string = (char *)"5.1";
211                     var_Change( p_aout, "audio-device",
212                                 VLC_VAR_ADDCHOICE, &val, &text );
213                     break;
214                 }
215             }
216
217             --i_channels;
218         }
219
220         /* Special case for mono on stereo only boards */
221         i_channels = aout_FormatNbChannels( &p_aout->output.output );
222         var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL );
223         if( val.i_int <= 0 && i_channels == 1 )
224         {
225             if ( !snd_pcm_hw_params_test_channels( p_sys->p_snd_pcm, p_hw, 2 ))
226             {
227                 val.i_int = AOUT_VAR_STEREO;
228                 text.psz_string = (char*)N_("Stereo");
229                 var_Change( p_aout, "audio-device",
230                             VLC_VAR_ADDCHOICE, &val, &text );
231                 var_Set( p_aout, "audio-device", val );
232             }
233         }
234
235         /* Close the previously opened device */
236         snd_pcm_close( p_sys->p_snd_pcm );
237     }
238     else if ( i_ret == -EBUSY )
239     {
240         msg_Warn( p_aout, "audio device: %s is already in use", psz_device );
241     }
242
243     /* Test for S/PDIF device if needed */
244     if ( psz_iec_device )
245     {
246         /* Opening the device should be enough */
247         i_ret = snd_pcm_open( &p_sys->p_snd_pcm, psz_iec_device,
248                               SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK );
249         if( i_ret == 0 )
250         {
251             val.i_int = AOUT_VAR_SPDIF;
252             text.psz_string = (char*)N_("A/52 over S/PDIF");
253             var_Change( p_aout, "audio-device",
254                         VLC_VAR_ADDCHOICE, &val, &text );
255             if( var_InheritBool( p_aout, "spdif" ) )
256                 var_Set( p_aout, "audio-device", val );
257
258             snd_pcm_close( p_sys->p_snd_pcm );
259         }
260         else if ( i_ret == -EBUSY )
261         {
262             msg_Warn( p_aout, "audio device: %s is already in use",
263                       psz_iec_device );
264         }
265     }
266
267     var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL );
268 #if (SND_LIB_VERSION <= 0x010015)
269 # warning Please update alsa-lib to version > 1.0.21a.
270     var_Create( p_aout->p_libvlc, "alsa-working", VLC_VAR_BOOL );
271     if( val.i_int <= 0 )
272     {
273         if( var_GetBool( p_aout->p_libvlc, "alsa-working" ) )
274             dialog_Fatal( p_aout, "ALSA version problem",
275                 "VLC failed to re-initialize your sound output device.\n"
276                 "Please update alsa-lib to version 1.0.22 or higher "
277                 "to fix this issue." );
278     }
279     else
280         var_SetBool( p_aout->p_libvlc, "alsa-working", true );
281 #endif
282     if( val.i_int <= 0 )
283     {
284         /* Probe() has failed. */
285 #if (SND_LIB_VERSION <= 0x010017)
286 # warning Please update alsa-lib to version > 1.0.23.
287         var_Create( p_aout->p_libvlc, "alsa-broken", VLC_VAR_BOOL );
288         if( !var_GetBool( p_aout->p_libvlc, "alsa-broken" ) )
289         {
290             var_SetBool( p_aout->p_libvlc, "alsa-broken", true );
291             dialog_Fatal( p_aout, "Potential ALSA version problem",
292                 "VLC failed to initialize your sound output device (if any).\n"
293                 "Please update alsa-lib to version 1.0.24 or higher "
294                 "to try to fix this issue." );
295         }
296 #endif
297         msg_Dbg( p_aout, "failed to find a usable ALSA configuration" );
298         var_Destroy( p_aout, "audio-device" );
299         GetDevices( VLC_OBJECT(p_aout), NULL );
300         return VLC_EGENERIC;
301     }
302
303     /* Add final settings to the variable */
304     var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
305     var_TriggerCallback( p_aout, "intf-change" );
306     return VLC_SUCCESS;
307 }
308
309 /*****************************************************************************
310  * Open: create a handle and open an alsa device
311  *****************************************************************************
312  * This function opens an alsa device, through the alsa API.
313  *
314  * Note: the only heap-allocated string is psz_device. All the other pointers
315  * are references to psz_device or to stack-allocated data.
316  *****************************************************************************/
317 static int Open( vlc_object_t *p_this )
318 {
319     aout_instance_t * p_aout = (aout_instance_t *)p_this;
320
321     /* Allocate structures */
322     aout_sys_t * p_sys = malloc( sizeof( aout_sys_t ) );
323     if( p_sys == NULL )
324         return VLC_ENOMEM;
325     p_aout->output.p_sys = p_sys;
326
327     /* Get device name */
328     char *psz_device = var_InheritString( p_aout, "alsa-audio-device" );
329     if( unlikely(psz_device == NULL) )
330     {
331         free( p_sys );
332         return VLC_EGENERIC;
333     }
334
335     /* Choose the IEC device for S/PDIF output:
336        if the device is overridden by the user then it will be the one
337        otherwise we compute the default device based on the output format. */
338     char *psz_iec_device = NULL;
339     if( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
340     {
341         if( !strcmp( psz_device, DEFAULT_ALSA_DEVICE ) )
342         {
343             unsigned aes3;
344
345             switch( p_aout->output.output.i_rate )
346             {
347               case 48000:
348                 aes3 = IEC958_AES3_CON_FS_48000;
349                 break;
350               case 44100:
351                 aes3 = IEC958_AES3_CON_FS_44100;
352                 break;
353               default:
354                 aes3 = IEC958_AES3_CON_FS_32000;
355                 break;
356             }
357
358             if( asprintf( &psz_iec_device,
359                           "iec958:AES0=0x%x,AES1=0x%x,AES2=0x%x,AES3=0x%x",
360                           IEC958_AES0_CON_EMPHASIS_NONE | IEC958_AES0_NONAUDIO,
361                           IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
362                           0, aes3 ) == -1 )
363             {
364                 free( psz_device );
365                 free( p_sys );
366                 return VLC_ENOMEM;
367             }
368         }
369         else
370             psz_iec_device = strdup( psz_device );
371     }
372
373     /* Choose the linear PCM format (read the comment above about FPU
374        and float32) */
375     int i_snd_pcm_format; /* Audio format for ALSA's data */
376     if( HAVE_FPU )
377         i_snd_pcm_format = SND_PCM_FORMAT_FLOAT;
378     else
379         i_snd_pcm_format = SND_PCM_FORMAT_S16;
380
381     /* If the variable doesn't exist then it's the first time we're called
382        and we have to probe the available audio formats and channels */
383     if( var_Type( p_aout, "audio-device" ) == 0
384      && Probe( p_aout, psz_device, psz_iec_device, &i_snd_pcm_format ) )
385     {
386          free( psz_iec_device );
387          free( psz_device );
388          free( p_sys );
389          return VLC_EGENERIC;
390     }
391
392     bool spdif = false;
393     switch( var_GetInteger( p_aout, "audio-device") )
394     {
395       case AOUT_VAR_5_1:
396         p_aout->output.output.i_physical_channels
397             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
398                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
399                | AOUT_CHAN_LFE;
400         free( psz_device );
401         psz_device = strdup( "surround51" );
402         break;
403       case AOUT_VAR_2F2R:
404         p_aout->output.output.i_physical_channels
405             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
406                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
407         free( psz_device );
408         psz_device = strdup( "surround40" );
409         break;
410     case AOUT_VAR_STEREO:
411         p_aout->output.output.i_physical_channels
412             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
413         break;
414     case AOUT_VAR_MONO:
415         p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
416         break;
417     case AOUT_VAR_SPDIF:
418         spdif = true;
419         free( psz_device );
420         psz_device = psz_iec_device;
421         psz_iec_device = NULL;
422         break;
423     default:
424         /* This should not happen ! */
425         msg_Err( p_aout, "cannot find audio-device" );
426         free( psz_iec_device );
427         free( psz_device );
428         free( p_sys );
429         return VLC_EGENERIC;
430     }
431
432 #ifdef ALSA_DEBUG
433     snd_output_stdio_attach( &p_sys->p_snd_stderr, stderr, 0 );
434 #endif
435
436     /* Open the device */
437     msg_Dbg( p_aout, "opening ALSA device `%s'", psz_device );
438     for( unsigned i = 10; i; i-- )
439     {
440         int val = snd_pcm_open( &p_sys->p_snd_pcm, psz_device,
441                                 SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK );
442         if( val == 0 )
443             break; /* success! */
444
445         if( val != -EBUSY )
446         {
447             msg_Err( p_aout, "cannot open ALSA device `%s' (%s)",
448                      psz_device, snd_strerror( val ) );
449             dialog_Fatal( p_aout, _("Audio output failed"),
450                           _("VLC could not open the ALSA device \"%s\" (%s)."),
451                           psz_device, snd_strerror( val ) );
452             free( psz_device );
453             free( p_sys );
454             return VLC_EGENERIC;
455         }
456
457         /* Since it seems snd_pcm_close hasn't really released the device at
458           the time it returns, probe if the device is available in loop for 1s.
459           We cannot use blocking mode since the we would wait indefinitely when
460           switching from a dmx device to surround51. */
461         if( i == 1 )
462         {
463             msg_Err( p_aout, "audio device %s is already in use",
464                      psz_device );
465             dialog_Fatal( p_aout, _("Audio output failed"),
466                           _("The audio device \"%s\" is already in use."),
467                           psz_device );
468             free( psz_device );
469             free( p_sys );
470             return VLC_EGENERIC;
471         }
472         msleep( CLOCK_FREQ / 10 );
473     }
474     free( psz_device );
475
476     /* We want blocking mode */
477     snd_pcm_nonblock( p_sys->p_snd_pcm, 0 );
478
479     snd_pcm_uframes_t i_buffer_size;
480     snd_pcm_uframes_t i_period_size;
481     int i_channels;
482
483     if( spdif )
484     {
485         i_buffer_size = ALSA_SPDIF_BUFFER_SIZE;
486         i_snd_pcm_format = SND_PCM_FORMAT_S16;
487         i_channels = 2;
488
489         p_aout->output.i_nb_samples = i_period_size = ALSA_SPDIF_PERIOD_SIZE;
490         p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
491         p_aout->output.output.i_frame_length = A52_FRAME_NB;
492
493         aout_VolumeNoneInit( p_aout );
494     }
495     else
496     {
497         i_buffer_size = ALSA_DEFAULT_BUFFER_SIZE;
498         i_channels = aout_FormatNbChannels( &p_aout->output.output );
499
500         p_aout->output.i_nb_samples = i_period_size = ALSA_DEFAULT_PERIOD_SIZE;
501
502         aout_VolumeSoftInit( p_aout );
503     }
504
505     p_aout->output.pf_play = Play;
506
507     snd_pcm_hw_params_t *p_hw;
508     snd_pcm_sw_params_t *p_sw;
509
510     snd_pcm_hw_params_alloca(&p_hw);
511     snd_pcm_sw_params_alloca(&p_sw);
512
513     /* Due to some bugs in alsa with some drivers, we need to retry in s16l
514        if snd_pcm_hw_params fails in fl32 */
515     int val;
516 retry:
517     /* Get Initial hardware parameters */
518     val = snd_pcm_hw_params_any( p_sys->p_snd_pcm, p_hw );
519     if( val < 0 )
520     {
521         msg_Err( p_aout, "unable to retrieve hardware parameters (%s)",
522                 snd_strerror( val ) );
523         goto error;
524     }
525
526     /* Set format. */
527     val = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, p_hw,
528                                         i_snd_pcm_format );
529     if( val < 0 )
530     {
531         if( i_snd_pcm_format != SND_PCM_FORMAT_S16 )
532         {
533             i_snd_pcm_format = SND_PCM_FORMAT_S16;
534             val = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm,
535                                                     p_hw, i_snd_pcm_format );
536         }
537         if ( val < 0 )
538         {
539             msg_Err( p_aout, "unable to set stream sample size and "
540                      "word order (%s)", snd_strerror( val ) );
541             goto error;
542         }
543     }
544
545     vlc_fourcc_t i_vlc_pcm_format;
546     if( spdif )
547         i_vlc_pcm_format = VLC_CODEC_SPDIFL;
548     else
549         switch( i_snd_pcm_format )
550         {
551           case SND_PCM_FORMAT_FLOAT:
552             i_vlc_pcm_format = VLC_CODEC_FL32;
553             break;
554           case SND_PCM_FORMAT_S16:
555             i_vlc_pcm_format = VLC_CODEC_S16N;
556             break;
557           default:
558             assert(0);
559         }
560     p_aout->output.output.i_format = i_vlc_pcm_format;
561
562     val = snd_pcm_hw_params_set_access( p_sys->p_snd_pcm, p_hw,
563                                         SND_PCM_ACCESS_RW_INTERLEAVED );
564     if( val < 0 )
565     {
566         msg_Err( p_aout, "unable to set interleaved stream format (%s)",
567                  snd_strerror( val ) );
568         goto error;
569     }
570
571     /* Set channels. */
572     val = snd_pcm_hw_params_set_channels( p_sys->p_snd_pcm, p_hw, i_channels );
573     if( val < 0 )
574     {
575         msg_Err( p_aout, "unable to set number of output channels (%s)",
576                  snd_strerror( val ) );
577         goto error;
578     }
579
580     /* Set rate. */
581     unsigned i_old_rate = p_aout->output.output.i_rate;
582     val = snd_pcm_hw_params_set_rate_near( p_sys->p_snd_pcm, p_hw,
583                                            &p_aout->output.output.i_rate,
584                                            NULL );
585     if( val < 0 || p_aout->output.output.i_rate != i_old_rate )
586     {
587         msg_Warn( p_aout, "The rate %d Hz is not supported by your " \
588                   "hardware. Using %d Hz instead.\n", i_old_rate, \
589                   p_aout->output.output.i_rate );
590     }
591
592     /* Set period size. */
593     val = snd_pcm_hw_params_set_period_size_near( p_sys->p_snd_pcm, p_hw,
594                                                   &i_period_size, NULL );
595     if( val < 0 )
596     {
597         msg_Err( p_aout, "unable to set period size (%s)",
598                  snd_strerror( val ) );
599         goto error;
600     }
601     p_aout->output.i_nb_samples = i_period_size;
602
603     /* Set buffer size. */
604     val = snd_pcm_hw_params_set_buffer_size_near( p_sys->p_snd_pcm, p_hw,
605                                                   &i_buffer_size );
606     if( val )
607     {
608         msg_Err( p_aout, "unable to set buffer size (%s)",
609                  snd_strerror( val ) );
610         goto error;
611     }
612
613     /* Commit hardware parameters. */
614     val = snd_pcm_hw_params( p_sys->p_snd_pcm, p_hw );
615     if( val < 0 )
616     {
617         if( i_snd_pcm_format == SND_PCM_FORMAT_FLOAT )
618         {
619             i_snd_pcm_format = SND_PCM_FORMAT_S16;
620             p_aout->output.output.i_format = VLC_CODEC_S16N;
621             msg_Warn( p_aout, "unable to commit hardware configuration "
622                      "with fl32 samples (%s). Retrying with s16l.",
623                      snd_strerror( val ) );
624             goto retry;
625         }
626         msg_Err( p_aout, "unable to commit hardware configuration (%s)",
627                  snd_strerror( val ) );
628         goto error;
629     }
630
631     val = snd_pcm_hw_params_get_period_time( p_hw, &p_sys->i_period_time,
632                                              NULL );
633     if( val < 0 )
634     {
635         msg_Err( p_aout, "unable to get period time (%s)",
636                  snd_strerror( val ) );
637         goto error;
638     }
639
640     /* Get Initial software parameters */
641     snd_pcm_sw_params_current( p_sys->p_snd_pcm, p_sw );
642
643     snd_pcm_sw_params_set_avail_min( p_sys->p_snd_pcm, p_sw,
644                                      p_aout->output.i_nb_samples );
645     /* start playing when one period has been written */
646     val = snd_pcm_sw_params_set_start_threshold( p_sys->p_snd_pcm, p_sw,
647                                                  ALSA_DEFAULT_PERIOD_SIZE);
648     if( val < 0 )
649     {
650         msg_Err( p_aout, "unable to set start threshold (%s)",
651                  snd_strerror( val ) );
652         goto error;
653     }
654
655     /* Commit software parameters. */
656     if ( snd_pcm_sw_params( p_sys->p_snd_pcm, p_sw ) < 0 )
657     {
658         msg_Err( p_aout, "unable to set software configuration" );
659         goto error;
660     }
661
662 #ifdef ALSA_DEBUG
663     snd_output_printf( p_sys->p_snd_stderr, "\nALSA hardware setup:\n\n" );
664     snd_pcm_dump_hw_setup( p_sys->p_snd_pcm, p_sys->p_snd_stderr );
665     snd_output_printf( p_sys->p_snd_stderr, "\nALSA software setup:\n\n" );
666     snd_pcm_dump_sw_setup( p_sys->p_snd_pcm, p_sys->p_snd_stderr );
667     snd_output_printf( p_sys->p_snd_stderr, "\n" );
668 #endif
669
670     p_sys->start_date = 0;
671     vlc_sem_init( &p_sys->wait, 0 );
672
673     /* Create ALSA thread and wait for its readiness. */
674     if( vlc_clone( &p_sys->thread, ALSAThread, p_aout,
675                    VLC_THREAD_PRIORITY_OUTPUT ) )
676     {
677         msg_Err( p_aout, "cannot create ALSA thread (%m)" );
678         vlc_sem_destroy( &p_sys->wait );
679         goto error;
680     }
681
682     return 0;
683
684 error:
685     snd_pcm_close( p_sys->p_snd_pcm );
686 #ifdef ALSA_DEBUG
687     snd_output_close( p_sys->p_snd_stderr );
688 #endif
689     free( p_sys );
690     return VLC_EGENERIC;
691 }
692
693 static void PlayIgnore( aout_instance_t *p_aout )
694 {   /* Already playing - nothing to do */
695     (void) p_aout;
696 }
697
698 /*****************************************************************************
699  * Play: start playback
700  *****************************************************************************/
701 static void Play( aout_instance_t *p_aout )
702 {
703     p_aout->output.pf_play = PlayIgnore;
704
705     /* get the playing date of the first aout buffer */
706     p_aout->output.p_sys->start_date =
707         aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
708
709     /* wake up the audio output thread */
710     sem_post( &p_aout->output.p_sys->wait );
711 }
712
713 /*****************************************************************************
714  * Close: close the ALSA device
715  *****************************************************************************/
716 static void Close( vlc_object_t *p_this )
717 {
718     aout_instance_t *p_aout = (aout_instance_t *)p_this;
719     struct aout_sys_t * p_sys = p_aout->output.p_sys;
720
721     /* Make sure that the thread will stop once it is waken up */
722     vlc_cancel( p_sys->thread );
723     vlc_join( p_sys->thread, NULL );
724     vlc_sem_destroy( &p_sys->wait );
725
726     snd_pcm_drop( p_sys->p_snd_pcm );
727     snd_pcm_close( p_sys->p_snd_pcm );
728 #ifdef ALSA_DEBUG
729     snd_output_close( p_sys->p_snd_stderr );
730 #endif
731     free( p_sys );
732 }
733
734 /*****************************************************************************
735  * ALSAThread: asynchronous thread used to DMA the data to the device
736  *****************************************************************************/
737 static void* ALSAThread( void *data )
738 {
739     aout_instance_t * p_aout = data;
740     struct aout_sys_t * p_sys = p_aout->output.p_sys;
741
742     /* Wait for the exact time to start playing (avoids resampling) */
743     vlc_sem_wait( &p_sys->wait );
744     mwait( p_sys->start_date - AOUT_PTS_TOLERANCE / 4 );
745
746     for(;;)
747         ALSAFill( p_aout );
748
749     assert(0);
750 }
751
752 /*****************************************************************************
753  * ALSAFill: function used to fill the ALSA buffer as much as possible
754  *****************************************************************************/
755 static void ALSAFill( aout_instance_t * p_aout )
756 {
757     struct aout_sys_t * p_sys = p_aout->output.p_sys;
758     snd_pcm_t *p_pcm = p_sys->p_snd_pcm;
759     snd_pcm_status_t * p_status;
760     int i_snd_rc;
761     mtime_t next_date;
762
763     int canc = vlc_savecancel();
764     /* Fill in the buffer until space or audio output buffer shortage */
765
766     /* Get the status */
767     snd_pcm_status_alloca(&p_status);
768     i_snd_rc = snd_pcm_status( p_pcm, p_status );
769     if( i_snd_rc < 0 )
770     {
771         msg_Err( p_aout, "cannot get device status" );
772         goto error;
773     }
774
775     /* Handle buffer underruns and get the status again */
776     if( snd_pcm_status_get_state( p_status ) == SND_PCM_STATE_XRUN )
777     {
778         /* Prepare the device */
779         i_snd_rc = snd_pcm_prepare( p_pcm );
780         if( i_snd_rc )
781         {
782             msg_Err( p_aout, "cannot recover from buffer underrun" );
783             goto error;
784         }
785
786         msg_Dbg( p_aout, "recovered from buffer underrun" );
787
788         /* Get the new status */
789         i_snd_rc = snd_pcm_status( p_pcm, p_status );
790         if( i_snd_rc < 0 )
791         {
792             msg_Err( p_aout, "cannot get device status after recovery" );
793             goto error;
794         }
795
796         /* Underrun, try to recover as quickly as possible */
797         next_date = mdate();
798     }
799     else
800     {
801         /* Here the device should be in RUNNING state, p_status is valid. */
802         snd_pcm_sframes_t delay = snd_pcm_status_get_delay( p_status );
803         if( delay == 0 ) /* workaround buggy alsa drivers */
804             if( snd_pcm_delay( p_pcm, &delay ) < 0 )
805                 delay = 0; /* FIXME: use a positive minimal delay */
806
807         size_t i_bytes = snd_pcm_frames_to_bytes( p_pcm, delay );
808         mtime_t delay_us = CLOCK_FREQ * i_bytes
809                 / p_aout->output.output.i_bytes_per_frame
810                 / p_aout->output.output.i_rate
811                 * p_aout->output.output.i_frame_length;
812
813 #ifdef ALSA_DEBUG
814         snd_pcm_state_t state = snd_pcm_status_get_state( p_status );
815         if( state != SND_PCM_STATE_RUNNING )
816             msg_Err( p_aout, "pcm status (%d) != RUNNING", state );
817
818         msg_Dbg( p_aout, "Delay is %ld frames (%zu bytes)", delay, i_bytes );
819
820         msg_Dbg( p_aout, "Bytes per frame: %d", p_aout->output.output.i_bytes_per_frame );
821         msg_Dbg( p_aout, "Rate: %d", p_aout->output.output.i_rate );
822         msg_Dbg( p_aout, "Frame length: %d", p_aout->output.output.i_frame_length );
823         msg_Dbg( p_aout, "Next date: in %"PRId64" microseconds", delay_us );
824 #endif
825         next_date = mdate() + delay_us;
826     }
827
828     block_t *p_buffer = aout_OutputNextBuffer( p_aout, next_date,
829            (p_aout->output.output.i_format ==  VLC_CODEC_SPDIFL) );
830
831     /* Audio output buffer shortage -> stop the fill process and wait */
832     if( p_buffer == NULL )
833         goto error;
834
835     block_cleanup_push( p_buffer );
836     for (;;)
837     {
838         int n = snd_pcm_poll_descriptors_count(p_pcm);
839         struct pollfd ufd[n];
840         unsigned short revents;
841
842         snd_pcm_poll_descriptors(p_pcm, ufd, n);
843         do
844         {
845             vlc_restorecancel(canc);
846             poll(ufd, n, -1);
847             canc = vlc_savecancel();
848             snd_pcm_poll_descriptors_revents(p_pcm, ufd, n, &revents);
849         }
850         while(!revents);
851
852         if(revents & POLLOUT)
853         {
854             i_snd_rc = snd_pcm_writei( p_pcm, p_buffer->p_buffer,
855                                        p_buffer->i_nb_samples );
856             if( i_snd_rc != -ESTRPIPE )
857                 break;
858         }
859
860         /* a suspend event occurred
861          * (stream is suspended and waiting for an application recovery) */
862         msg_Dbg( p_aout, "entering in suspend mode, trying to resume..." );
863
864         while( ( i_snd_rc = snd_pcm_resume( p_pcm ) ) == -EAGAIN )
865         {
866             vlc_restorecancel(canc);
867             msleep(CLOCK_FREQ); /* device still suspended, wait... */
868             canc = vlc_savecancel();
869         }
870
871         if( i_snd_rc < 0 )
872             /* Device does not support resuming, restart it */
873             i_snd_rc = snd_pcm_prepare( p_pcm );
874
875     }
876
877     if( i_snd_rc < 0 )
878         msg_Err( p_aout, "cannot write: %s", snd_strerror( i_snd_rc ) );
879
880     vlc_restorecancel(canc);
881     vlc_cleanup_run();
882     return;
883
884 error:
885     if( i_snd_rc < 0 )
886         msg_Err( p_aout, "ALSA error: %s", snd_strerror( i_snd_rc ) );
887
888     vlc_restorecancel(canc);
889     msleep(p_sys->i_period_time / 2);
890 }
891
892 /*****************************************************************************
893  * config variable callback
894  *****************************************************************************/
895 static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name,
896                                vlc_value_t newval, vlc_value_t oldval, void *p_unused )
897 {
898     module_config_t *p_item;
899     (void)newval;
900     (void)oldval;
901     (void)p_unused;
902
903     p_item = config_FindConfig( p_this, psz_name );
904     if( !p_item ) return VLC_SUCCESS;
905
906     /* Clear-up the current list */
907     if( p_item->i_list )
908     {
909         int i;
910
911         /* Keep the first entrie */
912         for( i = 1; i < p_item->i_list; i++ )
913         {
914             free( (char *)p_item->ppsz_list[i] );
915             free( (char *)p_item->ppsz_list_text[i] );
916         }
917         /* TODO: Remove when no more needed */
918         p_item->ppsz_list[i] = NULL;
919         p_item->ppsz_list_text[i] = NULL;
920     }
921     p_item->i_list = 1;
922
923     GetDevices( p_this, p_item );
924
925     /* Signal change to the interface */
926     p_item->b_dirty = true;
927
928     return VLC_SUCCESS;
929 }
930
931
932 static void GetDevices (vlc_object_t *obj, module_config_t *item)
933 {
934     void **hints;
935
936     msg_Dbg(obj, "Available ALSA PCM devices:");
937
938     if (snd_device_name_hint(-1, "pcm", &hints) < 0)
939         return;
940
941     for (size_t i = 0; hints[i] != NULL; i++)
942     {
943         void *hint = hints[i];
944         char *name = snd_device_name_get_hint(hint, "NAME");
945         if (unlikely(name == NULL))
946             continue;
947
948         char *desc = snd_device_name_get_hint(hint, "DESC");
949         if (desc != NULL)
950             for (char *lf = strchr(desc, '\n'); lf; lf = strchr(lf, '\n'))
951                  *lf = ' ';
952         msg_Dbg(obj, " %s (%s)", (desc != NULL) ? desc : name, name);
953
954         if (item != NULL)
955         {
956             item->ppsz_list = xrealloc(item->ppsz_list,
957                                        (item->i_list + 2) * sizeof(char *));
958             item->ppsz_list_text = xrealloc(item->ppsz_list_text,
959                                           (item->i_list + 2) * sizeof(char *));
960             item->ppsz_list[item->i_list] = name;
961             if (desc == NULL)
962                 desc = strdup(name);
963             item->ppsz_list_text[item->i_list] = desc;
964             item->i_list++;
965         }
966         else
967         {
968             free(desc);
969             free(name);
970         }
971     }
972
973     snd_device_name_free_hint(hints);
974
975     if (item != NULL)
976     {
977         item->ppsz_list[item->i_list] = NULL;
978         item->ppsz_list_text[item->i_list] = NULL;
979     }
980 }