[Spice-devel] [PATCH spice-server 11/18] sound: Convert SndChannelClient to GObject

Christophe Fergeau cfergeau at redhat.com
Tue Nov 29 14:56:00 UTC 2016


Hey,

On Wed, Nov 23, 2016 at 06:07:25PM +0000, Frediano Ziglio wrote:
> This patch is quite huge but have some benefits:
> - reduce dependency (DummyChannel* and some RedChannelClient internals);
> - reuse RedChannelClient instead of reading from the RedsStream
>   directly.
> 
> You can see that SndChannelClient has much less field
> as the code to read/write from/to client is reused from
> RedChannelClient instead of creating a fake RedChannelClient
> just to make the system happy.
> 
I started looking at this, and it's really big/hard to review with all
these changes :(
Wondering if the AudioFrame allocation change could be done before, if
we could have a few cosmetic commits first introducing
SpiceMarshaller *m = client->send_data.marshaller; in snd_send_xxx()
Maybe separate the GObjectification by itself from switching to using
facilities provided by RedChannelClient, ...
Dunno if the snd_set_command/snd_send API can be introduced before the
gobjectification

Just a few random thoughts after reading this patch in the hope that it
can be made simpler, some of them are probably not practical at all :(

Christophe



> One of the different between the old sound code and all other
> RedChannelClient objects was that the sound channel don't use
> a queue while RedChannelClient use RedPipeItem object. This was
> the main reason why RedChannelClient was not used. To implement
> the old behaviour a "persistent_pipe_item" is used. This RedPipeItem
> will be queued to RedChannelClient (only one!) so signal code we
> have data to send. The {playback,record}_channel_send_item will
> then send the messages to the client using RedChannelClient functions.
> For this reason snd_reset_send_data is replaced by a call to
> red_channel_client_init_send_data and snd_begin_send_message is
> replaced by red_channel_client_begin_send_message.
> 
> Another difference is the AudioFrame allocation. Previously these
> frames were allocated inside PlaybackChannelClient while now they
> are allocated in a different structure. This allows the samples
> to outlive the PlaybackChannelClient. This was possible even before
> and the client was kept alive. However having RedChannelClient as
> as base class can be an issue as this will cause the entire
> RedChannel and RedChannelClient to stay alive. To avoid this
> potential problem allocate the frames in a different structure
> keeping a reference from PlaybackChannelClient. When the client
> is freed the AudioFrameContainer is just unreferences.
> 
> Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
> ---
>  server/sound.c | 1100 +++++++++++++++++++++++++-------------------------------
>  1 file changed, 487 insertions(+), 613 deletions(-)
> 
> diff --git a/server/sound.c b/server/sound.c
> index ac11106..0c2a3ae 100644
> --- a/server/sound.c
> +++ b/server/sound.c
> @@ -32,14 +32,10 @@
>  
>  #include "spice.h"
>  #include "red-common.h"
> -#include "dummy-channel.h"
> -#include "dummy-channel-client.h"
>  #include "main-channel.h"
>  #include "reds.h"
>  #include "red-qxl.h"
>  #include "red-channel-client.h"
> -/* FIXME: for now, allow sound channel access to private RedChannelClient data */
> -#include "red-channel-client-private.h"
>  #include "red-client.h"
>  #include "sound.h"
>  #include "demarshallers.h"
> @@ -56,6 +52,7 @@ enum SndCommand {
>      SND_MIGRATE,
>      SND_CTRL,
>      SND_VOLUME,
> +    SND_MUTE,
>      SND_END_COMMAND,
>  };
>  
> @@ -68,67 +65,79 @@ enum PlaybackCommand {
>  #define SND_MIGRATE_MASK (1 << SND_MIGRATE)
>  #define SND_CTRL_MASK (1 << SND_CTRL)
>  #define SND_VOLUME_MASK (1 << SND_VOLUME)
> +#define SND_MUTE_MASK (1 << SND_MUTE)
> +#define SND_VOLUME_MUTE_MASK (SND_VOLUME_MASK|SND_MUTE_MASK)
>  
>  #define SND_PLAYBACK_MODE_MASK (1 << SND_PLAYBACK_MODE)
>  #define SND_PLAYBACK_PCM_MASK (1 << SND_PLAYBACK_PCM)
>  #define SND_PLAYBACK_LATENCY_MASK ( 1 << SND_PLAYBACK_LATENCY)
>  
>  typedef struct SndChannelClient SndChannelClient;
> -typedef void (*snd_channel_send_messages_proc)(void *in_channel);
> -typedef int (*snd_channel_handle_message_proc)(SndChannelClient *client, size_t size, uint32_t type, void *message);
> +typedef struct SndChannel SndChannel;
> +typedef struct PlaybackChannelClient PlaybackChannelClient;
> +typedef struct RecordChannelClient RecordChannelClient;
> +typedef struct AudioFrame AudioFrame;
> +typedef struct AudioFrameContainer AudioFrameContainer;
> +typedef struct SpicePlaybackState PlaybackChannel;
> +typedef struct SpiceRecordState RecordChannel;
> +
>  typedef void (*snd_channel_on_message_done_proc)(SndChannelClient *client);
> -typedef void (*snd_channel_cleanup_channel_proc)(SndChannelClient *client);
>  
> -typedef struct SndChannel SndChannel;
> +
> +#define TYPE_SND_CHANNEL_CLIENT snd_channel_client_get_type()
> +#define SND_CHANNEL_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_SND_CHANNEL_CLIENT, SndChannelClient))
> +GType snd_channel_client_get_type(void) G_GNUC_CONST;
>  
>  /* Connects an audio client to a Spice client */
>  struct SndChannelClient {
> -    RedsStream *stream;
> -    SndChannel *channel;
> -    spice_parse_channel_func_t parser;
> -    int refs;
> -
> -    RedChannelClient *channel_client;
> +    RedChannelClient parent;
>  
>      int active;
>      int client_active;
> -    int blocked;
>  
>      uint32_t command;
>  
> -    struct {
> -        uint64_t serial;
> -        SpiceMarshaller *marshaller;
> -        uint32_t size;
> -        uint32_t pos;
> -    } send_data;
> -
> -    struct {
> -        uint8_t buf[SND_RECEIVE_BUF_SIZE];
> -        uint8_t *message_start;
> -        uint8_t *now;
> -        uint8_t *end;
> -    } receive_data;
> -
> -    snd_channel_send_messages_proc send_messages;
> -    snd_channel_handle_message_proc handle_message;
> +    uint8_t receive_buf[SND_RECEIVE_BUF_SIZE];
> +    RedPipeItem persistent_pipe_item;
> +
>      snd_channel_on_message_done_proc on_message_done;
> -    snd_channel_cleanup_channel_proc cleanup;
>  };
>  
> -typedef struct PlaybackChannelClient PlaybackChannelClient;
> +typedef RedChannelClientClass SndChannelClientClass;
> +
> +G_DEFINE_TYPE(SndChannelClient, snd_channel_client, RED_TYPE_CHANNEL_CLIENT)
> +
> +
> +enum {
> +    RED_PIPE_ITEM_PERSISTENT = RED_PIPE_ITEM_TYPE_CHANNEL_BASE,
> +};
> +
>  
> -typedef struct AudioFrame AudioFrame;
>  struct AudioFrame {
>      uint32_t time;
>      uint32_t samples[SND_CODEC_MAX_FRAME_SIZE];
>      PlaybackChannelClient *client;
>      AudioFrame *next;
> +    AudioFrameContainer *container;
> +    gboolean allocated;
> +};
> +
> +#define NUM_AUDIO_FRAMES 3
> +struct AudioFrameContainer
> +{
> +    int refs;
> +    AudioFrame items[NUM_AUDIO_FRAMES];
>  };
>  
> +#define TYPE_PLAYBACK_CHANNEL_CLIENT playback_channel_client_get_type()
> +#define PLAYBACK_CHANNEL_CLIENT(obj) \
> +    (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_PLAYBACK_CHANNEL_CLIENT, PlaybackChannelClient))
> +GType playback_channel_client_get_type(void) G_GNUC_CONST;
> +
>  struct PlaybackChannelClient {
> -    SndChannelClient base;
> -    AudioFrame frames[3];
> +    SndChannelClient parent;
> +
> +    AudioFrameContainer *frames;
>      AudioFrame *free_frames;
>      AudioFrame *in_progress;   /* Frame being sent to the client */
>      AudioFrame *pending_frame; /* Next frame to send to the client */
> @@ -138,6 +147,11 @@ struct PlaybackChannelClient {
>      uint8_t  encode_buf[SND_CODEC_MAX_COMPRESSED_BYTES];
>  };
>  
> +typedef SndChannelClientClass PlaybackChannelClientClass;
> +
> +G_DEFINE_TYPE(PlaybackChannelClient, playback_channel_client, TYPE_SND_CHANNEL_CLIENT)
> +
> +
>  typedef struct SpiceVolumeState {
>      uint16_t *volume;
>      uint8_t volume_nchannels;
> @@ -165,9 +179,6 @@ typedef RedChannelClass SndChannelClass;
>  G_DEFINE_TYPE(SndChannel, snd_channel, RED_TYPE_CHANNEL)
>  
>  
> -typedef struct SpicePlaybackState PlaybackChannel;
> -typedef SndChannelClass PlaybackChannelClass;
> -
>  #define TYPE_PLAYBACK_CHANNEL playback_channel_get_type()
>  #define PLAYBACK_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_PLAYBACK_CHANNEL, PlaybackChannel))
>  GType playback_channel_get_type(void) G_GNUC_CONST;
> @@ -176,12 +187,11 @@ struct SpicePlaybackState {
>      SndChannel channel;
>  };
>  
> +typedef SndChannelClass PlaybackChannelClass;
> +
>  G_DEFINE_TYPE(PlaybackChannel, playback_channel, TYPE_SND_CHANNEL)
>  
>  
> -typedef struct SpiceRecordState RecordChannel;
> -typedef SndChannelClass RecordChannelClass;
> -
>  #define TYPE_RECORD_CHANNEL record_channel_get_type()
>  #define RECORD_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_RECORD_CHANNEL, RecordChannel))
>  GType record_channel_get_type(void) G_GNUC_CONST;
> @@ -190,11 +200,18 @@ struct SpiceRecordState {
>      SndChannel channel;
>  };
>  
> +typedef SndChannelClass RecordChannelClass;
> +
>  G_DEFINE_TYPE(RecordChannel, record_channel, TYPE_SND_CHANNEL)
>  
>  
> -typedef struct RecordChannelClient {
> -    SndChannelClient base;
> +#define TYPE_RECORD_CHANNEL_CLIENT record_channel_client_get_type()
> +#define RECORD_CHANNEL_CLIENT(obj) \
> +    (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_RECORD_CHANNEL_CLIENT, RecordChannelClient))
> +GType record_channel_client_get_type(void) G_GNUC_CONST;
> +
> +struct RecordChannelClient {
> +    SndChannelClient parent;
>      uint32_t samples[RECORD_SAMPLES_SIZE];
>      uint32_t write_pos;
>      uint32_t read_pos;
> @@ -203,64 +220,23 @@ typedef struct RecordChannelClient {
>      uint32_t start_time;
>      SndCodec codec;
>      uint8_t  decode_buf[SND_CODEC_MAX_FRAME_BYTES];
> -} RecordChannelClient;
> +};
> +
> +typedef SndChannelClientClass RecordChannelClientClass;
> +
> +G_DEFINE_TYPE(RecordChannelClient, record_channel_client, TYPE_SND_CHANNEL_CLIENT)
> +
>  
>  /* A list of all Spice{Playback,Record}State objects */
>  static SndChannel *snd_channels;
>  
> -static void snd_receive(SndChannelClient *client);
>  static void snd_playback_start(SndChannel *channel);
>  static void snd_record_start(SndChannel *channel);
>  
> -static SndChannelClient *snd_channel_ref(SndChannelClient *client)
> -{
> -    client->refs++;
> -    return client;
> -}
> -
> -static SndChannelClient *snd_channel_unref(SndChannelClient *client)
> -{
> -    if (!--client->refs) {
> -        spice_printerr("SndChannelClient=%p freed", client);
> -        free(client);
> -        return NULL;
> -    }
> -    return client;
> -}
> -
>  static RedsState* snd_channel_get_server(SndChannelClient *client)
>  {
>      g_return_val_if_fail(client != NULL, NULL);
> -    return red_channel_get_server(RED_CHANNEL(client->channel));
> -}
> -
> -static void snd_disconnect_channel(SndChannelClient *client)
> -{
> -    SndChannel *channel;
> -    RedsState *reds;
> -    RedChannel *red_channel;
> -    uint32_t type;
> -
> -    if (!client || !client->stream) {
> -        spice_debug("not connected");
> -        return;
> -    }
> -    red_channel = red_channel_client_get_channel(client->channel_client);
> -    reds = snd_channel_get_server(client);
> -    g_object_get(red_channel, "channel-type", &type, NULL);
> -    spice_debug("SndChannelClient=%p rcc=%p type=%d",
> -                 client, client->channel_client, type);
> -    channel = client->channel;
> -    client->cleanup(client);
> -    red_channel_client_disconnect(channel->connection->channel_client);
> -    channel->connection->channel_client = NULL;
> -    reds_core_watch_remove(reds, client->stream->watch);
> -    client->stream->watch = NULL;
> -    reds_stream_free(client->stream);
> -    client->stream = NULL;
> -    spice_marshaller_destroy(client->send_data.marshaller);
> -    snd_channel_unref(client);
> -    channel->connection = NULL;
> +    return red_channel_get_server(red_channel_client_get_channel(RED_CHANNEL_CLIENT(client)));
>  }
>  
>  static void snd_playback_free_frame(PlaybackChannelClient *playback_client, AudioFrame *frame)
> @@ -282,65 +258,6 @@ static void snd_playback_on_message_done(SndChannelClient *client)
>      }
>  }
>  
> -static void snd_record_on_message_done(SndChannelClient *client)
> -{
> -}
> -
> -static int snd_send_data(SndChannelClient *client)
> -{
> -    uint32_t n;
> -
> -    if (!client) {
> -        return FALSE;
> -    }
> -
> -    if (!(n = client->send_data.size - client->send_data.pos)) {
> -        return TRUE;
> -    }
> -
> -    RedsState *reds = snd_channel_get_server(client);
> -    for (;;) {
> -        struct iovec vec[IOV_MAX];
> -        int vec_size;
> -
> -        if (!n) {
> -            client->on_message_done(client);
> -
> -            if (client->blocked) {
> -                client->blocked = FALSE;
> -                reds_core_watch_update_mask(reds, client->stream->watch, SPICE_WATCH_EVENT_READ);
> -            }
> -            break;
> -        }
> -
> -        vec_size = spice_marshaller_fill_iovec(client->send_data.marshaller,
> -                                               vec, IOV_MAX, client->send_data.pos);
> -        n = reds_stream_writev(client->stream, vec, vec_size);
> -        if (n == -1) {
> -            switch (errno) {
> -            case EAGAIN:
> -                client->blocked = TRUE;
> -                reds_core_watch_update_mask(reds, client->stream->watch, SPICE_WATCH_EVENT_READ |
> -                                                                 SPICE_WATCH_EVENT_WRITE);
> -                return FALSE;
> -            case EINTR:
> -                break;
> -            case EPIPE:
> -                snd_disconnect_channel(client);
> -                return FALSE;
> -            default:
> -                spice_printerr("%s", strerror(errno));
> -                snd_disconnect_channel(client);
> -                return FALSE;
> -            }
> -        } else {
> -            client->send_data.pos += n;
> -        }
> -        n = client->send_data.size - client->send_data.pos;
> -    }
> -    return TRUE;
> -}
> -
>  static int snd_record_handle_write(RecordChannelClient *record_client, size_t size, void *message)
>  {
>      SpiceMsgcRecordPacket *packet;
> @@ -386,35 +303,29 @@ static int snd_record_handle_write(RecordChannelClient *record_client, size_t si
>      return TRUE;
>  }
>  
> -static int snd_playback_handle_message(SndChannelClient *client, size_t size, uint32_t type, void *message)
> +static int
> +playback_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type, void *message)
>  {
> -    if (!client) {
> -        return FALSE;
> -    }
> -
>      switch (type) {
>      case SPICE_MSGC_DISCONNECTING:
>          break;
>      default:
> -        spice_printerr("invalid message type %u", type);
> -        return FALSE;
> +        return red_channel_client_handle_message(rcc, size, type, message);
>      }
>      return TRUE;
>  }
>  
> -static int snd_record_handle_message(SndChannelClient *client, size_t size, uint32_t type, void *message)
> +static int
> +record_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type, void *message)
>  {
> -    RecordChannelClient *record_client = (RecordChannelClient *)client;
> +    RecordChannelClient *record_client = (RecordChannelClient *)rcc; // FIXME
>  
> -    if (!client) {
> -        return FALSE;
> -    }
>      switch (type) {
>      case SPICE_MSGC_RECORD_DATA:
> -        return snd_record_handle_write((RecordChannelClient *)client, size, message);
> +        return snd_record_handle_write(record_client, size, message);
>      case SPICE_MSGC_RECORD_MODE: {
>          SpiceMsgcRecordMode *mode = (SpiceMsgcRecordMode *)message;
> -        SndChannel *channel = client->channel;
> +        SndChannel *channel = SND_CHANNEL(red_channel_client_get_channel(rcc));
>          record_client->mode_time = mode->time;
>          if (mode->mode != SPICE_AUDIO_DATA_MODE_RAW) {
>              if (snd_codec_is_capable(mode->mode, channel->frequency)) {
> @@ -444,260 +355,134 @@ static int snd_record_handle_message(SndChannelClient *client, size_t size, uint
>      case SPICE_MSGC_DISCONNECTING:
>          break;
>      default:
> -        spice_printerr("invalid message type %u", type);
> -        return FALSE;
> -    }
> -    return TRUE;
> -}
> -
> -static void snd_receive(SndChannelClient *client)
> -{
> -    SpiceDataHeaderOpaque *header;
> -
> -    if (!client) {
> -        return;
> -    }
> -
> -    header = &client->channel_client->incoming.header;
> -
> -    for (;;) {
> -        ssize_t n;
> -        n = client->receive_data.end - client->receive_data.now;
> -        spice_warn_if_fail(n > 0);
> -        n = reds_stream_read(client->stream, client->receive_data.now, n);
> -        if (n <= 0) {
> -            if (n == 0) {
> -                snd_disconnect_channel(client);
> -                return;
> -            }
> -            spice_assert(n == -1);
> -            switch (errno) {
> -            case EAGAIN:
> -                return;
> -            case EINTR:
> -                break;
> -            case EPIPE:
> -                snd_disconnect_channel(client);
> -                return;
> -            default:
> -                spice_printerr("%s", strerror(errno));
> -                snd_disconnect_channel(client);
> -                return;
> -            }
> -        } else {
> -            client->receive_data.now += n;
> -            for (;;) {
> -                uint8_t *msg_start = client->receive_data.message_start;
> -                uint8_t *data = msg_start + header->header_size;
> -                size_t parsed_size;
> -                uint8_t *parsed;
> -                message_destructor_t parsed_free;
> -
> -                header->data = msg_start;
> -                n = client->receive_data.now - msg_start;
> -
> -                if (n < header->header_size ||
> -                    n < header->header_size + header->get_msg_size(header)) {
> -                    break;
> -                }
> -                parsed = client->parser((void *)data, data + header->get_msg_size(header),
> -                                         header->get_msg_type(header),
> -                                         SPICE_VERSION_MINOR, &parsed_size, &parsed_free);
> -                if (parsed == NULL) {
> -                    spice_printerr("failed to parse message type %d", header->get_msg_type(header));
> -                    snd_disconnect_channel(client);
> -                    return;
> -                }
> -                if (!client->handle_message(client, parsed_size,
> -                                             header->get_msg_type(header), parsed)) {
> -                    free(parsed);
> -                    snd_disconnect_channel(client);
> -                    return;
> -                }
> -                parsed_free(parsed);
> -                client->receive_data.message_start = msg_start + header->header_size +
> -                                                     header->get_msg_size(header);
> -            }
> -            if (client->receive_data.now == client->receive_data.message_start) {
> -                client->receive_data.now = client->receive_data.buf;
> -                client->receive_data.message_start = client->receive_data.buf;
> -            } else if (client->receive_data.now == client->receive_data.end) {
> -                memcpy(client->receive_data.buf, client->receive_data.message_start, n);
> -                client->receive_data.now = client->receive_data.buf + n;
> -                client->receive_data.message_start = client->receive_data.buf;
> -            }
> -        }
> -    }
> -}
> -
> -static void snd_event(int fd, int event, void *data)
> -{
> -    SndChannelClient *client = data;
> -
> -    if (event & SPICE_WATCH_EVENT_READ) {
> -        snd_receive(client);
> -    }
> -    if (event & SPICE_WATCH_EVENT_WRITE) {
> -        client->send_messages(client);
> +        return red_channel_client_handle_message(rcc, size, type, message);
>      }
> -}
> -
> -static inline int snd_reset_send_data(SndChannelClient *client, uint16_t verb)
> -{
> -    SpiceDataHeaderOpaque *header;
> -
> -    if (!client) {
> -        return FALSE;
> -    }
> -
> -    header = &client->channel_client->priv->send_data.header;
> -    spice_marshaller_reset(client->send_data.marshaller);
> -    header->data = spice_marshaller_reserve_space(client->send_data.marshaller,
> -                                                  header->header_size);
> -    spice_marshaller_set_base(client->send_data.marshaller,
> -                              header->header_size);
> -    client->send_data.pos = 0;
> -    header->set_msg_size(header, 0);
> -    header->set_msg_type(header, verb);
> -    client->send_data.serial++;
> -    if (!client->channel_client->priv->is_mini_header) {
> -        header->set_msg_serial(header, client->send_data.serial);
> -        header->set_msg_sub_list(header, 0);
> -    }
> -
>      return TRUE;
>  }
>  
> -static int snd_begin_send_message(SndChannelClient *client)
> -{
> -    SpiceDataHeaderOpaque *header = &client->channel_client->priv->send_data.header;
> -
> -    spice_marshaller_flush(client->send_data.marshaller);
> -    client->send_data.size = spice_marshaller_get_total_size(client->send_data.marshaller);
> -    header->set_msg_size(header, client->send_data.size - header->header_size);
> -    return snd_send_data(client);
> -}
> -
>  static int snd_channel_send_migrate(SndChannelClient *client)
>  {
> +    RedChannelClient *rcc = RED_CHANNEL_CLIENT(client);
> +    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
>      SpiceMsgMigrate migrate;
>  
> -    if (!snd_reset_send_data(client, SPICE_MSG_MIGRATE)) {
> -        return FALSE;
> -    }
> -    spice_debug(NULL);
> +    red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE, NULL);
>      migrate.flags = 0;
> -    spice_marshall_msg_migrate(client->send_data.marshaller, &migrate);
> +    spice_marshall_msg_migrate(m, &migrate);
>  
> -    return snd_begin_send_message(client);
> +    red_channel_client_begin_send_message(rcc);
> +    return FALSE;
>  }
>  
>  static int snd_playback_send_migrate(PlaybackChannelClient *client)
>  {
> -    return snd_channel_send_migrate(&client->base);
> +    return snd_channel_send_migrate(SND_CHANNEL_CLIENT(client));
>  }
>  
>  static int snd_send_volume(SndChannelClient *client, uint32_t cap, int msg)
>  {
>      SpiceMsgAudioVolume *vol;
>      uint8_t c;
> -    SpiceVolumeState *st = &client->channel->volume;
> +    RedChannelClient *rcc = RED_CHANNEL_CLIENT(client);
> +    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
> +    SndChannel *channel = SND_CHANNEL(red_channel_client_get_channel(rcc));
> +    SpiceVolumeState *st = &channel->volume;
>  
> -    if (!red_channel_client_test_remote_cap(client->channel_client, cap)) {
> +    if (!red_channel_client_test_remote_cap(rcc, cap)) {
>          return TRUE;
>      }
>  
>      vol = alloca(sizeof (SpiceMsgAudioVolume) +
>                   st->volume_nchannels * sizeof (uint16_t));
> -    if (!snd_reset_send_data(client, msg)) {
> -        return FALSE;
> -    }
> +    red_channel_client_init_send_data(rcc, msg, NULL);
>      vol->nchannels = st->volume_nchannels;
>      for (c = 0; c < st->volume_nchannels; ++c) {
>          vol->volume[c] = st->volume[c];
>      }
> -    spice_marshall_SpiceMsgAudioVolume(client->send_data.marshaller, vol);
> +    spice_marshall_SpiceMsgAudioVolume(m, vol);
>  
> -    return snd_begin_send_message(client);
> +    red_channel_client_begin_send_message(rcc);
> +    return FALSE;
>  }
>  
>  static int snd_playback_send_volume(PlaybackChannelClient *playback_client)
>  {
> -    return snd_send_volume(&playback_client->base, SPICE_PLAYBACK_CAP_VOLUME,
> +    return snd_send_volume(SND_CHANNEL_CLIENT(playback_client), SPICE_PLAYBACK_CAP_VOLUME,
>                             SPICE_MSG_PLAYBACK_VOLUME);
>  }
>  
>  static int snd_send_mute(SndChannelClient *client, uint32_t cap, int msg)
>  {
>      SpiceMsgAudioMute mute;
> -    SpiceVolumeState *st = &client->channel->volume;
> +    RedChannelClient *rcc = RED_CHANNEL_CLIENT(client);
> +    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
> +    SndChannel *channel = SND_CHANNEL(red_channel_client_get_channel(rcc));
> +    SpiceVolumeState *st = &channel->volume;
>  
> -    if (!red_channel_client_test_remote_cap(client->channel_client, cap)) {
> +    if (!red_channel_client_test_remote_cap(rcc, cap)) {
>          return TRUE;
>      }
>  
> -    if (!snd_reset_send_data(client, msg)) {
> -        return FALSE;
> -    }
> +    red_channel_client_init_send_data(rcc, msg, NULL);
>      mute.mute = st->mute;
> -    spice_marshall_SpiceMsgAudioMute(client->send_data.marshaller, &mute);
> +    spice_marshall_SpiceMsgAudioMute(m, &mute);
>  
> -    return snd_begin_send_message(client);
> +    red_channel_client_begin_send_message(rcc);
> +    return FALSE;
>  }
>  
>  static int snd_playback_send_mute(PlaybackChannelClient *playback_client)
>  {
> -    return snd_send_mute(&playback_client->base, SPICE_PLAYBACK_CAP_VOLUME,
> +    return snd_send_mute(SND_CHANNEL_CLIENT(playback_client), SPICE_PLAYBACK_CAP_VOLUME,
>                           SPICE_MSG_PLAYBACK_MUTE);
>  }
>  
>  static int snd_playback_send_latency(PlaybackChannelClient *playback_client)
>  {
> -    SndChannelClient *client = &playback_client->base;
> +    RedChannelClient *rcc = RED_CHANNEL_CLIENT(playback_client);
> +    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
>      SpiceMsgPlaybackLatency latency_msg;
>  
>      spice_debug("latency %u", playback_client->latency);
> -    if (!snd_reset_send_data(client, SPICE_MSG_PLAYBACK_LATENCY)) {
> -        return FALSE;
> -    }
> +    red_channel_client_init_send_data(rcc, SPICE_MSG_PLAYBACK_LATENCY, NULL);
>      latency_msg.latency_ms = playback_client->latency;
> -    spice_marshall_msg_playback_latency(client->send_data.marshaller, &latency_msg);
> +    spice_marshall_msg_playback_latency(m, &latency_msg);
>  
> -    return snd_begin_send_message(client);
> +    red_channel_client_begin_send_message(rcc);
> +    return FALSE;
>  }
> +
>  static int snd_playback_send_start(PlaybackChannelClient *playback_client)
>  {
> -    SndChannelClient *client = (SndChannelClient *)playback_client;
> +    RedChannelClient *rcc = RED_CHANNEL_CLIENT(playback_client);
> +    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
>      SpiceMsgPlaybackStart start;
>  
> -    if (!snd_reset_send_data(client, SPICE_MSG_PLAYBACK_START)) {
> -        return FALSE;
> -    }
> -
> +    red_channel_client_init_send_data(rcc, SPICE_MSG_PLAYBACK_START, NULL);
>      start.channels = SPICE_INTERFACE_PLAYBACK_CHAN;
> -    start.frequency = client->channel->frequency;
> +    start.frequency = SND_CHANNEL(red_channel_client_get_channel(rcc))->frequency;
>      spice_assert(SPICE_INTERFACE_PLAYBACK_FMT == SPICE_INTERFACE_AUDIO_FMT_S16);
>      start.format = SPICE_AUDIO_FMT_S16;
>      start.time = reds_get_mm_time();
> -    spice_marshall_msg_playback_start(client->send_data.marshaller, &start);
> +    spice_marshall_msg_playback_start(m, &start);
>  
> -    return snd_begin_send_message(client);
> +    red_channel_client_begin_send_message(rcc);
> +    return FALSE;
>  }
>  
>  static int snd_playback_send_stop(PlaybackChannelClient *playback_client)
>  {
> -    SndChannelClient *client = (SndChannelClient *)playback_client;
> +    RedChannelClient *rcc = RED_CHANNEL_CLIENT(playback_client);
>  
> -    if (!snd_reset_send_data(client, SPICE_MSG_PLAYBACK_STOP)) {
> -        return FALSE;
> -    }
> +    red_channel_client_init_send_data(rcc, SPICE_MSG_PLAYBACK_STOP, NULL);
>  
> -    return snd_begin_send_message(client);
> +    red_channel_client_begin_send_message(rcc);
> +    return FALSE;
>  }
>  
>  static int snd_playback_send_ctl(PlaybackChannelClient *playback_client)
>  {
> -    SndChannelClient *client = (SndChannelClient *)playback_client;
> +    SndChannelClient *client = SND_CHANNEL_CLIENT(playback_client);
>  
>      if ((client->client_active = client->active)) {
>          return snd_playback_send_start(playback_client);
> @@ -708,36 +493,35 @@ static int snd_playback_send_ctl(PlaybackChannelClient *playback_client)
>  
>  static int snd_record_send_start(RecordChannelClient *record_client)
>  {
> -    SndChannelClient *client = (SndChannelClient *)record_client;
> +    RedChannelClient *rcc = RED_CHANNEL_CLIENT(record_client);
> +    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
>      SpiceMsgRecordStart start;
>  
> -    if (!snd_reset_send_data(client, SPICE_MSG_RECORD_START)) {
> -        return FALSE;
> -    }
> +    red_channel_client_init_send_data(rcc, SPICE_MSG_RECORD_START, NULL);
>  
>      start.channels = SPICE_INTERFACE_RECORD_CHAN;
> -    start.frequency = client->channel->frequency;
> +    start.frequency = SND_CHANNEL(red_channel_client_get_channel(rcc))->frequency;
>      spice_assert(SPICE_INTERFACE_RECORD_FMT == SPICE_INTERFACE_AUDIO_FMT_S16);
>      start.format = SPICE_AUDIO_FMT_S16;
> -    spice_marshall_msg_record_start(client->send_data.marshaller, &start);
> +    spice_marshall_msg_record_start(m, &start);
>  
> -    return snd_begin_send_message(client);
> +    red_channel_client_begin_send_message(rcc);
> +    return FALSE;
>  }
>  
>  static int snd_record_send_stop(RecordChannelClient *record_client)
>  {
> -    SndChannelClient *client = (SndChannelClient *)record_client;
> +    RedChannelClient *rcc = RED_CHANNEL_CLIENT(record_client);
>  
> -    if (!snd_reset_send_data(client, SPICE_MSG_RECORD_STOP)) {
> -        return FALSE;
> -    }
> +    red_channel_client_init_send_data(rcc, SPICE_MSG_RECORD_STOP, NULL);
>  
> -    return snd_begin_send_message(client);
> +    red_channel_client_begin_send_message(rcc);
> +    return FALSE;
>  }
>  
>  static int snd_record_send_ctl(RecordChannelClient *record_client)
>  {
> -    SndChannelClient *client = (SndChannelClient *)record_client;
> +    SndChannelClient *client = SND_CHANNEL_CLIENT(record_client);
>  
>      if ((client->client_active = client->active)) {
>          return snd_record_send_start(record_client);
> @@ -748,13 +532,13 @@ static int snd_record_send_ctl(RecordChannelClient *record_client)
>  
>  static int snd_record_send_volume(RecordChannelClient *record_client)
>  {
> -    return snd_send_volume(&record_client->base, SPICE_RECORD_CAP_VOLUME,
> +    return snd_send_volume(SND_CHANNEL_CLIENT(record_client), SPICE_RECORD_CAP_VOLUME,
>                             SPICE_MSG_RECORD_VOLUME);
>  }
>  
>  static int snd_record_send_mute(RecordChannelClient *record_client)
>  {
> -    return snd_send_mute(&record_client->base, SPICE_RECORD_CAP_VOLUME,
> +    return snd_send_mute(SND_CHANNEL_CLIENT(record_client), SPICE_RECORD_CAP_VOLUME,
>                           SPICE_MSG_RECORD_MUTE);
>  }
>  
> @@ -764,27 +548,25 @@ static int snd_record_send_migrate(RecordChannelClient *record_client)
>       * the client receives RECORD_STOP from the src before the migration completion
>       * notification (when the vm is stopped).
>       * Afterwards, when the vm starts on the dest, the client receives RECORD_START. */
> -    return snd_channel_send_migrate(&record_client->base);
> +    return snd_channel_send_migrate(SND_CHANNEL_CLIENT(record_client));
>  }
>  
>  static int snd_playback_send_write(PlaybackChannelClient *playback_client)
>  {
> -    SndChannelClient *client = (SndChannelClient *)playback_client;
> +    RedChannelClient *rcc = RED_CHANNEL_CLIENT(playback_client);
> +    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
>      AudioFrame *frame;
>      SpiceMsgPlaybackPacket msg;
>  
> -    if (!snd_reset_send_data(client, SPICE_MSG_PLAYBACK_DATA)) {
> -        return FALSE;
> -    }
> +    red_channel_client_init_send_data(rcc, SPICE_MSG_PLAYBACK_DATA, NULL);
>  
>      frame = playback_client->in_progress;
>      msg.time = frame->time;
>  
> -    spice_marshall_msg_playback_data(client->send_data.marshaller, &msg);
> +    spice_marshall_msg_playback_data(m, &msg);
>  
>      if (playback_client->mode == SPICE_AUDIO_DATA_MODE_RAW) {
> -        spice_marshaller_add_ref(client->send_data.marshaller,
> -                                 (uint8_t *)frame->samples,
> +        spice_marshaller_add_ref(m, (uint8_t *)frame->samples,
>                                   snd_codec_frame_size(playback_client->codec) * sizeof(frame->samples[0]));
>      }
>      else {
> @@ -793,45 +575,70 @@ static int snd_playback_send_write(PlaybackChannelClient *playback_client)
>                                      snd_codec_frame_size(playback_client->codec) * sizeof(frame->samples[0]),
>                                      playback_client->encode_buf, &n) != SND_CODEC_OK) {
>              spice_printerr("encode failed");
> -            snd_disconnect_channel(client);
> -            return FALSE;
> +            red_channel_client_disconnect(rcc);
> +            return TRUE;
>          }
> -        spice_marshaller_add_ref(client->send_data.marshaller, playback_client->encode_buf, n);
> +        spice_marshaller_add_ref(m, playback_client->encode_buf, n);
>      }
>  
> -    return snd_begin_send_message(client);
> +    red_channel_client_begin_send_message(rcc);
> +    return FALSE;
>  }
>  
>  static int playback_send_mode(PlaybackChannelClient *playback_client)
>  {
> -    SndChannelClient *client = (SndChannelClient *)playback_client;
> +    RedChannelClient *rcc = RED_CHANNEL_CLIENT(playback_client);
> +    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
>      SpiceMsgPlaybackMode mode;
>  
> -    if (!snd_reset_send_data(client, SPICE_MSG_PLAYBACK_MODE)) {
> -        return FALSE;
> -    }
> +    red_channel_client_init_send_data(rcc, SPICE_MSG_PLAYBACK_MODE, NULL);
>      mode.time = reds_get_mm_time();
>      mode.mode = playback_client->mode;
> -    spice_marshall_msg_playback_mode(client->send_data.marshaller, &mode);
> +    spice_marshall_msg_playback_mode(m, &mode);
>  
> -    return snd_begin_send_message(client);
> +    red_channel_client_begin_send_message(rcc);
> +    return FALSE;
>  }
>  
> -static void snd_playback_send(void* data)
> +static void snd_persistent_pipe_item_free(struct RedPipeItem *item)
>  {
> -    PlaybackChannelClient *playback_client = (PlaybackChannelClient*)data;
> -    SndChannelClient *client = (SndChannelClient*)playback_client;
> +    SndChannelClient *client = SPICE_CONTAINEROF(item, SndChannelClient, persistent_pipe_item);
> +
> +    red_pipe_item_init_full(item, RED_PIPE_ITEM_PERSISTENT,
> +                            snd_persistent_pipe_item_free);
> +
> +    if (client->on_message_done) {
> +        client->on_message_done(client);
> +    }
> +}
>  
> -    if (!playback_client || !snd_send_data(data)) {
> +static void snd_send(SndChannelClient * client)
> +{
> +    RedChannelClient *rcc = RED_CHANNEL_CLIENT(client);
> +
> +    if (!client || !red_channel_client_pipe_is_empty(rcc) || !client->command) {
>          return;
>      }
> +    // just append a dummy item and push!
> +    red_pipe_item_init_full(&client->persistent_pipe_item, RED_PIPE_ITEM_PERSISTENT,
> +                            snd_persistent_pipe_item_free);
> +    red_channel_client_pipe_add_push(rcc, &client->persistent_pipe_item);
> +}
>  
> +static void playback_channel_send_item(RedChannelClient *rcc, RedPipeItem *base)
> +{
> +    PlaybackChannelClient *playback_client = PLAYBACK_CHANNEL_CLIENT(rcc);
> +    SndChannelClient *client = SND_CHANNEL_CLIENT(rcc);
> +
> +    client->command &= SND_PLAYBACK_MODE_MASK|SND_PLAYBACK_PCM_MASK|
> +                       SND_CTRL_MASK|SND_VOLUME_MUTE_MASK|
> +                       SND_MIGRATE_MASK|SND_PLAYBACK_LATENCY_MASK;
>      while (client->command) {
>          if (client->command & SND_PLAYBACK_MODE_MASK) {
> +            client->command &= ~SND_PLAYBACK_MODE_MASK;
>              if (!playback_send_mode(playback_client)) {
> -                return;
> +                break;
>              }
> -            client->command &= ~SND_PLAYBACK_MODE_MASK;
>          }
>          if (client->command & SND_PLAYBACK_PCM_MASK) {
>              spice_assert(!playback_client->in_progress && playback_client->pending_frame);
> @@ -839,163 +646,77 @@ static void snd_playback_send(void* data)
>              playback_client->pending_frame = NULL;
>              client->command &= ~SND_PLAYBACK_PCM_MASK;
>              if (!snd_playback_send_write(playback_client)) {
> -                spice_printerr("snd_send_playback_write failed");
> -                return;
> +                break;
>              }
> +            spice_printerr("snd_send_playback_write failed");
>          }
>          if (client->command & SND_CTRL_MASK) {
> +            client->command &= ~SND_CTRL_MASK;
>              if (!snd_playback_send_ctl(playback_client)) {
> -                return;
> +                break;
>              }
> -            client->command &= ~SND_CTRL_MASK;
>          }
>          if (client->command & SND_VOLUME_MASK) {
> -            if (!snd_playback_send_volume(playback_client) ||
> -                !snd_playback_send_mute(playback_client)) {
> -                return;
> -            }
>              client->command &= ~SND_VOLUME_MASK;
> +            if (!snd_playback_send_volume(playback_client)) {
> +                break;
> +            }
> +        }
> +        if (client->command & SND_MUTE_MASK) {
> +            client->command &= ~SND_MUTE_MASK;
> +            if (!snd_playback_send_mute(playback_client)) {
> +                break;
> +            }
>          }
>          if (client->command & SND_MIGRATE_MASK) {
> +            client->command &= ~SND_MIGRATE_MASK;
>              if (!snd_playback_send_migrate(playback_client)) {
> -                return;
> +                break;
>              }
> -            client->command &= ~SND_MIGRATE_MASK;
>          }
>          if (client->command & SND_PLAYBACK_LATENCY_MASK) {
> +            client->command &= ~SND_PLAYBACK_LATENCY_MASK;
>              if (!snd_playback_send_latency(playback_client)) {
> -                return;
> +                break;
>              }
> -            client->command &= ~SND_PLAYBACK_LATENCY_MASK;
>          }
>      }
> +    snd_send(client);
>  }
>  
> -static void snd_record_send(void* data)
> +static void record_channel_send_item(RedChannelClient *rcc, RedPipeItem *base)
>  {
> -    RecordChannelClient *record_client = (RecordChannelClient*)data;
> -    SndChannelClient *client = (SndChannelClient*)record_client;
> -
> -    if (!record_client || !snd_send_data(data)) {
> -        return;
> -    }
> +    RecordChannelClient *record_client = RECORD_CHANNEL_CLIENT(rcc);
> +    SndChannelClient *client = SND_CHANNEL_CLIENT(rcc);
>  
> +    client->command &= SND_CTRL_MASK|SND_VOLUME_MUTE_MASK|SND_MIGRATE_MASK;
>      while (client->command) {
>          if (client->command & SND_CTRL_MASK) {
> +            client->command &= ~SND_CTRL_MASK;
>              if (!snd_record_send_ctl(record_client)) {
> -                return;
> +                break;
>              }
> -            client->command &= ~SND_CTRL_MASK;
>          }
>          if (client->command & SND_VOLUME_MASK) {
> -            if (!snd_record_send_volume(record_client) ||
> -                !snd_record_send_mute(record_client)) {
> -                return;
> -            }
>              client->command &= ~SND_VOLUME_MASK;
> +            if (!snd_record_send_volume(record_client)) {
> +                break;
> +            }
> +        }
> +        if (client->command & SND_MUTE_MASK) {
> +            client->command &= ~SND_MUTE_MASK;
> +            if (!snd_record_send_mute(record_client)) {
> +                break;
> +            }
>          }
>          if (client->command & SND_MIGRATE_MASK) {
> +            client->command &= ~SND_MIGRATE_MASK;
>              if (!snd_record_send_migrate(record_client)) {
> -                return;
> +                break;
>              }
> -            client->command &= ~SND_MIGRATE_MASK;
> -        }
> -    }
> -}
> -
> -static SndChannelClient *__new_channel(SndChannel *channel, int size, uint32_t channel_id,
> -                                 RedClient *red_client,
> -                                 RedsStream *stream,
> -                                 int migrate,
> -                                 snd_channel_send_messages_proc send_messages,
> -                                 snd_channel_handle_message_proc handle_message,
> -                                 snd_channel_on_message_done_proc on_message_done,
> -                                 snd_channel_cleanup_channel_proc cleanup,
> -                                 uint32_t *common_caps, int num_common_caps,
> -                                 uint32_t *caps, int num_caps)
> -{
> -    SndChannelClient *client;
> -    int delay_val;
> -    int flags;
> -#ifdef SO_PRIORITY
> -    int priority;
> -#endif
> -    int tos;
> -    MainChannelClient *mcc = red_client_get_main(red_client);
> -    RedsState *reds = red_channel_get_server(RED_CHANNEL(channel));
> -
> -    spice_assert(stream);
> -    if ((flags = fcntl(stream->socket, F_GETFL)) == -1) {
> -        spice_printerr("accept failed, %s", strerror(errno));
> -        goto error1;
> -    }
> -
> -#ifdef SO_PRIORITY
> -    priority = 6;
> -    if (setsockopt(stream->socket, SOL_SOCKET, SO_PRIORITY, (void*)&priority,
> -                   sizeof(priority)) == -1) {
> -        if (errno != ENOTSUP) {
> -            spice_printerr("setsockopt failed, %s", strerror(errno));
>          }
>      }
> -#endif
> -
> -    tos = IPTOS_LOWDELAY;
> -    if (setsockopt(stream->socket, IPPROTO_IP, IP_TOS, (void*)&tos, sizeof(tos)) == -1) {
> -        if (errno != ENOTSUP) {
> -            spice_printerr("setsockopt failed, %s", strerror(errno));
> -        }
> -    }
> -
> -    delay_val = main_channel_client_is_low_bandwidth(mcc) ? 0 : 1;
> -    if (setsockopt(stream->socket, IPPROTO_TCP, TCP_NODELAY, &delay_val, sizeof(delay_val)) == -1) {
> -        if (errno != ENOTSUP) {
> -            spice_printerr("setsockopt failed, %s", strerror(errno));
> -        }
> -    }
> -
> -    if (fcntl(stream->socket, F_SETFL, flags | O_NONBLOCK) == -1) {
> -        spice_printerr("accept failed, %s", strerror(errno));
> -        goto error1;
> -    }
> -
> -    spice_assert(size >= sizeof(*client));
> -    client = spice_malloc0(size);
> -    client->refs = 1;
> -    client->parser = spice_get_client_channel_parser(channel_id, NULL);
> -    client->stream = stream;
> -    client->channel = channel;
> -    client->receive_data.message_start = client->receive_data.buf;
> -    client->receive_data.now = client->receive_data.buf;
> -    client->receive_data.end = client->receive_data.buf + sizeof(client->receive_data.buf);
> -    client->send_data.marshaller = spice_marshaller_new();
> -
> -    stream->watch = reds_core_watch_add(reds, stream->socket, SPICE_WATCH_EVENT_READ,
> -                                        snd_event, client);
> -    if (stream->watch == NULL) {
> -        spice_printerr("watch_add failed, %s", strerror(errno));
> -        goto error2;
> -    }
> -
> -    client->send_messages = send_messages;
> -    client->handle_message = handle_message;
> -    client->on_message_done = on_message_done;
> -    client->cleanup = cleanup;
> -
> -    client->channel_client =
> -        dummy_channel_client_create(RED_CHANNEL(channel), red_client,
> -                                    num_common_caps, common_caps, num_caps, caps);
> -    if (!client->channel_client) {
> -        goto error2;
> -    }
> -    return client;
> -
> -error2:
> -    free(client);
> -
> -error1:
> -    reds_stream_free(stream);
> -    return NULL;
> +    snd_send(client);
>  }
>  
>  static int snd_channel_config_socket(RedChannelClient *rcc)
> @@ -1047,6 +768,22 @@ static int snd_channel_config_socket(RedChannelClient *rcc)
>      return TRUE;
>  }
>  
> +static uint8_t*
> +snd_channel_client_alloc_recv_buf(RedChannelClient *rcc, uint16_t type, uint32_t size)
> +{
> +    SndChannelClient *client = SND_CHANNEL_CLIENT(rcc);
> +    // TODO isn't too much the buffer ?? return NULL ??
> +    spice_assert(size < sizeof(client->receive_buf));
> +    return client->receive_buf;
> +}
> +
> +static void
> +snd_channel_client_release_recv_buf(RedChannelClient *rcc, uint16_t type, uint32_t size,
> +                                    uint8_t *msg)
> +{
> +}
> +
> +// TODO remove and just use red_channel_client_disconnect ??
>  static void snd_disconnect_channel_client(RedChannelClient *rcc)
>  {
>      SndChannel *channel;
> @@ -1060,8 +797,8 @@ static void snd_disconnect_channel_client(RedChannelClient *rcc)
>  
>      spice_debug("channel-type=%d", type);
>      if (channel->connection) {
> -        spice_assert(channel->connection->channel_client == rcc);
> -        snd_disconnect_channel(channel->connection);
> +        spice_assert(RED_CHANNEL_CLIENT(channel->connection) == rcc);
> +        red_channel_client_disconnect(rcc);
>      }
>  }
>  
> @@ -1079,7 +816,6 @@ SPICE_GNUC_VISIBLE void spice_server_playback_set_volume(SpicePlaybackInstance *
>  {
>      SpiceVolumeState *st = &sin->st->channel.volume;
>      SndChannelClient *client = sin->st->channel.connection;
> -    PlaybackChannelClient *playback_client = SPICE_CONTAINEROF(client, PlaybackChannelClient, base);
>  
>      st->volume_nchannels = nchannels;
>      free(st->volume);
> @@ -1088,21 +824,22 @@ SPICE_GNUC_VISIBLE void spice_server_playback_set_volume(SpicePlaybackInstance *
>      if (!client || nchannels == 0)
>          return;
>  
> -    snd_playback_send_volume(playback_client);
> +    snd_set_command(client, SND_VOLUME_MUTE_MASK);
> +    snd_send(client);
>  }
>  
>  SPICE_GNUC_VISIBLE void spice_server_playback_set_mute(SpicePlaybackInstance *sin, uint8_t mute)
>  {
>      SpiceVolumeState *st = &sin->st->channel.volume;
>      SndChannelClient *client = sin->st->channel.connection;
> -    PlaybackChannelClient *playback_client = SPICE_CONTAINEROF(client, PlaybackChannelClient, base);
>  
>      st->mute = mute;
>  
>      if (!client)
>          return;
>  
> -    snd_playback_send_mute(playback_client);
> +    snd_set_command(client, SND_VOLUME_MUTE_MASK);
> +    snd_send(client);
>  }
>  
>  static void snd_playback_start(SndChannel *channel)
> @@ -1117,7 +854,7 @@ static void snd_playback_start(SndChannel *channel)
>      client->active = TRUE;
>      if (!client->client_active) {
>          snd_set_command(client, SND_CTRL_MASK);
> -        snd_playback_send(client);
> +        snd_send(client);
>      } else {
>          client->command &= ~SND_CTRL_MASK;
>      }
> @@ -1131,20 +868,20 @@ SPICE_GNUC_VISIBLE void spice_server_playback_start(SpicePlaybackInstance *sin)
>  SPICE_GNUC_VISIBLE void spice_server_playback_stop(SpicePlaybackInstance *sin)
>  {
>      SndChannelClient *client = sin->st->channel.connection;
> -    PlaybackChannelClient *playback_client = SPICE_CONTAINEROF(client, PlaybackChannelClient, base);
>  
>      sin->st->channel.active = 0;
>      if (!client)
>          return;
> -    spice_assert(playback_client->base.active);
> +    PlaybackChannelClient *playback_client = PLAYBACK_CHANNEL_CLIENT(client);
> +    spice_assert(client->active);
>      reds_enable_mm_time(snd_channel_get_server(client));
> -    playback_client->base.active = FALSE;
> -    if (playback_client->base.client_active) {
> -        snd_set_command(&playback_client->base, SND_CTRL_MASK);
> -        snd_playback_send(&playback_client->base);
> +    client->active = FALSE;
> +    if (client->client_active) {
> +        snd_set_command(client, SND_CTRL_MASK);
> +        snd_send(client);
>      } else {
> -        playback_client->base.command &= ~SND_CTRL_MASK;
> -        playback_client->base.command &= ~SND_PLAYBACK_PCM_MASK;
> +        client->command &= ~SND_CTRL_MASK;
> +        client->command &= ~SND_PLAYBACK_PCM_MASK;
>  
>          if (playback_client->pending_frame) {
>              spice_assert(!playback_client->in_progress);
> @@ -1159,15 +896,21 @@ SPICE_GNUC_VISIBLE void spice_server_playback_get_buffer(SpicePlaybackInstance *
>                                                           uint32_t **frame, uint32_t *num_samples)
>  {
>      SndChannelClient *client = sin->st->channel.connection;
> -    PlaybackChannelClient *playback_client = SPICE_CONTAINEROF(client, PlaybackChannelClient, base);
>  
> -    if (!client || !playback_client->free_frames) {
> -        *frame = NULL;
> -        *num_samples = 0;
> +    *frame = NULL;
> +    *num_samples = 0;
> +    if (!client) {
> +        return;
> +    }
> +    PlaybackChannelClient *playback_client = PLAYBACK_CHANNEL_CLIENT(client);
> +    if (!playback_client->free_frames) {
>          return;
>      }
> -    spice_assert(playback_client->base.active);
> -    snd_channel_ref(client);
> +    spice_assert(client->active);
> +    if (!playback_client->free_frames->allocated) {
> +        playback_client->free_frames->allocated = TRUE;
> +        ++playback_client->frames->refs;
> +    }
>  
>      *frame = playback_client->free_frames->samples;
>      playback_client->free_frames = playback_client->free_frames->next;
> @@ -1180,23 +923,28 @@ SPICE_GNUC_VISIBLE void spice_server_playback_put_samples(SpicePlaybackInstance
>      AudioFrame *frame;
>  
>      frame = SPICE_CONTAINEROF(samples, AudioFrame, samples[0]);
> +    if (frame->allocated) {
> +        frame->allocated = FALSE;
> +        if (--frame->container->refs == 0) {
> +            free(frame->container);
> +            return;
> +        }
> +    }
>      playback_client = frame->client;
> -    spice_assert(playback_client);
> -    if (!snd_channel_unref(&playback_client->base) ||
> -        sin->st->channel.connection != &playback_client->base) {
> +    if (!playback_client || sin->st->channel.connection != SND_CHANNEL_CLIENT(playback_client)) {
>          /* lost last reference, client has been destroyed previously */
>          spice_info("audio samples belong to a disconnected client");
>          return;
>      }
> -    spice_assert(playback_client->base.active);
> +    spice_assert(SND_CHANNEL_CLIENT(playback_client)->active);
>  
>      if (playback_client->pending_frame) {
>          snd_playback_free_frame(playback_client, playback_client->pending_frame);
>      }
>      frame->time = reds_get_mm_time();
>      playback_client->pending_frame = frame;
> -    snd_set_command(&playback_client->base, SND_PLAYBACK_PCM_MASK);
> -    snd_playback_send(&playback_client->base);
> +    snd_set_command(SND_CHANNEL_CLIENT(playback_client), SND_PLAYBACK_PCM_MASK);
> +    snd_send(SND_CHANNEL_CLIENT(playback_client));
>  }
>  
>  void snd_set_playback_latency(RedClient *client, uint32_t latency)
> @@ -1207,15 +955,15 @@ void snd_set_playback_latency(RedClient *client, uint32_t latency)
>          uint32_t type;
>          g_object_get(RED_CHANNEL(now), "channel-type", &type, NULL);
>          if (type == SPICE_CHANNEL_PLAYBACK && now->connection &&
> -            red_channel_client_get_client(now->connection->channel_client) == client) {
> +            red_channel_client_get_client(RED_CHANNEL_CLIENT(now->connection)) == client) {
>  
> -            if (red_channel_client_test_remote_cap(now->connection->channel_client,
> +            if (red_channel_client_test_remote_cap(RED_CHANNEL_CLIENT(now->connection),
>                  SPICE_PLAYBACK_CAP_LATENCY)) {
>                  PlaybackChannelClient* playback = (PlaybackChannelClient*)now->connection;
>  
>                  playback->latency = latency;
>                  snd_set_command(now->connection, SND_PLAYBACK_LATENCY_MASK);
> -                snd_playback_send(now->connection);
> +                snd_send(now->connection);
>              } else {
>                  spice_debug("client doesn't not support SPICE_PLAYBACK_CAP_LATENCY");
>              }
> @@ -1250,60 +998,58 @@ static void on_new_playback_channel(SndChannel *channel, SndChannelClient *snd_c
>          snd_set_command(snd_channel, SND_CTRL_MASK);
>      }
>      if (channel->volume.volume_nchannels) {
> -        snd_set_command(snd_channel, SND_VOLUME_MASK);
> +        snd_set_command(snd_channel, SND_VOLUME_MUTE_MASK);
>      }
>      if (snd_channel->active) {
>          reds_disable_mm_time(reds);
>      }
>  }
>  
> -static void snd_playback_cleanup(SndChannelClient *client)
> +static void
> +playback_channel_client_finalize(GObject *object)
>  {
> -    PlaybackChannelClient *playback_client = SPICE_CONTAINEROF(client, PlaybackChannelClient, base);
> +    int i;
> +    PlaybackChannelClient *playback_client = PLAYBACK_CHANNEL_CLIENT(object);
> +    SndChannelClient *client = SND_CHANNEL_CLIENT(playback_client);
>  
> -    if (playback_client->base.active) {
> +    // free frames, unref them
> +    for (i = 0; i < NUM_AUDIO_FRAMES; ++i) {
> +        playback_client->frames->items[i].client = NULL;
> +    }
> +    if (--playback_client->frames->refs == 0) {
> +        free(playback_client->frames);
> +    }
> +
> +    if (client->active) {
>          reds_enable_mm_time(snd_channel_get_server(client));
>      }
>  
>      snd_codec_destroy(&playback_client->codec);
> +
> +    G_OBJECT_CLASS(playback_channel_client_parent_class)->finalize(object);
>  }
>  
> -static void snd_set_playback_peer(RedChannel *red_channel, RedClient *client, RedsStream *stream,
> -                                  int migration, int num_common_caps, uint32_t *common_caps,
> -                                  int num_caps, uint32_t *caps)
> +static void
> +playback_channel_client_constructed(GObject *object)
>  {
> +    PlaybackChannelClient *playback_client = PLAYBACK_CHANNEL_CLIENT(object);
> +    RedChannel *red_channel = red_channel_client_get_channel(RED_CHANNEL_CLIENT(playback_client));
> +    RedClient *client = red_channel_client_get_client(RED_CHANNEL_CLIENT(playback_client));
>      SndChannel *channel = SND_CHANNEL(red_channel);
> -    PlaybackChannelClient *playback_client;
>  
> -    snd_disconnect_channel(channel->connection);
> -
> -    if (!(playback_client = (PlaybackChannelClient *)__new_channel(channel,
> -                                                              sizeof(*playback_client),
> -                                                              SPICE_CHANNEL_PLAYBACK,
> -                                                              client,
> -                                                              stream,
> -                                                              migration,
> -                                                              snd_playback_send,
> -                                                              snd_playback_handle_message,
> -                                                              snd_playback_on_message_done,
> -                                                              snd_playback_cleanup,
> -                                                              common_caps, num_common_caps,
> -                                                              caps, num_caps))) {
> -        return;
> -    }
> -    snd_playback_free_frame(playback_client, &playback_client->frames[0]);
> -    snd_playback_free_frame(playback_client, &playback_client->frames[1]);
> -    snd_playback_free_frame(playback_client, &playback_client->frames[2]);
> +    G_OBJECT_CLASS(playback_channel_client_parent_class)->constructed(object);
>  
> -    int client_can_celt = red_channel_client_test_remote_cap(playback_client->base.channel_client,
> +    SND_CHANNEL_CLIENT(playback_client)->on_message_done = snd_playback_on_message_done;
> +
> +    RedChannelClient *rcc = RED_CHANNEL_CLIENT(playback_client);
> +    int client_can_celt = red_channel_client_test_remote_cap(rcc,
>                                            SPICE_PLAYBACK_CAP_CELT_0_5_1);
> -    int client_can_opus = red_channel_client_test_remote_cap(playback_client->base.channel_client,
> +    int client_can_opus = red_channel_client_test_remote_cap(rcc,
>                                            SPICE_PLAYBACK_CAP_OPUS);
>      int playback_compression =
>          reds_config_get_playback_compression(red_channel_get_server(red_channel));
>      int desired_mode = snd_desired_audio_mode(playback_compression, channel->frequency,
>                                                client_can_celt, client_can_opus);
> -    playback_client->mode = SPICE_AUDIO_DATA_MODE_RAW;
>      if (desired_mode != SPICE_AUDIO_DATA_MODE_RAW) {
>          if (snd_codec_create(&playback_client->codec, desired_mode, channel->frequency,
>                               SND_CODEC_ENCODE) == SND_CODEC_OK) {
> @@ -1314,13 +1060,52 @@ static void snd_set_playback_peer(RedChannel *red_channel, RedClient *client, Re
>      }
>  
>      if (!red_client_during_migrate_at_target(client)) {
> -        on_new_playback_channel(channel, &playback_client->base);
> +        on_new_playback_channel(channel, SND_CHANNEL_CLIENT(playback_client));
>      }
>  
>      if (channel->active) {
>          snd_playback_start(channel);
>      }
> -    snd_playback_send(channel->connection);
> +    snd_send(SND_CHANNEL_CLIENT(playback_client));
> +}
> +
> +static void snd_set_playback_peer(RedChannel *red_channel, RedClient *client, RedsStream *stream,
> +                                  int migration, int num_common_caps, uint32_t *common_caps,
> +                                  int num_caps, uint32_t *caps)
> +{
> +    SndChannel *channel = SND_CHANNEL(red_channel);
> +    GArray *common_caps_array = NULL, *caps_array = NULL;
> +
> +    if (channel->connection) {
> +        red_channel_client_disconnect(RED_CHANNEL_CLIENT(channel->connection));
> +        channel->connection = NULL;
> +    }
> +
> +    if (common_caps) {
> +        common_caps_array = g_array_sized_new(FALSE, FALSE, sizeof (*common_caps),
> +                                              num_common_caps);
> +        g_array_append_vals(common_caps_array, common_caps, num_common_caps);
> +    }
> +    if (caps) {
> +        caps_array = g_array_sized_new(FALSE, FALSE, sizeof (*caps), num_caps);
> +        g_array_append_vals(caps_array, caps, num_caps);
> +    }
> +
> +    g_initable_new(TYPE_PLAYBACK_CHANNEL_CLIENT,
> +                   NULL, NULL,
> +                   "channel", channel,
> +                   "client", client,
> +                   "stream", stream,
> +                   "caps", caps_array,
> +                   "common-caps", common_caps_array,
> +                   NULL);
> +
> +    if (caps_array) {
> +        g_array_unref(caps_array);
> +    }
> +    if (common_caps_array) {
> +        g_array_unref(common_caps_array);
> +    }
>  }
>  
>  static void snd_record_migrate_channel_client(RedChannelClient *rcc)
> @@ -1333,9 +1118,9 @@ static void snd_record_migrate_channel_client(RedChannelClient *rcc)
>      spice_assert(channel);
>  
>      if (channel->connection) {
> -        spice_assert(channel->connection->channel_client == rcc);
> +        spice_assert(RED_CHANNEL_CLIENT(channel->connection) == rcc);
>          snd_set_command(channel->connection, SND_MIGRATE_MASK);
> -        snd_record_send(channel->connection);
> +        snd_send(channel->connection);
>      }
>  }
>  
> @@ -1345,7 +1130,6 @@ SPICE_GNUC_VISIBLE void spice_server_record_set_volume(SpiceRecordInstance *sin,
>  {
>      SpiceVolumeState *st = &sin->st->channel.volume;
>      SndChannelClient *client = sin->st->channel.connection;
> -    RecordChannelClient *record_client = SPICE_CONTAINEROF(client, RecordChannelClient, base);
>  
>      st->volume_nchannels = nchannels;
>      free(st->volume);
> @@ -1354,38 +1138,40 @@ SPICE_GNUC_VISIBLE void spice_server_record_set_volume(SpiceRecordInstance *sin,
>      if (!client || nchannels == 0)
>          return;
>  
> -    snd_record_send_volume(record_client);
> +    snd_set_command(client, SND_VOLUME_MUTE_MASK);
> +    snd_send(client);
>  }
>  
>  SPICE_GNUC_VISIBLE void spice_server_record_set_mute(SpiceRecordInstance *sin, uint8_t mute)
>  {
>      SpiceVolumeState *st = &sin->st->channel.volume;
>      SndChannelClient *client = sin->st->channel.connection;
> -    RecordChannelClient *record_client = SPICE_CONTAINEROF(client, RecordChannelClient, base);
>  
>      st->mute = mute;
>  
>      if (!client)
>          return;
>  
> -    snd_record_send_mute(record_client);
> +    snd_set_command(client, SND_VOLUME_MUTE_MASK);
> +    snd_send(client);
>  }
>  
>  static void snd_record_start(SndChannel *channel)
>  {
>      SndChannelClient *client = channel->connection;
> -    RecordChannelClient *record_client = SPICE_CONTAINEROF(client, RecordChannelClient, base);
>  
>      channel->active = 1;
> -    if (!client)
> +    if (!client) {
>          return;
> +    }
> +    RecordChannelClient *record_client = RECORD_CHANNEL_CLIENT(client);
>      spice_assert(!client->active);
>      record_client->read_pos = record_client->write_pos = 0;   //todo: improve by
>                                                                  //stream generation
>      client->active = TRUE;
>      if (!client->client_active) {
>          snd_set_command(client, SND_CTRL_MASK);
> -        snd_record_send(client);
> +        snd_send(client);
>      } else {
>          client->command &= ~SND_CTRL_MASK;
>      }
> @@ -1399,18 +1185,17 @@ SPICE_GNUC_VISIBLE void spice_server_record_start(SpiceRecordInstance *sin)
>  SPICE_GNUC_VISIBLE void spice_server_record_stop(SpiceRecordInstance *sin)
>  {
>      SndChannelClient *client = sin->st->channel.connection;
> -    RecordChannelClient *record_client = SPICE_CONTAINEROF(client, RecordChannelClient, base);
>  
>      sin->st->channel.active = 0;
>      if (!client)
>          return;
> -    spice_assert(record_client->base.active);
> -    record_client->base.active = FALSE;
> -    if (record_client->base.client_active) {
> -        snd_set_command(&record_client->base, SND_CTRL_MASK);
> -        snd_record_send(&record_client->base);
> +    spice_assert(client->active);
> +    client->active = FALSE;
> +    if (client->client_active) {
> +        snd_set_command(client, SND_CTRL_MASK);
> +        snd_send(client);
>      } else {
> -        record_client->base.command &= ~SND_CTRL_MASK;
> +        client->command &= ~SND_CTRL_MASK;
>      }
>  }
>  
> @@ -1418,14 +1203,14 @@ SPICE_GNUC_VISIBLE uint32_t spice_server_record_get_samples(SpiceRecordInstance
>                                                              uint32_t *samples, uint32_t bufsize)
>  {
>      SndChannelClient *client = sin->st->channel.connection;
> -    RecordChannelClient *record_client = SPICE_CONTAINEROF(client, RecordChannelClient, base);
>      uint32_t read_pos;
>      uint32_t now;
>      uint32_t len;
>  
>      if (!client)
>          return 0;
> -    spice_assert(record_client->base.active);
> +    RecordChannelClient *record_client = RECORD_CHANNEL_CLIENT(client);
> +    spice_assert(client->active);
>  
>      if (record_client->write_pos < RECORD_SAMPLES_SIZE / 2) {
>          return 0;
> @@ -1433,14 +1218,17 @@ SPICE_GNUC_VISIBLE uint32_t spice_server_record_get_samples(SpiceRecordInstance
>  
>      len = MIN(record_client->write_pos - record_client->read_pos, bufsize);
>  
> +    // TODO why ?? stream already call if got data
> +#if 0
>      if (len < bufsize) {
> -        SndChannel *channel = record_client->base.channel;
> -        snd_receive(&record_client->base);
> +        SndChannel *channel = SND_CHANNEL(red_channel_client_get_channel(RED_CHANNEL_CLIENT(client)));
> +        snd_receive(client);
>          if (!channel->connection) {
>              return 0;
>          }
>          len = MIN(record_client->write_pos - record_client->read_pos, bufsize);
>      }
> +#endif
>  
>      read_pos = record_client->read_pos % RECORD_SAMPLES_SIZE;
>      record_client->read_pos += len;
> @@ -1456,7 +1244,7 @@ static uint32_t snd_get_best_rate(SndChannelClient *client, uint32_t cap_opus)
>  {
>      int client_can_opus = TRUE;
>      if (client) {
> -        client_can_opus = red_channel_client_test_remote_cap(client->channel_client, cap_opus);
> +        client_can_opus = red_channel_client_test_remote_cap(RED_CHANNEL_CLIENT(client), cap_opus);
>      }
>  
>      if (client_can_opus && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, SND_CODEC_ANY_FREQUENCY))
> @@ -1498,52 +1286,79 @@ static void on_new_record_channel(SndChannel *channel, SndChannelClient *snd_cha
>  {
>      spice_assert(snd_channel);
>  
> -    channel->connection = snd_channel ;
> +    channel->connection = snd_channel;
>      if (channel->volume.volume_nchannels) {
> -        snd_set_command(snd_channel, SND_VOLUME_MASK);
> +        snd_set_command(snd_channel, SND_VOLUME_MUTE_MASK);
>      }
>      if (snd_channel->active) {
>          snd_set_command(snd_channel, SND_CTRL_MASK);
>      }
>  }
>  
> -static void snd_record_cleanup(SndChannelClient *client)
> +static void
> +record_channel_client_finalize(GObject *object)
>  {
> -    RecordChannelClient *record_client = SPICE_CONTAINEROF(client, RecordChannelClient, base);
> +    RecordChannelClient *record_client = RECORD_CHANNEL_CLIENT(object);
> +
>      snd_codec_destroy(&record_client->codec);
> +
> +    G_OBJECT_CLASS(record_channel_client_parent_class)->finalize(object);
>  }
>  
> +static void
> +record_channel_client_constructed(GObject *object)
> +{
> +    RecordChannelClient *record_client = RECORD_CHANNEL_CLIENT(object);
> +    RedChannel *red_channel = red_channel_client_get_channel(RED_CHANNEL_CLIENT(record_client));
> +    SndChannel *channel = SND_CHANNEL(red_channel);
> +
> +    G_OBJECT_CLASS(record_channel_client_parent_class)->constructed(object);
> +
> +    on_new_record_channel(channel, SND_CHANNEL_CLIENT(record_client));
> +    if (channel->active) {
> +        snd_record_start(channel);
> +    }
> +    snd_send(SND_CHANNEL_CLIENT(record_client));
> +}
> +
> +
>  static void snd_set_record_peer(RedChannel *red_channel, RedClient *client, RedsStream *stream,
>                                  int migration, int num_common_caps, uint32_t *common_caps,
>                                  int num_caps, uint32_t *caps)
>  {
>      SndChannel *channel = SND_CHANNEL(red_channel);
> -    RecordChannelClient *record_client;
> -
> -    snd_disconnect_channel(channel->connection);
> -
> -    if (!(record_client = (RecordChannelClient *)__new_channel(channel,
> -                                                          sizeof(*record_client),
> -                                                          SPICE_CHANNEL_RECORD,
> -                                                          client,
> -                                                          stream,
> -                                                          migration,
> -                                                          snd_record_send,
> -                                                          snd_record_handle_message,
> -                                                          snd_record_on_message_done,
> -                                                          snd_record_cleanup,
> -                                                          common_caps, num_common_caps,
> -                                                          caps, num_caps))) {
> -        return;
> +    GArray *common_caps_array = NULL, *caps_array = NULL;
> +
> +    if (channel->connection) {
> +        red_channel_client_disconnect(RED_CHANNEL_CLIENT(channel->connection));
> +        channel->connection = NULL;
>      }
>  
> -    record_client->mode = SPICE_AUDIO_DATA_MODE_RAW;
> +    if (common_caps) {
> +        common_caps_array = g_array_sized_new(FALSE, FALSE, sizeof (*common_caps),
> +                                              num_common_caps);
> +        g_array_append_vals(common_caps_array, common_caps, num_common_caps);
> +    }
> +    if (caps) {
> +        caps_array = g_array_sized_new(FALSE, FALSE, sizeof (*caps), num_caps);
> +        g_array_append_vals(caps_array, caps, num_caps);
> +    }
>  
> -    on_new_record_channel(channel, &record_client->base);
> -    if (channel->active) {
> -        snd_record_start(channel);
> +    g_initable_new(TYPE_RECORD_CHANNEL_CLIENT,
> +                   NULL, NULL,
> +                   "channel", channel,
> +                   "client", client,
> +                   "stream", stream,
> +                   "caps", caps_array,
> +                   "common-caps", common_caps_array,
> +                   NULL);
> +
> +    if (caps_array) {
> +        g_array_unref(caps_array);
> +    }
> +    if (common_caps_array) {
> +        g_array_unref(common_caps_array);
>      }
> -    snd_record_send(channel->connection);
>  }
>  
>  static void snd_playback_migrate_channel_client(RedChannelClient *rcc)
> @@ -1557,9 +1372,9 @@ static void snd_playback_migrate_channel_client(RedChannelClient *rcc)
>      spice_debug(NULL);
>  
>      if (channel->connection) {
> -        spice_assert(channel->connection->channel_client == rcc);
> +        spice_assert(RED_CHANNEL_CLIENT(channel->connection) == rcc);
>          snd_set_command(channel->connection, SND_MIGRATE_MASK);
> -        snd_playback_send(channel->connection);
> +        snd_send(channel->connection);
>      }
>  }
>  
> @@ -1593,6 +1408,8 @@ snd_channel_class_init(SndChannelClass *klass)
>  {
>      RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass);
>      channel_class->config_socket = snd_channel_config_socket;
> +    channel_class->alloc_recv_buf = snd_channel_client_alloc_recv_buf;
> +    channel_class->release_recv_buf = snd_channel_client_release_recv_buf;
>  }
>  
>  static void
> @@ -1627,8 +1444,13 @@ static void
>  playback_channel_class_init(SndChannelClass *klass)
>  {
>      GObjectClass *object_class = G_OBJECT_CLASS(klass);
> +    RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass);
>  
>      object_class->constructed = playback_channel_constructed;
> +
> +    channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_PLAYBACK, NULL);
> +    channel_class->handle_parsed = playback_channel_handle_parsed;
> +    channel_class->send_item = playback_channel_send_item;
>  }
>  
>  void snd_attach_playback(RedsState *reds, SpicePlaybackInstance *sin)
> @@ -1673,8 +1495,13 @@ static void
>  record_channel_class_init(SndChannelClass *klass)
>  {
>      GObjectClass *object_class = G_OBJECT_CLASS(klass);
> +    RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass);
>  
>      object_class->constructed = record_channel_constructed;
> +
> +    channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_RECORD, NULL);
> +    channel_class->handle_parsed = record_channel_handle_parsed;
> +    channel_class->send_item = record_channel_send_item;
>  }
>  
>  void snd_attach_record(RedsState *reds, SpiceRecordInstance *sin)
> @@ -1695,7 +1522,6 @@ static void snd_detach_common(SndChannel *channel)
>      RedsState *reds = red_channel_get_server(RED_CHANNEL(channel));
>  
>      remove_channel(channel);
> -    snd_disconnect_channel(channel->connection);
>      reds_unregister_channel(reds, RED_CHANNEL(channel));
>      free(channel->volume.volume);
>      channel->volume.volume = NULL;
> @@ -1721,9 +1547,10 @@ void snd_set_playback_compression(int on)
>          g_object_get(RED_CHANNEL(now), "channel-type", &type, NULL);
>          if (type == SPICE_CHANNEL_PLAYBACK && now->connection) {
>              PlaybackChannelClient* playback = (PlaybackChannelClient*)now->connection;
> -            int client_can_celt = red_channel_client_test_remote_cap(playback->base.channel_client,
> +            RedChannelClient *rcc = RED_CHANNEL_CLIENT(playback);
> +            int client_can_celt = red_channel_client_test_remote_cap(rcc,
>                                      SPICE_PLAYBACK_CAP_CELT_0_5_1);
> -            int client_can_opus = red_channel_client_test_remote_cap(playback->base.channel_client,
> +            int client_can_opus = red_channel_client_test_remote_cap(rcc,
>                                      SPICE_PLAYBACK_CAP_OPUS);
>              int desired_mode = snd_desired_audio_mode(on, now->frequency,
>                                                        client_can_opus, client_can_celt);
> @@ -1734,3 +1561,50 @@ void snd_set_playback_compression(int on)
>          }
>      }
>  }
> +
> +static void
> +snd_channel_client_class_init(SndChannelClientClass *self)
> +{
> +}
> +
> +static void
> +snd_channel_client_init(SndChannelClient *self)
> +{
> +}
> +
> +static void
> +playback_channel_client_class_init(PlaybackChannelClientClass *klass)
> +{
> +    GObjectClass *object_class = G_OBJECT_CLASS(klass);
> +    object_class->constructed = playback_channel_client_constructed;
> +    object_class->finalize = playback_channel_client_finalize;
> +}
> +
> +static void
> +playback_channel_client_init(PlaybackChannelClient *playback)
> +{
> +    int i;
> +
> +    playback->mode = SPICE_AUDIO_DATA_MODE_RAW;
> +
> +    playback->frames = spice_new0(AudioFrameContainer, 1);
> +    playback->frames->refs = 1;
> +    for (i = 0; i < NUM_AUDIO_FRAMES; ++i) {
> +        playback->frames->items[i].container = playback->frames;
> +        snd_playback_free_frame(playback, &playback->frames->items[i]);
> +    }
> +}
> +
> +static void
> +record_channel_client_class_init(RecordChannelClientClass *klass)
> +{
> +    GObjectClass *object_class = G_OBJECT_CLASS(klass);
> +    object_class->constructed = record_channel_client_constructed;
> +    object_class->finalize = record_channel_client_finalize;
> +}
> +
> +static void
> +record_channel_client_init(RecordChannelClient *record)
> +{
> +    record->mode = SPICE_AUDIO_DATA_MODE_RAW;
> +}
> -- 
> 2.7.4
> 
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/spice-devel
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <https://lists.freedesktop.org/archives/spice-devel/attachments/20161129/7ddb0ec0/attachment-0001.sig>


More information about the Spice-devel mailing list