[Spice-devel] [PATCH xf86-video-qxl v3] Revise the XSpice audio processing to avoid the use of pthreads.

Jeremy White jwhite at codeweavers.com
Wed Oct 22 09:33:17 PDT 2014


The initial implementation used a separate thread to drive the audio
playback channel.  But if you have adaptive streaming turned on,
you will eventually get a update_client_playback_latency message on the
display channel (which in the Xspice case is being driven by the main,
Xorg, thread).

After enough time you would get a thread collision and bad things
would result.  I saw this manifest as infinite spin loops in snd_send_data.

This patch eliminates the use of threading altogether, making everything
run in the main Xorg thread using watches and timers, eliminating the
possibility of thread collision.

Signed-off-by: Jeremy White <jwhite at codeweavers.com>
---
 configure.ac         |    2 +
 src/qxl.h            |    3 +-
 src/spiceqxl_audio.c |  581 +++++++++++++++++++++++++++++++-------------------
 3 files changed, 368 insertions(+), 218 deletions(-)

diff --git a/configure.ac b/configure.ac
index 3ca32b6..119340a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -126,6 +126,8 @@ AC_ARG_ENABLE(xspice,
 ])
 
 if test "x$enable_xspice" = "xyes"; then
+    AC_CHECK_HEADERS(sys/inotify.h)
+    AC_CHECK_FUNCS(inotify_init1)
     PKG_CHECK_MODULES([SPICE], [spice-server >= 0.6.3],
     [
         AC_SUBST(SPICE_CFLAGS)
diff --git a/src/qxl.h b/src/qxl.h
index fa9b13f..603faca 100644
--- a/src/qxl.h
+++ b/src/qxl.h
@@ -334,8 +334,6 @@ struct _qxl_screen_t
     /* XSpice specific, dragged from the Device */
     QXLReleaseInfo     *last_release;
 
-    pthread_t audio_thread;
-
     uint32_t           cmdflags;
     uint32_t           oom_running;
     uint32_t           num_free_res; /* is having a release ring effective
@@ -353,6 +351,7 @@ struct _qxl_screen_t
     } guest_primary;
 
     char playback_fifo_dir[PATH_MAX];
+    void *playback_opaque;
 #endif /* XSPICE */
 
     uint32_t deferred_fps;
diff --git a/src/spiceqxl_audio.c b/src/spiceqxl_audio.c
index 02859ee..086b943 100644
--- a/src/spiceqxl_audio.c
+++ b/src/spiceqxl_audio.c
@@ -20,6 +20,10 @@
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 
+/* XSpice based audio feed; reads from files (presumably fifos) in a configured directory,
+   and mixes their raw data on to the spice playback channel.  */
+
+
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
@@ -32,283 +36,379 @@
 #include <sys/time.h>
 #include <unistd.h>
 #include <dirent.h>
+#if defined(HAVE_SYS_INOTIFY_H)
+#include <sys/inotify.h>
+#endif
+
+/* mplayer + pulse will write data to the fifo as fast as we can read it.
+       So we need to pace both how quickly we consume the data and how quickly
+       we feed the data in to Spice.  We will read ahead (up to READ_BUFFER_PERIODS),
+       and feed ahead into the Spice server (up to FEED_BUFFER_PERIODS).
+*/
+
+#define PERIOD_MS            10
+#define READ_BUFFER_PERIODS  2
+#define FEED_BUFFER_PERIODS  8
 
-#define BUFFER_PERIODS 10
-#define PERIOD_MS 10
 #define MAX_FIFOS 16
 
+struct fifo_data {
+    char *buffer;
+    int   size;
+    int   len;
+    int   add_to;
+    int   fd;
+    SpiceWatch *watch;
+};
+
 struct audio_data {
-    int fifo_fds[MAX_FIFOS];
-    ino_t inodes[MAX_FIFOS];
-    uint32_t valid_bytes, write_offs;
-    char *buffer, *spice_buffer;
-    int period_frames;
-    uint32_t spice_write_offs, spice_buffer_bytes;
-    uint32_t frame_bytes, period_bytes, fed, buffer_bytes;
-    struct timeval last_read_time;
+    struct fifo_data fifos[MAX_FIFOS];
+    uint32_t *spice_buffer;
+    int spice_buffer_bytes;
+    int period_bytes;
+    struct timeval fed_through_time;
+    int remainder;
+    int fifo_count;
+    SpiceTimer *wall_timer;
+    int wall_timer_live;
+    int dir_watch;
+    int fifo_dir_watch;
+    SpiceWatch *fifo_dir_qxl_watch;
 };
 
-static ssize_t
-read_from_fifos (struct audio_data *data)
+/* We maintain a ring buffer for each file we are reading from;
+   these helper functions facilitate adding data to the buffer,
+   and removing it.  */
+static inline void fifo_data_added(struct fifo_data *f, int n)
 {
-    size_t to_read_bytes = min(data->period_bytes, data->buffer_bytes - data->write_offs);
-    int i;
-    ssize_t max_readed = 0;
-    int16_t *out_buf = (int16_t*)(data->buffer + data->write_offs), *buf;
+    f->add_to = (f->add_to + n) % f->size;
+    f->len += n;
+}
 
-    buf = malloc(to_read_bytes);
-    if (!buf)
-    {
-        ErrorF("playback: malloc failed: %s\n", strerror(errno));
-        return 0;
+static inline int fifo_read(struct fifo_data *f)
+{
+    int rc;
+    int len = min(f->size - f->len, f->size - f->add_to);
+    rc = read(f->fd, f->buffer + f->add_to, len);
+    if (rc > 0)
+        fifo_data_added(f, rc);
+
+    if (rc > 0 && rc == len && f->size - f->len > 0) {
+        rc = read(f->fd, f->buffer + f->add_to, f->size - f->len);
+        if (rc > 0)
+            fifo_data_added(f, rc);
     }
 
-    memset(out_buf, 0, to_read_bytes);
-
-    for (i = 0; i < MAX_FIFOS; ++i)
-    {
-        unsigned int s;
-        ssize_t readed;
-
-        if (data->fifo_fds[i] < 0)
-            continue;
+    return rc;
+}
 
-        readed = read(data->fifo_fds[i], buf, to_read_bytes);
-        if (readed < 0)
-        {
-            if (errno != EAGAIN && errno != EINTR)
-                ErrorF("playback: read from FIFO %d failed: %s\n", data->fifo_fds[i], strerror(errno));
-            continue;
-        }
+static inline void fifo_remove_data(struct fifo_data *f, unsigned char *dest, int len)
+{
+    int remove_from = f->add_to >= f->len ? f->add_to - f->len : f->add_to + f->size - f->len;
+    int remain = f->size - remove_from;
+
+    if (remain < len) {
+        memcpy(dest, f->buffer + remove_from, remain);
+        dest += remain;
+        len -= remain;
+        f->len -= remain;
+        remove_from = 0;
+    }
 
-        if (readed == 0)
-        {
-            ErrorF("playback: FIFO %d gave EOF\n", data->fifo_fds[i]);
-            close(data->fifo_fds[i]);
-            data->fifo_fds[i] = -1;
-            data->inodes[i] = 0;
-            continue;
-        }
+    memcpy(dest, f->buffer + remove_from, len);
+    f->len -= len;
+}
 
-        if (readed > max_readed)
-            max_readed = readed;
-
-        for (s = 0; s < readed / sizeof(int16_t); ++s)
-        {
-            /* FIXME: Ehhh, this'd be better as floats. With this algorithm,
-             * samples mixed after being clipped will have undue weight. But
-             * if we're clipping, then we're distorted anyway, so whatever. */
-            if (out_buf[s] + buf[s] > INT16_MAX)
-                out_buf[s] = INT16_MAX;
-            else if (out_buf[s] + buf[s] < -INT16_MAX)
-                out_buf[s] = -INT16_MAX;
-            else
-                out_buf[s] += buf[s];
-        }
+static void mix_in_one_fifo(struct fifo_data *f, int16_t *out, int len)
+{
+    int s;
+    int16_t *in;
+
+    if (len > f->len)
+        len = f->len;
+
+    in = calloc(1, len);
+
+    fifo_remove_data(f, (unsigned char *) in, len);
+
+    for (s = 0; s < (len / sizeof(int16_t)); s++) {
+        /* FIXME: Ehhh, this'd be better as floats. With this algorithm,
+         * samples mixed after being clipped will have undue weight. But
+         * if we're clipping, then we're distorted anyway, so whatever. */
+        if (out[s] + in[s] > INT16_MAX)
+            out[s] = INT16_MAX;
+        else if (out[s] + in[s] < -INT16_MAX)
+            out[s] = -INT16_MAX;
+        else
+            out[s] += in[s];
     }
 
-    free(buf);
+    free(in);
+}
 
-    if (!max_readed)
-        return 0;
+static void mix_in_fifos(qxl_screen_t *qxl)
+{
+    int i;
+    struct audio_data *data = qxl->playback_opaque;
+    struct fifo_data *f;
 
-    data->valid_bytes = min(data->valid_bytes + max_readed,
-            data->buffer_bytes);
+    memset(data->spice_buffer, 0, data->spice_buffer_bytes);
 
-    data->write_offs += max_readed;
-    data->write_offs %= data->buffer_bytes;
+    if (data->fifo_count == 0)
+        return;
 
-    ++data->fed;
+    /* First fifo can just be copied */
+    f = &data->fifos[0];
+    fifo_remove_data(f, (unsigned char *) data->spice_buffer, min(data->spice_buffer_bytes, f->len));
 
-    return max_readed;
+    /* Extra fifos need to be mixed in */
+    for (i = 1; i < data->fifo_count; i++) {
+        f = &data->fifos[i];
+        if (f->len > 0)
+            mix_in_one_fifo(f, (int16_t *) data->spice_buffer, data->spice_buffer_bytes);
+    }
 }
 
-static int
-scan_fifos (struct audio_data *data, const char *dirname)
+static int can_feed(struct audio_data *data)
 {
-    DIR *dir;
-    struct dirent *ent;
-    int i;
+    struct timeval end, diff;
 
-    dir = opendir(dirname);
-    if (!dir)
-    {
-        ErrorF("playback: failed to open FIFO directory '%s': %s\n", dirname, strerror(errno));
+    gettimeofday(&end, NULL);
+
+    if (end.tv_sec > data->fed_through_time.tv_sec ||
+        (end.tv_sec == data->fed_through_time.tv_sec &&
+         end.tv_usec >= data->fed_through_time.tv_usec)) {
+        data->fed_through_time.tv_sec = data->fed_through_time.tv_usec = 0;
+        data->remainder = 0;
         return 1;
     }
 
-    while ((ent = readdir(dir)))
-    {
-        char path[PATH_MAX];
+    timersub(&data->fed_through_time, &end, &diff);
+    if (diff.tv_sec == 0 && diff.tv_usec < PERIOD_MS * 1000 * FEED_BUFFER_PERIODS)
+        return 1;
 
-        if (ent->d_name[0] == '.')
-            /* skip dot-files */
-            continue;
+    return 0;
+}
 
-        for (i = 0; i < MAX_FIFOS; ++i)
-            if (ent->d_ino == data->inodes[i])
-                break;
-        if (i < MAX_FIFOS)
-            /* file already open */
-            continue;
+static void did_feed(struct audio_data *data, int len)
+{
+    struct timeval diff;
 
-        for (i = 0; i < MAX_FIFOS; ++i)
-            if (data->fifo_fds[i] < 0)
-                break;
-        if (i == MAX_FIFOS)
-        {
-            static int once = 0;
-            if (!once)
-            {
-                ErrorF("playback: Too many FIFOs already open\n");
-                ++once;
+    if (data->fed_through_time.tv_sec == 0 && data->fed_through_time.tv_usec == 0)
+        gettimeofday(&data->fed_through_time, NULL);
+
+    diff.tv_sec = 0;
+    diff.tv_usec = (data->remainder + (len * PERIOD_MS * 1000)) / data->period_bytes;
+    data->remainder = (data->remainder + (len * PERIOD_MS * 1000)) % data->period_bytes;
+
+    timeradd(&data->fed_through_time, &diff, &data->fed_through_time);
+}
+
+static void condense_fifos(struct audio_data *data)
+{
+    int i;
+    struct fifo_data tmp;
+
+    for (i = 0; i < data->fifo_count; i++) {
+        struct fifo_data *f = &data->fifos[i];
+        if (f->fd == -1 && f->len == 0) {
+            if ((i + 1) < data->fifo_count) {
+                tmp = *f;
+                *f = data->fifos[data->fifo_count - 1];
+                data->fifos[data->fifo_count - 1] = tmp;
             }
-            closedir(dir);
-            return 0;
+            data->fifo_count--;
+            i--;
         }
+    }
+}
 
-        if (snprintf(path, sizeof(path), "%s/%s", dirname, ent->d_name) >= sizeof(path)) {
-            ErrorF("playback: FIFO filename is too long - truncated into %s", path);
+static void watch_or_wait(qxl_screen_t *qxl);
+static void process_fifos(qxl_screen_t *qxl, struct audio_data *data, int maxlen)
+{
+    while (maxlen > 0) {
+        if (! data->spice_buffer) {
+            uint32_t chunk_frames;
+            spice_server_playback_get_buffer(&qxl->playback_sin, &data->spice_buffer, &chunk_frames);
+            data->spice_buffer_bytes = chunk_frames * sizeof(int16_t) * SPICE_INTERFACE_PLAYBACK_CHAN;
         }
 
-        data->fifo_fds[i] = open(path, O_RDONLY | O_RSYNC | O_NONBLOCK);
-        if (data->fifo_fds[i] < 0)
-        {
-            ErrorF("playback: open FIFO '%s' failed: %s\n", path, strerror(errno));
-            continue;
-        }
-        ErrorF("playback: opened FIFO '%s' as %d\n", path, data->fifo_fds[i]);
+        if (! data->spice_buffer)
+            break;
 
-        data->inodes[i] = ent->d_ino;
-    }
+        if (! can_feed(data))
+            break;
 
-    closedir(dir);
+        mix_in_fifos(qxl);
 
-    return 0;
+        did_feed(data, data->spice_buffer_bytes);
+        maxlen -= data->spice_buffer_bytes;
+
+        spice_server_playback_put_samples(&qxl->playback_sin, data->spice_buffer);
+        data->spice_buffer = NULL;
+    }
+
+    watch_or_wait(qxl);
 }
 
-static void *
-audio_thread_main (void *p)
+static void read_from_fifos(int fd, int event, void *opaque)
 {
-    qxl_screen_t *qxl = p;
+    qxl_screen_t *qxl = opaque;
+    struct audio_data *data = qxl->playback_opaque;
     int i;
-    struct audio_data data;
-    int freq = SPICE_INTERFACE_PLAYBACK_FREQ;
+    int maxlen = 0;
+    for (i = 0; i < data->fifo_count; i++) {
+        struct fifo_data *f = &data->fifos[i];
+
+        if (f->size - f->len > 0 && f->fd >= 0) {
+            int rc;
+
+            rc = fifo_read(f);
+            if (rc == -1 && (errno == EAGAIN || errno == EINTR))
+                /* no new data to read */;
+            else if (rc <= 0) {
+                if (rc == 0)
+                    ErrorF("fifo %d closed\n", f->fd);
+                else
+                    ErrorF("fifo %d error %d: %s\n", f->fd, errno, strerror(errno));
 
-    memset(&data, 0, sizeof(data));
-    for (i = 0; i < MAX_FIFOS; ++i)
-        data.fifo_fds[i] = -1;
+                if (f->watch)
+                    qxl->core->watch_remove(f->watch);
+                f->watch = NULL;
+                close(f->fd);
+                f->fd = -1;
+            }
 
-#if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3
-    freq = spice_server_get_best_playback_rate(&qxl->playback_sin);
-#endif
-    data.period_frames = freq * PERIOD_MS / 1000;
+            if (f->size == f->len) {
+                if (f->watch)
+                    qxl->core->watch_remove(f->watch);
+                f->watch = NULL;
+            }
+        }
 
+        if (f->len > maxlen)
+            maxlen = f->len;
+    }
 
-    data.frame_bytes = sizeof(int16_t) * SPICE_INTERFACE_PLAYBACK_CHAN;
+    process_fifos(qxl, data, maxlen);
+}
 
-    data.period_bytes = data.period_frames * data.frame_bytes;
-    data.buffer_bytes = data.period_bytes * BUFFER_PERIODS;
-    data.buffer = malloc(data.buffer_bytes);
-    memset(data.buffer, 0, data.buffer_bytes);
+static void start_watching(qxl_screen_t *qxl)
+{
+    struct audio_data *data = qxl->playback_opaque;
+    int i;
 
-    spice_server_playback_start(&qxl->playback_sin);
+    for (i = 0; i < data->fifo_count; i++) {
+        struct fifo_data *f = &data->fifos[i];
+        if (f->watch || f->size == f->len || f->fd == -1)
+            continue;
 
-    gettimeofday(&data.last_read_time, NULL);
+        f->watch = qxl->core->watch_add(f->fd, SPICE_WATCH_EVENT_READ, read_from_fifos, qxl);
+    }
+}
 
-    while (1)
-    {
-        struct timeval end, diff, period_tv;
-
-        if (scan_fifos(&data, qxl->playback_fifo_dir))
-            goto cleanup;
-
-        while (data.fed < BUFFER_PERIODS)
-        {
-            if (!read_from_fifos(&data))
-                break;
-
-            while (data.valid_bytes)
-            {
-                int to_copy_bytes;
-                uint32_t read_offs;
-
-                if (!data.spice_buffer)
-                {
-                    uint32_t chunk_frames;
-                    spice_server_playback_get_buffer(&qxl->playback_sin, (uint32_t**)&data.spice_buffer, &chunk_frames);
-                    data.spice_buffer_bytes = chunk_frames * data.frame_bytes;
-                }
-                if (!data.spice_buffer)
-                    break;
-
-                if (data.valid_bytes > data.write_offs)
-                {
-                    read_offs = data.buffer_bytes + data.write_offs - data.valid_bytes;
-                    to_copy_bytes = min(data.buffer_bytes - read_offs,
-                            data.spice_buffer_bytes - data.spice_write_offs);
-                }
-                else
-                {
-                    read_offs = data.write_offs - data.valid_bytes;
-                    to_copy_bytes = min(data.valid_bytes,
-                            data.spice_buffer_bytes - data.spice_write_offs);
-                }
+static void watch_or_wait(qxl_screen_t *qxl)
+{
+    struct audio_data *data = qxl->playback_opaque;
 
-                memcpy(data.spice_buffer + data.spice_write_offs,
-                        data.buffer + read_offs, to_copy_bytes);
+    if (! can_feed(data)) {
+        if (! data->wall_timer_live) {
+            qxl->core->timer_start(data->wall_timer, PERIOD_MS);
+            data->wall_timer_live++;
+        }
+    }
+    else {
+        start_watching(qxl);
+        if (data->wall_timer_live)
+            qxl->core->timer_cancel(data->wall_timer);
+        data->wall_timer_live = 0;
+    }
+}
 
-                data.valid_bytes -= to_copy_bytes;
+static void wall_ticker(void *opaque)
+{
+    qxl_screen_t *qxl = opaque;
+    struct audio_data *data = qxl->playback_opaque;
 
-                data.spice_write_offs += to_copy_bytes;
+    data->wall_timer_live = 0;
 
-                if (data.spice_write_offs >= data.spice_buffer_bytes)
-                {
-                    spice_server_playback_put_samples(&qxl->playback_sin, (uint32_t*)data.spice_buffer);
-                    data.spice_buffer = NULL;
-                    data.spice_buffer_bytes = data.spice_write_offs = 0;
-                }
-            }
-        }
+    condense_fifos(data);
 
-        period_tv.tv_sec = 0;
-        period_tv.tv_usec = PERIOD_MS * 1000;
+    read_from_fifos(-1, 0, qxl);
+}
 
-        usleep(period_tv.tv_usec);
+#if defined(HAVE_SYS_INOTIFY_H)
+static void handle_one_change(qxl_screen_t *qxl, struct inotify_event *e)
+{
 
-        gettimeofday(&end, NULL);
+    if (e->mask & (IN_CREATE | IN_MOVED_TO)) {
+        struct audio_data *data = qxl->playback_opaque;
+        struct fifo_data *f;
+        char *fname;
 
-        timersub(&end, &data.last_read_time, &diff);
+        condense_fifos(data);
 
-        while (data.fed &&
-                (diff.tv_sec > 0 || diff.tv_usec >= period_tv.tv_usec))
-        {
-            timersub(&diff, &period_tv, &diff);
+        f = &data->fifos[data->fifo_count];
 
-            --data.fed;
+        if (data->fifo_count == MAX_FIFOS) {
+            static int once = 0;
+            if (!once) {
+                ErrorF("playback: Too many FIFOs already open\n");
+                ++once;
+            }
+            return;
+        }
+
+        fname = malloc(strlen(e->name) + strlen(qxl->playback_fifo_dir) + 1 + 1);
+        strcpy(fname, qxl->playback_fifo_dir);
+        strcat(fname, "/");
+        strcat(fname, e->name);
 
-            timeradd(&data.last_read_time, &period_tv, &data.last_read_time);
+        f->fd = open(fname, O_RDONLY | O_RSYNC | O_NONBLOCK);
+        free(fname);
+        if (f->fd < 0) {
+            ErrorF("playback: open FIFO '%s' failed: %s\n", e->name, strerror(errno));
+            return;
         }
 
-        if (!data.fed)
-            data.last_read_time = end;
-    }
+        ErrorF("playback: opened FIFO '%s' as %d:%d\n", e->name, data->fifo_count, f->fd);
 
-cleanup:
-    if (data.spice_buffer)
-    {
-        memset(data.spice_buffer, 0, data.spice_buffer_bytes - data.spice_write_offs);
-        spice_server_playback_put_samples(&qxl->playback_sin, (uint32_t*)data.spice_buffer);
-        data.spice_buffer = NULL;
-        data.spice_buffer_bytes = data.spice_write_offs = 0;
+        data->fifo_count++;
+
+        f->watch = qxl->core->watch_add(f->fd, SPICE_WATCH_EVENT_READ, read_from_fifos, qxl);
     }
+}
 
-    free(data.buffer);
+static void playback_dir_changed(int fd, int event, void *opaque)
+{
+    qxl_screen_t *qxl = opaque;
+    static unsigned char buf[sizeof(struct inotify_event) + NAME_MAX + 1];
+    static int index = 0;
+    struct inotify_event *e;
+    int rc;
+
+    do {
+        rc = read(fd, buf + index, sizeof(buf) - index);
+        if (rc > 0) {
+            index += rc;
+            if (index >= sizeof(*e)) {
+                int len;
+                e = (struct inotify_event *) buf;
+                len = sizeof(*e) + e->len;
+                if (index >= len) {
+                    handle_one_change(qxl, e);
+                    if (index > len)
+                        memmove(buf, buf + index, index - len);
+                    index -= len;
+                }
+            }
+        }
+    }
+    while (rc > 0);
+}
+#endif
 
-    spice_server_playback_stop(&qxl->playback_sin);
 
-    return NULL;
-}
 
 static const SpicePlaybackInterface playback_sif = {
     {
@@ -319,21 +419,51 @@ static const SpicePlaybackInterface playback_sif = {
     }
 };
 
+static void audio_initialize (qxl_screen_t *qxl)
+{
+    int i;
+    struct audio_data *data = qxl->playback_opaque;
+    int freq = SPICE_INTERFACE_PLAYBACK_FREQ;
+    int period_frames;
+    int frame_bytes;
+
+#if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3
+    freq = spice_server_get_best_playback_rate(&qxl->playback_sin);
+#endif
+
+    period_frames = freq * PERIOD_MS / 1000;
+    frame_bytes = sizeof(int16_t) * SPICE_INTERFACE_PLAYBACK_CHAN;
+    data->period_bytes = period_frames * frame_bytes;
+
+    for (i = 0; i < MAX_FIFOS; ++i) {
+        data->fifos[i].fd = -1;
+        data->fifos[i].size = data->period_bytes * READ_BUFFER_PERIODS;
+        data->fifos[i].buffer = calloc(1, data->fifos[i].size);
+    }
+
+    spice_server_playback_start(&qxl->playback_sin);
+}
+
+
 int
 qxl_add_spice_playback_interface (qxl_screen_t *qxl)
 {
     int ret;
+    struct audio_data *data = calloc(1, sizeof(*data));
 
-    if (qxl->playback_fifo_dir[0] == 0)
-    {
+#if defined(HAVE_SYS_INOTIFY_H) && defined(HAVE_INOTIFY_INIT1)
+    if (qxl->playback_fifo_dir[0] == 0) {
         ErrorF("playback: no audio FIFO directory, audio is disabled\n");
+        free(data);
         return 0;
     }
 
     qxl->playback_sin.base.sif = &playback_sif.base;
     ret = spice_server_add_interface(qxl->spice_server, &qxl->playback_sin.base);
-    if (ret < 0)
+    if (ret < 0) {
+        free(data);
         return errno;
+    }
 
 #if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3
     spice_server_set_playback_rate(&qxl->playback_sin,
@@ -341,14 +471,33 @@ qxl_add_spice_playback_interface (qxl_screen_t *qxl)
 #else
     /* disable CELT */
     ret = spice_server_set_playback_compression(qxl->spice_server, 0);
-    if (ret < 0)
+    if (ret < 0) {
+        free(data);
         return errno;
-
+    }
 #endif
 
-    ret = pthread_create(&qxl->audio_thread, NULL, &audio_thread_main, qxl);
-    if (ret < 0)
+
+    qxl->playback_opaque = data;
+    audio_initialize(qxl);
+
+    data->wall_timer = qxl->core->timer_add(wall_ticker, qxl);
+
+    data->dir_watch = inotify_init1(IN_NONBLOCK);
+    data->fifo_dir_watch = -1;
+    if (data->dir_watch >= 0)
+        data->fifo_dir_watch = inotify_add_watch(data->dir_watch, qxl->playback_fifo_dir, IN_CREATE | IN_MOVE);
+
+    if (data->fifo_dir_watch == -1) {
+        ErrorF("Error %s(%d) watching the fifo dir\n", strerror(errno), errno);
         return errno;
+    }
+
+    data->fifo_dir_qxl_watch = qxl->core->watch_add(data->dir_watch,
+            SPICE_WATCH_EVENT_READ, playback_dir_changed, qxl);
 
+#else
+    ErrorF("inotify not available; audio disabled.\n");
+#endif
     return 0;
 }
-- 
1.7.10.4



More information about the Spice-devel mailing list