X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faudio_output%2Falsa.c;h=93078c875bf8bba4f9b8a15ca59165225ad407c3;hb=f7fd8b6514d5427b5e177396e236c486142f3d97;hp=572e33cc6573183322518be0633d880e0337f611;hpb=d4667ed20e1ab269558de7c4867dd9f550873870;p=vlc diff --git a/modules/audio_output/alsa.c b/modules/audio_output/alsa.c index 572e33cc65..93078c875b 100644 --- a/modules/audio_output/alsa.c +++ b/modules/audio_output/alsa.c @@ -2,7 +2,7 @@ * alsa.c : alsa plugin for vlc ***************************************************************************** * Copyright (C) 2000-2001 VideoLAN - * $Id: alsa.c,v 1.27 2003/05/19 23:36:44 gbazin Exp $ + * $Id: alsa.c,v 1.38 2004/01/25 17:58:29 murray Exp $ * * Authors: Henri Fallon - Original Author * Jeffrey Baker - Port to ALSA 1.0 API @@ -54,9 +54,15 @@ struct aout_sys_t snd_pcm_t * p_snd_pcm; int i_period_time; -#ifdef DEBUG +#ifdef ALSA_DEBUG snd_output_t * p_snd_stderr; #endif + + int b_playing; /* playing status */ + mtime_t start_date; + + vlc_mutex_t lock; + vlc_cond_t wait ; }; #define A52_FRAME_NB 1536 @@ -64,16 +70,16 @@ struct aout_sys_t /* These values are in frames. To convert them to a number of bytes you have to multiply them by the number of channel(s) (eg. 2 for stereo) and the size of a sample (eg. - 2 for s16). */ -#define ALSA_DEFAULT_PERIOD_SIZE 2048 -#define ALSA_DEFAULT_BUFFER_SIZE ( ALSA_DEFAULT_PERIOD_SIZE << 4 ) + 2 for int16_t). */ +#define ALSA_DEFAULT_PERIOD_SIZE 1024 +#define ALSA_DEFAULT_BUFFER_SIZE ( ALSA_DEFAULT_PERIOD_SIZE << 8 ) #define ALSA_SPDIF_PERIOD_SIZE A52_FRAME_NB #define ALSA_SPDIF_BUFFER_SIZE ( ALSA_SPDIF_PERIOD_SIZE << 4 ) /* Why << 4 ? --Meuuh */ /* Why not ? --Bozo */ /* Right. --Meuuh */ -#define DEFAULT_ALSA_DEVICE "default" +#define DEFAULT_ALSA_DEVICE N_("default") /***************************************************************************** * Local prototypes @@ -90,9 +96,9 @@ static void ALSAFill ( aout_instance_t * ); vlc_module_begin(); add_category_hint( N_("ALSA"), NULL, VLC_FALSE ); add_string( "alsadev", DEFAULT_ALSA_DEVICE, aout_FindAndRestart, - N_("ALSA device name"), NULL, VLC_FALSE ); + N_("ALSA Device Name"), NULL, VLC_FALSE ); set_description( _("ALSA audio output") ); - set_capability( "audio output", 50 ); + set_capability( "audio output", 150 ); set_callbacks( Open, Close ); vlc_module_end(); @@ -101,18 +107,24 @@ vlc_module_end(); *****************************************************************************/ static void Probe( aout_instance_t * p_aout, const char * psz_device, const char * psz_iec_device, - int i_snd_pcm_format ) + int *pi_snd_pcm_format ) { struct aout_sys_t * p_sys = p_aout->output.p_sys; vlc_value_t val, text; + int i_ret; var_Create ( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); - text.psz_string = _("Audio device"); + text.psz_string = _("Audio Device"); var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL ); + /* We'll open the audio device in non blocking mode so we can just exit + * when it is already in use, but for the real stuff we'll still use + * the blocking mode */ + /* Now test linear PCM capabilities */ - if ( !snd_pcm_open( &p_sys->p_snd_pcm, psz_device, - SND_PCM_STREAM_PLAYBACK, 0 ) ) + if ( !(i_ret = snd_pcm_open( &p_sys->p_snd_pcm, psz_device, + SND_PCM_STREAM_PLAYBACK, + SND_PCM_NONBLOCK ) ) ) { int i_channels; snd_pcm_hw_params_t * p_hw; @@ -123,32 +135,32 @@ static void Probe( aout_instance_t * p_aout, msg_Warn( p_aout, "unable to retrieve initial hardware parameters" ", disabling linear PCM audio" ); snd_pcm_close( p_sys->p_snd_pcm ); + var_Destroy( p_aout, "audio-device" ); return; } if ( snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, p_hw, - i_snd_pcm_format ) < 0 ) + *pi_snd_pcm_format ) < 0 ) { - /* Assume a FPU enabled computer can handle float32 format. - If somebody tells us it's not always true then we'll have - to change this */ - 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 ); - return; + 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_channels = aout_FormatNbChannels( &p_aout->output.output ); while ( i_channels > 0 ) { - /* Here we have to probe multi-channel capabilities but I have - no idea (at the moment) of how its managed by the ALSA - library. - It seems that '6' channels aren't well handled on a stereo - sound card like my i810 but it requires some more - investigations. That's why '4' and '6' cases are disabled. - -- Bozo */ if ( !snd_pcm_hw_params_test_channels( p_sys->p_snd_pcm, p_hw, i_channels ) ) { @@ -165,8 +177,8 @@ static void Probe( aout_instance_t * p_aout, text.psz_string = N_("Stereo"); var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text ); + var_Set( p_aout, "audio-device", val ); break; -/* case 4: val.i_int = AOUT_VAR_2F2R; text.psz_string = N_("2 Front 2 Rear"); @@ -179,7 +191,6 @@ static void Probe( aout_instance_t * p_aout, var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text ); break; -*/ } } @@ -189,13 +200,18 @@ static void Probe( aout_instance_t * p_aout, /* Close the previously opened device */ snd_pcm_close( p_sys->p_snd_pcm ); } + else if ( i_ret == -EBUSY ) + { + msg_Warn( p_aout, "audio device: %s is already in use", psz_device ); + } /* Test for S/PDIF device if needed */ if ( psz_iec_device ) { /* Opening the device should be enough */ - if ( !snd_pcm_open( &p_sys->p_snd_pcm, psz_iec_device, - SND_PCM_STREAM_PLAYBACK, 0 ) ) + if ( !(i_ret = snd_pcm_open( &p_sys->p_snd_pcm, psz_iec_device, + SND_PCM_STREAM_PLAYBACK, + SND_PCM_NONBLOCK ) ) ) { val.i_int = AOUT_VAR_SPDIF; text.psz_string = N_("A/52 over S/PDIF"); @@ -206,6 +222,19 @@ static void Probe( aout_instance_t * p_aout, snd_pcm_close( p_sys->p_snd_pcm ); } + else if ( i_ret == -EBUSY ) + { + msg_Warn( p_aout, "audio device: %s is already in use", + psz_iec_device ); + } + } + + var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL ); + if( val.i_int <= 0 ) + { + /* Probe() has failed. */ + var_Destroy( p_aout, "audio-device" ); + return; } /* Add final settings to the variable */ @@ -252,6 +281,10 @@ static int Open( vlc_object_t *p_this ) msg_Err( p_aout, "out of memory" ); return VLC_ENOMEM; } + p_sys->b_playing = VLC_FALSE; + p_sys->start_date = 0; + vlc_cond_init( p_aout, &p_sys->wait ); + vlc_mutex_init( p_aout, &p_sys->lock ); /* Get device name */ if( (psz_device = config_GetPsz( p_aout, "alsadev" )) == NULL ) @@ -304,7 +337,16 @@ static int Open( vlc_object_t *p_this ) and we have to probe the available audio formats and channels */ if ( var_Type( p_aout, "audio-device" ) == 0 ) { - Probe( p_aout, psz_device, psz_iec_device, i_snd_pcm_format ); + 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 ) @@ -325,6 +367,8 @@ static int Open( vlc_object_t *p_this ) = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE; + free( psz_device ); + psz_device = strdup( "surround51" ); } else if ( val.i_int == AOUT_VAR_2F2R ) { @@ -332,6 +376,8 @@ static int Open( vlc_object_t *p_this ) p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT; + free( psz_device ); + psz_device = strdup( "surround40" ); } else if ( val.i_int == AOUT_VAR_STEREO ) { @@ -353,7 +399,7 @@ static int Open( vlc_object_t *p_this ) return VLC_EGENERIC; } -#ifdef DEBUG +#ifdef ALSA_DEBUG snd_output_stdio_attach( &p_sys->p_snd_stderr, stderr, 0 ); #endif @@ -381,6 +427,8 @@ static int Open( vlc_object_t *p_this ) } else { + 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 ) { @@ -449,9 +497,10 @@ static int Open( vlc_object_t *p_this ) p_aout->output.output.i_rate, NULL ) ) < 0 ) #endif { - msg_Err( p_aout, "unable to set sample rate (%s)", - snd_strerror( i_snd_rc ) ); - goto error; + msg_Warn( p_aout, "The rate %d Hz is not supported by your hardware. " + "Using %d Hz instead.\n", p_aout->output.output.i_rate, + i_snd_rc ); + p_aout->output.output.i_rate = i_snd_rc; } /* Set buffer size. */ @@ -519,7 +568,7 @@ static int Open( vlc_object_t *p_this ) goto error; } -#ifdef DEBUG +#ifdef ALSA_DEBUG snd_output_printf( p_sys->p_snd_stderr, "\nALSA hardware setup:\n\n" ); snd_pcm_dump_hw_setup( p_sys->p_snd_pcm, p_sys->p_snd_stderr ); snd_output_printf( p_sys->p_snd_stderr, "\nALSA software setup:\n\n" ); @@ -539,7 +588,7 @@ static int Open( vlc_object_t *p_this ) error: snd_pcm_close( p_sys->p_snd_pcm ); -#ifdef DEBUG +#ifdef ALSA_DEBUG snd_output_close( p_sys->p_snd_stderr ); #endif free( p_sys ); @@ -551,6 +600,19 @@ error: *****************************************************************************/ static void Play( aout_instance_t *p_aout ) { + if( !p_aout->output.p_sys->b_playing ) + { + p_aout->output.p_sys->b_playing = 1; + + /* get the playing date of the first aout buffer */ + p_aout->output.p_sys->start_date = + aout_FifoFirstDate( p_aout, &p_aout->output.fifo ); + + /* wake up the audio output thread */ + vlc_mutex_lock( &p_aout->output.p_sys->lock ); + vlc_cond_signal( &p_aout->output.p_sys->wait ); + vlc_mutex_unlock( &p_aout->output.p_sys->lock ); + } } /***************************************************************************** @@ -562,6 +624,11 @@ static void Close( vlc_object_t *p_this ) struct aout_sys_t * p_sys = p_aout->output.p_sys; int i_snd_rc; + /* make sure the audio output thread is waken up */ + vlc_mutex_lock( &p_aout->output.p_sys->lock ); + 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_thread_join( p_aout ); p_aout->b_die = VLC_FALSE; @@ -574,7 +641,7 @@ static void Close( vlc_object_t *p_this ) snd_strerror( i_snd_rc ) ); } -#ifdef DEBUG +#ifdef ALSA_DEBUG snd_output_close( p_sys->p_snd_stderr ); #endif @@ -586,20 +653,18 @@ 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; + /* 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 ); + + mwait( p_aout->output.p_sys->start_date - AOUT_PTS_TOLERANCE / 4 ); while ( !p_aout->b_die ) { ALSAFill( p_aout ); - - /* Sleep during less than one period to avoid a lot of buffer - underruns */ - - /* Why do we need to sleep ? --Meuuh */ - /* Maybe because I don't want to eat all the cpu by looping - all the time. --Bozo */ - /* Shouldn't snd_pcm_wait() make us wait ? --Meuuh */ - msleep( p_sys->i_period_time >> 1 ); } return 0; @@ -620,18 +685,7 @@ static void ALSAFill( aout_instance_t * p_aout ) snd_pcm_status_alloca( &p_status ); - /* Wait for the device's readiness (ie. there is enough space in the - buffer to write at least one complete chunk) */ - i_snd_rc = snd_pcm_wait( p_sys->p_snd_pcm, THREAD_SLEEP ); - if( i_snd_rc < 0 ) - { - msg_Err( p_aout, "ALSA device not ready !!! (%s)", - snd_strerror( i_snd_rc ) ); - return; - } - /* Fill in the buffer until space or audio output buffer shortage */ - for ( ; ; ) { /* Get the status */ i_snd_rc = snd_pcm_status( p_sys->p_snd_pcm, p_status ); @@ -639,6 +693,8 @@ static void ALSAFill( aout_instance_t * p_aout ) { msg_Err( p_aout, "unable to get the device's status (%s)", snd_strerror( i_snd_rc ) ); + + msleep( p_sys->i_period_time >> 1 ); return; } @@ -656,35 +712,45 @@ static void ALSAFill( aout_instance_t * p_aout ) 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 ) ); + msg_Err( p_aout, "unable to get the device's status after " + "recovery (%s)", snd_strerror( i_snd_rc ) ); + + msleep( p_sys->i_period_time >> 1 ); return; } } else { msg_Err( p_aout, "unable to recover from buffer underrun" ); + + msleep( p_sys->i_period_time >> 1 ); return; } - } - /* Here the device should be either in the RUNNING state either in - the PREPARE state. p_status is valid. */ + /* 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. */ - snd_pcm_status_get_tstamp( p_status, &ts_next ); - next_date = (mtime_t)ts_next.tv_sec * 1000000 + ts_next.tv_usec; - next_date += (mtime_t)snd_pcm_status_get_delay(p_status) - * 1000000 / p_aout->output.output.i_rate; + snd_pcm_status_get_tstamp( p_status, &ts_next ); + next_date = (mtime_t)ts_next.tv_sec * 1000000 + ts_next.tv_usec; + next_date += (mtime_t)snd_pcm_status_get_delay(p_status) + * 1000000 / p_aout->output.output.i_rate; + } 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 in ALSAThread */ + /* Audio output buffer shortage -> stop the fill process and wait */ if( p_buffer == NULL ) + { + msleep( p_sys->i_period_time >> 1 ); return; + } i_snd_rc = snd_pcm_writei( p_sys->p_snd_pcm, p_buffer->p_buffer, p_buffer->i_nb_samples ); @@ -696,8 +762,5 @@ static void ALSAFill( aout_instance_t * p_aout ) } aout_BufferFree( p_buffer ); - - msleep( p_sys->i_period_time >> 2 ); } } -