RFC on sound codec refactoring

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Attached (sorry :-/) are two patches that express my current plan for
supporting Opus (and other codecs as well).

Essentially, I abstract out the celt functionality into a spice-common
shared library; snd_codec.c.  I then implement the server and client
using that logic.

The patches attached seem to Work For Me [1].  The goal was to use the
new abstraction without changing functionality.  They do have the side
effect of making celt optional, which will make the Debian guys happy.

I know of two flaws, currently.

First, the computation of the frame size is unsatisfying.  Opus is going
to require a different frequency, hence a different frame size.  I've
started abstracting it, but I want to dig more into Opus before I
finalize an approach.

Second, the current client does not record correctly in celt mode.  I
get a stuttered recording when I use that.  The gtk code works fine.  In
my testing, this is true both with and without this patch set.  But it's
still an unsatisfying result :-/.

I would appreciate comments and feedback, particularly if there is
anything that seems obviously wrong to others.

If this approach seems viable, I'll work on adding Opus support, and
then also revise the gtk implementation to use the new code.

Cheers,

Jeremy

[1]  I tested using qemu + fedora18, recording a sample and playing it
back; both with compression and without.  I have not yet tested a
windows vm; that's on my todo list.
>From 90c4b0e4915932cf444c852ce129adb5080ebe72 Mon Sep 17 00:00:00 2001
From: Jeremy White <jwhite@xxxxxxxxxxxxxxx>
Date: Fri, 4 Oct 2013 09:19:48 -0500
Subject: [PATCH] Add a snd_codec interface to abstract the use of audio
 codecs such as celt.
To: <spice-devel@xxxxxxxxxxxxxxxxxxxxx>

---
 common/Makefile.am |    4 +
 common/snd_codec.c |  260 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 common/snd_codec.h |   78 ++++++++++++++++
 configure.ac       |   16 ++++
 4 files changed, 358 insertions(+)
 create mode 100644 common/snd_codec.c
 create mode 100644 common/snd_codec.h

diff --git a/common/Makefile.am b/common/Makefile.am
index 45568c6..bef6a14 100644
--- a/common/Makefile.am
+++ b/common/Makefile.am
@@ -52,6 +52,8 @@ libspice_common_la_SOURCES =		\
 	ring.h				\
 	rop3.c				\
 	rop3.h				\
+	snd_codec.c			\
+	snd_codec.h			\
 	spice_common.h			\
 	ssl_verify.c			\
 	ssl_verify.h			\
@@ -81,6 +83,7 @@ endif
 AM_CPPFLAGS =				\
 	$(GL_CFLAGS)			\
 	$(PIXMAN_CFLAGS)		\
+	$(CELT051_CFLAGS)		\
 	$(PROTOCOL_CFLAGS)		\
 	$(SMARTCARD_CFLAGS)		\
 	$(VISIBILITY_HIDDEN_CFLAGS)	\
@@ -142,6 +145,7 @@ EXTRA_DIST =				\
 	quic_family_tmpl.c		\
 	quic_rgb_tmpl.c			\
 	quic_tmpl.c			\
+	snd_codec.h			\
 	sw_canvas.c			\
 	sw_canvas.h			\
 	$(NULL)
