1 /* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3    Copyright (C) 2013 Jeremy White
4 
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 /* snd_codec.c
20      General purpose sound codec routines for use by Spice.
21    These routines abstract the work of picking a codec and
22    encoding and decoding the buffers.
23      Note:  these routines have some peculiarities that come from
24    wanting to provide full backwards compatibility with the original
25    Spice celt 0.51 implementation.  It has some hard requirements
26    (fixed sample size, fixed compressed buffer size).
27 
28    See below for documentation of the public routines.
29 */
30 
31 #include "config.h"
32 #include <stdio.h>
33 #include <string.h>
34 #include <spice/macros.h>
35 #include <spice/enums.h>
36 
37 
38 #include "snd_codec.h"
39 #include "mem.h"
40 #include "log.h"
41 
42 typedef struct
43 {
44     int mode;
45     int frequency;
46 #if HAVE_CELT051
47     CELTMode *celt_mode;
48     CELTEncoder *celt_encoder;
49     CELTDecoder *celt_decoder;
50 #endif
51 
52 #if HAVE_OPUS
53     OpusEncoder *opus_encoder;
54     OpusDecoder *opus_decoder;
55 #endif
56 } SndCodecInternal;
57 
58 
59 
60 /* celt 0.51 specific support routines */
61 #if HAVE_CELT051
snd_codec_destroy_celt051(SndCodecInternal * codec)62 static void snd_codec_destroy_celt051(SndCodecInternal *codec)
63 {
64     if (codec->celt_decoder) {
65         celt051_decoder_destroy(codec->celt_decoder);
66         codec->celt_decoder = NULL;
67     }
68 
69     if (codec->celt_encoder) {
70         celt051_encoder_destroy(codec->celt_encoder);
71         codec->celt_encoder = NULL;
72     }
73 
74     if (codec->celt_mode) {
75         celt051_mode_destroy(codec->celt_mode);
76         codec->celt_mode = NULL;
77     }
78 }
79 
snd_codec_create_celt051(SndCodecInternal * codec,int purpose)80 static int snd_codec_create_celt051(SndCodecInternal *codec, int purpose)
81 {
82     int celt_error;
83 
84     codec->celt_mode = celt051_mode_create(codec->frequency,
85                                            SND_CODEC_PLAYBACK_CHAN,
86                                            SND_CODEC_CELT_FRAME_SIZE, &celt_error);
87     if (! codec->celt_mode) {
88         g_warning("create celt mode failed %d", celt_error);
89         return SND_CODEC_UNAVAILABLE;
90     }
91 
92     if (purpose & SND_CODEC_ENCODE) {
93         codec->celt_encoder = celt051_encoder_create(codec->celt_mode);
94         if (! codec->celt_encoder) {
95             g_warning("create celt encoder failed");
96             goto error;
97         }
98     }
99 
100     if (purpose & SND_CODEC_DECODE) {
101         codec->celt_decoder = celt051_decoder_create(codec->celt_mode);
102         if (! codec->celt_decoder) {
103             g_warning("create celt decoder failed");
104             goto error;
105         }
106     }
107 
108     codec->mode = SPICE_AUDIO_DATA_MODE_CELT_0_5_1;
109     return SND_CODEC_OK;
110 
111 error:
112     snd_codec_destroy_celt051(codec);
113     return SND_CODEC_UNAVAILABLE;
114 }
115 
snd_codec_encode_celt051(SndCodecInternal * codec,uint8_t * in_ptr,int in_size,uint8_t * out_ptr,int * out_size)116 static int snd_codec_encode_celt051(SndCodecInternal *codec, uint8_t *in_ptr, int in_size, uint8_t *out_ptr, int *out_size)
117 {
118     int n;
119     if (in_size != SND_CODEC_CELT_FRAME_SIZE * SND_CODEC_PLAYBACK_CHAN * 2)
120         return SND_CODEC_INVALID_ENCODE_SIZE;
121     n = celt051_encode(codec->celt_encoder, (celt_int16_t *) in_ptr, NULL, out_ptr, *out_size);
122     if (n < 0) {
123         g_warning("celt051_encode failed %d", n);
124         return SND_CODEC_ENCODE_FAILED;
125     }
126     *out_size = n;
127     return SND_CODEC_OK;
128 }
129 
snd_codec_decode_celt051(SndCodecInternal * codec,uint8_t * in_ptr,int in_size,uint8_t * out_ptr,int * out_size)130 static int snd_codec_decode_celt051(SndCodecInternal *codec, uint8_t *in_ptr, int in_size, uint8_t *out_ptr, int *out_size)
131 {
132     int n;
133     n = celt051_decode(codec->celt_decoder, in_ptr, in_size, (celt_int16_t *) out_ptr);
134     if (n < 0) {
135         g_warning("celt051_decode failed %d", n);
136         return SND_CODEC_DECODE_FAILED;
137     }
138     *out_size = SND_CODEC_CELT_FRAME_SIZE * SND_CODEC_PLAYBACK_CHAN * 2 /* 16 fmt */;
139     return SND_CODEC_OK;
140 }
141 #endif
142 
143 
144 /* Opus support routines */
145 #if HAVE_OPUS
snd_codec_destroy_opus(SndCodecInternal * codec)146 static void snd_codec_destroy_opus(SndCodecInternal *codec)
147 {
148     if (codec->opus_decoder) {
149         opus_decoder_destroy(codec->opus_decoder);
150         codec->opus_decoder = NULL;
151     }
152 
153     if (codec->opus_encoder) {
154         opus_encoder_destroy(codec->opus_encoder);
155         codec->opus_encoder = NULL;
156     }
157 
158 }
159 
snd_codec_create_opus(SndCodecInternal * codec,int purpose)160 static int snd_codec_create_opus(SndCodecInternal *codec, int purpose)
161 {
162     int opus_error;
163 
164     if (purpose & SND_CODEC_ENCODE) {
165         codec->opus_encoder = opus_encoder_create(codec->frequency,
166                                 SND_CODEC_PLAYBACK_CHAN,
167                                 OPUS_APPLICATION_AUDIO, &opus_error);
168         if (! codec->opus_encoder) {
169             g_warning("create opus encoder failed; error %d", opus_error);
170             goto error;
171         }
172     }
173 
174     if (purpose & SND_CODEC_DECODE) {
175         codec->opus_decoder = opus_decoder_create(codec->frequency,
176                                 SND_CODEC_PLAYBACK_CHAN, &opus_error);
177         if (! codec->opus_decoder) {
178             g_warning("create opus decoder failed; error %d", opus_error);
179             goto error;
180         }
181     }
182 
183     codec->mode = SPICE_AUDIO_DATA_MODE_OPUS;
184     return SND_CODEC_OK;
185 
186 error:
187     snd_codec_destroy_opus(codec);
188     return SND_CODEC_UNAVAILABLE;
189 }
190 
snd_codec_encode_opus(SndCodecInternal * codec,uint8_t * in_ptr,int in_size,uint8_t * out_ptr,int * out_size)191 static int snd_codec_encode_opus(SndCodecInternal *codec, uint8_t *in_ptr, int in_size, uint8_t *out_ptr, int *out_size)
192 {
193     int n;
194     if (in_size != SND_CODEC_OPUS_FRAME_SIZE * SND_CODEC_PLAYBACK_CHAN * 2)
195         return SND_CODEC_INVALID_ENCODE_SIZE;
196     n = opus_encode(codec->opus_encoder, (opus_int16 *) in_ptr, SND_CODEC_OPUS_FRAME_SIZE, out_ptr, *out_size);
197     if (n < 0) {
198         g_warning("opus_encode failed %d", n);
199         return SND_CODEC_ENCODE_FAILED;
200     }
201     *out_size = n;
202     return SND_CODEC_OK;
203 }
204 
snd_codec_decode_opus(SndCodecInternal * codec,uint8_t * in_ptr,int in_size,uint8_t * out_ptr,int * out_size)205 static int snd_codec_decode_opus(SndCodecInternal *codec, uint8_t *in_ptr, int in_size, uint8_t *out_ptr, int *out_size)
206 {
207     int n;
208     n = opus_decode(codec->opus_decoder, in_ptr, in_size, (opus_int16 *) out_ptr,
209                 *out_size / SND_CODEC_PLAYBACK_CHAN / 2, 0);
210     if (n < 0) {
211         g_warning("opus_decode failed %d", n);
212         return SND_CODEC_DECODE_FAILED;
213     }
214     *out_size = n * SND_CODEC_PLAYBACK_CHAN * 2 /* 16 fmt */;
215     return SND_CODEC_OK;
216 }
217 #endif
218 
219 
220 /*----------------------------------------------------------------------------
221 **          PUBLIC INTERFACE
222 **--------------------------------------------------------------------------*/
223 
224 /*
225   snd_codec_is_capable
226     Returns TRUE if the current spice implementation can
227       use the given codec, FALSE otherwise.
228    mode must be a SPICE_AUDIO_DATA_MODE_XXX enum from spice/enum.h
229  */
snd_codec_is_capable(int mode,int frequency)230 int snd_codec_is_capable(int mode, int frequency)
231 {
232 #if HAVE_CELT051
233     if (mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1)
234         return TRUE;
235 #endif
236 
237 #if HAVE_OPUS
238     if (mode == SPICE_AUDIO_DATA_MODE_OPUS &&
239          (frequency == SND_CODEC_ANY_FREQUENCY ||
240           frequency == 48000 || frequency == 24000 ||
241           frequency == 16000 || frequency == 12000 ||
242           frequency == 8000) )
243         return TRUE;
244 #endif
245 
246     return FALSE;
247 }
248 
249 /*
250   snd_codec_create
251     Create a codec control.  Required for most functions in this library.
252     Parameters:
253       1.  codec     Pointer to preallocated codec control
254       2.  mode      SPICE_AUDIO_DATA_MODE_XXX enum from spice/enum.h
255       3.  encode    TRUE if encoding is desired
256       4.  decode    TRUE if decoding is desired
257      Returns:
258        SND_CODEC_OK  if all went well; a different code if not.
259 
260   snd_codec_destroy is the obvious partner of snd_codec_create.
261  */
snd_codec_create(SndCodec * codec,int mode,int frequency,int purpose)262 int snd_codec_create(SndCodec *codec, int mode, int frequency, int purpose)
263 {
264     int rc = SND_CODEC_UNAVAILABLE;
265     SndCodecInternal **c = (SndCodecInternal **) codec;
266 
267     *c = spice_new0(SndCodecInternal, 1);
268     (*c)->frequency = frequency;
269 
270 #if HAVE_CELT051
271     if (mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1)
272         rc = snd_codec_create_celt051(*c, purpose);
273 #endif
274 
275 #if HAVE_OPUS
276     if (mode == SPICE_AUDIO_DATA_MODE_OPUS)
277         rc = snd_codec_create_opus(*c, purpose);
278 #endif
279 
280     return rc;
281 }
282 
283 /*
284   snd_codec_destroy
285     The obvious companion to snd_codec_create
286 */
snd_codec_destroy(SndCodec * codec)287 void snd_codec_destroy(SndCodec *codec)
288 {
289     SndCodecInternal **c = (SndCodecInternal **) codec;
290     if (! c || ! *c)
291         return;
292 
293 #if HAVE_CELT051
294     snd_codec_destroy_celt051(*c);
295 #endif
296 
297 #if HAVE_OPUS
298     snd_codec_destroy_opus(*c);
299 #endif
300 
301     free(*c);
302     *c = NULL;
303 }
304 
305 /*
306   snd_codec_frame_size
307     Returns the size, in frames, of the raw PCM frame buffer
308       required by this codec.  To get bytes, you'll need
309       to multiply by channels and sample width.
310  */
snd_codec_frame_size(SndCodec codec)311 int snd_codec_frame_size(SndCodec codec)
312 {
313 #if defined(HAVE_CELT051) || defined(HAVE_OPUS)
314     SndCodecInternal *c = (SndCodecInternal *) codec;
315 #endif
316 #if HAVE_CELT051
317     if (c && c->mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1)
318         return SND_CODEC_CELT_FRAME_SIZE;
319 #endif
320 #if HAVE_OPUS
321     if (c && c->mode == SPICE_AUDIO_DATA_MODE_OPUS)
322         return SND_CODEC_OPUS_FRAME_SIZE;
323 #endif
324     return SND_CODEC_MAX_FRAME_SIZE;
325 }
326 
327 /*
328   snd_codec_encode
329      Encode a block of data to a compressed buffer.
330 
331   Parameters:
332     1.  codec       Pointer to codec control previously allocated + created
333     2.  in_data     Pointer to uncompressed PCM data
334     3.  in_size     Input size  (for celt, this must be a
335                     particular size, governed by the frame size)
336     4.  out_ptr     Pointer to area to write encoded data
337     5.  out_size    On input, the maximum size of the output buffer; on
338                     successful return, it will hold the number of bytes
339                     returned.  For celt, this must be set to a particular
340                     size to ensure compatibility.
341 
342      Returns:
343        SND_CODEC_OK  if all went well
344 */
snd_codec_encode(SndCodec codec,uint8_t * in_ptr,int in_size,uint8_t * out_ptr,int * out_size)345 int snd_codec_encode(SndCodec codec, uint8_t *in_ptr, int in_size, uint8_t *out_ptr, int *out_size)
346 {
347 #if defined(HAVE_CELT051) || defined(HAVE_OPUS)
348     SndCodecInternal *c = (SndCodecInternal *) codec;
349 #endif
350 #if HAVE_CELT051
351     if (c && c->mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1) {
352         /* The output buffer size in celt determines the compression,
353             and so is essentially mandatory to use a certain value (47) */
354         if (*out_size > SND_CODEC_CELT_COMPRESSED_FRAME_BYTES)
355             *out_size = SND_CODEC_CELT_COMPRESSED_FRAME_BYTES;
356         return snd_codec_encode_celt051(c, in_ptr, in_size, out_ptr, out_size);
357     }
358 #endif
359 
360 #if HAVE_OPUS
361     if (c && c->mode == SPICE_AUDIO_DATA_MODE_OPUS)
362         return snd_codec_encode_opus(c, in_ptr, in_size, out_ptr, out_size);
363 #endif
364 
365     return SND_CODEC_ENCODER_UNAVAILABLE;
366 }
367 
368 /*
369   snd_codec_decode
370      Decode a block of data from a compressed buffer.
371 
372   Parameters:
373     1.  codec       Pointer to codec control previously allocated + created
374     2.  in_data     Pointer to compressed data
375     3.  in_size     Input size
376     4.  out_ptr     Pointer to area to write decoded data
377     5.  out_size    On input, the maximum size of the output buffer; on
378                     successful return, it will hold the number of bytes
379                     returned.
380 
381      Returns:
382        SND_CODEC_OK  if all went well
383 */
snd_codec_decode(SndCodec codec,uint8_t * in_ptr,int in_size,uint8_t * out_ptr,int * out_size)384 int snd_codec_decode(SndCodec codec, uint8_t *in_ptr, int in_size, uint8_t *out_ptr, int *out_size)
385 {
386 #if defined(HAVE_CELT051) || defined(HAVE_OPUS)
387     SndCodecInternal *c = (SndCodecInternal *) codec;
388 #endif
389 #if HAVE_CELT051
390     if (c && c->mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1)
391         return snd_codec_decode_celt051(c, in_ptr, in_size, out_ptr, out_size);
392 #endif
393 
394 #if HAVE_OPUS
395     if (c && c->mode == SPICE_AUDIO_DATA_MODE_OPUS)
396         return snd_codec_decode_opus(c, in_ptr, in_size, out_ptr, out_size);
397 #endif
398 
399     return SND_CODEC_DECODER_UNAVAILABLE;
400 }
401