[MPlayer-dev-eng] [PATCH] replace polyp- by pulseaudio ao

Reimar Döffinger Reimar.Doeffinger at stud.uni-karlsruhe.de
Sun Oct 14 14:27:03 CEST 2007


Hello,
polypaudio IMO is ancient enough so it can just be thrown away.
Attached patch would replace it by pulseaudio (though 
maybe it would be better to do this in two steps, any objections
against removing polypaudio right now?).
I still think it is too huge for what it does, but I don't see any more
ways to improve that right now.

Greetings,
Reimar Döffinger
-------------- next part --------------
diff --git a/AUTHORS b/AUTHORS
index f9ca6aa..f24147f 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -637,7 +637,7 @@ Plourde, Nicolas <nicolas.plourde at gmail.com>
     * Darwin VCD/SVCD support
 
 Poettering, Lennart <mzzcynlre at 0pointer.de>
-    * audio driver for the Polypaudio sound server
+    * audio driver for the PulseAudio sound server
 
 Poirier, Guillaume (poirierg) <poirierg at gmail.com>
     * French documentation translation and synchronization
diff --git a/configure b/configure
index 50c33ff..2bfa434 100755
--- a/configure
+++ b/configure
@@ -391,7 +391,7 @@ Audio output:
   --disable-ossaudio     disable OSS audio output [autodetect]
   --disable-arts         disable aRts audio output [autodetect]
   --disable-esd          disable esd audio output [autodetect]
-  --disable-polyp        disable Polypaudio audio output [autodetect]
+  --disable-pulse        disable Pulseaudio audio output [autodetect]
   --disable-jack         disable JACK audio output [autodetect]
   --disable-openal       disable OpenAL audio output [autodetect]
   --disable-nas          disable NAS audio output [autodetect]
@@ -556,7 +556,7 @@ _rtc=auto
 _ossaudio=auto
 _arts=auto
 _esd=auto
-_polyp=auto
+_pulse=auto
 _jack=auto
 _openal=auto
 _libcdio=auto
@@ -884,8 +884,8 @@ for ac_option do
   --disable-arts)	_arts=no	;;
   --enable-esd)		_esd=yes	;;
   --disable-esd)	_esd=no		;;
-  --enable-polyp)	_polyp=yes	;;
-  --disable-polyp)	_polyp=no		;;
+  --enable-pulse)	_pulse=yes	;;
+  --disable-pulse)	_pulse=no		;;
   --enable-jack)	_jack=yes	;;
   --disable-jack)	_jack=no	;;
   --enable-openal)	_openal=yes	;;
@@ -5096,32 +5096,30 @@ else
   _noaomodules="esd $_noaomodules"
 fi
 
-echocheck "Polyp"
-if test "$_polyp" = auto ; then
-  _polyp=no
-  if $_pkg_config --exists 'polyplib >= 0.6 polyplib-error >= 0.6 polyplib-mainloop >= 0.6' ; then
+echocheck "pulse"
+if test "$_pulse" = auto ; then
+  _pulse=no
+  if $_pkg_config --exists 'libpulse >= 0.9' ; then
 
 cat > $TMPC << EOF
-#include <polyp/polyplib.h>
-#include <polyp/mainloop.h>
-#include <polyp/polyplib-error.h>
+#include <pulse/pulseaudio.h>
 int main(void) { return 0; }
 EOF
-cc_check `$_pkg_config --libs --cflags polyplib polyplib-error polyplib-mainloop` && tmp_run && _polyp=yes
+cc_check `$_pkg_config --libs --cflags libpulse` && tmp_run && _pulse=yes
 
   fi
 fi
-echores "$_polyp"
+echores "$_pulse"
 
-if test "$_polyp" = yes ; then
-  _def_polyp='#define USE_POLYP 1'
-  _aosrc="$_aosrc ao_polyp.c"
-  _aomodules="polyp $_aomodules"
-  _libs_mplayer="$_libs_mplayer `$_pkg_config --libs polyplib polyplib-error polyplib-mainloop`"
-  _inc_extra="$_inc_extra `$_pkg_config --cflags polyplib polyplib-error polyplib-mainloop`"
+if test "$_pulse" = yes ; then
+  _def_pulse='#define USE_PULSE 1'
+  _aosrc="$_aosrc ao_pulse.c"
+  _aomodules="pulse $_aomodules"
+  _libs_mplayer="$_libs_mplayer `$_pkg_config --libs libpulse`"
+  _inc_extra="$_inc_extra `$_pkg_config --cflags libpulse`"
 else
-  _def_polyp='#undef USE_POLYP'
-  _noaomodules="polyp $_noaomodules"
+  _def_pulse='#undef USE_PULSE'
+  _noaomodules="pulse $_noaomodules"
 fi
 
 
@@ -8170,7 +8168,7 @@ $_def_alsa1x
 $_def_arts
 $_def_esd
 $_def_esd_latency
-$_def_polyp
+$_def_pulse
 $_def_jack
 $_def_openal
 $_def_openal_h
diff --git a/libao2/ao_polyp.c b/libao2/ao_polyp.c
deleted file mode 100644
index 2223976..0000000
--- a/libao2/ao_polyp.c
+++ /dev/null
@@ -1,324 +0,0 @@
-#include <assert.h>
-#include <string.h>
-
-#include <polyp/polyplib.h>
-#include <polyp/polyplib-error.h>
-#include <polyp/mainloop.h>
-
-#include "config.h"
-#include "audio_out.h"
-#include "audio_out_internal.h"
-#include "libaf/af_format.h"
-#include "mp_msg.h"
-
-#define	POLYP_CLIENT_NAME "MPlayer"
-
-/** General driver info */
-static ao_info_t info = {
-    "Polypaudio audio output",
-    "polyp",
-    "Lennart Poettering",
-    ""
-};
-
-/** The sink to connect to */
-static char *sink = NULL;
-
-/** Polypaudio playback stream object */
-static struct pa_stream *stream = NULL;
-
-/** Polypaudio connection context */
-static struct pa_context *context = NULL;
-
-/** Main event loop object */
-static struct pa_mainloop *mainloop = NULL;
-
-/** Some special libao macro magic */
-LIBAO_EXTERN(polyp)
-
-/** Wait until no further actions are pending on the connection context */
-static void wait_for_completion(void) {
-    assert(context && mainloop);
-
-    while (pa_context_is_pending(context))
-        pa_mainloop_iterate(mainloop, 1, NULL);
-}
-
-/** Make sure that the connection context doesn't starve to death */
-static void keep_alive(void) {
-    assert(context && mainloop);
-
-    while (pa_mainloop_iterate(mainloop, 0, NULL) > 0);
-}
-
-/** Wait until the specified operation completes */
-static void wait_for_operation(struct pa_operation *o) {
-    assert(o && context && mainloop);
-
-    while (pa_operation_get_state(o) == PA_OPERATION_RUNNING)
-        pa_mainloop_iterate(mainloop, 1, NULL);
-
-    pa_operation_unref(o);
-}
-
-/** libao initialization function, arguments are sampling frequency,
- * number of channels, sample type and some flags */
-static int init(int rate_hz, int channels, int format, int flags) {
-    struct pa_sample_spec ss;
-    struct pa_buffer_attr a;
-    char hn[128];
-    char *host = NULL;
-
-    assert(!context && !stream && !mainloop);
-
-    if (ao_subdevice) {
-        int i = strcspn(ao_subdevice, ":");
-        if (i >= sizeof(hn))
-            i = sizeof(hn)-1;
-
-        if (i > 0) {
-            strncpy(host = hn, ao_subdevice, i);
-            hn[i] = 0;
-        }
-
-        if (ao_subdevice[i] == ':')
-            sink = ao_subdevice+i+1;
-    }
-
-    mp_msg(MSGT_AO, MSGL_ERR, "AO: [polyp] -%s-%s-\n", host, sink);
-
-    
-    ss.channels = channels;
-    ss.rate = rate_hz;
-
-    switch (format) {
-        case AF_FORMAT_U8:
-            ss.format = PA_SAMPLE_U8;
-            break;
-        case AF_FORMAT_S16_LE:
-            ss.format = PA_SAMPLE_S16LE;
-            break;
-        case AF_FORMAT_S16_BE:
-            ss.format = PA_SAMPLE_S16BE;
-            break;
-        case AF_FORMAT_FLOAT_NE:
-            ss.format = PA_SAMPLE_FLOAT32;
-            break;
-        default:
-            mp_msg(MSGT_AO, MSGL_ERR, "AO: [polyp] Unsupported sample spec\n");
-            goto fail;
-    }
-
-
-    if (!pa_sample_spec_valid(&ss)) {
-        mp_msg(MSGT_AO, MSGL_ERR, "AO: [polyp] Invalid sample spec\n");
-        goto fail;
-    }
-        
-
-    mainloop = pa_mainloop_new();
-    assert(mainloop);
-
-    context = pa_context_new(pa_mainloop_get_api(mainloop), POLYP_CLIENT_NAME);
-    assert(context);
-
-    pa_context_connect(context, host, 1, NULL);
-
-    wait_for_completion();
-
-    if (pa_context_get_state(context) != PA_CONTEXT_READY) {
-        mp_msg(MSGT_AO, MSGL_ERR, "AO: [polyp] Failed to connect to server: %s\n", pa_strerror(pa_context_errno(context)));
-        goto fail;
-    }
-
-    stream = pa_stream_new(context, "audio stream", &ss);
-    assert(stream);
-
-    a.maxlength = pa_bytes_per_second(&ss)*1;
-    a.tlength = a.maxlength*9/10;
-    a.prebuf = a.tlength/2;
-    a.minreq = a.tlength/10;
-    
-    pa_stream_connect_playback(stream, sink, &a, PA_STREAM_INTERPOLATE_LATENCY, PA_VOLUME_NORM);
-
-    wait_for_completion();
-
-    if (pa_stream_get_state(stream) != PA_STREAM_READY) {
-        mp_msg(MSGT_AO, MSGL_ERR, "AO: [polyp] Failed to connect to server: %s\n", pa_strerror(pa_context_errno(context)));
-        goto fail;
-    }
-    
-    return 1;
-
-fail:
-    uninit(1);
-    return 0;
-}
-
-/** Destroy libao driver */
-static void uninit(int immed) {
-    if (stream) {
-        if (!immed && pa_stream_get_state(stream) == PA_STREAM_READY)
-                wait_for_operation(pa_stream_drain(stream, NULL, NULL));
-        
-        pa_stream_unref(stream);
-        stream = NULL;
-    }
-
-    if (context) {
-        pa_context_unref(context);
-        context = NULL;
-    }
-
-    if (mainloop) {
-        pa_mainloop_free(mainloop);
-        mainloop = NULL;
-    }
-}
-
-/** Play the specified data to the polypaudio server */
-static int play(void* data, int len, int flags) {
-    assert(stream && context);
-
-    if (pa_stream_get_state(stream) != PA_STREAM_READY)
-        return -1;
-
-    if (!len)
-        wait_for_operation(pa_stream_trigger(stream, NULL, NULL));
-    else
-        pa_stream_write(stream, data, len, NULL, 0);
-
-    wait_for_completion();
-
-    if (pa_stream_get_state(stream) != PA_STREAM_READY)
-        return -1;
-
-    return len;
-}
-
-/** Pause the audio stream by corking it on the server */
-static void audio_pause(void) {
-    assert(stream && context && pa_stream_get_state(stream) == PA_STREAM_READY);
-    wait_for_operation(pa_stream_cork(stream, 1, NULL, NULL));
-}
-
-/** Resume the audio stream by uncorking it on the server */
-static void audio_resume(void) {
-    assert(stream && context && pa_stream_get_state(stream) == PA_STREAM_READY);
-    wait_for_operation(pa_stream_cork(stream, 0, NULL, NULL));
-}
-
-/** Reset the audio stream, i.e. flush the playback buffer on the server side */
-static void reset(void) {
-    assert(stream && context && pa_stream_get_state(stream) == PA_STREAM_READY);
-    wait_for_operation(pa_stream_flush(stream, NULL, NULL));
-}
-
-/** Return number of bytes that may be written to the server without blocking */
-static int get_space(void) {
-    uint32_t l;
-    assert(stream && context && pa_stream_get_state(stream) == PA_STREAM_READY);
-    
-    keep_alive();
-
-    l = pa_stream_writable_size(stream);
-    
-    return l;
-}
-
-/* A temporary latency variable */
-/* static pa_usec_t latency = 0; */
-
-/* static void latency_func(struct pa_stream *s, const struct pa_latency_info *l, void *userdata) { */
-/*     int negative = 0; */
-    
-/*     if (!l) { */
-/*         mp_msg(MSGT_AO, MSGL_ERR, "AO: [polyp] Invalid sample spec: %s\n", pa_strerror(pa_context_errno(context))); */
-/*         return; */
-/*     } */
-
-/*     latency = pa_stream_get_latency(s, l, &negative); */
-
-/*     /\* Nor really required *\/ */
-/*     if (negative) */
-/*         latency = 0; */
-/* } */
-
-/** Return the current latency in seconds */
-static float get_delay(void) {
-    pa_usec_t latency;
-    assert(stream && context && pa_stream_get_state(stream) == PA_STREAM_READY);
-
-    /*     latency = 0; */
-/*     wait_for_operation(pa_stream_get_latency(stream, latency_func, NULL)); */
-    /*     pa_operation_unref(pa_stream_get_latency(stream, latency_func, NULL)); */
-
-    latency = pa_stream_get_interpolated_latency(stream, NULL);
-    
-    return (float) latency/1000000;
-}
-
-/** A temporary variable to store the current volume */
-static pa_volume_t volume = PA_VOLUME_NORM;
-
-/** A callback function that is called when the
- * pa_context_get_sink_input_info() operation completes. Saves the
- * volume field of the specified structure to the global variable volume. */
-static void info_func(struct pa_context *c, const struct pa_sink_input_info *i, int is_last, void *userdata) {
-    if (is_last < 0) {
-        mp_msg(MSGT_AO, MSGL_ERR, "AO: [polyp] Failed to get sink input info: %s\n", pa_strerror(pa_context_errno(context)));
-        return;
-    }
-
-    if (!i)
-        return;
-
-    volume = i->volume;
-}
-
-/** Issue special libao controls on the device */
-static int control(int cmd, void *arg) {
-    
-    if (!context || !stream)
-        return CONTROL_ERROR;
-    
-    switch (cmd) {
-
-        case AOCONTROL_SET_DEVICE:
-            /* Change the playback device */
-            sink = (char*)arg;
-            return CONTROL_OK;
-
-        case AOCONTROL_GET_DEVICE:
-            /* Return the playback device */
-            *(char**)arg = sink;
-            return CONTROL_OK;
-        
-        case AOCONTROL_GET_VOLUME: {
-            /* Return the current volume of the playback stream */
-            ao_control_vol_t *vol = (ao_control_vol_t*) arg;
-                
-            volume = PA_VOLUME_NORM;
-            wait_for_operation(pa_context_get_sink_input_info(context, pa_stream_get_index(stream), info_func, NULL));
-            vol->left = vol->right = (int) (pa_volume_to_user(volume)*100);
-            return CONTROL_OK;
-        }
-            
-        case AOCONTROL_SET_VOLUME: {
-            /* Set the playback volume of the stream */
-            const ao_control_vol_t *vol = (ao_control_vol_t*) arg;
-            int v = vol->left;
-            if (vol->right > v)
-                v = vol->left;
-            
-            wait_for_operation(pa_context_set_sink_input_volume(context, pa_stream_get_index(stream), pa_volume_from_user((double)v/100), NULL, NULL));
-            
-            return CONTROL_OK;
-        }
-            
-        default:
-            /* Unknown CONTROL command */
-            return CONTROL_UNKNOWN;
-    }
-}
-
diff --git a/libao2/ao_pulse.c b/libao2/ao_pulse.c
new file mode 100644
index 0000000..805583d
--- /dev/null
+++ b/libao2/ao_pulse.c
@@ -0,0 +1,406 @@
+#include <string.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "config.h"
+#include "libaf/af_format.h"
+#include "mp_msg.h"
+#include "audio_out.h"
+#include "audio_out_internal.h"
+
+#define PULSE_CLIENT_NAME "MPlayer"
+
+/** General driver info */
+static ao_info_t info = {
+    "PulseAudio audio output",
+    "pulse",
+    "Lennart Poettering",
+    ""
+};
+
+/** The sink to connect to */
+static char *sink;
+
+/** PulseAudio playback stream object */
+static struct pa_stream *stream;
+
+/** PulseAudio connection context */
+static struct pa_context *context;
+
+/** Main event loop object */
+static struct pa_threaded_mainloop *mainloop;
+
+/** A temporary variable to store the current volume */
+static pa_cvolume volume;
+
+/** Some special libao macro magic */
+LIBAO_EXTERN(pulse)
+
+#define GENERIC_ERR_MSG(ctx, str) \
+    mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] "str": %s\n", \
+    pa_strerror(pa_context_errno(ctx)))
+
+static void context_state_cb(pa_context *c, void *userdata) {
+    switch (pa_context_get_state(c)) {
+        case PA_CONTEXT_READY:
+        case PA_CONTEXT_TERMINATED:
+        case PA_CONTEXT_FAILED:
+            pa_threaded_mainloop_signal(mainloop, 0);
+            break;
+    }
+}
+
+static void stream_state_cb(pa_stream *s, void *userdata) {
+    switch (pa_stream_get_state(s)) {
+        case PA_STREAM_READY:
+        case PA_STREAM_FAILED:
+        case PA_STREAM_TERMINATED:
+            pa_threaded_mainloop_signal(mainloop, 0);
+            break;
+    }
+}
+
+static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {
+    pa_threaded_mainloop_signal(mainloop, 0);
+}
+
+static void stream_latency_update_cb(pa_stream *s, void *userdata) {
+    pa_threaded_mainloop_signal(mainloop, 0);
+}
+
+static void success_cb(pa_stream *s, int success, void *userdata) {
+    if (userdata)
+        *(int *)userdata = success;
+    pa_threaded_mainloop_signal(mainloop, 0);
+}
+
+static int waitop(pa_operation *op) {
+    pa_operation_state_t state;
+    if (!op) return 0;
+    state = pa_operation_get_state(op);
+    while (state == PA_OPERATION_RUNNING) {
+        pa_threaded_mainloop_wait(mainloop);
+        state = pa_operation_get_state(op);
+    };
+    pa_operation_unref(op);
+    pa_threaded_mainloop_unlock(mainloop);
+    return state == PA_OPERATION_DONE;
+}
+
+static const struct format_map_s {
+    int mp_format;
+    pa_sample_format_t pa_format;
+} format_maps[] = {
+    {AF_FORMAT_U8, PA_SAMPLE_U8},
+    {AF_FORMAT_S16_LE, PA_SAMPLE_S16LE},
+    {AF_FORMAT_S16_BE, PA_SAMPLE_S16BE},
+    {AF_FORMAT_FLOAT_LE, PA_SAMPLE_FLOAT32LE},
+    {AF_FORMAT_FLOAT_BE, PA_SAMPLE_FLOAT32BE},
+    {AF_FORMAT_MU_LAW, PA_SAMPLE_ULAW},
+    {AF_FORMAT_A_LAW, PA_SAMPLE_ALAW},
+    {AF_FORMAT_UNKNOWN, 0}
+};
+
+static int init(int rate_hz, int channels, int format, int flags) {
+    struct pa_sample_spec ss;
+    struct pa_channel_map map;
+    const struct format_map_s *fmt_map;
+    char *host = NULL;
+
+    if (ao_subdevice) {
+        int i = strcspn(ao_subdevice, ":");
+        host = strdup(ao_subdevice);
+        if (host[i] == ':')
+            sink = &host[i + 1];
+        host[i] = 0;
+    }
+
+    ss.channels = channels;
+    ss.rate = rate_hz;
+
+    ao_data.samplerate = rate_hz;
+    ao_data.format = format;
+    ao_data.channels = channels;
+
+    fmt_map = format_maps;
+    while (fmt_map->mp_format != format) {
+        if (fmt_map->mp_format == AF_FORMAT_UNKNOWN) {
+            mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Unsupported sample spec\n");
+            goto fail;
+        }
+        fmt_map++;
+    }
+    ss.format = fmt_map->pa_format;
+
+    if (!pa_sample_spec_valid(&ss)) {
+        mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Invalid sample spec\n");
+        goto fail;
+    }
+
+    pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_ALSA);
+    ao_data.bps = pa_bytes_per_second(&ss);
+
+    pa_cvolume_reset(&volume, ss.channels);
+
+    if (!(mainloop = pa_threaded_mainloop_new())) {
+        mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to allocate main loop\n");
+        goto fail;
+    }
+
+    if (!(context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), PULSE_CLIENT_NAME))) {
+        mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to allocate context\n");
+        goto fail;
+    }
+
+    pa_context_set_state_callback(context, context_state_cb, NULL);
+
+    if (pa_context_connect(context, host, 0, NULL) < 0)
+        goto fail;
+
+    pa_threaded_mainloop_lock(mainloop);
+
+    if (pa_threaded_mainloop_start(mainloop) < 0)
+        goto unlock_and_fail;
+
+    /* Wait until the context is ready */
+    pa_threaded_mainloop_wait(mainloop);
+
+    if (pa_context_get_state(context) != PA_CONTEXT_READY)
+        goto unlock_and_fail;
+
+    if (!(stream = pa_stream_new(context, "audio stream", &ss, &map)))
+        goto unlock_and_fail;
+
+    pa_stream_set_state_callback(stream, stream_state_cb, NULL);
+    pa_stream_set_write_callback(stream, stream_request_cb, NULL);
+    pa_stream_set_latency_update_callback(stream, stream_latency_update_cb, NULL);
+
+    if (pa_stream_connect_playback(stream, sink, NULL, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, &volume, NULL) < 0)
+        goto unlock_and_fail;
+
+    /* Wait until the stream is ready */
+    pa_threaded_mainloop_wait(mainloop);
+
+    if (pa_stream_get_state(stream) != PA_STREAM_READY)
+        goto unlock_and_fail;
+
+    pa_threaded_mainloop_unlock(mainloop);
+
+    free(host);
+    return 1;
+
+unlock_and_fail:
+
+    if (mainloop)
+        pa_threaded_mainloop_unlock(mainloop);
+
+fail:
+    if (context)
+        GENERIC_ERR_MSG(context, "Init failed");
+    free(host);
+    uninit(1);
+    return 0;
+}
+
+/** Destroy libao driver */
+static void uninit(int immed) {
+    if (stream && !immed) {
+            pa_operation *o;
+
+            pa_threaded_mainloop_lock(mainloop);
+
+            o = pa_stream_drain(stream, success_cb, NULL);
+            waitop(o);
+    }
+
+    if (mainloop)
+        pa_threaded_mainloop_stop(mainloop);
+
+    if (stream) {
+        pa_stream_disconnect(stream);
+        pa_stream_unref(stream);
+        stream = NULL;
+    }
+
+    if (context) {
+        pa_context_disconnect(context);
+        pa_context_unref(context);
+        context = NULL;
+    }
+
+    if (mainloop) {
+        pa_threaded_mainloop_free(mainloop);
+        mainloop = NULL;
+    }
+}
+
+/** Play the specified data to the pulseaudio server */
+static int play(void* data, int len, int flags) {
+    int r = -1;
+    pa_operation *o = NULL;
+
+    pa_threaded_mainloop_lock(mainloop);
+
+    if (len) {
+
+        if (pa_stream_write(stream, data, len, NULL, 0, PA_SEEK_RELATIVE) < 0) {
+            GENERIC_ERR_MSG(context, "pa_stream_write() failed");
+            goto fail;
+        }
+
+    } else {
+
+        if (!(o = pa_stream_trigger(stream, NULL, NULL))) {
+            GENERIC_ERR_MSG(context, "pa_stream_trigger() failed");
+            goto fail;
+        }
+
+        /* We don't wait for this operation to complete */
+    }
+
+    r = len;
+
+fail:
+    if (o)
+        pa_operation_unref(o);
+
+    pa_threaded_mainloop_unlock(mainloop);
+
+    return r;
+}
+
+static void cork(int b) {
+    pa_operation *o;
+    int success = 0;
+
+    pa_threaded_mainloop_lock(mainloop);
+
+    o = pa_stream_cork(stream, b, success_cb, &success);
+    if (!waitop(o) || !success)
+        GENERIC_ERR_MSG(context, "pa_stream_cork() failed");
+}
+
+/** Pause the audio stream by corking it on the server */
+static void audio_pause(void) {
+    cork(1);
+ }
+
+/** Resume the audio stream by uncorking it on the server */
+static void audio_resume(void) {
+    cork(0);
+}
+
+/** Reset the audio stream, i.e. flush the playback buffer on the server side */
+static void reset(void) {
+    pa_operation *o;
+    int success = 0;
+
+    pa_threaded_mainloop_lock(mainloop);
+
+    o = pa_stream_flush(stream, success_cb, &success);
+    if (!waitop(o) || !success)
+        GENERIC_ERR_MSG(context, "pa_stream_flush() failed");
+}
+
+/** Return number of bytes that may be written to the server without blocking */
+static int get_space(void) {
+    size_t l;
+
+    pa_threaded_mainloop_lock(mainloop);
+    l = pa_stream_writable_size(stream);
+    pa_threaded_mainloop_unlock(mainloop);
+
+    return l == (size_t) -1 ? -1 : l;
+}
+
+/** Return the current latency in seconds */
+static float get_delay(void) {
+    pa_usec_t latency = (pa_usec_t) -1;
+
+    pa_threaded_mainloop_lock(mainloop);
+
+    while (pa_stream_get_latency(stream, &latency, NULL) < 0) {
+        if (pa_context_errno(context) != PA_ERR_NODATA) {
+            GENERIC_ERR_MSG(context, "pa_stream_get_latency() failed");
+            break;
+        }
+
+        /* Wait until latency data is available again */
+        pa_threaded_mainloop_wait(mainloop);
+    }
+
+    pa_threaded_mainloop_unlock(mainloop);
+
+    return latency == (pa_usec_t) -1 ? 0 : latency / 1000000.0;
+}
+
+/** A callback function that is called when the
+ * pa_context_get_sink_input_info() operation completes. Saves the
+ * volume field of the specified structure to the global variable volume. */
+static void info_func(struct pa_context *c, const struct pa_sink_input_info *i, int is_last, void *userdata) {
+    if (is_last < 0) {
+        GENERIC_ERR_MSG(context, "Failed to get sink input info");
+        return;
+    }
+
+    if (!i)
+        return;
+
+    volume = i->volume;
+
+    pa_threaded_mainloop_signal(mainloop, 0);
+}
+
+/** Issue special libao controls on the device */
+static int control(int cmd, void *arg) {
+    switch (cmd) {
+        case AOCONTROL_GET_VOLUME: {
+            /* Return the current volume of the playback stream */
+            ao_control_vol_t *vol = arg;
+            pa_operation *o;
+            pa_threaded_mainloop_lock(mainloop);
+            o = pa_context_get_sink_input_info(context, pa_stream_get_index(stream), info_func, NULL);
+            if (!waitop(o)) {
+                GENERIC_ERR_MSG(context, "pa_stream_get_sink_input_info() failed");
+                return CONTROL_ERROR;
+            }
+
+            if (volume.channels != 2)
+                vol->left = vol->right = pa_cvolume_avg(&volume)*100/PA_VOLUME_NORM;
+            else {
+                vol->left = volume.values[0]*100/PA_VOLUME_NORM;
+                vol->right = volume.values[1]*100/PA_VOLUME_NORM;
+            }
+
+            return CONTROL_OK;
+        }
+
+        case AOCONTROL_SET_VOLUME: {
+            /* Set the playback volume of the stream */
+            const ao_control_vol_t *vol = arg;
+            pa_operation *o;
+
+            if (volume.channels != 2)
+                pa_cvolume_set(&volume, volume.channels, (pa_volume_t)vol->left*PA_VOLUME_NORM/100);
+            else {
+                volume.values[0] = (pa_volume_t)vol->left*PA_VOLUME_NORM/100;
+                volume.values[1] = (pa_volume_t)vol->right*PA_VOLUME_NORM/100;
+            }
+
+            if (!(o = pa_context_set_sink_input_volume(context, pa_stream_get_index(stream), &volume, NULL, NULL))) {
+                GENERIC_ERR_MSG(context, "pa_context_set_sink_input_volume() failed");
+                return CONTROL_ERROR;
+            }
+
+            pa_operation_unref(o);
+
+            /* We don't wait for completion here */
+
+            return CONTROL_OK;
+        }
+
+        default:
+            /* Unknown CONTROL command */
+            return CONTROL_UNKNOWN;
+    }
+}
diff --git a/libao2/audio_out.c b/libao2/audio_out.c
index 4cf0072..2cc3d4d 100644
--- a/libao2/audio_out.c
+++ b/libao2/audio_out.c
@@ -25,8 +25,8 @@ extern ao_functions_t audio_out_arts;
 #ifdef USE_ESD
 extern ao_functions_t audio_out_esd;
 #endif
-#ifdef USE_POLYP
-extern ao_functions_t audio_out_polyp;
+#ifdef USE_PULSE
+extern ao_functions_t audio_out_pulse;
 #endif
 #ifdef USE_JACK
 extern ao_functions_t audio_out_jack;
@@ -112,8 +112,8 @@ ao_functions_t* audio_out_drivers[] =
 #ifdef USE_ESD
         &audio_out_esd,
 #endif
-#ifdef USE_POLYP
-        &audio_out_polyp,
+#ifdef USE_PULSE
+        &audio_out_pulse,
 #endif
 #ifdef USE_JACK
         &audio_out_jack,


More information about the MPlayer-dev-eng mailing list