diff --git a/common/snd_codec.c b/common/snd_codec.c
new file mode 100644
index 0000000..1584830
--- /dev/null
+++ b/common/snd_codec.c
@@ -0,0 +1,260 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2013 Jeremy White
+
+   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/>.
+*/
+
+/* snd_codec.c
+     General purpose sound codec routines for use by Spice.
+   These routines abstract the work of picking a codec and
+   encoding and decoding the buffers.
+     Note:  these routines have some peculiarities that come from
+   wanting to provide full backwards compatibility with the original
+   Spice celt 0.51 implementation.  It has some hard requirements
+   (fixed sample size, fixed compressed buffer size).
+
+   See below for documentation of the public routines.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include "config.h"
+#include <spice/macros.h>
+#include <spice/enums.h>
+
+
+#include "snd_codec.h"
+#include "log.h"
+
+
+
+/* celt 0.51 specific support routines */
+#if HAVE_CELT051
+static void snd_codec_destroy_celt051(SndCodec *codec)
+{
+    if (codec->celt_decoder)
+    {
+        celt051_decoder_destroy(codec->celt_decoder);
+        codec->celt_decoder = NULL;
+    }
+
+    if (codec->celt_encoder)
+    {
+        celt051_encoder_destroy(codec->celt_encoder);
+        codec->celt_encoder = NULL;
+    }
+
+    if (codec->celt_mode)
+    {
+        celt051_mode_destroy(codec->celt_mode);
+        codec->celt_mode = NULL;
+    }
+}
+
+static int snd_codec_create_celt051(SndCodec *codec, int encode, int decode)
+{
+    int celt_error;
+
+    codec->celt_mode = celt051_mode_create(SND_CODEC_CELT_PLAYBACK_FREQ,
+                                           SND_CODEC_CELT_PLAYBACK_CHAN,
+                                           SND_CODEC_CELT_FRAME_SIZE, &celt_error);
+    if (! codec->celt_mode)
+    {
+        spice_printerr("create celt mode failed %d", celt_error);
+        return SND_CODEC_UNAVAILABLE;
+    }
+
+    if (encode)
+    {
+        codec->celt_encoder = celt051_encoder_create(codec->celt_mode);
+        if (! codec->celt_encoder)
+        {
+            spice_printerr("create celt encoder failed");
+            goto error;
+        }
+    }
+
+    if (decode)
+    {
+        codec->celt_decoder = celt051_decoder_create(codec->celt_mode);
+        if (! codec->celt_decoder)
+        {
+            spice_printerr("create celt decoder failed");
+            goto error;
+        }
+    }
+
+    codec->mode = SPICE_AUDIO_DATA_MODE_CELT_0_5_1;
+    return SND_CODEC_OK;
+
+error:
+    snd_codec_destroy_celt051(codec);
+    return SND_CODEC_UNAVAILABLE;
+}
+
+static int snd_codec_encode_celt051(SndCodec *codec, uint8_t *in_ptr, int in_size, uint8_t *out_ptr, int *out_size)
+{
+    int n;
+    if (in_size != SND_CODEC_CELT_FRAME_SIZE * SND_CODEC_CELT_PLAYBACK_CHAN * 2)
+        return SND_CODEC_INVALID_ENCODE_SIZE;
+    n = celt051_encode(codec->celt_encoder, (celt_int16_t *) in_ptr, NULL, out_ptr, *out_size);
+    if (n < 0) {
+        spice_printerr("celt051_encode failed %d\n", n);
+        return SND_CODEC_ENCODE_FAILED;
+    }
+    *out_size = n;
+    return SND_CODEC_OK;
+}
+
+static int snd_codec_decode_celt051(SndCodec *codec, uint8_t *in_ptr, int in_size, uint8_t *out_ptr, int *out_size)
+{
+    int n;
+    n = celt051_decode(codec->celt_decoder, in_ptr, in_size, (celt_int16_t *) out_ptr);
+    if (n < 0) {
+        spice_printerr("celt051_decode failed %d\n", n);
+        return SND_CODEC_DECODE_FAILED;
+    }
+    *out_size = SND_CODEC_CELT_FRAME_SIZE * SND_CODEC_CELT_PLAYBACK_CHAN * 2 /* 16 fmt */;
+    return SND_CODEC_OK;
+}
+#endif
+
+
+
+/*----------------------------------------------------------------------------
+**          PUBLIC INTERFACE
+**--------------------------------------------------------------------------*/
+
+/*
+  snd_codec_is_capable
+    Returns TRUE if the current spice implementation can
+      use the given codec, FALSE otherwise.
+   mode must be a SPICE_AUDIO_DATA_MODE_XXX enum from spice/enum.h
+ */
+int snd_codec_is_capable(int mode)
+{
+#if HAVE_CELT051
+    if (mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1)
+        return TRUE;
+#else
+    return FALSE;
+#endif
+}
+
+/*
+  snd_codec_create
+    Create a codec control.  Required for most functions in this library.
+    Parameters:
+      1.  codec     Pointer to preallocated codec control
+      2.  mode      SPICE_AUDIO_DATA_MODE_XXX enum from spice/enum.h
+      3.  encode    TRUE if encoding is desired
+      4.  encode    TRUE if decoding is desired
+     Returns:
+       SND_CODEC_OK  if all went well; a different code if not.
+
+  snd_codec_destroy is the obvious partner of snd_codec_create.
+ */
+int snd_codec_create(SndCodec *codec, int mode, int encode, int decode)
+{
+    int rc = SND_CODEC_UNAVAILABLE;
+    memset(codec, 0, sizeof(*codec));
+
+#if HAVE_CELT051
+    if (mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1)
+        rc = snd_codec_create_celt051(codec, encode, decode);
+#endif
+
+    return rc;
+}
+
+/*
+  snd_codec_destroy
+    The obvious companion to snd_codec_create
+*/
+void snd_codec_destroy(SndCodec *codec)
+{
+#if HAVE_CELT051
+    snd_codec_destroy_celt051(codec);
+#endif
+}
+
+/*
+  snd_codec_frame_size
+    Returns the size, in frames, of the raw PCM frame buffer
+      required by this codec.  To get bytes, you'll need
+      to multiply by channels and sample width.
+ */
+int snd_codec_frame_size(SndCodec *codec)
+{
+#if HAVE_CELT051
+    if (codec->mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1)
+        return SND_CODEC_CELT_FRAME_SIZE;
+#endif
+    return SND_CODEC_MAX_FRAME_SIZE;
+}
+
+/*
+  snd_codec_encode
+     Encode a block of data to a compressed buffer.
+
+  Parameters:
+    1.  codec       Pointer to codec control previously allocated + created
+    2.  in_data     Pointer to uncompressed PCM data
+    3.  in_size     Input size  (for celt, this must be a
+                    particular size, governed by the frame size)
+    4.  out_ptr     Pointer to area to write encoded data
+    5.  out_size    On input, the maximum size of the output buffer; on
+                    successful return, it will hold the number of bytes
+                    returned.  For celt, this must be set to a particular
+                    size to ensure compatibility.
+
+     Returns:
+       SND_CODEC_OK  if all went well
+*/
+int snd_codec_encode(SndCodec *codec, uint8_t *in_ptr, int in_size, uint8_t *out_ptr, int *out_size)
+{
+#if HAVE_CELT051
+    if (codec->mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1)
+        return snd_codec_encode_celt051(codec, in_ptr, in_size, out_ptr, out_size);
+#endif
+
+    return SND_CODEC_ENCODER_UNAVAILABLE;
+}
+
+/*
+  snd_codec_decode
+     Decode a block of data from a compressed buffer.
+
+  Parameters:
+    1.  codec       Pointer to codec control previously allocated + created
+    2.  in_data     Pointer to compressed data
+    3.  in_size     Input size
+    4.  out_ptr     Pointer to area to write decoded data
+    5.  out_size    On input, the maximum size of the output buffer; on
+                    successful return, it will hold the number of bytes
+                    returned.
+
+     Returns:
+       SND_CODEC_OK  if all went well
+*/
+int snd_codec_decode(SndCodec *codec, uint8_t *in_ptr, int in_size, uint8_t *out_ptr, int *out_size)
+{
+#if HAVE_CELT051
+    if (codec->mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1)
+        return snd_codec_decode_celt051(codec, in_ptr, in_size, out_ptr, out_size);
+#endif
+
+    return SND_CODEC_DECODER_UNAVAILABLE;
+}
diff --git a/common/snd_codec.h b/common/snd_codec.h
new file mode 100644
index 0000000..12fc7bb
--- /dev/null
+++ b/common/snd_codec.h
@@ -0,0 +1,78 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2013 Jeremy White <jwhite@xxxxxxxxxxxxxxx>
+
+   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 _H_SND_CODEC
+#define _H_SND_CODEC
+
+
+#if HAVE_CELT051
+#include <celt051/celt.h>
+#endif
+
+/* Spice uses a very fixed protocol when transmitting CELT audio;
+   audio must be transmitted in frames of 256, and we must compress
+   data down to a fairly specific size (47, computation below).
+   While the protocol doesn't inherently specify this, the expectation
+   of older clients and server mandates it.
+*/
+#define SND_CODEC_CELT_FRAME_SIZE       256
+#define SND_CODEC_CELT_BIT_RATE         (64 * 1024)
+#define SND_CODEC_CELT_PLAYBACK_FREQ    44100
+#define SND_CODEC_CELT_PLAYBACK_CHAN    2
+#define SND_CODEC_CELT_COMPRESSED_FRAME_BYTES (SND_CODEC_CELT_FRAME_SIZE * SND_CODEC_CELT_BIT_RATE / \
+                                        SND_CODEC_CELT_PLAYBACK_FREQ / 8)
+
+
+#define SND_CODEC_MAX_FRAME_SIZE        SND_CODEC_CELT_FRAME_SIZE
+#define SND_CODEC_MAX_FRAME_BYTES       (SND_CODEC_MAX_FRAME_SIZE * SND_CODEC_CELT_PLAYBACK_CHAN * 2 /* FMT_S16 */)
+#define SND_CODEC_MAX_COMPRESSED_BYTES  SND_CODEC_CELT_COMPRESSED_FRAME_BYTES
+
+#define SND_CODEC_OK                    0
+#define SND_CODEC_UNAVAILABLE           1
+#define SND_CODEC_ENCODER_UNAVAILABLE   2
+#define SND_CODEC_DECODER_UNAVAILABLE   3
+#define SND_CODEC_ENCODE_FAILED         4
+#define SND_CODEC_DECODE_FAILED         4
+#define SND_CODEC_INVALID_ENCODE_SIZE   5
+
+SPICE_BEGIN_DECLS
+
+typedef struct
+{
+    int mode;
+#if HAVE_CELT051
+    CELTMode *celt_mode;
+    CELTEncoder *celt_encoder;
+    CELTDecoder *celt_decoder;
+#endif
+} SndCodec;
+
+
+int  snd_codec_is_capable(int mode);
+
+int  snd_codec_create(SndCodec *codec, int mode, int encode, int decode);
+void snd_codec_destroy(SndCodec *codec);
+
+int  snd_codec_frame_size(SndCodec *codec);
+
+int  snd_codec_encode(SndCodec *codec, uint8_t *in_ptr, int in_size, uint8_t *out_ptr, int *out_size);
+int  snd_codec_decode(SndCodec *codec, uint8_t *in_ptr, int in_size, uint8_t *out_ptr, int *out_size);
+
+SPICE_END_DECLS
+
+#endif
diff --git a/configure.ac b/configure.ac
index acb7626..3443334 100644
--- a/configure.ac
+++ b/configure.ac
@@ -51,6 +51,22 @@ if test "x$enable_smartcard" != "xno"; then
 fi
 AM_CONDITIONAL([WITH_SMARTCARD], [test "x$have_smartcard" = "xyes"])
 
+AC_ARG_ENABLE(celt051,
+[  --disable-celt051       Disable celt051 audio codec (enabled by default)],,
+[enable_celt051="yes"])
+
+if test "x$enable_celt051" = "xyes"; then
+    PKG_CHECK_MODULES(CELT051, celt051 >= 0.5.1.1, have_celt051=yes, have_celt051=no)
+    AC_SUBST(CELT051_CFLAGS)
+    AC_SUBST(CELT051_LIBS)
+    AC_SUBST(CELT051_LIBDIR)
+else
+    have_celt051=no
+fi
+
+AM_CONDITIONAL([HAVE_CELT051], [test "x$have_celt051" = "xyes"])
+AM_COND_IF([HAVE_CELT051], AC_DEFINE([HAVE_CELT051], 1, [Define if we have celt051 codec]))
+
 AC_ARG_ENABLE([opengl],
   AS_HELP_STRING([--enable-opengl=@<:@yes/no@:>@],
                  [Enable opengl support (not recommended) @<:@default=no@:>@]),
-- 
1.7.10.4

>From cbf85871e1a6dc2d05b1f226a749f04860eea14e Mon Sep 17 00:00:00 2001
From: Jeremy White <jwhite@xxxxxxxxxxxxxxx>
Date: Fri, 4 Oct 2013 11:17:05 -0500
Subject: [PATCH] Revise the spice server to use the new snd_codec functions
 in spice-common.
To: <spice-devel@xxxxxxxxxxxxxxxxxxxxx>

This makes celt optional, and paves the way to readily add additional codecs.
---
 README                      |    1 -
 client/audio_channels.h     |   12 ++-
 client/playback_channel.cpp |   51 ++++-------
 client/record_channel.cpp   |   65 +++++---------
 configure.ac                |   22 +++--
 server/snd_worker.c         |  208 ++++++++++++++++++-------------------------
 server/snd_worker.h         |    4 +
 7 files changed, 156 insertions(+), 207 deletions(-)

diff --git a/README b/README
index e146a95..018ab1a 100644
--- a/README
+++ b/README
@@ -28,7 +28,6 @@ The following mandatory dependancies are required in order to
 build SPICE
 
     Spice protocol >= 0.9.0
-    Celt           >= 0.5.1.1, < 0.6.0
     Pixman         >= 0.17.7
     OpenSSL
     libjpeg
diff --git a/client/audio_channels.h b/client/audio_channels.h
index d38a79e..a6be70a 100644
--- a/client/audio_channels.h
+++ b/client/audio_channels.h
@@ -18,7 +18,7 @@
 #ifndef _H_AUDIO_CHANNELS
 #define _H_AUDIO_CHANNELS
 
-#include <celt051/celt.h>
+#include "common/snd_codec.h"
 
 #include "red_channel.h"
 #include "debug.h"
@@ -45,7 +45,7 @@ private:
     void handle_start(RedPeer::InMessage* message);
     void handle_stop(RedPeer::InMessage* message);
     void handle_raw_data(RedPeer::InMessage* message);
-    void handle_celt_data(RedPeer::InMessage* message);
+    void handle_compressed_data(RedPeer::InMessage* message);
     void null_handler(RedPeer::InMessage* message);
     void disable();
 
@@ -57,8 +57,7 @@ private:
     WavePlaybackAbstract* _wave_player;
     uint32_t _mode;
     uint32_t _frame_bytes;
-    CELTMode *_celt_mode;
-    CELTDecoder *_celt_decoder;
+    SndCodec  codec;
     bool _playing;
     uint32_t _frame_count;
 };
@@ -96,11 +95,10 @@ private:
     Mutex _messages_lock;
     std::list<RecordSamplesMessage *> _messages;
     int _mode;
-    CELTMode *_celt_mode;
-    CELTEncoder *_celt_encoder;
+    SndCodec codec;
     uint32_t _frame_bytes;
 
-    static int data_mode;
+    uint8_t compressed_buf[SND_CODEC_MAX_COMPRESSED_BYTES];
 
     friend class RecordSamplesMessage;
 };
diff --git a/client/playback_channel.cpp b/client/playback_channel.cpp
index 802a4d3..922c464 100644
--- a/client/playback_channel.cpp
+++ b/client/playback_channel.cpp
@@ -151,8 +151,6 @@ PlaybackChannel::PlaybackChannel(RedClient& client, uint32_t id)
                  Platform::PRIORITY_HIGH)
     , _wave_player (NULL)
     , _mode (SPICE_AUDIO_DATA_MODE_INVALID)
