[Spice-devel] [spice-common opus support 2/6 (take 4)] Add support for the Opus codec.

Jeremy White jwhite at codeweavers.com
Wed Nov 27 11:32:41 PST 2013


Signed-off-by: Jeremy White <jwhite at codeweavers.com>
---
  common/Makefile.am |    2 +
  common/snd_codec.c |  131 +++++++++++++++++++++++++++++++++++++++++++++++++---
  common/snd_codec.h |   20 ++++++--
  configure.ac       |    4 ++
  spice.proto        |    1 +
  spice1.proto       |    1 +
  6 files changed, 147 insertions(+), 12 deletions(-)

diff --git a/common/Makefile.am b/common/Makefile.am
index c79f596..73703fc 100644
--- a/common/Makefile.am
+++ b/common/Makefile.am
@@ -84,6 +84,7 @@ AM_CPPFLAGS =				\
  	$(GL_CFLAGS)			\
  	$(PIXMAN_CFLAGS)		\
  	$(CELT051_CFLAGS)		\
+	$(OPUS_CFLAGS)		        \
  	$(PROTOCOL_CFLAGS)		\
  	$(SMARTCARD_CFLAGS)		\
  	$(VISIBILITY_HIDDEN_CFLAGS)	\
@@ -92,6 +93,7 @@ AM_CPPFLAGS =				\
  	$(NULL)
  
  libspice_common_la_LIBADD =				\
+	$(OPUS_LIBS)		                        \
  	$(CELT051_LIBS)
  
  MARSHALLERS_DEPS =					\
diff --git a/common/snd_codec.c b/common/snd_codec.c
index 6dcadb7..e4809b4 100644
--- a/common/snd_codec.c
+++ b/common/snd_codec.c
@@ -48,6 +48,11 @@ typedef struct
      CELTEncoder *celt_encoder;
      CELTDecoder *celt_decoder;
  #endif
+
+#if HAVE_OPUS
+    OpusEncoder *opus_encoder;
+    OpusDecoder *opus_decoder;
+#endif
  } SndCodecInternal;
  
  
