X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faccess%2Falsa.c;h=31b736650333c2f4752ef692f8f3060bbcfdbc47;hb=295b0e07523b7e9daf6c7ed76eec02d2dcfd80f0;hp=7e82ee0f2816f12d9956d9addbebf570f19b95ea;hpb=33b03f0e648774acb6dcada2266c55ff58e6c4db;p=vlc diff --git a/modules/access/alsa.c b/modules/access/alsa.c index 7e82ee0f28..31b7366503 100644 --- a/modules/access/alsa.c +++ b/modules/access/alsa.c @@ -44,10 +44,7 @@ #include #include #include -#include -#include -#include #include #include #include @@ -80,26 +77,32 @@ static void DemuxClose( vlc_object_t * ); "Caching value for Alsa captures. This " \ "value should be set in milliseconds." ) +#define HELP_TEXT N_( \ + "Use alsa:// to open the default audio input. If multiple audio " \ + "inputs are available, they will be listed in the vlc debug output. " \ + "To select hw:0,1 , use alsa://hw:0,1 ." ) + #define ALSA_DEFAULT "hw" #define CFG_PREFIX "alsa-" -vlc_module_begin(); - set_shortname( N_("Alsa") ); - set_description( N_("Alsa audio capture input") ); - set_category( CAT_INPUT ); - set_subcategory( SUBCAT_INPUT_ACCESS ); +vlc_module_begin() + set_shortname( N_("Alsa") ) + set_description( N_("Alsa audio capture input") ) + set_category( CAT_INPUT ) + set_subcategory( SUBCAT_INPUT_ACCESS ) + set_help( HELP_TEXT ) - add_shortcut( "alsa" ); - set_capability( "access_demux", 10 ); - set_callbacks( DemuxOpen, DemuxClose ); + add_shortcut( "alsa" ) + set_capability( "access_demux", 10 ) + set_callbacks( DemuxOpen, DemuxClose ) add_bool( CFG_PREFIX "stereo", true, NULL, STEREO_TEXT, STEREO_LONGTEXT, - true ); + true ) add_integer( CFG_PREFIX "samplerate", 48000, NULL, SAMPLERATE_TEXT, - SAMPLERATE_LONGTEXT, true ); + SAMPLERATE_LONGTEXT, true ) add_integer( CFG_PREFIX "caching", DEFAULT_PTS_DELAY / 1000, NULL, - CACHING_TEXT, CACHING_LONGTEXT, true ); -vlc_module_end(); + CACHING_TEXT, CACHING_LONGTEXT, true ) +vlc_module_end() /***************************************************************************** * Access: local prototypes @@ -111,35 +114,49 @@ static int Demux( demux_t * ); static block_t* GrabAudio( demux_t *p_demux ); -static int OpenAudioDev( demux_t * ); -static bool ProbeAudioDevAlsa( demux_t *, const char *psz_device ); +static int OpenAudioDev( demux_t *, const char * ); +static bool ProbeAudioDevAlsa( demux_t *, const char * ); +static char *ListAvailableDevices( demux_t *, bool b_probe ); struct demux_sys_t { - const char *psz_device; /* Alsa device from MRL */ - /* Audio */ - int i_pts; + int i_cache; unsigned int i_sample_rate; bool b_stereo; - size_t i_audio_max_frame_size; - block_t *p_block_audio; - es_out_id_t *p_es_audio; + size_t i_max_frame_size; + block_t *p_block; + es_out_id_t *p_es; /* ALSA Audio */ snd_pcm_t *p_alsa_pcm; size_t i_alsa_frame_size; int i_alsa_chunk_size; + + int64_t i_next_demux_date; /* Used to handle alsa:// as input-slave properly */ }; -static int FindMainDevice( demux_t *p_demux ) +static int FindMainDevice( demux_t *p_demux, const char *psz_device ) { - msg_Dbg( p_demux, "opening device '%s'", p_demux->p_sys->psz_device ); - if( ProbeAudioDevAlsa( p_demux, p_demux->p_sys->psz_device ) ) + if( psz_device ) { - msg_Dbg( p_demux, "'%s' is an audio device", - p_demux->p_sys->psz_device ); - OpenAudioDev( p_demux ); + msg_Dbg( p_demux, "opening device '%s'", psz_device ); + if( ProbeAudioDevAlsa( p_demux, psz_device ) ) + { + msg_Dbg( p_demux, "'%s' is an audio device", psz_device ); + OpenAudioDev( p_demux, psz_device ); + } + } + else if( ProbeAudioDevAlsa( p_demux, ALSA_DEFAULT ) ) + { + msg_Dbg( p_demux, "'%s' is an audio device", ALSA_DEFAULT ); + OpenAudioDev( p_demux, ALSA_DEFAULT ); + } + else if( ( psz_device = ListAvailableDevices( p_demux, true ) ) ) + { + msg_Dbg( p_demux, "'%s' is an audio device", psz_device ); + OpenAudioDev( p_demux, psz_device ); + free( (char *)psz_device ); } if( p_demux->p_sys->p_alsa_pcm == NULL ) @@ -147,6 +164,64 @@ static int FindMainDevice( demux_t *p_demux ) return VLC_SUCCESS; } +static char *ListAvailableDevices( demux_t *p_demux, bool b_probe ) +{ + snd_ctl_card_info_t *p_info = NULL; + snd_ctl_card_info_alloca( &p_info ); + + snd_pcm_info_t *p_pcminfo = NULL; + snd_pcm_info_alloca( &p_pcminfo ); + + if( !b_probe ) + msg_Dbg( p_demux, "Available alsa capture devices:" ); + int i_card = -1; + while( !snd_card_next( &i_card ) && i_card >= 0 ) + { + char psz_devname[10]; + snprintf( psz_devname, 10, "hw:%d", i_card ); + + snd_ctl_t *p_ctl = NULL; + if( snd_ctl_open( &p_ctl, psz_devname, 0 ) < 0 ) continue; + + snd_ctl_card_info( p_ctl, p_info ); + if( !b_probe ) + msg_Dbg( p_demux, " %s (%s)", + snd_ctl_card_info_get_id( p_info ), + snd_ctl_card_info_get_name( p_info ) ); + + int i_dev = -1; + while( !snd_ctl_pcm_next_device( p_ctl, &i_dev ) && i_dev >= 0 ) + { + snd_pcm_info_set_device( p_pcminfo, i_dev ); + snd_pcm_info_set_subdevice( p_pcminfo, 0 ); + snd_pcm_info_set_stream( p_pcminfo, SND_PCM_STREAM_CAPTURE ); + if( snd_ctl_pcm_info( p_ctl, p_pcminfo ) < 0 ) continue; + + if( !b_probe ) + msg_Dbg( p_demux, " hw:%d,%d : %s (%s)", i_card, i_dev, + snd_pcm_info_get_id( p_pcminfo ), + snd_pcm_info_get_name( p_pcminfo ) ); + else + { + char *psz_device; + if( asprintf( &psz_device, "hw:%d,%d", i_card, i_dev ) > 0 ) + { + if( ProbeAudioDevAlsa( p_demux, psz_device ) ) + { + snd_ctl_close( p_ctl ); + return psz_device; + } + else + free( psz_device ); + } + } + } + + snd_ctl_close( p_ctl ); + } + return NULL; +} + /***************************************************************************** * DemuxOpen: opens alsa device, access_demux callback ***************************************************************************** @@ -172,20 +247,23 @@ static int DemuxOpen( vlc_object_t *p_this ) p_demux->p_sys = p_sys = calloc( 1, sizeof( demux_sys_t ) ); if( p_sys == NULL ) return VLC_ENOMEM; - p_sys->i_sample_rate = var_CreateGetInteger( p_demux, CFG_PREFIX "samplerate" ); - p_sys->b_stereo = var_CreateGetBool( p_demux, CFG_PREFIX "stereo" ); - p_sys->i_pts = var_CreateGetInteger( p_demux, CFG_PREFIX "caching" ); - p_sys->psz_device = NULL; - p_sys->p_es_audio = NULL; - p_sys->p_block_audio = NULL; + p_sys->i_sample_rate = var_InheritInteger( p_demux, CFG_PREFIX "samplerate" ); + p_sys->b_stereo = var_InheritBool( p_demux, CFG_PREFIX "stereo" ); + p_sys->i_cache = var_InheritInteger( p_demux, CFG_PREFIX "caching" ); + p_sys->p_es = NULL; + p_sys->p_block = NULL; + p_sys->i_next_demux_date = -1; - if( p_demux->psz_path && *p_demux->psz_path ) - p_sys->psz_device = p_demux->psz_path; + const char *psz_device = NULL; + if( p_demux->psz_location && *p_demux->psz_location ) + psz_device = p_demux->psz_location; else - p_sys->psz_device = ALSA_DEFAULT; + ListAvailableDevices( p_demux, false ); - if( FindMainDevice( p_demux ) != VLC_SUCCESS ) + if( FindMainDevice( p_demux, psz_device ) != VLC_SUCCESS ) { + if( p_demux->psz_location && *p_demux->psz_location ) + ListAvailableDevices( p_demux, false ); DemuxClose( p_this ); return VLC_EGENERIC; } @@ -206,7 +284,7 @@ static void DemuxClose( vlc_object_t *p_this ) snd_pcm_close( p_sys->p_alsa_pcm ); } - if( p_sys->p_block_audio ) block_Release( p_sys->p_block_audio ); + if( p_sys->p_block ) block_Release( p_sys->p_block ); free( p_sys ); } @@ -217,8 +295,6 @@ static void DemuxClose( vlc_object_t *p_this ) static int DemuxControl( demux_t *p_demux, int i_query, va_list args ) { demux_sys_t *p_sys = p_demux->p_sys; - bool *pb; - int64_t *pi64; switch( i_query ) { @@ -227,18 +303,19 @@ static int DemuxControl( demux_t *p_demux, int i_query, va_list args ) case DEMUX_CAN_SEEK: case DEMUX_SET_PAUSE_STATE: case DEMUX_CAN_CONTROL_PACE: - pb = (bool*)va_arg( args, bool * ); - *pb = false; + *va_arg( args, bool * ) = false; return VLC_SUCCESS; case DEMUX_GET_PTS_DELAY: - pi64 = (int64_t*)va_arg( args, int64_t * ); - *pi64 = (int64_t)p_sys->i_pts * 1000; + *va_arg( args, int64_t * ) = (int64_t)p_sys->i_cache * 1000; return VLC_SUCCESS; case DEMUX_GET_TIME: - pi64 = (int64_t*)va_arg( args, int64_t * ); - *pi64 = mdate(); + *va_arg( args, int64_t * ) = mdate(); + return VLC_SUCCESS; + + case DEMUX_SET_NEXT_DEMUX_TIME: + p_sys->i_next_demux_date = va_arg( args, int64_t ); return VLC_SUCCESS; /* TODO implement others */ @@ -256,31 +333,47 @@ static int Demux( demux_t *p_demux ) { demux_sys_t *p_sys = p_demux->p_sys; - int i_wait = snd_pcm_wait( p_sys->p_alsa_pcm, 500 ); - switch( i_wait ) + block_t *p_block = NULL; + + do { - case 1: + if( p_block ) { - block_t *p_block = GrabAudio( p_demux ); - if( p_block ) - { - es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_block->i_pts ); - es_out_Send( p_demux->out, p_sys->p_es_audio, p_block ); - } + es_out_Send( p_demux->out, p_sys->p_es, p_block ); + p_block = NULL; } - case -EPIPE: - /* xrun */ - snd_pcm_prepare( p_sys->p_alsa_pcm ); - break; - case -ESTRPIPE: + /* Wait for data */ + int i_wait = snd_pcm_wait( p_sys->p_alsa_pcm, 10 ); /* See poll() comment in oss.c */ + switch( i_wait ) { - /* suspend */ - int i_resume = snd_pcm_resume( p_sys->p_alsa_pcm ); - if( i_resume < 0 && i_resume != -EAGAIN ) snd_pcm_prepare( p_sys->p_alsa_pcm ); - break; + case 1: + { + p_block = GrabAudio( p_demux ); + if( p_block ) + es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_block->i_pts ); + } + + /* FIXME: this is a copy paste from below. Shouldn't be needed + * twice. */ + case -EPIPE: + /* xrun */ + snd_pcm_prepare( p_sys->p_alsa_pcm ); + break; + case -ESTRPIPE: + { + /* suspend */ + int i_resume = snd_pcm_resume( p_sys->p_alsa_pcm ); + if( i_resume < 0 && i_resume != -EAGAIN ) snd_pcm_prepare( p_sys->p_alsa_pcm ); + break; + } + /* */ } - } + } while( p_block && p_sys->i_next_demux_date > 0 && + p_block->i_pts < p_sys->i_next_demux_date ); + + if( p_block ) + es_out_Send( p_demux->out, p_sys->p_es, p_block ); return 1; } @@ -295,8 +388,8 @@ static block_t* GrabAudio( demux_t *p_demux ) int i_read, i_correct; block_t *p_block; - if( p_sys->p_block_audio ) p_block = p_sys->p_block_audio; - else p_block = block_New( p_demux, p_sys->i_audio_max_frame_size ); + if( p_sys->p_block ) p_block = p_sys->p_block; + else p_block = block_New( p_demux, p_sys->i_max_frame_size ); if( !p_block ) { @@ -304,7 +397,7 @@ static block_t* GrabAudio( demux_t *p_demux ) return 0; } - p_sys->p_block_audio = p_block; + p_sys->p_block = p_block; /* ALSA */ i_read = snd_pcm_readi( p_sys->p_alsa_pcm, p_block->p_buffer, p_sys->i_alsa_chunk_size ); @@ -338,7 +431,7 @@ static block_t* GrabAudio( demux_t *p_demux ) if( i_read <= 0 ) return 0; p_block->i_buffer = i_read; - p_sys->p_block_audio = 0; + p_sys->p_block = 0; /* Correct the date because of kernel buffering */ i_correct = i_read; @@ -349,11 +442,11 @@ static block_t* GrabAudio( demux_t *p_demux ) { size_t i_correction_delta = delay * p_sys->i_alsa_frame_size; /* Test for overrun */ - if( i_correction_delta > p_sys->i_audio_max_frame_size ) + if( i_correction_delta > p_sys->i_max_frame_size ) { msg_Warn( p_demux, "ALSA read overrun (%zu > %zu)", - i_correction_delta, p_sys->i_audio_max_frame_size ); - i_correction_delta = p_sys->i_audio_max_frame_size; + i_correction_delta, p_sys->i_max_frame_size ); + i_correction_delta = p_sys->i_max_frame_size; snd_pcm_prepare( p_sys->p_alsa_pcm ); } i_correct += i_correction_delta; @@ -376,10 +469,9 @@ static block_t* GrabAudio( demux_t *p_demux ) /***************************************************************************** * OpenAudioDev: open and set up the audio device and probe for capabilities *****************************************************************************/ -static int OpenAudioDevAlsa( demux_t *p_demux ) +static int OpenAudioDevAlsa( demux_t *p_demux, const char *psz_device ) { demux_sys_t *p_sys = p_demux->p_sys; - const char *psz_device = p_sys->psz_device; p_sys->p_alsa_pcm = NULL; snd_pcm_hw_params_t *p_hw_params = NULL; snd_pcm_uframes_t buffer_size; @@ -438,11 +530,7 @@ static int OpenAudioDevAlsa( demux_t *p_demux ) } /* Set sample rate */ -#ifdef HAVE_ALSA_NEW_API i_err = snd_pcm_hw_params_set_rate_near( p_sys->p_alsa_pcm, p_hw_params, &p_sys->i_sample_rate, NULL ); -#else - i_err = snd_pcm_hw_params_set_rate_near( p_sys->p_alsa_pcm, p_hw_params, p_sys->i_sample_rate, NULL ); -#endif if( i_err < 0 ) { msg_Err( p_demux, "ALSA: cannot set sample rate (%s)", @@ -480,11 +568,7 @@ static int OpenAudioDevAlsa( demux_t *p_demux ) /* Set period time */ unsigned int period_time = buffer_time / 4; -#ifdef HAVE_ALSA_NEW_API i_err = snd_pcm_hw_params_set_period_time_near( p_sys->p_alsa_pcm, p_hw_params, &period_time, 0 ); -#else - i_err = snd_pcm_hw_params_set_period_time_near( p_sys->p_alsa_pcm, p_hw_params, period_time, 0 ); -#endif if( i_err < 0 ) { msg_Err( p_demux, "ALSA: cannot set period time (%s)", @@ -493,11 +577,7 @@ static int OpenAudioDevAlsa( demux_t *p_demux ) } /* Set buffer time */ -#ifdef HAVE_ALSA_NEW_API i_err = snd_pcm_hw_params_set_buffer_time_near( p_sys->p_alsa_pcm, p_hw_params, &buffer_time, 0 ); -#else - i_err = snd_pcm_hw_params_set_buffer_time_near( p_sys->p_alsa_pcm, p_hw_params, buffer_time, 0 ); -#endif if( i_err < 0 ) { msg_Err( p_demux, "ALSA: cannot set buffer time (%s)", @@ -529,7 +609,7 @@ static int OpenAudioDevAlsa( demux_t *p_demux ) p_sys->i_alsa_chunk_size = chunk_size; p_sys->i_alsa_frame_size = bits_per_frame / 8; - p_sys->i_audio_max_frame_size = chunk_size * bits_per_frame / 8; + p_sys->i_max_frame_size = chunk_size * bits_per_frame / 8; snd_pcm_hw_params_free( p_hw_params ); p_hw_params = NULL; @@ -557,14 +637,14 @@ static int OpenAudioDevAlsa( demux_t *p_demux ) } -static int OpenAudioDev( demux_t *p_demux ) +static int OpenAudioDev( demux_t *p_demux, const char *psz_device ) { demux_sys_t *p_sys = p_demux->p_sys; - if( OpenAudioDevAlsa( p_demux ) != VLC_SUCCESS ) + if( OpenAudioDevAlsa( p_demux, psz_device ) != VLC_SUCCESS ) return VLC_EGENERIC; msg_Dbg( p_demux, "opened adev=`%s' %s %dHz", - p_sys->psz_device, p_sys->b_stereo ? "stereo" : "mono", + psz_device, p_sys->b_stereo ? "stereo" : "mono", p_sys->i_sample_rate ); es_format_t fmt; @@ -579,7 +659,7 @@ static int OpenAudioDev( demux_t *p_demux ) msg_Dbg( p_demux, "new audio es %d channels %dHz", fmt.audio.i_channels, fmt.audio.i_rate ); - p_sys->p_es_audio = es_out_Add( p_demux->out, &fmt ); + p_sys->p_es = es_out_Add( p_demux->out, &fmt ); return VLC_SUCCESS; }