1 /*****************************************************************************
2 * alsa.c : alsa plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2000-2001 the VideoLAN team
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
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.
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.
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 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
36 #include <vlc_common.h>
37 #include <vlc_plugin.h>
39 #include <errno.h> /* ENOMEM */
40 #include <vlc_dialog.h>
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>
52 /*#define ALSA_DEBUG*/
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 *****************************************************************************/
62 snd_pcm_t * p_snd_pcm;
63 unsigned int i_period_time;
66 snd_output_t * p_snd_stderr;
74 #define A52_FRAME_NB 1536
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.
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 */
88 #define DEFAULT_ALSA_DEVICE N_("default")
90 /*****************************************************************************
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 GetDevicesForCard( vlc_object_t *, module_config_t *, int card );
101 static void GetDevices( vlc_object_t *, module_config_t * );
103 /*****************************************************************************
105 *****************************************************************************/
106 static const char *const ppsz_devices[] = { "default" };
107 static const char *const ppsz_devices_text[] = { N_("Default") };
109 set_shortname( "ALSA" )
110 set_description( N_("ALSA audio output") )
111 set_category( CAT_AUDIO )
112 set_subcategory( SUBCAT_AUDIO_AOUT )
113 add_string( "alsa-audio-device", DEFAULT_ALSA_DEVICE, aout_FindAndRestart,
114 N_("ALSA Device Name"), NULL, false )
115 add_deprecated_alias( "alsadev" ) /* deprecated since 0.9.3 */
116 change_string_list( ppsz_devices, ppsz_devices_text, FindDevicesCallback )
117 change_action_add( FindDevicesCallback, N_("Refresh list") )
119 set_capability( "audio output", 150 )
120 set_callbacks( Open, Close )
123 /*****************************************************************************
124 * Probe: probe the audio device for available formats and channels
125 *****************************************************************************/
126 static void Probe( aout_instance_t * p_aout,
127 const char * psz_device, const char * psz_iec_device,
128 int *pi_snd_pcm_format )
130 struct aout_sys_t * p_sys = p_aout->output.p_sys;
131 vlc_value_t val, text;
134 var_Create ( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
135 text.psz_string = _("Audio Device");
136 var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
138 /* We'll open the audio device in non blocking mode so we can just exit
139 * when it is already in use, but for the real stuff we'll still use
140 * the blocking mode */
142 /* Now test linear PCM capabilities */
143 if ( !(i_ret = snd_pcm_open( &p_sys->p_snd_pcm, psz_device,
144 SND_PCM_STREAM_PLAYBACK,
145 SND_PCM_NONBLOCK ) ) )
148 snd_pcm_hw_params_t * p_hw;
149 snd_pcm_hw_params_alloca (&p_hw);
151 if ( snd_pcm_hw_params_any( p_sys->p_snd_pcm, p_hw ) < 0 )
153 msg_Warn( p_aout, "unable to retrieve initial hardware parameters"
154 ", disabling linear PCM audio" );
155 snd_pcm_close( p_sys->p_snd_pcm );
156 var_Destroy( p_aout, "audio-device" );
160 if ( snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, p_hw,
161 *pi_snd_pcm_format ) < 0 )
165 if( *pi_snd_pcm_format != SND_PCM_FORMAT_S16 )
167 *pi_snd_pcm_format = SND_PCM_FORMAT_S16;
168 i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm,
169 p_hw, *pi_snd_pcm_format );
173 msg_Warn( p_aout, "unable to set stream sample size and "
174 "word order, disabling linear PCM audio" );
175 snd_pcm_close( p_sys->p_snd_pcm );
176 var_Destroy( p_aout, "audio-device" );
181 i_channels = aout_FormatNbChannels( &p_aout->output.output );
183 while ( i_channels > 0 )
185 if ( !snd_pcm_hw_params_test_channels( p_sys->p_snd_pcm, p_hw,
188 switch ( i_channels )
191 val.i_int = AOUT_VAR_MONO;
192 text.psz_string = _("Mono");
193 var_Change( p_aout, "audio-device",
194 VLC_VAR_ADDCHOICE, &val, &text );
197 val.i_int = AOUT_VAR_STEREO;
198 text.psz_string = _("Stereo");
199 var_Change( p_aout, "audio-device",
200 VLC_VAR_ADDCHOICE, &val, &text );
201 var_Set( p_aout, "audio-device", val );
204 val.i_int = AOUT_VAR_2F2R;
205 text.psz_string = _("2 Front 2 Rear");
206 var_Change( p_aout, "audio-device",
207 VLC_VAR_ADDCHOICE, &val, &text );
210 val.i_int = AOUT_VAR_5_1;
211 text.psz_string = "5.1";
212 var_Change( p_aout, "audio-device",
213 VLC_VAR_ADDCHOICE, &val, &text );
221 /* Special case for mono on stereo only boards */
222 i_channels = aout_FormatNbChannels( &p_aout->output.output );
223 var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL );
224 if( val.i_int <= 0 && i_channels == 1 )
226 if ( !snd_pcm_hw_params_test_channels( p_sys->p_snd_pcm, p_hw, 2 ))
228 val.i_int = AOUT_VAR_STEREO;
229 text.psz_string = (char*)N_("Stereo");
230 var_Change( p_aout, "audio-device",
231 VLC_VAR_ADDCHOICE, &val, &text );
232 var_Set( p_aout, "audio-device", val );
236 /* Close the previously opened device */
237 snd_pcm_close( p_sys->p_snd_pcm );
239 else if ( i_ret == -EBUSY )
241 msg_Warn( p_aout, "audio device: %s is already in use", psz_device );
244 /* Test for S/PDIF device if needed */
245 if ( psz_iec_device )
247 /* Opening the device should be enough */
248 if ( !(i_ret = snd_pcm_open( &p_sys->p_snd_pcm, psz_iec_device,
249 SND_PCM_STREAM_PLAYBACK,
250 SND_PCM_NONBLOCK ) ) )
252 val.i_int = AOUT_VAR_SPDIF;
253 text.psz_string = (char*)N_("A/52 over S/PDIF");
254 var_Change( p_aout, "audio-device",
255 VLC_VAR_ADDCHOICE, &val, &text );
256 if( var_InheritBool( p_aout, "spdif" ) )
257 var_Set( p_aout, "audio-device", val );
259 snd_pcm_close( p_sys->p_snd_pcm );
261 else if ( i_ret == -EBUSY )
263 msg_Warn( p_aout, "audio device: %s is already in use",
268 var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL );
269 #if (SND_LIB_VERSION <= 0x010015)
270 # warning Please update alsa-lib to version > 1.0.21a.
271 var_Create( p_aout->p_libvlc, "alsa-working", VLC_VAR_BOOL );
274 if( var_GetBool( p_aout->p_libvlc, "alsa-working" ) )
275 dialog_Fatal( p_aout, "ALSA version problem",
276 "VLC failed to re-initialize your sound output device.\n"
277 "Please update alsa-lib to version 1.0.22 or higher "
278 "to fix this issue." );
281 var_SetBool( p_aout->p_libvlc, "alsa-working", true );
285 /* Probe() has failed. */
286 #if (SND_LIB_VERSION <= 0x010017)
287 # warning Please update alsa-lib to version > 1.0.23.
288 var_Create( p_aout->p_libvlc, "alsa-broken", VLC_VAR_BOOL );
289 if( !var_GetBool( p_aout->p_libvlc, "alsa-broken" ) )
291 var_SetBool( p_aout->p_libvlc, "alsa-broken", true );
292 dialog_Fatal( p_aout, "Potential ALSA version problem",
293 "VLC failed to initialize your sound output device (if any).\n"
294 "Please update alsa-lib to version 1.0.24 or higher "
295 "to try to fix this issue." );
298 msg_Dbg( p_aout, "failed to find a usable ALSA configuration" );
299 var_Destroy( p_aout, "audio-device" );
300 GetDevices( VLC_OBJECT(p_aout), NULL );
304 /* Add final settings to the variable */
305 var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
306 var_SetBool( p_aout, "intf-change", true );
309 /*****************************************************************************
310 * Open: create a handle and open an alsa device
311 *****************************************************************************
312 * This function opens an alsa device, through the alsa API.
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 )
319 aout_instance_t * p_aout = (aout_instance_t *)p_this;
320 struct aout_sys_t * p_sys;
323 char psz_default_iec_device[128]; /* Buffer used to store the default
325 char * psz_device, * psz_iec_device; /* device names for PCM and S/PDIF
328 int i_vlc_pcm_format; /* Audio format for VLC's data */
329 int i_snd_pcm_format; /* Audio format for ALSA's data */
331 snd_pcm_uframes_t i_buffer_size = 0;
332 snd_pcm_uframes_t i_period_size = 0;
335 snd_pcm_hw_params_t *p_hw;
336 snd_pcm_sw_params_t *p_sw;
339 unsigned int i_old_rate;
342 /* Allocate structures */
343 p_aout->output.p_sys = p_sys = malloc( sizeof( aout_sys_t ) );
347 /* Get device name */
348 psz_device = var_InheritString( p_aout, "alsa-audio-device" );
349 if( unlikely(psz_device == NULL) )
351 psz_device = strdup( DEFAULT_ALSA_DEVICE );
352 if( unlikely(psz_device == NULL) )
359 /* Choose the IEC device for S/PDIF output:
360 if the device is overriden by the user then it will be the one
361 otherwise we compute the default device based on the output format. */
362 if( AOUT_FMT_NON_LINEAR( &p_aout->output.output )
363 && !strcmp( psz_device, DEFAULT_ALSA_DEVICE ) )
365 snprintf( psz_default_iec_device, sizeof(psz_default_iec_device),
366 "iec958:AES0=0x%x,AES1=0x%x,AES2=0x%x,AES3=0x%x",
367 IEC958_AES0_CON_EMPHASIS_NONE | IEC958_AES0_NONAUDIO,
368 IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
370 ( p_aout->output.output.i_rate == 48000 ?
371 IEC958_AES3_CON_FS_48000 :
372 ( p_aout->output.output.i_rate == 44100 ?
373 IEC958_AES3_CON_FS_44100 : IEC958_AES3_CON_FS_32000 ) ) );
374 psz_iec_device = psz_default_iec_device;
376 else if( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
378 psz_iec_device = psz_device;
382 psz_iec_device = NULL;
385 /* Choose the linear PCM format (read the comment above about FPU
389 i_vlc_pcm_format = VLC_CODEC_FL32;
390 i_snd_pcm_format = SND_PCM_FORMAT_FLOAT;
394 i_vlc_pcm_format = VLC_CODEC_S16N;
395 i_snd_pcm_format = SND_PCM_FORMAT_S16;
398 /* If the variable doesn't exist then it's the first time we're called
399 and we have to probe the available audio formats and channels */
400 if ( var_Type( p_aout, "audio-device" ) == 0 )
402 Probe( p_aout, psz_device, psz_iec_device, &i_snd_pcm_format );
405 if ( var_Get( p_aout, "audio-device", &val ) < 0 )
412 p_aout->output.output.i_format = i_vlc_pcm_format;
413 if ( val.i_int == AOUT_VAR_5_1 )
415 p_aout->output.output.i_physical_channels
416 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
417 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
420 psz_device = strdup( "surround51" );
422 else if ( val.i_int == AOUT_VAR_2F2R )
424 p_aout->output.output.i_physical_channels
425 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
426 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
428 psz_device = strdup( "surround40" );
430 else if ( val.i_int == AOUT_VAR_STEREO )
432 p_aout->output.output.i_physical_channels
433 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
435 else if ( val.i_int == AOUT_VAR_MONO )
437 p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
439 else if( val.i_int != AOUT_VAR_SPDIF )
441 /* This should not happen ! */
442 msg_Err( p_aout, "internal: can't find audio-device (%"PRId64")",
450 snd_output_stdio_attach( &p_sys->p_snd_stderr, stderr, 0 );
453 /* Open the device */
454 if ( val.i_int == AOUT_VAR_SPDIF )
456 if ( ( i_snd_rc = snd_pcm_open( &p_sys->p_snd_pcm, psz_iec_device,
457 SND_PCM_STREAM_PLAYBACK, 0 ) ) < 0 )
459 msg_Err( p_aout, "cannot open ALSA device `%s' (%s)",
460 psz_iec_device, snd_strerror( i_snd_rc ) );
461 dialog_Fatal( p_aout, _("Audio output failed"),
462 _("VLC could not open the ALSA device \"%s\" (%s)."),
463 psz_iec_device, snd_strerror( i_snd_rc ) );
468 i_buffer_size = ALSA_SPDIF_BUFFER_SIZE;
469 i_snd_pcm_format = SND_PCM_FORMAT_S16;
472 i_vlc_pcm_format = VLC_CODEC_SPDIFL;
473 p_aout->output.i_nb_samples = i_period_size = ALSA_SPDIF_PERIOD_SIZE;
474 p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
475 p_aout->output.output.i_frame_length = A52_FRAME_NB;
477 aout_VolumeNoneInit( p_aout );
483 msg_Dbg( p_aout, "opening ALSA device `%s'", psz_device );
485 /* Since it seems snd_pcm_close hasn't really released the device at
486 the time it returns, probe if the device is available in loop for 1s.
487 We cannot use blocking mode since the we would wait indefinitely when
488 switching from a dmx device to surround51. */
490 for( i = 10; i >= 0; i-- )
492 if ( ( i_snd_rc = snd_pcm_open( &p_sys->p_snd_pcm, psz_device,
493 SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK ) ) == -EBUSY )
495 if( i ) msleep( 100000 /* 100ms */ );
498 msg_Err( p_aout, "audio device: %s is already in use",
500 dialog_Fatal( p_aout, _("Audio output failed"),
501 _("The audio device \"%s\" is already in use."),
510 msg_Err( p_aout, "cannot open ALSA device `%s' (%s)",
511 psz_device, snd_strerror( i_snd_rc ) );
512 dialog_Fatal( p_aout, _("Audio output failed"),
513 _("VLC could not open the ALSA device \"%s\" (%s)."),
514 psz_device, snd_strerror( i_snd_rc ) );
520 /* We want blocking mode */
521 snd_pcm_nonblock( p_sys->p_snd_pcm, 0 );
523 i_buffer_size = ALSA_DEFAULT_BUFFER_SIZE;
524 i_channels = aout_FormatNbChannels( &p_aout->output.output );
526 p_aout->output.i_nb_samples = i_period_size = ALSA_DEFAULT_PERIOD_SIZE;
528 aout_VolumeSoftInit( p_aout );
531 /* Free psz_device so that all the remaining data is stack-allocated */
534 p_aout->output.pf_play = Play;
536 snd_pcm_hw_params_alloca(&p_hw);
537 snd_pcm_sw_params_alloca(&p_sw);
539 /* Due to some bugs in alsa with some drivers, we need to retry in s16l
540 if snd_pcm_hw_params fails in fl32 */
545 /* Get Initial hardware parameters */
546 if ( ( i_snd_rc = snd_pcm_hw_params_any( p_sys->p_snd_pcm, p_hw ) ) < 0 )
548 msg_Err( p_aout, "unable to retrieve initial hardware parameters (%s)",
549 snd_strerror( i_snd_rc ) );
554 if ( ( i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, p_hw,
555 i_snd_pcm_format ) ) < 0 )
557 if( i_snd_pcm_format != SND_PCM_FORMAT_S16 )
559 i_snd_pcm_format = SND_PCM_FORMAT_S16;
560 i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm,
561 p_hw, i_snd_pcm_format );
565 msg_Err( p_aout, "unable to set stream sample size and "
566 "word order (%s)", snd_strerror( i_snd_rc ) );
570 if( i_vlc_pcm_format != VLC_CODEC_SPDIFL )
571 switch( i_snd_pcm_format )
573 case SND_PCM_FORMAT_FLOAT:
574 i_vlc_pcm_format = VLC_CODEC_FL32;
576 case SND_PCM_FORMAT_S16:
577 i_vlc_pcm_format = VLC_CODEC_S16N;
580 p_aout->output.output.i_format = i_vlc_pcm_format;
582 if ( ( i_snd_rc = snd_pcm_hw_params_set_access( p_sys->p_snd_pcm, p_hw,
583 SND_PCM_ACCESS_RW_INTERLEAVED ) ) < 0 )
585 msg_Err( p_aout, "unable to set interleaved stream format (%s)",
586 snd_strerror( i_snd_rc ) );
591 if ( ( i_snd_rc = snd_pcm_hw_params_set_channels( p_sys->p_snd_pcm, p_hw,
594 msg_Err( p_aout, "unable to set number of output channels (%s)",
595 snd_strerror( i_snd_rc ) );
600 i_old_rate = p_aout->output.output.i_rate;
601 i_snd_rc = snd_pcm_hw_params_set_rate_near( p_sys->p_snd_pcm, p_hw,
602 &p_aout->output.output.i_rate,
604 if( i_snd_rc < 0 || p_aout->output.output.i_rate != i_old_rate )
606 msg_Warn( p_aout, "The rate %d Hz is not supported by your " \
607 "hardware. Using %d Hz instead.\n", i_old_rate, \
608 p_aout->output.output.i_rate );
611 /* Set period size. */
612 if ( ( i_snd_rc = snd_pcm_hw_params_set_period_size_near( p_sys->p_snd_pcm,
613 p_hw, &i_period_size, NULL ) ) < 0 )
615 msg_Err( p_aout, "unable to set period size (%s)",
616 snd_strerror( i_snd_rc ) );
619 p_aout->output.i_nb_samples = i_period_size;
621 /* Set buffer size. */
622 if ( ( i_snd_rc = snd_pcm_hw_params_set_buffer_size_near( p_sys->p_snd_pcm,
623 p_hw, &i_buffer_size ) ) < 0 )
625 msg_Err( p_aout, "unable to set buffer size (%s)",
626 snd_strerror( i_snd_rc ) );
630 /* Commit hardware parameters. */
631 if ( ( i_snd_rc = snd_pcm_hw_params( p_sys->p_snd_pcm, p_hw ) ) < 0 )
633 if ( b_retry == false &&
634 i_snd_pcm_format == SND_PCM_FORMAT_FLOAT)
637 i_snd_pcm_format = SND_PCM_FORMAT_S16;
638 p_aout->output.output.i_format = VLC_CODEC_S16N;
639 msg_Warn( p_aout, "unable to commit hardware configuration "
640 "with fl32 samples. Retrying with s16l (%s)", snd_strerror( i_snd_rc ) );
644 msg_Err( p_aout, "unable to commit hardware configuration (%s)",
645 snd_strerror( i_snd_rc ) );
651 if( ( i_snd_rc = snd_pcm_hw_params_get_period_time( p_hw,
652 &p_sys->i_period_time, NULL ) ) < 0 )
654 msg_Err( p_aout, "unable to get period time (%s)",
655 snd_strerror( i_snd_rc ) );
659 /* Get Initial software parameters */
660 snd_pcm_sw_params_current( p_sys->p_snd_pcm, p_sw );
662 i_snd_rc = snd_pcm_sw_params_set_sleep_min( p_sys->p_snd_pcm, p_sw, 0 );
664 i_snd_rc = snd_pcm_sw_params_set_avail_min( p_sys->p_snd_pcm, p_sw,
665 p_aout->output.i_nb_samples );
666 /* start playing when one period has been written */
667 i_snd_rc = snd_pcm_sw_params_set_start_threshold( p_sys->p_snd_pcm, p_sw,
668 ALSA_DEFAULT_PERIOD_SIZE);
671 msg_Err( p_aout, "unable to set start threshold (%s)",
672 snd_strerror( i_snd_rc ) );
676 /* Commit software parameters. */
677 if ( snd_pcm_sw_params( p_sys->p_snd_pcm, p_sw ) < 0 )
679 msg_Err( p_aout, "unable to set software configuration" );
684 snd_output_printf( p_sys->p_snd_stderr, "\nALSA hardware setup:\n\n" );
685 snd_pcm_dump_hw_setup( p_sys->p_snd_pcm, p_sys->p_snd_stderr );
686 snd_output_printf( p_sys->p_snd_stderr, "\nALSA software setup:\n\n" );
687 snd_pcm_dump_sw_setup( p_sys->p_snd_pcm, p_sys->p_snd_stderr );
688 snd_output_printf( p_sys->p_snd_stderr, "\n" );
691 p_sys->start_date = 0;
692 vlc_sem_init( &p_sys->wait, 0 );
694 /* Create ALSA thread and wait for its readiness. */
695 if( vlc_clone( &p_sys->thread, ALSAThread, p_aout,
696 VLC_THREAD_PRIORITY_OUTPUT ) )
698 msg_Err( p_aout, "cannot create ALSA thread (%m)" );
699 vlc_sem_destroy( &p_sys->wait );
706 snd_pcm_close( p_sys->p_snd_pcm );
708 snd_output_close( p_sys->p_snd_stderr );
714 static void PlayIgnore( aout_instance_t *p_aout )
715 { /* Already playing - nothing to do */
719 /*****************************************************************************
720 * Play: start playback
721 *****************************************************************************/
722 static void Play( aout_instance_t *p_aout )
724 p_aout->output.pf_play = PlayIgnore;
726 /* get the playing date of the first aout buffer */
727 p_aout->output.p_sys->start_date =
728 aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
730 /* wake up the audio output thread */
731 sem_post( &p_aout->output.p_sys->wait );
734 /*****************************************************************************
735 * Close: close the ALSA device
736 *****************************************************************************/
737 static void Close( vlc_object_t *p_this )
739 aout_instance_t *p_aout = (aout_instance_t *)p_this;
740 struct aout_sys_t * p_sys = p_aout->output.p_sys;
743 /* Make sure that the thread will stop once it is waken up */
744 vlc_cancel( p_sys->thread );
745 vlc_join( p_sys->thread, NULL );
746 vlc_sem_destroy( &p_sys->wait );
749 i_snd_rc = snd_pcm_close( p_sys->p_snd_pcm );
753 msg_Err( p_aout, "failed closing ALSA device (%s)",
754 snd_strerror( i_snd_rc ) );
758 snd_output_close( p_sys->p_snd_stderr );
764 static void pcm_drop(void *pcm)
769 /*****************************************************************************
770 * ALSAThread: asynchronous thread used to DMA the data to the device
771 *****************************************************************************/
772 static void* ALSAThread( void *data )
774 aout_instance_t * p_aout = data;
775 struct aout_sys_t * p_sys = p_aout->output.p_sys;
777 /* Wait for the exact time to start playing (avoids resampling) */
778 vlc_sem_wait( &p_sys->wait );
779 mwait( p_sys->start_date - AOUT_PTS_TOLERANCE / 4 );
781 vlc_cleanup_push( pcm_drop, p_sys->p_snd_pcm );
789 /*****************************************************************************
790 * ALSAFill: function used to fill the ALSA buffer as much as possible
791 *****************************************************************************/
792 static void ALSAFill( aout_instance_t * p_aout )
794 struct aout_sys_t * p_sys = p_aout->output.p_sys;
795 snd_pcm_t *p_pcm = p_sys->p_snd_pcm;
796 snd_pcm_status_t * p_status;
800 int canc = vlc_savecancel();
801 /* Fill in the buffer until space or audio output buffer shortage */
804 snd_pcm_status_alloca(&p_status);
805 i_snd_rc = snd_pcm_status( p_pcm, p_status );
808 msg_Err( p_aout, "cannot get device status" );
812 /* Handle buffer underruns and get the status again */
813 if( snd_pcm_status_get_state( p_status ) == SND_PCM_STATE_XRUN )
815 /* Prepare the device */
816 i_snd_rc = snd_pcm_prepare( p_pcm );
819 msg_Err( p_aout, "cannot recover from buffer underrun" );
823 msg_Dbg( p_aout, "recovered from buffer underrun" );
825 /* Get the new status */
826 i_snd_rc = snd_pcm_status( p_pcm, p_status );
829 msg_Err( p_aout, "cannot get device status after recovery" );
833 /* Underrun, try to recover as quickly as possible */
838 /* Here the device should be in RUNNING state, p_status is valid. */
839 snd_pcm_sframes_t delay = snd_pcm_status_get_delay( p_status );
840 if( delay == 0 ) /* workaround buggy alsa drivers */
841 if( snd_pcm_delay( p_pcm, &delay ) < 0 )
842 delay = 0; /* FIXME: use a positive minimal delay */
844 size_t i_bytes = snd_pcm_frames_to_bytes( p_pcm, delay );
845 mtime_t delay_us = CLOCK_FREQ * i_bytes
846 / p_aout->output.output.i_bytes_per_frame
847 / p_aout->output.output.i_rate
848 * p_aout->output.output.i_frame_length;
851 snd_pcm_state_t state = snd_pcm_status_get_state( p_status );
852 if( state != SND_PCM_STATE_RUNNING )
853 msg_Err( p_aout, "pcm status (%d) != RUNNING", state );
855 msg_Dbg( p_aout, "Delay is %ld frames (%zu bytes)", delay, i_bytes );
857 msg_Dbg( p_aout, "Bytes per frame: %d", p_aout->output.output.i_bytes_per_frame );
858 msg_Dbg( p_aout, "Rate: %d", p_aout->output.output.i_rate );
859 msg_Dbg( p_aout, "Frame length: %d", p_aout->output.output.i_frame_length );
860 msg_Dbg( p_aout, "Next date: in %"PRId64" microseconds", delay_us );
862 next_date = mdate() + delay_us;
865 block_t *p_buffer = aout_OutputNextBuffer( p_aout, next_date,
866 (p_aout->output.output.i_format == VLC_CODEC_SPDIFL) );
868 /* Audio output buffer shortage -> stop the fill process and wait */
869 if( p_buffer == NULL )
872 block_cleanup_push( p_buffer );
875 int n = snd_pcm_poll_descriptors_count(p_pcm);
876 struct pollfd ufd[n];
877 unsigned short revents;
879 snd_pcm_poll_descriptors(p_pcm, ufd, n);
882 vlc_restorecancel(canc);
884 canc = vlc_savecancel();
885 snd_pcm_poll_descriptors_revents(p_pcm, ufd, n, &revents);
889 if(revents & POLLOUT)
891 i_snd_rc = snd_pcm_writei( p_pcm, p_buffer->p_buffer,
892 p_buffer->i_nb_samples );
893 if( i_snd_rc != -ESTRPIPE )
897 /* a suspend event occurred
898 * (stream is suspended and waiting for an application recovery) */
899 msg_Dbg( p_aout, "entering in suspend mode, trying to resume..." );
901 while( ( i_snd_rc = snd_pcm_resume( p_pcm ) ) == -EAGAIN )
903 vlc_restorecancel(canc);
904 msleep(CLOCK_FREQ); /* device still suspended, wait... */
905 canc = vlc_savecancel();
909 /* Device does not support resuming, restart it */
910 i_snd_rc = snd_pcm_prepare( p_pcm );
915 msg_Err( p_aout, "cannot write: %s", snd_strerror( i_snd_rc ) );
917 vlc_restorecancel(canc);
923 msg_Err( p_aout, "ALSA error: %s", snd_strerror( i_snd_rc ) );
925 vlc_restorecancel(canc);
926 msleep(p_sys->i_period_time / 2);
929 /*****************************************************************************
930 * config variable callback
931 *****************************************************************************/
932 static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name,
933 vlc_value_t newval, vlc_value_t oldval, void *p_unused )
935 module_config_t *p_item;
940 p_item = config_FindConfig( p_this, psz_name );
941 if( !p_item ) return VLC_SUCCESS;
943 /* Clear-up the current list */
948 /* Keep the first entrie */
949 for( i = 1; i < p_item->i_list; i++ )
951 free( (char *)p_item->ppsz_list[i] );
952 free( (char *)p_item->ppsz_list_text[i] );
954 /* TODO: Remove when no more needed */
955 p_item->ppsz_list[i] = NULL;
956 p_item->ppsz_list_text[i] = NULL;
960 GetDevices( p_this, p_item );
962 /* Signal change to the interface */
963 p_item->b_dirty = true;
969 static void GetDevicesForCard( vlc_object_t *obj, module_config_t *p_item,
972 int i_pcm_device = -1;
974 snd_pcm_info_t *p_pcm_info;
976 char psz_dev[4 + 3 * sizeof(int)];
979 snprintf( psz_dev, sizeof (psz_dev), "hw:%u", i_card );
981 if( ( i_err = snd_ctl_open( &p_ctl, psz_dev, 0 ) ) < 0 )
984 if( ( i_err = snd_card_get_name( i_card, &psz_card_name ) ) != 0)
985 psz_card_name = _("Unknown soundcard");
987 snd_pcm_info_alloca( &p_pcm_info );
991 char *psz_device, *psz_descr;
992 if( ( i_err = snd_ctl_pcm_next_device( p_ctl, &i_pcm_device ) ) < 0 )
994 if( i_pcm_device < 0 )
997 snd_pcm_info_set_device( p_pcm_info, i_pcm_device );
998 snd_pcm_info_set_subdevice( p_pcm_info, 0 );
999 snd_pcm_info_set_stream( p_pcm_info, SND_PCM_STREAM_PLAYBACK );
1001 if( ( i_err = snd_ctl_pcm_info( p_ctl, p_pcm_info ) ) < 0 )
1003 if( i_err != -ENOENT )
1004 msg_Err( obj, "cannot get PCM device %d:%d infos: %s", i_card,
1005 i_pcm_device, snd_strerror( -i_err ) );
1009 if( asprintf( &psz_device, "plughw:%u,%u", i_card, i_pcm_device ) == -1 )
1011 if( asprintf( &psz_descr, "%s: %s (%s)", psz_card_name,
1012 snd_pcm_info_get_name(p_pcm_info), psz_device ) == -1 )
1018 msg_Dbg( obj, " %s", psz_descr );
1022 p_item->ppsz_list = xrealloc( p_item->ppsz_list,
1023 (p_item->i_list + 2) * sizeof(char *) );
1024 p_item->ppsz_list_text = xrealloc( p_item->ppsz_list_text,
1025 (p_item->i_list + 2) * sizeof(char *) );
1026 p_item->ppsz_list[ p_item->i_list ] = psz_device;
1027 p_item->ppsz_list_text[ p_item->i_list ] = psz_descr;
1029 p_item->ppsz_list[ p_item->i_list ] = NULL;
1030 p_item->ppsz_list_text[ p_item->i_list ] = NULL;
1039 snd_ctl_close( p_ctl );
1043 static void GetDevices( vlc_object_t *obj, module_config_t *p_item )
1048 msg_Dbg( obj, "Available alsa output devices:" );
1049 while( (i_err = snd_card_next( &i_card )) == 0 && i_card > -1 )
1050 GetDevicesForCard( obj, p_item, i_card );
1053 msg_Err( obj, "cannot enumerate cards: %s", snd_strerror( -i_err ) );