rpms/xine-lib/devel xine-pulse-rework2.patch, NONE, 1.1 xine-lib.spec, 1.37, 1.38

Rex Dieter (rdieter) fedora-extras-commits at redhat.com
Mon Apr 7 13:10:46 UTC 2008


Author: rdieter

Update of /cvs/pkgs/rpms/xine-lib/devel
In directory cvs-int.fedora.redhat.com:/tmp/cvs-serv23470

Modified Files:
	xine-lib.spec 
Added Files:
	xine-pulse-rework2.patch 
Log Message:
* Mon Apr 07 2008 Rex Dieter <rdieter at fedoraproject.org> - 1.1.11.1-2
- pulse-rework2 patch (#439731)
- -pulseaudio subpkg (#439731)


xine-pulse-rework2.patch:

--- NEW FILE xine-pulse-rework2.patch ---
--- xine-lib-1.1.11.1/src/audio_out/audio_pulse_out.c	2008-03-28 15:24:49.000000000 +0100
+++ xine-lib-1.1.11.1.lennart/src/audio_out/audio_pulse_out.c	2008-04-02 22:55:49.000000000 +0200
@@ -1,28 +1,28 @@
-/* 
- * Copyright (C) 2000-2007 the xine project
- * 
+/* -*- Mode: C; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ * Copyright (C) 2000-2008 the xine project
+ *
  * This file is part of xine, a free video player.
- * 
+ *
  * xine is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
- * 
+ *
  * xine is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
  *
- * ao plugin for pulseaudio (rename of polypaudio):
+ * ao plugin for PulseAudio:
  * http://0pointer.de/lennart/projects/pulsaudio/
  *
- * originally written for polypaudio simple api. Lennart then suggested
- * using the async api for better control (such as volume), therefore, a lot
- * of this code comes from Lennart's patch to mplayer.
+ * Diego Petteno, Lennart Poettering
  */
 
 #ifdef HAVE_CONFIG_H
@@ -48,15 +48,9 @@
 
 #define GAP_TOLERANCE        AO_MAX_GAP
 
-/* CHECKME: should this be conditional on autotools? */
-extern const char *__progname;
-
 typedef struct {
   audio_driver_class_t  driver_class;
   xine_t                      *xine;
-
-  struct pa_context           *context;   /*< Pulseaudio connection context */
-  struct pa_threaded_mainloop *mainloop;  /*< Main event loop object */
 } pulse_class_t;
 
 typedef struct pulse_driver_s {
@@ -67,11 +61,13 @@ typedef struct pulse_driver_s {
 
   char             *host;    /*< The host to connect to */
   char             *sink;    /*< The sink to connect to */
-  struct pa_stream *stream;  /*< Pulseaudio playback stream object */
 
-  pthread_mutex_t   info_mutex; /**< Mutex for info callback signaling */
+  pa_threaded_mainloop *mainloop;  /*< Main event loop object */
+  pa_context *context;             /*< Pulseaudio connection context */
+  pa_stream  *stream;              /*< Pulseaudio playback stream object */
 
   pa_volume_t       swvolume;
+  int muted;
   pa_cvolume        cvolume;
 
   int               capabilities;
@@ -82,67 +78,117 @@ typedef struct pulse_driver_s {
   uint32_t          bits_per_sample;
   uint32_t          bytes_per_frame;
 
-  uint32_t          frames_written;
-
 } pulse_driver_t;
 
 
 /**
- * @brief Callback function called when a stream operation succeed
- * @param stream Stream which operation has succeeded
- * @param success The success value for the operation (ignored)
- * @param this_Gen pulse_driver_t pointer for the PulseAudio output
- *        instance.
+ * @brief Callback function called when the state of the context is changed
+ * @param c Context which changed status
+ * @param this_gen pulse_class_t pointer for the PulseAudio output class
  */
-static void __xine_pa_stream_success_callback(pa_stream *const stream, const int success,
-					      void *const mutex_gen)
+static void __xine_pa_context_state_callback(pa_context *c, void *this_gen)
 {
-  pthread_mutex_t *const completion_mutex = (pthread_mutex_t*)mutex_gen;
+  pulse_driver_t * this = (pulse_driver_t*) this_gen;
+
+  switch (pa_context_get_state(c)) {
+
+    case PA_CONTEXT_READY:
+    case PA_CONTEXT_TERMINATED:
+    case PA_CONTEXT_FAILED:
+      pa_threaded_mainloop_signal(this->mainloop, 0);
+      break;
 
-  pthread_mutex_unlock(completion_mutex);
+    case PA_CONTEXT_CONNECTING:
+    case PA_CONTEXT_UNCONNECTED:
+    case PA_CONTEXT_AUTHORIZING:
+    case PA_CONTEXT_SETTING_NAME:
+      break;
+  }
 }
 
 /**
- * @brief Callback function called when the state of the context is changed
- * @param ctx Context which operation has succeeded
+ * @brief Callback function called when the state of the stream is changed
+ * @param s Stream that changed status
  * @param this_gen pulse_driver_t pointer for the PulseAudio output
  *        instance.
  */
-static void __xine_pa_context_status_callback(pa_context *const ctx, void *const this_gen)
+static void __xine_pa_stream_state_callback(pa_stream *s, void *this_gen)
 {
-  pulse_driver_t *const this = (pulse_driver_t*)this_gen;
+  pulse_driver_t * this = (pulse_driver_t*) this_gen;
+
+  switch (pa_stream_get_state(s)) {
+
+    case PA_STREAM_READY:
+    case PA_STREAM_TERMINATED:
+    case PA_STREAM_FAILED:
+      pa_threaded_mainloop_signal(this->mainloop, 0);
+      break;
 
-  switch (pa_context_get_state(ctx)) {
-  case PA_CONTEXT_READY:
-  case PA_CONTEXT_TERMINATED:
-  case PA_CONTEXT_FAILED:
-    pa_threaded_mainloop_signal(this->pa_class->mainloop, 0);
-    break;
-
-  case PA_CONTEXT_CONNECTING:
-  case PA_CONTEXT_UNCONNECTED:
-  case PA_CONTEXT_AUTHORIZING:
-  case PA_CONTEXT_SETTING_NAME:
-    break;
+    case PA_STREAM_UNCONNECTED:
+    case PA_STREAM_CREATING:
+      break;
   }
 }
 
 /**
- * @brief Callback function called when a context operation succeed
+ * @brief Callback function called when PA asks for more audio data.
+ * @param s Stream on which data is requested
+ * @param nbytes the number of bytes PA requested
+ * @param this_gen pulse_driver_t pointer for the PulseAudio output
+ *        instance.
+ */
+static void __xine_pa_stream_request_callback(pa_stream *s, size_t nbytes, void *this_gen)
+{
+  pulse_driver_t * this = (pulse_driver_t*) this_gen;
+
+  pa_threaded_mainloop_signal(this->mainloop, 0);
+}
+
+/**
+ * @brief Callback function called when PA notifies about something
+ * @param s Stream on which the notification happened
+ * @param this_gen pulse_driver_t pointer for the PulseAudio output
+ *        instance.
+ */
+static void __xine_pa_stream_notify_callback(pa_stream *s, void *this_gen)
+{
+  pulse_driver_t * this = (pulse_driver_t*) this_gen;
+
+  pa_threaded_mainloop_signal(this->mainloop, 0);
+}
+
+/**
+ * @brief Callback function called when PA completed an operation
  * @param ctx Context which operation has succeeded
- * @param success The success value for the operation (ignored)
+ * @param nbytes the number of bytes PA requested
  * @param this_gen pulse_driver_t pointer for the PulseAudio output
  *        instance.
  */
-static void __xine_pa_context_success_callback(pa_context *const ctx, const int success,
-					      void *const this_gen)
+static void __xine_pa_stream_success_callback(pa_stream *s, int success, void *this_gen)
 {
-  pulse_driver_t *const this = (pulse_driver_t*)this_gen;
+  pulse_driver_t * this = (pulse_driver_t*) this_gen;
 
-  _x_assert(ctx); _x_assert(this);
-  _x_assert(ctx == this->pa_class->context);
+  if (!success)
+    xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: stream operation failed: %s\n", pa_strerror(pa_context_errno(this->context)));
 
-  pa_threaded_mainloop_signal(this->pa_class->mainloop, 0);
+  pa_threaded_mainloop_signal(this->mainloop, 0);
+}
+
+/**
+ * @brief Callback function called when PA completed an operation
+ * @param c Context on which operation has succeeded
+ * @param nbytes the number of bytes PA requested
+ * @param this_gen pulse_driver_t pointer for the PulseAudio output
+ *        instance.
+ */
+static void __xine_pa_context_success_callback(pa_context *c, int success, void *this_gen)
+{
+  pulse_driver_t *this = (pulse_driver_t*) this_gen;
+
+  if (!success)
+    xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: context operation failed: %s\n", pa_strerror(pa_context_errno(this->context)));
+
+  pa_threaded_mainloop_signal(this->mainloop, 0);
 }
 
 /**
@@ -156,14 +202,14 @@ static void __xine_pa_context_success_ca
  * This function saves the volume field of the passed structure to the
  * @c cvolume variable of the output instance.
  */
-static void __xine_pa_sink_info_callback(pa_context *const ctx, const pa_sink_input_info *const info,
-					 const int is_last, void *const userdata) {
+static void __xine_pa_sink_info_callback(pa_context *c, const pa_sink_input_info *info,
+                                         int is_last, void *userdata) {
 
   pulse_driver_t *const this = (pulse_driver_t *) userdata;
 
   if (is_last < 0) {
     xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: Failed to get sink input info: %s\n",
-             pa_strerror(pa_context_errno(this->pa_class->context)));
+             pa_strerror(pa_context_errno(this->context)));
     return;
   }
 
@@ -171,36 +217,90 @@ static void __xine_pa_sink_info_callback
       return;
 
   this->cvolume = info->volume;
+  this->swvolume = pa_sw_volume_to_linear(pa_cvolume_avg(&info->volume));
+  this->muted = info->mute;
+}
+
+static int connect_context(pulse_driver_t *this) {
+
+  if (this->context && (pa_context_get_state(this->context) == PA_CONTEXT_FAILED ||
+                        pa_context_get_state(this->context) == PA_CONTEXT_TERMINATED)) {
+    pa_context_unref(this->context);
+    this->context = NULL;
+  }
+
+  if (!this->context) {
+    char fn[PATH_MAX], *p;
+
+    if (pa_get_binary_name(fn, sizeof(fn)))
+      p = pa_path_get_filename(fn);
+    else
+      p = "Xine";
 
-  pthread_mutex_unlock(&this->info_mutex);
+    this->context = pa_context_new(pa_threaded_mainloop_get_api(this->mainloop), p);
+    _x_assert(this->context);
+
+    pa_context_set_state_callback(this->context, __xine_pa_context_state_callback, this);
+  }
+
+  if (pa_context_get_state(this->context) == PA_CONTEXT_UNCONNECTED) {
+
+    if (pa_context_connect(this->context, this->host, 0, NULL) < 0) {
+      xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: failed to connect context object %s\n", pa_strerror(pa_context_errno(this->context)));
+      return -1;
+    }
+  }
+
+  for (;;) {
+    pa_context_state_t state = pa_context_get_state(this->context);
+
+    if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED) {
+      xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: failed to connect context object: %s\n", pa_strerror(pa_context_errno(this->context)));
+      return -1;
+    }
+
+    if (state == PA_CONTEXT_READY)
+      break;
+
+    pa_threaded_mainloop_wait(this->mainloop);
+  }
+
+  return 0;
 }
 
 /*
  * open the audio device for writing to
  */
 static int ao_pulse_open(ao_driver_t *this_gen,
-		   uint32_t bits, uint32_t rate, int mode)
+                         uint32_t bits, uint32_t rate, int mode)
 {
   pulse_driver_t *this = (pulse_driver_t *) this_gen;
-  struct pa_sample_spec ss;
-  struct pa_buffer_attr a;
-  pa_stream_state_t streamstate;
+  pa_sample_spec ss;
+  pa_channel_map cm;
+  int r;
 
   xprintf (this->xine, XINE_VERBOSITY_DEBUG,
-	   "audio_pulse_out: ao_open bits=%d rate=%d, mode=%d\n", bits, rate, mode);
+           "audio_pulse_out: ao_open bits=%d rate=%d, mode=%d\n", bits, rate, mode);
 
   if ( (mode & this->capabilities) == 0 ) {
     xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: unsupported mode %08x\n", mode);
     return 0;
   }
 
+  pa_threaded_mainloop_lock(this->mainloop);
+
   if (this->stream) {
 
-    if ( mode == this->mode && rate == this->sample_rate &&
-         bits == this->bits_per_sample )
+    if (mode == this->mode && rate == this->sample_rate &&
+        bits == this->bits_per_sample) {
+
+      pa_threaded_mainloop_unlock(this->mainloop);
       return this->sample_rate;
+    }
 
-    this_gen->close(this_gen);
+    pa_stream_disconnect(this->stream);
+    pa_stream_unref(this->stream);
+    this->stream = NULL;
   }
 
   this->mode                   = mode;
@@ -221,6 +321,8 @@ static int ao_pulse_open(ao_driver_t *th
     case 32:
       ss.format = PA_SAMPLE_FLOAT32NE;
       break;
+    default:
+      _x_assert(!"Should not be reached");
   }
 
   if (!pa_sample_spec_valid(&ss)) {
@@ -228,70 +330,86 @@ static int ao_pulse_open(ao_driver_t *th
     goto fail;
   }
 
-  if ( this->pa_class->context && pa_context_get_state(this->pa_class->context) > PA_CONTEXT_READY ) {
-    pa_context_unref(this->pa_class->context);
-    this->pa_class->context = NULL;
-  }
-
-  if ( this->pa_class->context == NULL ) {
-    this->pa_class->context = pa_context_new(pa_threaded_mainloop_get_api(this->pa_class->mainloop),
-					     __progname);
-  }
-
-  pa_context_ref(this->pa_class->context);
+  cm.channels = ss.channels;
 
-  if ( pa_context_get_state(this->pa_class->context) == PA_CONTEXT_UNCONNECTED ) {
-    int ret;
+  switch (mode) {
+    case AO_CAP_MODE_MONO:
+      cm.map[0] = PA_CHANNEL_POSITION_MONO;
+      _x_assert(cm.channels == 1);
+      break;
 
-    pa_threaded_mainloop_lock(this->pa_class->mainloop);
-    ret = pa_context_connect(this->pa_class->context, this->host, 1, NULL);
-    if ( ret < 0 )
-      goto fail_unlock;
+    case AO_CAP_MODE_STEREO:
+      cm.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+      cm.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+      _x_assert(cm.channels == 2);
+      break;
 
-    pa_context_set_state_callback(this->pa_class->context, __xine_pa_context_status_callback, this);
+    case AO_CAP_MODE_4CHANNEL:
+      cm.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+      cm.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+      cm.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+      cm.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+      _x_assert(cm.channels == 4);
+      break;
 
-    pa_threaded_mainloop_wait(this->pa_class->mainloop);
-    pa_threaded_mainloop_unlock(this->pa_class->mainloop);
+    case AO_CAP_MODE_4_1CHANNEL:
+    case AO_CAP_MODE_5CHANNEL:
+    case AO_CAP_MODE_5_1CHANNEL:
+      cm.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+      cm.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+      cm.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+      cm.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+      cm.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
+      cm.map[5] = PA_CHANNEL_POSITION_LFE;
+      cm.channels = 6;
+      break;
+    default:
+      _x_assert(!"Should not be reached");
   }
 
-  if (pa_context_get_state(this->pa_class->context) != PA_CONTEXT_READY) {
-    xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: Failed to connect to server: %s\n",
-	     pa_strerror(pa_context_errno(this->pa_class->context)));
+  if (!pa_channel_map_valid(&cm)) {
+    xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: Invalid channel map\n");
     goto fail;
   }
 
-  this->stream = pa_stream_new(this->pa_class->context, "audio stream", &ss, NULL);
+  if (connect_context(this) < 0)
+    goto fail;
+
+  _x_assert(!this->stream);
+  this->stream = pa_stream_new(this->context, "Audio Stream", &ss, &cm);
   _x_assert(this->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_set_state_callback(this->stream, __xine_pa_stream_state_callback, this);
+  pa_stream_set_write_callback(this->stream, __xine_pa_stream_request_callback, this);
+  pa_stream_set_latency_update_callback(this->stream, __xine_pa_stream_notify_callback, this);
 
-  pa_stream_connect_playback(this->stream, this->sink, &a,
-                             PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, 
+  r = pa_stream_connect_playback(this->stream, this->sink, NULL,
+                             PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE,
                              NULL, NULL);
 
-  do {
-    xine_usec_sleep (100);
+  for (;;) {
+    pa_context_state_t cstate = pa_context_get_state(this->context);
+    pa_stream_state_t sstate = pa_stream_get_state(this->stream);
+
+    if (cstate == PA_CONTEXT_FAILED || cstate == PA_CONTEXT_TERMINATED ||
+        sstate == PA_STREAM_FAILED || sstate == PA_STREAM_TERMINATED) {
+      xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: failed to connect context object: %s\n", pa_strerror(pa_context_errno(this->context)));
+      goto fail;
+    }
 
-    streamstate = pa_stream_get_state(this->stream);
-  } while (streamstate < PA_STREAM_READY);
-     
-  if (streamstate != PA_STREAM_READY) {
-    xprintf (this->xine, XINE_VERBOSITY_LOG, "audio_pulse_out: Failed to connect to server: %s\n",
-             pa_strerror(pa_context_errno(this->pa_class->context)));
-    goto fail;
+    if (sstate == PA_STREAM_READY)
+      break;
+
+    pa_threaded_mainloop_wait(this->mainloop);
   }
-  this->frames_written = 0;
 
-  this->ao_driver.set_property(this, AO_PROP_PCM_VOL, 100);
+  pa_threaded_mainloop_unlock(this->mainloop);
 
   return this->sample_rate;
 
- fail_unlock:
-  pa_threaded_mainloop_unlock(this->pa_class->mainloop);
  fail:
+
+  pa_threaded_mainloop_unlock(this->mainloop);
   this_gen->close(this_gen);
   return 0;
 }
@@ -319,217 +437,348 @@ static int ao_pulse_write(ao_driver_t *t
 {
   pulse_driver_t *this = (pulse_driver_t *) this_gen;
   size_t size = num_frames * this->bytes_per_frame;
-  int ret = 0;
-  
-  if ( !this->stream || !this->pa_class->context)
-    return -1;
-
-  switch( pa_stream_get_state(this->stream) ) {
-  case PA_STREAM_READY:
-    while (size > 0) {
-      size_t l;
+  int ret = -1;
+  size_t done = 0;
 
-      while (!(l = pa_stream_writable_size(this->stream))) {
-        xine_usec_sleep (10000);
-      }
+  pa_threaded_mainloop_lock(this->mainloop);
 
-      if (l > size)
-        l = size;
-        
-      pa_stream_write(this->stream, data, l, NULL, 0, PA_SEEK_RELATIVE);
-      data = (int16_t *) ((uint8_t*) data + l);
-      size -= l;
-    }
+  while (size > 0) {
+    size_t l;
 
-    this->frames_written += num_frames;
+    for (;;) {
 
-    if (pa_stream_get_state(this->stream) == PA_STREAM_READY)
-      ret = 1;
+      if (!this->stream ||
+          !this->context ||
+          pa_context_get_state(this->context) != PA_CONTEXT_READY ||
+          pa_stream_get_state(this->stream) != PA_STREAM_READY)
+        goto finish;
 
-    break;
+      if ((l = pa_stream_writable_size(this->stream)) == (size_t) -1)
+        goto finish;
+
+      if (l > 0)
+        break;
+
+      pa_threaded_mainloop_wait(this->mainloop);
+    }
+
+    if (l > size)
+      l = size;
+
+    pa_stream_write(this->stream, data, l, NULL, 0, PA_SEEK_RELATIVE);
+    data = (int16_t *) ((uint8_t*) data + l);
+    size -= l;
+    done += l;
   }
 
+  ret = done;
+
+finish:
+
+  pa_threaded_mainloop_unlock(this->mainloop);
+
+/*   fprintf(stderr, "write-out\n"); */
+
   return ret;
-}
 
+}
 
 static int ao_pulse_delay (ao_driver_t *this_gen)
 {
   pulse_driver_t *this = (pulse_driver_t *) this_gen;
-  pa_usec_t latency = 0;
-  unsigned int delay_frames;
+  int ret = 0;
 
-  if ( ! this->stream ) return this->frames_written;
+/*   fprintf(stderr, "delay-in\n"); */
 
-  if (pa_stream_get_latency(this->stream, &latency, NULL) < 0) {
-    pa_context_unref(this->pa_class->context);
-    this->pa_class->context = NULL;
+  pa_threaded_mainloop_lock(this->mainloop);
 
-    pa_stream_disconnect(this->stream);
-    pa_stream_unref(this->stream);
-    this->stream = NULL;
+  for (;;) {
+    pa_usec_t latency = 0;
 
-    return 0;
+    if (!this->stream ||
+        !this->context ||
+        pa_context_get_state(this->context) != PA_CONTEXT_READY ||
+        pa_stream_get_state(this->stream) != PA_STREAM_READY)
+      goto finish;
+
+    if (pa_stream_get_latency(this->stream, &latency, NULL) >= 0) {
+      ret = (int) ((latency * this->sample_rate) / 1000000);
+      goto finish;
+    }
+
+    if (pa_context_errno(this->context) != PA_ERR_NODATA) {
+      xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: failed to query latency: %s\n", pa_strerror(pa_context_errno(this->context)));
+      goto finish;
+    }
+
+    pa_threaded_mainloop_wait(this->mainloop);
   }
 
-  /* convert latency (us) to frame units. */
-  delay_frames = (int)(latency * this->sample_rate / 1000000);
+finish:
 
-  if( delay_frames > this->frames_written )
-    return this->frames_written;
-  else
-    return delay_frames;
+  pa_threaded_mainloop_unlock(this->mainloop);
+
+  return ret;
 }
 
 static void ao_pulse_close(ao_driver_t *this_gen)
 {
   pulse_driver_t *this = (pulse_driver_t *) this_gen;
-  
-  if (this->stream) {
-    if (pa_stream_get_state(this->stream) == PA_STREAM_READY) {
-      pthread_mutex_t completion_callback = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(&completion_callback);
-      pa_stream_drain(this->stream, __xine_pa_stream_success_callback, &completion_callback);
 
-      pthread_mutex_lock(&completion_callback);
-      pthread_mutex_destroy(&completion_callback);
-    }
+  pa_threaded_mainloop_lock(this->mainloop);
 
+  if (this->stream) {
     pa_stream_disconnect(this->stream);
     pa_stream_unref(this->stream);
     this->stream = NULL;
-
-    pa_context_unref(this->pa_class->context);
   }
+
+  pa_threaded_mainloop_unlock(this->mainloop);
 }
 
 static uint32_t ao_pulse_get_capabilities (ao_driver_t *this_gen) {
   pulse_driver_t *this = (pulse_driver_t *) this_gen;
+
   return this->capabilities;
 }
 
-static void ao_pulse_exit(ao_driver_t *this_gen)
-{
+static void ao_pulse_exit(ao_driver_t *this_gen) {
   pulse_driver_t *this = (pulse_driver_t *) this_gen;
 
-  free (this);
+  ao_pulse_close(this_gen);
+
+  pa_threaded_mainloop_lock(this->mainloop);
+
+  if (this->context) {
+    pa_context_disconnect(this->context);
+    pa_context_unref(this->context);
+  }
+
+  pa_threaded_mainloop_unlock(this->mainloop);
+
+  pa_threaded_mainloop_free(this->mainloop);
+
+  free(this->host);
+  free(this->sink);
+  free(this);
+}
+
+static int wait_for_operation(pulse_driver_t *this, pa_operation *o) {
+
+  for (;;) {
+
+    if (!this->stream ||
+        !this->context ||
+        pa_context_get_state(this->context) != PA_CONTEXT_READY ||
+        pa_stream_get_state(this->stream) != PA_STREAM_READY)
+      return -1;
+
+    if (pa_operation_get_state(o) != PA_OPERATION_RUNNING)
+      return 0;
+
+    pa_threaded_mainloop_wait(this->mainloop);
+  }
 }
 
 static int ao_pulse_get_property (ao_driver_t *this_gen, int property) {
   pulse_driver_t *this = (pulse_driver_t *) this_gen;
   int result = 0;
+  pa_operation *o = NULL;
 
-  if ( ! this->stream || ! this->pa_class->context )
+  pa_threaded_mainloop_lock(this->mainloop);
+
+  if (!this->stream ||
+      !this->context ||
+      pa_context_get_state(this->context) != PA_CONTEXT_READY ||
+      pa_stream_get_state(this->stream) != PA_STREAM_READY) {
+    pa_threaded_mainloop_unlock(this->mainloop);
     return 0;
+  }
 
   switch(property) {
-  case AO_PROP_PCM_VOL:
-  case AO_PROP_MIXER_VOL:
-    {
-      pthread_mutex_lock(&this->info_mutex);
-      pa_operation *o = pa_context_get_sink_input_info(this->pa_class->context,
-						       pa_stream_get_index(this->stream),
-						       __xine_pa_sink_info_callback, this);
-      if ( ! o ) return 0;
-      pthread_mutex_lock(&this->info_mutex); pthread_mutex_unlock(&this->info_mutex);
-			 
-      result = (pa_sw_volume_to_linear(this->swvolume)*100);
-    }
-    break;
 
-  case AO_PROP_MUTE_VOL:
-    result = pa_cvolume_is_muted(&this->cvolume);
-    break;
+    case AO_PROP_MUTE_VOL:
+    case AO_PROP_PCM_VOL:
+    case AO_PROP_MIXER_VOL:
+
+      o = pa_context_get_sink_input_info(this->context, pa_stream_get_index(this->stream),
+                                         __xine_pa_sink_info_callback, this);
+
+      break;
+  }
+
+  if (o) {
+    wait_for_operation(this, o);
+    pa_operation_unref(o);
   }
-  
+
+  switch(property) {
+
+    case AO_PROP_MUTE_VOL:
+      result = this->muted;
+      break;
+
+    case AO_PROP_PCM_VOL:
+    case AO_PROP_MIXER_VOL:
+      result = (int) (pa_sw_volume_to_linear(this->swvolume)*100);
+      break;
+  }
+
+  pa_threaded_mainloop_unlock(this->mainloop);
+
   return result;
 }
 
 static int ao_pulse_set_property (ao_driver_t *this_gen, int property, int value) {
   pulse_driver_t *this = (pulse_driver_t *) this_gen;
   int result = ~value;
+  pa_operation *o = NULL;
 
-  if ( ! this->stream || ! this->pa_class->context )
-    return result;
+  pa_threaded_mainloop_lock(this->mainloop);
+
+  if (!this->stream ||
+      !this->context ||
+      pa_context_get_state(this->context) != PA_CONTEXT_READY ||
+      pa_stream_get_state(this->stream) != PA_STREAM_READY) {
+    pa_threaded_mainloop_unlock(this->mainloop);
+    return 0;
+  }
 
   switch(property) {
-  case AO_PROP_PCM_VOL:
-  case AO_PROP_MIXER_VOL:
-    this->swvolume = pa_sw_volume_from_linear((double)value/100.0);
-    pa_cvolume_set(&this->cvolume, pa_stream_get_sample_spec(this->stream)->channels, this->swvolume);
-
-    pa_context_set_sink_input_volume(this->pa_class->context, pa_stream_get_index(this->stream),
-				     &this->cvolume, __xine_pa_context_success_callback, this);
-
-    result = value;
-    break;
-
-  case AO_PROP_MUTE_VOL:
-    if ( value )
-      pa_cvolume_mute(&this->cvolume, pa_stream_get_sample_spec(this->stream)->channels);
-    else
+    case AO_PROP_PCM_VOL:
+    case AO_PROP_MIXER_VOL:
+
+      this->swvolume = pa_sw_volume_from_linear((double)value/100.0);
       pa_cvolume_set(&this->cvolume, pa_stream_get_sample_spec(this->stream)->channels, this->swvolume);
 
-    pa_context_set_sink_input_volume(this->pa_class->context, pa_stream_get_index(this->stream),
-				     &this->cvolume, __xine_pa_context_success_callback, this);
-    
-    result = value;
-    break;
+      o = pa_context_set_sink_input_volume(this->context, pa_stream_get_index(this->stream),
+                                           &this->cvolume, __xine_pa_context_success_callback, this);
+
+      result = value;
+      break;
+
+    case AO_PROP_MUTE_VOL:
+
+      this->muted = value;
+
+      o = pa_context_set_sink_input_mute(this->context, pa_stream_get_index(this->stream),
+                                           value, __xine_pa_context_success_callback, this);
+
+      result = value;
+  }
+
+  if (o) {
+    wait_for_operation(this, o);
+    pa_operation_unref(o);
   }
-  
+
+  pa_threaded_mainloop_unlock(this->mainloop);
+
   return result;
 }
 
 static int ao_pulse_ctrl(ao_driver_t *this_gen, int cmd, ...) {
   pulse_driver_t *this = (pulse_driver_t *) this_gen;
+  pa_operation *o = NULL;
+
+  pa_threaded_mainloop_lock(this->mainloop);
 
-  if ( ! this->stream ) return 0;
+  if (!this->stream ||
+      !this->context ||
+      pa_context_get_state(this->context) != PA_CONTEXT_READY ||
+      pa_stream_get_state(this->stream) != PA_STREAM_READY) {
+    pa_threaded_mainloop_unlock(this->mainloop);
+    return 0;
+  }
 
   switch (cmd) {
 
-  case AO_CTRL_FLUSH_BUFFERS:
-    _x_assert(this->stream && this->pa_class->context);
+    case AO_CTRL_FLUSH_BUFFERS:
 
-    if(pa_stream_get_state(this->stream) == PA_STREAM_READY) {
-      pthread_mutex_t completion_callback = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(&completion_callback);
-      pa_stream_flush(this->stream, __xine_pa_stream_success_callback, &completion_callback);
+      o = pa_stream_flush(this->stream, __xine_pa_stream_success_callback, this);
+      break;
 
-      pthread_mutex_lock(&completion_callback);
-      pthread_mutex_destroy(&completion_callback);
-    }
+    case AO_CTRL_PLAY_RESUME:
+    case AO_CTRL_PLAY_PAUSE:
 
-    this->frames_written = 0;
+      o = pa_stream_cork(this->stream, cmd == AO_CTRL_PLAY_PAUSE, __xine_pa_stream_success_callback, this);
+      break;
+  }
 
-    break;
+  if (o) {
+    wait_for_operation(this, o);
+    pa_operation_unref(o);
   }
 
+  pa_threaded_mainloop_unlock(this->mainloop);
+
   return 0;
 }
 
 static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *data) {
   pulse_class_t   *class = (pulse_class_t *) class_gen;
   pulse_driver_t  *this;
-  char *device;
+  const char* device;
+  int r;
 
   lprintf ("audio_pulse_out: open_plugin called\n");
 
   this = (pulse_driver_t *) xine_xmalloc (sizeof (pulse_driver_t));
   if (!this)
     return NULL;
+
   this->xine = class->xine;
+  this->host = NULL;
+  this->sink = NULL;
+  this->context = NULL;
+  this->mainloop = NULL;
+
+  device = class->xine->config->register_string(class->xine->config,
+                                         "audio.pulseaudio_device",
+                                         "",
+                                         _("device used for pulseaudio"),
+                                         _("use 'server[:sink]' for setting the "
+                                           "pulseaudio sink device."),
+                                         10, NULL,
+                                         NULL);
+
+  if (device && *device) {
+    char *sep = strrchr(device, ':');
+    if ( sep ) {
+      if (!(this->host = strndup(device, sep-device))) {
+        free(this);
+        return NULL;
+      }
+
+      if (!(this->sink = strdup(sep+1))) {
+        free(this->host);
+        free(this);
+        return NULL;
+      }
+    } else {
+
+      if (!(this->host = strdup(device))) {
+        free(this);
+        return NULL;
+      }
+    }
+  }
+
+  this->mainloop = pa_threaded_mainloop_new();
+  _x_assert(this->mainloop);
+  pa_threaded_mainloop_start(this->mainloop);
 
   /*
    * set capabilities
    */
-  this->capabilities = AO_CAP_MODE_MONO | AO_CAP_MODE_STEREO | AO_CAP_MODE_4CHANNEL |
-                       AO_CAP_MODE_4_1CHANNEL | AO_CAP_MODE_5CHANNEL |
-                       AO_CAP_MODE_5_1CHANNEL | AO_CAP_MIXER_VOL |
-                       AO_CAP_PCM_VOL | AO_CAP_MUTE_VOL | AO_CAP_8BITS |
-                       AO_CAP_16BITS | AO_CAP_FLOAT32;
+  this->capabilities =
+    AO_CAP_MODE_MONO | AO_CAP_MODE_STEREO | AO_CAP_MODE_4CHANNEL |
+    AO_CAP_MODE_4_1CHANNEL | AO_CAP_MODE_5CHANNEL | AO_CAP_MODE_5_1CHANNEL |
+    AO_CAP_MIXER_VOL | AO_CAP_PCM_VOL | AO_CAP_MUTE_VOL |
+    AO_CAP_8BITS | AO_CAP_16BITS | AO_CAP_FLOAT32;
 
   this->sample_rate  = 0;
-  this->host         = NULL;
-  this->sink         = NULL;
-  
+
   this->ao_driver.get_capabilities    = ao_pulse_get_capabilities;
   this->ao_driver.get_property        = ao_pulse_get_property;
   this->ao_driver.set_property        = ao_pulse_set_property;
@@ -541,33 +790,22 @@ static ao_driver_t *open_plugin (audio_d
   this->ao_driver.close               = ao_pulse_close;
   this->ao_driver.exit                = ao_pulse_exit;
   this->ao_driver.get_gap_tolerance   = ao_pulse_get_gap_tolerance;
-  this->ao_driver.control	      = ao_pulse_ctrl;
-
-  device = this->xine->config->register_string(this->xine->config,
-                                               "audio.pulseaudio_device",
-                                               "",
-                                               _("device used for pulseaudio"),
-                                               _("use 'server[:sink]' for setting the "
-                                                 "pulseaudio sink device."),
-                                               10, NULL,
-                                               NULL);
-
-  if (device && *device) {
-    char *sep = strchr(device, ':');
-    if ( sep ) {
-      this->host = strndup(device, sep-device);
-      this->sink = strdup(&sep[1]);
-    } else
-      this->host = strdup(device);
-  }
-
-  pthread_mutex_init(&this->info_mutex, NULL);
+  this->ao_driver.control             = ao_pulse_ctrl;
 
   xprintf (class->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: host %s sink %s\n",
            this->host ? this->host : "(null)", this->sink ? this->sink : "(null)");
 
   this->pa_class = class;
 
+  pa_threaded_mainloop_lock(this->mainloop);
+  r = connect_context(this);
+  pa_threaded_mainloop_unlock(this->mainloop);
+
+  if (r < 0) {
+    ao_pulse_exit((ao_driver_t *) this);
+    return NULL;
+  }
+
   return &this->ao_driver;
 }
 
@@ -587,13 +825,7 @@ static void dispose_class (audio_driver_
 
   pulse_class_t *this = (pulse_class_t *) this_gen;
 
-  if ( this->context )
-    pa_context_unref(this->context);
-
-  pa_threaded_mainloop_stop(this->mainloop);
-  pa_threaded_mainloop_free(this->mainloop);
-
-  free (this);
+  free(this);
 }
 
 static void *init_class (xine_t *xine, void *data) {
@@ -606,25 +838,17 @@ static void *init_class (xine_t *xine, v
   if (!this)
     return NULL;
 
+  this->xine = xine;
   this->driver_class.open_plugin     = open_plugin;
   this->driver_class.get_identifier  = get_identifier;
   this->driver_class.get_description = get_description;
   this->driver_class.dispose         = dispose_class;
 
-  this->xine                         = xine;
-
-  this->mainloop = pa_threaded_mainloop_new();
-  _x_assert(this->mainloop);
-
-  pa_threaded_mainloop_start(this->mainloop);
-  
-  this->context = NULL;
-
   return this;
 }
 
 static const ao_info_t ao_info_pulse = {
-  6
+  12
 };
 
 /*
@@ -636,5 +860,3 @@ const plugin_info_t xine_plugin_info[] E
   { PLUGIN_AUDIO_OUT, 8, "pulseaudio", XINE_VERSION_CODE, &ao_info_pulse, init_class },
   { PLUGIN_NONE, 0, "", 0, NULL, NULL }
 };
-
-


Index: xine-lib.spec
===================================================================
RCS file: /cvs/pkgs/rpms/xine-lib/devel/xine-lib.spec,v
retrieving revision 1.37
retrieving revision 1.38
diff -u -r1.37 -r1.38
--- xine-lib.spec	30 Mar 2008 18:50:26 -0000	1.37
+++ xine-lib.spec	7 Apr 2008 13:09:55 -0000	1.38
@@ -32,7 +32,7 @@
 Summary:        Xine library
 Name:           xine-lib
 Version:        1.1.11.1
-Release:        1%{?dist}
+Release:        2%{?dist}
 License:        GPLv2+
 Group:          System Environment/Libraries
 URL:            http://xinehq.de/
@@ -48,6 +48,10 @@
 Patch6:         %{name}-1.1.1-deepbind-939.patch
 BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 
+# upstream patches
+# lennart said this was upstreamed already.  :)
+Patch100: http://0pointer.de/public/xine-pulse-rework2.patch
+
 Provides:       xine-lib(plugin-abi) = %{abiver}
 # X11
 BuildRequires:  libX11-devel
@@ -119,6 +123,7 @@
 %description    devel
 This package contains development files for xine-lib.
 
+# TODO: consider removing and/or marking as deprecated for F9+ -- Rex
 %package        arts
 Summary:        aRts plugin for xine-lib
 Group:          System Environment/Libraries
@@ -129,6 +134,16 @@
 %description    arts
 This package contains the aRts extra plugin for xine-lib.
 
+%package        pulseaudio
+Summary:        Pulseaudio plugin for xine-lib
+Group:          System Environment/Libraries
+Requires:       %{name} = %{version}-%{release}
+# -pulseaudio was split off -extras at 1.1.11.1-2
+#Obsoletes:     xine-lib-extras < 1.1.11.1-2
+
+%description    pulseaudio
+This package contains the pulseaudio plugin for xine-lib.
+
 %package        extras
 Summary:        Additional plugins for xine-lib
 Group:          System Environment/Libraries
@@ -138,9 +153,6 @@
 This package contains extra plugins for xine-lib:
   - EsounD
   - JACK
-%if %{with_pa}
-  - Pulseaudio
-%endif # pa
   - GDK-Pixbuf
   - GNOME VFS
   - SMB
@@ -169,6 +181,8 @@
 # Patch6 needed at least when compiling with external ffmpeg, #939.
 %patch6 -p1 -b .deepbind
 
+%patch100 -p1 -b .pulse-rework2
+
 # Avoid standard rpaths on lib64 archs:
 sed -i -e 's|"/lib /usr/lib\b|"/%{_lib} %{_libdir}|' configure
 
@@ -348,13 +362,16 @@
 %defattr(-,root,root,-)
 %{_libdir}/xine/plugins/%{abiver}/xineplug_ao_out_arts.so
 
+%if %{with_pa}
+%files pulseaudio
+%defattr(-,root,root,-)
+%{_libdir}/xine/plugins/%{abiver}/xineplug_ao_out_pulseaudio.so
+%endif 
+
 %files extras
 %defattr(-,root,root,-)
 %{_libdir}/xine/plugins/%{abiver}/xineplug_ao_out_esd.so
 %{_libdir}/xine/plugins/%{abiver}/xineplug_ao_out_jack.so
-%if %{with_pa}
-%{_libdir}/xine/plugins/%{abiver}/xineplug_ao_out_pulseaudio.so
-%endif # pa
 %{_libdir}/xine/plugins/%{abiver}/xineplug_decode_gdk_pixbuf.so
 %if 0%{!?_without_imagemagick:1}
 %{_libdir}/xine/plugins/%{abiver}/xineplug_decode_image.so
@@ -386,6 +403,10 @@
 
 
 %changelog
+* Mon Apr 07 2008 Rex Dieter <rdieter at fedoraproject.org> - 1.1.11.1-2
+- pulse-rework2 patch (#439731)
+- -pulseaudio subpkg (#439731)
+
 * Sun Mar 30 2008 Ville Skyttä <ville.skytta at iki.fi> - 1.1.11.1-1
 - 1.1.11.1 (security update, #438663, CVE-2008-1482).
 - Provide versioned xine-lib(plugin-abi) so 3rd party packages installing




More information about the fedora-extras-commits mailing list