[Spice-commits] 3 commits - README client/audio_channels.h client/audio_devices.h client/platform.h client/playback_channel.cpp client/record_channel.cpp client/windows client/x11 configure.ac server/Makefile.am server/snd_worker.c server/spice-server.syms server/spice.h spice-common

Christophe Fergau teuf at kemper.freedesktop.org
Thu Jan 2 03:41:48 PST 2014


 README                      |    1 
 client/audio_channels.h     |   15 +-
 client/audio_devices.h      |    8 -
 client/platform.h           |   10 -
 client/playback_channel.cpp |   68 ++++------
 client/record_channel.cpp   |   88 ++++++-------
 client/windows/platform.cpp |    8 -
 client/windows/playback.cpp |   14 +-
 client/windows/playback.h   |    4 
 client/windows/record.cpp   |   12 -
 client/windows/record.h     |    4 
 client/x11/platform.cpp     |   14 +-
 client/x11/playback.cpp     |   25 ++-
 client/x11/playback.h       |    7 -
 client/x11/record.cpp       |   23 ++-
 client/x11/record.h         |   11 +
 configure.ac                |   12 +
 server/Makefile.am          |    2 
 server/snd_worker.c         |  278 ++++++++++++++++++++++++--------------------
 server/spice-server.syms    |    8 +
 server/spice.h              |   15 +-
 spice-common                |    2 
 22 files changed, 335 insertions(+), 294 deletions(-)

New commits:
commit 530888ad900421ddbf43154fe0f9b916e7cde3c7
Author: Jeremy White <jwhite at codeweavers.com>
Date:   Sat Nov 30 09:19:47 2013 -0600

    Fix typo; sampel --> sample
    
    Signed-off-by: Jeremy White <jwhite at codeweavers.com>

diff --git a/client/platform.h b/client/platform.h
index 0a88f06..7b543e9 100644
--- a/client/platform.h
+++ b/client/platform.h
@@ -66,11 +66,11 @@ public:
 
     class RecordClient;
     static WaveRecordAbstract* create_recorder(RecordClient& client,
-                                               uint32_t sampels_per_sec,
+                                               uint32_t samples_per_sec,
                                                uint32_t bits_per_sample,
                                                uint32_t channels,
                                                uint32_t frame_size);
-    static WavePlaybackAbstract* create_player(uint32_t sampels_per_sec,
+    static WavePlaybackAbstract* create_player(uint32_t samples_per_sec,
                                                uint32_t bits_per_sample,
                                                uint32_t channels,
                                                uint32_t frame_size);
diff --git a/client/windows/platform.cpp b/client/windows/platform.cpp
index d3417e3..fc51a12 100644
--- a/client/windows/platform.cpp
+++ b/client/windows/platform.cpp
@@ -640,18 +640,18 @@ void Platform::set_process_loop(ProcessLoop& main_process_loop)
 }
 
 WaveRecordAbstract* Platform::create_recorder(RecordClient& client,
-                                              uint32_t sampels_per_sec,
+                                              uint32_t samples_per_sec,
                                               uint32_t bits_per_sample,
                                               uint32_t channels)
 {
-    return new WaveRecorder(client, sampels_per_sec, bits_per_sample, channels);
+    return new WaveRecorder(client, samples_per_sec, bits_per_sample, channels);
 }
 
