[Spice-devel] [opus 3/spice] Add support for the Opus codec

Jeremy White jwhite at codeweavers.com
Tue Oct 15 16:29:44 CEST 2013


Signed-off-by: Jeremy White <jwhite at codeweavers.com>
---
 client/Makefile.am          |    2 +
 client/audio_devices.h      |    8 ---
 client/platform.h           |    6 ++-
 client/playback_channel.cpp |   17 ++++---
 client/record_channel.cpp   |   24 +++++----
 client/x11/platform.cpp     |   10 ++--
 client/x11/playback.cpp     |   13 ++---
 client/x11/playback.h       |    5 +-
 client/x11/record.cpp       |   15 +++---
 client/x11/record.h         |    7 ++-
 configure.ac                |   12 +++++
 server/Makefile.am          |    2 +
 server/snd_worker.c         |  118 ++++++++++++++++++++++++++++++++++---------
 server/spice-server.syms    |    9 ++++
 server/spice.h              |   15 ++++--
 15 files changed, 187 insertions(+), 76 deletions(-)

diff --git a/client/Makefile.am b/client/Makefile.am
index ebc6ce0..867ba3b 100644
--- a/client/Makefile.am
+++ b/client/Makefile.am
@@ -204,6 +204,7 @@ AM_CPPFLAGS = \
 	$(CEGUI_CFLAGS)					\
 	$(CEGUI06_CFLAGS)				\
 	$(CELT051_CFLAGS)				\
+	$(OPUS_CFLAGS)				        \
 	$(GL_CFLAGS)					\
 	$(MISC_X_CFLAGS)				\
 	$(PIXMAN_CFLAGS)				\
@@ -227,6 +228,7 @@ spicec_LDADD =								\
 	$(CEGUI_LIBS)							\
 	$(CEGUI06_LIBS)							\
 	$(CELT051_LIBS)							\
+	$(OPUS_LIBS)							\
 	$(GL_LIBS)							\
 	$(JPEG_LIBS)							\
 	$(MISC_X_LIBS)							\
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 922c464..0793bd0 100644
--- a/client/playback_channel.cpp
+++ b/client/playback_channel.cpp
@@ -169,6 +169,8 @@ PlaybackChannel::PlaybackChannel(RedClient& client, uint32_t id)
 
     if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1))
         set_capability(SPICE_PLAYBACK_CAP_CELT_0_5_1);
+    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS))
+        set_capability(SPICE_PLAYBACK_CAP_OPUS);
 }
 
 void PlaybackChannel::clear()
@@ -262,9 +264,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, FALSE, TRUE) != 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
@@ -272,12 +281,6 @@ void PlaybackChannel::handle_start(RedPeer::InMessage* message)
             return;
         }
 
-        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);
-        }
-
         _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 5c878f2..cfa2771 100644
--- a/client/record_channel.cpp
+++ b/client/record_channel.cpp
@@ -88,6 +88,8 @@ RecordChannel::RecordChannel(RedClient& client, uint32_t id)
 
     if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1))
         set_capability(SPICE_RECORD_CAP_CELT_0_5_1);
+    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS))
+        set_capability(SPICE_RECORD_CAP_OPUS);
 }
 
 RecordChannel::~RecordChannel(void)
@@ -111,7 +113,10 @@ void RecordChannel::on_connect()
     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) &&
+      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) &&
       test_capability(SPICE_RECORD_CAP_CELT_0_5_1))
           _mode = SPICE_AUDIO_DATA_MODE_CELT_0_5_1;
       else
@@ -149,22 +154,24 @@ void RecordChannel::handle_start(RedPeer::InMessage* message)
         THROW("unexpected format");
     }
 
+    int frame_size = SND_CODEC_MAX_FRAME_SIZE;
+    if (_mode != SPICE_AUDIO_DATA_MODE_RAW) {
+        if (snd_codec_create(&codec, _mode, start->frequency, TRUE, FALSE) != 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, 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();
@@ -233,7 +240,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/configure.ac b/configure.ac
index 4519a84..a7437f3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -263,6 +263,15 @@ 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]))
 
+PKG_CHECK_MODULES([OPUS], [opus >= 0.9.14], have_opus=yes, have_opus=no)
+
+AM_CONDITIONAL([HAVE_OPUS], [test "x$have_opus" = "xyes"])
+if test "x$have_opus" = "xyes" ; then
+  AC_DEFINE([HAVE_OPUS], [1], [Define if we have OPUS])
+  SPICE_REQUIRES+=" opus >= 0.9.14"
+  opus_version=`pkg-config --modversion opus`
+fi
+
 if test ! -e client/generated_marshallers.cpp; then
 AC_MSG_CHECKING([for pyparsing python module])
 echo "import pyparsing" | ${PYTHON} - >/dev/null 2>&1
@@ -543,6 +552,9 @@ echo "
 
         GUI:                      ${enable_gui}
 " ; fi ; echo "\
+
+        Opus:                     ${have_opus} (${opus_version})
+
         Support tunneling:        ${enable_tunnel}
 
         Smartcard:                ${enable_smartcard}
diff --git a/server/Makefile.am b/server/Makefile.am
index 8cbd87b..09a2d40 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -4,6 +4,7 @@ SUBDIRS = . tests
 AM_CPPFLAGS =					\
 	-DSPICE_SERVER_INTERNAL			\
 	-DRED_STATISTICS			\
+	$(OPUS_CFLAGS)				\
 	$(CELT051_CFLAGS)			\
 	$(COMMON_CFLAGS)			\
 	$(GLIB2_CFLAGS)				\
@@ -36,6 +37,7 @@ libspice_server_la_LIBADD =						\
 	$(GLIB2_LIBS)							\
 	$(JPEG_LIBS)							\
 	$(LIBRT)							\
+	$(OPUS_LIBS)							\
 	$(PIXMAN_LIBS)							\
 	$(SASL_LIBS)							\
 	$(SLIRP_LIBS)							\
diff --git a/server/snd_worker.c b/server/snd_worker.c
index a1e452b..48d5b69 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;
-        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);
+        SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker);
+        if (mode->mode != SPICE_AUDIO_DATA_MODE_RAW)
+        {
+            if (snd_codec_is_capable(mode->mode)) {
+                record_channel->mode_time = mode->time;
+                if (snd_codec_create(&record_channel->codec, mode->mode, st->frequency, FALSE, TRUE) == SND_CODEC_OK) {
+                    record_channel->mode = mode->mode;
+                } else {
+                    spice_printerr("create decoder failed");
+                }
+            }
+            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,11 +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 == SPICE_PLAYBACK_COMPRESSION_NONE)
         return SPICE_AUDIO_DATA_MODE_RAW;
 
+    if (client_can_opus && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS))
+        if (frequency == 48000 || frequency == 24000 || frequency == 16000 || frequency == 12000 || frequency == 8000)
+            return SPICE_AUDIO_DATA_MODE_OPUS;
+
     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;
 
@@ -1199,11 +1222,13 @@ 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, TRUE, FALSE) == SND_CODEC_OK) {
+        if (snd_codec_create(&playback_channel->codec, desired_mode, st->frequency, TRUE, FALSE) == SND_CODEC_OK) {
             playback_channel->mode = desired_mode;
         } else {
             spice_printerr("create encoder failed");
@@ -1341,6 +1366,50 @@ 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))
+        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)
+{
+    sin->st->frequency = frequency;
+}
+
+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))
+        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)
+{
+    sin->st->frequency = frequency;
+}
+
 static void on_new_record_channel(SndWorker *worker)
 {
     RecordChannel *record_channel = (RecordChannel *)worker->connection;
@@ -1387,18 +1456,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, FALSE, TRUE) == SND_CODEC_OK) {
-            record_channel->mode = desired_mode;
-        } else {
-            spice_printerr("create decoder failed");
-        }
-    }
 
     worker->connection = &record_channel->base;
 
@@ -1453,6 +1511,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);
@@ -1466,6 +1525,8 @@ void snd_attach_playback(SpicePlaybackInstance *sin)
 
     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);
+    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS))
+        red_channel_set_cap(channel, SPICE_PLAYBACK_CAP_OPUS);
 
     red_channel_set_cap(channel, SPICE_PLAYBACK_CAP_VOLUME);
 
@@ -1483,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);
@@ -1495,6 +1557,8 @@ void snd_attach_record(SpiceRecordInstance *sin)
     red_channel_set_data(channel, record_worker);
     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);
+    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS))
+        red_channel_set_cap(channel, SPICE_RECORD_CAP_OPUS);
     red_channel_set_cap(channel, SPICE_RECORD_CAP_VOLUME);
 
     record_worker->base_channel = channel;
@@ -1543,11 +1607,15 @@ void snd_set_playback_compression(int comp)
 
     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) {
             PlaybackChannel* playback = (PlaybackChannel*)now->connection;
+            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..0e8cf2e 100644
--- a/server/spice-server.syms
+++ b/server/spice-server.syms
@@ -145,3 +145,12 @@ 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"
-- 
1.7.10.4




More information about the Spice-devel mailing list