X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faudio_output%2Foss.c;h=8dc4a4a75d9854f0769925965df48a52d4b2257e;hb=e8ede98dac1e3be6aec4ba91124e2535f629e7e2;hp=6ee0688655bd63e73786d86170262f0b672fc5da;hpb=78d87996ccb92d1dc91c9987685f976ed3be08a6;p=vlc diff --git a/modules/audio_output/oss.c b/modules/audio_output/oss.c index 6ee0688655..8dc4a4a75d 100644 --- a/modules/audio_output/oss.c +++ b/modules/audio_output/oss.c @@ -1,696 +1,452 @@ /***************************************************************************** - * oss.c : OSS /dev/dsp module for vlc + * oss.c: Open Sound System audio output plugin for VLC ***************************************************************************** - * Copyright (C) 2000-2002 the VideoLAN team - * $Id$ + * Copyright (C) 2000-2002 VLC authors and VideoLAN + * Copyright (C) 2007-2012 Rémi Denis-Courmont * * Authors: Michel Kaempf * 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 + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 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 - * GNU General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. * - * 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. - *****************************************************************************/ - -/***************************************************************************** - * Preamble + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #ifdef HAVE_CONFIG_H # include "config.h" #endif -#include /* ENOMEM */ -#include /* open(), O_WRONLY */ -#include /* ioctl() */ -#include /* write(), close() */ - -#include -#include - -#ifdef HAVE_ALLOCA_H -# include -#endif - -#include - -/* SNDCTL_DSP_RESET, SNDCTL_DSP_SETFMT, SNDCTL_DSP_STEREO, SNDCTL_DSP_SPEED, - * SNDCTL_DSP_GETOSPACE */ +#include +#include +#include +#include +#include #ifdef HAVE_SOUNDCARD_H -# include -#elif defined( HAVE_SYS_SOUNDCARD_H ) -# include +# include +#else +# include #endif -/* Patches for ignorant OSS versions */ -#ifndef AFMT_AC3 -# define AFMT_AC3 0x00000400 /* Dolby Digital AC3 */ +#ifndef SNDCTL_DSP_HALT +# define SNDCTL_DSP_HALT SNDCTL_DSP_RESET #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 +#include +#include +#include +#include +#include + +#define A52_FRAME_NB 1536 -/***************************************************************************** - * 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. - *****************************************************************************/ struct aout_sys_t { - int i_fd; - int b_workaround_buggy_driver; - int i_fragstotal; - mtime_t max_buffer_duration; -}; - -/* This must be a power of 2. */ -#define FRAME_SIZE 1024 -#define FRAME_COUNT 32 + int fd; + audio_sample_format_t format; + bool starting; -/***************************************************************************** - * Local prototypes - *****************************************************************************/ -static int Open ( vlc_object_t * ); -static void Close ( vlc_object_t * ); - -static void Play ( aout_instance_t * ); -static void* OSSThread ( vlc_object_t * ); + bool mute; + uint8_t level; + char *device; +}; -static mtime_t BufferDuration( aout_instance_t * p_aout ); +static int Open (vlc_object_t *); +static void Close (vlc_object_t *); -/***************************************************************************** - * Module descriptor - *****************************************************************************/ -#define BUGGY_TEXT N_("Try to work around buggy OSS drivers") -#define BUGGY_LONGTEXT N_( \ - "Some buggy OSS drivers just don't like when their internal buffers " \ - "are completely filled (the sound gets heavily hashed). If you have one " \ - "of these drivers, then you need to enable this option." ) +#define AUDIO_DEV_TEXT N_("Audio output device") +#define AUDIO_DEV_LONGTEXT N_("OSS device node path.") vlc_module_begin () set_shortname( "OSS" ) - set_description( N_("UNIX OSS audio output") ) - + set_description (N_("Open Sound System 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", 0, NULL, BUGGY_TEXT, BUGGY_LONGTEXT, true ) - + add_string ("oss-audio-device", "", + AUDIO_DEV_TEXT, AUDIO_DEV_LONGTEXT, false) set_capability( "audio output", 100 ) - add_shortcut( "oss" ) - set_callbacks( Open, Close ) + set_callbacks (Open, Close) vlc_module_end () -/***************************************************************************** - * Probe: probe the audio device for available formats and channels - *****************************************************************************/ -static void Probe( aout_instance_t * p_aout ) -{ - struct aout_sys_t * p_sys = p_aout->output.p_sys; - vlc_value_t val, text; - int i_format, i_nb_channels; - - 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; +static int TimeGet (audio_output_t *, mtime_t *); +static void Play (audio_output_t *, block_t *); +static void Pause (audio_output_t *, bool, mtime_t); +static void Flush (audio_output_t *, bool); +static int VolumeSync (audio_output_t *); - /* 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)", - i_chanmask ); - return; - } - - if ( (i_chanmask & (DSP_BIND_SURR | DSP_BIND_CENTER_LFE)) - && (p_aout->output.output.i_physical_channels == - (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER - | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT - | AOUT_CHAN_LFE)) ) - { - 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) - && (p_aout->output.output.i_physical_channels & - (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT - | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT)) ) - { - 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 +static int Start (audio_output_t *aout, audio_sample_format_t *restrict fmt) +{ + aout_sys_t* sys = aout->sys; - /* 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; - } + /* Open the device */ + const char *device = sys->device; + if (device == NULL) + device = getenv ("OSS_AUDIODEV"); + if (device == NULL) + device = "/dev/dsp"; - /* Test for stereo. */ - i_nb_channels = 2; - if( ioctl( p_sys->i_fd, SNDCTL_DSP_CHANNELS, &i_nb_channels ) >= 0 - && i_nb_channels == 2 ) + int fd = vlc_open (device, O_WRONLY); + if (fd == -1) { - val.i_int = AOUT_VAR_STEREO; - text.psz_string = _("Stereo"); - var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text ); + msg_Err (aout, "cannot open OSS device %s: %m", device); + return VLC_EGENERIC; } + sys->fd = fd; + msg_Dbg (aout, "using OSS device: %s", device); - /* 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; - } + /* Select audio format */ + int format; + bool spdif = false; - /* Test for mono. */ - i_nb_channels = 1; - if( ioctl( p_sys->i_fd, SNDCTL_DSP_CHANNELS, &i_nb_channels ) >= 0 - && i_nb_channels == 1 ) + switch (fmt->i_format) { - 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 ); - } +#ifdef AFMT_FLOAT + case VLC_CODEC_FL64: + case VLC_CODEC_FL32: + format = AFMT_FLOAT; + break; +#endif + case VLC_CODEC_S32N: + format = AFMT_S32_NE; + break; + case VLC_CODEC_S16N: + format = AFMT_S16_NE; + break; + case VLC_CODEC_U8: + format = AFMT_U8; + break; + default: + if (AOUT_FMT_SPDIF(fmt)) + spdif = var_InheritBool (aout, "spdif"); + if (spdif) + format = AFMT_AC3; +#ifdef AFMT_FLOAT + else if (HAVE_FPU) + format = AFMT_FLOAT; +#endif + else + format = AFMT_S16_NE; } - if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 ) + if (ioctl (fd, SNDCTL_DSP_SETFMT, &format) < 0) { - msg_Err( p_aout, "cannot reset OSS audio device" ); - var_Destroy( p_aout, "audio-device" ); - return; + msg_Err (aout, "cannot set audio format 0x%X: %m", format); + goto error; } - /* Test for spdif. */ - if ( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) ) + switch (format) { - 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( config_GetInt( p_aout, "spdif" ) ) - var_Set( p_aout, "audio-device", val ); - } - else if( config_GetInt( p_aout, "spdif" ) ) - { - msg_Warn( p_aout, "S/PDIF not supported by card" ); - } + case AFMT_U8: fmt->i_format = VLC_CODEC_U8; break; + case AFMT_S16_NE: fmt->i_format = VLC_CODEC_S16N; break; + case AFMT_S32_NE: fmt->i_format = VLC_CODEC_S32N; break; +#ifdef AFMT_FLOAT + case AFMT_FLOAT: fmt->i_format = VLC_CODEC_FL32; break; +#endif + case AFMT_AC3: + if (spdif) + { + fmt->i_format = VLC_CODEC_SPDIFL; + break; + } + default: + msg_Err (aout, "unsupported audio format 0x%X", format); + goto error; } - var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, - NULL ); -} - -/***************************************************************************** - * Open: open the audio device (the digital sound processor) - ***************************************************************************** - * 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 ) -{ - aout_instance_t * p_aout = (aout_instance_t *)p_this; - struct aout_sys_t * p_sys; - char * psz_device; - vlc_value_t val; - - /* Allocate structure */ - p_aout->output.p_sys = p_sys = malloc( sizeof( aout_sys_t ) ); - if( p_sys == NULL ) - return VLC_ENOMEM; - - /* Get device name */ - if( (psz_device = config_GetPsz( p_aout, "oss-audio-device" )) == NULL ) + /* Select channels count */ + int channels = spdif ? 2 : aout_FormatNbChannels (fmt); + if (ioctl (fd, SNDCTL_DSP_CHANNELS, &channels) < 0) { - msg_Err( p_aout, "no audio device specified (maybe /dev/dsp?)" ); - free( p_sys ); - return VLC_EGENERIC; + msg_Err (aout, "cannot set %d channels: %m", channels); + goto error; } - /* 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 = open( psz_device, O_WRONLY | O_NDELAY ); - if( p_sys->i_fd < 0 ) + switch (channels) { - msg_Err( p_aout, "cannot open audio device (%s)", psz_device ); - free( psz_device ); - free( p_sys ); - return VLC_EGENERIC; + case 1: channels = AOUT_CHAN_CENTER; break; + case 2: channels = AOUT_CHANS_STEREO; break; + case 4: channels = AOUT_CHANS_4_0; break; + case 6: channels = AOUT_CHANS_5_1; break; + case 8: channels = AOUT_CHANS_7_1; break; + default: + msg_Err (aout, "unsupported channels count %d", channels); + goto error; } - /* 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; - - if ( var_Type( p_aout, "audio-device" ) == 0 ) + /* Select sample rate */ + int rate = spdif ? 48000 : fmt->i_rate; + if (ioctl (fd, SNDCTL_DSP_SPEED, &rate) < 0) { - Probe( p_aout ); + msg_Err (aout, "cannot set %d Hz sample rate: %m", rate); + goto error; } - if ( var_Get( p_aout, "audio-device", &val ) < 0 ) - { - /* Probe() has failed. */ - close( p_sys->i_fd ); - free( p_sys ); - return VLC_EGENERIC; - } + /* Setup audio_output_t */ + aout->time_get = TimeGet; + aout->play = Play; + aout->pause = Pause; + aout->flush = Flush; - if ( val.i_int == AOUT_VAR_SPDIF ) - { - p_aout->output.output.i_format = VLC_CODEC_SPDIFL; - } - else if ( val.i_int == AOUT_VAR_5_1 ) - { - 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_REARRIGHT - | AOUT_CHAN_LFE; - } - else if ( val.i_int == AOUT_VAR_2F2R ) - { - 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_REARRIGHT; - } - else if ( val.i_int == AOUT_VAR_STEREO ) - { - p_aout->output.output.i_format = VLC_CODEC_S16N; - p_aout->output.output.i_physical_channels - = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; - } - else if ( val.i_int == AOUT_VAR_MONO ) + if (spdif) { - p_aout->output.output.i_format = VLC_CODEC_S16N; - p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER; + fmt->i_bytes_per_frame = AOUT_SPDIF_SIZE; + fmt->i_frame_length = A52_FRAME_NB; } else { - /* This should not happen ! */ - msg_Err( p_aout, "internal: can't find audio-device (%i)", val.i_int ); - close( p_sys->i_fd ); - free( p_sys ); - return VLC_EGENERIC; + fmt->i_rate = rate; + fmt->i_original_channels = + fmt->i_physical_channels = channels; } - var_SetBool( p_aout, "intf-change", true ); + VolumeSync (aout); + sys->starting = true; + sys->format = *fmt; + return VLC_SUCCESS; +error: + close (fd); + return VLC_EGENERIC; +} - /* Reset the DSP device */ - if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 ) - { - msg_Err( p_aout, "cannot reset OSS audio device" ); - close( p_sys->i_fd ); - free( p_sys ); - return VLC_EGENERIC; - } +static int TimeGet (audio_output_t *aout, mtime_t *restrict pts) +{ + aout_sys_t *sys = aout->sys; + int delay; - /* Set the output format */ - if ( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) ) + if (ioctl (sys->fd, SNDCTL_DSP_GETODELAY, &delay) < 0) { - int i_format = AFMT_AC3; - - if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 - || i_format != AFMT_AC3 ) - { - msg_Err( p_aout, "cannot reset OSS audio device" ); - close( p_sys->i_fd ); - free( p_sys ); - return VLC_EGENERIC; - } + msg_Warn (aout, "cannot get delay: %m"); + return -1; + } - 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; + *pts = (delay * CLOCK_FREQ * sys->format.i_frame_length) + / (sys->format.i_rate * sys->format.i_bytes_per_frame); + return 0; +} - aout_VolumeNoneInit( p_aout ); - } +/** + * Queues one audio buffer to the hardware. + */ +static void Play (audio_output_t *aout, block_t *block) +{ + aout_sys_t *sys = aout->sys; + int fd = sys->fd; - if ( !AOUT_FMT_NON_LINEAR( &p_aout->output.output ) ) + while (block->i_buffer > 0) { - unsigned int i_format = AFMT_S16_NE; - unsigned int i_frame_size, i_fragments; - unsigned int i_rate; - unsigned int i_nb_channels; - audio_buf_info audio_buf; - - if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 ) + ssize_t bytes = write (fd, block->p_buffer, block->i_buffer); + if (bytes >= 0) { - msg_Err( p_aout, "cannot set audio output format" ); - close( p_sys->i_fd ); - free( p_sys ); - return VLC_EGENERIC; + block->p_buffer += bytes; + block->i_buffer -= bytes; } + else + msg_Err (aout, "cannot write samples: %m"); + } + block_Release (block); - switch ( i_format ) - { - case AFMT_U8: - p_aout->output.output.i_format = VLC_CODEC_U8; - break; - case AFMT_S8: - p_aout->output.output.i_format = VLC_CODEC_S8; - break; - case AFMT_U16_LE: - p_aout->output.output.i_format = VLC_CODEC_U16L; - break; - case AFMT_S16_LE: - p_aout->output.output.i_format = VLC_CODEC_S16L; - break; - case AFMT_U16_BE: - p_aout->output.output.i_format = VLC_CODEC_U16B; - break; - case AFMT_S16_BE: - p_aout->output.output.i_format = VLC_CODEC_S16B; - break; - default: - msg_Err( p_aout, "OSS fell back to an unknown format (%d)", - i_format ); - close( p_sys->i_fd ); - free( p_sys ); - return VLC_EGENERIC; - } + /* Dumb OSS cannot send any kind of events for this... */ + VolumeSync (aout); +} - i_nb_channels = aout_FormatNbChannels( &p_aout->output.output ); +/** + * Pauses/resumes the audio playback. + */ +static void Pause (audio_output_t *aout, bool pause, mtime_t date) +{ + aout_sys_t *sys = aout->sys; + int fd = sys->fd; - /* Set the number of channels */ - if( ioctl( p_sys->i_fd, SNDCTL_DSP_CHANNELS, &i_nb_channels ) < 0 || - i_nb_channels != aout_FormatNbChannels( &p_aout->output.output ) ) - { - msg_Err( p_aout, "cannot set number of audio channels (%s)", - aout_FormatPrintChannels( &p_aout->output.output) ); - close( p_sys->i_fd ); - free( p_sys ); - return VLC_EGENERIC; - } + (void) date; + ioctl (fd, pause ? SNDCTL_DSP_SILENCE : SNDCTL_DSP_SKIP, NULL); +} - /* Set the output rate */ - i_rate = p_aout->output.output.i_rate; - if( ioctl( p_sys->i_fd, SNDCTL_DSP_SPEED, &i_rate ) < 0 ) - { - msg_Err( p_aout, "cannot set audio output rate (%i)", - p_aout->output.output.i_rate ); - close( p_sys->i_fd ); - free( p_sys ); - return VLC_EGENERIC; - } +/** + * Flushes/drains the audio playback buffer. + */ +static void Flush (audio_output_t *aout, bool wait) +{ + aout_sys_t *sys = aout->sys; + int fd = sys->fd; - if( i_rate != p_aout->output.output.i_rate ) - { - p_aout->output.output.i_rate = i_rate; - } + if (wait) + return; /* drain is implicit with OSS */ + ioctl (fd, SNDCTL_DSP_HALT_OUTPUT, NULL); +} - /* Set the fragment size */ - aout_FormatPrepare( &p_aout->output.output ); +static int VolumeSync (audio_output_t *aout) +{ + aout_sys_t *sys = aout->sys; + int fd = sys->fd; + + int level; + if (ioctl (fd, SNDCTL_DSP_GETPLAYVOL, &level) < 0) + return -1; + + sys->mute = !level; + if (level) /* try to keep last volume before mute */ + sys->level = level; + aout_MuteReport (aout, !level); + aout_VolumeReport (aout, (float)(level & 0xFF) / 100.f); + return 0; +} - /* i_fragment = xxxxyyyy where: xxxx is fragtotal - * 1 << yyyy is fragsize */ - 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; - } - i_fragments |= FRAME_COUNT << 16; - if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFRAGMENT, &i_fragments ) < 0 ) - { - msg_Warn( p_aout, "cannot set fragment size (%.8x)", i_fragments ); - } +/** + * Releases the audio output device. + */ +static void Stop (audio_output_t *aout) +{ + aout_sys_t *sys = aout->sys; + int fd = sys->fd; - if( ioctl( p_sys->i_fd, SNDCTL_DSP_GETOSPACE, &audio_buf ) < 0 ) - { - msg_Err( p_aout, "cannot get fragment size" ); - close( p_sys->i_fd ); - free( p_sys ); - return VLC_EGENERIC; - } - else - { - /* Number of fragments actually allocated */ - p_aout->output.p_sys->i_fragstotal = audio_buf.fragstotal; - - /* Maximum duration the soundcard's buffer can hold */ - p_aout->output.p_sys->max_buffer_duration = - (mtime_t)audio_buf.fragstotal * audio_buf.fragsize * 1000000 - / p_aout->output.output.i_bytes_per_frame - / p_aout->output.output.i_rate - * p_aout->output.output.i_frame_length; - - p_aout->output.i_nb_samples = audio_buf.fragsize / - p_aout->output.output.i_bytes_per_frame; - } + ioctl (fd, SNDCTL_DSP_HALT, NULL); + close (fd); + sys->fd = -1; +} - aout_VolumeSoftInit( p_aout ); +static int VolumeSet (audio_output_t *aout, float vol) +{ + aout_sys_t *sys = aout->sys; + int fd = sys->fd; + if (fd == -1) + return -1; + + int level = lroundf (vol * 100.f); + if (level > 0xFF) + level = 0xFFFF; + else + level |= level << 8; + if (!sys->mute && ioctl (fd, SNDCTL_DSP_SETPLAYVOL, &level) < 0) + { + msg_Err (aout, "cannot set volume: %m"); + return -1; } - p_aout->output.p_sys->b_workaround_buggy_driver = - config_GetInt( p_aout, "oss-buggy" ); + sys->level = level; + aout_VolumeReport (aout, (float)(level & 0xFF) / 100.f); + return 0; +} - /* Create OSS thread and wait for its readiness. */ - if( vlc_thread_create( p_aout, "aout", OSSThread, - VLC_THREAD_PRIORITY_OUTPUT ) ) +static int MuteSet (audio_output_t *aout, bool mute) +{ + aout_sys_t *sys = aout->sys; + int fd = sys->fd; + if (fd == -1) + return -1; + + int level = mute ? 0 : (sys->level | (sys->level << 8)); + if (ioctl (fd, SNDCTL_DSP_SETPLAYVOL, &level) < 0) { - msg_Err( p_aout, "cannot create OSS thread (%m)" ); - close( p_sys->i_fd ); - free( p_sys ); - return VLC_ETHREAD; + msg_Err (aout, "cannot mute: %m"); + return -1; } - return VLC_SUCCESS; + sys->mute = mute; + aout_MuteReport (aout, mute); + return 0; } -/***************************************************************************** - * Play: nothing to do - *****************************************************************************/ -static void Play( aout_instance_t *p_aout ) +static int DevicesEnum (audio_output_t *aout) { - VLC_UNUSED(p_aout); -} + int fd = vlc_open ("/dev/dsp", O_WRONLY); + if (fd == -1) + return -1; -/***************************************************************************** - * 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; + oss_sysinfo si; + int n = -1; - vlc_object_kill( p_aout ); - vlc_thread_join( p_aout ); - p_aout->b_die = false; + if (ioctl (fd, SNDCTL_SYSINFO, &si) < 0) + { + msg_Err (aout, "cannot get system infos: %m"); + goto out; + } - ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ); - close( p_sys->i_fd ); + msg_Dbg (aout, "using %s version %s (0x%06X) under %s", si.product, + si.version, si.versionnum, si.license); - free( p_sys ); -} + for (int i = 0; i < si.numaudios; i++) + { + oss_audioinfo ai = { .dev = i }; -/***************************************************************************** - * BufferDuration: buffer status query - ***************************************************************************** - * This function returns the duration in microseconds of the current buffer. - *****************************************************************************/ -static mtime_t BufferDuration( aout_instance_t * p_aout ) -{ - struct aout_sys_t * p_sys = p_aout->output.p_sys; - audio_buf_info audio_buf; - int i_bytes; - - /* Fill the audio_buf_info structure: - * - fragstotal: total number of fragments allocated - * - fragsize: size of a fragment in bytes - * - bytes: available space in bytes (includes partially used fragments) - * Note! 'bytes' could be more than fragments*fragsize */ - ioctl( p_sys->i_fd, SNDCTL_DSP_GETOSPACE, &audio_buf ); - - /* calculate number of available fragments (not partially used ones) */ - i_bytes = (audio_buf.fragstotal * audio_buf.fragsize) - audio_buf.bytes; - - /* Return the fragment duration */ - return (mtime_t)i_bytes * 1000000 - / p_aout->output.output.i_bytes_per_frame - / p_aout->output.output.i_rate - * p_aout->output.output.i_frame_length; + if (ioctl (fd, SNDCTL_AUDIOINFO, &ai) < 0) + { + msg_Warn (aout, "cannot get device %d infos: %m", i); + continue; + } + if (ai.caps & (PCM_CAP_HIDDEN|PCM_CAP_MODEM)) + continue; + if (!(ai.caps & PCM_CAP_OUTPUT)) + continue; + if (!ai.enabled) + continue; + + aout_HotplugReport (aout, ai.devnode, ai.name); + n++; + } +out: + close (fd); + return n; } -/***************************************************************************** - * OSSThread: asynchronous thread used to DMA the data to the device - *****************************************************************************/ -static void* OSSThread( vlc_object_t *p_this ) +static int DeviceSelect (audio_output_t *aout, const char *id) { - 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 (); + aout_sys_t *sys = aout->sys; + char *path = NULL; - while ( vlc_object_alive (p_aout) ) + if (id != NULL) { - aout_buffer_t * p_buffer = NULL; - int i_tmp, i_size; - uint8_t * p_bytes; - - if ( p_aout->output.output.i_format != VLC_CODEC_SPDIFL ) - { - mtime_t buffered = BufferDuration( p_aout ); + path = strdup (id); + if (unlikely(path == NULL)) + return -1; + } - if( p_aout->output.p_sys->b_workaround_buggy_driver ) - { -#define i_fragstotal p_aout->output.p_sys->i_fragstotal - /* Wait a bit - we don't want our buffer to be full */ - if( buffered > (p_aout->output.p_sys->max_buffer_duration - / i_fragstotal * (i_fragstotal - 1)) ) - { - msleep((p_aout->output.p_sys->max_buffer_duration - / i_fragstotal )); - buffered = BufferDuration( p_aout ); - } -#undef i_fragstotal - } + free (sys->device); + sys->device = path; + aout_DeviceReport (aout, path); + aout_RestartRequest (aout, AOUT_RESTART_OUTPUT); + return 0; +} - /* Next buffer will be played at mdate() + buffered */ - p_buffer = aout_OutputNextBuffer( p_aout, mdate() + buffered, - false ); +static int Open (vlc_object_t *obj) +{ + audio_output_t *aout = (audio_output_t *)obj; - if( p_buffer == NULL && - buffered > ( p_aout->output.p_sys->max_buffer_duration - / p_aout->output.p_sys->i_fragstotal ) ) - { - /* If we have at least a fragment full, then we can wait a - * little and retry to get a new audio buffer instead of - * playing a blank sample */ - msleep( ( p_aout->output.p_sys->max_buffer_duration - / p_aout->output.p_sys->i_fragstotal / 2 ) ); - continue; - } - } - else - { - /* emu10k1 driver does not report Buffer Duration correctly in - * passthrough mode so we have to cheat */ - if( !next_date ) - { - next_date = mdate(); - } - else - { - mtime_t delay = next_date - mdate(); - if( delay > AOUT_PTS_TOLERANCE ) - { - msleep( delay / 2 ); - } - } + aout_sys_t *sys = malloc (sizeof (*sys)); + if(unlikely( sys == NULL )) + return VLC_ENOMEM; - while( vlc_object_alive (p_aout) && ! ( p_buffer = - aout_OutputNextBuffer( p_aout, next_date, true ) ) ) - { - msleep( 1000 ); - next_date = mdate(); - } - } + sys->fd = -1; - if ( p_buffer != NULL ) - { - p_bytes = p_buffer->p_buffer; - i_size = p_buffer->i_nb_bytes; - /* This is theoretical ... we'll see next iteration whether - * we're drifting */ - next_date += p_buffer->end_date - p_buffer->start_date; - } - else - { - i_size = FRAME_SIZE / p_aout->output.output.i_frame_length - * p_aout->output.output.i_bytes_per_frame; - p_bytes = malloc( i_size ); - memset( p_bytes, 0, i_size ); - next_date = 0; - } + sys->level = 100; + sys->mute = false; + sys->device = var_InheritString (aout, "oss-audio-device"); - i_tmp = write( p_sys->i_fd, p_bytes, i_size ); + aout->sys = sys; + aout->start = Start; + aout->stop = Stop; + aout->volume_set = VolumeSet; + aout->mute_set = MuteSet; + aout->device_select = DeviceSelect; - if( i_tmp < 0 ) - { - msg_Err( p_aout, "write failed (%m)" ); - } + DevicesEnum (aout); + return VLC_SUCCESS; +} - if ( p_buffer != NULL ) - { - aout_BufferFree( p_buffer ); - } - else - { - free( p_bytes ); - } - } +static void Close (vlc_object_t *obj) +{ + audio_output_t *aout = (audio_output_t *)obj; + aout_sys_t *sys = aout->sys; - vlc_restorecancel (canc); - return NULL; + free (sys->device); + free (sys); }