-    , _celt_mode (NULL)
-    , _celt_decoder (NULL)
     , _playing (false)
 {
 #ifdef WAVE_CAPTURE
@@ -169,7 +167,8 @@ PlaybackChannel::PlaybackChannel(RedClient& client, uint32_t id)
 
     handler->set_handler(SPICE_MSG_PLAYBACK_MODE, &PlaybackChannel::handle_mode);
 
-    set_capability(SPICE_PLAYBACK_CAP_CELT_0_5_1);
+    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1))
+        set_capability(SPICE_PLAYBACK_CAP_CELT_0_5_1);
 }
 
 void PlaybackChannel::clear()
@@ -182,15 +181,7 @@ void PlaybackChannel::clear()
     }
     _mode = SPICE_AUDIO_DATA_MODE_INVALID;
 
-    if (_celt_decoder) {
-        celt051_decoder_destroy(_celt_decoder);
-        _celt_decoder = NULL;
-    }
-
-    if (_celt_mode) {
-        celt051_mode_destroy(_celt_mode);
-        _celt_mode = NULL;
-    }
+    snd_codec_destroy(&codec);
 }
 
 void PlaybackChannel::on_disconnect()
@@ -214,18 +205,19 @@ void PlaybackChannel::set_data_handler()
 
     if (_mode == SPICE_AUDIO_DATA_MODE_RAW) {
         handler->set_handler(SPICE_MSG_PLAYBACK_DATA, &PlaybackChannel::handle_raw_data);
-    } else if (_mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1) {
-        handler->set_handler(SPICE_MSG_PLAYBACK_DATA, &PlaybackChannel::handle_celt_data);
+    } else if (snd_codec_is_capable(_mode)) {
+        handler->set_handler(SPICE_MSG_PLAYBACK_DATA, &PlaybackChannel::handle_compressed_data);
     } else {
         THROW("invalid mode");
     }
+
 }
 
 void PlaybackChannel::handle_mode(RedPeer::InMessage* message)
 {
     SpiceMsgPlaybackMode* playbacke_mode = (SpiceMsgPlaybackMode*)message->data();
-    if (playbacke_mode->mode != SPICE_AUDIO_DATA_MODE_RAW &&
-        playbacke_mode->mode != SPICE_AUDIO_DATA_MODE_CELT_0_5_1) {
+    if (playbacke_mode->mode != SPICE_AUDIO_DATA_MODE_RAW
+        && !snd_codec_is_capable(playbacke_mode->mode) ) {
         THROW("invalid mode");
     }
 
@@ -265,15 +257,11 @@ void PlaybackChannel::handle_start(RedPeer::InMessage* message)
     start_wave();
 #endif
     if (!_wave_player) {
-        // for now support only one setting
-        int celt_mode_err;
-
         if (start->format != SPICE_AUDIO_FMT_S16) {
             THROW("unexpected format");
         }
         int bits_per_sample = 16;
-        int frame_size = 256;
-        _frame_bytes = frame_size * start->channels * bits_per_sample / 8;
+        int frame_size = SND_CODEC_MAX_FRAME_SIZE;
         try {
             _wave_player = Platform::create_player(start->frequency, bits_per_sample,
                                                    start->channels);
@@ -284,14 +272,13 @@ void PlaybackChannel::handle_start(RedPeer::InMessage* message)
             return;
         }
 
-        if (!(_celt_mode = celt051_mode_create(start->frequency, start->channels,
-                                               frame_size, &celt_mode_err))) {
-            THROW("create celt mode failed %d", celt_mode_err);
+        if (_mode != SPICE_AUDIO_DATA_MODE_RAW) {
+            if (snd_codec_create(&codec, _mode, FALSE, TRUE) != SND_CODEC_OK)
+                THROW("create decoder");
+            frame_size = snd_codec_frame_size(&codec);
         }
 
-        if (!(_celt_decoder = celt051_decoder_create(_celt_mode))) {
-            THROW("create celt decoder");
-        }
+        _frame_bytes = frame_size * start->channels * bits_per_sample / 8;
     }
     _playing = true;
     _frame_count = 0;
@@ -333,16 +320,16 @@ void PlaybackChannel::handle_raw_data(RedPeer::InMessage* message)
     _wave_player->write(data);
 }
 
-void PlaybackChannel::handle_celt_data(RedPeer::InMessage* message)
+void PlaybackChannel::handle_compressed_data(RedPeer::InMessage* message)
 {
     SpiceMsgPlaybackPacket* packet = (SpiceMsgPlaybackPacket*)message->data();
     uint8_t* data = packet->data;
     uint32_t size = packet->data_size;
-    celt_int16_t pcm[256 * 2];
+    int pcm_size = _frame_bytes;
+    uint8_t pcm[_frame_bytes];
 
-    if (celt051_decode(_celt_decoder, data, size, pcm) != CELT_OK) {
-        THROW("celt decode failed");
-    }
+    if (snd_codec_decode(&codec, data, size, pcm, &pcm_size) != SND_CODEC_OK)
+        THROW("decode failed");
 #ifdef WAVE_CAPTURE
     put_wave_data(pcm, _frame_bytes);
     return;
diff --git a/client/record_channel.cpp b/client/record_channel.cpp
index d9332c6..5c878f2 100644
--- a/client/record_channel.cpp
+++ b/client/record_channel.cpp
@@ -60,8 +60,6 @@ void RecordSamplesMessage::release()
     _channel.release_message(this);
 }
 
-int RecordChannel::data_mode = SPICE_AUDIO_DATA_MODE_CELT_0_5_1;
-
 class RecordHandler: public MessageHandlerImp<RecordChannel, SPICE_CHANNEL_RECORD> {
 public:
     RecordHandler(RecordChannel& channel)
@@ -72,8 +70,6 @@ RecordChannel::RecordChannel(RedClient& client, uint32_t id)
     : RedChannel(client, SPICE_CHANNEL_RECORD, id, new RecordHandler(*this))
     , _wave_recorder (NULL)
     , _mode (SPICE_AUDIO_DATA_MODE_INVALID)
-    , _celt_mode (NULL)
-    , _celt_encoder (NULL)
 {
     for (int i = 0; i < NUM_SAMPLES_MESSAGES; i++) {
         _messages.push_front(new RecordSamplesMessage(*this));
@@ -90,7 +86,8 @@ RecordChannel::RecordChannel(RedClient& client, uint32_t id)
 
     handler->set_handler(SPICE_MSG_RECORD_START, &RecordChannel::handle_start);
 
-    set_capability(SPICE_RECORD_CAP_CELT_0_5_1);
+    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1))
+        set_capability(SPICE_RECORD_CAP_CELT_0_5_1);
 }
 
 RecordChannel::~RecordChannel(void)
@@ -114,9 +111,12 @@ void RecordChannel::on_connect()
     Message* message = new Message(SPICE_MSGC_RECORD_MODE);
     SpiceMsgcRecordMode mode;
     mode.time = get_mm_time();
-    mode.mode = _mode =
-      test_capability(SPICE_RECORD_CAP_CELT_0_5_1) ? RecordChannel::data_mode :
-                                                                      SPICE_AUDIO_DATA_MODE_RAW;
+    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1) &&
+      test_capability(SPICE_RECORD_CAP_CELT_0_5_1))
+          _mode = SPICE_AUDIO_DATA_MODE_CELT_0_5_1;
+      else
+          _mode = SPICE_AUDIO_DATA_MODE_RAW;
+    mode.mode = _mode;
     _marshallers->msgc_record_mode(message->marshaller(), &mode);
     post_message(message);
 }
@@ -142,7 +142,7 @@ void RecordChannel::handle_start(RedPeer::InMessage* message)
 
     handler->set_handler(SPICE_MSG_RECORD_START, NULL);
     handler->set_handler(SPICE_MSG_RECORD_STOP, &RecordChannel::handle_stop);
-    ASSERT(!_wave_recorder && !_celt_mode && !_celt_encoder);
+    ASSERT(!_wave_recorder);
 
     // for now support only one setting
     if (start->format != SPICE_AUDIO_FMT_S16) {
@@ -159,17 +159,13 @@ void RecordChannel::handle_start(RedPeer::InMessage* message)
         return;
     }
 
-    int frame_size = 256;
-    int celt_mode_err;
-    _frame_bytes = frame_size * bits_per_sample * start->channels / 8;
-    if (!(_celt_mode = celt051_mode_create(start->frequency, start->channels, frame_size,
-                                           &celt_mode_err))) {
-        THROW("create celt mode failed %d", celt_mode_err);
-    }
-
-    if (!(_celt_encoder = celt051_encoder_create(_celt_mode))) {
-        THROW("create celt encoder failed");
+    int frame_size = SND_CODEC_MAX_FRAME_SIZE;
+    if (_mode != SPICE_AUDIO_DATA_MODE_RAW) {
+        if (snd_codec_create(&codec, _mode, TRUE, FALSE) != SND_CODEC_OK)
+            THROW("create encoder failed");
+        frame_size = snd_codec_frame_size(&codec);
     }
+    _frame_bytes = frame_size * bits_per_sample * start->channels / 8;
 
     send_start_mark();
     _wave_recorder->start();
@@ -182,14 +178,7 @@ void RecordChannel::clear()
         delete _wave_recorder;
         _wave_recorder = NULL;
     }
-    if (_celt_encoder) {
-        celt051_encoder_destroy(_celt_encoder);
-        _celt_encoder = NULL;
-    }
-    if (_celt_mode) {
-        celt051_mode_destroy(_celt_mode);
-        _celt_mode = NULL;
-    }
+    snd_codec_destroy(&codec);
 }
 
 void RecordChannel::handle_stop(RedPeer::InMessage* message)
@@ -200,7 +189,6 @@ void RecordChannel::handle_stop(RedPeer::InMessage* message)
     if (!_wave_recorder) {
         return;
     }
-    ASSERT(_celt_mode && _celt_encoder);
     clear();
 }
 
@@ -242,10 +230,6 @@ void RecordChannel::remove_event_source(EventSources::Trigger& event_source)
     get_process_loop().remove_trigger(event_source);
 }
 
-#define FRAME_SIZE 256
-#define CELT_BIT_RATE (64 * 1024)
-#define CELT_COMPRESSED_FRAME_BYTES (FRAME_SIZE * CELT_BIT_RATE / 44100 / 8)
-
 void RecordChannel::push_frame(uint8_t *frame)
 {
     RecordSamplesMessage *message;
@@ -254,19 +238,18 @@ void RecordChannel::push_frame(uint8_t *frame)
         DBG(0, "blocked");
         return;
     }
-    uint8_t celt_buf[CELT_COMPRESSED_FRAME_BYTES];
     int n;
 
