mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice-common
synced 2026-01-08 21:14:20 +00:00
Add a snd_codec interface to abstract the use of audio codecs such as celt.
Signed-off-by: Jeremy White <jwhite@codeweavers.com>
This commit is contained in:
parent
261d270cc8
commit
c108e4ee8c
@ -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)
|
||||
|
||||
277
common/snd_codec.c
Normal file
277
common/snd_codec.c
Normal file
@ -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;
|
||||
}
|
||||
72
common/snd_codec.h
Normal file
72
common/snd_codec.h
Normal file
@ -0,0 +1,72 @@
|
||||
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
Copyright (C) 2013 Jeremy White <jwhite@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
|
||||
16
configure.ac
16
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@:>@]),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user