X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faudio_output%2Falsa.c;h=dbcb02bc1a6b01d71961a844e3222e84d9811216;hb=8f3d5d03e6c625f3527e8e8dd9e5391180e2a398;hp=6044630a9d950f2b875371d775cae6629d707dfc;hpb=85b29bdc288a1573d43bd524908be5748a9b3640;p=vlc diff --git a/modules/audio_output/alsa.c b/modules/audio_output/alsa.c index 6044630a9d..dbcb02bc1a 100644 --- a/modules/audio_output/alsa.c +++ b/modules/audio_output/alsa.c @@ -1,7 +1,7 @@ /***************************************************************************** * alsa.c : alsa plugin for vlc ***************************************************************************** - * Copyright (C) 2000-2001 VideoLAN (Centrale Réseaux) and its contributors + * Copyright (C) 2000-2001 the VideoLAN team * $Id$ * * Authors: Henri Fallon - Original Author @@ -21,21 +21,18 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ -#include /* ENOMEM */ -#include /* strerror() */ -#include /* calloc(), malloc(), free() */ - #include -#include +#include /* ENOMEM */ +#include -#include "aout_internal.h" +#include /* ALSA part Note: we use the new API which is available since 0.9.0beta10a. */ @@ -43,6 +40,8 @@ #define ALSA_PCM_NEW_SW_PARAMS_API #include +/*#define ALSA_DEBUG*/ + /***************************************************************************** * aout_sys_t: ALSA audio output method descriptor ***************************************************************************** @@ -52,7 +51,7 @@ struct aout_sys_t { snd_pcm_t * p_snd_pcm; - int i_period_time; + unsigned int i_period_time; #ifdef ALSA_DEBUG snd_output_t * p_snd_stderr; @@ -97,8 +96,8 @@ static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name, /***************************************************************************** * Module descriptor *****************************************************************************/ -static char *ppsz_devices[] = { "default" }; -static char *ppsz_devices_text[] = { N_("Default") }; +static const char *ppsz_devices[] = { "default" }; +static const char *ppsz_devices_text[] = { N_("Default") }; vlc_module_begin(); set_shortname( "ALSA" ); set_description( _("ALSA audio output") ); @@ -109,8 +108,6 @@ vlc_module_begin(); change_string_list( ppsz_devices, ppsz_devices_text, FindDevicesCallback ); change_action_add( FindDevicesCallback, N_("Refresh list") ); - - set_capability( "audio output", 150 ); set_callbacks( Open, Close ); vlc_module_end(); @@ -155,18 +152,21 @@ static void Probe( aout_instance_t * p_aout, if ( snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, p_hw, *pi_snd_pcm_format ) < 0 ) { + int i_snd_rc = -1; + if( *pi_snd_pcm_format != SND_PCM_FORMAT_S16 ) { *pi_snd_pcm_format = SND_PCM_FORMAT_S16; - if ( snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, p_hw, - *pi_snd_pcm_format ) < 0 ) - { - msg_Warn( p_aout, "unable to set stream sample size and " - "word order, disabling linear PCM audio" ); - snd_pcm_close( p_sys->p_snd_pcm ); - var_Destroy( p_aout, "audio-device" ); - return; - } + i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, + p_hw, *pi_snd_pcm_format ); + } + if ( i_snd_rc < 0 ) + { + msg_Warn( p_aout, "unable to set stream sample size and " + "word order, disabling linear PCM audio" ); + snd_pcm_close( p_sys->p_snd_pcm ); + var_Destroy( p_aout, "audio-device" ); + return; } } @@ -200,7 +200,7 @@ static void Probe( aout_instance_t * p_aout, break; case 6: val.i_int = AOUT_VAR_5_1; - text.psz_string = N_("5.1"); + text.psz_string = "5.1"; var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text ); break; @@ -211,7 +211,7 @@ static void Probe( aout_instance_t * p_aout, } /* Special case for mono on stereo only boards */ - i_channels = aout_FormatNbChannels( &p_aout->output.output ); + i_channels = aout_FormatNbChannels( &p_aout->output.output ); var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL ); if( val.i_int <= 0 && i_channels == 1 ) { @@ -224,7 +224,7 @@ static void Probe( aout_instance_t * p_aout, var_Set( p_aout, "audio-device", val ); } } - + /* Close the previously opened device */ snd_pcm_close( p_sys->p_snd_pcm ); } @@ -303,6 +303,7 @@ static int Open( vlc_object_t *p_this ) int i_snd_rc = -1; unsigned int i_old_rate; + vlc_bool_t b_retry = VLC_TRUE; /* Allocate structures */ p_aout->output.p_sys = p_sys = malloc( sizeof( aout_sys_t ) ); @@ -313,7 +314,6 @@ static int Open( vlc_object_t *p_this ) } p_sys->b_playing = VLC_FALSE; p_sys->start_date = 0; - p_sys->p_status = (snd_pcm_status_t *)malloc(snd_pcm_status_sizeof()); vlc_cond_init( p_aout, &p_sys->wait ); vlc_mutex_init( p_aout, &p_sys->lock ); @@ -321,6 +321,9 @@ static int Open( vlc_object_t *p_this ) if( (psz_device = config_GetPsz( p_aout, "alsadev" )) == NULL ) { msg_Err( p_aout, "no audio device given (maybe \"default\" ?)" ); + intf_UserFatal( p_aout, VLC_FALSE, _("No Audio Device"), + _("No audio device name was given. You might want to " \ + "enter \"default\".") ); free( p_sys ); return VLC_EGENERIC; } @@ -353,7 +356,7 @@ static int Open( vlc_object_t *p_this ) /* Choose the linear PCM format (read the comment above about FPU and float32) */ - if( p_aout->p_libvlc->i_cpu & CPU_CAPABILITY_FPU ) + if( vlc_CPU() & CPU_CAPABILITY_FPU ) { i_vlc_pcm_format = VLC_FOURCC('f','l','3','2'); i_snd_pcm_format = SND_PCM_FORMAT_FLOAT; @@ -369,15 +372,6 @@ static int Open( vlc_object_t *p_this ) if ( var_Type( p_aout, "audio-device" ) == 0 ) { Probe( p_aout, psz_device, psz_iec_device, &i_snd_pcm_format ); - switch( i_snd_pcm_format ) - { - case SND_PCM_FORMAT_FLOAT: - i_vlc_pcm_format = VLC_FOURCC('f','l','3','2'); - break; - case SND_PCM_FORMAT_S16: - i_vlc_pcm_format = AOUT_FMT_S16_NE; - break; - } } if ( var_Get( p_aout, "audio-device", &val ) < 0 ) @@ -387,13 +381,9 @@ static int Open( vlc_object_t *p_this ) return VLC_EGENERIC; } - if ( val.i_int == AOUT_VAR_SPDIF ) + p_aout->output.output.i_format = i_vlc_pcm_format; + if ( val.i_int == AOUT_VAR_5_1 ) { - p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i'); - } - else if ( val.i_int == AOUT_VAR_5_1 ) - { - p_aout->output.output.i_format = i_vlc_pcm_format; p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT @@ -403,7 +393,6 @@ static int Open( vlc_object_t *p_this ) } else if ( val.i_int == AOUT_VAR_2F2R ) { - p_aout->output.output.i_format = i_vlc_pcm_format; p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT; @@ -412,21 +401,19 @@ static int Open( vlc_object_t *p_this ) } else if ( val.i_int == AOUT_VAR_STEREO ) { - p_aout->output.output.i_format = i_vlc_pcm_format; p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; } else if ( val.i_int == AOUT_VAR_MONO ) { - p_aout->output.output.i_format = i_vlc_pcm_format; p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER; } - - else + else if( val.i_int != AOUT_VAR_SPDIF ) { /* This should not happen ! */ msg_Err( p_aout, "internal: can't find audio-device (%i)", val.i_int ); free( p_sys ); + free( psz_device ); return VLC_EGENERIC; } @@ -435,13 +422,16 @@ static int Open( vlc_object_t *p_this ) #endif /* Open the device */ - if ( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) ) + if ( val.i_int == AOUT_VAR_SPDIF ) { if ( ( i_snd_rc = snd_pcm_open( &p_sys->p_snd_pcm, psz_iec_device, SND_PCM_STREAM_PLAYBACK, 0 ) ) < 0 ) { msg_Err( p_aout, "cannot open ALSA device `%s' (%s)", psz_iec_device, snd_strerror( i_snd_rc ) ); + intf_UserFatal( p_aout, VLC_FALSE, _("Audio output failed"), + _("VLC could not open the ALSA device \"%s\" (%s)."), + psz_iec_device, snd_strerror( i_snd_rc ) ); free( p_sys ); free( psz_device ); return VLC_EGENERIC; @@ -450,6 +440,7 @@ static int Open( vlc_object_t *p_this ) i_snd_pcm_format = SND_PCM_FORMAT_S16; i_channels = 2; + i_vlc_pcm_format = VLC_FOURCC('s','p','d','i'); p_aout->output.i_nb_samples = i_period_size = ALSA_SPDIF_PERIOD_SIZE; p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE; p_aout->output.output.i_frame_length = A52_FRAME_NB; @@ -458,17 +449,48 @@ static int Open( vlc_object_t *p_this ) } else { + int i; + msg_Dbg( p_aout, "opening ALSA device `%s'", psz_device ); - if ( ( i_snd_rc = snd_pcm_open( &p_sys->p_snd_pcm, psz_device, - SND_PCM_STREAM_PLAYBACK, 0 ) ) < 0 ) + /* Since it seems snd_pcm_close hasn't really released the device at + the time it returns, probe if the device is available in loop for 1s. + We cannot use blocking mode since the we would wait indefinitely when + switching from a dmx device to surround51. */ + + for( i = 10; i >= 0; i-- ) + { + if ( ( i_snd_rc = snd_pcm_open( &p_sys->p_snd_pcm, psz_device, + SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK ) ) == -EBUSY ) + { + if( i ) msleep( 100000 /* 100ms */ ); + else + { + msg_Err( p_aout, "audio device: %s is already in use", + psz_device ); + intf_UserFatal( p_aout, VLC_FALSE, _("Audio output failed"), + _("The audio device \"%s\" is already in use."), + psz_device ); + } + continue; + } + break; + } + if( i_snd_rc < 0 ) { msg_Err( p_aout, "cannot open ALSA device `%s' (%s)", psz_device, snd_strerror( i_snd_rc ) ); + intf_UserFatal( p_aout, VLC_FALSE, _("Audio output failed"), + _("VLC could not open the ALSA device \"%s\" (%s)."), + psz_device, snd_strerror( i_snd_rc ) ); free( p_sys ); free( psz_device ); return VLC_EGENERIC; } + + /* We want blocking mode */ + snd_pcm_nonblock( p_sys->p_snd_pcm, 0 ); + i_buffer_size = ALSA_DEFAULT_BUFFER_SIZE; i_channels = aout_FormatNbChannels( &p_aout->output.output ); @@ -485,93 +507,132 @@ static int Open( vlc_object_t *p_this ) snd_pcm_hw_params_alloca(&p_hw); snd_pcm_sw_params_alloca(&p_sw); - /* Get Initial hardware parameters */ - if ( ( i_snd_rc = snd_pcm_hw_params_any( p_sys->p_snd_pcm, p_hw ) ) < 0 ) + /* Due to some bugs in alsa with some drivers, we need to retry in s16l + if snd_pcm_hw_params fails in fl32 */ + while ( b_retry ) { - msg_Err( p_aout, "unable to retrieve initial hardware parameters (%s)", + b_retry = VLC_FALSE; + + /* Get Initial hardware parameters */ + if ( ( i_snd_rc = snd_pcm_hw_params_any( p_sys->p_snd_pcm, p_hw ) ) < 0 ) + { + msg_Err( p_aout, "unable to retrieve initial hardware parameters (%s)", snd_strerror( i_snd_rc ) ); - goto error; - } + goto error; + } - /* Set format. */ - if ( ( i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, p_hw, + /* Set format. */ + if ( ( i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, p_hw, i_snd_pcm_format ) ) < 0 ) - { - msg_Err( p_aout, "unable to set stream sample size and word order (%s)", - snd_strerror( i_snd_rc ) ); - goto error; - } + { + if( i_snd_pcm_format != SND_PCM_FORMAT_S16 ) + { + i_snd_pcm_format = SND_PCM_FORMAT_S16; + i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, + p_hw, i_snd_pcm_format ); + } + if ( i_snd_rc < 0 ) + { + msg_Err( p_aout, "unable to set stream sample size and " + "word order (%s)", snd_strerror( i_snd_rc ) ); + goto error; + } + } + if( i_vlc_pcm_format != VLC_FOURCC('s','p','d','i') ) + switch( i_snd_pcm_format ) + { + case SND_PCM_FORMAT_FLOAT: + i_vlc_pcm_format = VLC_FOURCC('f','l','3','2'); + break; + case SND_PCM_FORMAT_S16: + i_vlc_pcm_format = AOUT_FMT_S16_NE; + break; + } + p_aout->output.output.i_format = i_vlc_pcm_format; - if ( ( i_snd_rc = snd_pcm_hw_params_set_access( p_sys->p_snd_pcm, p_hw, + if ( ( i_snd_rc = snd_pcm_hw_params_set_access( p_sys->p_snd_pcm, p_hw, SND_PCM_ACCESS_RW_INTERLEAVED ) ) < 0 ) - { - msg_Err( p_aout, "unable to set interleaved stream format (%s)", - snd_strerror( i_snd_rc ) ); - goto error; - } + { + msg_Err( p_aout, "unable to set interleaved stream format (%s)", + snd_strerror( i_snd_rc ) ); + goto error; + } - /* Set channels. */ - if ( ( i_snd_rc = snd_pcm_hw_params_set_channels( p_sys->p_snd_pcm, p_hw, + /* Set channels. */ + if ( ( i_snd_rc = snd_pcm_hw_params_set_channels( p_sys->p_snd_pcm, p_hw, i_channels ) ) < 0 ) - { - msg_Err( p_aout, "unable to set number of output channels (%s)", - snd_strerror( i_snd_rc ) ); - goto error; - } + { + msg_Err( p_aout, "unable to set number of output channels (%s)", + snd_strerror( i_snd_rc ) ); + goto error; + } - /* Set rate. */ - i_old_rate = p_aout->output.output.i_rate; + /* Set rate. */ + i_old_rate = p_aout->output.output.i_rate; #ifdef HAVE_ALSA_NEW_API - i_snd_rc = snd_pcm_hw_params_set_rate_near( p_sys->p_snd_pcm, p_hw, + i_snd_rc = snd_pcm_hw_params_set_rate_near( p_sys->p_snd_pcm, p_hw, &p_aout->output.output.i_rate, NULL ); #else - i_snd_rc = snd_pcm_hw_params_set_rate_near( p_sys->p_snd_pcm, p_hw, + i_snd_rc = snd_pcm_hw_params_set_rate_near( p_sys->p_snd_pcm, p_hw, p_aout->output.output.i_rate, NULL ); #endif - if( i_snd_rc < 0 || p_aout->output.output.i_rate != i_old_rate ) - { - msg_Warn( p_aout, "The rate %d Hz is not supported by your hardware. " - "Using %d Hz instead.\n", i_old_rate, - p_aout->output.output.i_rate ); - } + if( i_snd_rc < 0 || p_aout->output.output.i_rate != i_old_rate ) + { + msg_Warn( p_aout, "The rate %d Hz is not supported by your " \ + "hardware. Using %d Hz instead.\n", i_old_rate, \ + p_aout->output.output.i_rate ); + } - /* Set buffer size. */ + /* Set period size. */ #ifdef HAVE_ALSA_NEW_API - if ( ( i_snd_rc = snd_pcm_hw_params_set_buffer_size_near( p_sys->p_snd_pcm, - p_hw, &i_buffer_size ) ) < 0 ) + if ( ( i_snd_rc = snd_pcm_hw_params_set_period_size_near( p_sys->p_snd_pcm, + p_hw, &i_period_size, NULL ) ) < 0 ) #else - if ( ( i_snd_rc = snd_pcm_hw_params_set_buffer_size_near( p_sys->p_snd_pcm, - p_hw, i_buffer_size ) ) < 0 ) + if ( ( i_snd_rc = snd_pcm_hw_params_set_period_size_near( p_sys->p_snd_pcm, + p_hw, i_period_size, NULL ) ) < 0 ) #endif - { - msg_Err( p_aout, "unable to set buffer size (%s)", + { + msg_Err( p_aout, "unable to set period size (%s)", snd_strerror( i_snd_rc ) ); - goto error; - } + goto error; + } + p_aout->output.i_nb_samples = i_period_size; - /* Set period size. */ +/* Set buffer size. */ #ifdef HAVE_ALSA_NEW_API - if ( ( i_snd_rc = snd_pcm_hw_params_set_period_size_near( p_sys->p_snd_pcm, - p_hw, &i_period_size, NULL ) ) < 0 ) + if ( ( i_snd_rc = snd_pcm_hw_params_set_buffer_size_near( p_sys->p_snd_pcm, + p_hw, &i_buffer_size ) ) < 0 ) #else - if ( ( i_snd_rc = snd_pcm_hw_params_set_period_size_near( p_sys->p_snd_pcm, - p_hw, i_period_size, NULL ) ) < 0 ) + if ( ( i_snd_rc = snd_pcm_hw_params_set_buffer_size_near( p_sys->p_snd_pcm, + p_hw, i_buffer_size ) ) < 0 ) #endif - { - msg_Err( p_aout, "unable to set period size (%s)", + { + msg_Err( p_aout, "unable to set buffer size (%s)", snd_strerror( i_snd_rc ) ); - goto error; - } - p_aout->output.i_nb_samples = i_period_size; + goto error; + } - /* Commit hardware parameters. */ - if ( ( i_snd_rc = snd_pcm_hw_params( p_sys->p_snd_pcm, p_hw ) ) < 0 ) - { - msg_Err( p_aout, "unable to commit hardware configuration (%s)", + /* Commit hardware parameters. */ + if ( ( i_snd_rc = snd_pcm_hw_params( p_sys->p_snd_pcm, p_hw ) ) < 0 ) + { + if ( b_retry == VLC_FALSE && + i_snd_pcm_format == SND_PCM_FORMAT_FLOAT) + { + b_retry = VLC_TRUE; + i_snd_pcm_format = SND_PCM_FORMAT_S16; + p_aout->output.output.i_format = AOUT_FMT_S16_NE; + msg_Warn( p_aout, "unable to commit hardware configuration " + "with fl32 samples. Retrying with s16l (%s)", snd_strerror( i_snd_rc ) ); + } + else + { + msg_Err( p_aout, "unable to commit hardware configuration (%s)", snd_strerror( i_snd_rc ) ); - goto error; + goto error; + } + } } #ifdef HAVE_ALSA_NEW_API @@ -579,7 +640,7 @@ static int Open( vlc_object_t *p_this ) &p_sys->i_period_time, NULL ) ) < 0 ) #else if( ( p_sys->i_period_time = - snd_pcm_hw_params_get_period_time( p_hw, NULL ) ) < 0 ) + (int)snd_pcm_hw_params_get_period_time( p_hw, NULL ) ) < 0 ) #endif { msg_Err( p_aout, "unable to get period time (%s)", @@ -594,6 +655,15 @@ static int Open( vlc_object_t *p_this ) i_snd_rc = snd_pcm_sw_params_set_avail_min( p_sys->p_snd_pcm, p_sw, p_aout->output.i_nb_samples ); + /* start playing when one period has been written */ + i_snd_rc = snd_pcm_sw_params_set_start_threshold( p_sys->p_snd_pcm, p_sw, + ALSA_DEFAULT_PERIOD_SIZE); + if( i_snd_rc < 0 ) + { + msg_Err( p_aout, "unable to set start threshold (%s)", + snd_strerror( i_snd_rc ) ); + goto error; + } /* Commit software parameters. */ if ( snd_pcm_sw_params( p_sys->p_snd_pcm, p_sw ) < 0 ) @@ -614,7 +684,7 @@ static int Open( vlc_object_t *p_this ) if( vlc_thread_create( p_aout, "aout", ALSAThread, VLC_THREAD_PRIORITY_OUTPUT, VLC_FALSE ) ) { - msg_Err( p_aout, "cannot create ALSA thread (%s)", strerror(errno) ); + msg_Err( p_aout, "cannot create ALSA thread (%m)" ); goto error; } @@ -663,7 +733,7 @@ static void Close( vlc_object_t *p_this ) vlc_cond_signal( &p_aout->output.p_sys->wait ); vlc_mutex_unlock( &p_aout->output.p_sys->lock ); - p_aout->b_die = VLC_TRUE; + vlc_object_kill( p_aout ); vlc_thread_join( p_aout ); p_aout->b_die = VLC_FALSE; @@ -679,7 +749,6 @@ static void Close( vlc_object_t *p_this ) snd_output_close( p_sys->p_snd_stderr ); #endif - free( p_sys->p_status ); free( p_sys ); } @@ -688,20 +757,28 @@ static void Close( vlc_object_t *p_this ) *****************************************************************************/ static int ALSAThread( aout_instance_t * p_aout ) { + struct aout_sys_t * p_sys = p_aout->output.p_sys; + p_sys->p_status = (snd_pcm_status_t *)malloc(snd_pcm_status_sizeof()); + /* Wait for the exact time to start playing (avoids resampling) */ - vlc_mutex_lock( &p_aout->output.p_sys->lock ); - if( !p_aout->output.p_sys->start_date ) - vlc_cond_wait( &p_aout->output.p_sys->wait, - &p_aout->output.p_sys->lock ); - vlc_mutex_unlock( &p_aout->output.p_sys->lock ); + vlc_mutex_lock( &p_sys->lock ); + while( !p_sys->start_date && !p_aout->b_die ) + vlc_cond_wait( &p_sys->wait, &p_sys->lock ); + vlc_mutex_unlock( &p_sys->lock ); + + if( p_aout->b_die ) + goto cleanup; - mwait( p_aout->output.p_sys->start_date - AOUT_PTS_TOLERANCE / 4 ); + mwait( p_sys->start_date - AOUT_PTS_TOLERANCE / 4 ); while ( !p_aout->b_die ) { ALSAFill( p_aout ); } +cleanup: + snd_pcm_drop( p_sys->p_snd_pcm ); + free( p_aout->output.p_sys->p_status ); return 0; } @@ -711,113 +788,115 @@ static int ALSAThread( aout_instance_t * p_aout ) static void ALSAFill( aout_instance_t * p_aout ) { struct aout_sys_t * p_sys = p_aout->output.p_sys; - aout_buffer_t * p_buffer; snd_pcm_status_t * p_status = p_sys->p_status; - snd_timestamp_t ts_next; int i_snd_rc; mtime_t next_date; /* Fill in the buffer until space or audio output buffer shortage */ + + /* Get the status */ + i_snd_rc = snd_pcm_status( p_sys->p_snd_pcm, p_status ); + if( i_snd_rc < 0 ) { - /* Get the status */ - i_snd_rc = snd_pcm_status( p_sys->p_snd_pcm, p_status ); - if( i_snd_rc < 0 ) - { - msg_Err( p_aout, "unable to get the device's status (%s)", - snd_strerror( i_snd_rc ) ); + msg_Err( p_aout, "cannot get device status" ); + goto error; + } - msleep( p_sys->i_period_time >> 1 ); - return; - } + /* Handle buffer underruns and get the status again */ + if( snd_pcm_status_get_state( p_status ) == SND_PCM_STATE_XRUN ) + { + /* Prepare the device */ + i_snd_rc = snd_pcm_prepare( p_sys->p_snd_pcm ); - /* Handle buffer underruns and reget the status */ - if( snd_pcm_status_get_state( p_status ) == SND_PCM_STATE_XRUN ) + if( i_snd_rc ) { - /* Prepare the device */ - i_snd_rc = snd_pcm_prepare( p_sys->p_snd_pcm ); + msg_Err( p_aout, "cannot recover from buffer underrun" ); + goto error; + } - if( i_snd_rc == 0 ) - { - msg_Warn( p_aout, "recovered from buffer underrun" ); + msg_Dbg( p_aout, "recovered from buffer underrun" ); - /* Reget the status */ - i_snd_rc = snd_pcm_status( p_sys->p_snd_pcm, p_status ); - if( i_snd_rc < 0 ) - { - msg_Err( p_aout, "unable to get the device's status after " - "recovery (%s)", snd_strerror( i_snd_rc ) ); + /* Get the new status */ + i_snd_rc = snd_pcm_status( p_sys->p_snd_pcm, p_status ); + if( i_snd_rc < 0 ) + { + msg_Err( p_aout, "cannot get device status after recovery" ); + goto error; + } - msleep( p_sys->i_period_time >> 1 ); - return; - } - } - else - { - msg_Err( p_aout, "unable to recover from buffer underrun" ); + /* Underrun, try to recover as quickly as possible */ + next_date = mdate(); + } + else + { + /* Here the device should be in RUNNING state. + * p_status is valid. */ + snd_pcm_sframes_t delay = snd_pcm_status_get_delay( p_status ); + int i_bytes = snd_pcm_frames_to_bytes( p_sys->p_snd_pcm, delay ); + next_date = mdate() + ( (mtime_t)i_bytes * 1000000 + / p_aout->output.output.i_bytes_per_frame + / p_aout->output.output.i_rate + * p_aout->output.output.i_frame_length ); - msleep( p_sys->i_period_time >> 1 ); - return; - } +#ifdef ALSA_DEBUG + snd_pcm_state_t state = snd_pcm_status_get_state( p_status ); + if( state != SND_PCM_STATE_RUNNING ) + msg_Err( p_aout, "pcm status (%d) != RUNNING", state ); - /* Underrun, try to recover as quickly as possible */ - next_date = mdate(); - } - else - { - /* Here the device should be either in the RUNNING state. - * p_status is valid. */ + msg_Dbg( p_aout, "Delay is %ld frames (%d bytes)", delay, i_bytes ); - snd_pcm_status_get_tstamp( p_status, &ts_next ); - next_date = (mtime_t)ts_next.tv_sec * 1000000 + ts_next.tv_usec; - if( next_date ) - { - next_date += (mtime_t)snd_pcm_status_get_delay(p_status) - * 1000000 / p_aout->output.output.i_rate; - } - else - { - /* With screwed ALSA drivers the timestamp is always zero; - * use another method then */ - snd_pcm_sframes_t delay; - ssize_t i_bytes = 0; + msg_Dbg( p_aout, "Bytes per frame: %d", p_aout->output.output.i_bytes_per_frame ); + msg_Dbg( p_aout, "Rate: %d", p_aout->output.output.i_rate ); + msg_Dbg( p_aout, "Frame length: %d", p_aout->output.output.i_frame_length ); - if( !snd_pcm_delay( p_sys->p_snd_pcm, &delay ) ) - { - i_bytes = snd_pcm_frames_to_bytes(p_sys->p_snd_pcm, delay); - } - next_date = mdate() + (mtime_t)i_bytes * 1000000 - / p_aout->output.output.i_bytes_per_frame - / p_aout->output.output.i_rate - * p_aout->output.output.i_frame_length; - } - } + msg_Dbg( p_aout, "Next date is in %d microseconds", (int)(next_date - mdate()) ); +#endif + } - p_buffer = aout_OutputNextBuffer( p_aout, next_date, - (p_aout->output.output.i_format == - VLC_FOURCC('s','p','d','i')) ); + p_buffer = aout_OutputNextBuffer( p_aout, next_date, + (p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i')) ); - /* Audio output buffer shortage -> stop the fill process and wait */ - if( p_buffer == NULL ) - { - msleep( p_sys->i_period_time >> 1 ); - return; - } + /* Audio output buffer shortage -> stop the fill process and wait */ + if( p_buffer == NULL ) + goto error; + for (;;) + { i_snd_rc = snd_pcm_writei( p_sys->p_snd_pcm, p_buffer->p_buffer, p_buffer->i_nb_samples ); + if( i_snd_rc != -ESTRPIPE ) + break; - if( i_snd_rc < 0 ) + /* a suspend event occurred + * (stream is suspended and waiting for an application recovery) */ + msg_Dbg( p_aout, "entering in suspend mode, trying to resume..." ); + + while( !p_aout->b_die && !p_aout->p_libvlc->b_die && + ( i_snd_rc = snd_pcm_resume( p_sys->p_snd_pcm ) ) == -EAGAIN ) { - msg_Err( p_aout, "write failed (%s)", - snd_strerror( i_snd_rc ) ); + msleep( 1000000 ); } - aout_BufferFree( p_buffer ); + if( i_snd_rc < 0 ) + /* Device does not supprot resuming, restart it */ + i_snd_rc = snd_pcm_prepare( p_sys->p_snd_pcm ); + } + + if( i_snd_rc < 0 ) + msg_Err( p_aout, "cannot write: %s", snd_strerror( i_snd_rc ) ); + + aout_BufferFree( p_buffer ); + return; + +error: + if( i_snd_rc < 0 ) + msg_Err( p_aout, "ALSA error: %s", snd_strerror( i_snd_rc ) ); + msleep( p_sys->i_period_time >> 1 ); } -static void GetDevicesForCard(module_config_t *p_item, int i_card); +static void GetDevicesForCard( module_config_t *p_item, int i_card ); static void GetDevices( module_config_t *p_item ); /***************************************************************************** @@ -838,8 +917,8 @@ static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name, /* Keep the first entrie */ for( i = 1; i < p_item->i_list; i++ ) { - free( p_item->ppsz_list[i] ); - free( p_item->ppsz_list_text[i] ); + free( (char *)p_item->ppsz_list[i] ); + free( (char *)p_item->ppsz_list_text[i] ); } /* TODO: Remove when no more needed */ p_item->ppsz_list[i] = NULL; @@ -857,7 +936,7 @@ static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name, } -static void GetDevicesForCard(module_config_t *p_item, int i_card) +static void GetDevicesForCard( module_config_t *p_item, int i_card ) { int i_pcm_device = -1; int i_err = 0; @@ -865,43 +944,37 @@ static void GetDevicesForCard(module_config_t *p_item, int i_card) snd_ctl_t *p_ctl; char psz_dev[64]; char *psz_card_name; - - sprintf(psz_dev, "hw:%i", i_card); - - if (( i_err = snd_ctl_open(&p_ctl, psz_dev, 0)) < 0 ) - { + + sprintf( psz_dev, "hw:%i", i_card ); + + if( ( i_err = snd_ctl_open( &p_ctl, psz_dev, 0 ) ) < 0 ) return; - } - - if ((i_err = snd_card_get_name(i_card, &psz_card_name)) != 0) - { + + if( ( i_err = snd_card_get_name( i_card, &psz_card_name ) ) != 0) psz_card_name = _("Unknown soundcard"); - } - snd_pcm_info_alloca(&p_pcm_info); + snd_pcm_info_alloca( &p_pcm_info ); for (;;) { char *psz_device, *psz_descr; - if ((i_err = snd_ctl_pcm_next_device(p_ctl, &i_pcm_device)) < 0) - { + if( ( i_err = snd_ctl_pcm_next_device( p_ctl, &i_pcm_device ) ) < 0 ) i_pcm_device = -1; - } - if ( i_pcm_device < 0 ) + if( i_pcm_device < 0 ) break; - snd_pcm_info_set_device(p_pcm_info, i_pcm_device); - snd_pcm_info_set_subdevice(p_pcm_info, 0); - snd_pcm_info_set_stream(p_pcm_info, SND_PCM_STREAM_PLAYBACK); + snd_pcm_info_set_device( p_pcm_info, i_pcm_device ); + snd_pcm_info_set_subdevice( p_pcm_info, 0 ); + snd_pcm_info_set_stream( p_pcm_info, SND_PCM_STREAM_PLAYBACK ); - if ((i_err = snd_ctl_pcm_info(p_ctl, p_pcm_info)) < 0) + if( ( i_err = snd_ctl_pcm_info( p_ctl, p_pcm_info ) ) < 0 ) { - if (i_err != -ENOENT) + if( i_err != -ENOENT ) { -/* printf("get_devices_for_card(): " + /*printf( "get_devices_for_card(): " "snd_ctl_pcm_info() " "failed (%d:%d): %s.\n", i_card, - i_pcm_device, snd_strerror(-i_err));*/ + i_pcm_device, snd_strerror( -i_err ) );*/ } continue; } @@ -911,17 +984,16 @@ static void GetDevicesForCard(module_config_t *p_item, int i_card) snd_pcm_info_get_name(p_pcm_info), psz_device ); p_item->ppsz_list = - (char **)realloc( p_item->ppsz_list, + (const char **)realloc( p_item->ppsz_list, (p_item->i_list + 2) * sizeof(char *) ); p_item->ppsz_list_text = - (char **)realloc( p_item->ppsz_list_text, + (const char **)realloc( p_item->ppsz_list_text, (p_item->i_list + 2) * sizeof(char *) ); p_item->ppsz_list[ p_item->i_list ] = psz_device; p_item->ppsz_list_text[ p_item->i_list ] = psz_descr; p_item->i_list++; p_item->ppsz_list[ p_item->i_list ] = NULL; p_item->ppsz_list_text[ p_item->i_list ] = NULL; - } snd_ctl_close( p_ctl ); @@ -933,22 +1005,20 @@ static void GetDevices( module_config_t *p_item ) { int i_card = -1; int i_err = 0; - - if ((i_err = snd_card_next(&i_card)) != 0) + + if( ( i_err = snd_card_next( &i_card ) ) != 0 ) { -// g_warning("snd_next_card() failed: %s", snd_strerror(-err)); + /*printf( "snd_card_next() failed: %s", snd_strerror( -i_err ) );*/ return; } - - while (i_card > -1) + + while( i_card > -1 ) { - GetDevicesForCard(p_item, i_card); - if ((i_err = snd_card_next(&i_card)) != 0) + GetDevicesForCard( p_item, i_card ); + if( ( i_err = snd_card_next( &i_card ) ) != 0 ) { -// g_warning("snd_next_card() failed: %s", -// snd_strerror(-err)); + /*printf( "snd_card_next() failed: %s", snd_strerror( -i_err ) );*/ break; } } } -