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