@@ -77,7 +82,7 @@ static int snd_codec_create_celt051(SndCodecInternal *codec, int purpose)
      int celt_error;
  
      codec->celt_mode = celt051_mode_create(codec->frequency,
-                                           SND_CODEC_CELT_PLAYBACK_CHAN,
+                                           SND_CODEC_PLAYBACK_CHAN,
                                             SND_CODEC_CELT_FRAME_SIZE, &celt_error);
      if (! codec->celt_mode) {
          spice_printerr("create celt mode failed %d", celt_error);
@@ -111,7 +116,7 @@ error:
  static int snd_codec_encode_celt051(SndCodecInternal *codec, uint8_t *in_ptr, int in_size, uint8_t *out_ptr, int *out_size)
  {
      int n;
-    if (in_size != SND_CODEC_CELT_FRAME_SIZE * SND_CODEC_CELT_PLAYBACK_CHAN * 2)
+    if (in_size != SND_CODEC_CELT_FRAME_SIZE * SND_CODEC_PLAYBACK_CHAN * 2)
          return SND_CODEC_INVALID_ENCODE_SIZE;
      n = celt051_encode(codec->celt_encoder, (celt_int16_t *) in_ptr, NULL, out_ptr, *out_size);
      if (n < 0) {
@@ -130,12 +135,87 @@ static int snd_codec_decode_celt051(SndCodecInternal *codec, uint8_t *in_ptr, in
          spice_printerr("celt051_decode failed %d\n", n);
          return SND_CODEC_DECODE_FAILED;
      }
-    *out_size = SND_CODEC_CELT_FRAME_SIZE * SND_CODEC_CELT_PLAYBACK_CHAN * 2 /* 16 fmt */;
+    *out_size = SND_CODEC_CELT_FRAME_SIZE * SND_CODEC_PLAYBACK_CHAN * 2 /* 16 fmt */;
      return SND_CODEC_OK;
  }
  #endif
  
  
+/* Opus support routines */
+#if HAVE_OPUS
+static void snd_codec_destroy_opus(SndCodecInternal *codec)
+{
+    if (codec->opus_decoder) {
+        opus_decoder_destroy(codec->opus_decoder);
+        codec->opus_decoder = NULL;
+    }
+
+    if (codec->opus_encoder) {
+        opus_encoder_destroy(codec->opus_encoder);
+        codec->opus_encoder = NULL;
+    }
+
+}
+
+static int snd_codec_create_opus(SndCodecInternal *codec, int purpose)
+{
+    int opus_error;
+
+    if (purpose & SND_CODEC_ENCODE) {
+        codec->opus_encoder = opus_encoder_create(codec->frequency,
+                                SND_CODEC_PLAYBACK_CHAN,
+                                OPUS_APPLICATION_AUDIO, &opus_error);
+        if (! codec->opus_encoder) {
+            spice_printerr("create opus encoder failed; error %d", opus_error);
+            goto error;
+        }
+    }
+
+    if (purpose & SND_CODEC_DECODE) {
+        codec->opus_decoder = opus_decoder_create(codec->frequency,
+                                SND_CODEC_PLAYBACK_CHAN, &opus_error);
+        if (! codec->opus_decoder) {
+            spice_printerr("create opus decoder failed; error %d", opus_error);
+            goto error;
+        }
+    }
+
+    codec->mode = SPICE_AUDIO_DATA_MODE_OPUS;
+    return SND_CODEC_OK;
+
+error:
+    snd_codec_destroy_opus(codec);
+    return SND_CODEC_UNAVAILABLE;
+}
+
+static int snd_codec_encode_opus(SndCodecInternal *codec, uint8_t *in_ptr, int in_size, uint8_t *out_ptr, int *out_size)
+{
+    int n;
+    if (in_size != SND_CODEC_OPUS_FRAME_SIZE * SND_CODEC_PLAYBACK_CHAN * 2)
+        return SND_CODEC_INVALID_ENCODE_SIZE;
+    n = opus_encode(codec->opus_encoder, (opus_int16 *) in_ptr, SND_CODEC_OPUS_FRAME_SIZE, out_ptr, *out_size);
+    if (n < 0) {
+        spice_printerr("opus_encode failed %d\n", n);
+        return SND_CODEC_ENCODE_FAILED;
+    }
+    *out_size = n;
+    return SND_CODEC_OK;
+}
+
+static int snd_codec_decode_opus(SndCodecInternal *codec, uint8_t *in_ptr, int in_size, uint8_t *out_ptr, int *out_size)
+{
+    int n;
+    n = opus_decode(codec->opus_decoder, in_ptr, in_size, (opus_int16 *) out_ptr,
+                *out_size / SND_CODEC_PLAYBACK_CHAN / 2, 0);
+    if (n < 0) {
+        spice_printerr("opus_decode failed %d\n", n);
+        return SND_CODEC_DECODE_FAILED;
+    }
+    *out_size = n * SND_CODEC_PLAYBACK_CHAN * 2 /* 16 fmt */;
+    return SND_CODEC_OK;
+}
+#endif
+
  
  /*----------------------------------------------------------------------------
  **          PUBLIC INTERFACE
@@ -147,14 +227,23 @@ static int snd_codec_decode_celt051(SndCodecInternal *codec, uint8_t *in_ptr, in
        use the given codec, FALSE otherwise.
     mode must be a SPICE_AUDIO_DATA_MODE_XXX enum from spice/enum.h
   */
-int snd_codec_is_capable(int mode)
+int snd_codec_is_capable(int mode, int frequency)
  {
  #if HAVE_CELT051
      if (mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1)
          return TRUE;
-#else
-    return FALSE;
  #endif
+
+#if HAVE_OPUS
+    if (mode == SPICE_AUDIO_DATA_MODE_OPUS &&
+         (frequency == SND_CODEC_ANY_FREQUENCY ||
+          frequency == 48000 || frequency == 24000 ||
+          frequency == 16000 || frequency == 12000 ||
+          frequency == 8000) )
+        return TRUE;
+#endif
+
+    return FALSE;
  }
  
  /*
@@ -183,6 +272,11 @@ int snd_codec_create(SndCodec *codec, int mode, int frequency, int purpose)
          rc = snd_codec_create_celt051(*c, purpose);
  #endif
  
+#if HAVE_OPUS
+    if (mode == SPICE_AUDIO_DATA_MODE_OPUS)
+        rc = snd_codec_create_opus(*c, purpose);
+#endif
+
      return rc;
  }
  
@@ -200,6 +294,10 @@ void snd_codec_destroy(SndCodec *codec)
      snd_codec_destroy_celt051(*c);
  #endif
  
+#if HAVE_OPUS
+    snd_codec_destroy_opus(*c);
+#endif
+
      free(*c);
      *c = NULL;
  }
@@ -217,6 +315,10 @@ int snd_codec_frame_size(SndCodec codec)
      if (c && c->mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1)
          return SND_CODEC_CELT_FRAME_SIZE;
  #endif
+#if HAVE_OPUS
+    if (c && c->mode == SPICE_AUDIO_DATA_MODE_OPUS)
+        return SND_CODEC_OPUS_FRAME_SIZE;
+#endif
      return SND_CODEC_MAX_FRAME_SIZE;
  }
  
@@ -242,8 +344,18 @@ int snd_codec_encode(SndCodec codec, uint8_t *in_ptr, int in_size, uint8_t *out_
  {
      SndCodecInternal *c = (SndCodecInternal *) codec;
  #if HAVE_CELT051
-    if (c && c->mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1)
+    if (c && c->mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1) {
+        /* The output buffer size in celt determines the compression,
+            and so is essentially mandatory to use a certain value (47) */
+        if (*out_size > SND_CODEC_CELT_COMPRESSED_FRAME_BYTES)
+            *out_size = SND_CODEC_CELT_COMPRESSED_FRAME_BYTES;
          return snd_codec_encode_celt051(c, in_ptr, in_size, out_ptr, out_size);
+    }
+#endif
+
+#if HAVE_OPUS
+    if (c && c->mode == SPICE_AUDIO_DATA_MODE_OPUS)
+        return snd_codec_encode_opus(c, in_ptr, in_size, out_ptr, out_size);
  #endif
  
      return SND_CODEC_ENCODER_UNAVAILABLE;
@@ -273,5 +385,10 @@ int snd_codec_decode(SndCodec codec, uint8_t *in_ptr, int in_size, uint8_t *out_
          return snd_codec_decode_celt051(c, in_ptr, in_size, out_ptr, out_size);
  #endif
  
+#if HAVE_OPUS
+    if (c && c->mode == SPICE_AUDIO_DATA_MODE_OPUS)
+        return snd_codec_decode_opus(c, in_ptr, in_size, out_ptr, out_size);
+#endif
+
      return SND_CODEC_DECODER_UNAVAILABLE;
  }
diff --git a/common/snd_codec.h b/common/snd_codec.h
index 752e79d..e4122a4 100644
--- a/common/snd_codec.h
+++ b/common/snd_codec.h
@@ -24,6 +24,10 @@
  #include <celt051/celt.h>
  #endif
  
+#if HAVE_OPUS
+#include  <opus.h>
+#endif
+
  /* Spice uses a very fixed protocol when transmitting CELT audio;
     audio must be transmitted in frames of 256, and we must compress
     data down to a fairly specific size (47, computation below).
@@ -33,14 +37,20 @@
  #define SND_CODEC_CELT_FRAME_SIZE       256
  #define SND_CODEC_CELT_BIT_RATE         (64 * 1024)
  #define SND_CODEC_CELT_PLAYBACK_FREQ    44100
-#define SND_CODEC_CELT_PLAYBACK_CHAN    2
  #define SND_CODEC_CELT_COMPRESSED_FRAME_BYTES (SND_CODEC_CELT_FRAME_SIZE * SND_CODEC_CELT_BIT_RATE / \
                                          SND_CODEC_CELT_PLAYBACK_FREQ / 8)
  
+#define SND_CODEC_OPUS_FRAME_SIZE       480
+#define SND_CODEC_OPUS_PLAYBACK_FREQ    48000
+#define SND_CODEC_OPUS_COMPRESSED_FRAME_BYTES 480
+
+#define SND_CODEC_PLAYBACK_CHAN         2
+
+#define SND_CODEC_MAX_FRAME_SIZE        (MAX(SND_CODEC_CELT_FRAME_SIZE, SND_CODEC_OPUS_FRAME_SIZE))
+#define SND_CODEC_MAX_FRAME_BYTES       (SND_CODEC_MAX_FRAME_SIZE * SND_CODEC_PLAYBACK_CHAN * 2 /* FMT_S16 */)
+#define SND_CODEC_MAX_COMPRESSED_BYTES  MAX(SND_CODEC_CELT_COMPRESSED_FRAME_BYTES, SND_CODEC_OPUS_COMPRESSED_FRAME_BYTES)
  
