[Spice-commits] 9 commits - server/Makefile.am server/dummy-channel-client.c server/dummy-channel-client.h server/sound.c

Christophe Fergau teuf at kemper.freedesktop.org
Thu Feb 2 15:31:21 UTC 2017


 server/Makefile.am            |    2 
 server/dummy-channel-client.c |  138 ------
 server/dummy-channel-client.h |   64 --
 server/sound.c                |  966 ++++++++++++++++++------------------------
 4 files changed, 423 insertions(+), 747 deletions(-)

New commits:
commit d8dc09b817518b7e13b76f3f778094d3bd191553
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Tue Jan 10 16:43:38 2017 +0100

    sound: Convert SndChannelClient to RedChannelClient
    
    Now that SndChannelClient has switched from using its own code for
    sending data to using RedChannelClient, it's very close to being an
    actual RedChannelClient.
    This commit makes it directly inherit from RedChannelClient rather than
    having a channel_client field. This allows to get rid of the whole
    DummyChannel/DummyChannelClient code.
    
    Based on a patch from Frediano Ziglio <fziglio at redhat.com>
    
    Signed-off-by: Christophe Fergeau <cfergeau at redhat.com>

diff --git a/server/Makefile.am b/server/Makefile.am
index 90ff779..1442bf1 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -104,8 +104,6 @@ libserver_la_SOURCES =				\
 	red-channel-client-private.h		\
 	red-client.c				\
 	red-client.h				\
-	dummy-channel-client.c			\
-	dummy-channel-client.h			\
 	red-common.h				\
 	dispatcher.c				\
 	dispatcher.h				\
diff --git a/server/dummy-channel-client.c b/server/dummy-channel-client.c
deleted file mode 100644
index 4efaa31..0000000
--- a/server/dummy-channel-client.c
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
-   Copyright (C) 2009-2015 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include "dummy-channel-client.h"
-#include "red-channel.h"
-#include "red-client.h"
-
-static void dummy_channel_client_initable_interface_init(GInitableIface *iface);
-
-G_DEFINE_TYPE_WITH_CODE(DummyChannelClient, dummy_channel_client, RED_TYPE_CHANNEL_CLIENT,
-                        G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE,
-                                              dummy_channel_client_initable_interface_init))
-
-#define DUMMY_CHANNEL_CLIENT_PRIVATE(o) \
-    (G_TYPE_INSTANCE_GET_PRIVATE((o), TYPE_DUMMY_CHANNEL_CLIENT, DummyChannelClientPrivate))
-
-struct DummyChannelClientPrivate
-{
-    gboolean connected;
-};
-
-static gboolean dummy_channel_client_initable_init(GInitable *initable,
-                                                   GCancellable *cancellable,
-                                                   GError **error)
-{
-    GError *local_error = NULL;
-    RedChannelClient *rcc = RED_CHANNEL_CLIENT(initable);
-    RedClient *client = red_channel_client_get_client(rcc);
-    RedChannel *channel = red_channel_client_get_channel(rcc);
-
-    red_channel_add_client(channel, rcc);
-    if (!red_client_add_channel(client, rcc, &local_error)) {
-        red_channel_remove_client(channel, rcc);
-    }
-
-    if (local_error) {
-        g_warning("Failed to create channel client: %s", local_error->message);
-        g_propagate_error(error, local_error);
-    }
-    return local_error == NULL;
-}
-
-static void dummy_channel_client_initable_interface_init(GInitableIface *iface)
-{
-    iface->init = dummy_channel_client_initable_init;
-}
-
-static gboolean dummy_channel_client_is_connected(RedChannelClient *rcc)
-{
-    return DUMMY_CHANNEL_CLIENT(rcc)->priv->connected;
-}
-
-static void dummy_channel_client_disconnect(RedChannelClient *rcc)
-{
-    DummyChannelClient *self = DUMMY_CHANNEL_CLIENT(rcc);
-    RedChannel *channel = red_channel_client_get_channel(rcc);
-    GList *link;
-    uint32_t type, id;
-
-    if (channel && (link = g_list_find(red_channel_get_clients(channel), rcc))) {
-        g_object_get(channel, "channel-type", &type, "id", &id, NULL);
-        spice_printerr("rcc=%p (channel=%p type=%d id=%d)", rcc, channel,
-                       type, id);
-        red_channel_remove_client(channel, link->data);
-    }
-    self->priv->connected = FALSE;
-}
-
-static void
-dummy_channel_client_class_init(DummyChannelClientClass *klass)
-{
-    RedChannelClientClass *cc_class = RED_CHANNEL_CLIENT_CLASS(klass);
-
-    g_type_class_add_private(klass, sizeof(DummyChannelClientPrivate));
-
-    cc_class->is_connected = dummy_channel_client_is_connected;
-    cc_class->disconnect = dummy_channel_client_disconnect;
-}
-
-static void
-dummy_channel_client_init(DummyChannelClient *self)
-{
-    self->priv = DUMMY_CHANNEL_CLIENT_PRIVATE(self);
-
-    self->priv->connected = TRUE;
-}
-
-RedChannelClient* dummy_channel_client_create(RedChannel *channel,
-                                              RedClient  *client,
-                                              RedsStream *stream,
-                                              int num_common_caps,
-                                              uint32_t *common_caps,
-                                              int num_caps, uint32_t *caps)
-{
-    RedChannelClient *rcc;
-    GArray *common_caps_array = NULL, *caps_array = 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);
-    }
-
-    rcc = g_initable_new(TYPE_DUMMY_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);
-
-    return rcc;
-}
diff --git a/server/dummy-channel-client.h b/server/dummy-channel-client.h
deleted file mode 100644
index 54e0e6c..0000000
--- a/server/dummy-channel-client.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
-   Copyright (C) 2009-2015 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library; if not, see <http://www.gnu.org/licenses/>.
-   */
-#ifndef __DUMMY_CHANNEL_CLIENT_H__
-#define __DUMMY_CHANNEL_CLIENT_H__
-
-#include <glib-object.h>
-
-#include "red-channel-client.h"
-
-G_BEGIN_DECLS
-
-#define TYPE_DUMMY_CHANNEL_CLIENT dummy_channel_client_get_type()
-
-#define DUMMY_CHANNEL_CLIENT(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_DUMMY_CHANNEL_CLIENT, DummyChannelClient))
-#define DUMMY_CHANNEL_CLIENT_CLASS(klass) \
-    (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_DUMMY_CHANNEL_CLIENT, DummyChannelClientClass))
-#define IS_DUMMY_CHANNEL_CLIENT(obj) \
-    (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_DUMMY_CHANNEL_CLIENT))
-#define IS_DUMMY_CHANNEL_CLIENT_CLASS(klass) \
-    (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_DUMMY_CHANNEL_CLIENT))
-#define DUMMY_CHANNEL_CLIENT_GET_CLASS(obj) \
-    (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_DUMMY_CHANNEL_CLIENT, DummyChannelClientClass))
-
-typedef struct DummyChannelClient DummyChannelClient;
-typedef struct DummyChannelClientClass DummyChannelClientClass;
-typedef struct DummyChannelClientPrivate DummyChannelClientPrivate;
-
-struct DummyChannelClient
-{
-    RedChannelClient parent;
-
-    DummyChannelClientPrivate *priv;
-};
-
-struct DummyChannelClientClass
-{
-    RedChannelClientClass parent_class;
-};
-
-GType dummy_channel_client_get_type(void) G_GNUC_CONST;
-
-RedChannelClient *dummy_channel_client_create(RedChannel *channel,
-                                              RedClient  *client,
-                                              RedsStream *stream,
-                                              int num_common_caps, uint32_t *common_caps,
-                                              int num_caps, uint32_t *caps);
-
-G_END_DECLS
-
-#endif /* __DUMMY_CHANNEL_CLIENT_H__ */
diff --git a/server/sound.c b/server/sound.c
index 39235f0..7095e2b 100644
--- a/server/sound.c
+++ b/server/sound.c
@@ -31,13 +31,10 @@
 
 #include "spice.h"
 #include "red-common.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 "main-channel-client.h"
@@ -83,15 +80,16 @@ 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);
 
-#define SND_CHANNEL_CLIENT(obj) (&(obj)->base)
+
+#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 {
-    int refs;
-
-    RedChannelClient *channel_client;
+    RedChannelClient parent;
 
     int active;
     int client_active;
@@ -104,9 +102,14 @@ struct SndChannelClient {
     RedPipeItem persistent_pipe_item;
 
     snd_channel_on_message_done_proc on_message_done;
-    snd_channel_cleanup_channel_proc cleanup;
 };
 