-    if (_mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1) {
-        n = celt051_encode(_celt_encoder, (celt_int16_t *)frame, NULL, celt_buf,
-                           CELT_COMPRESSED_FRAME_BYTES);
-        if (n < 0) {
-            THROW("celt encode failed");
-        }
-        frame = celt_buf;
-    } else {
+
+    if (_mode == SPICE_AUDIO_DATA_MODE_RAW) {
         n = _frame_bytes;
+    } else {
+        n = sizeof(compressed_buf);
+        if (snd_codec_encode(&codec, frame, _frame_bytes, compressed_buf, &n) != SND_CODEC_OK)
+            THROW("encode failed");
+        frame = compressed_buf;
     }
+
     RedPeer::OutMessage& peer_message = message->peer_message();
     peer_message.reset(SPICE_MSGC_RECORD_DATA);
     SpiceMsgcRecordPacket packet;
diff --git a/configure.ac b/configure.ac
index fa1ba31..4519a84 100644
--- a/configure.ac
+++ b/configure.ac
@@ -149,6 +149,10 @@ if test "x$enable_smartcard" = "xyes"; then
    AC_DEFINE([USE_SMARTCARD], [1], [Define if supporting smartcard proxying])
 fi
 
+AC_ARG_ENABLE(celt051,
+[  --disable-celt051       Disable celt051 audio codec (enabled by default)],,
+[enable_celt051="yes"])
+
 AC_ARG_ENABLE(client,
 [  --enable-client         Enable spice client],,
 [enable_client="no"])
@@ -247,11 +251,17 @@ AC_SUBST(PIXMAN_CFLAGS)
 AC_SUBST(PIXMAN_LIBS)
 SPICE_REQUIRES+=" pixman-1 >= 0.17.7"
 
-PKG_CHECK_MODULES(CELT051, celt051 >= 0.5.1.1)
-AC_SUBST(CELT051_CFLAGS)
-AC_SUBST(CELT051_LIBS)
-AC_SUBST(CELT051_LIBDIR)
-SPICE_REQUIRES+=" celt051 >= 0.5.1.1"
+if test "x$enable_celt051" = "xyes"; then
+    PKG_CHECK_MODULES(CELT051, celt051 >= 0.5.1.1, have_celt051=yes, have_celt051=no)
+    AC_SUBST(CELT051_CFLAGS)
+    AC_SUBST(CELT051_LIBS)
+    AC_SUBST(CELT051_LIBDIR)
+else
+    have_celt051=no
+fi
+
+AM_CONDITIONAL([HAVE_CELT051], [test "x$have_celt051" = "xyes"])
+AM_COND_IF([HAVE_CELT051], AC_DEFINE([HAVE_CELT051], 1, [Define if we have celt051 codec]))
 
 if test ! -e client/generated_marshallers.cpp; then
 AC_MSG_CHECKING([for pyparsing python module])
@@ -537,6 +547,8 @@ echo "
 
         Smartcard:                ${enable_smartcard}
 
+        celt051:                  ${have_celt051}
+
         SASL support:             ${enable_sasl}
 
         Automated tests:          ${enable_automated_tests}
diff --git a/server/snd_worker.c b/server/snd_worker.c
index ebddfcd..487849e 100644
--- a/server/snd_worker.c
+++ b/server/snd_worker.c
@@ -25,7 +25,6 @@
 #include <sys/socket.h>
 #include <netinet/ip.h>
 #include <netinet/tcp.h>
-#include <celt051/celt.h>
 
 #include "common/marshaller.h"
 #include "common/generated_server_marshallers.h"
@@ -36,20 +35,14 @@
 #include "reds.h"
 #include "red_dispatcher.h"
 #include "snd_worker.h"
+#include "common/snd_codec.h"
 #include "demarshallers.h"
 
 #ifndef IOV_MAX
 #define IOV_MAX 1024
 #endif
 
-#define RECIVE_BUF_SIZE (16 * 1024 * 2)
-
-#define FRAME_SIZE 256
-#define PLAYBACK_BUF_SIZE (FRAME_SIZE * 4)
-
-#define CELT_BIT_RATE (64 * 1024)
-#define CELT_COMPRESSED_FRAME_BYTES (FRAME_SIZE * CELT_BIT_RATE / SPICE_INTERFACE_PLAYBACK_FREQ / 8)
-
+#define RECIVE_BUF_SIZE     ((16 * 1024 * 2) >> 2)
 #define RECORD_SAMPLES_SIZE (RECIVE_BUF_SIZE >> 2)
 
 enum PlaybackeCommand {
@@ -129,7 +122,7 @@ typedef struct PlaybackChannel PlaybackChannel;
 typedef struct AudioFrame AudioFrame;
 struct AudioFrame {
     uint32_t time;
-    uint32_t samples[FRAME_SIZE];
+    uint32_t samples[SND_CODEC_MAX_FRAME_SIZE];
     PlaybackChannel *channel;
     AudioFrame *next;
 };
@@ -140,13 +133,10 @@ struct PlaybackChannel {
     AudioFrame *free_frames;
     AudioFrame *in_progress;
     AudioFrame *pending_frame;
-    CELTMode *celt_mode;
-    CELTEncoder *celt_encoder;
     uint32_t mode;
-    struct {
-        uint8_t celt_buf[CELT_COMPRESSED_FRAME_BYTES];
-    } send_data;
     uint32_t latency;
+    SndCodec codec;
+    uint8_t  encode_buf[SND_CODEC_MAX_COMPRESSED_BYTES];
 };
 
 struct SndWorker {
@@ -182,13 +172,12 @@ typedef struct RecordChannel {
     uint32_t mode;
     uint32_t mode_time;
     uint32_t start_time;
-    CELTDecoder *celt_decoder;
-    CELTMode *celt_mode;
-    uint32_t celt_buf[FRAME_SIZE];
+    SndCodec codec;
+    uint8_t  decode_buf[SND_CODEC_MAX_FRAME_BYTES];
 } RecordChannel;
 
 static SndWorker *workers;
-static uint32_t playback_compression = SPICE_AUDIO_DATA_MODE_CELT_0_5_1;
+static uint32_t playback_compression = SPICE_PLAYBACK_COMPRESSION_AUTO;
 
 static void snd_receive(void* data);
 
@@ -321,23 +310,19 @@ static int snd_record_handle_write(RecordChannel *record_channel, size_t size, v
     }
 
     packet = (SpiceMsgcRecordPacket *)message;
-    size = packet->data_size;
 
-    if (record_channel->mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1) {
-        int celt_err = celt051_decode(record_channel->celt_decoder, packet->data, size,
-                                      (celt_int16_t *)record_channel->celt_buf);
-        if (celt_err != CELT_OK) {
-            spice_printerr("celt decode failed (%d)", celt_err);
-            return FALSE;
-        }
-        data = record_channel->celt_buf;
-        size = FRAME_SIZE;
-    } else if (record_channel->mode == SPICE_AUDIO_DATA_MODE_RAW) {
+    if (record_channel->mode == SPICE_AUDIO_DATA_MODE_RAW) {
         data = (uint32_t *)packet->data;
-        size = size >> 2;
+        size = packet->data_size >> 2;
         size = MIN(size, RECORD_SAMPLES_SIZE);
-    } else {
-        return FALSE;
+     } else {
+        int decode_size;
+        decode_size = sizeof(record_channel->decode_buf);
+        if (snd_codec_decode(&record_channel->codec, packet->data, packet->data_size,
+                    record_channel->decode_buf, &decode_size) != SND_CODEC_OK)
+            return FALSE;
+        data = (uint32_t *) record_channel->decode_buf;
+        size = decode_size >> 2;
     }
 
     write_pos = record_channel->write_pos % RECORD_SAMPLES_SIZE;
@@ -387,9 +372,9 @@ static int snd_record_handle_message(SndChannel *channel, size_t size, uint32_t
         SpiceMsgcRecordMode *mode = (SpiceMsgcRecordMode *)message;
         record_channel->mode = mode->mode;
         record_channel->mode_time = mode->time;
-        if (record_channel->mode != SPICE_AUDIO_DATA_MODE_CELT_0_5_1 &&
-                                                  record_channel->mode != SPICE_AUDIO_DATA_MODE_RAW) {
-            spice_printerr("unsupported mode");
+        if (record_channel->mode != SPICE_AUDIO_DATA_MODE_RAW &&
+             ! snd_codec_is_capable(record_channel->mode)) {
+            spice_printerr("unsupported mode %d", record_channel->mode);
         }
         break;
     }
@@ -758,19 +743,19 @@ static int snd_playback_send_write(PlaybackChannel *playback_channel)
 
     spice_marshall_msg_playback_data(channel->send_data.marshaller, &msg);
 
-    if (playback_channel->mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1) {
-        int n = celt051_encode(playback_channel->celt_encoder, (celt_int16_t *)frame->samples, NULL,
-                               playback_channel->send_data.celt_buf, CELT_COMPRESSED_FRAME_BYTES);
-        if (n < 0) {
-            spice_printerr("celt encode failed");
+    if (playback_channel->mode == SPICE_AUDIO_DATA_MODE_RAW) {
+        spice_marshaller_add_ref(channel->send_data.marshaller,
+                                 (uint8_t *)frame->samples, sizeof(frame->samples));
+    }
+    else {
+        int n = sizeof(playback_channel->encode_buf);
+        if (snd_codec_encode(&playback_channel->codec, (uint8_t *) frame->samples, sizeof(frame->samples),
+                                    playback_channel->encode_buf, &n) != SND_CODEC_OK) {
+            spice_printerr("encode failed");
             snd_disconnect_channel(channel);
             return FALSE;
         }
-        spice_marshaller_add_ref(channel->send_data.marshaller,
-                                 playback_channel->send_data.celt_buf, n);
-    } else {
-        spice_marshaller_add_ref(channel->send_data.marshaller,
-                                 (uint8_t *)frame->samples, sizeof(frame->samples));
+        spice_marshaller_add_ref(channel->send_data.marshaller, playback_channel->encode_buf, n);
     }
 
     return snd_begin_send_message(channel);
@@ -1090,7 +1075,7 @@ SPICE_GNUC_VISIBLE void spice_server_playback_get_buffer(SpicePlaybackInstance *
 
     *frame = playback_channel->free_frames->samples;
     playback_channel->free_frames = playback_channel->free_frames->next;
-    *num_samples = FRAME_SIZE;
+    *num_samples = snd_codec_frame_size(&playback_channel->codec);
 }
 
 SPICE_GNUC_VISIBLE void spice_server_playback_put_samples(SpicePlaybackInstance *sin, uint32_t *samples)
@@ -1140,6 +1125,18 @@ void snd_set_playback_latency(RedClient *client, uint32_t latency)
         }
     }
 }
+
+static int snd_desired_audio_mode(int client_can_celt)
+{
+    if (playback_compression == SPICE_PLAYBACK_COMPRESSION_NONE)
+        return SPICE_AUDIO_DATA_MODE_RAW;
+
+    if (client_can_celt && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1))
+        return SPICE_AUDIO_DATA_MODE_CELT_0_5_1;
+
+    return SPICE_AUDIO_DATA_MODE_RAW;
+}
+
 static void on_new_playback_channel(SndWorker *worker)
 {
     PlaybackChannel *playback_channel =
@@ -1168,8 +1165,7 @@ static void snd_playback_cleanup(SndChannel *channel)
         reds_enable_mm_timer();
     }
 
-    celt051_encoder_destroy(playback_channel->celt_encoder);
-    celt051_mode_destroy(playback_channel->celt_mode);
+    snd_codec_destroy(&playback_channel->codec);
 }
 
 static void snd_set_playback_peer(RedChannel *channel, RedClient *client, RedsStream *stream,
@@ -1179,25 +1175,9 @@ static void snd_set_playback_peer(RedChannel *channel, RedClient *client, RedsSt
     SndWorker *worker = channel->data;
     PlaybackChannel *playback_channel;
     SpicePlaybackState *st = SPICE_CONTAINEROF(worker, SpicePlaybackState, worker);
-    CELTEncoder *celt_encoder;
-    CELTMode *celt_mode;
-    int celt_error;
-    RedChannelClient *rcc;
 
     snd_disconnect_channel(worker->connection);
 
-    if (!(celt_mode = celt051_mode_create(SPICE_INTERFACE_PLAYBACK_FREQ,
-                                          SPICE_INTERFACE_PLAYBACK_CHAN,
-                                          FRAME_SIZE, &celt_error))) {
-        spice_printerr("create celt mode failed %d", celt_error);
-        return;
-    }
-
-    if (!(celt_encoder = celt051_encoder_create(celt_mode))) {
-        spice_printerr("create celt encoder failed");
-        goto error_1;
-    }
-
     if (!(playback_channel = (PlaybackChannel *)__new_channel(worker,
                                                               sizeof(*playback_channel),
                                                               SPICE_CHANNEL_PLAYBACK,
@@ -1210,32 +1190,31 @@ static void snd_set_playback_peer(RedChannel *channel, RedClient *client, RedsSt
                                                               snd_playback_cleanup,
                                                               common_caps, num_common_caps,
                                                               caps, num_caps))) {
-        goto error_2;
+        return;
     }
     worker->connection = &playback_channel->base;
-    rcc = playback_channel->base.channel_client;
     snd_playback_free_frame(playback_channel, &playback_channel->frames[0]);
     snd_playback_free_frame(playback_channel, &playback_channel->frames[1]);
     snd_playback_free_frame(playback_channel, &playback_channel->frames[2]);
 
-    playback_channel->celt_mode = celt_mode;
-    playback_channel->celt_encoder = celt_encoder;
-    playback_channel->mode = red_channel_client_test_remote_cap(rcc,
-                                                                SPICE_PLAYBACK_CAP_CELT_0_5_1) ?
-        playback_compression : SPICE_AUDIO_DATA_MODE_RAW;
+    int client_can_celt = red_channel_client_test_remote_cap(playback_channel->base.channel_client,
+                                          SPICE_PLAYBACK_CAP_CELT_0_5_1);
+    int desired_mode = snd_desired_audio_mode(client_can_celt);
+    playback_channel->mode = SPICE_AUDIO_DATA_MODE_RAW;
+    if (desired_mode != SPICE_AUDIO_DATA_MODE_RAW)
+    {
+        if (snd_codec_create(&playback_channel->codec, desired_mode, TRUE, FALSE) == SND_CODEC_OK) {
+            playback_channel->mode = desired_mode;
+        } else {
+            spice_printerr("create encoder failed");
+        }
+    }
 
     on_new_playback_channel(worker);
     if (worker->active) {
         spice_server_playback_start(st->sin);
     }
     snd_playback_send(worker->connection);
-    return;
-
-error_2:
-    celt051_encoder_destroy(celt_encoder);
-
-error_1:
-    celt051_mode_destroy(celt_mode);
 }
 
 static void snd_record_migrate_channel_client(RedChannelClient *rcc)
@@ -1380,9 +1359,7 @@ static void on_new_record_channel(SndWorker *worker)
 static void snd_record_cleanup(SndChannel *channel)
 {
     RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
-
-    celt051_decoder_destroy(record_channel->celt_decoder);
-    celt051_mode_destroy(record_channel->celt_mode);
+    snd_codec_destroy(&record_channel->codec);
 }
 
 static void snd_set_record_peer(RedChannel *channel, RedClient *client, RedsStream *stream,
@@ -1392,24 +1369,9 @@ static void snd_set_record_peer(RedChannel *channel, RedClient *client, RedsStre
     SndWorker *worker = channel->data;
     RecordChannel *record_channel;
     SpiceRecordState *st = SPICE_CONTAINEROF(worker, SpiceRecordState, worker);
-    CELTDecoder *celt_decoder;
-    CELTMode *celt_mode;
-    int celt_error;
 
     snd_disconnect_channel(worker->connection);
 
-    if (!(celt_mode = celt051_mode_create(SPICE_INTERFACE_RECORD_FREQ,
-                                          SPICE_INTERFACE_RECORD_CHAN,
-                                          FRAME_SIZE, &celt_error))) {
-        spice_printerr("create celt mode failed %d", celt_error);
-        return;
-    }
-
-    if (!(celt_decoder = celt051_decoder_create(celt_mode))) {
-        spice_printerr("create celt decoder failed");
-        goto error_1;
-    }
-
     if (!(record_channel = (RecordChannel *)__new_channel(worker,
                                                           sizeof(*record_channel),
                                                           SPICE_CHANNEL_RECORD,
@@ -1422,26 +1384,29 @@ static void snd_set_record_peer(RedChannel *channel, RedClient *client, RedsStre
                                                           snd_record_cleanup,
                                                           common_caps, num_common_caps,
                                                           caps, num_caps))) {
-        goto error_2;
+        return;
     }
 
-    worker->connection = &record_channel->base;
+    int client_can_celt = red_channel_client_test_remote_cap(record_channel->base.channel_client,
+                                          SPICE_RECORD_CAP_CELT_0_5_1);
+    int desired_mode = snd_desired_audio_mode(client_can_celt);
+    record_channel->mode = SPICE_AUDIO_DATA_MODE_RAW;
+    if (desired_mode != SPICE_AUDIO_DATA_MODE_RAW)
+    {
+        if (snd_codec_create(&record_channel->codec, desired_mode, FALSE, TRUE) == SND_CODEC_OK) {
+            record_channel->mode = desired_mode;
+        } else {
+            spice_printerr("create decoder failed");
+        }
+    }
 
-    record_channel->celt_mode = celt_mode;
-    record_channel->celt_decoder = celt_decoder;
+    worker->connection = &record_channel->base;
 
     on_new_record_channel(worker);
     if (worker->active) {
         spice_server_record_start(st->sin);
     }
     snd_record_send(worker->connection);
-    return;
-
-error_2:
-    celt051_decoder_destroy(celt_decoder);
-
-error_1:
-    celt051_mode_destroy(celt_mode);
 }
 
 static void snd_playback_migrate_channel_client(RedChannelClient *rcc)
@@ -1498,7 +1463,10 @@ void snd_attach_playback(SpicePlaybackInstance *sin)
     client_cbs.migrate = snd_playback_migrate_channel_client;
     red_channel_register_client_cbs(channel, &client_cbs);
     red_channel_set_data(channel, playback_worker);
-    red_channel_set_cap(channel, SPICE_PLAYBACK_CAP_CELT_0_5_1);
+
+    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1))
+        red_channel_set_cap(channel, SPICE_PLAYBACK_CAP_CELT_0_5_1);
+
     red_channel_set_cap(channel, SPICE_PLAYBACK_CAP_VOLUME);
 
     playback_worker->base_channel = channel;
@@ -1525,7 +1493,8 @@ void snd_attach_record(SpiceRecordInstance *sin)
     client_cbs.migrate = snd_record_migrate_channel_client;
     red_channel_register_client_cbs(channel, &client_cbs);
     red_channel_set_data(channel, record_worker);
-    red_channel_set_cap(channel, SPICE_RECORD_CAP_CELT_0_5_1);
+    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1))
+        red_channel_set_cap(channel, SPICE_RECORD_CAP_CELT_0_5_1);
     red_channel_set_cap(channel, SPICE_RECORD_CAP_VOLUME);
 
     record_worker->base_channel = channel;
@@ -1568,22 +1537,19 @@ void snd_detach_record(SpiceRecordInstance *sin)
     spice_record_state_free(sin->st);
 }
 
-void snd_set_playback_compression(int on)
+void snd_set_playback_compression(int comp)
 {
     SndWorker *now = workers;
 
-    playback_compression = on ? SPICE_AUDIO_DATA_MODE_CELT_0_5_1 : SPICE_AUDIO_DATA_MODE_RAW;
+    playback_compression = comp;
+
+    int desired_mode = snd_desired_audio_mode(TRUE);
+
     for (; now; now = now->next) {
         if (now->base_channel->type == SPICE_CHANNEL_PLAYBACK && now->connection) {
-            SndChannel* sndchannel = now->connection;
             PlaybackChannel* playback = (PlaybackChannel*)now->connection;
-            if (!red_channel_client_test_remote_cap(sndchannel->channel_client,
-                                                    SPICE_PLAYBACK_CAP_CELT_0_5_1)) {
-                spice_assert(playback->mode == SPICE_AUDIO_DATA_MODE_RAW);
-                continue;
-            }
-            if (playback->mode != playback_compression) {
-                playback->mode = playback_compression;
+            if (playback->mode != desired_mode) {
+                playback->mode = desired_mode;
                 snd_set_command(now->connection, SND_PLAYBACK_MODE_MASK);
             }
         }
@@ -1592,5 +1558,5 @@ void snd_set_playback_compression(int on)
 
 int snd_get_playback_compression(void)
 {
-    return (playback_compression == SPICE_AUDIO_DATA_MODE_RAW) ? FALSE : TRUE;
+    return playback_compression;
 }
diff --git a/server/snd_worker.h b/server/snd_worker.h
index 8de746d..1448eab 100644
--- a/server/snd_worker.h
+++ b/server/snd_worker.h
@@ -18,6 +18,10 @@
 #ifndef _H_SND_WORKER
 #define _H_SND_WORKER
 
+#define SPICE_PLAYBACK_COMPRESSION_NONE     0
+#define SPICE_PLAYBACK_COMPRESSION_AUTO     1
+#define SPICE_PLAYBACK_COMPRESSION_CELT051  2
+
 #include "spice.h"
 
 void snd_attach_playback(SpicePlaybackInstance *sin);
-- 
1.7.10.4

_______________________________________________
Spice-devel mailing list
Spice-devel@xxxxxxxxxxxxxxxxxxxxx
http://lists.freedesktop.org/mailman/listinfo/spice-devel

[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]     [Monitors]