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

Christophe Fergeau cfergeau at redhat.com
Thu Oct 23 05:28:20 PDT 2014


ACK

On Wed, Oct 22, 2014 at 11:33:17AM -0500, Jeremy White wrote:
> 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
> 
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/spice-devel
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.freedesktop.org/archives/spice-devel/attachments/20141023/89cb38ad/attachment-0001.sig>


More information about the Spice-devel mailing list