+typedef struct SndChannelClientClass {
+    RedChannelClientClass parent_class;
+} SndChannelClientClass;
+
+G_DEFINE_TYPE(SndChannelClient, snd_channel_client, RED_TYPE_CHANNEL_CLIENT)
+
 
 enum {
     RED_PIPE_ITEM_PERSISTENT = RED_PIPE_ITEM_TYPE_CHANNEL_BASE,
@@ -129,8 +132,13 @@ struct AudioFrameContainer
     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;
+    SndChannelClient parent;
 
     AudioFrameContainer *frames;
     AudioFrame *free_frames;
@@ -142,6 +150,13 @@ struct PlaybackChannelClient {
     uint8_t  encode_buf[SND_CODEC_MAX_COMPRESSED_BYTES];
 };
 
+typedef struct PlaybackChannelClientClass {
+    SndChannelClientClass parent_class;
+} PlaybackChannelClientClass;
+
+G_DEFINE_TYPE(PlaybackChannelClient, playback_channel_client, TYPE_SND_CHANNEL_CLIENT)
+
+
 typedef struct SpiceVolumeState {
     uint16_t *volume;
     uint8_t volume_nchannels;
@@ -202,8 +217,13 @@ typedef struct RecordChannelClass {
 G_DEFINE_TYPE(RecordChannel, record_channel, TYPE_SND_CHANNEL)
 
 
+#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 base;
+    SndChannelClient parent;
     uint32_t samples[RECORD_SAMPLES_SIZE];
     uint32_t write_pos;
     uint32_t read_pos;
@@ -214,60 +234,24 @@ struct RecordChannelClient {
     uint8_t  decode_buf[SND_CODEC_MAX_FRAME_BYTES];
 };
 
+typedef struct RecordChannelClientClass {
+    SndChannelClientClass parent_class;
+} 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_playback_start(SndChannel *channel);
 static void snd_record_start(SndChannel *channel);
-static void snd_playback_alloc_frames(PlaybackChannelClient *playback);
 static void snd_send(SndChannelClient * client);
 
-static SndChannelClient *snd_channel_unref(SndChannelClient *client)
-{
-    if (!--client->refs) {
-        spice_printerr("SndChannelClient=%p freed", client);
-        free(client);
-        return NULL;
-    }
-    return client;
-}
-
-static SndChannelClient *snd_channel_client_from_dummy(RedChannelClient *dummy)
-{
-    SndChannelClient *sound_client;
-
-    g_assert(IS_DUMMY_CHANNEL_CLIENT(dummy));
-    sound_client =  g_object_get_data(G_OBJECT(dummy), "sound-channel-client");
-
-    return sound_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_get_channel(client->channel_client));
-}
-
-static void snd_disconnect_channel(SndChannelClient *client)
-{
-    SndChannel *channel;
-    RedChannel *red_channel;
-    uint32_t type;
-
-    if (!client || !red_channel_client_is_connected(client->channel_client)) {
-        spice_debug("not connected");
-        return;
-    }
-    red_channel = red_channel_client_get_channel(client->channel_client);
-    g_object_get(red_channel, "channel-type", &type, NULL);
-    spice_debug("SndChannelClient=%p rcc=%p type=%d",
-                 client, client->channel_client, type);
-    channel = SND_CHANNEL(red_channel);
-    client->cleanup(client);
-    red_channel_client_disconnect(channel->connection->channel_client);
-    channel->connection->channel_client = NULL;
-    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)
@@ -290,10 +274,6 @@ static void snd_playback_on_message_done(SndChannelClient *client)
     }
 }
 
