[Spice-devel] [codec/common 1] Add a snd_codec interface to abstract the use of audio codecs such as celt.

Christophe Fergeau cfergeau at redhat.com
Wed Oct 16 11:59:49 CEST 2013


On Tue, Oct 15, 2013 at 09:28:23AM -0500, Jeremy White wrote:
> Signed-off-by: Jeremy White <jwhite at codeweavers.com>
> ---
>  common/Makefile.am |    4 +
>  common/snd_codec.c |  261 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  common/snd_codec.h |   79 ++++++++++++++++
>  configure.ac       |   16 ++++
>  4 files changed, 360 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..bef6a14 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)	\
> @@ -142,6 +145,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..5d8ded1
> --- /dev/null
> +++ b/common/snd_codec.c
> @@ -0,0 +1,261 @@
> +/* -*- 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 "log.h"
> +
> +
> +
> +/* celt 0.51 specific support routines */
> +#if HAVE_CELT051
> +static void snd_codec_destroy_celt051(SndCodec *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(SndCodec *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(SndCodec *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(SndCodec *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;
> +    memset(codec, 0, sizeof(*codec));
> +    codec->frequency = frequency;
> +
> +#if HAVE_CELT051
> +    if (mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1)
> +        rc = snd_codec_create_celt051(codec, encode, decode);
> +#endif
> +
> +    return rc;
> +}
> +
> +/*
> +  snd_codec_destroy
> +    The obvious companion to snd_codec_create
> +*/
> +void snd_codec_destroy(SndCodec *codec)
> +{
> +#if HAVE_CELT051
> +    snd_codec_destroy_celt051(codec);
> +#endif
> +}
> +
> +/*
> +  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)
> +{
> +#if HAVE_CELT051
> +    if (codec->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)
> +{
> +#if HAVE_CELT051
> +    if (codec->mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1)
> +        return snd_codec_encode_celt051(codec, 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)
> +{
> +#if HAVE_CELT051
> +    if (codec->mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1)
> +        return snd_codec_decode_celt051(codec, 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..632588e
> --- /dev/null
> +++ b/common/snd_codec.h
> @@ -0,0 +1,79 @@
> +/* -*- 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         4

Are you intentionally using the same value for ENCODE/DECODE_FAILED? I'm
asking because ENCODER/DECODER_UNAVAILABLE got different values.

Looks good otherwise, ACK.

Christophe
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.freedesktop.org/archives/spice-devel/attachments/20131016/f6e2bfbc/attachment.pgp>


More information about the Spice-devel mailing list