[Spice-devel] [spice opus support 3/6 (take 4b)] Add support for the Opus codec

Christophe Fergeau cfergeau at redhat.com
Tue Dec 10 10:46:11 PST 2013


On Sat, Nov 30, 2013 at 09:19:21AM -0600, Jeremy White wrote:
> 
> Signed-off-by: Jeremy White <jwhite at codeweavers.com>
> ---
>  client/audio_channels.h     |    3 +-
>  client/audio_devices.h      |    8 ---
>  client/platform.h           |    6 ++-
>  client/playback_channel.cpp |   23 ++++----
>  client/record_channel.cpp   |   41 ++++++++++-----
>  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 ++-
>  server/snd_worker.c         |  121 +++++++++++++++++++++++++++++++++----------
>  server/spice-server.syms    |    8 +++
>  server/spice.h              |   15 ++++--
>  13 files changed, 188 insertions(+), 87 deletions(-)
> 
> 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..240f0dd 100644
> --- a/server/snd_worker.c
> +++ b/server/snd_worker.c
> +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_PLAYBACK_CAP_OPUS);

small c&p issue here, this should be SPICE_RECORD_CAP_OPUS. I'll squash
this in, and finally push the whole series! Thanks a lot for all
the hardwork!

Christophe
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.freedesktop.org/archives/spice-devel/attachments/20131210/5d03cc47/attachment-0001.pgp>


More information about the Spice-devel mailing list