00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030 #include <errno.h>
00031 #include <string.h>
00032 #include <stdlib.h>
00033
00034 #include <vlc/vlc.h>
00035
00036 #include <vlc/aout.h>
00037
00038 #include "aout_internal.h"
00039
00040
00041
00042 #define ALSA_PCM_NEW_HW_PARAMS_API
00043 #define ALSA_PCM_NEW_SW_PARAMS_API
00044 #include <alsa/asoundlib.h>
00045
00046
00047
00048
00049
00050
00051
00052 struct aout_sys_t
00053 {
00054 snd_pcm_t * p_snd_pcm;
00055 unsigned int i_period_time;
00056
00057 #ifdef ALSA_DEBUG
00058 snd_output_t * p_snd_stderr;
00059 #endif
00060
00061 int b_playing;
00062 mtime_t start_date;
00063
00064 vlc_mutex_t lock;
00065 vlc_cond_t wait ;
00066
00067 snd_pcm_status_t *p_status;
00068 };
00069
00070 #define A52_FRAME_NB 1536
00071
00072
00073
00074
00075
00076 #define ALSA_DEFAULT_PERIOD_SIZE 1024
00077 #define ALSA_DEFAULT_BUFFER_SIZE ( ALSA_DEFAULT_PERIOD_SIZE << 8 )
00078 #define ALSA_SPDIF_PERIOD_SIZE A52_FRAME_NB
00079 #define ALSA_SPDIF_BUFFER_SIZE ( ALSA_SPDIF_PERIOD_SIZE << 4 )
00080
00081
00082
00083
00084 #define DEFAULT_ALSA_DEVICE N_("default")
00085
00086
00087
00088
00089 static int Open ( vlc_object_t * );
00090 static void Close ( vlc_object_t * );
00091 static void Play ( aout_instance_t * );
00092 static int ALSAThread ( aout_instance_t * );
00093 static void ALSAFill ( aout_instance_t * );
00094 static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name,
00095 vlc_value_t newval, vlc_value_t oldval, void *p_unused );
00096
00097
00098
00099
00100 static char *ppsz_devices[] = { "default" };
00101 static char *ppsz_devices_text[] = { N_("Default") };
00102 vlc_module_begin();
00103 set_shortname( "ALSA" );
00104 set_description( _("ALSA audio output") );
00105 set_category( CAT_AUDIO );
00106 set_subcategory( SUBCAT_AUDIO_AOUT );
00107 add_string( "alsadev", DEFAULT_ALSA_DEVICE, aout_FindAndRestart,
00108 N_("ALSA Device Name"), NULL, VLC_FALSE );
00109 change_string_list( ppsz_devices, ppsz_devices_text, FindDevicesCallback );
00110 change_action_add( FindDevicesCallback, N_("Refresh list") );
00111
00112 set_capability( "audio output", 150 );
00113 set_callbacks( Open, Close );
00114 vlc_module_end();
00115
00116
00117
00118
00119 static void Probe( aout_instance_t * p_aout,
00120 const char * psz_device, const char * psz_iec_device,
00121 int *pi_snd_pcm_format )
00122 {
00123 struct aout_sys_t * p_sys = p_aout->output.p_sys;
00124 vlc_value_t val, text;
00125 int i_ret;
00126
00127 var_Create ( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
00128 text.psz_string = _("Audio Device");
00129 var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
00130
00131
00132
00133
00134
00135
00136 if ( !(i_ret = snd_pcm_open( &p_sys->p_snd_pcm, psz_device,
00137 SND_PCM_STREAM_PLAYBACK,
00138 SND_PCM_NONBLOCK ) ) )
00139 {
00140 int i_channels;
00141 snd_pcm_hw_params_t * p_hw;
00142 snd_pcm_hw_params_alloca (&p_hw);
00143
00144 if ( snd_pcm_hw_params_any( p_sys->p_snd_pcm, p_hw ) < 0 )
00145 {
00146 msg_Warn( p_aout, "unable to retrieve initial hardware parameters"
00147 ", disabling linear PCM audio" );
00148 snd_pcm_close( p_sys->p_snd_pcm );
00149 var_Destroy( p_aout, "audio-device" );
00150 return;
00151 }
00152
00153 if ( snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, p_hw,
00154 *pi_snd_pcm_format ) < 0 )
00155 {
00156 int i_snd_rc = -1;
00157
00158 if( *pi_snd_pcm_format != SND_PCM_FORMAT_S16 )
00159 {
00160 *pi_snd_pcm_format = SND_PCM_FORMAT_S16;
00161 i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm,
00162 p_hw, *pi_snd_pcm_format );
00163 }
00164 if ( i_snd_rc < 0 )
00165 {
00166 msg_Warn( p_aout, "unable to set stream sample size and "
00167 "word order, disabling linear PCM audio" );
00168 snd_pcm_close( p_sys->p_snd_pcm );
00169 var_Destroy( p_aout, "audio-device" );
00170 return;
00171 }
00172 }
00173
00174 i_channels = aout_FormatNbChannels( &p_aout->output.output );
00175
00176 while ( i_channels > 0 )
00177 {
00178 if ( !snd_pcm_hw_params_test_channels( p_sys->p_snd_pcm, p_hw,
00179 i_channels ) )
00180 {
00181 switch ( i_channels )
00182 {
00183 case 1:
00184 val.i_int = AOUT_VAR_MONO;
00185 text.psz_string = N_("Mono");
00186 var_Change( p_aout, "audio-device",
00187 VLC_VAR_ADDCHOICE, &val, &text );
00188 break;
00189 case 2:
00190 val.i_int = AOUT_VAR_STEREO;
00191 text.psz_string = N_("Stereo");
00192 var_Change( p_aout, "audio-device",
00193 VLC_VAR_ADDCHOICE, &val, &text );
00194 var_Set( p_aout, "audio-device", val );
00195 break;
00196 case 4:
00197 val.i_int = AOUT_VAR_2F2R;
00198 text.psz_string = N_("2 Front 2 Rear");
00199 var_Change( p_aout, "audio-device",
00200 VLC_VAR_ADDCHOICE, &val, &text );
00201 break;
00202 case 6:
00203 val.i_int = AOUT_VAR_5_1;
00204 text.psz_string = N_("5.1");
00205 var_Change( p_aout, "audio-device",
00206 VLC_VAR_ADDCHOICE, &val, &text );
00207 break;
00208 }
00209 }
00210
00211 --i_channels;
00212 }
00213
00214
00215 i_channels = aout_FormatNbChannels( &p_aout->output.output );
00216 var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL );
00217 if( val.i_int <= 0 && i_channels == 1 )
00218 {
00219 if ( !snd_pcm_hw_params_test_channels( p_sys->p_snd_pcm, p_hw, 2 ))
00220 {
00221 val.i_int = AOUT_VAR_STEREO;
00222 text.psz_string = N_("Stereo");
00223 var_Change( p_aout, "audio-device",
00224 VLC_VAR_ADDCHOICE, &val, &text );
00225 var_Set( p_aout, "audio-device", val );
00226 }
00227 }
00228
00229
00230 snd_pcm_close( p_sys->p_snd_pcm );
00231 }
00232 else if ( i_ret == -EBUSY )
00233 {
00234 msg_Warn( p_aout, "audio device: %s is already in use", psz_device );
00235 }
00236
00237
00238 if ( psz_iec_device )
00239 {
00240
00241 if ( !(i_ret = snd_pcm_open( &p_sys->p_snd_pcm, psz_iec_device,
00242 SND_PCM_STREAM_PLAYBACK,
00243 SND_PCM_NONBLOCK ) ) )
00244 {
00245 val.i_int = AOUT_VAR_SPDIF;
00246 text.psz_string = N_("A/52 over S/PDIF");
00247 var_Change( p_aout, "audio-device",
00248 VLC_VAR_ADDCHOICE, &val, &text );
00249 if( config_GetInt( p_aout, "spdif" ) )
00250 var_Set( p_aout, "audio-device", val );
00251
00252 snd_pcm_close( p_sys->p_snd_pcm );
00253 }
00254 else if ( i_ret == -EBUSY )
00255 {
00256 msg_Warn( p_aout, "audio device: %s is already in use",
00257 psz_iec_device );
00258 }
00259 }
00260
00261 var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL );
00262 if( val.i_int <= 0 )
00263 {
00264
00265 msg_Dbg( p_aout, "failed to find a useable alsa configuration" );
00266 var_Destroy( p_aout, "audio-device" );
00267 return;
00268 }
00269
00270
00271 var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
00272 val.b_bool = VLC_TRUE;
00273 var_Set( p_aout, "intf-change", val );
00274 }
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284 static int Open( vlc_object_t *p_this )
00285 {
00286 aout_instance_t * p_aout = (aout_instance_t *)p_this;
00287 struct aout_sys_t * p_sys;
00288 vlc_value_t val;
00289
00290 char psz_default_iec_device[128];
00291
00292 char * psz_device, * psz_iec_device;
00293
00294
00295 int i_vlc_pcm_format;
00296 int i_snd_pcm_format;
00297
00298 snd_pcm_uframes_t i_buffer_size = 0;
00299 snd_pcm_uframes_t i_period_size = 0;
00300 int i_channels = 0;
00301
00302 snd_pcm_hw_params_t *p_hw;
00303 snd_pcm_sw_params_t *p_sw;
00304
00305 int i_snd_rc = -1;
00306 unsigned int i_old_rate;
00307 vlc_bool_t b_retry = VLC_TRUE;
00308
00309
00310 p_aout->output.p_sys = p_sys = malloc( sizeof( aout_sys_t ) );
00311 if( p_sys == NULL )
00312 {
00313 msg_Err( p_aout, "out of memory" );
00314 return VLC_ENOMEM;
00315 }
00316 p_sys->b_playing = VLC_FALSE;
00317 p_sys->start_date = 0;
00318 vlc_cond_init( p_aout, &p_sys->wait );
00319 vlc_mutex_init( p_aout, &p_sys->lock );
00320
00321
00322 if( (psz_device = config_GetPsz( p_aout, "alsadev" )) == NULL )
00323 {
00324 msg_Err( p_aout, "no audio device given (maybe \"default\" ?)" );
00325 free( p_sys );
00326 return VLC_EGENERIC;
00327 }
00328
00329
00330
00331
00332 if( AOUT_FMT_NON_LINEAR( &p_aout->output.output )
00333 && !strcmp( psz_device, DEFAULT_ALSA_DEVICE ) )
00334 {
00335 snprintf( psz_default_iec_device, sizeof(psz_default_iec_device),
00336 "iec958:AES0=0x%x,AES1=0x%x,AES2=0x%x,AES3=0x%x",
00337 IEC958_AES0_CON_EMPHASIS_NONE | IEC958_AES0_NONAUDIO,
00338 IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
00339 0,
00340 ( p_aout->output.output.i_rate == 48000 ?
00341 IEC958_AES3_CON_FS_48000 :
00342 ( p_aout->output.output.i_rate == 44100 ?
00343 IEC958_AES3_CON_FS_44100 : IEC958_AES3_CON_FS_32000 ) ) );
00344 psz_iec_device = psz_default_iec_device;
00345 }
00346 else if( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
00347 {
00348 psz_iec_device = psz_device;
00349 }
00350 else
00351 {
00352 psz_iec_device = NULL;
00353 }
00354
00355
00356
00357 if( p_aout->p_libvlc->i_cpu & CPU_CAPABILITY_FPU )
00358 {
00359 i_vlc_pcm_format = VLC_FOURCC('f','l','3','2');
00360 i_snd_pcm_format = SND_PCM_FORMAT_FLOAT;
00361 }
00362 else
00363 {
00364 i_vlc_pcm_format = AOUT_FMT_S16_NE;
00365 i_snd_pcm_format = SND_PCM_FORMAT_S16;
00366 }
00367
00368
00369
00370 if ( var_Type( p_aout, "audio-device" ) == 0 )
00371 {
00372 Probe( p_aout, psz_device, psz_iec_device, &i_snd_pcm_format );
00373 }
00374
00375 if ( var_Get( p_aout, "audio-device", &val ) < 0 )
00376 {
00377 free( p_sys );
00378 free( psz_device );
00379 return VLC_EGENERIC;
00380 }
00381
00382 p_aout->output.output.i_format = i_vlc_pcm_format;
00383 if ( val.i_int == AOUT_VAR_5_1 )
00384 {
00385 p_aout->output.output.i_physical_channels
00386 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
00387 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
00388 | AOUT_CHAN_LFE;
00389 free( psz_device );
00390 psz_device = strdup( "surround51" );
00391 }
00392 else if ( val.i_int == AOUT_VAR_2F2R )
00393 {
00394 p_aout->output.output.i_physical_channels
00395 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
00396 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
00397 free( psz_device );
00398 psz_device = strdup( "surround40" );
00399 }
00400 else if ( val.i_int == AOUT_VAR_STEREO )
00401 {
00402 p_aout->output.output.i_physical_channels
00403 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
00404 }
00405 else if ( val.i_int == AOUT_VAR_MONO )
00406 {
00407 p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
00408 }
00409 else if( val.i_int != AOUT_VAR_SPDIF )
00410 {
00411
00412 msg_Err( p_aout, "internal: can't find audio-device (%i)", val.i_int );
00413 free( p_sys );
00414 free( psz_device );
00415 return VLC_EGENERIC;
00416 }
00417
00418 #ifdef ALSA_DEBUG
00419 snd_output_stdio_attach( &p_sys->p_snd_stderr, stderr, 0 );
00420 #endif
00421
00422
00423 if ( val.i_int == AOUT_VAR_SPDIF )
00424 {
00425 if ( ( i_snd_rc = snd_pcm_open( &p_sys->p_snd_pcm, psz_iec_device,
00426 SND_PCM_STREAM_PLAYBACK, 0 ) ) < 0 )
00427 {
00428 msg_Err( p_aout, "cannot open ALSA device `%s' (%s)",
00429 psz_iec_device, snd_strerror( i_snd_rc ) );
00430 free( p_sys );
00431 free( psz_device );
00432 return VLC_EGENERIC;
00433 }
00434 i_buffer_size = ALSA_SPDIF_BUFFER_SIZE;
00435 i_snd_pcm_format = SND_PCM_FORMAT_S16;
00436 i_channels = 2;
00437
00438 i_vlc_pcm_format = VLC_FOURCC('s','p','d','i');
00439 p_aout->output.i_nb_samples = i_period_size = ALSA_SPDIF_PERIOD_SIZE;
00440 p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
00441 p_aout->output.output.i_frame_length = A52_FRAME_NB;
00442
00443 aout_VolumeNoneInit( p_aout );
00444 }
00445 else
00446 {
00447 int i;
00448
00449 msg_Dbg( p_aout, "opening ALSA device `%s'", psz_device );
00450
00451
00452
00453
00454
00455
00456 for( i = 10; i >= 0; i-- )
00457 {
00458 if ( ( i_snd_rc = snd_pcm_open( &p_sys->p_snd_pcm, psz_device,
00459 SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK ) ) == -EBUSY )
00460 {
00461 if( i ) msleep( 100000 );
00462 else msg_Err( p_aout, "audio device: %s is already in use",
00463 psz_device );
00464 continue;
00465 }
00466 break;
00467 }
00468 if( i_snd_rc < 0 )
00469 {
00470 msg_Err( p_aout, "cannot open ALSA device `%s' (%s)",
00471 psz_device, snd_strerror( i_snd_rc ) );
00472 free( p_sys );
00473 free( psz_device );
00474 return VLC_EGENERIC;
00475 }
00476
00477
00478 snd_pcm_nonblock( p_sys->p_snd_pcm, 0 );
00479
00480 i_buffer_size = ALSA_DEFAULT_BUFFER_SIZE;
00481 i_channels = aout_FormatNbChannels( &p_aout->output.output );
00482
00483 p_aout->output.i_nb_samples = i_period_size = ALSA_DEFAULT_PERIOD_SIZE;
00484
00485 aout_VolumeSoftInit( p_aout );
00486 }
00487
00488
00489 free( psz_device );
00490
00491 p_aout->output.pf_play = Play;
00492
00493 snd_pcm_hw_params_alloca(&p_hw);
00494 snd_pcm_sw_params_alloca(&p_sw);
00495
00496
00497
00498 while ( b_retry )
00499 {
00500 b_retry = VLC_FALSE;
00501
00502
00503 if ( ( i_snd_rc = snd_pcm_hw_params_any( p_sys->p_snd_pcm, p_hw ) ) < 0 )
00504 {
00505 msg_Err( p_aout, "unable to retrieve initial hardware parameters (%s)",
00506 snd_strerror( i_snd_rc ) );
00507 goto error;
00508 }
00509
00510
00511 if ( ( i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, p_hw,
00512 i_snd_pcm_format ) ) < 0 )
00513 {
00514 if( i_snd_pcm_format != SND_PCM_FORMAT_S16 )
00515 {
00516 i_snd_pcm_format = SND_PCM_FORMAT_S16;
00517 i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm,
00518 p_hw, i_snd_pcm_format );
00519 }
00520 if ( i_snd_rc < 0 )
00521 {
00522 msg_Err( p_aout, "unable to set stream sample size and "
00523 "word order (%s)", snd_strerror( i_snd_rc ) );
00524 goto error;
00525 }
00526 }
00527 if( i_vlc_pcm_format != VLC_FOURCC('s','p','d','i') )
00528 switch( i_snd_pcm_format )
00529 {
00530 case SND_PCM_FORMAT_FLOAT:
00531 i_vlc_pcm_format = VLC_FOURCC('f','l','3','2');
00532 break;
00533 case SND_PCM_FORMAT_S16:
00534 i_vlc_pcm_format = AOUT_FMT_S16_NE;
00535 break;
00536 }
00537 p_aout->output.output.i_format = i_vlc_pcm_format;
00538
00539 if ( ( i_snd_rc = snd_pcm_hw_params_set_access( p_sys->p_snd_pcm, p_hw,
00540 SND_PCM_ACCESS_RW_INTERLEAVED ) ) < 0 )
00541 {
00542 msg_Err( p_aout, "unable to set interleaved stream format (%s)",
00543 snd_strerror( i_snd_rc ) );
00544 goto error;
00545 }
00546
00547
00548 if ( ( i_snd_rc = snd_pcm_hw_params_set_channels( p_sys->p_snd_pcm, p_hw,
00549 i_channels ) ) < 0 )
00550 {
00551 msg_Err( p_aout, "unable to set number of output channels (%s)",
00552 snd_strerror( i_snd_rc ) );
00553 goto error;
00554 }
00555
00556
00557 i_old_rate = p_aout->output.output.i_rate;
00558 #ifdef HAVE_ALSA_NEW_API
00559 i_snd_rc = snd_pcm_hw_params_set_rate_near( p_sys->p_snd_pcm, p_hw,
00560 &p_aout->output.output.i_rate,
00561 NULL );
00562 #else
00563 i_snd_rc = snd_pcm_hw_params_set_rate_near( p_sys->p_snd_pcm, p_hw,
00564 p_aout->output.output.i_rate,
00565 NULL );
00566 #endif
00567 if( i_snd_rc < 0 || p_aout->output.output.i_rate != i_old_rate )
00568 {
00569 msg_Warn( p_aout, "The rate %d Hz is not supported by your hardware. "
00570 "Using %d Hz instead.\n", i_old_rate,
00571 p_aout->output.output.i_rate );
00572 }
00573
00574
00575 #ifdef HAVE_ALSA_NEW_API
00576 if ( ( i_snd_rc = snd_pcm_hw_params_set_buffer_size_near( p_sys->p_snd_pcm,
00577 p_hw, &i_buffer_size ) ) < 0 )
00578 #else
00579 if ( ( i_snd_rc = snd_pcm_hw_params_set_buffer_size_near( p_sys->p_snd_pcm,
00580 p_hw, i_buffer_size ) ) < 0 )
00581 #endif
00582 {
00583 msg_Err( p_aout, "unable to set buffer size (%s)",
00584 snd_strerror( i_snd_rc ) );
00585 goto error;
00586 }
00587
00588
00589 #ifdef HAVE_ALSA_NEW_API
00590 if ( ( i_snd_rc = snd_pcm_hw_params_set_period_size_near( p_sys->p_snd_pcm,
00591 p_hw, &i_period_size, NULL ) ) < 0 )
00592 #else
00593 if ( ( i_snd_rc = snd_pcm_hw_params_set_period_size_near( p_sys->p_snd_pcm,
00594 p_hw, i_period_size, NULL ) ) < 0 )
00595 #endif
00596 {
00597 msg_Err( p_aout, "unable to set period size (%s)",
00598 snd_strerror( i_snd_rc ) );
00599 goto error;
00600 }
00601 p_aout->output.i_nb_samples = i_period_size;
00602
00603
00604 if ( ( i_snd_rc = snd_pcm_hw_params( p_sys->p_snd_pcm, p_hw ) ) < 0 )
00605 {
00606 if ( b_retry == VLC_FALSE &&
00607 i_snd_pcm_format == SND_PCM_FORMAT_FLOAT)
00608 {
00609 b_retry = VLC_TRUE;
00610 i_snd_pcm_format = SND_PCM_FORMAT_S16;
00611 p_aout->output.output.i_format = AOUT_FMT_S16_NE;
00612 msg_Warn( p_aout, "unable to commit hardware configuration "
00613 "with fl32 samples. Retrying with s16l (%s)", snd_strerror( i_snd_rc ) );
00614 }
00615 else
00616 {
00617 msg_Err( p_aout, "unable to commit hardware configuration (%s)",
00618 snd_strerror( i_snd_rc ) );
00619 goto error;
00620 }
00621 }
00622 }
00623
00624 #ifdef HAVE_ALSA_NEW_API
00625 if( ( i_snd_rc = snd_pcm_hw_params_get_period_time( p_hw,
00626 &p_sys->i_period_time, NULL ) ) < 0 )
00627 #else
00628 if( ( p_sys->i_period_time =
00629 (int)snd_pcm_hw_params_get_period_time( p_hw, NULL ) ) < 0 )
00630 #endif
00631 {
00632 msg_Err( p_aout, "unable to get period time (%s)",
00633 snd_strerror( i_snd_rc ) );
00634 goto error;
00635 }
00636
00637
00638 snd_pcm_sw_params_current( p_sys->p_snd_pcm, p_sw );
00639
00640 i_snd_rc = snd_pcm_sw_params_set_sleep_min( p_sys->p_snd_pcm, p_sw, 0 );
00641
00642 i_snd_rc = snd_pcm_sw_params_set_avail_min( p_sys->p_snd_pcm, p_sw,
00643 p_aout->output.i_nb_samples );
00644
00645
00646 if ( snd_pcm_sw_params( p_sys->p_snd_pcm, p_sw ) < 0 )
00647 {
00648 msg_Err( p_aout, "unable to set software configuration" );
00649 goto error;
00650 }
00651
00652 #ifdef ALSA_DEBUG
00653 snd_output_printf( p_sys->p_snd_stderr, "\nALSA hardware setup:\n\n" );
00654 snd_pcm_dump_hw_setup( p_sys->p_snd_pcm, p_sys->p_snd_stderr );
00655 snd_output_printf( p_sys->p_snd_stderr, "\nALSA software setup:\n\n" );
00656 snd_pcm_dump_sw_setup( p_sys->p_snd_pcm, p_sys->p_snd_stderr );
00657 snd_output_printf( p_sys->p_snd_stderr, "\n" );
00658 #endif
00659
00660
00661 if( vlc_thread_create( p_aout, "aout", ALSAThread,
00662 VLC_THREAD_PRIORITY_OUTPUT, VLC_FALSE ) )
00663 {
00664 msg_Err( p_aout, "cannot create ALSA thread (%s)", strerror(errno) );
00665 goto error;
00666 }
00667
00668 return 0;
00669
00670 error:
00671 snd_pcm_close( p_sys->p_snd_pcm );
00672 #ifdef ALSA_DEBUG
00673 snd_output_close( p_sys->p_snd_stderr );
00674 #endif
00675 free( p_sys );
00676 return VLC_EGENERIC;
00677 }
00678
00679
00680
00681
00682 static void Play( aout_instance_t *p_aout )
00683 {
00684 if( !p_aout->output.p_sys->b_playing )
00685 {
00686 p_aout->output.p_sys->b_playing = 1;
00687
00688
00689 p_aout->output.p_sys->start_date =
00690 aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
00691
00692
00693 vlc_mutex_lock( &p_aout->output.p_sys->lock );
00694 vlc_cond_signal( &p_aout->output.p_sys->wait );
00695 vlc_mutex_unlock( &p_aout->output.p_sys->lock );
00696 }
00697 }
00698
00699
00700
00701
00702 static void Close( vlc_object_t *p_this )
00703 {
00704 aout_instance_t *p_aout = (aout_instance_t *)p_this;
00705 struct aout_sys_t * p_sys = p_aout->output.p_sys;
00706 int i_snd_rc;
00707
00708
00709 vlc_mutex_lock( &p_aout->output.p_sys->lock );
00710 vlc_cond_signal( &p_aout->output.p_sys->wait );
00711 vlc_mutex_unlock( &p_aout->output.p_sys->lock );
00712
00713 p_aout->b_die = VLC_TRUE;
00714 vlc_thread_join( p_aout );
00715 p_aout->b_die = VLC_FALSE;
00716
00717 i_snd_rc = snd_pcm_close( p_sys->p_snd_pcm );
00718
00719 if( i_snd_rc > 0 )
00720 {
00721 msg_Err( p_aout, "failed closing ALSA device (%s)",
00722 snd_strerror( i_snd_rc ) );
00723 }
00724
00725 #ifdef ALSA_DEBUG
00726 snd_output_close( p_sys->p_snd_stderr );
00727 #endif
00728
00729 free( p_sys );
00730 }
00731
00732
00733
00734
00735 static int ALSAThread( aout_instance_t * p_aout )
00736 {
00737 p_aout->output.p_sys->p_status =
00738 (snd_pcm_status_t *)malloc(snd_pcm_status_sizeof());
00739
00740
00741 vlc_mutex_lock( &p_aout->output.p_sys->lock );
00742 if( !p_aout->output.p_sys->start_date )
00743 vlc_cond_wait( &p_aout->output.p_sys->wait,
00744 &p_aout->output.p_sys->lock );
00745 vlc_mutex_unlock( &p_aout->output.p_sys->lock );
00746
00747 mwait( p_aout->output.p_sys->start_date - AOUT_PTS_TOLERANCE / 4 );
00748
00749 while ( !p_aout->b_die )
00750 {
00751 ALSAFill( p_aout );
00752 }
00753
00754 free( p_aout->output.p_sys->p_status );
00755 return 0;
00756 }
00757
00758
00759
00760
00761 static void ALSAFill( aout_instance_t * p_aout )
00762 {
00763 struct aout_sys_t * p_sys = p_aout->output.p_sys;
00764
00765 aout_buffer_t * p_buffer;
00766 snd_pcm_status_t * p_status = p_sys->p_status;
00767 snd_timestamp_t ts_next;
00768 int i_snd_rc;
00769 mtime_t next_date;
00770
00771
00772 {
00773
00774 i_snd_rc = snd_pcm_status( p_sys->p_snd_pcm, p_status );
00775 if( i_snd_rc < 0 )
00776 {
00777 msg_Err( p_aout, "unable to get the device's status (%s)",
00778 snd_strerror( i_snd_rc ) );
00779
00780 msleep( p_sys->i_period_time >> 1 );
00781 return;
00782 }
00783
00784
00785 if( snd_pcm_status_get_state( p_status ) == SND_PCM_STATE_XRUN )
00786 {
00787
00788 i_snd_rc = snd_pcm_prepare( p_sys->p_snd_pcm );
00789
00790 if( i_snd_rc == 0 )
00791 {
00792 msg_Warn( p_aout, "recovered from buffer underrun" );
00793
00794
00795 i_snd_rc = snd_pcm_status( p_sys->p_snd_pcm, p_status );
00796 if( i_snd_rc < 0 )
00797 {
00798 msg_Err( p_aout, "unable to get the device's status after "
00799 "recovery (%s)", snd_strerror( i_snd_rc ) );
00800
00801 msleep( p_sys->i_period_time >> 1 );
00802 return;
00803 }
00804 }
00805 else
00806 {
00807 msg_Err( p_aout, "unable to recover from buffer underrun" );
00808
00809 msleep( p_sys->i_period_time >> 1 );
00810 return;
00811 }
00812
00813
00814 next_date = mdate();
00815 }
00816 else
00817 {
00818
00819
00820
00821 snd_pcm_status_get_tstamp( p_status, &ts_next );
00822 next_date = (mtime_t)ts_next.tv_sec * 1000000 + ts_next.tv_usec;
00823 if( next_date )
00824 {
00825 next_date += (mtime_t)snd_pcm_status_get_delay(p_status)
00826 * 1000000 / p_aout->output.output.i_rate;
00827 }
00828 else
00829 {
00830
00831
00832 snd_pcm_sframes_t delay;
00833 ssize_t i_bytes = 0;
00834
00835 if( !snd_pcm_delay( p_sys->p_snd_pcm, &delay ) )
00836 {
00837 i_bytes = snd_pcm_frames_to_bytes(p_sys->p_snd_pcm, delay);
00838 }
00839 next_date = mdate() + (mtime_t)i_bytes * 1000000
00840 / p_aout->output.output.i_bytes_per_frame
00841 / p_aout->output.output.i_rate
00842 * p_aout->output.output.i_frame_length;
00843 }
00844 }
00845
00846 p_buffer = aout_OutputNextBuffer( p_aout, next_date,
00847 (p_aout->output.output.i_format ==
00848 VLC_FOURCC('s','p','d','i')) );
00849
00850
00851 if( p_buffer == NULL )
00852 {
00853 msleep( p_sys->i_period_time >> 1 );
00854 return;
00855 }
00856
00857 i_snd_rc = snd_pcm_writei( p_sys->p_snd_pcm, p_buffer->p_buffer,
00858 p_buffer->i_nb_samples );
00859
00860 if( i_snd_rc < 0 )
00861 {
00862 msg_Err( p_aout, "write failed (%s)",
00863 snd_strerror( i_snd_rc ) );
00864 }
00865
00866 aout_BufferFree( p_buffer );
00867 }
00868 }
00869
00870 static void GetDevicesForCard(module_config_t *p_item, int i_card);
00871 static void GetDevices( module_config_t *p_item );
00872
00873
00874
00875
00876 static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name,
00877 vlc_value_t newval, vlc_value_t oldval, void *p_unused )
00878 {
00879 module_config_t *p_item;
00880 int i;
00881
00882 p_item = config_FindConfig( p_this, psz_name );
00883 if( !p_item ) return VLC_SUCCESS;
00884
00885
00886 if( p_item->i_list )
00887 {
00888
00889 for( i = 1; i < p_item->i_list; i++ )
00890 {
00891 free( p_item->ppsz_list[i] );
00892 free( p_item->ppsz_list_text[i] );
00893 }
00894
00895 p_item->ppsz_list[i] = NULL;
00896 p_item->ppsz_list_text[i] = NULL;
00897 }
00898 p_item->i_list = 1;
00899
00900 GetDevices( p_item );
00901
00902
00903 p_item->b_dirty = VLC_TRUE;
00904
00905 return VLC_SUCCESS;
00906
00907 }
00908
00909
00910 static void GetDevicesForCard(module_config_t *p_item, int i_card)
00911 {
00912 int i_pcm_device = -1;
00913 int i_err = 0;
00914 snd_pcm_info_t *p_pcm_info;
00915 snd_ctl_t *p_ctl;
00916 char psz_dev[64];
00917 char *psz_card_name;
00918
00919 sprintf(psz_dev, "hw:%i", i_card);
00920
00921 if (( i_err = snd_ctl_open(&p_ctl, psz_dev, 0)) < 0 )
00922 {
00923 return;
00924 }
00925
00926 if ((i_err = snd_card_get_name(i_card, &psz_card_name)) != 0)
00927 {
00928 psz_card_name = _("Unknown soundcard");
00929 }
00930
00931 snd_pcm_info_alloca(&p_pcm_info);
00932
00933 for (;;)
00934 {
00935 char *psz_device, *psz_descr;
00936 if ((i_err = snd_ctl_pcm_next_device(p_ctl, &i_pcm_device)) < 0)
00937 {
00938 i_pcm_device = -1;
00939 }
00940 if ( i_pcm_device < 0 )
00941 break;
00942
00943 snd_pcm_info_set_device(p_pcm_info, i_pcm_device);
00944 snd_pcm_info_set_subdevice(p_pcm_info, 0);
00945 snd_pcm_info_set_stream(p_pcm_info, SND_PCM_STREAM_PLAYBACK);
00946
00947 if ((i_err = snd_ctl_pcm_info(p_ctl, p_pcm_info)) < 0)
00948 {
00949 if (i_err != -ENOENT)
00950 {
00951
00952
00953
00954
00955 }
00956 continue;
00957 }
00958
00959 asprintf( &psz_device, "hw:%d,%d", i_card, i_pcm_device );
00960 asprintf( &psz_descr, "%s: %s (%s)", psz_card_name,
00961 snd_pcm_info_get_name(p_pcm_info), psz_device );
00962
00963 p_item->ppsz_list =
00964 (char **)realloc( p_item->ppsz_list,
00965 (p_item->i_list + 2) * sizeof(char *) );
00966 p_item->ppsz_list_text =
00967 (char **)realloc( p_item->ppsz_list_text,
00968 (p_item->i_list + 2) * sizeof(char *) );
00969 p_item->ppsz_list[ p_item->i_list ] = psz_device;
00970 p_item->ppsz_list_text[ p_item->i_list ] = psz_descr;
00971 p_item->i_list++;
00972 p_item->ppsz_list[ p_item->i_list ] = NULL;
00973 p_item->ppsz_list_text[ p_item->i_list ] = NULL;
00974
00975 }
00976
00977 snd_ctl_close( p_ctl );
00978 }
00979
00980
00981
00982 static void GetDevices( module_config_t *p_item )
00983 {
00984 int i_card = -1;
00985 int i_err = 0;
00986
00987 if ((i_err = snd_card_next(&i_card)) != 0)
00988 {
00989
00990 return;
00991 }
00992
00993 while (i_card > -1)
00994 {
00995 GetDevicesForCard(p_item, i_card);
00996 if ((i_err = snd_card_next(&i_card)) != 0)
00997 {
00998
00999
01000 break;
01001 }
01002 }
01003 }