-WavePlaybackAbstract* Platform::create_player(uint32_t sampels_per_sec,
+WavePlaybackAbstract* Platform::create_player(uint32_t samples_per_sec,
                                               uint32_t bits_per_sample,
                                               uint32_t channels)
 {
-    return new WavePlayer(sampels_per_sec, bits_per_sample, channels);
+    return new WavePlayer(samples_per_sec, bits_per_sample, channels);
 }
 
 static void toggle_modifier(int key)
diff --git a/client/windows/playback.cpp b/client/windows/playback.cpp
index 41b92bc..87ed26a 100644
--- a/client/windows/playback.cpp
+++ b/client/windows/playback.cpp
@@ -29,9 +29,9 @@
 #define LOW_MARK_MS 40
 
 
-WavePlayer::WavePlayer(uint32_t sampels_per_sec, uint32_t bits_per_sample, uint32_t channels)
+WavePlayer::WavePlayer(uint32_t samples_per_sec, uint32_t bits_per_sample, uint32_t channels)
     : _wave_out (NULL)
-    , _sampels_per_ms (sampels_per_sec / 1000)
+    , _samples_per_ms (samples_per_sec / 1000)
     , _ring (NULL)
     , _head (0)
     , _in_use (0)
@@ -42,9 +42,9 @@ WavePlayer::WavePlayer(uint32_t sampels_per_sec, uint32_t bits_per_sample, uint3
 
     info.wFormatTag = WAVE_FORMAT_PCM;
     info.nChannels = channels;
-    info.nSamplesPerSec = sampels_per_sec;
+    info.nSamplesPerSec = samples_per_sec;
     sample_bytes = info.nBlockAlign = channels * bits_per_sample / 8;
-    info.nAvgBytesPerSec = sampels_per_sec * info.nBlockAlign;
+    info.nAvgBytesPerSec = samples_per_sec * info.nBlockAlign;
     info.wBitsPerSample = bits_per_sample;
 
     if (waveOutOpen(&_wave_out, WAVE_MAPPER, &info, 0, 0, CALLBACK_NULL)
@@ -53,8 +53,8 @@ WavePlayer::WavePlayer(uint32_t sampels_per_sec, uint32_t bits_per_sample, uint3
     }
 
     int frame_size = WavePlaybackAbstract::FRAME_SIZE;
-    _ring_size = (sampels_per_sec * RING_SIZE_MS / 1000) / frame_size;
-    _start_mark = (sampels_per_sec * START_MARK_MS / 1000) / frame_size;
+    _ring_size = (samples_per_sec * RING_SIZE_MS / 1000) / frame_size;
+    _start_mark = (samples_per_sec * START_MARK_MS / 1000) / frame_size;
     _frame_bytes = frame_size * channels * bits_per_sample / 8;
     _ring_item_size = sizeof(WAVEHDR) + _frame_bytes + sample_bytes;
 
@@ -174,5 +174,5 @@ bool WavePlayer::abort()
 
 uint32_t WavePlayer::get_delay_ms()
 {
-    return _in_use * WavePlaybackAbstract::FRAME_SIZE / _sampels_per_ms;
+    return _in_use * WavePlaybackAbstract::FRAME_SIZE / _samples_per_ms;
 }
diff --git a/client/windows/playback.h b/client/windows/playback.h
index 6f70a7b..6493eff 100644
--- a/client/windows/playback.h
+++ b/client/windows/playback.h
@@ -22,7 +22,7 @@
 
 class WavePlayer: public WavePlaybackAbstract {
 public:
-    WavePlayer(uint32_t sampels_per_sec, uint32_t bits_per_sample, uint32_t channels);
+    WavePlayer(uint32_t samples_per_sec, uint32_t bits_per_sample, uint32_t channels);
     virtual ~WavePlayer();
 
     virtual bool write(uint8_t* frame);
@@ -40,7 +40,7 @@ private:
 
 private:
     HWAVEOUT _wave_out;
-    uint32_t _sampels_per_ms;
+    uint32_t _samples_per_ms;
     uint32_t _frame_bytes;
     uint32_t _start_mark;
     uint32_t _ring_item_size;
diff --git a/client/windows/record.cpp b/client/windows/record.cpp
index 0736b11..a2e5f39 100644
--- a/client/windows/record.cpp
+++ b/client/windows/record.cpp
@@ -32,7 +32,7 @@ static void CALLBACK in_proc(HWAVEIN handle, UINT msg, DWORD user_data, DWORD pa
     recorder->trigger();
 }
 
-WaveRecorder::WaveRecorder(Platform::RecordClient& client, uint32_t sampels_per_sec,
+WaveRecorder::WaveRecorder(Platform::RecordClient& client, uint32_t samples_per_sec,
                            uint32_t bits_per_sample, uint32_t channels)
     : _client (client)
     , _ring (NULL)
@@ -45,9 +45,9 @@ WaveRecorder::WaveRecorder(Platform::RecordClient& client, uint32_t sampels_per_
 
     info.wFormatTag = WAVE_FORMAT_PCM;
     info.nChannels = channels;
-    info.nSamplesPerSec = sampels_per_sec;
+    info.nSamplesPerSec = samples_per_sec;
     info.nBlockAlign = frame_align = channels * bits_per_sample / 8;
-    info.nAvgBytesPerSec = sampels_per_sec * info.nBlockAlign;
+    info.nAvgBytesPerSec = samples_per_sec * info.nBlockAlign;
     info.wBitsPerSample = bits_per_sample;
 
 
@@ -63,7 +63,7 @@ WaveRecorder::WaveRecorder(Platform::RecordClient& client, uint32_t sampels_per_
         _frame = new uint8_t[frame_bytes];
         _frame_pos = _frame;
         _frame_end = _frame + frame_bytes;
-        init_ring(sampels_per_sec, frame_bytes, frame_align);
+        init_ring(samples_per_sec, frame_bytes, frame_align);
         _client.add_event_source(*this);
     } catch (...) {
         delete[] _ring;
@@ -83,11 +83,11 @@ WaveRecorder::~WaveRecorder()
     delete[] _frame;
 }
 
-void WaveRecorder::init_ring(uint32_t sampels_per_sec, uint32_t frame_bytes, uint32_t frame_align)
+void WaveRecorder::init_ring(uint32_t samples_per_sec, uint32_t frame_bytes, uint32_t frame_align)
 {
     const int frame_size = WavePlaybackAbstract::FRAME_SIZE;
 
-    _ring_size = (sampels_per_sec * RING_SIZE_MS / 1000) / frame_size;
+    _ring_size = (samples_per_sec * RING_SIZE_MS / 1000) / frame_size;
     _ring_item_size = sizeof(WAVEHDR) + frame_bytes + frame_align;
 
     int ring_bytes = _ring_size * _ring_item_size;
diff --git a/client/windows/record.h b/client/windows/record.h
index 4071af4..7326419 100644
--- a/client/windows/record.h
+++ b/client/windows/record.h
@@ -23,7 +23,7 @@
 
 class WaveRecorder: public WaveRecordAbstract, public EventSources::Trigger {
 public:
-    WaveRecorder(Platform::RecordClient& client, uint32_t sampels_per_sec,
+    WaveRecorder(Platform::RecordClient& client, uint32_t samples_per_sec,
                  uint32_t bits_per_sample, uint32_t channels);
     virtual ~WaveRecorder();
 
@@ -34,7 +34,7 @@ public:
     virtual void on_event();
 
 private:
-    void init_ring(uint32_t sampels_per_sec, uint32_t frame_bytes, uint32_t frame_align);
+    void init_ring(uint32_t samples_per_sec, uint32_t frame_bytes, uint32_t frame_align);
     WAVEHDR* wave_hader(uint32_t position);
     void move_head();
 
diff --git a/client/x11/platform.cpp b/client/x11/platform.cpp
index bb8704c..6a37483 100644
--- a/client/x11/platform.cpp
+++ b/client/x11/platform.cpp
@@ -3431,20 +3431,20 @@ void Platform::reset_cursor_pos()
 }
 
 WaveRecordAbstract* Platform::create_recorder(RecordClient& client,
-                                              uint32_t sampels_per_sec,
+                                              uint32_t samples_per_sec,
                                               uint32_t bits_per_sample,
                                               uint32_t channels,
                                               uint32_t frame_size)
 {
-    return new WaveRecorder(client, sampels_per_sec, bits_per_sample, channels, frame_size);
+    return new WaveRecorder(client, samples_per_sec, bits_per_sample, channels, frame_size);
 }
 
-WavePlaybackAbstract* Platform::create_player(uint32_t sampels_per_sec,
+WavePlaybackAbstract* Platform::create_player(uint32_t samples_per_sec,
                                               uint32_t bits_per_sample,
                                               uint32_t channels,
                                               uint32_t frame_size)
 {
-    return new WavePlayer(sampels_per_sec, bits_per_sample, channels, frame_size);
+    return new WavePlayer(samples_per_sec, bits_per_sample, channels, frame_size);
 }
 
 void XPlatform::on_focus_in()
diff --git a/client/x11/playback.cpp b/client/x11/playback.cpp
index 035d6de..83946aa 100644
--- a/client/x11/playback.cpp
+++ b/client/x11/playback.cpp
@@ -24,12 +24,12 @@
 
 #define REING_SIZE_MS 300
 
-WavePlayer::WavePlayer(uint32_t sampels_per_sec, uint32_t bits_per_sample, uint32_t channels, uint32_t frame_size)
+WavePlayer::WavePlayer(uint32_t samples_per_sec, uint32_t bits_per_sample, uint32_t channels, uint32_t frame_size)
     : _pcm (NULL)
     , _hw_params (NULL)
     , _sw_params (NULL)
 {
-    if (!init(sampels_per_sec, bits_per_sample, channels, frame_size)) {
+    if (!init(samples_per_sec, bits_per_sample, channels, frame_size)) {
         cleanup();
         THROW("failed");
     }
@@ -55,7 +55,7 @@ WavePlayer::~WavePlayer()
     cleanup();
 }
 
-bool WavePlayer::init(uint32_t sampels_per_sec,
+bool WavePlayer::init(uint32_t samples_per_sec,
                       uint32_t bits_per_sample,
                       uint32_t channels,
                       uint32_t frame_size)
@@ -74,7 +74,7 @@ bool WavePlayer::init(uint32_t sampels_per_sec,
     default:
         return false;
     }
-    _sampels_per_ms = sampels_per_sec / 1000;
+    _samples_per_ms = samples_per_sec / 1000;
     _frame_size = frame_size;
 
     if ((err = snd_pcm_open(&_pcm, pcm_device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) {
@@ -108,7 +108,7 @@ bool WavePlayer::init(uint32_t sampels_per_sec,
         return false;
     }
 
-    if ((err = snd_pcm_hw_params_set_rate(_pcm, _hw_params, sampels_per_sec, 0)) < 0) {
+    if ((err = snd_pcm_hw_params_set_rate(_pcm, _hw_params, samples_per_sec, 0)) < 0) {
         LOG_ERROR("cannot set sample rate %s", snd_strerror(err));
         return false;
     }
@@ -124,7 +124,7 @@ bool WavePlayer::init(uint32_t sampels_per_sec,
     }
 
     snd_pcm_uframes_t buffer_size;
-    buffer_size = (sampels_per_sec * REING_SIZE_MS / 1000) / frame_size * frame_size;
+    buffer_size = (samples_per_sec * REING_SIZE_MS / 1000) / frame_size * frame_size;
 
     if ((err = snd_pcm_hw_params_set_buffer_size_near(_pcm, _hw_params, &buffer_size)) < 0) {
         LOG_ERROR("cannot set buffer size %s", snd_strerror(err));
@@ -132,7 +132,7 @@ bool WavePlayer::init(uint32_t sampels_per_sec,
     }
 
     int direction = 1;
-    snd_pcm_uframes_t period_size = (sampels_per_sec * 20 / 1000) / frame_size * frame_size;
+    snd_pcm_uframes_t period_size = (samples_per_sec * 20 / 1000) / frame_size * frame_size;
     if ((err = snd_pcm_hw_params_set_period_size_near(_pcm, _hw_params, &period_size,
                                                       &direction)) < 0) {
         LOG_ERROR("cannot set period size %s", snd_strerror(err));
@@ -217,5 +217,5 @@ uint32_t WavePlayer::get_delay_ms()
     if (snd_pcm_delay(_pcm, &delay) < 0) {
         return 0;
     }
-    return delay / _sampels_per_ms;
+    return delay / _samples_per_ms;
 }
diff --git a/client/x11/playback.h b/client/x11/playback.h
index 383f02d..c0b8c52 100644
--- a/client/x11/playback.h
+++ b/client/x11/playback.h
@@ -25,7 +25,7 @@
 
 class WavePlayer: public WavePlaybackAbstract {
 public:
-    WavePlayer(uint32_t sampels_per_sec, uint32_t bits_per_sample, uint32_t channels, uint32_t frame_size);
+    WavePlayer(uint32_t samples_per_sec, uint32_t bits_per_sample, uint32_t channels, uint32_t frame_size);
     virtual ~WavePlayer();
 
     virtual bool write(uint8_t* frame);
@@ -34,14 +34,14 @@ public:
     virtual uint32_t get_delay_ms();
 
 private:
-    bool init(uint32_t sampels_per_sec, uint32_t bits_per_sample, uint32_t channel, uint32_t frame_size);
+    bool init(uint32_t samples_per_sec, uint32_t bits_per_sample, uint32_t channel, uint32_t frame_size);
     void cleanup();
 
 private:
     snd_pcm_t* _pcm;
     snd_pcm_hw_params_t* _hw_params;
     snd_pcm_sw_params_t* _sw_params;
-    uint32_t _sampels_per_ms;
+    uint32_t _samples_per_ms;
     uint32_t _frame_size;
 };
 
diff --git a/client/x11/record.cpp b/client/x11/record.cpp
index 32c0af7..6bc9f60 100644
--- a/client/x11/record.cpp
+++ b/client/x11/record.cpp
@@ -46,7 +46,7 @@ void WaveRecorder::EventTrigger::on_event()
 }
 
 WaveRecorder::WaveRecorder(Platform::RecordClient& client,
-                           uint32_t sampels_per_sec,
+                           uint32_t samples_per_sec,
                            uint32_t bits_per_sample,
                            uint32_t channels,
                            uint32_t frame_size)
@@ -60,7 +60,7 @@ WaveRecorder::WaveRecorder(Platform::RecordClient& client,
     , _frame_end (_frame + _sample_bytes * frame_size)
     , _event_trigger (NULL)
 {
-    if (!init(sampels_per_sec, bits_per_sample, channels, frame_size)) {
+    if (!init(samples_per_sec, bits_per_sample, channels, frame_size)) {
         cleanup();
         THROW("failed");
     }
@@ -92,7 +92,7 @@ void WaveRecorder::cleanup()
     }
 }
 
-bool WaveRecorder::init(uint32_t sampels_per_sec,
+bool WaveRecorder::init(uint32_t samples_per_sec,
                         uint32_t bits_per_sample,
                         uint32_t channels,
                         uint32_t frame_size)
@@ -144,7 +144,7 @@ bool WaveRecorder::init(uint32_t sampels_per_sec,
         return false;
     }
 
-    if ((err = snd_pcm_hw_params_set_rate(_pcm, _hw_params, sampels_per_sec, 0)) < 0) {
+    if ((err = snd_pcm_hw_params_set_rate(_pcm, _hw_params, samples_per_sec, 0)) < 0) {
         LOG_ERROR("cannot set sample rate %s", snd_strerror(err));
         return false;
     }
@@ -160,7 +160,7 @@ bool WaveRecorder::init(uint32_t sampels_per_sec,
     }
 
     int direction = 0;
-    snd_pcm_uframes_t buffer_size = (sampels_per_sec * 160 / 1000) / frame_size * frame_size;
+    snd_pcm_uframes_t buffer_size = (samples_per_sec * 160 / 1000) / frame_size * frame_size;
 
     if ((err = snd_pcm_hw_params_set_buffer_size_near(_pcm, _hw_params, &buffer_size)) < 0) {
         LOG_ERROR("cannot set buffer size %s", snd_strerror(err));
diff --git a/client/x11/record.h b/client/x11/record.h
index 9470791..fc46948 100644
--- a/client/x11/record.h
+++ b/client/x11/record.h
@@ -27,7 +27,7 @@
 class WaveRecorder: public WaveRecordAbstract {
 public:
     WaveRecorder(Platform::RecordClient& client,
-                 uint32_t sampels_per_sec,
+                 uint32_t samples_per_sec,
                  uint32_t bits_per_sample,
                  uint32_t channels,
                  uint32_t frame_size);
@@ -38,7 +38,7 @@ public:
     virtual bool abort();
 
 private:
-    bool init(uint32_t sampels_per_sec,
+    bool init(uint32_t samples_per_sec,
               uint32_t bits_per_sample,
               uint32_t channels,
               uint32_t frame_size);
commit ce9b714137a767b81f2d3c40b5f3ce0d5cf70fc8
Author: Jeremy White <jwhite at codeweavers.com>
Date:   Sat Nov 30 09:19:21 2013 -0600

    Add support for the Opus codec
    
    Signed-off-by: Jeremy White <jwhite at codeweavers.com>

diff --git a/client/audio_channels.h b/client/audio_channels.h
index d7b81e7..fdb4dc9 100644
--- a/client/audio_channels.h
+++ b/client/audio_channels.h
@@ -72,7 +72,6 @@ public:
     static ChannelFactory& Factory();
 
 protected:
-    virtual void on_connect();
     virtual void on_disconnect();
 
 private:
@@ -85,6 +84,8 @@ private:
     virtual void remove_event_source(EventSources::Trigger& event_source);
     virtual void push_frame(uint8_t *frame);
 
+    void set_desired_mode(int frequency);
+    void send_record_mode();
     void send_start_mark();
     void release_message(RecordSamplesMessage *message);
     RecordSamplesMessage * get_message();
diff --git a/client/audio_devices.h b/client/audio_devices.h
index a1da1f7..111c366 100644
--- a/client/audio_devices.h
+++ b/client/audio_devices.h
@@ -27,10 +27,6 @@ public:
     virtual bool abort() = 0;
     virtual void stop() = 0;
     virtual uint32_t get_delay_ms() = 0;
-
-    enum {
-        FRAME_SIZE = 256,
-    };
 };
 
 class WaveRecordAbstract {
@@ -42,10 +38,6 @@ public:
     virtual void start() = 0;
     virtual void stop() = 0;
     virtual bool abort() = 0;
-
-    enum {
-        FRAME_SIZE = 256,
-    };
 };
 
 #endif
diff --git a/client/platform.h b/client/platform.h
index 913bcde..0a88f06 100644
--- a/client/platform.h
+++ b/client/platform.h
@@ -68,10 +68,12 @@ public:
     static WaveRecordAbstract* create_recorder(RecordClient& client,
                                                uint32_t sampels_per_sec,
                                                uint32_t bits_per_sample,
-                                               uint32_t channels);
+                                               uint32_t channels,
+                                               uint32_t frame_size);
     static WavePlaybackAbstract* create_player(uint32_t sampels_per_sec,
                                                uint32_t bits_per_sample,
-                                               uint32_t channels);
+                                               uint32_t channels,
+                                               uint32_t frame_size);
 
     enum {
         SCROLL_LOCK_MODIFIER_SHIFT,
diff --git a/client/playback_channel.cpp b/client/playback_channel.cpp
index 173c94a..9f8c334 100644
--- a/client/playback_channel.cpp
+++ b/client/playback_channel.cpp
@@ -168,8 +168,10 @@ PlaybackChannel::PlaybackChannel(RedClient& client, uint32_t id)
 
     handler->set_handler(SPICE_MSG_PLAYBACK_MODE, &PlaybackChannel::handle_mode);
 
-    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1))
+    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, SND_CODEC_ANY_FREQUENCY))
         set_capability(SPICE_PLAYBACK_CAP_CELT_0_5_1);
+    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, SND_CODEC_ANY_FREQUENCY))
+        set_capability(SPICE_PLAYBACK_CAP_OPUS);
 }
 
 void PlaybackChannel::clear()
@@ -206,7 +208,7 @@ 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 (snd_codec_is_capable(_mode)) {
+    } else if (snd_codec_is_capable(_mode, SND_CODEC_ANY_FREQUENCY)) {
         handler->set_handler(SPICE_MSG_PLAYBACK_DATA, &PlaybackChannel::handle_compressed_data);
     } else {
         THROW("invalid mode");
@@ -218,7 +220,7 @@ void PlaybackChannel::handle_mode(RedPeer::InMessage* message)
 {
     SpiceMsgPlaybackMode* playback_mode = (SpiceMsgPlaybackMode*)message->data();
     if (playback_mode->mode != SPICE_AUDIO_DATA_MODE_RAW
-        && !snd_codec_is_capable(playback_mode->mode) ) {
+        && !snd_codec_is_capable(playback_mode->mode, SND_CODEC_ANY_FREQUENCY) ) {
         THROW("invalid mode");
     }
 
@@ -266,9 +268,16 @@ void PlaybackChannel::handle_start(RedPeer::InMessage* message)
         }
         int bits_per_sample = 16;
         int frame_size = SND_CODEC_MAX_FRAME_SIZE;
+
+        if (_mode != SPICE_AUDIO_DATA_MODE_RAW) {
+            if (snd_codec_create(&_codec, _mode, start->frequency, SND_CODEC_DECODE) != SND_CODEC_OK)
+                THROW("create decoder");
+            frame_size = snd_codec_frame_size(_codec);
+        }
+
         try {
             _wave_player = Platform::create_player(start->frequency, bits_per_sample,
-                                                   start->channels);
+                                                   start->channels, frame_size);
         } catch (...) {
             LOG_WARN("create player failed");
             //todo: support disconnecting single channel
@@ -276,12 +285,6 @@ void PlaybackChannel::handle_start(RedPeer::InMessage* message)
             return;
         }
 
-        if (_mode != SPICE_AUDIO_DATA_MODE_RAW) {
-            if (snd_codec_create(&_codec, _mode, start->frequency, SND_CODEC_DECODE) != SND_CODEC_OK)
-                THROW("create decoder");
-            frame_size = snd_codec_frame_size(_codec);
-        }
-
         _frame_bytes = frame_size * start->channels * bits_per_sample / 8;
     }
     _playing = true;
diff --git a/client/record_channel.cpp b/client/record_channel.cpp
index 2870f62..047cdd0 100644
--- a/client/record_channel.cpp
+++ b/client/record_channel.cpp
@@ -87,8 +87,10 @@ RecordChannel::RecordChannel(RedClient& client, uint32_t id)
 
     handler->set_handler(SPICE_MSG_RECORD_START, &RecordChannel::handle_start);
 
-    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1))
+    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, SND_CODEC_ANY_FREQUENCY))
         set_capability(SPICE_RECORD_CAP_CELT_0_5_1);
+    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, SND_CODEC_ANY_FREQUENCY))
+        set_capability(SPICE_RECORD_CAP_OPUS);
 }
 
 RecordChannel::~RecordChannel(void)
@@ -107,16 +109,23 @@ bool RecordChannel::abort(void)
     return (!_wave_recorder || _wave_recorder->abort()) && RedChannel::abort();
 }
 
-void RecordChannel::on_connect()
+void RecordChannel::set_desired_mode(int frequency)
 {
-    Message* message = new Message(SPICE_MSGC_RECORD_MODE);
-    SpiceMsgcRecordMode mode;
-    mode.time = get_mm_time();
-    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1) &&
+    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, frequency) &&
+      test_capability(SPICE_RECORD_CAP_OPUS))
+          _mode = SPICE_AUDIO_DATA_MODE_OPUS;
+    else if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, frequency) &&
       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;
