# include "config.h"
#endif
-#include <vlc/vlc.h>
+#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_aout.h>
+#include <vlc_cpu.h>
#include <pulse/pulseaudio.h>
+#ifdef X_DISPLAY_MISSING
+# error Xlib required due to PulseAudio bug 799!
+#endif
+#include <vlc_xlib.h>
+
+#include <assert.h>
/*****************************************************************************
* aout_sys_t: Pulseaudio output method descriptor
mtime_t start_date;
};
-#define PULSE_CLIENT_NAME N_("VLC media player")
-
#if 0
#define PULSE_DEBUG( ...) \
msg_Dbg( p_aout, __VA_ARGS__ )
goto label; \
} \
} while(0);
+
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static void stream_latency_update_cb(pa_stream *s, void *userdata);
static void success_cb(pa_stream *s, int sucess, void *userdata);
static void uninit(aout_instance_t *p_aout);
+
/*****************************************************************************
* Module descriptor
*****************************************************************************/
-vlc_module_begin();
- set_shortname( "Pulse Audio" );
- set_description( N_("Pulseaudio audio output") );
- set_capability( "audio output", 40 );
- set_category( CAT_AUDIO );
- set_subcategory( SUBCAT_AUDIO_AOUT );
- add_shortcut( "pulseaudio" );
- add_shortcut( "pa" );
- set_callbacks( Open, Close );
-vlc_module_end();
+vlc_module_begin ()
+ set_shortname( "PulseAudio" )
+ set_description( N_("Pulseaudio audio output") )
+ set_capability( "audio output", 160 )
+ set_category( CAT_AUDIO )
+ set_subcategory( SUBCAT_AUDIO_AOUT )
+ add_shortcut( "pulseaudio", "pa" )
+ set_callbacks( Open, Close )
+vlc_module_end ()
/*****************************************************************************
* Open: open the audio device
const struct pa_buffer_attr *buffer_attr;
struct pa_buffer_attr a;
struct pa_channel_map map;
+ char * p_client_name;
+
+ if( !vlc_xlib_init( p_this ) )
+ return VLC_EGENERIC;
/* Allocate structures */
- p_aout->output.p_sys = p_sys = malloc( sizeof( aout_sys_t ) );
+ p_aout->output.p_sys = p_sys = calloc( 1, sizeof( aout_sys_t ) );
if( p_sys == NULL )
return VLC_ENOMEM;
- memset( p_sys, 0, sizeof( aout_sys_t ) );
PULSE_DEBUG( "Pulse start initialization");
- ss.rate = p_aout->output.output.i_rate;
- ss.channels = 2;
+ ss.channels = aout_FormatNbChannels( &p_aout->output.output ); /* Get the input stream channel count */
+
+ /* Setup the pulse audio stream based on the input stream count */
+ switch(ss.channels)
+ {
+ case 8:
+ p_aout->output.output.i_physical_channels
+ = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
+ | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT
+ | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
+ | AOUT_CHAN_LFE;
+ break;
+ case 6:
+ 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;
+ break;
- ss.format = PA_SAMPLE_S16LE;
- p_aout->output.output.i_physical_channels =
- AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
- p_aout->output.output.i_format = AOUT_FMT_S16_NE;
+ case 4:
+ p_aout->output.output.i_physical_channels
+ = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
+ | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
+ break;
+
+ case 2:
+ p_aout->output.output.i_physical_channels
+ = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
+ break;
+
+ case 1:
+ p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
+ break;
+
+ default:
+ msg_Err(p_aout,"Invalid number of channels");
+ goto fail;
+ }
+
+ /* Add a quick command line info message */
+ msg_Dbg(p_aout, "%d audio channels", ss.channels);
+
+ ss.rate = p_aout->output.output.i_rate;
+ if (HAVE_FPU)
+ {
+ ss.format = PA_SAMPLE_FLOAT32NE;
+ p_aout->output.output.i_format = VLC_CODEC_FL32;
+ }
+ else
+ {
+ ss.format = PA_SAMPLE_S16NE;
+ p_aout->output.output.i_format = VLC_CODEC_S16N;
+ }
if (!pa_sample_spec_valid(&ss)) {
msg_Err(p_aout,"Invalid sample spec");
goto fail;
}
-
- a.maxlength = pa_bytes_per_second(&ss)/4/pa_frame_size(&ss);
- a.tlength = a.maxlength*9/10;
- a.prebuf = a.tlength/2;
- a.minreq = a.tlength/10;
- a.maxlength *= pa_frame_size(&ss);
- a.tlength *= pa_frame_size(&ss);
- a.prebuf *= pa_frame_size(&ss);
- a.minreq *= pa_frame_size(&ss);
+ /* Reduce overall latency to 200mS to reduce audible clicks
+ * Also pulse minreq and internal buffers are now 20mS which reduces resampling
+ */
+ a.tlength = pa_bytes_per_second(&ss)/5;
+ a.maxlength = a.tlength * 2;
+ a.prebuf = a.tlength / 2;
+ a.minreq = a.tlength / 10;
+ /* Buffer size is 20mS */
p_sys->buffer_size = a.minreq;
- pa_channel_map_init_stereo(&map);
-
+ /* Initialise the speaker map setup above */
+ pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_ALSA);
if (!(p_sys->mainloop = pa_threaded_mainloop_new())) {
msg_Err(p_aout, "Failed to allocate main loop");
goto fail;
}
- if (!(p_sys->context = pa_context_new(pa_threaded_mainloop_get_api(p_sys->mainloop), _( PULSE_CLIENT_NAME )))) {
+ if ((p_client_name = var_InheritString(p_aout, "user-agent")) == NULL) {
+ msg_Err(p_aout, "No user-agent string available.");
+ goto fail;
+ }
+
+ p_sys->context = pa_context_new(pa_threaded_mainloop_get_api(p_sys->mainloop), p_client_name);
+ free(p_client_name);
+ if(!p_sys->context)
+ {
msg_Err(p_aout, "Failed to allocate context");
goto fail;
}
PULSE_DEBUG( "Pulse after context connect");
pa_threaded_mainloop_lock(p_sys->mainloop);
-
+
if (pa_threaded_mainloop_start(p_sys->mainloop) < 0) {
msg_Err(p_aout, "Failed to start main loop");
goto unlock_and_fail;
pa_threaded_mainloop_wait(p_sys->mainloop);
if (pa_context_get_state(p_sys->context) != PA_CONTEXT_READY) {
- msg_Err(p_aout, "Failed to connect to server: %s", pa_strerror(pa_context_errno(p_sys->context)));
+ msg_Dbg(p_aout, "Failed to connect to server: %s", pa_strerror(pa_context_errno(p_sys->context)));
goto unlock_and_fail;
}
pa_stream_set_write_callback(p_sys->stream, stream_request_cb, p_aout);
pa_stream_set_latency_update_callback(p_sys->stream, stream_latency_update_cb, p_aout);
- if (pa_stream_connect_playback(p_sys->stream, NULL, &a, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL) < 0) {
+ if (pa_stream_connect_playback(p_sys->stream, NULL, &a, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_ADJUST_LATENCY, NULL, NULL) < 0) {
msg_Err(p_aout, "Failed to connect stream: %s", pa_strerror(pa_context_errno(p_sys->context)));
goto unlock_and_fail;
}
if (p_sys->mainloop)
pa_threaded_mainloop_unlock(p_sys->mainloop);
fail:
- msg_Err(p_aout, "Pulse initialization failed");
+ msg_Dbg(p_aout, "Pulse initialization failed");
uninit(p_aout);
return VLC_EGENERIC;
}
{
struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
- pa_operation *o;
-
if(!p_sys->started){
msg_Dbg(p_aout, "Pulse stream started");
+ pa_threaded_mainloop_lock(p_sys->mainloop);
p_sys->start_date =
aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
p_sys->started = 1;
- pa_threaded_mainloop_lock(p_sys->mainloop);
- if((o = pa_stream_flush(p_sys->stream, success_cb, p_aout))){
- pa_operation_unref(o);
- }
- pa_threaded_mainloop_unlock(p_sys->mainloop);
-
pa_threaded_mainloop_signal(p_sys->mainloop, 0);
+ pa_threaded_mainloop_unlock(p_sys->mainloop);
}
}
msg_Dbg(p_aout, "Pulse Close");
if(p_sys->stream){
- pa_operation *o;
pa_threaded_mainloop_lock(p_sys->mainloop);
pa_stream_set_write_callback(p_sys->stream, NULL, NULL);
- if((o = pa_stream_drain(p_sys->stream, success_cb, p_aout))){
- while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
- CHECK_DEAD_GOTO(fail);
- pa_threaded_mainloop_wait(p_sys->mainloop);
- }
+ pa_operation *o;
- fail:
+ o = pa_stream_flush(p_sys->stream, success_cb, p_aout);
+ while( pa_operation_get_state(o) == PA_OPERATION_RUNNING )
+ pa_threaded_mainloop_wait(p_sys->mainloop);
+ pa_operation_unref(o);
- pa_operation_unref(o);
- }
+ o = pa_stream_drain(p_sys->stream, success_cb, p_aout);
+ while( pa_operation_get_state(o) == PA_OPERATION_RUNNING )
+ pa_threaded_mainloop_wait(p_sys->mainloop);
+ pa_operation_unref(o);
pa_threaded_mainloop_unlock(p_sys->mainloop);
}
}
static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {
+ VLC_UNUSED( s );
aout_instance_t *p_aout = (aout_instance_t *)userdata;
struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
mtime_t next_date;
latency = 0;
}
+
PULSE_DEBUG( "Pulse stream request latency=%"PRId64"", latency);
next_date = mdate() + latency;
-
if(p_sys->start_date < next_date + AOUT_PTS_TOLERANCE ){
- /*
- vlc_mutex_lock( &p_aout->output_fifo_lock );
- p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo );
- vlc_mutex_unlock( &p_aout->output_fifo_lock );
- */
p_buffer = aout_OutputNextBuffer( p_aout, next_date, 0);
}
}
if ( p_buffer != NULL )
{
- PULSE_DEBUG( "Pulse stream request write buffer %d", p_buffer->i_nb_bytes);
- pa_stream_write(p_sys->stream, p_buffer->p_buffer, p_buffer->i_nb_bytes, NULL, 0, PA_SEEK_RELATIVE);
- length -= p_buffer->i_nb_bytes;
+ PULSE_DEBUG( "Pulse stream request write buffer %d", p_buffer->i_buffer);
+ pa_stream_write(p_sys->stream, p_buffer->p_buffer, p_buffer->i_buffer, NULL, 0, PA_SEEK_RELATIVE);
+ length -= p_buffer->i_buffer;
aout_BufferFree( p_buffer );
}
else
}
static void stream_latency_update_cb(pa_stream *s, void *userdata) {
+ VLC_UNUSED( s );
aout_instance_t *p_aout = (aout_instance_t *)userdata;
struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;
static void success_cb(pa_stream *s, int sucess, void *userdata)
{
+ VLC_UNUSED( s );
aout_instance_t *p_aout = (aout_instance_t *)userdata;
struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys;