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 #include <errno.h>
00030 #include <fcntl.h>
00031 #include <sys/ioctl.h>
00032 #include <string.h>
00033 #include <unistd.h>
00034 #include <stdlib.h>
00035
00036 #include <vlc/vlc.h>
00037
00038 #ifdef HAVE_ALLOCA_H
00039 # include <alloca.h>
00040 #endif
00041
00042 #include <vlc/aout.h>
00043
00044 #include "aout_internal.h"
00045
00046
00047
00048 #ifdef HAVE_SOUNDCARD_H
00049 # include <soundcard.h>
00050 #elif defined( HAVE_SYS_SOUNDCARD_H )
00051 # include <sys/soundcard.h>
00052 #elif defined( HAVE_MACHINE_SOUNDCARD_H )
00053 # include <machine/soundcard.h>
00054 #endif
00055
00056
00057 #ifndef AFMT_AC3
00058 # define AFMT_AC3 0x00000400
00059 #endif
00060
00061 #ifndef AFMT_S16_NE
00062 # ifdef WORDS_BIGENDIAN
00063 # define AFMT_S16_NE AFMT_S16_BE
00064 # else
00065 # define AFMT_S16_NE AFMT_S16_LE
00066 # endif
00067 #endif
00068
00069
00070
00071
00072
00073
00074
00075 struct aout_sys_t
00076 {
00077 int i_fd;
00078 int b_workaround_buggy_driver;
00079 int i_fragstotal;
00080 mtime_t max_buffer_duration;
00081 };
00082
00083
00084 #define FRAME_SIZE 1024
00085 #define FRAME_COUNT 4
00086
00087
00088
00089
00090 static int Open ( vlc_object_t * );
00091 static void Close ( vlc_object_t * );
00092
00093 static void Play ( aout_instance_t * );
00094 static int OSSThread ( aout_instance_t * );
00095
00096 static mtime_t BufferDuration( aout_instance_t * p_aout );
00097
00098
00099
00100
00101 #define BUGGY_TEXT N_("Try to work around buggy OSS drivers")
00102 #define BUGGY_LONGTEXT N_( \
00103 "Some buggy OSS drivers just don't like when their internal buffers " \
00104 "are completely filled (the sound gets heavily hashed). If you have one " \
00105 "of these drivers, then you need to enable this option." )
00106
00107 vlc_module_begin();
00108 set_shortname( "OSS" );
00109 set_description( _("Linux OSS audio output") );
00110
00111 set_category( CAT_AUDIO );
00112 set_subcategory( SUBCAT_AUDIO_AOUT );
00113 add_file( "dspdev", "/dev/dsp", aout_FindAndRestart,
00114 N_("OSS DSP device"), NULL, VLC_FALSE );
00115 add_bool( "oss-buggy", 0, NULL, BUGGY_TEXT, BUGGY_LONGTEXT, VLC_TRUE );
00116
00117 set_capability( "audio output", 100 );
00118 add_shortcut( "oss" );
00119 set_callbacks( Open, Close );
00120 vlc_module_end();
00121
00122
00123
00124
00125 static void Probe( aout_instance_t * p_aout )
00126 {
00127 struct aout_sys_t * p_sys = p_aout->output.p_sys;
00128 vlc_value_t val, text;
00129 int i_format, i_nb_channels;
00130
00131 var_Create( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
00132 text.psz_string = _("Audio Device");
00133 var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
00134
00135
00136 #ifdef SNDCTL_DSP_GETCHANNELMASK
00137 if ( aout_FormatNbChannels( &p_aout->output.output ) > 2 )
00138 {
00139
00140
00141 int i_chanmask;
00142
00143
00144 i_format = AFMT_S16_NE;
00145 if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 ||
00146 ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 )
00147 {
00148 msg_Err( p_aout, "cannot reset OSS audio device" );
00149 var_Destroy( p_aout, "audio-device" );
00150 return;
00151 }
00152
00153 if ( ioctl( p_sys->i_fd, SNDCTL_DSP_GETCHANNELMASK,
00154 &i_chanmask ) == 0 )
00155 {
00156 if ( !(i_chanmask & DSP_BIND_FRONT) )
00157 {
00158 msg_Err( p_aout, "No front channels ! (%x)",
00159 i_chanmask );
00160 return;
00161 }
00162
00163 if ( (i_chanmask & (DSP_BIND_SURR | DSP_BIND_CENTER_LFE))
00164 && (p_aout->output.output.i_physical_channels ==
00165 (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
00166 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
00167 | AOUT_CHAN_LFE)) )
00168 {
00169 val.i_int = AOUT_VAR_5_1;
00170 text.psz_string = N_("5.1");
00171 var_Change( p_aout, "audio-device",
00172 VLC_VAR_ADDCHOICE, &val, &text );
00173 }
00174
00175 if ( (i_chanmask & DSP_BIND_SURR)
00176 && (p_aout->output.output.i_physical_channels &
00177 (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
00178 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT)) )
00179 {
00180 val.i_int = AOUT_VAR_2F2R;
00181 text.psz_string = N_("2 Front 2 Rear");
00182 var_Change( p_aout, "audio-device",
00183 VLC_VAR_ADDCHOICE, &val, &text );
00184 }
00185 }
00186 }
00187 #endif
00188
00189
00190 i_format = AFMT_S16_NE;
00191 if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 ||
00192 ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 )
00193 {
00194 msg_Err( p_aout, "cannot reset OSS audio device" );
00195 var_Destroy( p_aout, "audio-device" );
00196 return;
00197 }
00198
00199
00200 i_nb_channels = 2;
00201 if( ioctl( p_sys->i_fd, SNDCTL_DSP_CHANNELS, &i_nb_channels ) >= 0
00202 && i_nb_channels == 2 )
00203 {
00204 val.i_int = AOUT_VAR_STEREO;
00205 text.psz_string = N_("Stereo");
00206 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
00207 }
00208
00209
00210 i_format = AFMT_S16_NE;
00211 if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 ||
00212 ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 )
00213 {
00214 msg_Err( p_aout, "cannot reset OSS audio device" );
00215 var_Destroy( p_aout, "audio-device" );
00216 return;
00217 }
00218
00219
00220 i_nb_channels = 1;
00221 if( ioctl( p_sys->i_fd, SNDCTL_DSP_CHANNELS, &i_nb_channels ) >= 0
00222 && i_nb_channels == 1 )
00223 {
00224 val.i_int = AOUT_VAR_MONO;
00225 text.psz_string = N_("Mono");
00226 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
00227 if ( p_aout->output.output.i_physical_channels == AOUT_CHAN_CENTER )
00228 {
00229 var_Set( p_aout, "audio-device", val );
00230 }
00231 }
00232
00233 if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 )
00234 {
00235 msg_Err( p_aout, "cannot reset OSS audio device" );
00236 var_Destroy( p_aout, "audio-device" );
00237 return;
00238 }
00239
00240
00241 if ( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
00242 {
00243 i_format = AFMT_AC3;
00244
00245 if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) >= 0
00246 && i_format == AFMT_AC3 )
00247 {
00248 val.i_int = AOUT_VAR_SPDIF;
00249 text.psz_string = N_("A/52 over S/PDIF");
00250 var_Change( p_aout, "audio-device",
00251 VLC_VAR_ADDCHOICE, &val, &text );
00252 if( config_GetInt( p_aout, "spdif" ) )
00253 var_Set( p_aout, "audio-device", val );
00254 }
00255 else if( config_GetInt( p_aout, "spdif" ) )
00256 {
00257 msg_Warn( p_aout, "s/pdif not supported by card" );
00258 }
00259 }
00260
00261 var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart,
00262 NULL );
00263 }
00264
00265
00266
00267
00268
00269
00270
00271 static int Open( vlc_object_t *p_this )
00272 {
00273 aout_instance_t * p_aout = (aout_instance_t *)p_this;
00274 struct aout_sys_t * p_sys;
00275 char * psz_device;
00276 vlc_value_t val;
00277
00278
00279 p_aout->output.p_sys = p_sys = malloc( sizeof( aout_sys_t ) );
00280 if( p_sys == NULL )
00281 {
00282 msg_Err( p_aout, "out of memory" );
00283 return VLC_ENOMEM;
00284 }
00285
00286
00287 if( (psz_device = config_GetPsz( p_aout, "dspdev" )) == NULL )
00288 {
00289 msg_Err( p_aout, "no audio device specified (maybe /dev/dsp?)" );
00290 free( p_sys );
00291 return VLC_EGENERIC;
00292 }
00293
00294
00295
00296
00297
00298
00299 p_sys->i_fd = open( psz_device, O_WRONLY | O_NDELAY );
00300 if( p_sys->i_fd < 0 )
00301 {
00302 msg_Err( p_aout, "cannot open audio device (%s)", psz_device );
00303 free( p_sys );
00304 return VLC_EGENERIC;
00305 }
00306
00307
00308 fcntl( p_sys->i_fd, F_SETFL,
00309 fcntl( p_sys->i_fd, F_GETFL ) &~ FNDELAY );
00310
00311 free( psz_device );
00312
00313 p_aout->output.pf_play = Play;
00314
00315 if ( var_Type( p_aout, "audio-device" ) == 0 )
00316 {
00317 Probe( p_aout );
00318 }
00319
00320 if ( var_Get( p_aout, "audio-device", &val ) < 0 )
00321 {
00322
00323 free( p_sys );
00324 return VLC_EGENERIC;
00325 }
00326
00327 if ( val.i_int == AOUT_VAR_SPDIF )
00328 {
00329 p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
00330 }
00331 else if ( val.i_int == AOUT_VAR_5_1 )
00332 {
00333 p_aout->output.output.i_format = AOUT_FMT_S16_NE;
00334 p_aout->output.output.i_physical_channels
00335 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
00336 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
00337 | AOUT_CHAN_LFE;
00338 }
00339 else if ( val.i_int == AOUT_VAR_2F2R )
00340 {
00341 p_aout->output.output.i_format = AOUT_FMT_S16_NE;
00342 p_aout->output.output.i_physical_channels
00343 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
00344 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
00345 }
00346 else if ( val.i_int == AOUT_VAR_STEREO )
00347 {
00348 p_aout->output.output.i_format = AOUT_FMT_S16_NE;
00349 p_aout->output.output.i_physical_channels
00350 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
00351 }
00352 else if ( val.i_int == AOUT_VAR_MONO )
00353 {
00354 p_aout->output.output.i_format = AOUT_FMT_S16_NE;
00355 p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
00356 }
00357 else
00358 {
00359
00360 msg_Err( p_aout, "internal: can't find audio-device (%i)", val.i_int );
00361 free( p_sys );
00362 return VLC_EGENERIC;
00363 }
00364
00365 val.b_bool = VLC_TRUE;
00366 var_Set( p_aout, "intf-change", val );
00367
00368
00369 if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 )
00370 {
00371 msg_Err( p_aout, "cannot reset OSS audio device" );
00372 close( p_sys->i_fd );
00373 free( p_sys );
00374 return VLC_EGENERIC;
00375 }
00376
00377
00378 if ( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
00379 {
00380 int i_format = AFMT_AC3;
00381
00382 if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0
00383 || i_format != AFMT_AC3 )
00384 {
00385 msg_Err( p_aout, "cannot reset OSS audio device" );
00386 close( p_sys->i_fd );
00387 free( p_sys );
00388 return VLC_EGENERIC;
00389 }
00390
00391 p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
00392 p_aout->output.i_nb_samples = A52_FRAME_NB;
00393 p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
00394 p_aout->output.output.i_frame_length = A52_FRAME_NB;
00395
00396 aout_VolumeNoneInit( p_aout );
00397 }
00398
00399 if ( !AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
00400 {
00401 unsigned int i_format = AFMT_S16_NE;
00402 unsigned int i_frame_size, i_fragments;
00403 unsigned int i_rate;
00404 unsigned int i_nb_channels;
00405 audio_buf_info audio_buf;
00406
00407 if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 )
00408 {
00409 msg_Err( p_aout, "cannot set audio output format" );
00410 close( p_sys->i_fd );
00411 free( p_sys );
00412 return VLC_EGENERIC;
00413 }
00414
00415 switch ( i_format )
00416 {
00417 case AFMT_U8:
00418 p_aout->output.output.i_format = VLC_FOURCC('u','8',' ',' ');
00419 break;
00420 case AFMT_S8:
00421 p_aout->output.output.i_format = VLC_FOURCC('s','8',' ',' ');
00422 break;
00423 case AFMT_U16_LE:
00424 p_aout->output.output.i_format = VLC_FOURCC('u','1','6','l');
00425 break;
00426 case AFMT_S16_LE:
00427 p_aout->output.output.i_format = VLC_FOURCC('s','1','6','l');
00428 break;
00429 case AFMT_U16_BE:
00430 p_aout->output.output.i_format = VLC_FOURCC('u','1','6','b');
00431 break;
00432 case AFMT_S16_BE:
00433 p_aout->output.output.i_format = VLC_FOURCC('s','1','6','b');
00434 break;
00435 default:
00436 msg_Err( p_aout, "OSS fell back to an unknown format (%d)",
00437 i_format );
00438 close( p_sys->i_fd );
00439 free( p_sys );
00440 return VLC_EGENERIC;
00441 }
00442
00443 i_nb_channels = aout_FormatNbChannels( &p_aout->output.output );
00444
00445
00446 if( ioctl( p_sys->i_fd, SNDCTL_DSP_CHANNELS, &i_nb_channels ) < 0 ||
00447 i_nb_channels != aout_FormatNbChannels( &p_aout->output.output ) )
00448 {
00449 msg_Err( p_aout, "cannot set number of audio channels (%s)",
00450 aout_FormatPrintChannels( &p_aout->output.output) );
00451 close( p_sys->i_fd );
00452 free( p_sys );
00453 return VLC_EGENERIC;
00454 }
00455
00456
00457 i_rate = p_aout->output.output.i_rate;
00458 if( ioctl( p_sys->i_fd, SNDCTL_DSP_SPEED, &i_rate ) < 0 )
00459 {
00460 msg_Err( p_aout, "cannot set audio output rate (%i)",
00461 p_aout->output.output.i_rate );
00462 close( p_sys->i_fd );
00463 free( p_sys );
00464 return VLC_EGENERIC;
00465 }
00466
00467 if( i_rate != p_aout->output.output.i_rate )
00468 {
00469 p_aout->output.output.i_rate = i_rate;
00470 }
00471
00472
00473 aout_FormatPrepare( &p_aout->output.output );
00474
00475
00476
00477 i_fragments = 0;
00478 i_frame_size = FRAME_SIZE * p_aout->output.output.i_bytes_per_frame;
00479 while( i_frame_size >>= 1 )
00480 {
00481 ++i_fragments;
00482 }
00483 i_fragments |= FRAME_COUNT << 16;
00484 if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFRAGMENT, &i_fragments ) < 0 )
00485 {
00486 msg_Warn( p_aout, "cannot set fragment size (%.8x)", i_fragments );
00487 }
00488
00489 if( ioctl( p_sys->i_fd, SNDCTL_DSP_GETOSPACE, &audio_buf ) < 0 )
00490 {
00491 msg_Err( p_aout, "cannot get fragment size" );
00492 close( p_sys->i_fd );
00493 free( p_sys );
00494 return VLC_EGENERIC;
00495 }
00496 else
00497 {
00498
00499 p_aout->output.p_sys->i_fragstotal = audio_buf.fragstotal;
00500
00501
00502 p_aout->output.p_sys->max_buffer_duration =
00503 (mtime_t)audio_buf.fragstotal * audio_buf.fragsize * 1000000
00504 / p_aout->output.output.i_bytes_per_frame
00505 / p_aout->output.output.i_rate
00506 * p_aout->output.output.i_frame_length;
00507
00508 p_aout->output.i_nb_samples = audio_buf.fragsize /
00509 p_aout->output.output.i_bytes_per_frame;
00510 }
00511
00512 aout_VolumeSoftInit( p_aout );
00513 }
00514
00515 p_aout->output.p_sys->b_workaround_buggy_driver =
00516 config_GetInt( p_aout, "oss-buggy" );
00517
00518
00519 if( vlc_thread_create( p_aout, "aout", OSSThread,
00520 VLC_THREAD_PRIORITY_OUTPUT, VLC_FALSE ) )
00521 {
00522 msg_Err( p_aout, "cannot create OSS thread (%s)", strerror(errno) );
00523 close( p_sys->i_fd );
00524 free( p_sys );
00525 return VLC_ETHREAD;
00526 }
00527
00528 return VLC_SUCCESS;
00529 }
00530
00531
00532
00533
00534 static void Play( aout_instance_t *p_aout )
00535 {
00536 }
00537
00538
00539
00540
00541 static void Close( vlc_object_t * p_this )
00542 {
00543 aout_instance_t *p_aout = (aout_instance_t *)p_this;
00544 struct aout_sys_t * p_sys = p_aout->output.p_sys;
00545
00546 p_aout->b_die = VLC_TRUE;
00547 vlc_thread_join( p_aout );
00548 p_aout->b_die = VLC_FALSE;
00549
00550 ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL );
00551 close( p_sys->i_fd );
00552
00553 free( p_sys );
00554 }
00555
00556
00557
00558
00559
00560
00561 static mtime_t BufferDuration( aout_instance_t * p_aout )
00562 {
00563 struct aout_sys_t * p_sys = p_aout->output.p_sys;
00564 audio_buf_info audio_buf;
00565 int i_bytes;
00566
00567
00568
00569
00570
00571
00572 ioctl( p_sys->i_fd, SNDCTL_DSP_GETOSPACE, &audio_buf );
00573
00574
00575 i_bytes = (audio_buf.fragstotal * audio_buf.fragsize) - audio_buf.bytes;
00576
00577
00578 return (mtime_t)i_bytes * 1000000
00579 / p_aout->output.output.i_bytes_per_frame
00580 / p_aout->output.output.i_rate
00581 * p_aout->output.output.i_frame_length;
00582 }
00583
00584
00585
00586
00587 static int OSSThread( aout_instance_t * p_aout )
00588 {
00589 struct aout_sys_t * p_sys = p_aout->output.p_sys;
00590 mtime_t next_date = 0;
00591
00592 while ( !p_aout->b_die )
00593 {
00594 aout_buffer_t * p_buffer = NULL;
00595 int i_tmp, i_size;
00596 byte_t * p_bytes;
00597
00598 if ( p_aout->output.output.i_format != VLC_FOURCC('s','p','d','i') )
00599 {
00600 mtime_t buffered = BufferDuration( p_aout );
00601
00602 if( p_aout->output.p_sys->b_workaround_buggy_driver )
00603 {
00604 #define i_fragstotal p_aout->output.p_sys->i_fragstotal
00605
00606 if( buffered > (p_aout->output.p_sys->max_buffer_duration
00607 / i_fragstotal * (i_fragstotal - 1)) )
00608 {
00609 msleep((p_aout->output.p_sys->max_buffer_duration
00610 / i_fragstotal ));
00611 buffered = BufferDuration( p_aout );
00612 }
00613 #undef i_fragstotal
00614 }
00615
00616
00617 p_buffer = aout_OutputNextBuffer( p_aout, mdate() + buffered,
00618 VLC_FALSE );
00619
00620 if( p_buffer == NULL &&
00621 buffered > ( p_aout->output.p_sys->max_buffer_duration
00622 / p_aout->output.p_sys->i_fragstotal ) )
00623 {
00624
00625
00626
00627 msleep( ( p_aout->output.p_sys->max_buffer_duration
00628 / p_aout->output.p_sys->i_fragstotal / 2 ) );
00629 continue;
00630 }
00631 }
00632 else
00633 {
00634
00635
00636 if( !next_date )
00637 {
00638 next_date = mdate();
00639 }
00640 else
00641 {
00642 mtime_t delay = next_date - mdate();
00643 if( delay > AOUT_PTS_TOLERANCE )
00644 {
00645 msleep( delay / 2 );
00646 }
00647 }
00648
00649 while( !p_aout->b_die && ! ( p_buffer =
00650 aout_OutputNextBuffer( p_aout, next_date, VLC_TRUE ) ) )
00651 {
00652 msleep( 1000 );
00653 next_date = mdate();
00654 }
00655 }
00656
00657 if ( p_buffer != NULL )
00658 {
00659 p_bytes = p_buffer->p_buffer;
00660 i_size = p_buffer->i_nb_bytes;
00661
00662
00663 next_date += p_buffer->end_date - p_buffer->start_date;
00664 }
00665 else
00666 {
00667 i_size = FRAME_SIZE / p_aout->output.output.i_frame_length
00668 * p_aout->output.output.i_bytes_per_frame;
00669 p_bytes = malloc( i_size );
00670 memset( p_bytes, 0, i_size );
00671 next_date = 0;
00672 }
00673
00674 i_tmp = write( p_sys->i_fd, p_bytes, i_size );
00675
00676 if( i_tmp < 0 )
00677 {
00678 msg_Err( p_aout, "write failed (%s)", strerror(errno) );
00679 }
00680
00681 if ( p_buffer != NULL )
00682 {
00683 aout_BufferFree( p_buffer );
00684 }
00685 else
00686 {
00687 free( p_bytes );
00688 }
00689 }
00690
00691 return VLC_SUCCESS;
00692 }