[Spice-devel,v6,01/18] sound: Convert SndChannel to GObject

Submitted by Frediano Ziglio on Dec. 2, 2016, 10:53 a.m.

Details

Message ID f7610168c415ec48635d7b8b31a2881b82550b9c.1480676032.git-series.fziglio@redhat.com
State Accepted
Commit 00a701e787475d6a29f59b87adef4807bba12c77
Headers show
Series "Remove DummyChannel* objects" ( rev: 1 ) in Spice

Not browsing as part of any series.

Commit Message

Frediano Ziglio Dec. 2, 2016, 10:53 a.m.
Stops using the dummy channel.
Data handling still goes through DummyChannelClient which is why
empty implementation of some required vfuncs is working.

Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
---
 server/sound.c | 245 ++++++++++++++++++++++++++++++++++----------------
 1 file changed, 171 insertions(+), 74 deletions(-)

Patch hide | download patch | download mbox

diff --git a/server/sound.c b/server/sound.c
index 5b70c2c..912dc7a 100644
--- a/server/sound.c
+++ b/server/sound.c
@@ -74,13 +74,17 @@  enum PlaybackCommand {
 #define SND_PLAYBACK_LATENCY_MASK ( 1 << SND_PLAYBACK_LATENCY)
 
 typedef struct SndChannelClient SndChannelClient;
+typedef struct SndChannel SndChannel;
+typedef struct PlaybackChannelClient PlaybackChannelClient;
+typedef struct RecordChannelClient RecordChannelClient;
+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);
 