+}
+
+void RecordChannel::send_record_mode()
+{
+    Message* message = new Message(SPICE_MSGC_RECORD_MODE);
+    SpiceMsgcRecordMode mode;
+    mode.time = get_mm_time();
     mode.mode = _mode;
     _marshallers->msgc_record_mode(message->marshaller(), &mode);
     post_message(message);
@@ -153,24 +162,29 @@ void RecordChannel::handle_start(RedPeer::InMessage* message)
         THROW("unexpected number of channels");
     }
 
+    set_desired_mode(start->frequency);
+
+    int frame_size = SND_CODEC_MAX_FRAME_SIZE;
+    if (_mode != SPICE_AUDIO_DATA_MODE_RAW) {
+        if (snd_codec_create(&_codec, _mode, start->frequency, SND_CODEC_ENCODE) != SND_CODEC_OK)
+            THROW("create encoder failed");
+        frame_size = snd_codec_frame_size(_codec);
+    }
+
     int bits_per_sample = 16;
     try {
         _wave_recorder = Platform::create_recorder(*this, start->frequency,
                                                    bits_per_sample,
-                                                   start->channels);
+                                                   start->channels,
+                                                   frame_size);
     } catch (...) {
         LOG_WARN("create recorder failed");
         return;
     }
 
-    int frame_size = SND_CODEC_MAX_FRAME_SIZE;
-    if (_mode != SPICE_AUDIO_DATA_MODE_RAW) {
-        if (snd_codec_create(&_codec, _mode, start->frequency, SND_CODEC_ENCODE) != 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_record_mode();
     send_start_mark();
     _wave_recorder->start();
 }
@@ -237,7 +251,6 @@ void RecordChannel::remove_event_source(EventSources::Trigger& event_source)
 void RecordChannel::push_frame(uint8_t *frame)
 {
     RecordSamplesMessage *message;
-    ASSERT(_frame_bytes == FRAME_SIZE * 4);
     if (!(message = get_message())) {
         DBG(0, "blocked");
         return;
diff --git a/client/x11/platform.cpp b/client/x11/platform.cpp
index b8563b3..bb8704c 100644
--- a/client/x11/platform.cpp
+++ b/client/x11/platform.cpp
@@ -3433,16 +3433,18 @@ void Platform::reset_cursor_pos()
 WaveRecordAbstract* Platform::create_recorder(RecordClient& client,
                                               uint32_t sampels_per_sec,
                                               uint32_t bits_per_sample,
-                                              uint32_t channels)
+                                              uint32_t channels,
+                                              uint32_t frame_size)
 {
-    return new WaveRecorder(client, sampels_per_sec, bits_per_sample, channels);
+    return new WaveRecorder(client, sampels_per_sec, bits_per_sample, channels, frame_size);
 }
 
 WavePlaybackAbstract* Platform::create_player(uint32_t sampels_per_sec,
                                               uint32_t bits_per_sample,
-                                              uint32_t channels)
+                                              uint32_t channels,
+                                              uint32_t frame_size)
 {
-    return new WavePlayer(sampels_per_sec, bits_per_sample, channels);
+    return new WavePlayer(sampels_per_sec, bits_per_sample, channels, frame_size);
 }
 
 void XPlatform::on_focus_in()
diff --git a/client/x11/playback.cpp b/client/x11/playback.cpp
index 5fa7e18..035d6de 100644
--- a/client/x11/playback.cpp
+++ b/client/x11/playback.cpp
@@ -24,12 +24,12 @@
 
 #define REING_SIZE_MS 300
 
-WavePlayer::WavePlayer(uint32_t sampels_per_sec, uint32_t bits_per_sample, uint32_t channels)
+WavePlayer::WavePlayer(uint32_t sampels_per_sec, uint32_t bits_per_sample, uint32_t channels, uint32_t frame_size)
     : _pcm (NULL)
     , _hw_params (NULL)
     , _sw_params (NULL)
 {
-    if (!init(sampels_per_sec, bits_per_sample, channels)) {
+    if (!init(sampels_per_sec, bits_per_sample, channels, frame_size)) {
         cleanup();
         THROW("failed");
     }
@@ -57,9 +57,9 @@ WavePlayer::~WavePlayer()
 
 bool WavePlayer::init(uint32_t sampels_per_sec,
                       uint32_t bits_per_sample,
-                      uint32_t channels)
+                      uint32_t channels,
+                      uint32_t frame_size)
 {
-    const int frame_size = WavePlaybackAbstract::FRAME_SIZE;
     const char* pcm_device = "default";
     snd_pcm_format_t format;
     int err;
@@ -75,6 +75,7 @@ bool WavePlayer::init(uint32_t sampels_per_sec,
         return false;
     }
     _sampels_per_ms = sampels_per_sec / 1000;
+    _frame_size = frame_size;
 
     if ((err = snd_pcm_open(&_pcm, pcm_device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) {
         LOG_ERROR("cannot open audio playback device %s %s", pcm_device, snd_strerror(err));
@@ -183,14 +184,14 @@ bool WavePlayer::init(uint32_t sampels_per_sec,
 
 bool WavePlayer::write(uint8_t* frame)
 {
-    snd_pcm_sframes_t ret = snd_pcm_writei(_pcm, frame, WavePlaybackAbstract::FRAME_SIZE);
+    snd_pcm_sframes_t ret = snd_pcm_writei(_pcm, frame, _frame_size);
     if (ret < 0) {
         if (ret == -EAGAIN) {
             return false;
         }
         DBG(0, "err %s", snd_strerror(-ret));
         if (snd_pcm_recover(_pcm, ret, 1) == 0) {
-            snd_pcm_writei(_pcm, frame, WavePlaybackAbstract::FRAME_SIZE);
+            snd_pcm_writei(_pcm, frame, _frame_size);
         }
     }
     return true;
diff --git a/client/x11/playback.h b/client/x11/playback.h
index 27ef9ed..383f02d 100644
--- a/client/x11/playback.h
+++ b/client/x11/playback.h
@@ -25,7 +25,7 @@
 
 class WavePlayer: public WavePlaybackAbstract {
 public:
-    WavePlayer(uint32_t sampels_per_sec, uint32_t bits_per_sample, uint32_t channels);
+    WavePlayer(uint32_t sampels_per_sec, uint32_t bits_per_sample, uint32_t channels, uint32_t frame_size);
     virtual ~WavePlayer();
 
     virtual bool write(uint8_t* frame);
@@ -34,7 +34,7 @@ public:
     virtual uint32_t get_delay_ms();
 
 private:
-    bool init(uint32_t sampels_per_sec, uint32_t bits_per_sample, uint32_t channel);
+    bool init(uint32_t sampels_per_sec, uint32_t bits_per_sample, uint32_t channel, uint32_t frame_size);
     void cleanup();
 
 private:
@@ -42,6 +42,7 @@ private:
     snd_pcm_hw_params_t* _hw_params;
     snd_pcm_sw_params_t* _sw_params;
     uint32_t _sampels_per_ms;
+    uint32_t _frame_size;
 };
 
 #endif
diff --git a/client/x11/record.cpp b/client/x11/record.cpp
index 535a8c9..32c0af7 100644
--- a/client/x11/record.cpp
+++ b/client/x11/record.cpp
@@ -48,18 +48,19 @@ void WaveRecorder::EventTrigger::on_event()
 WaveRecorder::WaveRecorder(Platform::RecordClient& client,
                            uint32_t sampels_per_sec,
                            uint32_t bits_per_sample,
-                           uint32_t channels)
+                           uint32_t channels,
+                           uint32_t frame_size)
     : _client (client)
     , _pcm (NULL)
     , _hw_params (NULL)
     , _sw_params (NULL)
     , _sample_bytes (bits_per_sample * channels / 8)
-    , _frame (new uint8_t[_sample_bytes * WaveRecordAbstract::FRAME_SIZE])
+    , _frame (new uint8_t[_sample_bytes * frame_size])
     , _frame_pos (_frame)
-    , _frame_end (_frame + _sample_bytes * WaveRecordAbstract::FRAME_SIZE)
+    , _frame_end (_frame + _sample_bytes * frame_size)
     , _event_trigger (NULL)
 {
-    if (!init(sampels_per_sec, bits_per_sample, channels)) {
+    if (!init(sampels_per_sec, bits_per_sample, channels, frame_size)) {
         cleanup();
         THROW("failed");
     }
@@ -93,13 +94,15 @@ void WaveRecorder::cleanup()
 
 bool WaveRecorder::init(uint32_t sampels_per_sec,
                         uint32_t bits_per_sample,
-                        uint32_t channels)
+                        uint32_t channels,
+                        uint32_t frame_size)
 {
-    const int frame_size = WaveRecordAbstract::FRAME_SIZE;
     const char* pcm_device = "default";
     snd_pcm_format_t format;
     int err;
 
+    _frame_size = frame_size;
+
     switch (bits_per_sample) {
     case 8:
         format = SND_PCM_FORMAT_S8;
diff --git a/client/x11/record.h b/client/x11/record.h
index 9141096..9470791 100644
--- a/client/x11/record.h
+++ b/client/x11/record.h
@@ -29,7 +29,8 @@ public:
     WaveRecorder(Platform::RecordClient& client,
                  uint32_t sampels_per_sec,
                  uint32_t bits_per_sample,
-                 uint32_t channels);
+                 uint32_t channels,
+                 uint32_t frame_size);
     virtual ~WaveRecorder();
 
     virtual void start();
@@ -39,7 +40,8 @@ public:
 private:
     bool init(uint32_t sampels_per_sec,
               uint32_t bits_per_sample,
-              uint32_t channels);
+              uint32_t channels,
+              uint32_t frame_size);
     void cleanup();
     void on_event();
 
@@ -49,6 +51,7 @@ private:
     snd_pcm_hw_params_t* _hw_params;
     snd_pcm_sw_params_t* _sw_params;
     uint32_t _sample_bytes;
+    uint32_t _frame_size;
     uint8_t* _frame;
     uint8_t* _frame_pos;
     uint8_t* _frame_end;
diff --git a/server/snd_worker.c b/server/snd_worker.c
index f40fd65..7d52ded 100644
--- a/server/snd_worker.c
+++ b/server/snd_worker.c
@@ -156,12 +156,14 @@ struct SpicePlaybackState {
     struct SndWorker worker;
     SpicePlaybackInstance *sin;
     SpiceVolumeState volume;
+    uint32_t frequency;
 };
 
 struct SpiceRecordState {
     struct SndWorker worker;
     SpiceRecordInstance *sin;
     SpiceVolumeState volume;
+    uint32_t frequency;
 };
 
 typedef struct RecordChannel {
@@ -370,14 +372,27 @@ static int snd_record_handle_message(SndChannel *channel, size_t size, uint32_t
         return snd_record_handle_write((RecordChannel *)channel, size, message);
     case SPICE_MSGC_RECORD_MODE: {
         SpiceMsgcRecordMode *mode = (SpiceMsgcRecordMode *)message;
-        record_channel->mode = mode->mode;
+        SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker);
         record_channel->mode_time = mode->time;
-        if (record_channel->mode != SPICE_AUDIO_DATA_MODE_RAW &&
-             ! snd_codec_is_capable(record_channel->mode)) {
-            spice_printerr("unsupported mode %d", record_channel->mode);
+        if (mode->mode != SPICE_AUDIO_DATA_MODE_RAW) {
+            if (snd_codec_is_capable(mode->mode, st->frequency)) {
+                if (snd_codec_create(&record_channel->codec, mode->mode, st->frequency, SND_CODEC_DECODE) == SND_CODEC_OK) {
+                    record_channel->mode = mode->mode;
+                } else {
+                    spice_printerr("create decoder failed");
+                    return FALSE;
+                }
+            }
+            else {
+                spice_printerr("unsupported mode %d", record_channel->mode);
+                return FALSE;
+            }
         }
+        else
+            record_channel->mode = mode->mode;
         break;
     }
+
     case SPICE_MSGC_RECORD_START_MARK: {
         SpiceMsgcRecordStartMark *mark = (SpiceMsgcRecordStartMark *)message;
         record_channel->start_time = mark->time;
@@ -615,6 +630,7 @@ static int snd_playback_send_latency(PlaybackChannel *playback_channel)
 static int snd_playback_send_start(PlaybackChannel *playback_channel)
 {
     SndChannel *channel = (SndChannel *)playback_channel;
+    SpicePlaybackState *st = SPICE_CONTAINEROF(channel->worker, SpicePlaybackState, worker);
     SpiceMsgPlaybackStart start;
 
     if (!snd_reset_send_data(channel, SPICE_MSG_PLAYBACK_START)) {
@@ -622,7 +638,7 @@ static int snd_playback_send_start(PlaybackChannel *playback_channel)
     }
 
     start.channels = SPICE_INTERFACE_PLAYBACK_CHAN;
-    start.frequency = SPICE_INTERFACE_PLAYBACK_FREQ;
+    start.frequency = st->frequency;
     spice_assert(SPICE_INTERFACE_PLAYBACK_FMT == SPICE_INTERFACE_AUDIO_FMT_S16);
     start.format = SPICE_AUDIO_FMT_S16;
     start.time = reds_get_mm_time();
@@ -656,6 +672,7 @@ static int snd_playback_send_ctl(PlaybackChannel *playback_channel)
 static int snd_record_send_start(RecordChannel *record_channel)
 {
     SndChannel *channel = (SndChannel *)record_channel;
+    SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker);
     SpiceMsgRecordStart start;
 
     if (!snd_reset_send_data(channel, SPICE_MSG_RECORD_START)) {
@@ -663,7 +680,7 @@ static int snd_record_send_start(RecordChannel *record_channel)
     }
 
     start.channels = SPICE_INTERFACE_RECORD_CHAN;
-    start.frequency = SPICE_INTERFACE_RECORD_FREQ;
+    start.frequency = st->frequency;
     spice_assert(SPICE_INTERFACE_RECORD_FMT == SPICE_INTERFACE_AUDIO_FMT_S16);
     start.format = SPICE_AUDIO_FMT_S16;
     spice_marshall_msg_record_start(channel->send_data.marshaller, &start);
@@ -745,11 +762,13 @@ static int snd_playback_send_write(PlaybackChannel *playback_channel)
 
     if (playback_channel->mode == SPICE_AUDIO_DATA_MODE_RAW) {
         spice_marshaller_add_ref(channel->send_data.marshaller,
-                                 (uint8_t *)frame->samples, sizeof(frame->samples));
+                                 (uint8_t *)frame->samples,
+                                 snd_codec_frame_size(playback_channel->codec) * sizeof(frame->samples[0]));
     }
     else {
         int n = sizeof(playback_channel->encode_buf);
-        if (snd_codec_encode(playback_channel->codec, (uint8_t *) frame->samples, sizeof(frame->samples),
+        if (snd_codec_encode(playback_channel->codec, (uint8_t *) frame->samples,
+                                    snd_codec_frame_size(playback_channel->codec) * sizeof(frame->samples[0]),
                                     playback_channel->encode_buf, &n) != SND_CODEC_OK) {
             spice_printerr("encode failed");
             snd_disconnect_channel(channel);
@@ -1126,12 +1145,15 @@ void snd_set_playback_latency(RedClient *client, uint32_t latency)
     }
 }
 
-static int snd_desired_audio_mode(int client_can_celt)
+static int snd_desired_audio_mode(int frequency, int client_can_celt, int client_can_opus)
 {
     if (! playback_compression)
         return SPICE_AUDIO_DATA_MODE_RAW;
 
-    if (client_can_celt && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1))
+    if (client_can_opus && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, frequency))
+        return SPICE_AUDIO_DATA_MODE_OPUS;
+
+    if (client_can_celt && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, frequency))
         return SPICE_AUDIO_DATA_MODE_CELT_0_5_1;
 
     return SPICE_AUDIO_DATA_MODE_RAW;
@@ -1199,10 +1221,12 @@ static void snd_set_playback_peer(RedChannel *channel, RedClient *client, RedsSt
 
     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);
+    int client_can_opus = red_channel_client_test_remote_cap(playback_channel->base.channel_client,
+                                          SPICE_PLAYBACK_CAP_OPUS);
+    int desired_mode = snd_desired_audio_mode(st->frequency, client_can_celt, client_can_opus);
     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, SPICE_INTERFACE_PLAYBACK_FREQ, SND_CODEC_ENCODE) == SND_CODEC_OK) {
+        if (snd_codec_create(&playback_channel->codec, desired_mode, st->frequency, SND_CODEC_ENCODE) == SND_CODEC_OK) {
             playback_channel->mode = desired_mode;
         } else {
             spice_printerr("create encoder failed");
@@ -1340,6 +1364,54 @@ SPICE_GNUC_VISIBLE uint32_t spice_server_record_get_samples(SpiceRecordInstance
     return len;
 }
 
+SPICE_GNUC_VISIBLE uint32_t spice_server_get_best_playback_rate(SpicePlaybackInstance *sin)
+{
+    int client_can_opus = TRUE;
+    if (sin && sin->st->worker.connection) {
+        SndChannel *channel = sin->st->worker.connection;
+        PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
+        client_can_opus = red_channel_client_test_remote_cap(playback_channel->base.channel_client,
+                                          SPICE_PLAYBACK_CAP_OPUS);
+    }
+
+    if (client_can_opus && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, SND_CODEC_ANY_FREQUENCY))
+        return SND_CODEC_OPUS_PLAYBACK_FREQ;
+
+    return SND_CODEC_CELT_PLAYBACK_FREQ;
+}
+
+SPICE_GNUC_VISIBLE void spice_server_set_playback_rate(SpicePlaybackInstance *sin, uint32_t frequency)
+{
+    RedChannel *channel = sin->st->worker.base_channel;
+    sin->st->frequency = frequency;
+    if (channel && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, frequency))
+        red_channel_set_cap(channel, SPICE_PLAYBACK_CAP_OPUS);
+}
+
+SPICE_GNUC_VISIBLE uint32_t spice_server_get_best_record_rate(SpiceRecordInstance *sin)
+{
+    int client_can_opus = TRUE;
+    if (sin && sin->st->worker.connection) {
+        SndChannel *channel = sin->st->worker.connection;
+        RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
+        client_can_opus = red_channel_client_test_remote_cap(record_channel->base.channel_client,
+                                          SPICE_RECORD_CAP_OPUS);
+    }
+
+    if (client_can_opus && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, SND_CODEC_ANY_FREQUENCY))
+        return SND_CODEC_OPUS_PLAYBACK_FREQ;
+
+    return SND_CODEC_CELT_PLAYBACK_FREQ;
+}
+
+SPICE_GNUC_VISIBLE void spice_server_set_record_rate(SpiceRecordInstance *sin, uint32_t frequency)
+{
+    RedChannel *channel = sin->st->worker.base_channel;
+    sin->st->frequency = frequency;
+    if (channel && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, frequency))
+        red_channel_set_cap(channel, SPICE_RECORD_CAP_OPUS);
+}
+
 static void on_new_record_channel(SndWorker *worker)
 {
     RecordChannel *record_channel = (RecordChannel *)worker->connection;
@@ -1386,17 +1458,7 @@ static void snd_set_record_peer(RedChannel *channel, RedClient *client, RedsStre
         return;
     }
 
-    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, SPICE_INTERFACE_RECORD_FREQ, SND_CODEC_DECODE) == SND_CODEC_OK) {
-            record_channel->mode = desired_mode;
-        } else {
-            spice_printerr("create decoder failed");
-        }
-    }
 
     worker->connection = &record_channel->base;
 
@@ -1451,6 +1513,7 @@ void snd_attach_playback(SpicePlaybackInstance *sin)
     sin->st = spice_new0(SpicePlaybackState, 1);
     sin->st->sin = sin;
     playback_worker = &sin->st->worker;
+    sin->st->frequency = SND_CODEC_CELT_PLAYBACK_FREQ; /* Default to the legacy rate */
 
     // TODO: Make RedChannel base of worker? instead of assigning it to channel->data
     channel = red_channel_create_dummy(sizeof(RedChannel), SPICE_CHANNEL_PLAYBACK, 0);
@@ -1462,7 +1525,7 @@ void snd_attach_playback(SpicePlaybackInstance *sin)
     red_channel_register_client_cbs(channel, &client_cbs);
     red_channel_set_data(channel, playback_worker);
 
-    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_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(channel, SPICE_PLAYBACK_CAP_CELT_0_5_1);
 
     red_channel_set_cap(channel, SPICE_PLAYBACK_CAP_VOLUME);
@@ -1481,6 +1544,7 @@ void snd_attach_record(SpiceRecordInstance *sin)
     sin->st = spice_new0(SpiceRecordState, 1);
     sin->st->sin = sin;
     record_worker = &sin->st->worker;
+    sin->st->frequency = SND_CODEC_CELT_PLAYBACK_FREQ; /* Default to the legacy rate */
 
     // TODO: Make RedChannel base of worker? instead of assigning it to channel->data
     channel = red_channel_create_dummy(sizeof(RedChannel), SPICE_CHANNEL_RECORD, 0);
@@ -1491,7 +1555,7 @@ 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);
-    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_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(channel, SPICE_RECORD_CAP_CELT_0_5_1);
     red_channel_set_cap(channel, SPICE_RECORD_CAP_VOLUME);
 
@@ -1544,9 +1608,12 @@ void snd_set_playback_compression(int on)
     for (; now; now = now->next) {
         if (now->base_channel->type == SPICE_CHANNEL_PLAYBACK && now->connection) {
             PlaybackChannel* playback = (PlaybackChannel*)now->connection;
-            int desired_mode = snd_desired_audio_mode(
-                    red_channel_client_test_remote_cap(now->connection->channel_client, SPICE_PLAYBACK_CAP_CELT_0_5_1)
-                    );
+            SpicePlaybackState *st = SPICE_CONTAINEROF(now, SpicePlaybackState, worker);
+            int client_can_celt = red_channel_client_test_remote_cap(playback->base.channel_client,
+                                    SPICE_PLAYBACK_CAP_CELT_0_5_1);
+            int client_can_opus = red_channel_client_test_remote_cap(playback->base.channel_client,
+                                    SPICE_PLAYBACK_CAP_OPUS);
+            int desired_mode = snd_desired_audio_mode(st->frequency, client_can_opus, client_can_celt);
             if (playback->mode != desired_mode) {
                 playback->mode = desired_mode;
                 snd_set_command(now->connection, SND_PLAYBACK_MODE_MASK);
diff --git a/server/spice-server.syms b/server/spice-server.syms
index 4f2dc37..2193811 100644
--- a/server/spice-server.syms
+++ b/server/spice-server.syms
@@ -145,3 +145,11 @@ SPICE_SERVER_0.12.4 {
 global:
     spice_server_set_agent_file_xfer;
 } SPICE_SERVER_0.12.3;
+
+SPICE_SERVER_0.12.5 {
+global:
+    spice_server_get_best_playback_rate;
+    spice_server_set_playback_rate;
+    spice_server_get_best_record_rate;
+    spice_server_set_record_rate;
+} SPICE_SERVER_0.12.4;
diff --git a/server/spice.h b/server/spice.h
index b645112..c648a1d 100644
--- a/server/spice.h
+++ b/server/spice.h
@@ -24,7 +24,7 @@
 #include <spice/vd_agent.h>
 #include <spice/macros.h>
 
-#define SPICE_SERVER_VERSION 0x000c04 /* release 0.12.4 */
+#define SPICE_SERVER_VERSION 0x000c05 /* release 0.12.5 */
 
 #ifdef SPICE_SERVER_INTERNAL
 #undef SPICE_GNUC_DEPRECATED
@@ -333,7 +333,7 @@ struct SpiceTabletInstance {
 
 #define SPICE_INTERFACE_PLAYBACK "playback"
 #define SPICE_INTERFACE_PLAYBACK_MAJOR 1
-#define SPICE_INTERFACE_PLAYBACK_MINOR 2
+#define SPICE_INTERFACE_PLAYBACK_MINOR 3
 typedef struct SpicePlaybackInterface SpicePlaybackInterface;
 typedef struct SpicePlaybackInstance SpicePlaybackInstance;
 typedef struct SpicePlaybackState SpicePlaybackState;
@@ -342,7 +342,7 @@ enum {
     SPICE_INTERFACE_AUDIO_FMT_S16 = 1,
 };
 
-#define SPICE_INTERFACE_PLAYBACK_FREQ  44100
+#define SPICE_INTERFACE_PLAYBACK_FREQ  48000
 #define SPICE_INTERFACE_PLAYBACK_CHAN  2
 #define SPICE_INTERFACE_PLAYBACK_FMT   SPICE_INTERFACE_AUDIO_FMT_S16
 
@@ -367,12 +367,12 @@ void spice_server_playback_set_mute(SpicePlaybackInstance *sin, uint8_t mute);
 
 #define SPICE_INTERFACE_RECORD "record"
 #define SPICE_INTERFACE_RECORD_MAJOR 2
-#define SPICE_INTERFACE_RECORD_MINOR 2
+#define SPICE_INTERFACE_RECORD_MINOR 3
 typedef struct SpiceRecordInterface SpiceRecordInterface;
 typedef struct SpiceRecordInstance SpiceRecordInstance;
 typedef struct SpiceRecordState SpiceRecordState;
 
-#define SPICE_INTERFACE_RECORD_FREQ  44100
+#define SPICE_INTERFACE_RECORD_FREQ  48000
 #define SPICE_INTERFACE_RECORD_CHAN  2
 #define SPICE_INTERFACE_RECORD_FMT   SPICE_INTERFACE_AUDIO_FMT_S16
 
@@ -393,6 +393,11 @@ void spice_server_record_set_volume(SpiceRecordInstance *sin,
                                     uint8_t nchannels, uint16_t *volume);
 void spice_server_record_set_mute(SpiceRecordInstance *sin, uint8_t mute);
 
+uint32_t spice_server_get_best_playback_rate(SpicePlaybackInstance *sin);
+void     spice_server_set_playback_rate(SpicePlaybackInstance *sin, uint32_t frequency);
+uint32_t spice_server_get_best_record_rate(SpiceRecordInstance *sin);
+void     spice_server_set_record_rate(SpiceRecordInstance *sin, uint32_t frequency);
+
 /* char device interfaces */
 
 #define SPICE_INTERFACE_CHAR_DEVICE "char_device"
diff --git a/spice-common b/spice-common
index c108e4e..57ce430 160000
--- a/spice-common
+++ b/spice-common
@@ -1 +1 @@
-Subproject commit c108e4ee8cb33218d9a64e25de9e79b63d23a8e7
+Subproject commit 57ce430ccd66bd1ca2447c14503234cfb88e2365
commit 4c7c0ef3a70001b1bc9011ef824d3c6fcccd9ca0
Author: Jeremy White <jwhite at codeweavers.com>
Date:   Sat Nov 30 09:14:44 2013 -0600

    Revise the spice client and server to use the new snd_codec functions in spice-common.
    
    This makes celt optional, and paves the way to readily add additional codecs.
    
    Signed-off-by: Jeremy White <jwhite at codeweavers.com>

diff --git a/README b/README
index b344066..dea4344 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..d7b81e7 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..173c94a 100644
--- a/client/playback_channel.cpp
+++ b/client/playback_channel.cpp
@@ -151,8 +151,7 @@ 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)
+    , _codec(NULL)
     , _playing (false)
 {
 #ifdef WAVE_CAPTURE
@@ -169,7 +168,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 +182,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,22 +206,23 @@ 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) {
+    SpiceMsgPlaybackMode* playback_mode = (SpiceMsgPlaybackMode*)message->data();
+    if (playback_mode->mode != SPICE_AUDIO_DATA_MODE_RAW
+        && !snd_codec_is_capable(playback_mode->mode) ) {
         THROW("invalid mode");
     }
 
-    _mode = playbacke_mode->mode;
+    _mode = playback_mode->mode;
     if (_playing) {
         set_data_handler();
         return;
@@ -265,15 +258,14 @@ 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");
         }
+        if (start->channels != 2) {
+            THROW("unexpected number of channels");
+        }
         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 +276,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, start->frequency, SND_CODEC_DECODE) != 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 +324,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..2870f62 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,7 @@ 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)
+    , _codec(NULL)
 {
     for (int i = 0; i < NUM_SAMPLES_MESSAGES; i++) {
         _messages.push_front(new RecordSamplesMessage(*this));
@@ -90,7 +87,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 +112,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,12 +143,15 @@ 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) {
         THROW("unexpected format");
     }
+    if (start->channels != 2) {
+        THROW("unexpected number of channels");
+    }
 
     int bits_per_sample = 16;
     try {
@@ -159,17 +163,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, start->frequency, SND_CODEC_ENCODE) != 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 +182,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 +193,6 @@ void RecordChannel::handle_stop(RedPeer::InMessage* message)
     if (!_wave_recorder) {
         return;
     }
-    ASSERT(_celt_mode && _celt_encoder);
     clear();
 }
 
@@ -242,10 +234,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 +242,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 db4fd89..c897368 100644
--- a/configure.ac
+++ b/configure.ac
@@ -230,11 +230,13 @@ 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"
+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, SPICE_REQUIRES+=" celt051 >= 0.5.1.1")
+fi
 
 if test ! -e client/generated_marshallers.cpp; then
 AC_MSG_CHECKING([for pyparsing python module])
diff --git a/server/Makefile.am b/server/Makefile.am
index 6dcf2ee..13c6223 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -4,7 +4,6 @@ SUBDIRS = . tests
 AM_CPPFLAGS =					\
 	-DSPICE_SERVER_INTERNAL			\
 	-DRED_STATISTICS			\
-	$(CELT051_CFLAGS)			\
 	$(COMMON_CFLAGS)			\
 	$(GLIB2_CFLAGS)				\
 	$(PIXMAN_CFLAGS)			\
@@ -33,7 +32,6 @@ endif
 libspice_server_la_LIBADD =						\
 	$(top_builddir)/spice-common/common/libspice-common.la		\
 	$(top_builddir)/spice-common/common/libspice-common-server.la	\
-	$(CELT051_LIBS)							\
 	$(GL_LIBS)							\
 	$(GLIB2_LIBS)							\
 	$(JPEG_LIBS)							\
diff --git a/server/snd_worker.c b/server/snd_worker.c
index 9156bf5..f40fd65 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 SND_RECEIVE_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 SND_RECEIVE_BUF_SIZE     (16 * 1024 * 2)
 #define RECORD_SAMPLES_SIZE (SND_RECEIVE_BUF_SIZE >> 2)
 
 enum PlaybackCommand {
@@ -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 = TRUE;
 
 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)
+        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,30 @@ 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, SPICE_INTERFACE_PLAYBACK_FREQ, SND_CODEC_ENCODE) == 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 +1358,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 +1368,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 +1383,28 @@ 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, SPICE_INTERFACE_RECORD_FREQ, SND_CODEC_DECODE) == 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 +1461,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 +1491,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;
@@ -1572,18 +1539,16 @@ void snd_set_playback_compression(int on)
 {
     SndWorker *now = workers;
 
-    playback_compression = on ? SPICE_AUDIO_DATA_MODE_CELT_0_5_1 : SPICE_AUDIO_DATA_MODE_RAW;
+    playback_compression = !!on;
+
     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;
+            int desired_mode = snd_desired_audio_mode(
+                    red_channel_client_test_remote_cap(now->connection->channel_client, SPICE_PLAYBACK_CAP_CELT_0_5_1)
+                    );
+            if (playback->mode != desired_mode) {
+                playback->mode = desired_mode;
                 snd_set_command(now->connection, SND_PLAYBACK_MODE_MASK);
             }
         }
@@ -1592,5 +1557,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/spice-common b/spice-common
index 7e8ba10..c108e4e 160000
--- a/spice-common
+++ b/spice-common
@@ -1 +1 @@
-Subproject commit 7e8ba10779a3fb11d587e8a59fe389acd2412dd0
+Subproject commit c108e4ee8cb33218d9a64e25de9e79b63d23a8e7


More information about the Spice-commits mailing list