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 * Copyright (C) 2000-2002 the VideoLAN team
* $Id$ * Copyright (C) 2007-2012 Rémi Denis-Courmont
* *
* Authors: Michel Kaempf <maxx@via.ecp.fr> * Authors: Michel Kaempf <maxx@via.ecp.fr>
* Sam Hocevar <sam@zoy.org> * Sam Hocevar <sam@zoy.org>
...@@ -23,651 +23,361 @@ ...@@ -23,651 +23,361 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/ *****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
# include "config.h" # include "config.h"
#endif #endif
#include <fcntl.h> /* open(), O_WRONLY */ #include <stdlib.h>
#include <sys/ioctl.h> /* ioctl() */ #include <math.h>
#include <unistd.h> /* write(), close() */ #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_common.h>
#include <vlc_plugin.h> #include <vlc_plugin.h>
#include <vlc_fs.h> #include <vlc_fs.h>
#include <vlc_cpu.h>
#include <vlc_aout.h> #include <vlc_aout.h>
/* SNDCTL_DSP_RESET, SNDCTL_DSP_SETFMT, SNDCTL_DSP_STEREO, SNDCTL_DSP_SPEED, #define A52_FRAME_NB 1536
* 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
#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 struct aout_sys_t
{ {
aout_packet_t packet; int fd;
int i_fd; uint8_t level;
int i_fragstotal; bool mute;
mtime_t max_buffer_duration;
vlc_thread_t thread;
float soft_gain;
bool soft_mute;
}; };
/* This must be a power of 2. */ static int Open (vlc_object_t *);
#define FRAME_SIZE 1024 static void Close (vlc_object_t *);
#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 );
#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 () vlc_module_begin ()
set_shortname( "OSS" ) set_shortname( "OSS" )
set_description( N_("Open Sound System") ) set_description (N_("Open Sound System audio output"))
set_category( CAT_AUDIO ) set_category( CAT_AUDIO )
set_subcategory( SUBCAT_AUDIO_AOUT ) set_subcategory( SUBCAT_AUDIO_AOUT )
add_loadfile( "oss-audio-device", "/dev/dsp", add_string ("oss-audio-device", "",
N_("OSS DSP device"), NULL, false ) AUDIO_DEV_TEXT, AUDIO_DEV_LONGTEXT, false)
add_sw_gain ()
set_capability( "audio output", 100 ) set_capability( "audio output", 100 )
add_shortcut( "oss" )
set_callbacks( Open, Close ) set_callbacks( Open, Close )
vlc_module_end () vlc_module_end ()
/***************************************************************************** static void Play (audio_output_t *, block_t *);
* Probe: probe the audio device for available formats and channels static void Pause (audio_output_t *, bool, mtime_t);
*****************************************************************************/ static void Flush (audio_output_t *, bool);
static void Probe( audio_output_t * p_aout ) static int VolumeSync (audio_output_t *);
{ static int VolumeSet (audio_output_t *, float);
struct aout_sys_t * p_sys = p_aout->sys; static int MuteSet (audio_output_t *, bool);
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. */ static int Open (vlc_object_t *obj)
#ifdef SNDCTL_DSP_GETCHANNELMASK {
if ( aout_FormatNbChannels( &p_aout->format ) > 2 ) 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. */ /* Select audio format */
i_format = AFMT_S16_NE; int format;
if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 || vlc_fourcc_t fourcc = aout->format.i_format;
ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 ) bool spdif = false;
{
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, switch (fourcc)
&i_chanmask ) == 0 ) {
{ case VLC_CODEC_F64B:
if ( !(i_chanmask & DSP_BIND_FRONT) ) case VLC_CODEC_F64L:
{ case VLC_CODEC_F32B:
msg_Err( p_aout, "no front channels! (%x)", case VLC_CODEC_F32L:
i_chanmask ); format = AFMT_FLOAT;
return; 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)) if (ioctl (fd, SNDCTL_DSP_SETFMT, &format) < 0)
&& (p_aout->format.i_physical_channels == {
(AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER msg_Err (aout, "cannot set audio format 0x%X: %m", format);
| AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT goto error;
| 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) switch (format)
&& (p_aout->format.i_physical_channels & {
(AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT case AFMT_S8: fourcc = VLC_CODEC_S8; break;
| AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT)) ) 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; fourcc = VLC_CODEC_SPDIFL;
text.psz_string = _("2 Front 2 Rear"); break;
var_Change( p_aout, "audio-device",
VLC_VAR_ADDCHOICE, &val, &text );
} }
} default:
msg_Err (aout, "unsupported audio format 0x%X", format);
goto error;
} }
#endif
/* Reset all. */ /* Select channels count */
i_format = AFMT_S16_NE; int channels = spdif ? 2 : aout_FormatNbChannels (&aout->format);
if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 || if (ioctl (fd, SNDCTL_DSP_CHANNELS, &channels) < 0)
ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 )
{ {
msg_Err( p_aout, "cannot reset OSS audio device" ); msg_Err (aout, "cannot set %d channels: %m", channels);
var_Destroy( p_aout, "audio-device" ); goto error;
return;
} }
/* Test for stereo. */ switch (channels)
i_nb_channels = 2;
if( ioctl( p_sys->i_fd, SNDCTL_DSP_CHANNELS, &i_nb_channels ) >= 0
&& i_nb_channels == 2 )
{ {
val.i_int = AOUT_VAR_STEREO; case 1: channels = AOUT_CHAN_CENTER; break;
text.psz_string = _("Stereo"); case 2: channels = AOUT_CHANS_STEREO; break;
var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text ); 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. */ /* Select sample rate */
i_format = AFMT_S16_NE; int rate = spdif ? 48000 : aout->format.i_rate;
if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 || if (ioctl (fd, SNDCTL_DSP_SPEED, &rate) < 0)
ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 )
{ {
msg_Err( p_aout, "cannot reset OSS audio device" ); msg_Err (aout, "cannot set %d Hz sample rate: %m", rate);
var_Destroy( p_aout, "audio-device" ); goto error;
return;
} }
/* Test for mono. */ /* Setup audio_output_t */
i_nb_channels = 1; aout->format.i_format = fourcc;
if( ioctl( p_sys->i_fd, SNDCTL_DSP_CHANNELS, &i_nb_channels ) >= 0 aout->format.i_rate = rate;
&& i_nb_channels == 1 ) aout->pf_play = Play;
{ aout->pf_pause = Pause;
val.i_int = AOUT_VAR_MONO; aout->pf_flush = Flush;
text.psz_string = _("Mono"); aout->volume_set = NULL;
var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text ); aout->mute_set = NULL;
if ( p_aout->format.i_physical_channels == AOUT_CHAN_CENTER )
{
var_Set( p_aout, "audio-device", val );
}
}
if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 ) if (spdif)
{ {
msg_Err( p_aout, "cannot reset OSS audio device" ); aout->format.i_bytes_per_frame = AOUT_SPDIF_SIZE;
var_Destroy( p_aout, "audio-device" ); aout->format.i_frame_length = A52_FRAME_NB;
return;
} }
else
/* Test for spdif. */
if ( AOUT_FMT_SPDIF( &p_aout->format ) )
{ {
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 sys->level = 100;
&& i_format == AFMT_AC3 ) sys->mute = false;
{ if (VolumeSync (aout) == 0)
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" ); 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) * Releases the audio output.
***************************************************************************** */
* This function opens the DSP as a usual non-blocking write-only file, and static void Close (vlc_object_t *obj)
* modifies the p_aout->p_sys->i_fd with the file's descriptor.
*****************************************************************************/
static int Open( vlc_object_t *p_this )
{ {
audio_output_t * p_aout = (audio_output_t *)p_this; audio_output_t *aout = (audio_output_t *)obj;
struct aout_sys_t * p_sys; aout_sys_t *sys = aout->sys;
char * psz_device; int fd = sys->fd;
vlc_value_t val;
#if 0
/* Allocate structure */ /* FIXME: ugly hack so selected OSS device survives restart */
p_aout->sys = p_sys = malloc( sizeof( aout_sys_t ) ); char *device = var_InheritString (obj, "audio-device");
if( p_sys == NULL ) if (device != 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 )
{ {
msg_Err( p_aout, "cannot open audio device (%s)", psz_device ); if (!var_Type (obj, "oss-audio-device"))
free( psz_device ); var_Create (obj, "oss-audio-device", VLC_VAR_STRING);
free( p_sys ); var_SetString (obj, "oss-audio-device", device);
return VLC_EGENERIC; free (device);
} }
/* if the opening was ok, put the device back in blocking mode */ var_DelCallback (obj, "audio-device", aout_ChannelsRestart, NULL);
fcntl( p_sys->i_fd, F_SETFL, var_Destroy (obj, "audio-device");
fcntl( p_sys->i_fd, F_GETFL ) &~ FNDELAY ); #endif
free( psz_device );
p_aout->pf_play = aout_PacketPlay;
p_aout->pf_pause = aout_PacketPause;
p_aout->pf_flush = aout_PacketFlush;
if ( var_Type( p_aout, "audio-device" ) == 0 ) ioctl (fd, SNDCTL_DSP_HALT, NULL);
Probe( p_aout ); close (fd);
var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL ); free (sys);
}
if ( var_Get( p_aout, "audio-device", &val ) < 0 ) /**
/* Probe() has failed. */ * Queues one audio buffer to the hardware.
goto error; */
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 ) int delay;
{ if (ioctl (sys->fd, SNDCTL_DSP_GETODELAY, &delay) >= 0)
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 )
{ {
p_aout->format.i_format = VLC_CODEC_S16N; mtime_t latency = (delay * CLOCK_FREQ * aout->format.i_frame_length)
p_aout->format.i_physical_channels = AOUT_CHAN_CENTER; / (aout->format.i_rate * aout->format.i_bytes_per_frame);
/* TODO: insert zeroes when starting playback */
aout_TimeReport (aout, block->i_pts - latency);
} }
else else
{ msg_Warn (aout, "cannot get delay: %m");
/* This should not happen ! */
msg_Err( p_aout, "internal: can't find audio-device (%"PRId64")",
val.i_int );
goto error;
}
/* Reset the DSP device */ while (block->i_buffer > 0)
if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 )
{ {
msg_Err( p_aout, "cannot reset OSS audio device" ); ssize_t bytes = write (fd, block->p_buffer, block->i_buffer);
goto error; if (bytes >= 0)
}
/* 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 )
{ {
msg_Err( p_aout, "cannot reset OSS audio device" ); block->p_buffer += bytes;
goto error; block->i_buffer -= bytes;
} }
else
p_aout->format.i_format = VLC_CODEC_SPDIFL; msg_Err (aout, "cannot write samples: %m");
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;
} }
block_Release (block);
return VLC_SUCCESS; /* Dumb OSS cannot send any kind of events for this... */
VolumeSync (aout);
error:
var_DelCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
close( p_sys->i_fd );
free( p_sys );
return VLC_EGENERIC;
} }
/***************************************************************************** /**
* Close: close the DSP audio device * Pauses/resumes the audio playback.
*****************************************************************************/ */
static void Close( vlc_object_t * p_this ) static void Pause (audio_output_t *aout, bool pause, mtime_t date)
{ {
audio_output_t *p_aout = (audio_output_t *)p_this; aout_sys_t *sys = aout->sys;
struct aout_sys_t * p_sys = p_aout->sys; int fd = sys->fd;
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_PacketDestroy( p_aout ); (void) date;
free( p_sys ); ioctl (fd, pause ? SNDCTL_DSP_SILENCE : SNDCTL_DSP_SKIP, NULL);
} }
/***************************************************************************** /**
* BufferDuration: buffer status query * Flushes/drains the audio playback buffer.
***************************************************************************** */
* This function returns the duration in microseconds of the current buffer. static void Flush (audio_output_t *aout, bool wait)
*****************************************************************************/
static mtime_t BufferDuration( audio_output_t * p_aout )
{ {
struct aout_sys_t * p_sys = p_aout->sys; aout_sys_t *sys = aout->sys;
audio_buf_info audio_buf; int fd = sys->fd;
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;
}
/* Return the fragment duration */ if (wait)
return (mtime_t)i_bytes * 1000000 return; /* drain is implicit with OSS */
/ p_aout->format.i_bytes_per_frame ioctl (fd, SNDCTL_DSP_HALT_OUTPUT, NULL);
/ p_aout->format.i_rate
* p_aout->format.i_frame_length;
} }
typedef struct static int VolumeSync (audio_output_t *aout)
{ {
block_t *p_buffer; aout_sys_t *sys = aout->sys;
void *p_bytes; int fd = sys->fd;
} oss_thread_ctx_t;
int level;
static void OSSThreadCleanup( void *data ) if (ioctl (fd, SNDCTL_DSP_GETPLAYVOL, &level) < 0)
{ return -1;
oss_thread_ctx_t *p_ctx = data;
if( p_ctx->p_buffer ) sys->mute = !level;
block_Release( p_ctx->p_buffer ); if (level) /* try to keep last volume before mute */
else sys->level = level;
free( p_ctx->p_bytes ); aout_MuteReport (aout, !level);
aout_VolumeReport (aout, (float)(level & 0xFF) / 100.f);
return 0;
} }
/***************************************************************************** static int VolumeSet (audio_output_t *aout, float vol)
* OSSThread: asynchronous thread used to DMA the data to the device
*****************************************************************************/
static void* OSSThread( void *obj )
{ {
audio_output_t * p_aout = (audio_output_t*)obj; aout_sys_t *sys = aout->sys;
struct aout_sys_t * p_sys = p_aout->sys; int fd = sys->fd;
mtime_t next_date = 0;
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; msg_Err (aout, "cannot set volume: %m");
return -1;
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,
};
vlc_cleanup_push( OSSThreadCleanup, &ctx ); sys->level = level;
vlc_restorecancel( canc ); 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 ) int level = mute ? 0 : (sys->level | (sys->level << 8));
{ if (ioctl (fd, SNDCTL_DSP_SETPLAYVOL, &level) < 0)
msg_Err( p_aout, "write failed (%m)" ); {
} msg_Err (aout, "cannot mute: %m");
vlc_cleanup_run(); 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