-typedef struct SndChannel SndChannel;
-
 /* Connects an audio client to a Spice client */
 struct SndChannelClient {
     RedsStream *stream;
@@ -116,8 +120,6 @@  struct SndChannelClient {
     snd_channel_cleanup_channel_proc cleanup;
 };
 
-typedef struct PlaybackChannelClient PlaybackChannelClient;
-
 typedef struct AudioFrame AudioFrame;
 struct AudioFrame {
     uint32_t time;
@@ -144,9 +146,14 @@  typedef struct SpiceVolumeState {
     int mute;
 } SpiceVolumeState;
 
+#define TYPE_SND_CHANNEL snd_channel_get_type()
+#define SND_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_SND_CHANNEL, SndChannel))
+GType snd_channel_get_type(void) G_GNUC_CONST;
+
 /* Base class for SpicePlaybackState and SpiceRecordState */
 struct SndChannel {
-    RedChannel *base_channel;
+    RedChannel parent;
+
     SndChannelClient *connection; /* Only one client is supported */
     SndChannel *next; /* For the global SndChannel list */
 
@@ -155,15 +162,45 @@  struct SndChannel {
     uint32_t frequency;
 };
 
+typedef struct SndChannelClass {
+    RedChannelClass parent_class;
+} SndChannelClass;
+
+G_DEFINE_TYPE(SndChannel, snd_channel, RED_TYPE_CHANNEL)
+
+
+#define TYPE_PLAYBACK_CHANNEL playback_channel_get_type()
+#define PLAYBACK_CHANNEL(obj) \
+    (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_PLAYBACK_CHANNEL, PlaybackChannel))
+GType playback_channel_get_type(void) G_GNUC_CONST;
+
 struct SpicePlaybackState {
-    struct SndChannel channel;
+    SndChannel channel;
 };
 
+typedef struct PlaybackChannelClass {
+    SndChannelClass parent_class;
+} PlaybackChannelClass;
+
+G_DEFINE_TYPE(PlaybackChannel, playback_channel, TYPE_SND_CHANNEL)
+
+
+#define TYPE_RECORD_CHANNEL record_channel_get_type()
+#define RECORD_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_RECORD_CHANNEL, RecordChannel))
+GType record_channel_get_type(void) G_GNUC_CONST;
+
 struct SpiceRecordState {
-    struct SndChannel channel;
+    SndChannel channel;
 };
 
-typedef struct RecordChannelClient {
+typedef struct RecordChannelClass {
+    SndChannelClass parent_class;
+} RecordChannelClass;
+
+G_DEFINE_TYPE(RecordChannel, record_channel, TYPE_SND_CHANNEL)
+
+
+struct RecordChannelClient {
     SndChannelClient base;
     uint32_t samples[RECORD_SAMPLES_SIZE];
     uint32_t write_pos;
@@ -173,7 +210,7 @@  typedef struct RecordChannelClient {
     uint32_t start_time;
     SndCodec codec;
     uint8_t  decode_buf[SND_CODEC_MAX_FRAME_BYTES];
-} RecordChannelClient;
+};
 
 /* A list of all Spice{Playback,Record}State objects */
 static SndChannel *snd_channels;
@@ -201,7 +238,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(client->channel->base_channel);
+    return red_channel_get_server(RED_CHANNEL(client->channel));
 }
 
 static void snd_disconnect_channel(SndChannelClient *client)
@@ -892,7 +929,7 @@  static SndChannelClient *__new_channel(SndChannel *channel, int size, uint32_t c
 #endif
     int tos;
     MainChannelClient *mcc = red_client_get_main(red_client);
-    RedsState *reds = red_channel_get_server(channel->base_channel);
+    RedsState *reds = red_channel_get_server(RED_CHANNEL(channel));
 
     spice_assert(stream);
     if ((flags = fcntl(stream->socket, F_GETFL)) == -1) {
@@ -953,7 +990,7 @@  static SndChannelClient *__new_channel(SndChannel *channel, int size, uint32_t c
     client->cleanup = cleanup;
 
     client->channel_client =
-        dummy_channel_client_create(channel->base_channel, red_client,
+        dummy_channel_client_create(RED_CHANNEL(channel), red_client,
                                     num_common_caps, common_caps, num_caps, caps);
     if (!client->channel_client) {
         goto error2;
@@ -968,14 +1005,36 @@  error1:
     return NULL;
 }
 
+static int snd_channel_config_socket(RedChannelClient *rcc)
+{
+    g_assert_not_reached();
+}
+
+static void snd_channel_on_disconnect(RedChannelClient *rcc)
+{
+    g_assert_not_reached();
+}
+
+static uint8_t*
+snd_channel_client_alloc_recv_buf(RedChannelClient *rcc, uint16_t type, uint32_t size)
+{
+    g_assert_not_reached();
+}
+
+static void
+snd_channel_client_release_recv_buf(RedChannelClient *rcc, uint16_t type, uint32_t size,
+                                    uint8_t *msg)
+{
+    g_assert_not_reached();
+}
+
 static void snd_disconnect_channel_client(RedChannelClient *rcc)
 {
     SndChannel *channel;
     RedChannel *red_channel = red_channel_client_get_channel(rcc);
     uint32_t type;
 
-    spice_assert(red_channel);
-    channel = (SndChannel *)g_object_get_data(G_OBJECT(red_channel), "sound-channel");
+    channel = SND_CHANNEL(red_channel);
     spice_assert(channel);
     g_object_get(red_channel, "channel-type", &type, NULL);
 
@@ -1126,7 +1185,7 @@  void snd_set_playback_latency(RedClient *client, uint32_t latency)
 
     for (; now; now = now->next) {
         uint32_t type;
-        g_object_get(now->base_channel, "channel-type", &type, NULL);
+        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) {
 
@@ -1161,7 +1220,7 @@  static int snd_desired_audio_mode(int playback_compression, int frequency,
 
 static void on_new_playback_channel(SndChannel *channel, SndChannelClient *snd_channel)
 {
-    RedsState *reds = red_channel_get_server(channel->base_channel);
+    RedsState *reds = red_channel_get_server(RED_CHANNEL(channel));
 
     spice_assert(snd_channel);
 
@@ -1193,7 +1252,7 @@  static void snd_set_playback_peer(RedChannel *red_channel, RedClient *client, Re
                                   int migration, int num_common_caps, uint32_t *common_caps,
                                   int num_caps, uint32_t *caps)
 {
-    SndChannel *channel = g_object_get_data(G_OBJECT(red_channel), "sound-channel");
+    SndChannel *channel = SND_CHANNEL(red_channel);
     PlaybackChannelClient *playback_client;
 
     snd_disconnect_channel(channel->connection);
@@ -1249,9 +1308,7 @@  static void snd_record_migrate_channel_client(RedChannelClient *rcc)
     SndChannel *channel;
     RedChannel *red_channel = red_channel_client_get_channel(rcc);
 
-    spice_debug(NULL);
-    spice_assert(red_channel);
-    channel = (SndChannel *)g_object_get_data(G_OBJECT(red_channel), "sound-channel");
+    channel = SND_CHANNEL(red_channel);
     spice_assert(channel);
 
     if (channel->connection) {
@@ -1389,7 +1446,7 @@  static uint32_t snd_get_best_rate(SndChannelClient *client, uint32_t cap_opus)
 
 static void snd_set_rate(SndChannel *channel, uint32_t frequency, uint32_t cap_opus)
 {
-    RedChannel *red_channel = channel->base_channel;
+    RedChannel *red_channel = RED_CHANNEL(channel);
     channel->frequency = frequency;
     if (red_channel && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, frequency)) {
         red_channel_set_cap(red_channel, cap_opus);
@@ -1439,7 +1496,7 @@  static void snd_set_record_peer(RedChannel *red_channel, RedClient *client, Reds
                                 int migration, int num_common_caps, uint32_t *common_caps,
                                 int num_caps, uint32_t *caps)
 {
-    SndChannel *channel = g_object_get_data(G_OBJECT(red_channel), "sound-channel");
+    SndChannel *channel = SND_CHANNEL(red_channel);
     RecordChannelClient *record_client;
 
     snd_disconnect_channel(channel->connection);
@@ -1473,8 +1530,7 @@  static void snd_playback_migrate_channel_client(RedChannelClient *rcc)
     SndChannel *channel;
     RedChannel *red_channel = red_channel_client_get_channel(rcc);
 
-    spice_assert(red_channel);
-    channel = (SndChannel *)g_object_get_data(G_OBJECT(red_channel), "sound-channel");
+    channel = SND_CHANNEL(red_channel);
     spice_assert(channel);
     spice_debug(NULL);
 
@@ -1504,60 +1560,113 @@  static void remove_channel(SndChannel *channel)
     spice_printerr("not found");
 }
 
-void snd_attach_playback(RedsState *reds, SpicePlaybackInstance *sin)
+static void
+snd_channel_init(SndChannel *self)
 {
-    SndChannel *playback;
-    RedChannel *red_channel;
-    ClientCbs client_cbs = { NULL, };
+    self->frequency = SND_CODEC_CELT_PLAYBACK_FREQ; /* Default to the legacy rate */
+}
 
-    sin->st = spice_new0(SpicePlaybackState, 1);
-    playback = &sin->st->channel;
-    playback->frequency = SND_CODEC_CELT_PLAYBACK_FREQ; /* Default to the legacy rate */
+static void
+snd_channel_class_init(SndChannelClass *klass)
+{
+    RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass);
 
-    // TODO: Make RedChannel base of channel? instead of assigning it to channel->data
-    red_channel = dummy_channel_new(reds, SPICE_CHANNEL_PLAYBACK, 0);
+    channel_class->config_socket = snd_channel_config_socket;
+    channel_class->alloc_recv_buf = snd_channel_client_alloc_recv_buf;
+    channel_class->release_recv_buf = snd_channel_client_release_recv_buf;
+    channel_class->on_disconnect = snd_channel_on_disconnect;
+}
+
+static void
+playback_channel_init(PlaybackChannel *self)
+{
+}
+
+static void
+playback_channel_constructed(GObject *object)
+{
+    ClientCbs client_cbs = { NULL, };
+    SndChannel *self = SND_CHANNEL(object);
+    RedsState *reds = red_channel_get_server(RED_CHANNEL(self));
+
+    G_OBJECT_CLASS(playback_channel_parent_class)->constructed(object);
 
-    g_object_set_data(G_OBJECT(red_channel), "sound-channel", playback);
     client_cbs.connect = snd_set_playback_peer;
     client_cbs.disconnect = snd_disconnect_channel_client;
     client_cbs.migrate = snd_playback_migrate_channel_client;
-    red_channel_register_client_cbs(red_channel, &client_cbs, playback);
+    red_channel_register_client_cbs(RED_CHANNEL(self), &client_cbs, self);
 
-    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, SND_CODEC_ANY_FREQUENCY))
-        red_channel_set_cap(red_channel, SPICE_PLAYBACK_CAP_CELT_0_5_1);
+    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, SND_CODEC_ANY_FREQUENCY)) {
+        red_channel_set_cap(RED_CHANNEL(self), SPICE_PLAYBACK_CAP_CELT_0_5_1);
+    }
+    red_channel_set_cap(RED_CHANNEL(self), SPICE_PLAYBACK_CAP_VOLUME);
 
-    red_channel_set_cap(red_channel, SPICE_PLAYBACK_CAP_VOLUME);
+    add_channel(self);
+    reds_register_channel(reds, RED_CHANNEL(self));
+}
 
-    playback->base_channel = red_channel;
-    add_channel(playback);
-    reds_register_channel(reds, red_channel);
+static void
+playback_channel_class_init(PlaybackChannelClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+    object_class->constructed = playback_channel_constructed;
 }
 
-void snd_attach_record(RedsState *reds, SpiceRecordInstance *sin)
+void snd_attach_playback(RedsState *reds, SpicePlaybackInstance *sin)
 {
-    SndChannel *record;
-    RedChannel *red_channel;
-    ClientCbs client_cbs = { NULL, };
+    sin->st = g_object_new(TYPE_PLAYBACK_CHANNEL,
+                           "spice-server", reds,
+                           "core-interface", reds_get_core_interface(reds),
+                           "channel-type", SPICE_CHANNEL_PLAYBACK,
+                           "id", 0,
+                           NULL);
+}
+
+static void
+record_channel_init(RecordChannel *self)
+{
+}
 
-    sin->st = spice_new0(SpiceRecordState, 1);
-    record = &sin->st->channel;
-    record->frequency = SND_CODEC_CELT_PLAYBACK_FREQ; /* Default to the legacy rate */
+static void
+record_channel_constructed(GObject *object)
+{
+    ClientCbs client_cbs = { NULL, };
+    SndChannel *self = SND_CHANNEL(object);
+    RedsState *reds = red_channel_get_server(RED_CHANNEL(self));
 
-    // TODO: Make RedChannel base of channel? instead of assigning it to channel->data
-    red_channel = dummy_channel_new(reds, SPICE_CHANNEL_RECORD, 0);
+    G_OBJECT_CLASS(record_channel_parent_class)->constructed(object);
 
-    g_object_set_data(G_OBJECT(red_channel), "sound-channel", record);
     client_cbs.connect = snd_set_record_peer;
     client_cbs.disconnect = snd_disconnect_channel_client;
     client_cbs.migrate = snd_record_migrate_channel_client;
-    red_channel_register_client_cbs(red_channel, &client_cbs, record);
-    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, SND_CODEC_ANY_FREQUENCY))
-        red_channel_set_cap(red_channel, SPICE_RECORD_CAP_CELT_0_5_1);
-    red_channel_set_cap(red_channel, SPICE_RECORD_CAP_VOLUME);
+    red_channel_register_client_cbs(RED_CHANNEL(self), &client_cbs, self);
+
+    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, SND_CODEC_ANY_FREQUENCY)) {
+        red_channel_set_cap(RED_CHANNEL(self), SPICE_RECORD_CAP_CELT_0_5_1);
+    }
+    red_channel_set_cap(RED_CHANNEL(self), SPICE_RECORD_CAP_VOLUME);
 
-    record->base_channel = red_channel;
-    add_channel(record);
-    reds_register_channel(reds, red_channel);
+    add_channel(self);
+    reds_register_channel(reds, RED_CHANNEL(self));
+}
+
+static void
+record_channel_class_init(RecordChannelClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+    object_class->constructed = record_channel_constructed;
+}
+
+void snd_attach_record(RedsState *reds, SpiceRecordInstance *sin)
+{
+    sin->st = g_object_new(TYPE_RECORD_CHANNEL,
+                           "spice-server", reds,
+                           "core-interface", reds_get_core_interface(reds),
+                           "channel-type", SPICE_CHANNEL_RECORD,
+                           "id", 0,
+                           NULL);
 }
 
 static void snd_detach_common(SndChannel *channel)
@@ -1565,36 +1674,24 @@  static void snd_detach_common(SndChannel *channel)
     if (!channel) {
         return;
     }
-    RedsState *reds = red_channel_get_server(channel->base_channel);
+    RedsState *reds = red_channel_get_server(RED_CHANNEL(channel));
 
     remove_channel(channel);
     snd_disconnect_channel(channel->connection);
-    reds_unregister_channel(reds, channel->base_channel);
-    red_channel_destroy(channel->base_channel);
+    reds_unregister_channel(reds, RED_CHANNEL(channel));
     free(channel->volume.volume);
     channel->volume.volume = NULL;
-}
-
-static void spice_playback_state_free(SpicePlaybackState *st)
-{
-    free(st);
+    red_channel_destroy(RED_CHANNEL(channel));
 }
 
 void snd_detach_playback(SpicePlaybackInstance *sin)
 {
     snd_detach_common(&sin->st->channel);
-    spice_playback_state_free(sin->st);
-}
-
-static void spice_record_state_free(SpiceRecordState *st)
-{
-    free(st);
 }
 
 void snd_detach_record(SpiceRecordInstance *sin)
 {
     snd_detach_common(&sin->st->channel);
-    spice_record_state_free(sin->st);
 }
 
 void snd_set_playback_compression(int on)
@@ -1603,7 +1700,7 @@  void snd_set_playback_compression(int on)
 
     for (; now; now = now->next) {
         uint32_t type;
-        g_object_get(now->base_channel, "channel-type", &type, NULL);
+        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,

Comments

Acked-by: Jonathon Jongsma <jjongsma@redhat.com>


On Fri, 2016-12-02 at 10:53 +0000, Frediano Ziglio wrote:
> Stops using the dummy channel.
> Data handling still goes through DummyChannelClient which is why
> empty implementation of some required vfuncs is working.
> 
> Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
> ---
>  server/sound.c | 245 ++++++++++++++++++++++++++++++++++-------------
> ---
>  1 file changed, 171 insertions(+), 74 deletions(-)
> 
> diff --git a/server/sound.c b/server/sound.c
> index 5b70c2c..912dc7a 100644
> --- a/server/sound.c
> +++ b/server/sound.c
> @@ -74,13 +74,17 @@ enum PlaybackCommand {
>  #define SND_PLAYBACK_LATENCY_MASK ( 1 << SND_PLAYBACK_LATENCY)
>  
>  typedef struct SndChannelClient SndChannelClient;
> +typedef struct SndChannel SndChannel;
> +typedef struct PlaybackChannelClient PlaybackChannelClient;
> +typedef struct RecordChannelClient RecordChannelClient;
> +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);
>  
> -typedef struct SndChannel SndChannel;
> -
>  /* Connects an audio client to a Spice client */
>  struct SndChannelClient {
>      RedsStream *stream;
> @@ -116,8 +120,6 @@ struct SndChannelClient {
>      snd_channel_cleanup_channel_proc cleanup;
>  };
>  
> -typedef struct PlaybackChannelClient PlaybackChannelClient;
> -
>  typedef struct AudioFrame AudioFrame;
>  struct AudioFrame {
>      uint32_t time;
> @@ -144,9 +146,14 @@ typedef struct SpiceVolumeState {
>      int mute;
>  } SpiceVolumeState;
>  
> +#define TYPE_SND_CHANNEL snd_channel_get_type()
> +#define SND_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),
> TYPE_SND_CHANNEL, SndChannel))
> +GType snd_channel_get_type(void) G_GNUC_CONST;
> +
>  /* Base class for SpicePlaybackState and SpiceRecordState */
>  struct SndChannel {
> -    RedChannel *base_channel;
> +    RedChannel parent;
> +
>      SndChannelClient *connection; /* Only one client is supported */
>      SndChannel *next; /* For the global SndChannel list */
>  
> @@ -155,15 +162,45 @@ struct SndChannel {
>      uint32_t frequency;
>  };
>  
> +typedef struct SndChannelClass {
> +    RedChannelClass parent_class;
> +} SndChannelClass;
> +
> +G_DEFINE_TYPE(SndChannel, snd_channel, RED_TYPE_CHANNEL)
> +
> +
> +#define TYPE_PLAYBACK_CHANNEL playback_channel_get_type()
> +#define PLAYBACK_CHANNEL(obj) \
> +    (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_PLAYBACK_CHANNEL,
> PlaybackChannel))
> +GType playback_channel_get_type(void) G_GNUC_CONST;
> +
>  struct SpicePlaybackState {
> -    struct SndChannel channel;
> +    SndChannel channel;
>  };
>  
> +typedef struct PlaybackChannelClass {
> +    SndChannelClass parent_class;
> +} PlaybackChannelClass;
> +
> +G_DEFINE_TYPE(PlaybackChannel, playback_channel, TYPE_SND_CHANNEL)
> +
> +
> +#define TYPE_RECORD_CHANNEL record_channel_get_type()
> +#define RECORD_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),
> TYPE_RECORD_CHANNEL, RecordChannel))
> +GType record_channel_get_type(void) G_GNUC_CONST;
> +
>  struct SpiceRecordState {
> -    struct SndChannel channel;
> +    SndChannel channel;
>  };
>  
> -typedef struct RecordChannelClient {
> +typedef struct RecordChannelClass {
> +    SndChannelClass parent_class;
> +} RecordChannelClass;
> +
> +G_DEFINE_TYPE(RecordChannel, record_channel, TYPE_SND_CHANNEL)
> +
> +
> +struct RecordChannelClient {
>      SndChannelClient base;
>      uint32_t samples[RECORD_SAMPLES_SIZE];
>      uint32_t write_pos;
> @@ -173,7 +210,7 @@ typedef struct RecordChannelClient {
>      uint32_t start_time;
>      SndCodec codec;
>      uint8_t  decode_buf[SND_CODEC_MAX_FRAME_BYTES];
> -} RecordChannelClient;
> +};
>  
>  /* A list of all Spice{Playback,Record}State objects */
>  static SndChannel *snd_channels;
> @@ -201,7 +238,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(client->channel->base_channel);
> +    return red_channel_get_server(RED_CHANNEL(client->channel));
>  }
>  
>  static void snd_disconnect_channel(SndChannelClient *client)
> @@ -892,7 +929,7 @@ static SndChannelClient *__new_channel(SndChannel
> *channel, int size, uint32_t c
>  #endif
>      int tos;
>      MainChannelClient *mcc = red_client_get_main(red_client);
> -    RedsState *reds = red_channel_get_server(channel->base_channel);
> +    RedsState *reds = red_channel_get_server(RED_CHANNEL(channel));
>  
>      spice_assert(stream);
>      if ((flags = fcntl(stream->socket, F_GETFL)) == -1) {
> @@ -953,7 +990,7 @@ static SndChannelClient *__new_channel(SndChannel
> *channel, int size, uint32_t c
>      client->cleanup = cleanup;
>  
>      client->channel_client =
> -        dummy_channel_client_create(channel->base_channel,
> red_client,
> +        dummy_channel_client_create(RED_CHANNEL(channel),
> red_client,
>                                      num_common_caps, common_caps,
> num_caps, caps);
>      if (!client->channel_client) {
>          goto error2;
> @@ -968,14 +1005,36 @@ error1:
>      return NULL;
>  }
>  
> +static int snd_channel_config_socket(RedChannelClient *rcc)
> +{
> +    g_assert_not_reached();
> +}
> +
> +static void snd_channel_on_disconnect(RedChannelClient *rcc)
> +{
> +    g_assert_not_reached();
> +}
> +
> +static uint8_t*
> +snd_channel_client_alloc_recv_buf(RedChannelClient *rcc, uint16_t
> type, uint32_t size)
> +{
> +    g_assert_not_reached();
> +}
> +
> +static void
> +snd_channel_client_release_recv_buf(RedChannelClient *rcc, uint16_t
> type, uint32_t size,
> +                                    uint8_t *msg)
> +{
> +    g_assert_not_reached();
> +}
> +
>  static void snd_disconnect_channel_client(RedChannelClient *rcc)
>  {
>      SndChannel *channel;
>      RedChannel *red_channel = red_channel_client_get_channel(rcc);
>      uint32_t type;
>  
> -    spice_assert(red_channel);
> -    channel = (SndChannel *)g_object_get_data(G_OBJECT(red_channel),
> "sound-channel");
> +    channel = SND_CHANNEL(red_channel);
>      spice_assert(channel);
>      g_object_get(red_channel, "channel-type", &type, NULL);
>  
> @@ -1126,7 +1185,7 @@ void snd_set_playback_latency(RedClient
> *client, uint32_t latency)
>  
>      for (; now; now = now->next) {
>          uint32_t type;
> -        g_object_get(now->base_channel, "channel-type", &type,
> NULL);
> +        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) {
>  
> @@ -1161,7 +1220,7 @@ static int snd_desired_audio_mode(int
> playback_compression, int frequency,
>  
>  static void on_new_playback_channel(SndChannel *channel,
> SndChannelClient *snd_channel)
>  {
> -    RedsState *reds = red_channel_get_server(channel->base_channel);
> +    RedsState *reds = red_channel_get_server(RED_CHANNEL(channel));
>  
>      spice_assert(snd_channel);
>  
> @@ -1193,7 +1252,7 @@ static void snd_set_playback_peer(RedChannel
> *red_channel, RedClient *client, Re
>                                    int migration, int
> num_common_caps, uint32_t *common_caps,
>                                    int num_caps, uint32_t *caps)
>  {
> -    SndChannel *channel = g_object_get_data(G_OBJECT(red_channel),
> "sound-channel");
> +    SndChannel *channel = SND_CHANNEL(red_channel);
>      PlaybackChannelClient *playback_client;
>  
>      snd_disconnect_channel(channel->connection);
> @@ -1249,9 +1308,7 @@ static void
> snd_record_migrate_channel_client(RedChannelClient *rcc)
>      SndChannel *channel;
>      RedChannel *red_channel = red_channel_client_get_channel(rcc);
>  
> -    spice_debug(NULL);
> -    spice_assert(red_channel);
> -    channel = (SndChannel *)g_object_get_data(G_OBJECT(red_channel),
> "sound-channel");
> +    channel = SND_CHANNEL(red_channel);
>      spice_assert(channel);
>  
>      if (channel->connection) {
> @@ -1389,7 +1446,7 @@ static uint32_t
> snd_get_best_rate(SndChannelClient *client, uint32_t cap_opus)
>  
>  static void snd_set_rate(SndChannel *channel, uint32_t frequency,
> uint32_t cap_opus)
>  {
> -    RedChannel *red_channel = channel->base_channel;
> +    RedChannel *red_channel = RED_CHANNEL(channel);
>      channel->frequency = frequency;
>      if (red_channel &&
> snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, frequency)) {
>          red_channel_set_cap(red_channel, cap_opus);
> @@ -1439,7 +1496,7 @@ static void snd_set_record_peer(RedChannel
> *red_channel, RedClient *client, Reds
>                                  int migration, int num_common_caps,
> uint32_t *common_caps,
>                                  int num_caps, uint32_t *caps)
>  {
> -    SndChannel *channel = g_object_get_data(G_OBJECT(red_channel),
> "sound-channel");
> +    SndChannel *channel = SND_CHANNEL(red_channel);
>      RecordChannelClient *record_client;
>  
>      snd_disconnect_channel(channel->connection);
> @@ -1473,8 +1530,7 @@ static void
> snd_playback_migrate_channel_client(RedChannelClient *rcc)
>      SndChannel *channel;
>      RedChannel *red_channel = red_channel_client_get_channel(rcc);
>  
> -    spice_assert(red_channel);
> -    channel = (SndChannel *)g_object_get_data(G_OBJECT(red_channel),
> "sound-channel");
> +    channel = SND_CHANNEL(red_channel);
>      spice_assert(channel);
>      spice_debug(NULL);
>  
> @@ -1504,60 +1560,113 @@ static void remove_channel(SndChannel
> *channel)
>      spice_printerr("not found");
>  }
>  
> -void snd_attach_playback(RedsState *reds, SpicePlaybackInstance
> *sin)
> +static void
> +snd_channel_init(SndChannel *self)
>  {
> -    SndChannel *playback;
> -    RedChannel *red_channel;
> -    ClientCbs client_cbs = { NULL, };
> +    self->frequency = SND_CODEC_CELT_PLAYBACK_FREQ; /* Default to
> the legacy rate */
> +}
>  
> -    sin->st = spice_new0(SpicePlaybackState, 1);
> -    playback = &sin->st->channel;
> -    playback->frequency = SND_CODEC_CELT_PLAYBACK_FREQ; /* Default
> to the legacy rate */
> +static void
> +snd_channel_class_init(SndChannelClass *klass)
> +{
> +    RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass);
>  
> -    // TODO: Make RedChannel base of channel? instead of assigning
> it to channel->data
> -    red_channel = dummy_channel_new(reds, SPICE_CHANNEL_PLAYBACK,
> 0);
> +    channel_class->config_socket = snd_channel_config_socket;
> +    channel_class->alloc_recv_buf =
> snd_channel_client_alloc_recv_buf;
> +    channel_class->release_recv_buf =
> snd_channel_client_release_recv_buf;
> +    channel_class->on_disconnect = snd_channel_on_disconnect;
> +}
> +
> +static void
> +playback_channel_init(PlaybackChannel *self)
> +{
> +}
> +
> +static void
> +playback_channel_constructed(GObject *object)
> +{
> +    ClientCbs client_cbs = { NULL, };
> +    SndChannel *self = SND_CHANNEL(object);
> +    RedsState *reds = red_channel_get_server(RED_CHANNEL(self));
> +
> +    G_OBJECT_CLASS(playback_channel_parent_class)-
> >constructed(object);
>  
> -    g_object_set_data(G_OBJECT(red_channel), "sound-channel",
> playback);
>      client_cbs.connect = snd_set_playback_peer;
>      client_cbs.disconnect = snd_disconnect_channel_client;
>      client_cbs.migrate = snd_playback_migrate_channel_client;
> -    red_channel_register_client_cbs(red_channel, &client_cbs,
> playback);
> +    red_channel_register_client_cbs(RED_CHANNEL(self), &client_cbs,
> self);
>  
> -    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1,
> SND_CODEC_ANY_FREQUENCY))
> -        red_channel_set_cap(red_channel,
> SPICE_PLAYBACK_CAP_CELT_0_5_1);
> +    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1,
> SND_CODEC_ANY_FREQUENCY)) {
> +        red_channel_set_cap(RED_CHANNEL(self),
> SPICE_PLAYBACK_CAP_CELT_0_5_1);
> +    }
> +    red_channel_set_cap(RED_CHANNEL(self),
> SPICE_PLAYBACK_CAP_VOLUME);
>  
> -    red_channel_set_cap(red_channel, SPICE_PLAYBACK_CAP_VOLUME);
> +    add_channel(self);
> +    reds_register_channel(reds, RED_CHANNEL(self));
> +}
>  
> -    playback->base_channel = red_channel;
> -    add_channel(playback);
> -    reds_register_channel(reds, red_channel);
> +static void
> +playback_channel_class_init(PlaybackChannelClass *klass)
> +{
> +    GObjectClass *object_class = G_OBJECT_CLASS(klass);
> +
> +    object_class->constructed = playback_channel_constructed;
>  }
>  
> -void snd_attach_record(RedsState *reds, SpiceRecordInstance *sin)
> +void snd_attach_playback(RedsState *reds, SpicePlaybackInstance
> *sin)
>  {
> -    SndChannel *record;
> -    RedChannel *red_channel;
> -    ClientCbs client_cbs = { NULL, };
> +    sin->st = g_object_new(TYPE_PLAYBACK_CHANNEL,
> +                           "spice-server", reds,
> +                           "core-interface",
> reds_get_core_interface(reds),
> +                           "channel-type", SPICE_CHANNEL_PLAYBACK,
> +                           "id", 0,
> +                           NULL);
> +}
> +
> +static void
> +record_channel_init(RecordChannel *self)
> +{
> +}
>  
> -    sin->st = spice_new0(SpiceRecordState, 1);
> -    record = &sin->st->channel;
> -    record->frequency = SND_CODEC_CELT_PLAYBACK_FREQ; /* Default to
> the legacy rate */
> +static void
> +record_channel_constructed(GObject *object)
> +{
> +    ClientCbs client_cbs = { NULL, };
> +    SndChannel *self = SND_CHANNEL(object);
> +    RedsState *reds = red_channel_get_server(RED_CHANNEL(self));
>  
> -    // TODO: Make RedChannel base of channel? instead of assigning
> it to channel->data
> -    red_channel = dummy_channel_new(reds, SPICE_CHANNEL_RECORD, 0);
> +    G_OBJECT_CLASS(record_channel_parent_class)-
> >constructed(object);
>  
> -    g_object_set_data(G_OBJECT(red_channel), "sound-channel",
> record);
>      client_cbs.connect = snd_set_record_peer;
>      client_cbs.disconnect = snd_disconnect_channel_client;
>      client_cbs.migrate = snd_record_migrate_channel_client;
> -    red_channel_register_client_cbs(red_channel, &client_cbs,
> record);
> -    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1,
> SND_CODEC_ANY_FREQUENCY))
> -        red_channel_set_cap(red_channel,
> SPICE_RECORD_CAP_CELT_0_5_1);
> -    red_channel_set_cap(red_channel, SPICE_RECORD_CAP_VOLUME);
> +    red_channel_register_client_cbs(RED_CHANNEL(self), &client_cbs,
> self);
> +
> +    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1,
> SND_CODEC_ANY_FREQUENCY)) {
> +        red_channel_set_cap(RED_CHANNEL(self),
> SPICE_RECORD_CAP_CELT_0_5_1);
> +    }
> +    red_channel_set_cap(RED_CHANNEL(self), SPICE_RECORD_CAP_VOLUME);
>  
> -    record->base_channel = red_channel;
> -    add_channel(record);
> -    reds_register_channel(reds, red_channel);
> +    add_channel(self);
> +    reds_register_channel(reds, RED_CHANNEL(self));
> +}
> +
> +static void
> +record_channel_class_init(RecordChannelClass *klass)
> +{
> +    GObjectClass *object_class = G_OBJECT_CLASS(klass);
> +
> +    object_class->constructed = record_channel_constructed;
> +}
> +
> +void snd_attach_record(RedsState *reds, SpiceRecordInstance *sin)
> +{
> +    sin->st = g_object_new(TYPE_RECORD_CHANNEL,
> +                           "spice-server", reds,
> +                           "core-interface",
> reds_get_core_interface(reds),
> +                           "channel-type", SPICE_CHANNEL_RECORD,
> +                           "id", 0,
> +                           NULL);
>  }
>  
>  static void snd_detach_common(SndChannel *channel)
> @@ -1565,36 +1674,24 @@ static void snd_detach_common(SndChannel
> *channel)
>      if (!channel) {
>          return;
>      }
> -    RedsState *reds = red_channel_get_server(channel->base_channel);
> +    RedsState *reds = red_channel_get_server(RED_CHANNEL(channel));
>  
>      remove_channel(channel);
>      snd_disconnect_channel(channel->connection);
> -    reds_unregister_channel(reds, channel->base_channel);
> -    red_channel_destroy(channel->base_channel);
> +    reds_unregister_channel(reds, RED_CHANNEL(channel));
>      free(channel->volume.volume);
>      channel->volume.volume = NULL;
> -}
> -
> -static void spice_playback_state_free(SpicePlaybackState *st)
> -{
> -    free(st);
> +    red_channel_destroy(RED_CHANNEL(channel));
>  }
>  
>  void snd_detach_playback(SpicePlaybackInstance *sin)
>  {
>      snd_detach_common(&sin->st->channel);
> -    spice_playback_state_free(sin->st);
> -}
> -
> -static void spice_record_state_free(SpiceRecordState *st)
> -{
> -    free(st);
>  }
>  
>  void snd_detach_record(SpiceRecordInstance *sin)
>  {
>      snd_detach_common(&sin->st->channel);
> -    spice_record_state_free(sin->st);
>  }
>  
>  void snd_set_playback_compression(int on)
> @@ -1603,7 +1700,7 @@ void snd_set_playback_compression(int on)
>  
>      for (; now; now = now->next) {
>          uint32_t type;
> -        g_object_get(now->base_channel, "channel-type", &type,
> NULL);
> +        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,