-#define SND_CODEC_MAX_FRAME_SIZE        SND_CODEC_CELT_FRAME_SIZE
-#define SND_CODEC_MAX_FRAME_BYTES       (SND_CODEC_MAX_FRAME_SIZE * SND_CODEC_CELT_PLAYBACK_CHAN * 2 /* FMT_S16 */)
-#define SND_CODEC_MAX_COMPRESSED_BYTES  SND_CODEC_CELT_COMPRESSED_FRAME_BYTES
+#define SND_CODEC_ANY_FREQUENCY        -1
  
  #define SND_CODEC_OK                    0
  #define SND_CODEC_UNAVAILABLE           1
@@ -57,7 +67,7 @@ SPICE_BEGIN_DECLS
  
  typedef struct SndCodecInternal * SndCodec;
  
-int  snd_codec_is_capable(int mode);
+int  snd_codec_is_capable(int mode, int frequency);
  
  int  snd_codec_create(SndCodec *codec, int mode, int frequency, int purpose);
  void snd_codec_destroy(SndCodec *codec);
diff --git a/configure.ac b/configure.ac
index 3443334..273d935 100644
--- a/configure.ac
+++ b/configure.ac
@@ -67,6 +67,10 @@ 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"])
+AM_COND_IF([HAVE_OPUS], AC_DEFINE([HAVE_OPUS], 1, [Define if we have Opus]))
+
  AC_ARG_ENABLE([opengl],
    AS_HELP_STRING([--enable-opengl=@<:@yes/no@:>@],
                   [Enable opengl support (not recommended) @<:@default=no@:>@]),
diff --git a/spice.proto b/spice.proto
index 04e7ea4..67b3803 100644
--- a/spice.proto
+++ b/spice.proto
@@ -1067,6 +1067,7 @@ enum16 audio_data_mode {
      INVALID,
      RAW,
      CELT_0_5_1,
+    OPUS,
  };
  
  enum16 audio_fmt {
diff --git a/spice1.proto b/spice1.proto
index 2d22cdf..67eb0e6 100644
--- a/spice1.proto
+++ b/spice1.proto
@@ -876,6 +876,7 @@ enum32 audio_data_mode {
      INVALID,
      RAW,
      CELT_0_5_1,
+    OPUS,
  };
  
  enum32 audio_fmt {
-- 
1.7.10.4



More information about the Spice-devel mailing list