X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faudio_output%2Foss.c;h=a05fcf16b6768bf79aad50a3eaf3494ec09685bd;hb=c12acbd7d48431d7b5029d765e69e87378aca7a2;hp=0e5acf95653b7a8afe0a2ec5a09066f401502744;hpb=61b87efdaca14f33696bc7e69d495b2d1179e28d;p=vlc diff --git a/modules/audio_output/oss.c b/modules/audio_output/oss.c index 0e5acf9565..a05fcf16b6 100644 --- a/modules/audio_output/oss.c +++ b/modules/audio_output/oss.c @@ -1,18 +1,18 @@ /***************************************************************************** * oss.c : OSS /dev/dsp module for vlc ***************************************************************************** - * Copyright (C) 2000-2002 VideoLAN - * $Id: oss.c,v 1.44 2003/01/13 14:51:25 massiot Exp $ + * Copyright (C) 2000-2002 the VideoLAN team + * $Id$ * * Authors: Michel Kaempf - * Samuel Hocevar + * Sam Hocevar * Christophe Massiot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -20,28 +20,26 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ -#include /* ENOMEM */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + #include /* open(), O_WRONLY */ #include /* ioctl() */ -#include /* strerror() */ #include /* write(), close() */ -#include /* calloc(), malloc(), free() */ - -#include -#ifdef HAVE_ALLOCA_H -# include -#endif +#include +#include +#include -#include - -#include "aout_internal.h" +#include /* SNDCTL_DSP_RESET, SNDCTL_DSP_SETFMT, SNDCTL_DSP_STEREO, SNDCTL_DSP_SPEED, * SNDCTL_DSP_GETOSPACE */ @@ -49,15 +47,26 @@ # include #elif defined( HAVE_SYS_SOUNDCARD_H ) # include -#elif defined( HAVE_MACHINE_SOUNDCARD_H ) -# include +#endif + +/* Patches for ignorant OSS versions */ +#ifndef AFMT_AC3 +# define AFMT_AC3 0x00000400 /* Dolby Digital AC3 */ +#endif + +#ifndef AFMT_S16_NE +# ifdef WORDS_BIGENDIAN +# define AFMT_S16_NE AFMT_S16_BE +# else +# define AFMT_S16_NE AFMT_S16_LE +# endif #endif /***************************************************************************** * aout_sys_t: OSS audio output method descriptor ***************************************************************************** * This structure is part of the audio output thread descriptor. - * It describes the dsp specific properties of an audio device. + * It describes the DSP specific properties of an audio device. *****************************************************************************/ struct aout_sys_t { @@ -69,7 +78,7 @@ struct aout_sys_t /* This must be a power of 2. */ #define FRAME_SIZE 1024 -#define FRAME_COUNT 4 +#define FRAME_COUNT 32 /***************************************************************************** * Local prototypes @@ -78,7 +87,7 @@ static int Open ( vlc_object_t * ); static void Close ( vlc_object_t * ); static void Play ( aout_instance_t * ); -static int OSSThread ( aout_instance_t * ); +static void* OSSThread ( vlc_object_t * ); static mtime_t BufferDuration( aout_instance_t * p_aout ); @@ -91,23 +100,21 @@ static mtime_t BufferDuration( aout_instance_t * p_aout ); "are completely filled (the sound gets heavily hashed). If you have one " \ "of these drivers, then you need to enable this option." ) -#define SPDIF_TEXT N_("Try to use S/PDIF output") -#define SPDIF_LONGTEXT N_( \ - "Sometimes we attempt to use the S/PDIF output, even if nothing is " \ - "connected to it. Un-checking this option disables this behaviour, " \ - "and permanently selects analog PCM output." ) - -vlc_module_begin(); - add_category_hint( N_("OSS"), NULL ); - add_file( "dspdev", "/dev/dsp", aout_FindAndRestart, - N_("OSS dsp device"), NULL ); - add_bool( "oss-buggy", 0, NULL, BUGGY_TEXT, BUGGY_LONGTEXT ); - add_bool( "spdif", 1, NULL, SPDIF_TEXT, SPDIF_LONGTEXT ); - set_description( _("Linux OSS /dev/dsp module") ); - set_capability( "audio output", 100 ); - add_shortcut( "oss" ); - set_callbacks( Open, Close ); -vlc_module_end(); +vlc_module_begin () + set_shortname( "OSS" ) + set_description( N_("UNIX OSS audio output") ) + + set_category( CAT_AUDIO ) + set_subcategory( SUBCAT_AUDIO_AOUT ) + add_file( "oss-audio-device", "/dev/dsp", aout_FindAndRestart, + N_("OSS DSP device"), NULL, false ) + add_deprecated_alias( "dspdev" ) /* deprecated since 0.9.3 */ + add_bool( "oss-buggy", false, NULL, BUGGY_TEXT, BUGGY_LONGTEXT, true ) + + set_capability( "audio output", 100 ) + add_shortcut( "oss" ) + set_callbacks( Open, Close ) +vlc_module_end () /***************************************************************************** * Probe: probe the audio device for available formats and channels @@ -115,51 +122,37 @@ vlc_module_end(); static void Probe( aout_instance_t * p_aout ) { struct aout_sys_t * p_sys = p_aout->output.p_sys; - vlc_value_t val; + vlc_value_t val, text; int i_format, i_nb_channels; - var_Create( p_aout, "audio-device", VLC_VAR_STRING | VLC_VAR_HASCHOICE ); - - if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 ) - { - msg_Err( p_aout, "cannot reset OSS audio device" ); - var_Destroy( p_aout, "audio-device" ); - return; - } - - if ( config_GetInt( p_aout, "spdif" ) - && AOUT_FMT_NON_LINEAR( &p_aout->output.output ) ) - { - i_format = AFMT_AC3; - - if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) >= 0 - && i_format == AFMT_AC3 ) - { - val.psz_string = N_("A/52 over S/PDIF"); - var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val ); - } - } - - /* Go to PCM mode. */ - i_format = AFMT_S16_NE; - if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 || - ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 ) - { - return; - } + var_Create( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); + text.psz_string = _("Audio Device"); + var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL ); + /* Test for multi-channel. */ #ifdef SNDCTL_DSP_GETCHANNELMASK if ( aout_FormatNbChannels( &p_aout->output.output ) > 2 ) { /* Check that the device supports this. */ int i_chanmask; + + /* Reset all. */ + i_format = AFMT_S16_NE; + if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 || + ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 ) + { + msg_Err( p_aout, "cannot reset OSS audio device" ); + var_Destroy( p_aout, "audio-device" ); + return; + } + if ( ioctl( p_sys->i_fd, SNDCTL_DSP_GETCHANNELMASK, &i_chanmask ) == 0 ) { if ( !(i_chanmask & DSP_BIND_FRONT) ) { - msg_Err( p_aout, "No front channels ! (%x)", + msg_Err( p_aout, "no front channels! (%x)", i_chanmask ); return; } @@ -170,8 +163,10 @@ static void Probe( aout_instance_t * p_aout ) | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE)) ) { - val.psz_string = N_("5.1"); - var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val ); + val.i_int = AOUT_VAR_5_1; + text.psz_string = (char*) "5.1"; + var_Change( p_aout, "audio-device", + VLC_VAR_ADDCHOICE, &val, &text ); } if ( (i_chanmask & DSP_BIND_SURR) @@ -179,20 +174,33 @@ static void Probe( aout_instance_t * p_aout ) (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT)) ) { - val.psz_string = N_("2 Front 2 Rear"); - var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val ); + val.i_int = AOUT_VAR_2F2R; + text.psz_string = _("2 Front 2 Rear"); + var_Change( p_aout, "audio-device", + VLC_VAR_ADDCHOICE, &val, &text ); } } } #endif + /* Reset all. */ + i_format = AFMT_S16_NE; + if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 || + ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 ) + { + msg_Err( p_aout, "cannot reset OSS audio device" ); + var_Destroy( p_aout, "audio-device" ); + return; + } + /* Test for stereo. */ i_nb_channels = 2; if( ioctl( p_sys->i_fd, SNDCTL_DSP_CHANNELS, &i_nb_channels ) >= 0 && i_nb_channels == 2 ) { - val.psz_string = N_("Stereo"); - var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val ); + val.i_int = AOUT_VAR_STEREO; + text.psz_string = _("Stereo"); + var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text ); } /* Reset all. */ @@ -207,13 +215,45 @@ static void Probe( aout_instance_t * p_aout ) /* Test for mono. */ i_nb_channels = 1; - if( ioctl( p_sys->i_fd, SNDCTL_DSP_CHANNELS, &i_nb_channels ) >= 0 + if( ioctl( p_sys->i_fd, SNDCTL_DSP_CHANNELS, &i_nb_channels ) >= 0 && i_nb_channels == 1 ) { - val.psz_string = N_("Mono"); - var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val ); + val.i_int = AOUT_VAR_MONO; + text.psz_string = _("Mono"); + var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text ); + if ( p_aout->output.output.i_physical_channels == AOUT_CHAN_CENTER ) + { + var_Set( p_aout, "audio-device", val ); + } } + if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 ) + { + msg_Err( p_aout, "cannot reset OSS audio device" ); + var_Destroy( p_aout, "audio-device" ); + return; + } + + /* Test for spdif. */ + if ( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) ) + { + i_format = AFMT_AC3; + + if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) >= 0 + && i_format == AFMT_AC3 ) + { + val.i_int = AOUT_VAR_SPDIF; + text.psz_string = _("A/52 over S/PDIF"); + var_Change( p_aout, "audio-device", + VLC_VAR_ADDCHOICE, &val, &text ); + if( var_InheritBool( p_aout, "spdif" ) ) + var_Set( p_aout, "audio-device", val ); + } + else if( var_InheritBool( p_aout, "spdif" ) ) + { + msg_Warn( p_aout, "S/PDIF not supported by card" ); + } + } var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL ); @@ -222,7 +262,7 @@ static void Probe( aout_instance_t * p_aout ) /***************************************************************************** * Open: open the audio device (the digital sound processor) ***************************************************************************** - * This function opens the dsp as a usual non-blocking write-only file, and + * This function opens the DSP as a usual non-blocking write-only file, and * modifies the p_aout->p_sys->i_fd with the file's descriptor. *****************************************************************************/ static int Open( vlc_object_t *p_this ) @@ -235,27 +275,34 @@ static int Open( vlc_object_t *p_this ) /* Allocate structure */ p_aout->output.p_sys = p_sys = malloc( sizeof( aout_sys_t ) ); if( p_sys == NULL ) - { - msg_Err( p_aout, "out of memory" ); return VLC_ENOMEM; - } /* Get device name */ - if( (psz_device = config_GetPsz( p_aout, "dspdev" )) == NULL ) + if( (psz_device = var_InheritString( p_aout, "oss-audio-device" )) == NULL ) { - msg_Err( p_aout, "no audio device given (maybe /dev/dsp ?)" ); + msg_Err( p_aout, "no audio device specified (maybe /dev/dsp?)" ); free( p_sys ); return VLC_EGENERIC; } - /* Open the sound device */ - p_sys->i_fd = open( psz_device, O_WRONLY ); + /* Open the sound device in non-blocking mode, because ALSA's OSS + * emulation and some broken OSS drivers would make a blocking call + * wait forever until the device is available. Since this breaks the + * OSS spec, we immediately put it back to blocking mode if the + * operation was successful. */ + p_sys->i_fd = vlc_open( psz_device, O_WRONLY | O_NDELAY ); if( p_sys->i_fd < 0 ) { msg_Err( p_aout, "cannot open audio device (%s)", psz_device ); + free( psz_device ); free( p_sys ); return VLC_EGENERIC; } + + /* if the opening was ok, put the device back in blocking mode */ + fcntl( p_sys->i_fd, F_SETFL, + fcntl( p_sys->i_fd, F_GETFL ) &~ FNDELAY ); + free( psz_device ); p_aout->output.pf_play = Play; @@ -268,52 +315,52 @@ static int Open( vlc_object_t *p_this ) if ( var_Get( p_aout, "audio-device", &val ) < 0 ) { /* Probe() has failed. */ + close( p_sys->i_fd ); free( p_sys ); return VLC_EGENERIC; } - if ( !strcmp( val.psz_string, N_("A/52 over S/PDIF") ) ) + if ( val.i_int == AOUT_VAR_SPDIF ) { - p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i'); + p_aout->output.output.i_format = VLC_CODEC_SPDIFL; } - else if ( !strcmp( val.psz_string, N_("5.1") ) ) + else if ( val.i_int == AOUT_VAR_5_1 ) { - p_aout->output.output.i_format = AOUT_FMT_S16_NE; + p_aout->output.output.i_format = VLC_CODEC_S16N; p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER - | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARLEFT + | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE; } - else if ( !strcmp( val.psz_string, N_("2 Front 2 Rear") ) ) + else if ( val.i_int == AOUT_VAR_2F2R ) { - p_aout->output.output.i_format = AOUT_FMT_S16_NE; + p_aout->output.output.i_format = VLC_CODEC_S16N; p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT - | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARLEFT; + | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT; } - else if ( !strcmp( val.psz_string, N_("Stereo") ) ) + else if ( val.i_int == AOUT_VAR_STEREO ) { - p_aout->output.output.i_format = AOUT_FMT_S16_NE; + p_aout->output.output.i_format = VLC_CODEC_S16N; p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; } - else if ( !strcmp( val.psz_string, N_("Mono") ) ) + else if ( val.i_int == AOUT_VAR_MONO ) { - p_aout->output.output.i_format = AOUT_FMT_S16_NE; + p_aout->output.output.i_format = VLC_CODEC_S16N; p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER; } else { /* This should not happen ! */ - msg_Err( p_aout, "internal: can't find audio-device (%s)", - val.psz_string ); + msg_Err( p_aout, "internal: can't find audio-device (%"PRId64")", + val.i_int ); + close( p_sys->i_fd ); free( p_sys ); return VLC_EGENERIC; } - free( val.psz_string ); - val.b_bool = VLC_TRUE; - var_Set( p_aout, "intf-change", val ); + var_SetBool( p_aout, "intf-change", true ); /* Reset the DSP device */ if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 ) @@ -338,7 +385,7 @@ static int Open( vlc_object_t *p_this ) return VLC_EGENERIC; } - p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i'); + p_aout->output.output.i_format = VLC_CODEC_SPDIFL; p_aout->output.i_nb_samples = A52_FRAME_NB; p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE; p_aout->output.output.i_frame_length = A52_FRAME_NB; @@ -365,22 +412,22 @@ static int Open( vlc_object_t *p_this ) switch ( i_format ) { case AFMT_U8: - p_aout->output.output.i_format = VLC_FOURCC('u','8',' ',' '); + p_aout->output.output.i_format = VLC_CODEC_U8; break; case AFMT_S8: - p_aout->output.output.i_format = VLC_FOURCC('s','8',' ',' '); + p_aout->output.output.i_format = VLC_CODEC_S8; break; case AFMT_U16_LE: - p_aout->output.output.i_format = VLC_FOURCC('u','1','6','l'); + p_aout->output.output.i_format = VLC_CODEC_U16L; break; case AFMT_S16_LE: - p_aout->output.output.i_format = VLC_FOURCC('s','1','6','l'); + p_aout->output.output.i_format = VLC_CODEC_S16L; break; case AFMT_U16_BE: - p_aout->output.output.i_format = VLC_FOURCC('u','1','6','b'); + p_aout->output.output.i_format = VLC_CODEC_U16B; break; case AFMT_S16_BE: - p_aout->output.output.i_format = VLC_FOURCC('s','1','6','b'); + p_aout->output.output.i_format = VLC_CODEC_S16B; break; default: msg_Err( p_aout, "OSS fell back to an unknown format (%d)", @@ -424,9 +471,9 @@ static int Open( vlc_object_t *p_this ) /* i_fragment = xxxxyyyy where: xxxx is fragtotal * 1 << yyyy is fragsize */ - i_fragments = 0; - i_frame_size = FRAME_SIZE * p_aout->output.output.i_bytes_per_frame; - while( i_frame_size >>= 1 ) + i_frame_size = ((uint64_t)p_aout->output.output.i_bytes_per_frame * p_aout->output.output.i_rate * 65536) / (48000 * 2 * 2) / FRAME_COUNT; + i_fragments = 4; + while( i_fragments < 12 && (1U << i_fragments) < i_frame_size ) { ++i_fragments; } @@ -463,16 +510,16 @@ static int Open( vlc_object_t *p_this ) } p_aout->output.p_sys->b_workaround_buggy_driver = - config_GetInt( p_aout, "oss-buggy" ); + var_InheritBool( p_aout, "oss-buggy" ); /* Create OSS thread and wait for its readiness. */ if( vlc_thread_create( p_aout, "aout", OSSThread, - VLC_THREAD_PRIORITY_OUTPUT, VLC_FALSE ) ) + VLC_THREAD_PRIORITY_OUTPUT ) ) { - msg_Err( p_aout, "cannot create OSS thread (%s)", strerror(errno) ); + msg_Err( p_aout, "cannot create OSS thread (%m)" ); close( p_sys->i_fd ); free( p_sys ); - return VLC_ETHREAD; + return VLC_ENOMEM; } return VLC_SUCCESS; @@ -483,19 +530,20 @@ static int Open( vlc_object_t *p_this ) *****************************************************************************/ static void Play( aout_instance_t *p_aout ) { + VLC_UNUSED(p_aout); } /***************************************************************************** - * Close: close the dsp audio device + * Close: close the DSP audio device *****************************************************************************/ static void Close( vlc_object_t * p_this ) { aout_instance_t *p_aout = (aout_instance_t *)p_this; struct aout_sys_t * p_sys = p_aout->output.p_sys; - p_aout->b_die = VLC_TRUE; + vlc_object_kill( p_aout ); vlc_thread_join( p_aout ); - p_aout->b_die = VLC_FALSE; + p_aout->b_die = false; ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ); close( p_sys->i_fd ); @@ -534,18 +582,20 @@ static mtime_t BufferDuration( aout_instance_t * p_aout ) /***************************************************************************** * OSSThread: asynchronous thread used to DMA the data to the device *****************************************************************************/ -static int OSSThread( aout_instance_t * p_aout ) +static void* OSSThread( vlc_object_t *p_this ) { + aout_instance_t * p_aout = (aout_instance_t*)p_this; struct aout_sys_t * p_sys = p_aout->output.p_sys; mtime_t next_date = 0; + int canc = vlc_savecancel (); - while ( !p_aout->b_die ) + while ( vlc_object_alive (p_aout) ) { aout_buffer_t * p_buffer = NULL; int i_tmp, i_size; - byte_t * p_bytes; + uint8_t * p_bytes; - if ( p_aout->output.output.i_format != VLC_FOURCC('s','p','d','i') ) + if ( p_aout->output.output.i_format != VLC_CODEC_SPDIFL ) { mtime_t buffered = BufferDuration( p_aout ); @@ -565,7 +615,7 @@ static int OSSThread( aout_instance_t * p_aout ) /* Next buffer will be played at mdate() + buffered */ p_buffer = aout_OutputNextBuffer( p_aout, mdate() + buffered, - VLC_FALSE ); + false ); if( p_buffer == NULL && buffered > ( p_aout->output.p_sys->max_buffer_duration @@ -595,11 +645,11 @@ static int OSSThread( aout_instance_t * p_aout ) msleep( delay / 2 ); } } - - while( !p_aout->b_die && ! ( p_buffer = - aout_OutputNextBuffer( p_aout, next_date, VLC_TRUE ) ) ) + + while( vlc_object_alive (p_aout) && ! ( p_buffer = + aout_OutputNextBuffer( p_aout, next_date, true ) ) ) { - msleep( 1000 ); + msleep( VLC_HARD_MIN_SLEEP ); next_date = mdate(); } } @@ -607,10 +657,10 @@ static int OSSThread( aout_instance_t * p_aout ) if ( p_buffer != NULL ) { p_bytes = p_buffer->p_buffer; - i_size = p_buffer->i_nb_bytes; + i_size = p_buffer->i_buffer; /* This is theoretical ... we'll see next iteration whether * we're drifting */ - next_date += p_buffer->end_date - p_buffer->start_date; + next_date += p_buffer->i_length; } else { @@ -625,7 +675,7 @@ static int OSSThread( aout_instance_t * p_aout ) if( i_tmp < 0 ) { - msg_Err( p_aout, "write failed (%s)", strerror(errno) ); + msg_Err( p_aout, "write failed (%m)" ); } if ( p_buffer != NULL ) @@ -638,5 +688,6 @@ static int OSSThread( aout_instance_t * p_aout ) } } - return VLC_SUCCESS; + vlc_restorecancel (canc); + return NULL; }