X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faudio_output%2Falsa.c;h=b5168d2f1f2eea0baa0f49dc7865e6db7eb3954e;hb=cc5b3af019db3893c84e4f09fef4277727151a84;hp=fc05cf11feb657117a4b3708a1a564211e5409c7;hpb=015989f3baedbc3a5f8674637048d46d8fc00836;p=vlc diff --git a/modules/audio_output/alsa.c b/modules/audio_output/alsa.c index fc05cf11fe..b5168d2f1f 100644 --- a/modules/audio_output/alsa.c +++ b/modules/audio_output/alsa.c @@ -31,6 +31,8 @@ # include "config.h" #endif +#include + #include #include @@ -38,12 +40,14 @@ #include #include +#include /* ALSA part Note: we use the new API which is available since 0.9.0beta10a. */ #define ALSA_PCM_NEW_HW_PARAMS_API #define ALSA_PCM_NEW_SW_PARAMS_API #include +#include /*#define ALSA_DEBUG*/ @@ -62,13 +66,9 @@ struct aout_sys_t snd_output_t * p_snd_stderr; #endif - int b_playing; /* playing status */ - mtime_t start_date; - - vlc_mutex_t lock; - vlc_cond_t wait ; - - snd_pcm_status_t *p_status; + mtime_t start_date; + vlc_thread_t thread; + vlc_sem_t wait; }; #define A52_FRAME_NB 1536 @@ -93,10 +93,12 @@ struct aout_sys_t static int Open ( vlc_object_t * ); static void Close ( vlc_object_t * ); static void Play ( aout_instance_t * ); -static void* ALSAThread ( vlc_object_t * ); +static void* ALSAThread ( void * ); static void ALSAFill ( aout_instance_t * ); static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name, vlc_value_t newval, vlc_value_t oldval, void *p_unused ); +static void GetDevicesForCard( vlc_object_t *, module_config_t *, int card ); +static void GetDevices( vlc_object_t *, module_config_t * ); /***************************************************************************** * Module descriptor @@ -251,7 +253,7 @@ static void Probe( aout_instance_t * p_aout, text.psz_string = (char*)N_("A/52 over S/PDIF"); var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text ); - if( config_GetInt( p_aout, "spdif" ) ) + if( var_InheritBool( p_aout, "spdif" ) ) var_Set( p_aout, "audio-device", val ); snd_pcm_close( p_sys->p_snd_pcm ); @@ -264,11 +266,38 @@ static void Probe( aout_instance_t * p_aout, } var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL ); +#if (SND_LIB_VERSION <= 0x010015) +# warning Please update alsa-lib to version > 1.0.21a. + var_Create( p_aout->p_libvlc, "alsa-working", VLC_VAR_BOOL ); + if( val.i_int <= 0 ) + { + if( var_GetBool( p_aout->p_libvlc, "alsa-working" ) ) + dialog_Fatal( p_aout, "ALSA version problem", + "VLC failed to re-initialize your sound output device.\n" + "Please update alsa-lib to version 1.0.22 or higher " + "to fix this issue." ); + } + else + var_SetBool( p_aout->p_libvlc, "alsa-working", true ); +#endif if( val.i_int <= 0 ) { /* Probe() has failed. */ - msg_Dbg( p_aout, "failed to find a usable alsa configuration" ); +#if (SND_LIB_VERSION <= 0x010017) +# warning Please update alsa-lib to version > 1.0.23. + var_Create( p_aout->p_libvlc, "alsa-broken", VLC_VAR_BOOL ); + if( !var_GetBool( p_aout->p_libvlc, "alsa-broken" ) ) + { + var_SetBool( p_aout->p_libvlc, "alsa-broken", true ); + dialog_Fatal( p_aout, "Potential ALSA version problem", + "VLC failed to initialize your sound output device (if any).\n" + "Please update alsa-lib to version 1.0.24 or higher " + "to try to fix this issue." ); + } +#endif + msg_Dbg( p_aout, "failed to find a usable ALSA configuration" ); var_Destroy( p_aout, "audio-device" ); + GetDevices( VLC_OBJECT(p_aout), NULL ); return; } @@ -314,13 +343,9 @@ static int Open( vlc_object_t *p_this ) p_aout->output.p_sys = p_sys = malloc( sizeof( aout_sys_t ) ); if( p_sys == NULL ) return VLC_ENOMEM; - p_sys->b_playing = false; - p_sys->start_date = 0; - vlc_cond_init( &p_sys->wait ); - vlc_mutex_init( &p_sys->lock ); /* Get device name */ - if( (psz_device = config_GetPsz( p_aout, "alsa-audio-device" )) == NULL ) + if( (psz_device = var_InheritString( p_aout, "alsa-audio-device" )) == NULL ) { msg_Err( p_aout, "no audio device given (maybe \"default\" ?)" ); dialog_Fatal( p_aout, _("No Audio Device"), "%s", @@ -358,7 +383,7 @@ static int Open( vlc_object_t *p_this ) /* Choose the linear PCM format (read the comment above about FPU and float32) */ - if( vlc_CPU() & CPU_CAPABILITY_FPU ) + if( HAVE_FPU ) { i_vlc_pcm_format = VLC_CODEC_FL32; i_snd_pcm_format = SND_PCM_FORMAT_FLOAT; @@ -661,11 +686,15 @@ static int Open( vlc_object_t *p_this ) snd_output_printf( p_sys->p_snd_stderr, "\n" ); #endif + p_sys->start_date = 0; + vlc_sem_init( &p_sys->wait, 0 ); + /* Create ALSA thread and wait for its readiness. */ - if( vlc_thread_create( p_aout, "aout", ALSAThread, - VLC_THREAD_PRIORITY_OUTPUT ) ) + if( vlc_clone( &p_sys->thread, ALSAThread, p_aout, + VLC_THREAD_PRIORITY_OUTPUT ) ) { msg_Err( p_aout, "cannot create ALSA thread (%m)" ); + vlc_sem_destroy( &p_sys->wait ); goto error; } @@ -680,24 +709,24 @@ error: return VLC_EGENERIC; } +static void PlayIgnore( aout_instance_t *p_aout ) +{ /* Already playing - nothing to do */ + (void) p_aout; +} + /***************************************************************************** - * Play: nothing to do + * Play: start playback *****************************************************************************/ static void Play( aout_instance_t *p_aout ) { - if( !p_aout->output.p_sys->b_playing ) - { - p_aout->output.p_sys->b_playing = 1; + p_aout->output.pf_play = PlayIgnore; - /* get the playing date of the first aout buffer */ - p_aout->output.p_sys->start_date = - aout_FifoFirstDate( p_aout, &p_aout->output.fifo ); + /* 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 ); - } + /* wake up the audio output thread */ + sem_post( &p_aout->output.p_sys->wait ); } /***************************************************************************** @@ -710,17 +739,11 @@ static void Close( vlc_object_t *p_this ) int i_snd_rc; /* Make sure that the thread will stop once it is waken up */ - vlc_object_kill( p_aout ); - - /* 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 ); + vlc_cancel( p_sys->thread ); + vlc_join( p_sys->thread, NULL ); + vlc_sem_destroy( &p_sys->wait ); /* */ - vlc_thread_join( p_aout ); - p_aout->b_die = false; - i_snd_rc = snd_pcm_close( p_sys->p_snd_pcm ); if( i_snd_rc > 0 ) @@ -736,37 +759,29 @@ static void Close( vlc_object_t *p_this ) free( p_sys ); } +static void pcm_drop(void *pcm) +{ + snd_pcm_drop(pcm); +} + /***************************************************************************** * ALSAThread: asynchronous thread used to DMA the data to the device *****************************************************************************/ -static void* ALSAThread( vlc_object_t* p_this ) +static void* ALSAThread( void *data ) { - aout_instance_t * p_aout = (aout_instance_t*)p_this; + aout_instance_t * p_aout = data; struct aout_sys_t * p_sys = p_aout->output.p_sys; - int canc = vlc_savecancel (); - 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_sys->lock ); - while( !p_sys->start_date && vlc_object_alive (p_aout) ) - vlc_cond_wait( &p_sys->wait, &p_sys->lock ); - vlc_mutex_unlock( &p_sys->lock ); - - if( !vlc_object_alive (p_aout) ) - goto cleanup; - + vlc_sem_wait( &p_sys->wait ); mwait( p_sys->start_date - AOUT_PTS_TOLERANCE / 4 ); - while ( vlc_object_alive (p_aout) ) - { + vlc_cleanup_push( pcm_drop, p_sys->p_snd_pcm ); + for(;;) ALSAFill( p_aout ); - } -cleanup: - snd_pcm_drop( p_sys->p_snd_pcm ); - free( p_aout->output.p_sys->p_status ); - vlc_restorecancel (canc); - return NULL; + assert(0); + vlc_cleanup_pop(); } /***************************************************************************** @@ -775,15 +790,17 @@ cleanup: 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_pcm_t *p_pcm = p_sys->p_snd_pcm; + snd_pcm_status_t * p_status; int i_snd_rc; mtime_t next_date; + int canc = vlc_savecancel(); /* 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 ); + snd_pcm_status_alloca(&p_status); + i_snd_rc = snd_pcm_status( p_pcm, p_status ); if( i_snd_rc < 0 ) { msg_Err( p_aout, "cannot get device status" ); @@ -794,8 +811,7 @@ static void ALSAFill( aout_instance_t * p_aout ) 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 ); - + i_snd_rc = snd_pcm_prepare( p_pcm ); if( i_snd_rc ) { msg_Err( p_aout, "cannot recover from buffer underrun" ); @@ -805,7 +821,7 @@ static void ALSAFill( aout_instance_t * p_aout ) msg_Dbg( p_aout, "recovered from buffer underrun" ); /* Get the new status */ - i_snd_rc = snd_pcm_status( p_sys->p_snd_pcm, p_status ); + i_snd_rc = snd_pcm_status( p_pcm, p_status ); if( i_snd_rc < 0 ) { msg_Err( p_aout, "cannot get device status after recovery" ); @@ -820,73 +836,93 @@ static void ALSAFill( aout_instance_t * p_aout ) /* Here the device should be in RUNNING state, p_status is valid. */ snd_pcm_sframes_t delay = snd_pcm_status_get_delay( p_status ); if( delay == 0 ) /* workaround buggy alsa drivers */ - if( snd_pcm_delay( p_sys->p_snd_pcm, &delay ) < 0 ) + if( snd_pcm_delay( p_pcm, &delay ) < 0 ) delay = 0; /* FIXME: use a positive minimal delay */ - int i_bytes = snd_pcm_frames_to_bytes( p_sys->p_snd_pcm, delay ); - next_date = mdate() + ( (mtime_t)i_bytes * 1000000 + + size_t i_bytes = snd_pcm_frames_to_bytes( p_pcm, delay ); + mtime_t delay_us = CLOCK_FREQ * i_bytes / p_aout->output.output.i_bytes_per_frame / p_aout->output.output.i_rate - * p_aout->output.output.i_frame_length ); + * p_aout->output.output.i_frame_length; #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 ); - msg_Dbg( p_aout, "Delay is %ld frames (%d bytes)", delay, i_bytes ); + msg_Dbg( p_aout, "Delay is %ld frames (%zu bytes)", delay, i_bytes ); 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 ); - - msg_Dbg( p_aout, "Next date is in %d microseconds", (int)(next_date - mdate()) ); + msg_Dbg( p_aout, "Next date: in %"PRId64" microseconds", delay_us ); #endif + next_date = mdate() + delay_us; } - p_buffer = aout_OutputNextBuffer( p_aout, next_date, + block_t *p_buffer = aout_OutputNextBuffer( p_aout, next_date, (p_aout->output.output.i_format == VLC_CODEC_SPDIFL) ); /* Audio output buffer shortage -> stop the fill process and wait */ if( p_buffer == NULL ) goto error; + block_cleanup_push( p_buffer ); 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; + int n = snd_pcm_poll_descriptors_count(p_pcm); + struct pollfd ufd[n]; + unsigned short revents; + + snd_pcm_poll_descriptors(p_pcm, ufd, n); + do + { + vlc_restorecancel(canc); + poll(ufd, n, -1); + canc = vlc_savecancel(); + snd_pcm_poll_descriptors_revents(p_pcm, ufd, n, &revents); + } + while(!revents); + + if(revents & POLLOUT) + { + i_snd_rc = snd_pcm_writei( p_pcm, p_buffer->p_buffer, + p_buffer->i_nb_samples ); + if( i_snd_rc != -ESTRPIPE ) + break; + } /* 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( vlc_object_alive (p_aout) && vlc_object_alive (p_aout->p_libvlc) && - ( i_snd_rc = snd_pcm_resume( p_sys->p_snd_pcm ) ) == -EAGAIN ) + while( ( i_snd_rc = snd_pcm_resume( p_pcm ) ) == -EAGAIN ) { - msleep( 1000000 ); + vlc_restorecancel(canc); + msleep(CLOCK_FREQ); /* device still suspended, wait... */ + canc = vlc_savecancel(); } if( i_snd_rc < 0 ) - /* Device does not supprot resuming, restart it */ - i_snd_rc = snd_pcm_prepare( p_sys->p_snd_pcm ); + /* Device does not support resuming, restart it */ + i_snd_rc = snd_pcm_prepare( p_pcm ); } if( i_snd_rc < 0 ) msg_Err( p_aout, "cannot write: %s", snd_strerror( i_snd_rc ) ); - aout_BufferFree( p_buffer ); + vlc_restorecancel(canc); + vlc_cleanup_run(); 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 GetDevices( module_config_t *p_item ); + vlc_restorecancel(canc); + msleep(p_sys->i_period_time / 2); +} /***************************************************************************** * config variable callback @@ -895,7 +931,6 @@ static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name, vlc_value_t newval, vlc_value_t oldval, void *p_unused ) { module_config_t *p_item; - int i; (void)newval; (void)oldval; (void)p_unused; @@ -906,6 +941,8 @@ static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name, /* Clear-up the current list */ if( p_item->i_list ) { + int i; + /* Keep the first entrie */ for( i = 1; i < p_item->i_list; i++ ) { @@ -918,7 +955,7 @@ static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name, } p_item->i_list = 1; - GetDevices( p_item ); + GetDevices( p_this, p_item ); /* Signal change to the interface */ p_item->b_dirty = true; @@ -927,7 +964,8 @@ 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( vlc_object_t *obj, module_config_t *p_item, + int i_card ) { int i_pcm_device = -1; int i_err = 0; @@ -961,12 +999,8 @@ static void GetDevicesForCard( module_config_t *p_item, int i_card ) if( ( i_err = snd_ctl_pcm_info( p_ctl, p_pcm_info ) ) < 0 ) { if( i_err != -ENOENT ) - { - /*printf( "get_devices_for_card(): " - "snd_ctl_pcm_info() " - "failed (%d:%d): %s.\n", i_card, - i_pcm_device, snd_strerror( -i_err ) );*/ - } + msg_Err( obj, "cannot get PCM device %d:%d infos: %s", i_card, + i_pcm_device, snd_strerror( -i_err ) ); continue; } @@ -979,42 +1013,40 @@ static void GetDevicesForCard( module_config_t *p_item, int i_card ) break; } - p_item->ppsz_list = - (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, - (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; + msg_Dbg( obj, " %s", psz_descr ); + + if( p_item ) + { + p_item->ppsz_list = xrealloc( p_item->ppsz_list, + (p_item->i_list + 2) * sizeof(char *) ); + p_item->ppsz_list_text = xrealloc( 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; + } + else + { + free( psz_device ); + free( psz_descr ); + } } snd_ctl_close( p_ctl ); } - -static void GetDevices( module_config_t *p_item ) +static void GetDevices( vlc_object_t *obj, module_config_t *p_item ) { int i_card = -1; - int i_err = 0; + int i_err; - if( ( i_err = snd_card_next( &i_card ) ) != 0 ) - { - /*printf( "snd_card_next() failed: %s", snd_strerror( -i_err ) );*/ - return; - } + msg_Dbg( obj, "Available alsa output devices:" ); + while( (i_err = snd_card_next( &i_card )) == 0 && i_card > -1 ) + GetDevicesForCard( obj, p_item, i_card ); - while( i_card > -1 ) - { - GetDevicesForCard( p_item, i_card ); - if( ( i_err = snd_card_next( &i_card ) ) != 0 ) - { - /*printf( "snd_card_next() failed: %s", snd_strerror( -i_err ) );*/ - break; - } - } + if( i_err ) + msg_Err( obj, "cannot enumerate cards: %s", snd_strerror( -i_err ) ); }