[Spice-devel] [PATCH v3 10/17] sound: Convert SndChannelClient to GObject

Frediano Ziglio fziglio at redhat.com
Tue Nov 29 14:57:10 UTC 2016


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.

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 | 1104 +++++++++++++++++++++----------------------------
 1 file changed, 491 insertions(+), 613 deletions(-)

diff --git a/server/sound.c b/server/sound.c
index 6041241..24fd729 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,80 @@ 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 +148,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 +180,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))
@@ -177,12 +189,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;
@@ -191,11 +202,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;
@@ -204,64 +222,24 @@ 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 void snd_send(SndChannelClient * 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)
@@ -279,69 +257,11 @@ static void snd_playback_on_message_done(SndChannelClient *client)
         playback_client->in_progress = NULL;
         if (playback_client->pending_frame) {
             client->command |= SND_PLAYBACK_PCM_MASK;
+            snd_send(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;
@@ -387,35 +307,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)) {
@@ -445,260 +359,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);
@@ -709,36 +497,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);
@@ -749,13 +536,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);
 }
 
@@ -765,27 +552,26 @@ 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,
+                                      &SND_CHANNEL_CLIENT(playback_client)->persistent_pipe_item);
 
     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 {
@@ -794,45 +580,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);
@@ -840,163 +651,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)
@@ -1048,6 +773,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;
@@ -1061,8 +802,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);
     }
 }
 
@@ -1080,7 +821,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);
@@ -1089,21 +829,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)
@@ -1118,7 +859,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;
     }
@@ -1132,20 +873,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);
@@ -1160,15 +901,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;
@@ -1181,23 +928,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)
@@ -1208,15 +960,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");
             }
@@ -1251,60 +1003,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) {
@@ -1315,13 +1065,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)
@@ -1334,9 +1123,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);
     }
 }
 
@@ -1346,7 +1135,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);
@@ -1355,38 +1143,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;
     }
@@ -1400,18 +1190,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;
     }
 }
 
@@ -1419,14 +1208,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;
@@ -1434,14 +1223,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;
@@ -1457,7 +1249,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))
@@ -1499,52 +1291,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)
@@ -1558,9 +1377,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);
     }
 }
 
@@ -1594,6 +1413,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
@@ -1628,8 +1449,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)
@@ -1674,8 +1500,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)
@@ -1696,7 +1527,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;
@@ -1722,9 +1552,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);
@@ -1735,3 +1566,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;
+}
-- 
git-series 0.9.1


More information about the Spice-devel mailing list