Commit 08569199 authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

OSS: rewrite and update to OSSv4

parent 9b1e5cc8
/*****************************************************************************
* 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) 2007-2012 Rémi Denis-Courmont
*
* Authors: Michel Kaempf <maxx@via.ecp.fr>
* Sam Hocevar <sam@zoy.org>
......@@ -23,651 +23,361 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <fcntl.h> /* open(), O_WRONLY */
#include <sys/ioctl.h> /* ioctl() */
#include <unistd.h> /* write(), close() */
#include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#ifdef HAVE_SOUNDCARD_H
# include <soundcard.h>
#else
# include <sys/soundcard.h>
#endif
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_fs.h>
#include <vlc_cpu.h>
#include <vlc_aout.h>
/* SNDCTL_DSP_RESET, SNDCTL_DSP_SETFMT, SNDCTL_DSP_STEREO, SNDCTL_DSP_SPEED,
* SNDCTL_DSP_GETOSPACE */
#ifdef HAVE_SOUNDCARD_H
# include <soundcard.h>
#elif defined( HAVE_SYS_SOUNDCARD_H )
# include <sys/soundcard.h>
#endif
/* Patches for ignorant OSS versions */
#ifndef AFMT_AC3
# define AFMT_AC3 0x00000400 /* Dolby Digital AC3 */
#endif
#define A52_FRAME_NB 1536
#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.
*****************************************************************************/
struct aout_sys_t
{
aout_packet_t packet;
int i_fd;
int i_fragstotal;
mtime_t max_buffer_duration;
vlc_thread_t thread;
float soft_gain;
bool soft_mute;
int fd;
uint8_t level;
bool mute;
};
/* This must be a power of 2. */
#define FRAME_SIZE 1024
#define FRAME_COUNT 32
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int Open ( vlc_object_t * );
static void Close ( vlc_object_t * );
static void* OSSThread ( void * );
static mtime_t BufferDuration( audio_output_t * p_aout );
static int Open (vlc_object_t *);
static void Close (vlc_object_t *);
#include "volume.h"
#define AUDIO_DEV_TEXT N_("Audio output device")
#define AUDIO_DEV_LONGTEXT N_("OSS device node path.")
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin ()
set_shortname( "OSS" )
set_description( N_("Open Sound System") )
set_description (N_("Open Sound System audio output"))
set_category( CAT_AUDIO )
set_subcategory( SUBCAT_AUDIO_AOUT )
add_loadfile( "oss-audio-device", "/dev/dsp",
N_("OSS DSP device"), NULL, false )
add_sw_gain ()
add_string ("oss-audio-device", "",
AUDIO_DEV_TEXT, AUDIO_DEV_LONGTEXT, false)
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
*****************************************************************************/
static void Probe( audio_output_t * p_aout )
{
struct aout_sys_t * p_sys = p_aout->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 );
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 *);
static int VolumeSet (audio_output_t *, float);
static int MuteSet (audio_output_t *, bool);
/* Test for multi-channel. */
#ifdef SNDCTL_DSP_GETCHANNELMASK
if ( aout_FormatNbChannels( &p_aout->format ) > 2 )
static int Open (vlc_object_t *obj)
{
audio_output_t *aout = (audio_output_t *)obj;
/* Open the device */
const char *device;
char *devicebuf = var_InheritString (aout, "oss-audio-device");
device = devicebuf;
if (device == NULL)
device = getenv ("OSS_AUDIODEV");
if (device == NULL)
device = "/dev/dsp";
msg_Dbg (aout, "using OSS device: %s", device);
int fd = vlc_open (device, O_WRONLY);
free (devicebuf);
if (fd == -1)
{
/* Check that the device supports this. */
msg_Err (aout, "cannot open OSS device: %m");
return VLC_EGENERIC;
}
int i_chanmask;
aout_sys_t *sys = malloc (sizeof (*sys));
if (unlikely(sys == NULL))
goto error;
aout->sys = sys;
sys->fd = fd;
/* 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;
vlc_fourcc_t fourcc = aout->format.i_format;
bool spdif = false;
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;
}
switch (fourcc)
{
case VLC_CODEC_F64B:
case VLC_CODEC_F64L:
case VLC_CODEC_F32B:
case VLC_CODEC_F32L:
format = AFMT_FLOAT;
break;
case VLC_CODEC_S32B:
format = AFMT_S32_BE;
break;
case VLC_CODEC_S32L:
format = AFMT_S32_LE;
break;
case VLC_CODEC_S16B:
format = AFMT_S16_BE;
break;
case VLC_CODEC_S16L:
format = AFMT_S16_LE;
break;
case VLC_CODEC_S8:
case VLC_CODEC_U8:
format = AFMT_U8;
break;
default:
if (AOUT_FMT_SPDIF(&aout->format))
spdif = var_InheritBool (aout, "spdif");
if (spdif)
format = AFMT_AC3;
else if (HAVE_FPU)
format = AFMT_FLOAT;
else
format = AFMT_S16_NE;
}
if ( (i_chanmask & (DSP_BIND_SURR | DSP_BIND_CENTER_LFE))
&& (p_aout->format.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 (ioctl (fd, SNDCTL_DSP_SETFMT, &format) < 0)
{
msg_Err (aout, "cannot set audio format 0x%X: %m", format);
goto error;
}
if ( (i_chanmask & DSP_BIND_SURR)
&& (p_aout->format.i_physical_channels &
(AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
| AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT)) )
switch (format)
{
case AFMT_S8: fourcc = VLC_CODEC_S8; break;
case AFMT_U8: fourcc = VLC_CODEC_U8; break;
case AFMT_S16_BE: fourcc = VLC_CODEC_S16B; break;
case AFMT_S16_LE: fourcc = VLC_CODEC_S16L; break;
//case AFMT_S24_BE:
//case AFMT_S24_LE:
case AFMT_S32_BE: fourcc = VLC_CODEC_S32B; break;
case AFMT_S32_LE: fourcc = VLC_CODEC_S32L; break;
case AFMT_FLOAT: fourcc = VLC_CODEC_FL32; break;
case AFMT_AC3:
if (spdif)
{
val.i_int = AOUT_VAR_2F2R;
text.psz_string = _("2 Front 2 Rear");
var_Change( p_aout, "audio-device",
VLC_VAR_ADDCHOICE, &val, &text );
fourcc = VLC_CODEC_SPDIFL;
break;
}
}
default:
msg_Err (aout, "unsupported audio format 0x%X", format);
goto error;
}
#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 )
/* Select channels count */
int channels = spdif ? 2 : aout_FormatNbChannels (&aout->format);
if (ioctl (fd, SNDCTL_DSP_CHANNELS, &channels) < 0)
{
msg_Err( p_aout, "cannot reset OSS audio device" );
var_Destroy( p_aout, "audio-device" );
return;
msg_Err (aout, "cannot set %d channels: %m", channels);
goto error;
}
/* Test for stereo. */
i_nb_channels = 2;
if( ioctl( p_sys->i_fd, SNDCTL_DSP_CHANNELS, &i_nb_channels ) >= 0
&& i_nb_channels == 2 )
switch (channels)
{
val.i_int = AOUT_VAR_STEREO;
text.psz_string = _("Stereo");
var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
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;
}
/* 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 )
/* Select sample rate */
int rate = spdif ? 48000 : aout->format.i_rate;
if (ioctl (fd, SNDCTL_DSP_SPEED, &rate) < 0)
{
msg_Err( p_aout, "cannot reset OSS audio device" );
var_Destroy( p_aout, "audio-device" );
return;
msg_Err (aout, "cannot set %d Hz sample rate: %m", rate);
goto error;
}
/* Test for mono. */
i_nb_channels = 1;
if( ioctl( p_sys->i_fd, SNDCTL_DSP_CHANNELS, &i_nb_channels ) >= 0
&& i_nb_channels == 1 )
{
val.i_int = AOUT_VAR_MONO;
text.psz_string = _("Mono");
var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
if ( p_aout->format.i_physical_channels == AOUT_CHAN_CENTER )
{
var_Set( p_aout, "audio-device", val );
}
}
/* Setup audio_output_t */
aout->format.i_format = fourcc;
aout->format.i_rate = rate;
aout->pf_play = Play;
aout->pf_pause = Pause;
aout->pf_flush = Flush;
aout->volume_set = NULL;
aout->mute_set = NULL;
if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 )
if (spdif)
{
msg_Err( p_aout, "cannot reset OSS audio device" );
var_Destroy( p_aout, "audio-device" );
return;
aout->format.i_bytes_per_frame = AOUT_SPDIF_SIZE;
aout->format.i_frame_length = A52_FRAME_NB;
}
/* Test for spdif. */
if ( AOUT_FMT_SPDIF( &p_aout->format ) )
else
{
i_format = AFMT_AC3;
aout->format.i_original_channels =
aout->format.i_physical_channels = channels;
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" ) )
sys->level = 100;
sys->mute = false;
if (VolumeSync (aout) == 0)
{
msg_Warn( p_aout, "S/PDIF not supported by card" );
aout->volume_set = VolumeSet;
aout->mute_set = MuteSet;
}
}
return 0;
error:
close (fd);
free (sys);
return VLC_EGENERIC;
}
/*****************************************************************************
* 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 )
/**
* Releases the audio output.
*/
static void Close (vlc_object_t *obj)
{
audio_output_t * p_aout = (audio_output_t *)p_this;
struct aout_sys_t * p_sys;
char * psz_device;
vlc_value_t val;
/* Allocate structure */
p_aout->sys = p_sys = malloc( sizeof( aout_sys_t ) );
if( p_sys == NULL )
return VLC_ENOMEM;
/* Get device name */
if( (psz_device = var_InheritString( p_aout, "oss-audio-device" )) == NULL )
{
msg_Err( p_aout, "no audio device specified (maybe /dev/dsp?)" );
free( p_sys );
return VLC_EGENERIC;
}
/* 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 )
audio_output_t *aout = (audio_output_t *)obj;
aout_sys_t *sys = aout->sys;
int fd = sys->fd;
#if 0
/* FIXME: ugly hack so selected OSS device survives restart */
char *device = var_InheritString (obj, "audio-device");
if (device != NULL)
{
msg_Err( p_aout, "cannot open audio device (%s)", psz_device );
free( psz_device );
free( p_sys );
return VLC_EGENERIC;
if (!var_Type (obj, "oss-audio-device"))
var_Create (obj, "oss-audio-device", VLC_VAR_STRING);
var_SetString (obj, "oss-audio-device", device);
free (device);
}
/* 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->pf_play = aout_PacketPlay;
p_aout->pf_pause = aout_PacketPause;
p_aout->pf_flush = aout_PacketFlush;
var_DelCallback (obj, "audio-device", aout_ChannelsRestart, NULL);
var_Destroy (obj, "audio-device");
#endif
if ( var_Type( p_aout, "audio-device" ) == 0 )
Probe( p_aout );
var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
ioctl (fd, SNDCTL_DSP_HALT, NULL);
close (fd);
free (sys);
}
if ( var_Get( p_aout, "audio-device", &val ) < 0 )
/* Probe() has failed. */
goto error;
/**
* 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 ( val.i_int == AOUT_VAR_SPDIF )
{
p_aout->format.i_format = VLC_CODEC_SPDIFL;
}
else if ( val.i_int == AOUT_VAR_5_1 )
{
p_aout->format.i_format = VLC_CODEC_S16N;
p_aout->format.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->format.i_format = VLC_CODEC_S16N;
p_aout->format.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->format.i_format = VLC_CODEC_S16N;
p_aout->format.i_physical_channels
= AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
}
else if ( val.i_int == AOUT_VAR_MONO )
int delay;
if (ioctl (sys->fd, SNDCTL_DSP_GETODELAY, &delay) >= 0)
{
p_aout->format.i_format = VLC_CODEC_S16N;
p_aout->format.i_physical_channels = AOUT_CHAN_CENTER;
mtime_t latency = (delay * CLOCK_FREQ * aout->format.i_frame_length)
/ (aout->format.i_rate * aout->format.i_bytes_per_frame);
/* TODO: insert zeroes when starting playback */
aout_TimeReport (aout, block->i_pts - latency);
}
else
{
/* This should not happen ! */
msg_Err( p_aout, "internal: can't find audio-device (%"PRId64")",
val.i_int );
goto error;
}
msg_Warn (aout, "cannot get delay: %m");
/* Reset the DSP device */
if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 )
while (block->i_buffer > 0)
{
msg_Err( p_aout, "cannot reset OSS audio device" );
goto error;
}
/* Set the output format */
if ( AOUT_FMT_SPDIF( &p_aout->format ) )
{
int i_format = AFMT_AC3;
if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0
|| i_format != AFMT_AC3 )
ssize_t bytes = write (fd, block->p_buffer, block->i_buffer);
if (bytes >= 0)
{
msg_Err( p_aout, "cannot reset OSS audio device" );
goto error;
block->p_buffer += bytes;
block->i_buffer -= bytes;
}
p_aout->format.i_format = VLC_CODEC_SPDIFL;
p_aout->format.i_bytes_per_frame = AOUT_SPDIF_SIZE;
p_aout->format.i_frame_length = A52_FRAME_NB;
aout_PacketInit( p_aout, &p_sys->packet, A52_FRAME_NB );
p_aout->volume_set = NULL;
p_aout->mute_set = NULL;
}
else
{
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 )
{
msg_Err( p_aout, "cannot set audio output format" );
goto error;
}
switch ( i_format )
{
case AFMT_U8:
p_aout->format.i_format = VLC_CODEC_U8;
break;
case AFMT_S8:
p_aout->format.i_format = VLC_CODEC_S8;
break;
case AFMT_U16_LE:
p_aout->format.i_format = VLC_CODEC_U16L;
break;
case AFMT_S16_LE:
p_aout->format.i_format = VLC_CODEC_S16L;
break;
case AFMT_U16_BE:
p_aout->format.i_format = VLC_CODEC_U16B;
break;
case AFMT_S16_BE:
p_aout->format.i_format = VLC_CODEC_S16B;
break;
default:
msg_Err( p_aout, "OSS fell back to an unknown format (%d)",
i_format );
goto error;
}
i_nb_channels = aout_FormatNbChannels( &p_aout->format );
/* 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->format ) )
{
msg_Err( p_aout, "cannot set number of audio channels (%s)",
aout_FormatPrintChannels( &p_aout->format) );
goto error;
}
/* Set the output rate */
i_rate = p_aout->format.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->format.i_rate );
goto error;
}
if( i_rate != p_aout->format.i_rate )
{
p_aout->format.i_rate = i_rate;
}
/* Set the fragment size */
aout_FormatPrepare( &p_aout->format );
/* i_fragment = xxxxyyyy where: xxxx is fragtotal
* 1 << yyyy is fragsize */
i_frame_size = ((uint64_t)p_aout->format.i_bytes_per_frame * p_aout->format.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 );
}
if( ioctl( p_sys->i_fd, SNDCTL_DSP_GETOSPACE, &audio_buf ) < 0 )
{
msg_Err( p_aout, "cannot get fragment size" );
goto error;
}
/* Number of fragments actually allocated */
p_aout->sys->i_fragstotal = audio_buf.fragstotal;
/* Maximum duration the soundcard's buffer can hold */
p_aout->sys->max_buffer_duration =
(mtime_t)audio_buf.fragstotal * audio_buf.fragsize * 1000000
/ p_aout->format.i_bytes_per_frame
/ p_aout->format.i_rate
* p_aout->format.i_frame_length;
aout_PacketInit( p_aout, &p_sys->packet,
audio_buf.fragsize/p_aout->format.i_bytes_per_frame );
aout_SoftVolumeInit( p_aout );
}
/* Create OSS thread and wait for its readiness. */
if( vlc_clone( &p_sys->thread, OSSThread, p_aout,
VLC_THREAD_PRIORITY_OUTPUT ) )
{
msg_Err( p_aout, "cannot create OSS thread (%m)" );
aout_PacketDestroy( p_aout );
goto error;
else
msg_Err (aout, "cannot write samples: %m");
}
block_Release (block);
return VLC_SUCCESS;
error:
var_DelCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
close( p_sys->i_fd );
free( p_sys );
return VLC_EGENERIC;
/* Dumb OSS cannot send any kind of events for this... */
VolumeSync (aout);
}
/*****************************************************************************
* Close: close the DSP audio device
*****************************************************************************/
static void Close( vlc_object_t * p_this )
/**
* Pauses/resumes the audio playback.
*/
static void Pause (audio_output_t *aout, bool pause, mtime_t date)
{
audio_output_t *p_aout = (audio_output_t *)p_this;
struct aout_sys_t * p_sys = p_aout->sys;
vlc_cancel( p_sys->thread );
vlc_join( p_sys->thread, NULL );
p_aout->b_die = false;
var_DelCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL );
close( p_sys->i_fd );
aout_sys_t *sys = aout->sys;
int fd = sys->fd;
aout_PacketDestroy( p_aout );
free( p_sys );
(void) date;
ioctl (fd, pause ? SNDCTL_DSP_SILENCE : SNDCTL_DSP_SKIP, NULL);
}
/*****************************************************************************
* BufferDuration: buffer status query
*****************************************************************************
* This function returns the duration in microseconds of the current buffer.
*****************************************************************************/
static mtime_t BufferDuration( audio_output_t * p_aout )
/**
* Flushes/drains the audio playback buffer.
*/
static void Flush (audio_output_t *aout, bool wait)
{
struct aout_sys_t * p_sys = p_aout->sys;
audio_buf_info audio_buf;
int i_bytes;
#ifdef SNDCTL_DSP_GETODELAY
if ( ioctl( p_sys->i_fd, SNDCTL_DSP_GETODELAY, &i_bytes ) < 0 )
#endif
{
/* Fall back to GETOSPACE and approximate latency. */
/* 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;
}
aout_sys_t *sys = aout->sys;
int fd = sys->fd;
/* Return the fragment duration */
return (mtime_t)i_bytes * 1000000
/ p_aout->format.i_bytes_per_frame
/ p_aout->format.i_rate
* p_aout->format.i_frame_length;
if (wait)
return; /* drain is implicit with OSS */
ioctl (fd, SNDCTL_DSP_HALT_OUTPUT, NULL);
}
typedef struct
static int VolumeSync (audio_output_t *aout)
{
block_t *p_buffer;
void *p_bytes;
} oss_thread_ctx_t;
static void OSSThreadCleanup( void *data )
{
oss_thread_ctx_t *p_ctx = data;
if( p_ctx->p_buffer )
block_Release( p_ctx->p_buffer );
else
free( p_ctx->p_bytes );
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;
}
/*****************************************************************************
* OSSThread: asynchronous thread used to DMA the data to the device
*****************************************************************************/
static void* OSSThread( void *obj )
static int VolumeSet (audio_output_t *aout, float vol)
{
audio_output_t * p_aout = (audio_output_t*)obj;
struct aout_sys_t * p_sys = p_aout->sys;
mtime_t next_date = 0;
aout_sys_t *sys = aout->sys;
int fd = sys->fd;
for( ;; )
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)
{
block_t * p_buffer = NULL;
int canc = vlc_savecancel ();
if ( p_aout->format.i_format != VLC_CODEC_SPDIFL )
{
mtime_t buffered = BufferDuration( p_aout );
/* Next buffer will be played at mdate() + buffered */
p_buffer = aout_PacketNext( p_aout, mdate() + buffered );
if( p_buffer == NULL &&
buffered > ( p_aout->sys->max_buffer_duration
/ p_aout->sys->i_fragstotal ) )
{
vlc_restorecancel (canc);
/* 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->sys->max_buffer_duration
/ p_aout->sys->i_fragstotal / 2 ) );
continue;
}
}
else
{
vlc_restorecancel (canc);
/* 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_MAX_PTS_ADVANCE )
{
msleep( delay / 2 );
}
}
for( ;; )
{
canc = vlc_savecancel ();
p_buffer = aout_PacketNext( p_aout, next_date );
if ( p_buffer )
break;
vlc_restorecancel (canc);
msleep( VLC_HARD_MIN_SLEEP );
next_date = mdate();
}
}
uint8_t * p_bytes;
int i_size;
if ( p_buffer != NULL )
{
p_bytes = p_buffer->p_buffer;
i_size = p_buffer->i_buffer;
/* This is theoretical ... we'll see next iteration whether
* we're drifting */
next_date += p_buffer->i_length;
}
else
{
i_size = FRAME_SIZE / p_aout->format.i_frame_length
* p_aout->format.i_bytes_per_frame;
p_bytes = malloc( i_size );
memset( p_bytes, 0, i_size );
next_date = 0;
}
oss_thread_ctx_t ctx = {
.p_buffer = p_buffer,
.p_bytes = p_bytes,
};
msg_Err (aout, "cannot set volume: %m");
return -1;
}
vlc_cleanup_push( OSSThreadCleanup, &ctx );
vlc_restorecancel( canc );
sys->level = level;
aout_VolumeReport (aout, (float)(level & 0xFF) / 100.f);
return 0;
}
int i_tmp = write( p_sys->i_fd, p_bytes, i_size );
static int MuteSet (audio_output_t *aout, bool mute)
{
aout_sys_t *sys = aout->sys;
int fd = sys->fd;
if( i_tmp < 0 )
{
msg_Err( p_aout, "write failed (%m)" );
}
vlc_cleanup_run();
int level = mute ? 0 : (sys->level | (sys->level << 8));
if (ioctl (fd, SNDCTL_DSP_SETPLAYVOL, &level) < 0)
{
msg_Err (aout, "cannot mute: %m");
return -1;
}
return NULL;
sys->mute = mute;
aout_MuteReport (aout, mute);
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment