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 "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 GetDevices( vlc_object_t *, module_config_t * );
102 /*****************************************************************************
104 *****************************************************************************/
105 static const char *const ppsz_devices[] = { "default" };
106 static const char *const ppsz_devices_text[] = { N_("Default") };
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") )
118 set_capability( "audio output", 150 )
119 set_callbacks( Open, Close )
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 )
129 struct aout_sys_t * p_sys = p_aout->output.p_sys;
130 vlc_value_t val, text;
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 );
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 */
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 );
147 snd_pcm_hw_params_t * p_hw;
148 snd_pcm_hw_params_alloca (&p_hw);
150 if ( snd_pcm_hw_params_any( p_sys->p_snd_pcm, p_hw ) < 0 )
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" );
159 if ( snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, p_hw,
160 *pi_snd_pcm_format ) < 0 )
164 if( *pi_snd_pcm_format != SND_PCM_FORMAT_S16 )
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 );
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" );
180 i_channels = aout_FormatNbChannels( &p_aout->output.output );
182 while ( i_channels > 0 )
184 if ( !snd_pcm_hw_params_test_channels( p_sys->p_snd_pcm, p_hw,
187 switch ( i_channels )
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 );
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 );
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 );
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 );
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 )
225 if ( !snd_pcm_hw_params_test_channels( p_sys->p_snd_pcm, p_hw, 2 ))
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 );
235 /* Close the previously opened device */
236 snd_pcm_close( p_sys->p_snd_pcm );
238 else if ( i_ret == -EBUSY )
240 msg_Warn( p_aout, "audio device: %s is already in use", psz_device );
243 /* Test for S/PDIF device if needed */
244 if ( psz_iec_device )
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 );
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 );
258 snd_pcm_close( p_sys->p_snd_pcm );
260 else if ( i_ret == -EBUSY )
262 msg_Warn( p_aout, "audio device: %s is already in use",
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 );
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." );
280 var_SetBool( p_aout->p_libvlc, "alsa-working", true );
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" ) )
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." );
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 );
303 /* Add final settings to the variable */
304 var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
305 var_TriggerCallback( p_aout, "intf-change" );
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;
321 /* Allocate structures */
322 aout_sys_t * p_sys = malloc( sizeof( aout_sys_t ) );
325 p_aout->output.p_sys = p_sys;
327 /* Get device name */
328 char *psz_device = var_InheritString( p_aout, "alsa-audio-device" );
329 if( unlikely(psz_device == NULL) )
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 ) )
341 if( !strcmp( psz_device, DEFAULT_ALSA_DEVICE ) )
345 switch( p_aout->output.output.i_rate )
348 aes3 = IEC958_AES3_CON_FS_48000;
351 aes3 = IEC958_AES3_CON_FS_44100;
354 aes3 = IEC958_AES3_CON_FS_32000;
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,
370 psz_iec_device = strdup( psz_device );
373 /* Choose the linear PCM format (read the comment above about FPU
375 int i_snd_pcm_format; /* Audio format for ALSA's data */
377 i_snd_pcm_format = SND_PCM_FORMAT_FLOAT;
379 i_snd_pcm_format = SND_PCM_FORMAT_S16;
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 ) )
386 free( psz_iec_device );
393 switch( var_GetInteger( p_aout, "audio-device") )
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
401 psz_device = strdup( "surround51" );
404 p_aout->output.output.i_physical_channels
405 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
406 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
408 psz_device = strdup( "surround40" );
410 case AOUT_VAR_STEREO:
411 p_aout->output.output.i_physical_channels
412 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
415 p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
420 psz_device = psz_iec_device;
421 psz_iec_device = NULL;
424 /* This should not happen ! */
425 msg_Err( p_aout, "cannot find audio-device" );
426 free( psz_iec_device );
433 snd_output_stdio_attach( &p_sys->p_snd_stderr, stderr, 0 );
436 /* Open the device */
437 msg_Dbg( p_aout, "opening ALSA device `%s'", psz_device );
438 for( unsigned i = 10; i; i-- )
440 int val = snd_pcm_open( &p_sys->p_snd_pcm, psz_device,
441 SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK );
443 break; /* success! */
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 ) );
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. */
463 msg_Err( p_aout, "audio device %s is already in use",
465 dialog_Fatal( p_aout, _("Audio output failed"),
466 _("The audio device \"%s\" is already in use."),
472 msleep( CLOCK_FREQ / 10 );
476 /* We want blocking mode */
477 snd_pcm_nonblock( p_sys->p_snd_pcm, 0 );
479 snd_pcm_uframes_t i_buffer_size;
480 snd_pcm_uframes_t i_period_size;
485 i_buffer_size = ALSA_SPDIF_BUFFER_SIZE;
486 i_snd_pcm_format = SND_PCM_FORMAT_S16;
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;
493 aout_VolumeNoneInit( p_aout );
497 i_buffer_size = ALSA_DEFAULT_BUFFER_SIZE;
498 i_channels = aout_FormatNbChannels( &p_aout->output.output );
500 p_aout->output.i_nb_samples = i_period_size = ALSA_DEFAULT_PERIOD_SIZE;
502 aout_VolumeSoftInit( p_aout );
505 p_aout->output.pf_play = Play;
507 snd_pcm_hw_params_t *p_hw;
508 snd_pcm_sw_params_t *p_sw;
510 snd_pcm_hw_params_alloca(&p_hw);
511 snd_pcm_sw_params_alloca(&p_sw);
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 */
517 /* Get Initial hardware parameters */
518 val = snd_pcm_hw_params_any( p_sys->p_snd_pcm, p_hw );
521 msg_Err( p_aout, "unable to retrieve hardware parameters (%s)",
522 snd_strerror( val ) );
527 val = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, p_hw,
531 if( i_snd_pcm_format != SND_PCM_FORMAT_S16 )
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 );
539 msg_Err( p_aout, "unable to set stream sample size and "
540 "word order (%s)", snd_strerror( val ) );
545 vlc_fourcc_t i_vlc_pcm_format;
547 i_vlc_pcm_format = VLC_CODEC_SPDIFL;
549 switch( i_snd_pcm_format )
551 case SND_PCM_FORMAT_FLOAT:
552 i_vlc_pcm_format = VLC_CODEC_FL32;
554 case SND_PCM_FORMAT_S16:
555 i_vlc_pcm_format = VLC_CODEC_S16N;
560 p_aout->output.output.i_format = i_vlc_pcm_format;
562 val = snd_pcm_hw_params_set_access( p_sys->p_snd_pcm, p_hw,
563 SND_PCM_ACCESS_RW_INTERLEAVED );
566 msg_Err( p_aout, "unable to set interleaved stream format (%s)",
567 snd_strerror( val ) );
572 val = snd_pcm_hw_params_set_channels( p_sys->p_snd_pcm, p_hw, i_channels );
575 msg_Err( p_aout, "unable to set number of output channels (%s)",
576 snd_strerror( val ) );
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,
585 if( val < 0 || p_aout->output.output.i_rate != i_old_rate )
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 );
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 );
597 msg_Err( p_aout, "unable to set period size (%s)",
598 snd_strerror( val ) );
601 p_aout->output.i_nb_samples = i_period_size;
603 /* Set buffer size. */
604 val = snd_pcm_hw_params_set_buffer_size_near( p_sys->p_snd_pcm, p_hw,
608 msg_Err( p_aout, "unable to set buffer size (%s)",
609 snd_strerror( val ) );
613 /* Commit hardware parameters. */
614 val = snd_pcm_hw_params( p_sys->p_snd_pcm, p_hw );
617 if( i_snd_pcm_format == SND_PCM_FORMAT_FLOAT )
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 ) );
626 msg_Err( p_aout, "unable to commit hardware configuration (%s)",
627 snd_strerror( val ) );
631 val = snd_pcm_hw_params_get_period_time( p_hw, &p_sys->i_period_time,
635 msg_Err( p_aout, "unable to get period time (%s)",
636 snd_strerror( val ) );
640 /* Get Initial software parameters */
641 snd_pcm_sw_params_current( p_sys->p_snd_pcm, p_sw );
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);
650 msg_Err( p_aout, "unable to set start threshold (%s)",
651 snd_strerror( val ) );
655 /* Commit software parameters. */
656 if ( snd_pcm_sw_params( p_sys->p_snd_pcm, p_sw ) < 0 )
658 msg_Err( p_aout, "unable to set software configuration" );
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" );
670 p_sys->start_date = 0;
671 vlc_sem_init( &p_sys->wait, 0 );
673 /* Create ALSA thread and wait for its readiness. */
674 if( vlc_clone( &p_sys->thread, ALSAThread, p_aout,
675 VLC_THREAD_PRIORITY_OUTPUT ) )
677 msg_Err( p_aout, "cannot create ALSA thread (%m)" );
678 vlc_sem_destroy( &p_sys->wait );
685 snd_pcm_close( p_sys->p_snd_pcm );
687 snd_output_close( p_sys->p_snd_stderr );
693 static void PlayIgnore( aout_instance_t *p_aout )
694 { /* Already playing - nothing to do */
698 /*****************************************************************************
699 * Play: start playback
700 *****************************************************************************/
701 static void Play( aout_instance_t *p_aout )
703 p_aout->output.pf_play = PlayIgnore;
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 );
709 /* wake up the audio output thread */
710 sem_post( &p_aout->output.p_sys->wait );
713 /*****************************************************************************
714 * Close: close the ALSA device
715 *****************************************************************************/
716 static void Close( vlc_object_t *p_this )
718 aout_instance_t *p_aout = (aout_instance_t *)p_this;
719 struct aout_sys_t * p_sys = p_aout->output.p_sys;
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 );
726 snd_pcm_drop( p_sys->p_snd_pcm );
727 snd_pcm_close( p_sys->p_snd_pcm );
729 snd_output_close( p_sys->p_snd_stderr );
734 /*****************************************************************************
735 * ALSAThread: asynchronous thread used to DMA the data to the device
736 *****************************************************************************/
737 static void* ALSAThread( void *data )
739 aout_instance_t * p_aout = data;
740 struct aout_sys_t * p_sys = p_aout->output.p_sys;
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 );
752 /*****************************************************************************
753 * ALSAFill: function used to fill the ALSA buffer as much as possible
754 *****************************************************************************/
755 static void ALSAFill( aout_instance_t * p_aout )
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;
763 int canc = vlc_savecancel();
764 /* Fill in the buffer until space or audio output buffer shortage */
767 snd_pcm_status_alloca(&p_status);
768 i_snd_rc = snd_pcm_status( p_pcm, p_status );
771 msg_Err( p_aout, "cannot get device status" );
775 /* Handle buffer underruns and get the status again */
776 if( snd_pcm_status_get_state( p_status ) == SND_PCM_STATE_XRUN )
778 /* Prepare the device */
779 i_snd_rc = snd_pcm_prepare( p_pcm );
782 msg_Err( p_aout, "cannot recover from buffer underrun" );
786 msg_Dbg( p_aout, "recovered from buffer underrun" );
788 /* Get the new status */
789 i_snd_rc = snd_pcm_status( p_pcm, p_status );
792 msg_Err( p_aout, "cannot get device status after recovery" );
796 /* Underrun, try to recover as quickly as possible */
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 */
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;
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 );
818 msg_Dbg( p_aout, "Delay is %ld frames (%zu bytes)", delay, i_bytes );
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 );
825 next_date = mdate() + delay_us;
828 block_t *p_buffer = aout_OutputNextBuffer( p_aout, next_date,
829 (p_aout->output.output.i_format == VLC_CODEC_SPDIFL) );
831 /* Audio output buffer shortage -> stop the fill process and wait */
832 if( p_buffer == NULL )
835 block_cleanup_push( p_buffer );
838 int n = snd_pcm_poll_descriptors_count(p_pcm);
839 struct pollfd ufd[n];
840 unsigned short revents;
842 snd_pcm_poll_descriptors(p_pcm, ufd, n);
845 vlc_restorecancel(canc);
847 canc = vlc_savecancel();
848 snd_pcm_poll_descriptors_revents(p_pcm, ufd, n, &revents);
852 if(revents & POLLOUT)
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 )
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..." );
864 while( ( i_snd_rc = snd_pcm_resume( p_pcm ) ) == -EAGAIN )
866 vlc_restorecancel(canc);
867 msleep(CLOCK_FREQ); /* device still suspended, wait... */
868 canc = vlc_savecancel();
872 /* Device does not support resuming, restart it */
873 i_snd_rc = snd_pcm_prepare( p_pcm );
878 msg_Err( p_aout, "cannot write: %s", snd_strerror( i_snd_rc ) );
880 vlc_restorecancel(canc);
886 msg_Err( p_aout, "ALSA error: %s", snd_strerror( i_snd_rc ) );
888 vlc_restorecancel(canc);
889 msleep(p_sys->i_period_time / 2);
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 )
898 module_config_t *p_item;
903 p_item = config_FindConfig( p_this, psz_name );
904 if( !p_item ) return VLC_SUCCESS;
906 /* Clear-up the current list */
911 /* Keep the first entrie */
912 for( i = 1; i < p_item->i_list; i++ )
914 free( (char *)p_item->ppsz_list[i] );
915 free( (char *)p_item->ppsz_list_text[i] );
917 /* TODO: Remove when no more needed */
918 p_item->ppsz_list[i] = NULL;
919 p_item->ppsz_list_text[i] = NULL;
923 GetDevices( p_this, p_item );
925 /* Signal change to the interface */
926 p_item->b_dirty = true;
932 static void GetDevices (vlc_object_t *obj, module_config_t *item)
936 msg_Dbg(obj, "Available ALSA PCM devices:");
938 if (snd_device_name_hint(-1, "pcm", &hints) < 0)
941 for (size_t i = 0; hints[i] != NULL; i++)
943 void *hint = hints[i];
944 char *name = snd_device_name_get_hint(hint, "NAME");
945 if (unlikely(name == NULL))
948 char *desc = snd_device_name_get_hint(hint, "DESC");
950 for (char *lf = strchr(desc, '\n'); lf; lf = strchr(lf, '\n'))
952 msg_Dbg(obj, " %s (%s)", (desc != NULL) ? desc : name, name);
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;
963 item->ppsz_list_text[item->i_list] = desc;
973 snd_device_name_free_hint(hints);
977 item->ppsz_list[item->i_list] = NULL;
978 item->ppsz_list_text[item->i_list] = NULL;