Signed-off-by: Jeremy White <jwhite@xxxxxxxxxxxxxxx> --- 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 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel