]> git.sesse.net Git - vlc/blobdiff - modules/audio_output/pulse.c
Use var_Inherit* instead of var_CreateGet*.
[vlc] / modules / audio_output / pulse.c
index 1222211cf0b6e1b7c4aadb233d62ab09a4d15a03..4a239640a6ce841c201f26baaf706ef38878b4f4 100644 (file)
 # 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
@@ -55,8 +63,6 @@ struct aout_sys_t
     mtime_t start_date;
 };
 
-#define    PULSE_CLIENT_NAME N_("VLC media player")
-
 #if 0
 #define PULSE_DEBUG( ...) \
     msg_Dbg( p_aout, __VA_ARGS__ )
@@ -73,6 +79,7 @@ if (!p_sys->context || pa_context_get_state(p_sys->context) != PA_CONTEXT_READY
         goto label; \
     }  \
 } while(0);
+
 /*****************************************************************************
  * Local prototypes
  *****************************************************************************/
@@ -86,19 +93,19 @@ static void stream_request_cb(pa_stream *s, size_t length, void *userdata);
 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( _("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
@@ -111,49 +118,105 @@ static int Open ( vlc_object_t *p_this )
     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;
     }
@@ -170,7 +233,7 @@ static int Open ( vlc_object_t *p_this )
     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;
@@ -182,7 +245,7 @@ static int Open ( vlc_object_t *p_this )
     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;
     }
 
@@ -197,7 +260,7 @@ static int Open ( vlc_object_t *p_this )
     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;
     }
@@ -246,7 +309,7 @@ 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;
 }
@@ -258,21 +321,15 @@ static void Play( aout_instance_t * p_aout )
 {
     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);
     }
 }
 
@@ -287,20 +344,20 @@ static void Close ( vlc_object_t *p_this )
     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);
     }
@@ -382,6 +439,7 @@ static void stream_state_cb(pa_stream *s, void * userdata) {
 }
 
 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;
@@ -405,25 +463,20 @@ static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {
                 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
@@ -440,6 +493,7 @@ static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {
 }
 
 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;
 
@@ -452,6 +506,7 @@ static void stream_latency_update_cb(pa_stream *s, void *userdata) {
 
 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;