[Spice-devel] [spice-common opus support 2/5 (take 2)] Add support for the Opus codec.
Jeremy White
jwhite at codeweavers.com
Thu Oct 31 18:13:36 CET 2013
Signed-off-by: Jeremy White <jwhite at codeweavers.com>
---
common/Makefile.am | 2 +
common/snd_codec.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++--
common/snd_codec.h | 17 +++++--
configure.ac | 9 ++++
spice.proto | 1 +
spice1.proto | 1 +
6 files changed, 151 insertions(+), 9 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 2683143..0e0f24b 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;
@@ -80,7 +85,7 @@ static int snd_codec_create_celt051(SndCodecInternal *codec, int encode, int dec
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)
{
@@ -119,7 +124,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) {
@@ -138,12 +143,93 @@ 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 encode, int decode)
+{
+ int opus_error;
+
+ if (encode)
+ {
+ codec->opus_encoder = opus_encoder_create(codec->frequency,
+ SND_CODEC_OPUS_PLAYBACK_CHAN,
+ OPUS_APPLICATION_AUDIO, &opus_error);
+ if (! codec->opus_encoder)
+ {
+ spice_printerr("create opus encoder failed; error %d", opus_error);
+ goto error;
+ }
+ }
+
+ if (decode)
+ {
+ codec->opus_decoder = opus_decoder_create(codec->frequency,
+ SND_CODEC_OPUS_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_OPUS_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_OPUS_PLAYBACK_CHAN * 2 /* 16 fmt */;
+ return SND_CODEC_OK;
+}
+#endif
+
/*----------------------------------------------------------------------------
** PUBLIC INTERFACE
@@ -160,9 +246,14 @@ int snd_codec_is_capable(int mode)
#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)
+ return TRUE;
+#endif
+
+ return FALSE;
}
/*
@@ -191,6 +282,11 @@ int snd_codec_create(SndCodec *codec, int mode, int frequency, int encode, int d
rc = snd_codec_create_celt051(*c, encode, decode);
#endif
+#if HAVE_OPUS
+ if (mode == SPICE_AUDIO_DATA_MODE_OPUS)
+ rc = snd_codec_create_opus(*c, encode, decode);
+#endif
+
return rc;
}
@@ -208,6 +304,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;
}
@@ -225,6 +325,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;
}
@@ -251,7 +355,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)
+ {
+ /* 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;
@@ -281,5 +396,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 069356f..60d7865 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,19 @@
#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_PLAYBACK_CHAN 2
+#define SND_CODEC_OPUS_COMPRESSED_FRAME_BYTES 480
+
+#define SND_CODEC_PLAYBACK_CHAN 2
-#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_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_OK 0
#define SND_CODEC_UNAVAILABLE 1
diff --git a/configure.ac b/configure.ac
index 3443334..bb469c6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -67,6 +67,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
+
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