[Spice-commits] 2 commits - common/Makefile.am common/snd_codec.c common/snd_codec.h configure.ac spice-protocol spice.proto spice1.proto

Christophe Fergau teuf at kemper.freedesktop.org
Thu Jan 2 03:30:16 PST 2014


 common/Makefile.am |    9 +
 common/snd_codec.c |  394 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 common/snd_codec.h |   82 +++++++++++
 configure.ac       |   20 ++
 spice-protocol     |    2 
 spice.proto        |    1 
 spice1.proto       |    1 
 7 files changed, 508 insertions(+), 1 deletion(-)

New commits:
commit 57ce430ccd66bd1ca2447c14503234cfb88e2365
Author: Jeremy White <jwhite at codeweavers.com>
Date:   Sat Nov 30 09:18:48 2013 -0600

    Add support for the Opus codec.
    
    Signed-off-by: Jeremy White <jwhite at codeweavers.com>

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-protocol b/spice-protocol
index 5ff3fa7..2575626 160000
--- a/spice-protocol
+++ b/spice-protocol
@@ -1 +1 @@
-Subproject commit 5ff3fa7080bd08392fc011175657264d57dddcec
+Subproject commit 2575626fb6c0eb145a11e6d5bf399c80ed207d91
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 {
commit c108e4ee8cb33218d9a64e25de9e79b63d23a8e7
Author: Jeremy White <jwhite at codeweavers.com>
Date:   Sat Nov 30 09:13:07 2013 -0600

    Add a snd_codec interface to abstract the use of audio codecs such as celt.
    
    Signed-off-by: Jeremy White <jwhite at codeweavers.com>

diff --git a/common/Makefile.am b/common/Makefile.am
index 45568c6..c79f596 100644
--- a/common/Makefile.am
+++ b/common/Makefile.am
@@ -52,6 +52,8 @@ libspice_common_la_SOURCES =		\
 	ring.h				\
 	rop3.c				\
 	rop3.h				\
+	snd_codec.c			\
+	snd_codec.h			\
 	spice_common.h			\
 	ssl_verify.c			\
 	ssl_verify.h			\
@@ -81,6 +83,7 @@ endif
 AM_CPPFLAGS =				\
 	$(GL_CFLAGS)			\
 	$(PIXMAN_CFLAGS)		\
+	$(CELT051_CFLAGS)		\
 	$(PROTOCOL_CFLAGS)		\
 	$(SMARTCARD_CFLAGS)		\
 	$(VISIBILITY_HIDDEN_CFLAGS)	\
@@ -88,6 +91,9 @@ AM_CPPFLAGS =				\
 	-std=gnu99			\
 	$(NULL)
 
+libspice_common_la_LIBADD =				\
+	$(CELT051_LIBS)
+
 MARSHALLERS_DEPS =					\
 	$(top_srcdir)/python_modules/__init__.py	\
 	$(top_srcdir)/python_modules/codegen.py		\
@@ -142,6 +148,7 @@ EXTRA_DIST =				\
 	quic_family_tmpl.c		\
 	quic_rgb_tmpl.c			\
 	quic_tmpl.c			\
+	snd_codec.h			\
 	sw_canvas.c			\
 	sw_canvas.h			\
 	$(NULL)
diff --git a/common/snd_codec.c b/common/snd_codec.c
new file mode 100644
index 0000000..6dcadb7
--- /dev/null
+++ b/common/snd_codec.c
@@ -0,0 +1,277 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2013 Jeremy White
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* snd_codec.c
+     General purpose sound codec routines for use by Spice.
+   These routines abstract the work of picking a codec and
+   encoding and decoding the buffers.
+     Note:  these routines have some peculiarities that come from
+   wanting to provide full backwards compatibility with the original
+   Spice celt 0.51 implementation.  It has some hard requirements
+   (fixed sample size, fixed compressed buffer size).
+
+   See below for documentation of the public routines.
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <spice/macros.h>
+#include <spice/enums.h>
+
+
+#include "snd_codec.h"
+#include "mem.h"
+#include "log.h"
+
+typedef struct
+{
+    int mode;
+    int frequency;
+#if HAVE_CELT051
+    CELTMode *celt_mode;
+    CELTEncoder *celt_encoder;
+    CELTDecoder *celt_decoder;
+#endif
+} SndCodecInternal;
+
+
+
+/* celt 0.51 specific support routines */
+#if HAVE_CELT051
+static void snd_codec_destroy_celt051(SndCodecInternal *codec)
+{
+    if (codec->celt_decoder) {
+        celt051_decoder_destroy(codec->celt_decoder);
+        codec->celt_decoder = NULL;
+    }
+
+    if (codec->celt_encoder) {
+        celt051_encoder_destroy(codec->celt_encoder);
+        codec->celt_encoder = NULL;
+    }
+
+    if (codec->celt_mode) {
+        celt051_mode_destroy(codec->celt_mode);
+        codec->celt_mode = NULL;
+    }
+}
+
+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_CELT_FRAME_SIZE, &celt_error);
+    if (! codec->celt_mode) {
+        spice_printerr("create celt mode failed %d", celt_error);
+        return SND_CODEC_UNAVAILABLE;
+    }
+
+    if (purpose & SND_CODEC_ENCODE) {
+        codec->celt_encoder = celt051_encoder_create(codec->celt_mode);
+        if (! codec->celt_encoder) {
+            spice_printerr("create celt encoder failed");
+            goto error;
+        }
+    }
+
+    if (purpose & SND_CODEC_DECODE) {
+        codec->celt_decoder = celt051_decoder_create(codec->celt_mode);
+        if (! codec->celt_decoder) {
+            spice_printerr("create celt decoder failed");
+            goto error;
+        }
+    }
+
+    codec->mode = SPICE_AUDIO_DATA_MODE_CELT_0_5_1;
+    return SND_CODEC_OK;
+
+error:
+    snd_codec_destroy_celt051(codec);
+    return SND_CODEC_UNAVAILABLE;
+}
+
+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)
+        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) {
+        spice_printerr("celt051_encode failed %d\n", n);
+        return SND_CODEC_ENCODE_FAILED;
+    }
+    *out_size = n;
+    return SND_CODEC_OK;
+}
+
+static int snd_codec_decode_celt051(SndCodecInternal *codec, uint8_t *in_ptr, int in_size, uint8_t *out_ptr, int *out_size)
+{
+    int n;
+    n = celt051_decode(codec->celt_decoder, in_ptr, in_size, (celt_int16_t *) out_ptr);
+    if (n < 0) {
+        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 */;
+    return SND_CODEC_OK;
+}
+#endif
+
+
+
+/*----------------------------------------------------------------------------
+**          PUBLIC INTERFACE
+**--------------------------------------------------------------------------*/
+
+/*
+  snd_codec_is_capable
+    Returns TRUE if the current spice implementation can
+      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)
+{
+#if HAVE_CELT051
+    if (mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1)
+        return TRUE;
+#else
+    return FALSE;
+#endif
+}
+
+/*
+  snd_codec_create
+    Create a codec control.  Required for most functions in this library.
+    Parameters:
+      1.  codec     Pointer to preallocated codec control
+      2.  mode      SPICE_AUDIO_DATA_MODE_XXX enum from spice/enum.h
+      3.  encode    TRUE if encoding is desired
+      4.  decode    TRUE if decoding is desired
+     Returns:
+       SND_CODEC_OK  if all went well; a different code if not.
+
+  snd_codec_destroy is the obvious partner of snd_codec_create.
+ */
+int snd_codec_create(SndCodec *codec, int mode, int frequency, int purpose)
+{
+    int rc = SND_CODEC_UNAVAILABLE;
+    SndCodecInternal **c = (SndCodecInternal **) codec;
+
+    *c = spice_new0(SndCodecInternal, 1);
+    (*c)->frequency = frequency;
+
+#if HAVE_CELT051
+    if (mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1)
+        rc = snd_codec_create_celt051(*c, purpose);
+#endif
+
+    return rc;
+}
+
+/*
+  snd_codec_destroy
+    The obvious companion to snd_codec_create
+*/
+void snd_codec_destroy(SndCodec *codec)
+{
+    SndCodecInternal **c = (SndCodecInternal **) codec;
+    if (! c || ! *c)
+        return;
+
+#if HAVE_CELT051
+    snd_codec_destroy_celt051(*c);
+#endif
+
+    free(*c);
+    *c = NULL;
+}
+
+/*
+  snd_codec_frame_size
+    Returns the size, in frames, of the raw PCM frame buffer
+      required by this codec.  To get bytes, you'll need
+      to multiply by channels and sample width.
+ */
+int snd_codec_frame_size(SndCodec codec)
+{
+    SndCodecInternal *c = (SndCodecInternal *) codec;
+#if HAVE_CELT051
+    if (c && c->mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1)
+        return SND_CODEC_CELT_FRAME_SIZE;
+#endif
+    return SND_CODEC_MAX_FRAME_SIZE;
+}
+
+/*
+  snd_codec_encode
+     Encode a block of data to a compressed buffer.
+
+  Parameters:
+    1.  codec       Pointer to codec control previously allocated + created
+    2.  in_data     Pointer to uncompressed PCM data
+    3.  in_size     Input size  (for celt, this must be a
+                    particular size, governed by the frame size)
+    4.  out_ptr     Pointer to area to write encoded data
+    5.  out_size    On input, the maximum size of the output buffer; on
+                    successful return, it will hold the number of bytes
+                    returned.  For celt, this must be set to a particular
+                    size to ensure compatibility.
+
+     Returns:
+       SND_CODEC_OK  if all went well
+*/
+int snd_codec_encode(SndCodec codec, uint8_t *in_ptr, int in_size, uint8_t *out_ptr, int *out_size)
+{
+    SndCodecInternal *c = (SndCodecInternal *) codec;
+#if HAVE_CELT051
+    if (c && c->mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1)
+        return snd_codec_encode_celt051(c, in_ptr, in_size, out_ptr, out_size);
+#endif
+
+    return SND_CODEC_ENCODER_UNAVAILABLE;
+}
+
+/*
+  snd_codec_decode
+     Decode a block of data from a compressed buffer.
+
+  Parameters:
+    1.  codec       Pointer to codec control previously allocated + created
+    2.  in_data     Pointer to compressed data
+    3.  in_size     Input size
+    4.  out_ptr     Pointer to area to write decoded data
+    5.  out_size    On input, the maximum size of the output buffer; on
+                    successful return, it will hold the number of bytes
+                    returned.
+
+     Returns:
+       SND_CODEC_OK  if all went well
+*/
+int snd_codec_decode(SndCodec codec, uint8_t *in_ptr, int in_size, uint8_t *out_ptr, int *out_size)
+{
+    SndCodecInternal *c = (SndCodecInternal *) codec;
+#if HAVE_CELT051
+    if (c && c->mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1)
+        return snd_codec_decode_celt051(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
new file mode 100644
index 0000000..752e79d
--- /dev/null
+++ b/common/snd_codec.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2013 Jeremy White <jwhite at codeweavers.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _H_SND_CODEC
+#define _H_SND_CODEC
+
+
+#if HAVE_CELT051
+#include <celt051/celt.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).
+   While the protocol doesn't inherently specify this, the expectation
+   of older clients and server mandates it.
+*/
+#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_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_OK                    0
+#define SND_CODEC_UNAVAILABLE           1
+#define SND_CODEC_ENCODER_UNAVAILABLE   2
+#define SND_CODEC_DECODER_UNAVAILABLE   3
+#define SND_CODEC_ENCODE_FAILED         4
+#define SND_CODEC_DECODE_FAILED         5
+#define SND_CODEC_INVALID_ENCODE_SIZE   6
+
+#define SND_CODEC_ENCODE                0x0001
+#define SND_CODEC_DECODE                0x0002
+
+SPICE_BEGIN_DECLS
+
+typedef struct SndCodecInternal * SndCodec;
+
+int  snd_codec_is_capable(int mode);
+
+int  snd_codec_create(SndCodec *codec, int mode, int frequency, int purpose);
+void snd_codec_destroy(SndCodec *codec);
+
+int  snd_codec_frame_size(SndCodec codec);
+
+int  snd_codec_encode(SndCodec codec, uint8_t *in_ptr, int in_size, uint8_t *out_ptr, int *out_size);
+int  snd_codec_decode(SndCodec codec, uint8_t *in_ptr, int in_size, uint8_t *out_ptr, int *out_size);
+
+SPICE_END_DECLS
+
+#endif
diff --git a/configure.ac b/configure.ac
index acb7626..3443334 100644
--- a/configure.ac
+++ b/configure.ac
@@ -51,6 +51,22 @@ if test "x$enable_smartcard" != "xno"; then
 fi
 AM_CONDITIONAL([WITH_SMARTCARD], [test "x$have_smartcard" = "xyes"])
 
+AC_ARG_ENABLE(celt051,
+[  --disable-celt051       Disable celt051 audio codec (enabled by default)],,
+[enable_celt051="yes"])
+
+if test "x$enable_celt051" = "xyes"; then
+    PKG_CHECK_MODULES(CELT051, celt051 >= 0.5.1.1, have_celt051=yes, have_celt051=no)
+    AC_SUBST(CELT051_CFLAGS)
+    AC_SUBST(CELT051_LIBS)
+    AC_SUBST(CELT051_LIBDIR)
+else
+    have_celt051=no
+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]))
+
 AC_ARG_ENABLE([opengl],
   AS_HELP_STRING([--enable-opengl=@<:@yes/no@:>@],
                  [Enable opengl support (not recommended) @<:@default=no@:>@]),


More information about the Spice-commits mailing list