[Spice-devel] [spice-common sound rework 1/3 (take 4)] Add a snd_codec interface to abstract the use of audio codecs such as celt.
Jeremy White
jwhite at codeweavers.com
Tue Nov 5 14:24:47 PST 2013
Signed-off-by: Jeremy White <jwhite at codeweavers.com>
---
common/Makefile.am | 7 ++
common/snd_codec.c | 285 ++++++++++++++++++++++++++++++++++++++++++++++++++++
common/snd_codec.h | 69 +++++++++++++
configure.ac | 16 +++
4 files changed, 377 insertions(+)
create mode 100644 common/snd_codec.c
create mode 100644 common/snd_codec.h
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..2683143
--- /dev/null
+++ b/common/snd_codec.c
@@ -0,0 +1,285 @@
+/* -*- 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 <stdio.h>
+#include <string.h>
+#include "config.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 encode, int decode)
+{
+ 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 (encode)
+ {
+ codec->celt_encoder = celt051_encoder_create(codec->celt_mode);
+ if (! codec->celt_encoder)
+ {
+ spice_printerr("create celt encoder failed");
+ goto error;
+ }
+ }
+
+ if (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 encode, int decode)
+{
+ 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, encode, decode);
+#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..069356f
--- /dev/null
+++ b/common/snd_codec.h
@@ -0,0 +1,69 @@
+/* -*- 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
+
+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 encode, int decode);
+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@:>@]),
--
1.7.10.4
More information about the Spice-devel
mailing list