-static void snd_record_on_message_done(SndChannelClient *client)
-{
-}
-
 static int snd_record_handle_write(RecordChannelClient *record_client, size_t size, void *message)
 {
     SpiceMsgcRecordPacket *packet;
@@ -354,8 +334,7 @@ playback_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t ty
 static int
 record_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type, void *message)
 {
-    SndChannelClient *snd_client = snd_channel_client_from_dummy(rcc);
-    RecordChannelClient *record_client = SPICE_CONTAINEROF(snd_client, RecordChannelClient, base);
+    RecordChannelClient *record_client = RECORD_CHANNEL_CLIENT(rcc);
 
     switch (type) {
     case SPICE_MSGC_RECORD_DATA:
@@ -399,7 +378,7 @@ record_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type
 
 static int snd_channel_send_migrate(SndChannelClient *client)
 {
-    RedChannelClient *rcc = client->channel_client;
+    RedChannelClient *rcc = RED_CHANNEL_CLIENT(client);
     SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
     SpiceMsgMigrate migrate;
 
@@ -420,7 +399,7 @@ static int snd_send_volume(SndChannelClient *client, uint32_t cap, int msg)
 {
     SpiceMsgAudioVolume *vol;
     uint8_t c;
-    RedChannelClient *rcc = client->channel_client;
+    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;
@@ -451,7 +430,7 @@ static int snd_playback_send_volume(PlaybackChannelClient *playback_client)
 static int snd_send_mute(SndChannelClient *client, uint32_t cap, int msg)
 {
     SpiceMsgAudioMute mute;
-    RedChannelClient *rcc = client->channel_client;
+    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;
@@ -476,8 +455,7 @@ static int snd_playback_send_mute(PlaybackChannelClient *playback_client)
 
 static int snd_playback_send_latency(PlaybackChannelClient *playback_client)
 {
-    SndChannelClient *client = SND_CHANNEL_CLIENT(playback_client);
-    RedChannelClient *rcc = client->channel_client;
+    RedChannelClient *rcc = RED_CHANNEL_CLIENT(playback_client);
     SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
     SpiceMsgPlaybackLatency latency_msg;
 
@@ -492,8 +470,7 @@ static int snd_playback_send_latency(PlaybackChannelClient *playback_client)
 
 static int snd_playback_send_start(PlaybackChannelClient *playback_client)
 {
-    SndChannelClient *client = (SndChannelClient *)playback_client;
-    RedChannelClient *rcc = client->channel_client;
+    RedChannelClient *rcc = RED_CHANNEL_CLIENT(playback_client);
     SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
     SpiceMsgPlaybackStart start;
 
@@ -511,8 +488,7 @@ static int snd_playback_send_start(PlaybackChannelClient *playback_client)
 
 static int snd_playback_send_stop(PlaybackChannelClient *playback_client)
 {
-    SndChannelClient *client = (SndChannelClient *)playback_client;
-    RedChannelClient *rcc = client->channel_client;
+    RedChannelClient *rcc = RED_CHANNEL_CLIENT(playback_client);
 
     red_channel_client_init_send_data(rcc, SPICE_MSG_PLAYBACK_STOP);
 
@@ -533,8 +509,7 @@ 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 = client->channel_client;
+    RedChannelClient *rcc = RED_CHANNEL_CLIENT(record_client);
     SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
     SpiceMsgRecordStart start;
 
@@ -552,8 +527,7 @@ static int snd_record_send_start(RecordChannelClient *record_client)
 
 static int snd_record_send_stop(RecordChannelClient *record_client)
 {
-    SndChannelClient *client = (SndChannelClient *)record_client;
-    RedChannelClient *rcc = client->channel_client;
+    RedChannelClient *rcc = RED_CHANNEL_CLIENT(record_client);
 
     red_channel_client_init_send_data(rcc, SPICE_MSG_RECORD_STOP);
 
@@ -595,8 +569,7 @@ static int snd_record_send_migrate(RecordChannelClient *record_client)
 
 static int snd_playback_send_write(PlaybackChannelClient *playback_client)
 {
-    SndChannelClient *client = (SndChannelClient *)playback_client;
-    RedChannelClient *rcc = client->channel_client;
+    RedChannelClient *rcc = RED_CHANNEL_CLIENT(playback_client);
     SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
     AudioFrame *frame;
     SpiceMsgPlaybackPacket msg;
@@ -621,7 +594,7 @@ 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);
+            red_channel_client_disconnect(rcc);
             return FALSE;
         }
         spice_marshaller_add_by_ref_full(m, playback_client->encode_buf, n,
@@ -634,8 +607,7 @@ static int snd_playback_send_write(PlaybackChannelClient *playback_client)
 
 static int playback_send_mode(PlaybackChannelClient *playback_client)
 {
-    SndChannelClient *client = (SndChannelClient *)playback_client;
-    RedChannelClient *rcc = client->channel_client;
+    RedChannelClient *rcc = RED_CHANNEL_CLIENT(playback_client);
     SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
     SpiceMsgPlaybackMode mode;
 
@@ -648,48 +620,6 @@ static int playback_send_mode(PlaybackChannelClient *playback_client)
     return TRUE;
 }
 
-static int snd_channel_config_socket(RedChannelClient *rcc);
-
-static SndChannelClient *__new_channel(SndChannel *channel, int size, uint32_t channel_id,
-                                       RedClient *red_client,
-                                       RedsStream *stream,
-                                       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;
-
-    spice_assert(size >= sizeof(*client));
-    client = spice_malloc0(size);
-    client->refs = 1;
-    client->on_message_done = on_message_done;
-    client->cleanup = cleanup;
-
-    client->channel_client =
-        dummy_channel_client_create(RED_CHANNEL(channel), red_client, stream,
-                                    num_common_caps, common_caps, num_caps, caps);
-    if (!client->channel_client) {
-        goto error2;
-    }
-
-    /* SndChannelClient is not yet a RedChannelClient, but we still need to go from our
-     * RedChannelClient implementation (DummyChannelClient) to the SndChannelClient instance
-     * in various vfuncs
-     */
-    g_object_set_data(G_OBJECT(client->channel_client), "sound-channel-client", client);
-
-    if (!snd_channel_config_socket(RED_CHANNEL_CLIENT(client->channel_client))) {
-        goto error2;
-    }
-
-    return client;
-
-error2:
-    free(client);
-    return NULL;
-}
-
 /* This function is called when the "persistent" item is removed from the
  * queue. Note that there is not free call as the item is allocated into
  * SndChannelClient.
@@ -715,9 +645,9 @@ static void snd_send(SndChannelClient * client)
 {
     RedChannelClient *rcc;
 
-    g_return_if_fail(client != NULL);
+    g_return_if_fail(RED_IS_CHANNEL_CLIENT(client));
 
-    rcc = client->channel_client;
+    rcc = RED_CHANNEL_CLIENT(client);
     if (!red_channel_client_pipe_is_empty(rcc) || !client->command) {
         return;
     }
@@ -729,8 +659,8 @@ static void snd_send(SndChannelClient * client)
 
 static void playback_channel_send_item(RedChannelClient *rcc, G_GNUC_UNUSED RedPipeItem *item)
 {
-    SndChannelClient *client = snd_channel_client_from_dummy(rcc);
-    PlaybackChannelClient *playback_client = SPICE_CONTAINEROF(client, PlaybackChannelClient, 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|
@@ -788,8 +718,8 @@ static void playback_channel_send_item(RedChannelClient *rcc, G_GNUC_UNUSED RedP
 
 static void record_channel_send_item(RedChannelClient *rcc, G_GNUC_UNUSED RedPipeItem *item)
 {
-    SndChannelClient *client = snd_channel_client_from_dummy(rcc);
-    RecordChannelClient *record_client = SPICE_CONTAINEROF(client, RecordChannelClient, base);
+    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) {
@@ -881,7 +811,7 @@ static void snd_channel_on_disconnect(RedChannelClient *rcc)
 static uint8_t*
 snd_channel_client_alloc_recv_buf(RedChannelClient *rcc, uint16_t type, uint32_t size)
 {
-    SndChannelClient *client = snd_channel_client_from_dummy(rcc);
+    SndChannelClient *client = SND_CHANNEL_CLIENT(rcc);
     // If message is too big allocate one, this should never happen
     if (size > sizeof(client->receive_buf)) {
         return spice_malloc(size);
@@ -893,7 +823,7 @@ static void
 snd_channel_client_release_recv_buf(RedChannelClient *rcc, uint16_t type, uint32_t size,
                                     uint8_t *msg)
 {
-    SndChannelClient *client = snd_channel_client_from_dummy(rcc);
+    SndChannelClient *client = SND_CHANNEL_CLIENT(rcc);
     if (msg != client->receive_buf) {
         free(msg);
     }
@@ -911,8 +841,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);
     }
 }
 
@@ -986,7 +916,7 @@ SPICE_GNUC_VISIBLE void spice_server_playback_stop(SpicePlaybackInstance *sin)
     sin->st->channel.active = 0;
     if (!client)
         return;
-    PlaybackChannelClient *playback_client = SPICE_CONTAINEROF(client, PlaybackChannelClient, base);
+    PlaybackChannelClient *playback_client = PLAYBACK_CHANNEL_CLIENT(client);
     spice_assert(client->active);
     reds_enable_mm_time(snd_channel_get_server(client));
     client->active = FALSE;
@@ -1016,7 +946,7 @@ SPICE_GNUC_VISIBLE void spice_server_playback_get_buffer(SpicePlaybackInstance *
     if (!client) {
         return;
     }
-    PlaybackChannelClient *playback_client = SPICE_CONTAINEROF(client, PlaybackChannelClient, base);
+    PlaybackChannelClient *playback_client = PLAYBACK_CHANNEL_CLIENT(client);
     if (!playback_client->free_frames) {
         return;
     }
@@ -1069,9 +999,9 @@ 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;
 
@@ -1119,10 +1049,12 @@ static void on_new_playback_channel_client(SndChannel *channel, SndChannelClient
     }
 }
 
-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);
 
     // free frames, unref them
     for (i = 0; i < NUM_AUDIO_FRAMES; ++i) {
@@ -1137,41 +1069,31 @@ static void snd_playback_cleanup(SndChannelClient *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,
-                                  G_GNUC_UNUSED 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,
-                                                                   snd_playback_on_message_done,
-                                                                   snd_playback_cleanup,
-                                                                   common_caps, num_common_caps,
-                                                                   caps, num_caps))) {
-        return;
-    }
+    G_OBJECT_CLASS(playback_channel_client_parent_class)->constructed(object);
 
-    snd_playback_alloc_frames(playback_client);
+    SND_CHANNEL_CLIENT(playback_client)->on_message_done = snd_playback_on_message_done;
 
-    int client_can_celt = red_channel_client_test_remote_cap(playback_client->base.channel_client,
+    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) {
@@ -1188,7 +1110,49 @@ static void snd_set_playback_peer(RedChannel *red_channel, RedClient *client, Re
     if (channel->active) {
         snd_playback_start(channel);
     }
-    snd_send(channel->connection);
+    snd_send(SND_CHANNEL_CLIENT(playback_client));
+}
+
+static void snd_set_playback_peer(RedChannel *red_channel, RedClient *client, RedsStream *stream,
+                                  G_GNUC_UNUSED 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;
+    PlaybackChannelClient *playback_client;
+
+    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);
+    }
+
+    playback_client = g_initable_new(TYPE_PLAYBACK_CHANNEL_CLIENT,
+                                     NULL, NULL,
+                                     "channel", channel,
+                                     "client", client,
+                                     "stream", stream,
+                                     "caps", caps_array,
+                                     "common-caps", common_caps_array,
+                                     NULL);
+    g_warn_if_fail(playback_client != 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)
@@ -1200,7 +1164,7 @@ 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_send(channel->connection);
     }
@@ -1243,9 +1207,10 @@ static void snd_record_start(SndChannel *channel)
     SndChannelClient *client = channel->connection;
 
     channel->active = 1;
-    if (!client)
+    if (!client) {
         return;
-    RecordChannelClient *record_client = SPICE_CONTAINEROF(client, RecordChannelClient, base);
+    }
+    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
@@ -1290,7 +1255,7 @@ SPICE_GNUC_VISIBLE uint32_t spice_server_record_get_samples(SpiceRecordInstance
 
     if (!client)
         return 0;
-    RecordChannelClient *record_client = SPICE_CONTAINEROF(client, RecordChannelClient, base);
+    RecordChannelClient *record_client = RECORD_CHANNEL_CLIENT(client);
     spice_assert(client->active);
 
     if (record_client->write_pos < RECORD_SAMPLES_SIZE / 2) {
@@ -1313,7 +1278,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))
@@ -1364,41 +1329,73 @@ static void on_new_record_channel_client(SndChannel *channel, SndChannelClient *
     }
 }
 
-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_client(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,
                                 G_GNUC_UNUSED 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;
     RecordChannelClient *record_client;
 
-    snd_disconnect_channel(channel->connection);
-
-    if (!(record_client = (RecordChannelClient *)__new_channel(channel,
-                                                               sizeof(*record_client),
-                                                               SPICE_CHANNEL_RECORD,
-                                                               client,
-                                                               stream,
-                                                               snd_record_on_message_done,
-                                                               snd_record_cleanup,
-                                                               common_caps, num_common_caps,
-                                                               caps, num_caps))) {
-        return;
+    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_client(channel, SND_CHANNEL_CLIENT(record_client));
-    if (channel->active) {
-        snd_record_start(channel);
+    record_client = g_initable_new(TYPE_RECORD_CHANNEL_CLIENT,
+                                   NULL, NULL,
+                                   "channel", channel,
+                                   "client", client,
+                                   "stream", stream,
+                                   "caps", caps_array,
+                                   "common-caps", common_caps_array,
+                                   NULL);
+    g_warn_if_fail(record_client != NULL);
+
+    if (caps_array) {
+        g_array_unref(caps_array);
+    }
+    if (common_caps_array) {
+        g_array_unref(common_caps_array);
     }
-    snd_send(channel->connection);
 }
 
 static void snd_playback_migrate_channel_client(RedChannelClient *rcc)
@@ -1411,7 +1408,7 @@ 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_send(channel->connection);
     }
@@ -1563,7 +1560,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;
@@ -1589,9 +1585,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);
@@ -1603,6 +1600,24 @@ 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 snd_playback_alloc_frames(PlaybackChannelClient *playback)
 {
     int i;
@@ -1614,3 +1629,24 @@ static void snd_playback_alloc_frames(PlaybackChannelClient *playback)
         snd_playback_free_frame(playback, &playback->frames->items[i]);
     }
 }
+
+static void
+playback_channel_client_init(PlaybackChannelClient *playback)
+{
+    playback->mode = SPICE_AUDIO_DATA_MODE_RAW;
+    snd_playback_alloc_frames(playback);
+}
+
+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;
+}
commit 39c22ee98f776eebcd1c9ead0bb1d3783debfefa
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Mon Jan 9 15:36:14 2017 +0100

    sound: Prefer snd_set_command() over snd_*_send_*()
    
    snd_set_command()/snd_send() are higher level methods which take care of
    scheduling calls to the corresponding snd_*_send_*() methods when
    appropriate. This commit switches a few direct snd_*_send_*() calls to
    snd_set_command()/snd_send().
    
    Based on a patch from Frediano Ziglio <fziglio at redhat.com>
    
    Signed-off-by: Christophe Fergeau <cfergeau at redhat.com>
    Acked-by: Frediano Ziglio <fziglio at redhat.com>

diff --git a/server/sound.c b/server/sound.c
index 4bdee14..39235f0 100644
--- a/server/sound.c
+++ b/server/sound.c
@@ -930,7 +930,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);
@@ -939,21 +938,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_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_MUTE_MASK);
+    snd_send(client);
 }
 
 static void snd_playback_start(SndChannel *channel)
@@ -1212,7 +1212,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);
@@ -1221,21 +1220,22 @@ 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_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_MUTE_MASK);
+    snd_send(client);
 }
 
 static void snd_record_start(SndChannel *channel)
commit 7ea1f2c133a8e57523078ba3112cec6a1d2f353e
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Tue Jan 10 19:33:44 2017 +0100

    sound: Use RedChannelClient to receive/send data
    
    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.
    
    Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
    Signed-off-by: Christophe Fergeau <cfergeau at redhat.com>

diff --git a/server/sound.c b/server/sound.c
index f27a53d..4bdee14 100644
--- a/server/sound.c
+++ b/server/sound.c
@@ -82,8 +82,6 @@ typedef struct AudioFrameContainer AudioFrameContainer;
 typedef struct SpicePlaybackState PlaybackChannel;
 typedef struct SpiceRecordState RecordChannel;
 
-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 void (*snd_channel_on_message_done_proc)(SndChannelClient *client);
 typedef void (*snd_channel_cleanup_channel_proc)(SndChannelClient *client);
 
@@ -91,37 +89,30 @@ typedef void (*snd_channel_cleanup_channel_proc)(SndChannelClient *client);
 
 /* Connects an audio client to a Spice client */
 struct SndChannelClient {
-    RedsStream *stream;
-    spice_parse_channel_func_t parser;
     int refs;
 
     RedChannelClient *channel_client;
 
     int active;
     int client_active;
-    int blocked;
 
     uint32_t command;
 
-    struct {
-        uint64_t serial;
-        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;
+    /* we don't expect very big messages so don't allocate too much
+     * bytes, data will be cached in RecordChannelClient::samples */
+    uint8_t receive_buf[SND_CODEC_MAX_FRAME_BYTES + 64];
+    RedPipeItem persistent_pipe_item;
+
     snd_channel_on_message_done_proc on_message_done;
     snd_channel_cleanup_channel_proc cleanup;
 };
 
+
+enum {
+    RED_PIPE_ITEM_PERSISTENT = RED_PIPE_ITEM_TYPE_CHANNEL_BASE,
+};
+
+
 struct AudioFrame {
     uint32_t time;
     uint32_t samples[SND_CODEC_MAX_FRAME_SIZE];
@@ -226,10 +217,10 @@ struct RecordChannelClient {
 /* 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 void snd_playback_alloc_frames(PlaybackChannelClient *playback);
+static void snd_send(SndChannelClient * client);
 
 static SndChannelClient *snd_channel_unref(SndChannelClient *client)
 {
@@ -241,6 +232,16 @@ static SndChannelClient *snd_channel_unref(SndChannelClient *client)
     return client;
 }
 
+static SndChannelClient *snd_channel_client_from_dummy(RedChannelClient *dummy)
+{
+    SndChannelClient *sound_client;
+
+    g_assert(IS_DUMMY_CHANNEL_CLIENT(dummy));
+    sound_client =  g_object_get_data(G_OBJECT(dummy), "sound-channel-client");
+
+    return sound_client;
+}
+
 static RedsState* snd_channel_get_server(SndChannelClient *client)
 {
     g_return_val_if_fail(client != NULL, NULL);
@@ -250,16 +251,14 @@ static RedsState* snd_channel_get_server(SndChannelClient *client)
 static void snd_disconnect_channel(SndChannelClient *client)
 {
     SndChannel *channel;
-    RedsState *reds;
     RedChannel *red_channel;
     uint32_t type;
 
-    if (!client || !client->stream) {
+    if (!client || !red_channel_client_is_connected(client->channel_client)) {
         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);
@@ -267,10 +266,6 @@ static void snd_disconnect_channel(SndChannelClient *client)
     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;
     snd_channel_unref(client);
     channel->connection = NULL;
 }
@@ -290,6 +285,7 @@ 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);
         }
     }
 }
@@ -298,63 +294,6 @@ static void snd_record_on_message_done(SndChannelClient *client)
 {
 }
 
-static int snd_send_data(SndChannelClient *client)
-{
-    uint32_t n;
-    RedChannelClient *rcc = client->channel_client;
-    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
-
-    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(m,
-                                               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;
@@ -400,30 +339,24 @@ 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;
-    RedChannelClient *rcc = client->channel_client;
+    SndChannelClient *snd_client = snd_channel_client_from_dummy(rcc);
+    RecordChannelClient *record_client = SPICE_CONTAINEROF(snd_client, RecordChannelClient, base);
 
-    if (!client) {
-        return FALSE;
-    }
     switch (type) {
     case SPICE_MSGC_RECORD_DATA:
         return snd_record_handle_write(record_client, size, message);
@@ -459,156 +392,23 @@ 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);
-    }
-}
-
-static inline int snd_reset_send_data(SndChannelClient *client, uint16_t verb)
-{
-    SpiceDataHeaderOpaque *header;
-    RedChannelClient *rcc = client->channel_client;
-    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
-
-    if (!client) {
-        return FALSE;
+        return red_channel_client_handle_message(rcc, size, type, message);
     }
-
-    header = &client->channel_client->priv->send_data.header;
-    spice_marshaller_reset(m);
-    header->data = spice_marshaller_reserve_space(m, header->header_size);
-    spice_marshaller_set_base(m, 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;
-    RedChannelClient *rcc = client->channel_client;
-    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
-
-    spice_marshaller_flush(m);
-    client->send_data.size = spice_marshaller_get_total_size(m);
-    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 = client->channel_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);
     migrate.flags = 0;
     spice_marshall_msg_migrate(m, &migrate);
 
-    return snd_begin_send_message(client);
+    red_channel_client_begin_send_message(rcc);
+    return TRUE;
 }
 
 static int snd_playback_send_migrate(PlaybackChannelClient *client)
@@ -625,22 +425,21 @@ static int snd_send_volume(SndChannelClient *client, uint32_t cap, int msg)
     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)) {
-        return TRUE;
+    if (!red_channel_client_test_remote_cap(rcc, cap)) {
+        return FALSE;
     }
 
     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);
     vol->nchannels = st->volume_nchannels;
     for (c = 0; c < st->volume_nchannels; ++c) {
         vol->volume[c] = st->volume[c];
     }
     spice_marshall_SpiceMsgAudioVolume(m, vol);
 
-    return snd_begin_send_message(client);
+    red_channel_client_begin_send_message(rcc);
+    return TRUE;
 }
 
 static int snd_playback_send_volume(PlaybackChannelClient *playback_client)
@@ -657,17 +456,16 @@ static int snd_send_mute(SndChannelClient *client, uint32_t cap, int msg)
     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)) {
-        return TRUE;
-    }
-
-    if (!snd_reset_send_data(client, msg)) {
+    if (!red_channel_client_test_remote_cap(rcc, cap)) {
         return FALSE;
     }
+
+    red_channel_client_init_send_data(rcc, msg);
     mute.mute = st->mute;
     spice_marshall_SpiceMsgAudioMute(m, &mute);
 
-    return snd_begin_send_message(client);
+    red_channel_client_begin_send_message(rcc);
+    return TRUE;
 }
 
 static int snd_playback_send_mute(PlaybackChannelClient *playback_client)
@@ -684,14 +482,14 @@ static int snd_playback_send_latency(PlaybackChannelClient *playback_client)
     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);
     latency_msg.latency_ms = playback_client->latency;
     spice_marshall_msg_playback_latency(m, &latency_msg);
 
-    return snd_begin_send_message(client);
+    red_channel_client_begin_send_message(rcc);
+    return TRUE;
 }
+
 static int snd_playback_send_start(PlaybackChannelClient *playback_client)
 {
     SndChannelClient *client = (SndChannelClient *)playback_client;
@@ -699,10 +497,7 @@ static int snd_playback_send_start(PlaybackChannelClient *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);
     start.channels = SPICE_INTERFACE_PLAYBACK_CHAN;
     start.frequency = SND_CHANNEL(red_channel_client_get_channel(rcc))->frequency;
     spice_assert(SPICE_INTERFACE_PLAYBACK_FMT == SPICE_INTERFACE_AUDIO_FMT_S16);
@@ -710,18 +505,19 @@ static int snd_playback_send_start(PlaybackChannelClient *playback_client)
     start.time = reds_get_mm_time();
     spice_marshall_msg_playback_start(m, &start);
 
-    return snd_begin_send_message(client);
+    red_channel_client_begin_send_message(rcc);
+    return TRUE;
 }
 
 static int snd_playback_send_stop(PlaybackChannelClient *playback_client)
 {
     SndChannelClient *client = (SndChannelClient *)playback_client;
+    RedChannelClient *rcc = client->channel_client;
 
-    if (!snd_reset_send_data(client, SPICE_MSG_PLAYBACK_STOP)) {
-        return FALSE;
-    }
+    red_channel_client_init_send_data(rcc, SPICE_MSG_PLAYBACK_STOP);
 
-    return snd_begin_send_message(client);
+    red_channel_client_begin_send_message(rcc);
+    return TRUE;
 }
 
 static int snd_playback_send_ctl(PlaybackChannelClient *playback_client)
@@ -742,9 +538,7 @@ static int snd_record_send_start(RecordChannelClient *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);
 
     start.channels = SPICE_INTERFACE_RECORD_CHAN;
     start.frequency = SND_CHANNEL(red_channel_client_get_channel(rcc))->frequency;
@@ -752,18 +546,19 @@ static int snd_record_send_start(RecordChannelClient *record_client)
     start.format = SPICE_AUDIO_FMT_S16;
     spice_marshall_msg_record_start(m, &start);
 
-    return snd_begin_send_message(client);
+    red_channel_client_begin_send_message(rcc);
+    return TRUE;
 }
 
 static int snd_record_send_stop(RecordChannelClient *record_client)
 {
     SndChannelClient *client = (SndChannelClient *)record_client;
+    RedChannelClient *rcc = client->channel_client;
 
-    if (!snd_reset_send_data(client, SPICE_MSG_RECORD_STOP)) {
-        return FALSE;
-    }
+    red_channel_client_init_send_data(rcc, SPICE_MSG_RECORD_STOP);
 
-    return snd_begin_send_message(client);
+    red_channel_client_begin_send_message(rcc);
+    return TRUE;
 }
 
 static int snd_record_send_ctl(RecordChannelClient *record_client)
@@ -805,10 +600,9 @@ static int snd_playback_send_write(PlaybackChannelClient *playback_client)
     SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
     AudioFrame *frame;
     SpiceMsgPlaybackPacket msg;
+    RedPipeItem *pipe_item = &SND_CHANNEL_CLIENT(playback_client)->persistent_pipe_item;
 
-    if (!snd_reset_send_data(client, SPICE_MSG_PLAYBACK_DATA)) {
-        return FALSE;
-    }
+    red_channel_client_init_send_data(rcc, SPICE_MSG_PLAYBACK_DATA);
 
     frame = playback_client->in_progress;
     msg.time = frame->time;
@@ -816,9 +610,10 @@ static int snd_playback_send_write(PlaybackChannelClient *playback_client)
     spice_marshall_msg_playback_data(m, &msg);
 
     if (playback_client->mode == SPICE_AUDIO_DATA_MODE_RAW) {
-        spice_marshaller_add_by_ref(m, (uint8_t *)frame->samples,
-                                    snd_codec_frame_size(playback_client->codec) *
-                                    sizeof(frame->samples[0]));
+        spice_marshaller_add_by_ref_full(m, (uint8_t *)frame->samples,
+                                         snd_codec_frame_size(playback_client->codec) *
+                                         sizeof(frame->samples[0]),
+                                         marshaller_unref_pipe_item, pipe_item);
     }
     else {
         int n = sizeof(playback_client->encode_buf);
@@ -829,10 +624,12 @@ static int snd_playback_send_write(PlaybackChannelClient *playback_client)
             snd_disconnect_channel(client);
             return FALSE;
         }
-        spice_marshaller_add_by_ref(m, playback_client->encode_buf, n);
+        spice_marshaller_add_by_ref_full(m, playback_client->encode_buf, n,
+                                         marshaller_unref_pipe_item, pipe_item);
     }
 
-    return snd_begin_send_message(client);
+    red_channel_client_begin_send_message(rcc);
+    return TRUE;
 }
 
 static int playback_send_mode(PlaybackChannelClient *playback_client)
@@ -842,167 +639,186 @@ static int playback_send_mode(PlaybackChannelClient *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);
     mode.time = reds_get_mm_time();
     mode.mode = playback_client->mode;
     spice_marshall_msg_playback_mode(m, &mode);
 
-    return snd_begin_send_message(client);
+    red_channel_client_begin_send_message(rcc);
+    return TRUE;
+}
+
+static int snd_channel_config_socket(RedChannelClient *rcc);
+
+static SndChannelClient *__new_channel(SndChannel *channel, int size, uint32_t channel_id,
+                                       RedClient *red_client,
+                                       RedsStream *stream,
+                                       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;
+
+    spice_assert(size >= sizeof(*client));
+    client = spice_malloc0(size);
+    client->refs = 1;
+    client->on_message_done = on_message_done;
+    client->cleanup = cleanup;
+
+    client->channel_client =
+        dummy_channel_client_create(RED_CHANNEL(channel), red_client, stream,
+                                    num_common_caps, common_caps, num_caps, caps);
+    if (!client->channel_client) {
+        goto error2;
+    }
+
+    /* SndChannelClient is not yet a RedChannelClient, but we still need to go from our
+     * RedChannelClient implementation (DummyChannelClient) to the SndChannelClient instance
+     * in various vfuncs
+     */
+    g_object_set_data(G_OBJECT(client->channel_client), "sound-channel-client", client);
+
+    if (!snd_channel_config_socket(RED_CHANNEL_CLIENT(client->channel_client))) {
+        goto error2;
+    }
+
+    return client;
+
+error2:
+    free(client);
+    return NULL;
 }
 
-static void snd_playback_send(void* data)
+/* This function is called when the "persistent" item is removed from the
+ * queue. Note that there is not free call as the item is allocated into
+ * SndChannelClient.
+ * This is used to have a simple item in RedChannelClient queue but to send
+ * multiple messages in a row if possible.
+ * During realtime sound transmission you usually don't want to queue too
+ * much data or having retransmission preferring instead loosing some
+ * samples.
+ */
+static void snd_persistent_pipe_item_free(struct RedPipeItem *item)
 {
-    PlaybackChannelClient *playback_client = (PlaybackChannelClient*)data;
-    SndChannelClient *client = SND_CHANNEL_CLIENT(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);
+    }
+}
+
+static void snd_send(SndChannelClient * client)
+{
+    RedChannelClient *rcc;
+
+    g_return_if_fail(client != NULL);
 
-    if (!playback_client || !snd_send_data(data)) {
+    rcc = client->channel_client;
+    if (!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, G_GNUC_UNUSED RedPipeItem *item)
+{
+    SndChannelClient *client = snd_channel_client_from_dummy(rcc);
+    PlaybackChannelClient *playback_client = SPICE_CONTAINEROF(client, PlaybackChannelClient, base);
 
     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) {
-            if (!playback_send_mode(playback_client)) {
-                return;
-            }
             client->command &= ~SND_PLAYBACK_MODE_MASK;
+            if (playback_send_mode(playback_client)) {
+                break;
+            }
         }
         if (client->command & SND_PLAYBACK_PCM_MASK) {
             spice_assert(!playback_client->in_progress && playback_client->pending_frame);
             playback_client->in_progress = playback_client->pending_frame;
             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;
+            if (snd_playback_send_write(playback_client)) {
+                break;
             }
+            spice_printerr("snd_send_playback_write failed");
         }
         if (client->command & SND_CTRL_MASK) {
-            if (!snd_playback_send_ctl(playback_client)) {
-                return;
-            }
             client->command &= ~SND_CTRL_MASK;
+            if (snd_playback_send_ctl(playback_client)) {
+                break;
+            }
         }
         if (client->command & SND_VOLUME_MASK) {
-            if (!snd_playback_send_volume(playback_client)) {
-                return;
-            }
             client->command &= ~SND_VOLUME_MASK;
+            if (snd_playback_send_volume(playback_client)) {
+                break;
+            }
         }
         if (client->command & SND_MUTE_MASK) {
-            if (!snd_playback_send_mute(playback_client)) {
-                return;
-            }
             client->command &= ~SND_MUTE_MASK;
+            if (snd_playback_send_mute(playback_client)) {
+                break;
+            }
         }
         if (client->command & SND_MIGRATE_MASK) {
-            if (!snd_playback_send_migrate(playback_client)) {
-                return;
-            }
             client->command &= ~SND_MIGRATE_MASK;
+            if (snd_playback_send_migrate(playback_client)) {
+                break;
+            }
         }
         if (client->command & SND_PLAYBACK_LATENCY_MASK) {
-            if (!snd_playback_send_latency(playback_client)) {
-                return;
-            }
             client->command &= ~SND_PLAYBACK_LATENCY_MASK;
+            if (snd_playback_send_latency(playback_client)) {
+                break;
+            }
         }
     }
+    snd_send(client);
 }
 
-static void snd_record_send(void* data)
+static void record_channel_send_item(RedChannelClient *rcc, G_GNUC_UNUSED RedPipeItem *item)
 {
-    RecordChannelClient *record_client = (RecordChannelClient*)data;
-    SndChannelClient *client = SND_CHANNEL_CLIENT(record_client);
-
-    if (!record_client || !snd_send_data(data)) {
-        return;
-    }
+    SndChannelClient *client = snd_channel_client_from_dummy(rcc);
+    RecordChannelClient *record_client = SPICE_CONTAINEROF(client, RecordChannelClient, base);
 
     client->command &= SND_CTRL_MASK|SND_VOLUME_MUTE_MASK|SND_MIGRATE_MASK;
     while (client->command) {
         if (client->command & SND_CTRL_MASK) {
-            if (!snd_record_send_ctl(record_client)) {
-                return;
-            }
             client->command &= ~SND_CTRL_MASK;
+            if (snd_record_send_ctl(record_client)) {
+                break;
+            }
         }
         if (client->command & SND_VOLUME_MASK) {
-            if (!snd_record_send_volume(record_client)) {
-                return;
-            }
             client->command &= ~SND_VOLUME_MASK;
+            if (snd_record_send_volume(record_client)) {
+                break;
+            }
         }
         if (client->command & SND_MUTE_MASK) {
-            if (!snd_record_send_mute(record_client)) {
-                return;
-            }
             client->command &= ~SND_MUTE_MASK;
+            if (snd_record_send_mute(record_client)) {
+                break;
+            }
         }
         if (client->command & SND_MIGRATE_MASK) {
-            if (!snd_record_send_migrate(record_client)) {
-                return;
-            }
             client->command &= ~SND_MIGRATE_MASK;
+            if (snd_record_send_migrate(record_client)) {
+                break;
+            }
         }
     }
-}
-
-static int snd_channel_config_socket(RedChannelClient *rcc);
-
-static SndChannelClient *__new_channel(SndChannel *channel, int size, uint32_t channel_id,
-                                       RedClient *red_client,
-                                       RedsStream *stream,
-                                       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;
-    RedsState *reds = red_channel_get_server(RED_CHANNEL(channel));
-
-    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->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);
-
-    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, stream,
-                                    num_common_caps, common_caps, num_caps, caps);
-    if (!client->channel_client) {
-        goto error2;
-    }
-    if (!snd_channel_config_socket(RED_CHANNEL_CLIENT(client->channel_client))) {
-        goto error2;
-    }
-
-    return client;
-
-error2:
-    free(client);
-    return NULL;
+    snd_send(client);
 }
 
 static int snd_channel_config_socket(RedChannelClient *rcc)
@@ -1056,20 +872,31 @@ static int snd_channel_config_socket(RedChannelClient *rcc)
 
 static void snd_channel_on_disconnect(RedChannelClient *rcc)
 {
-    g_assert_not_reached();
+    SndChannel *channel = SND_CHANNEL(red_channel_client_get_channel(rcc));
+    if (channel->connection && rcc == RED_CHANNEL_CLIENT(channel->connection)) {
+        channel->connection = NULL;
+    }
 }
 
 static uint8_t*
 snd_channel_client_alloc_recv_buf(RedChannelClient *rcc, uint16_t type, uint32_t size)
 {
-    g_assert_not_reached();
+    SndChannelClient *client = snd_channel_client_from_dummy(rcc);
+    // If message is too big allocate one, this should never happen
+    if (size > sizeof(client->receive_buf)) {
+        return spice_malloc(size);
+    }
+    return client->receive_buf;
 }
 
 static void
 snd_channel_client_release_recv_buf(RedChannelClient *rcc, uint16_t type, uint32_t size,
                                     uint8_t *msg)
 {
-    g_assert_not_reached();
+    SndChannelClient *client = snd_channel_client_from_dummy(rcc);
+    if (msg != client->receive_buf) {
+        free(msg);
+    }
 }
 
 static void snd_disconnect_channel_client(RedChannelClient *rcc)
@@ -1141,7 +968,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;
     }
@@ -1165,7 +992,7 @@ SPICE_GNUC_VISIBLE void spice_server_playback_stop(SpicePlaybackInstance *sin)
     client->active = FALSE;
     if (client->client_active) {
         snd_set_command(client, SND_CTRL_MASK);
-        snd_playback_send(client);
+        snd_send(client);
     } else {
         client->command &= ~SND_CTRL_MASK;
         client->command &= ~SND_PLAYBACK_PCM_MASK;
@@ -1231,7 +1058,7 @@ SPICE_GNUC_VISIBLE void spice_server_playback_put_samples(SpicePlaybackInstance
     frame->time = reds_get_mm_time();
     playback_client->pending_frame = frame;
     snd_set_command(SND_CHANNEL_CLIENT(playback_client), SND_PLAYBACK_PCM_MASK);
-    snd_playback_send(SND_CHANNEL_CLIENT(playback_client));
+    snd_send(SND_CHANNEL_CLIENT(playback_client));
 }
 
 void snd_set_playback_latency(RedClient *client, uint32_t latency)
@@ -1250,7 +1077,7 @@ void snd_set_playback_latency(RedClient *client, uint32_t latency)
 
                 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");
             }
@@ -1327,8 +1154,6 @@ static void snd_set_playback_peer(RedChannel *red_channel, RedClient *client, Re
                                                                    SPICE_CHANNEL_PLAYBACK,
                                                                    client,
                                                                    stream,
-                                                                   snd_playback_send,
-                                                                   snd_playback_handle_message,
                                                                    snd_playback_on_message_done,
                                                                    snd_playback_cleanup,
                                                                    common_caps, num_common_caps,
@@ -1363,7 +1188,7 @@ static void snd_set_playback_peer(RedChannel *red_channel, RedClient *client, Re
     if (channel->active) {
         snd_playback_start(channel);
     }
-    snd_playback_send(channel->connection);
+    snd_send(channel->connection);
 }
 
 static void snd_record_migrate_channel_client(RedChannelClient *rcc)
@@ -1377,7 +1202,7 @@ static void snd_record_migrate_channel_client(RedChannelClient *rcc)
     if (channel->connection) {
         spice_assert(channel->connection->channel_client == rcc);
         snd_set_command(channel->connection, SND_MIGRATE_MASK);
-        snd_record_send(channel->connection);
+        snd_send(channel->connection);
     }
 }
 
@@ -1427,7 +1252,7 @@ static void snd_record_start(SndChannel *channel)
     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;
     }
@@ -1449,7 +1274,7 @@ SPICE_GNUC_VISIBLE void spice_server_record_stop(SpiceRecordInstance *sin)
     client->active = FALSE;
     if (client->client_active) {
         snd_set_command(client, SND_CTRL_MASK);
-        snd_record_send(client);
+        snd_send(client);
     } else {
         client->command &= ~SND_CTRL_MASK;
     }
@@ -1560,8 +1385,6 @@ static void snd_set_record_peer(RedChannel *red_channel, RedClient *client, Reds
                                                                SPICE_CHANNEL_RECORD,
                                                                client,
                                                                stream,
-                                                               snd_record_send,
-                                                               snd_record_handle_message,
                                                                snd_record_on_message_done,
                                                                snd_record_cleanup,
                                                                common_caps, num_common_caps,
@@ -1575,7 +1398,7 @@ static void snd_set_record_peer(RedChannel *red_channel, RedClient *client, Reds
     if (channel->active) {
         snd_record_start(channel);
     }
-    snd_record_send(channel->connection);
+    snd_send(channel->connection);
 }
 
 static void snd_playback_migrate_channel_client(RedChannelClient *rcc)
@@ -1590,7 +1413,7 @@ static void snd_playback_migrate_channel_client(RedChannelClient *rcc)
     if (channel->connection) {
         spice_assert(channel->connection->channel_client == rcc);
         snd_set_command(channel->connection, SND_MIGRATE_MASK);
-        snd_playback_send(channel->connection);
+        snd_send(channel->connection);
     }
 }
 
@@ -1662,8 +1485,13 @@ static void
 playback_channel_class_init(PlaybackChannelClass *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)
@@ -1708,8 +1536,13 @@ static void
 record_channel_class_init(RecordChannelClass *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)
commit cb0e45cd1661e5d21cb338818122253b908eabb4
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Mon Jan 9 18:41:54 2017 +0100

    sound: Remove code from spice_server_record_get_samples()
    
    The removed code was trying to read data when
    spice_server_record_get_samples() is called. Since reading of data is
    event-driven anyway (see snd_event), it's redundant to try
    again to read more data.
    This commit removes this code as this will some refactoring easier in
    the next commits.
    
    Based on a patch from Frediano Ziglio <fziglio at redhat.com>
    
    Signed-off-by: Christophe Fergeau <cfergeau at redhat.com>
    Acked-by: Frediano Ziglio <fziglio at redhat.com>

diff --git a/server/sound.c b/server/sound.c
index ac69bfd..f27a53d 100644
--- a/server/sound.c
+++ b/server/sound.c
@@ -1474,15 +1474,6 @@ SPICE_GNUC_VISIBLE uint32_t spice_server_record_get_samples(SpiceRecordInstance
 
     len = MIN(record_client->write_pos - record_client->read_pos, bufsize);
 
-    if (len < bufsize) {
-        SndChannel *channel = SND_CHANNEL(red_channel_client_get_channel(client->channel_client));
-        snd_receive(client);
-        if (!channel->connection) {
-            return 0;
-        }
-        len = MIN(record_client->write_pos - record_client->read_pos, bufsize);
-    }
-
     read_pos = record_client->read_pos % RECORD_SAMPLES_SIZE;
     record_client->read_pos += len;
     now = MIN(len, RECORD_SAMPLES_SIZE - read_pos);
commit 4bb9c1fe5606b8dcbc7d10a28bd88a45159e0198
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Mon Jan 9 17:37:51 2017 +0100

    sound: Remove SndChannelClient::channel
    
    We can get it from our DummyChannelClient rather than storing it in
    SndChannelClient.
    
    Based on a patch from Frediano Ziglio <fziglio at redhat.com>
    
    Signed-off-by: Christophe Fergeau <cfergeau at redhat.com>
    Acked-by: Frediano Ziglio <fziglio at redhat.com>

diff --git a/server/sound.c b/server/sound.c
index a0b6675..ac69bfd 100644
--- a/server/sound.c
+++ b/server/sound.c
@@ -92,7 +92,6 @@ typedef void (*snd_channel_cleanup_channel_proc)(SndChannelClient *client);
 /* Connects an audio client to a Spice client */
 struct SndChannelClient {
     RedsStream *stream;
-    SndChannel *channel;
     spice_parse_channel_func_t parser;
     int refs;
 
@@ -245,7 +244,7 @@ static SndChannelClient *snd_channel_unref(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));
+    return red_channel_get_server(red_channel_client_get_channel(client->channel_client));
 }
 
 static void snd_disconnect_channel(SndChannelClient *client)
@@ -264,7 +263,7 @@ static void snd_disconnect_channel(SndChannelClient *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;
+    channel = SND_CHANNEL(red_channel);
     client->cleanup(client);
     red_channel_client_disconnect(channel->connection->channel_client);
     channel->connection->channel_client = NULL;
@@ -420,6 +419,7 @@ static int snd_playback_handle_message(SndChannelClient *client, size_t size, ui
 static int snd_record_handle_message(SndChannelClient *client, size_t size, uint32_t type, void *message)
 {
     RecordChannelClient *record_client = (RecordChannelClient *)client;
+    RedChannelClient *rcc = client->channel_client;
 
     if (!client) {
         return FALSE;
@@ -429,7 +429,7 @@ static int snd_record_handle_message(SndChannelClient *client, size_t size, uint
         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)) {
@@ -622,7 +622,8 @@ static int snd_send_volume(SndChannelClient *client, uint32_t cap, int msg)
     uint8_t c;
     RedChannelClient *rcc = client->channel_client;
     SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
-    SpiceVolumeState *st = &client->channel->volume;
+    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)) {
         return TRUE;
@@ -653,7 +654,8 @@ static int snd_send_mute(SndChannelClient *client, uint32_t cap, int msg)
     SpiceMsgAudioMute mute;
     RedChannelClient *rcc = client->channel_client;
     SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
-    SpiceVolumeState *st = &client->channel->volume;
+    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)) {
         return TRUE;
@@ -702,7 +704,7 @@ static int snd_playback_send_start(PlaybackChannelClient *playback_client)
     }
 
     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();
@@ -745,7 +747,7 @@ static int snd_record_send_start(RecordChannelClient *record_client)
     }
 
     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(m, &start);
@@ -970,7 +972,6 @@ static SndChannelClient *__new_channel(SndChannel *channel, int size, uint32_t c
     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);
@@ -1474,7 +1475,7 @@ SPICE_GNUC_VISIBLE uint32_t spice_server_record_get_samples(SpiceRecordInstance
     len = MIN(record_client->write_pos - record_client->read_pos, bufsize);
 
     if (len < bufsize) {
-        SndChannel *channel = client->channel;
+        SndChannel *channel = SND_CHANNEL(red_channel_client_get_channel(client->channel_client));
         snd_receive(client);
         if (!channel->connection) {
             return 0;
commit bc2a51043894e6ca44fef354de88f8083ab9104d
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Thu Jan 5 14:31:33 2017 +0100

    sound: Remove SndChannelClient::send_data::marshaller
    
    We can use the marshaller provided by the dummy RedChannelClient
    associated with SndChannelClient.
    
    Based on a patch from Frediano Ziglio <fziglio at redhat.com>
    
    Signed-off-by: Christophe Fergeau <cfergeau at redhat.com>
    Acked-by: Frediano Ziglio <fziglio at redhat.com>

diff --git a/server/sound.c b/server/sound.c
index b24d89e..a0b6675 100644
--- a/server/sound.c
+++ b/server/sound.c
@@ -26,7 +26,6 @@
 #include <netinet/ip.h>
 #include <netinet/tcp.h>
 
-#include <common/marshaller.h>
 #include <common/generated_server_marshallers.h>
 #include <common/snd_codec.h>
 
@@ -41,7 +40,6 @@
 #include "red-channel-client-private.h"
 #include "red-client.h"
 #include "sound.h"
-#include "demarshallers.h"
 #include "main-channel-client.h"
 
 #ifndef IOV_MAX
@@ -108,7 +106,6 @@ struct SndChannelClient {
 
     struct {
         uint64_t serial;
-        SpiceMarshaller *marshaller;
         uint32_t size;
         uint32_t pos;
     } send_data;
@@ -275,7 +272,6 @@ static void snd_disconnect_channel(SndChannelClient *client)
     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;
 }
@@ -306,6 +302,8 @@ static void snd_record_on_message_done(SndChannelClient *client)
 static int snd_send_data(SndChannelClient *client)
 {
     uint32_t n;
+    RedChannelClient *rcc = client->channel_client;
+    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
 
     if (!client) {
         return FALSE;
@@ -330,7 +328,7 @@ static int snd_send_data(SndChannelClient *client)
             break;
         }
 
-        vec_size = spice_marshaller_fill_iovec(client->send_data.marshaller,
+        vec_size = spice_marshaller_fill_iovec(m,
                                                vec, IOV_MAX, client->send_data.pos);
         n = reds_stream_writev(client->stream, vec, vec_size);
         if (n == -1) {
@@ -562,17 +560,17 @@ static void snd_event(int fd, int event, void *data)
 static inline int snd_reset_send_data(SndChannelClient *client, uint16_t verb)
 {
     SpiceDataHeaderOpaque *header;
+    RedChannelClient *rcc = client->channel_client;
+    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
 
     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);
+    spice_marshaller_reset(m);
+    header->data = spice_marshaller_reserve_space(m, header->header_size);
+    spice_marshaller_set_base(m, header->header_size);
     client->send_data.pos = 0;
     header->set_msg_size(header, 0);
     header->set_msg_type(header, verb);
@@ -588,16 +586,19 @@ static inline int snd_reset_send_data(SndChannelClient *client, uint16_t verb)
 static int snd_begin_send_message(SndChannelClient *client)
 {
     SpiceDataHeaderOpaque *header = &client->channel_client->priv->send_data.header;
+    RedChannelClient *rcc = client->channel_client;
+    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
 
-    spice_marshaller_flush(client->send_data.marshaller);
-    client->send_data.size = spice_marshaller_get_total_size(client->send_data.marshaller);
+    spice_marshaller_flush(m);
+    client->send_data.size = spice_marshaller_get_total_size(m);
     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)
 {
-    SpiceMarshaller *m = client->send_data.marshaller;
+    RedChannelClient *rcc = client->channel_client;
+    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
     SpiceMsgMigrate migrate;
 
     if (!snd_reset_send_data(client, SPICE_MSG_MIGRATE)) {
@@ -619,8 +620,9 @@ static int snd_send_volume(SndChannelClient *client, uint32_t cap, int msg)
 {
     SpiceMsgAudioVolume *vol;
     uint8_t c;
+    RedChannelClient *rcc = client->channel_client;
+    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
     SpiceVolumeState *st = &client->channel->volume;
-    SpiceMarshaller *m = client->send_data.marshaller;
 
     if (!red_channel_client_test_remote_cap(client->channel_client, cap)) {
         return TRUE;
@@ -649,8 +651,9 @@ static int snd_playback_send_volume(PlaybackChannelClient *playback_client)
 static int snd_send_mute(SndChannelClient *client, uint32_t cap, int msg)
 {
     SpiceMsgAudioMute mute;
+    RedChannelClient *rcc = client->channel_client;
+    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
     SpiceVolumeState *st = &client->channel->volume;
-    SpiceMarshaller *m = client->send_data.marshaller;
 
     if (!red_channel_client_test_remote_cap(client->channel_client, cap)) {
         return TRUE;
@@ -674,7 +677,8 @@ static int snd_playback_send_mute(PlaybackChannelClient *playback_client)
 static int snd_playback_send_latency(PlaybackChannelClient *playback_client)
 {
     SndChannelClient *client = SND_CHANNEL_CLIENT(playback_client);
-    SpiceMarshaller *m = client->send_data.marshaller;
+    RedChannelClient *rcc = client->channel_client;
+    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
     SpiceMsgPlaybackLatency latency_msg;
 
     spice_debug("latency %u", playback_client->latency);
@@ -689,7 +693,8 @@ static int snd_playback_send_latency(PlaybackChannelClient *playback_client)
 static int snd_playback_send_start(PlaybackChannelClient *playback_client)
 {
     SndChannelClient *client = (SndChannelClient *)playback_client;
-    SpiceMarshaller *m = client->send_data.marshaller;
+    RedChannelClient *rcc = client->channel_client;
+    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
     SpiceMsgPlaybackStart start;
 
     if (!snd_reset_send_data(client, SPICE_MSG_PLAYBACK_START)) {
@@ -731,7 +736,8 @@ static int snd_playback_send_ctl(PlaybackChannelClient *playback_client)
 static int snd_record_send_start(RecordChannelClient *record_client)
 {
     SndChannelClient *client = (SndChannelClient *)record_client;
-    SpiceMarshaller *m = client->send_data.marshaller;
+    RedChannelClient *rcc = client->channel_client;
+    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
     SpiceMsgRecordStart start;
 
     if (!snd_reset_send_data(client, SPICE_MSG_RECORD_START)) {
@@ -793,7 +799,8 @@ static int snd_record_send_migrate(RecordChannelClient *record_client)
 static int snd_playback_send_write(PlaybackChannelClient *playback_client)
 {
     SndChannelClient *client = (SndChannelClient *)playback_client;
-    SpiceMarshaller *m = client->send_data.marshaller;
+    RedChannelClient *rcc = client->channel_client;
+    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
     AudioFrame *frame;
     SpiceMsgPlaybackPacket msg;
 
@@ -829,7 +836,8 @@ static int snd_playback_send_write(PlaybackChannelClient *playback_client)
 static int playback_send_mode(PlaybackChannelClient *playback_client)
 {
     SndChannelClient *client = (SndChannelClient *)playback_client;
-    SpiceMarshaller *m = client->send_data.marshaller;
+    RedChannelClient *rcc = client->channel_client;
+    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
     SpiceMsgPlaybackMode mode;
 
     if (!snd_reset_send_data(client, SPICE_MSG_PLAYBACK_MODE)) {
@@ -966,7 +974,6 @@ static SndChannelClient *__new_channel(SndChannel *channel, int size, uint32_t c
     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);
commit 85b73636d952c21f400de2f5fe000ceb7f47a1aa
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Mon Jan 9 15:10:13 2017 +0100

    sound: Add sanity checks in snd_{playback,record}_send
    
    Filter out commands which should not happen. Should it be a
    g_warn_if_fail() or such instead?
    
    Based on a patch from Frediano Ziglio <fziglio at redhat.com>
    
    Signed-off-by: Christophe Fergeau <cfergeau at redhat.com>
    Acked-by: Frediano Ziglio <fziglio at redhat.com>

diff --git a/server/sound.c b/server/sound.c
index e04f1e7..b24d89e 100644
--- a/server/sound.c
+++ b/server/sound.c
@@ -851,6 +851,9 @@ static void snd_playback_send(void* data)
         return;
     }
 
+    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) {
             if (!playback_send_mode(playback_client)) {
@@ -910,6 +913,7 @@ static void snd_record_send(void* data)
         return;
     }
 
+    client->command &= SND_CTRL_MASK|SND_VOLUME_MUTE_MASK|SND_MIGRATE_MASK;
     while (client->command) {
         if (client->command & SND_CTRL_MASK) {
             if (!snd_record_send_ctl(record_client)) {
commit c4a58c90c669aba597467e4252a48120b9742194
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Wed Jan 11 09:26:58 2017 +0100

    sound: Implement snd_channel_config_socket
    
    This is in preparation for switching SndChannelClient into a proper
    RedChannelClient. The prototype of the new helper matches what is
    expected from the RedChannel::config_socket vfunc.
    
    To be able to achieve that, this commit associates the sound channel
    RedsStream instance with the DummyChannelClient instance we have, and
    then call snd_channel_config_socket() on that instance.
    
    Based on a patch from Frediano Ziglio <fziglio at redhat.com>
    
    Signed-off-by: Christophe Fergeau <cfergeau at redhat.com>
    Acked-by: Frediano Ziglio <fziglio at redhat.com>

diff --git a/server/dummy-channel-client.c b/server/dummy-channel-client.c
index 61d5683..4efaa31 100644
--- a/server/dummy-channel-client.c
+++ b/server/dummy-channel-client.c
@@ -104,6 +104,7 @@ dummy_channel_client_init(DummyChannelClient *self)
 
 RedChannelClient* dummy_channel_client_create(RedChannel *channel,
                                               RedClient  *client,
+                                              RedsStream *stream,
                                               int num_common_caps,
                                               uint32_t *common_caps,
                                               int num_caps, uint32_t *caps)
@@ -125,6 +126,7 @@ RedChannelClient* dummy_channel_client_create(RedChannel *channel,
                          NULL, NULL,
                          "channel", channel,
                          "client", client,
+                         "stream", stream,
                          "caps", caps_array,
                          "common-caps", common_caps_array,
                          NULL);
diff --git a/server/dummy-channel-client.h b/server/dummy-channel-client.h
index 8013aa2..54e0e6c 100644
--- a/server/dummy-channel-client.h
+++ b/server/dummy-channel-client.h
@@ -56,6 +56,7 @@ GType dummy_channel_client_get_type(void) G_GNUC_CONST;
 
 RedChannelClient *dummy_channel_client_create(RedChannel *channel,
                                               RedClient  *client,
+                                              RedsStream *stream,
                                               int num_common_caps, uint32_t *common_caps,
                                               int num_caps, uint32_t *caps);
 
diff --git a/server/sound.c b/server/sound.c
index 2165644..e04f1e7 100644
--- a/server/sound.c
+++ b/server/sound.c
@@ -938,6 +938,8 @@ static void snd_record_send(void* data)
     }
 }
 
+static int snd_channel_config_socket(RedChannelClient *rcc);
+
 static SndChannelClient *__new_channel(SndChannel *channel, int size, uint32_t channel_id,
                                        RedClient *red_client,
                                        RedsStream *stream,
@@ -949,19 +951,63 @@ static SndChannelClient *__new_channel(SndChannel *channel, int size, uint32_t c
                                        uint32_t *caps, int num_caps)
 {
     SndChannelClient *client;
+    RedsState *reds = red_channel_get_server(RED_CHANNEL(channel));
+
+    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, stream,
+                                    num_common_caps, common_caps, num_caps, caps);
+    if (!client->channel_client) {
+        goto error2;
+    }
+    if (!snd_channel_config_socket(RED_CHANNEL_CLIENT(client->channel_client))) {
+        goto error2;
+    }
+
+    return client;
+
+error2:
+    free(client);
+    return NULL;
+}
+
+static int snd_channel_config_socket(RedChannelClient *rcc)
+{
     int delay_val;
     int flags;
 #ifdef SO_PRIORITY
     int priority;
 #endif
     int tos;
+    RedsStream *stream = red_channel_client_get_stream(rcc);
+    RedClient *red_client = red_channel_client_get_client(rcc);
     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;
+        return FALSE;
     }
 
 #ifdef SO_PRIORITY
@@ -990,51 +1036,10 @@ static SndChannelClient *__new_channel(SndChannel *channel, int size, uint32_t c
 
     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 FALSE;
     }
-    return client;
 
-error2:
-    free(client);
-
-error1:
-    reds_stream_free(stream);
-    return NULL;
-}
-
-static int snd_channel_config_socket(RedChannelClient *rcc)
-{
-    g_assert_not_reached();
+    return TRUE;
 }
 
 static void snd_channel_on_disconnect(RedChannelClient *rcc)
commit f5a972fdbf340fa1fdd7fc6a71d7fa887c24f7e2
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Thu Jan 5 16:32:45 2017 +0100

    sound: Rework spice_server_playback_get_buffer() error handling
    
    The main goal of this commit is to avoid to dereference 'client' before
    it's checked for NULL. This meant splitting one error condition in 2
    separate ones.
    
    This also sets default values for the 'frame' and 'num-samples'
    out parameters so that we just have to return on error conditions.
    
    Based on a patch from Frediano Ziglio <fziglio at redhat.com>
    
    Signed-off-by: Christophe Fergeau <cfergeau at redhat.com>
    Acked-by: Frediano Ziglio <fziglio at redhat.com>

diff --git a/server/sound.c b/server/sound.c
index bacd340..2165644 100644
--- a/server/sound.c
+++ b/server/sound.c
@@ -1166,11 +1166,14 @@ 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 = SPICE_CONTAINEROF(client, PlaybackChannelClient, base);
+    if (!playback_client->free_frames) {
         return;
     }
     spice_assert(client->active);


More information about the Spice-commits mailing list