blob: b1bb8df4796221c5052d656b0beaffbec68015fe [file] [log] [blame]
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001/* $Id$ */
2/*
3 * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program 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
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20#include <pjmedia-codec/speex.h>
21#include <pjmedia/codec.h>
22#include <pjmedia/errno.h>
23#include <pjmedia/endpoint.h>
24#include <pjmedia/port.h>
25#include <speex/speex.h>
26#include <pj/assert.h>
Benny Prijonof6aba4e2006-03-06 16:24:50 +000027#include <pj/log.h>
Benny Prijonoeb30bf52006-03-04 20:43:52 +000028#include <pj/pool.h>
29#include <pj/string.h>
30#include <pj/os.h>
31
Benny Prijono4381efe2006-03-16 14:24:26 +000032/*
33 * Only build this file if PJMEDIA_HAS_SPEEX_CODEC != 0
34 */
35#if defined(PJMEDIA_HAS_SPEEX_CODEC) && PJMEDIA_HAS_SPEEX_CODEC!=0
36
37
Benny Prijonof6aba4e2006-03-06 16:24:50 +000038#define THIS_FILE "speex_codec.c"
39
Benny Prijono70c68912006-05-19 15:58:13 +000040#define DEFAULT_QUALITY 10
41#define DEFAULT_COMPLEXITY 10
Benny Prijonoeb30bf52006-03-04 20:43:52 +000042
Benny Prijonoeb30bf52006-03-04 20:43:52 +000043/* Prototypes for Speex factory */
44static pj_status_t spx_test_alloc( pjmedia_codec_factory *factory,
45 const pjmedia_codec_info *id );
46static pj_status_t spx_default_attr( pjmedia_codec_factory *factory,
47 const pjmedia_codec_info *id,
48 pjmedia_codec_param *attr );
49static pj_status_t spx_enum_codecs( pjmedia_codec_factory *factory,
50 unsigned *count,
51 pjmedia_codec_info codecs[]);
52static pj_status_t spx_alloc_codec( pjmedia_codec_factory *factory,
53 const pjmedia_codec_info *id,
54 pjmedia_codec **p_codec);
55static pj_status_t spx_dealloc_codec( pjmedia_codec_factory *factory,
56 pjmedia_codec *codec );
57
58/* Prototypes for Speex implementation. */
Benny Prijonoeb30bf52006-03-04 20:43:52 +000059static pj_status_t spx_codec_init( pjmedia_codec *codec,
60 pj_pool_t *pool );
61static pj_status_t spx_codec_open( pjmedia_codec *codec,
62 pjmedia_codec_param *attr );
63static pj_status_t spx_codec_close( pjmedia_codec *codec );
Benny Prijono8befd9f2006-05-13 22:46:23 +000064static pj_status_t spx_codec_parse( pjmedia_codec *codec,
65 void *pkt,
66 pj_size_t pkt_size,
67 const pj_timestamp *ts,
68 unsigned *frame_cnt,
69 pjmedia_frame frames[]);
Benny Prijonoeb30bf52006-03-04 20:43:52 +000070static pj_status_t spx_codec_encode( pjmedia_codec *codec,
71 const struct pjmedia_frame *input,
72 unsigned output_buf_len,
73 struct pjmedia_frame *output);
74static pj_status_t spx_codec_decode( pjmedia_codec *codec,
75 const struct pjmedia_frame *input,
76 unsigned output_buf_len,
77 struct pjmedia_frame *output);
Benny Prijono8befd9f2006-05-13 22:46:23 +000078static pj_status_t spx_codec_recover(pjmedia_codec *codec,
79 unsigned output_buf_len,
80 struct pjmedia_frame *output);
Benny Prijonoeb30bf52006-03-04 20:43:52 +000081
82/* Definition for Speex codec operations. */
83static pjmedia_codec_op spx_op =
84{
Benny Prijonoeb30bf52006-03-04 20:43:52 +000085 &spx_codec_init,
86 &spx_codec_open,
87 &spx_codec_close,
Benny Prijono8befd9f2006-05-13 22:46:23 +000088 &spx_codec_parse,
Benny Prijonoeb30bf52006-03-04 20:43:52 +000089 &spx_codec_encode,
Benny Prijono8befd9f2006-05-13 22:46:23 +000090 &spx_codec_decode,
91 &spx_codec_recover
Benny Prijonoeb30bf52006-03-04 20:43:52 +000092};
93
94/* Definition for Speex codec factory operations. */
95static pjmedia_codec_factory_op spx_factory_op =
96{
97 &spx_test_alloc,
98 &spx_default_attr,
99 &spx_enum_codecs,
100 &spx_alloc_codec,
101 &spx_dealloc_codec
102};
103
104/* Index to Speex parameter. */
105enum
106{
107 PARAM_NB, /* Index for narrowband parameter. */
108 PARAM_WB, /* Index for wideband parameter. */
109 PARAM_UWB, /* Index for ultra-wideband parameter */
110};
111
112/* Speex default parameter */
113struct speex_param
114{
Benny Prijono7bd15132006-03-06 13:32:57 +0000115 int enabled; /* Is this mode enabled? */
116 const SpeexMode *mode; /* Speex mode. */
117 int pt; /* Payload type. */
118 unsigned clock_rate; /* Default sampling rate to be used.*/
119 int quality; /* Default encoder quality. */
120 int complexity; /* Default encoder complexity. */
121 int samples_per_frame; /* Samples per frame. */
122 int framesize; /* Frame size for current mode. */
123 int bitrate; /* Bit rate for current mode. */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000124};
125
126/* Speex factory */
127static struct spx_factory
128{
129 pjmedia_codec_factory base;
130 pjmedia_endpt *endpt;
131 pj_pool_t *pool;
132 pj_mutex_t *mutex;
133 pjmedia_codec codec_list;
134 struct speex_param speex_param[3];
135
136} spx_factory;
137
138/* Speex codec private data. */
139struct spx_private
140{
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000141 int param_id; /**< Index to speex param. */
142
143 void *enc; /**< Encoder state. */
144 SpeexBits enc_bits; /**< Encoder bits. */
145 void *dec; /**< Decoder state. */
146 SpeexBits dec_bits; /**< Decoder bits. */
147};
148
149
150/*
151 * Get codec bitrate and frame size.
152 */
153static pj_status_t get_speex_info( struct speex_param *p )
154{
155 void *state;
156 int tmp;
157
158 /* Create temporary encoder */
159 state = speex_encoder_init(p->mode);
160 if (!state)
161 return PJMEDIA_CODEC_EFAILED;
162
163 /* Set the quality */
Benny Prijono7bd15132006-03-06 13:32:57 +0000164 if (p->quality != -1)
165 speex_encoder_ctl(state, SPEEX_SET_QUALITY, &p->quality);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000166
167 /* Sampling rate. */
168 speex_encoder_ctl(state, SPEEX_SET_SAMPLING_RATE, &p->clock_rate);
169
Benny Prijono7bd15132006-03-06 13:32:57 +0000170 /* VAD off to have max bitrate */
171 tmp = 0;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000172 speex_encoder_ctl(state, SPEEX_SET_VAD, &tmp);
173
174 /* Complexity. */
Benny Prijono7bd15132006-03-06 13:32:57 +0000175 if (p->complexity != -1)
176 speex_encoder_ctl(state, SPEEX_SET_COMPLEXITY, &p->complexity);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000177
178 /* Now get the frame size */
179 speex_encoder_ctl(state, SPEEX_GET_FRAME_SIZE, &p->samples_per_frame);
180
181 /* Now get the the averate bitrate */
182 speex_encoder_ctl(state, SPEEX_GET_BITRATE, &p->bitrate);
183
184 /* Calculate framesize. */
185 p->framesize = p->bitrate * 20 / 1000;
186
187 /* Destroy encoder. */
188 speex_encoder_destroy(state);
189
190 return PJ_SUCCESS;
191}
192
193/*
194 * Initialize and register Speex codec factory to pjmedia endpoint.
195 */
196PJ_DEF(pj_status_t) pjmedia_codec_speex_init( pjmedia_endpt *endpt,
197 unsigned options,
198 int quality,
199 int complexity )
200{
201 pjmedia_codec_mgr *codec_mgr;
202 unsigned i;
203 pj_status_t status;
204
205 if (spx_factory.pool != NULL) {
206 /* Already initialized. */
207 return PJ_SUCCESS;
208 }
209
210 /* Get defaults */
211 if (quality < 0) quality = DEFAULT_QUALITY;
212 if (complexity < 0) complexity = DEFAULT_COMPLEXITY;
213
214 /* Create Speex codec factory. */
215 spx_factory.base.op = &spx_factory_op;
216 spx_factory.base.factory_data = NULL;
217 spx_factory.endpt = endpt;
218
219 spx_factory.pool = pjmedia_endpt_create_pool(endpt, "speex",
220 4000, 4000);
221 if (!spx_factory.pool)
222 return PJ_ENOMEM;
223
224 pj_list_init(&spx_factory.codec_list);
225
226 /* Create mutex. */
227 status = pj_mutex_create_simple(spx_factory.pool, "speex",
228 &spx_factory.mutex);
229 if (status != PJ_SUCCESS)
230 goto on_error;
231
232 /* Initialize default Speex parameter. */
233 spx_factory.speex_param[PARAM_NB].enabled =
234 ((options & PJMEDIA_SPEEX_NO_NB) == 0);
Benny Prijonoa837c302006-04-27 22:36:40 +0000235 spx_factory.speex_param[PARAM_NB].pt = PJMEDIA_RTP_PT_SPEEX_NB;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000236 spx_factory.speex_param[PARAM_NB].mode = &speex_nb_mode;
237 spx_factory.speex_param[PARAM_NB].clock_rate = 8000;
238 spx_factory.speex_param[PARAM_NB].quality = quality;
239 spx_factory.speex_param[PARAM_NB].complexity = complexity;
240
241 spx_factory.speex_param[PARAM_WB].enabled =
242 ((options & PJMEDIA_SPEEX_NO_WB) == 0);
Benny Prijonoa837c302006-04-27 22:36:40 +0000243 spx_factory.speex_param[PARAM_WB].pt = PJMEDIA_RTP_PT_SPEEX_WB;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000244 spx_factory.speex_param[PARAM_WB].mode = &speex_wb_mode;
245 spx_factory.speex_param[PARAM_WB].clock_rate = 16000;
246 spx_factory.speex_param[PARAM_WB].quality = quality;
247 spx_factory.speex_param[PARAM_WB].complexity = complexity;
248
249 spx_factory.speex_param[PARAM_UWB].enabled =
250 ((options & PJMEDIA_SPEEX_NO_UWB) == 0);
Benny Prijonoa837c302006-04-27 22:36:40 +0000251 spx_factory.speex_param[PARAM_UWB].pt = PJMEDIA_RTP_PT_SPEEX_UWB;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000252 spx_factory.speex_param[PARAM_UWB].mode = &speex_uwb_mode;
253 spx_factory.speex_param[PARAM_UWB].clock_rate = 32000;
254 spx_factory.speex_param[PARAM_UWB].quality = quality;
255 spx_factory.speex_param[PARAM_UWB].complexity = complexity;
256
Benny Prijonof6aba4e2006-03-06 16:24:50 +0000257 /* Somehow quality <=4 is broken in linux. */
Benny Prijono70c68912006-05-19 15:58:13 +0000258 if (quality <= 4 && quality >= 0) {
259 PJ_LOG(5,(THIS_FILE, "Adjusting quality to 5 for uwb"));
Benny Prijonof6aba4e2006-03-06 16:24:50 +0000260 spx_factory.speex_param[PARAM_UWB].quality = 5;
261 }
262
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000263 /* Get codec framesize and avg bitrate for each mode. */
264 for (i=0; i<PJ_ARRAY_SIZE(spx_factory.speex_param); ++i) {
265 status = get_speex_info(&spx_factory.speex_param[i]);
266 }
267
268 /* Get the codec manager. */
269 codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
270 if (!codec_mgr) {
271 status = PJ_EINVALIDOP;
272 goto on_error;
273 }
274
275 /* Register codec factory to endpoint. */
276 status = pjmedia_codec_mgr_register_factory(codec_mgr,
277 &spx_factory.base);
278 if (status != PJ_SUCCESS)
279 goto on_error;
280
281 /* Done. */
282 return PJ_SUCCESS;
283
284on_error:
285 pj_pool_release(spx_factory.pool);
286 spx_factory.pool = NULL;
287 return status;
288}
289
290
291/*
292 * Initialize with default settings.
293 */
294PJ_DEF(pj_status_t) pjmedia_codec_speex_init_default(pjmedia_endpt *endpt)
295{
296 return pjmedia_codec_speex_init(endpt, 0, -1, -1);
297}
298
299/*
300 * Unregister Speex codec factory from pjmedia endpoint and deinitialize
301 * the Speex codec library.
302 */
303PJ_DEF(pj_status_t) pjmedia_codec_speex_deinit(void)
304{
305 pjmedia_codec_mgr *codec_mgr;
306 pj_status_t status;
307
308 if (spx_factory.pool == NULL) {
309 /* Already deinitialized */
310 return PJ_SUCCESS;
311 }
312
313 /* We don't want to deinit if there's outstanding codec. */
314 pj_mutex_lock(spx_factory.mutex);
315 if (!pj_list_empty(&spx_factory.codec_list)) {
316 pj_mutex_unlock(spx_factory.mutex);
317 return PJ_EBUSY;
318 }
319
320 /* Get the codec manager. */
321 codec_mgr = pjmedia_endpt_get_codec_mgr(spx_factory.endpt);
322 if (!codec_mgr) {
323 pj_pool_release(spx_factory.pool);
324 spx_factory.pool = NULL;
325 return PJ_EINVALIDOP;
326 }
327
328 /* Unregister Speex codec factory. */
329 status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
330 &spx_factory.base);
331
332 /* Destroy mutex. */
333 pj_mutex_destroy(spx_factory.mutex);
334
335 /* Destroy pool. */
336 pj_pool_release(spx_factory.pool);
337 spx_factory.pool = NULL;
338
339 return status;
340}
341
342/*
343 * Check if factory can allocate the specified codec.
344 */
345static pj_status_t spx_test_alloc( pjmedia_codec_factory *factory,
346 const pjmedia_codec_info *info )
347{
348 const pj_str_t speex_tag = { "speex", 5};
349 unsigned i;
350
351 PJ_UNUSED_ARG(factory);
352
353 /* Type MUST be audio. */
354 if (info->type != PJMEDIA_TYPE_AUDIO)
355 return PJMEDIA_CODEC_EUNSUP;
356
357 /* Check encoding name. */
358 if (pj_stricmp(&info->encoding_name, &speex_tag) != 0)
359 return PJMEDIA_CODEC_EUNSUP;
360
361 /* Check clock-rate */
362 for (i=0; i<PJ_ARRAY_SIZE(spx_factory.speex_param); ++i) {
Benny Prijonoa837c302006-04-27 22:36:40 +0000363 if (info->clock_rate == spx_factory.speex_param[i].clock_rate) {
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000364 /* Okay, let's Speex! */
365 return PJ_SUCCESS;
366 }
367 }
368
369
370 /* Unsupported, or mode is disabled. */
371 return PJMEDIA_CODEC_EUNSUP;
372}
373
374/*
375 * Generate default attribute.
376 */
377static pj_status_t spx_default_attr (pjmedia_codec_factory *factory,
378 const pjmedia_codec_info *id,
379 pjmedia_codec_param *attr )
380{
381
382 PJ_ASSERT_RETURN(factory==&spx_factory.base, PJ_EINVAL);
383
384 pj_memset(attr, 0, sizeof(pjmedia_codec_param));
Benny Prijono8befd9f2006-05-13 22:46:23 +0000385 attr->info.pt = (pj_uint8_t)id->pt;
386 attr->info.channel_cnt = 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000387
Benny Prijonoa837c302006-04-27 22:36:40 +0000388 if (id->clock_rate <= 8000) {
Benny Prijono8befd9f2006-05-13 22:46:23 +0000389 attr->info.clock_rate = spx_factory.speex_param[PARAM_NB].clock_rate;
390 attr->info.avg_bps = spx_factory.speex_param[PARAM_NB].bitrate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000391
Benny Prijonoa837c302006-04-27 22:36:40 +0000392 } else if (id->clock_rate <= 16000) {
Benny Prijono8befd9f2006-05-13 22:46:23 +0000393 attr->info.clock_rate = spx_factory.speex_param[PARAM_WB].clock_rate;
394 attr->info.avg_bps = spx_factory.speex_param[PARAM_WB].bitrate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000395
396 } else {
397 /* Wow.. somebody is doing ultra-wideband. Cool...! */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000398 attr->info.clock_rate = spx_factory.speex_param[PARAM_UWB].clock_rate;
399 attr->info.avg_bps = spx_factory.speex_param[PARAM_UWB].bitrate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000400 }
401
Benny Prijono8befd9f2006-05-13 22:46:23 +0000402 attr->info.pcm_bits_per_sample = 16;
403 attr->info.frm_ptime = 20;
404 attr->info.pt = (pj_uint8_t)id->pt;
405
406 attr->setting.frm_per_pkt = 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000407
408 /* Default flags. */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000409 attr->setting.cng = 1;
410 attr->setting.plc = 1;
411 attr->setting.hpf = 1;
412 attr->setting.lpf =1 ;
413 attr->setting.penh =1 ;
Benny Prijono70c68912006-05-19 15:58:13 +0000414 attr->setting.vad = 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000415
416 return PJ_SUCCESS;
417}
418
419/*
420 * Enum codecs supported by this factory (i.e. only Speex!).
421 */
422static pj_status_t spx_enum_codecs(pjmedia_codec_factory *factory,
423 unsigned *count,
424 pjmedia_codec_info codecs[])
425{
426 unsigned max;
427 int i; /* Must be signed */
428
429 PJ_UNUSED_ARG(factory);
430 PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
431
432 max = *count;
433 *count = 0;
434
435 /*
436 * We return three codecs here, and in this order:
437 * - ultra-wideband, wideband, and narrowband.
438 */
439 for (i=PJ_ARRAY_SIZE(spx_factory.speex_param)-1; i>=0 && *count<max; --i) {
440
441 if (!spx_factory.speex_param[i].enabled)
442 continue;
443
444 pj_memset(&codecs[*count], 0, sizeof(pjmedia_codec_info));
445 codecs[*count].encoding_name = pj_str("speex");
446 codecs[*count].pt = spx_factory.speex_param[i].pt;
447 codecs[*count].type = PJMEDIA_TYPE_AUDIO;
Benny Prijonoa837c302006-04-27 22:36:40 +0000448 codecs[*count].clock_rate = spx_factory.speex_param[i].clock_rate;
449 codecs[*count].channel_cnt = 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000450
451 ++*count;
452 }
453
454 return PJ_SUCCESS;
455}
456
457/*
458 * Allocate a new Speex codec instance.
459 */
460static pj_status_t spx_alloc_codec( pjmedia_codec_factory *factory,
461 const pjmedia_codec_info *id,
462 pjmedia_codec **p_codec)
463{
464 pjmedia_codec *codec;
465 struct spx_private *spx;
466
467 PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
468 PJ_ASSERT_RETURN(factory == &spx_factory.base, PJ_EINVAL);
469
470
471 pj_mutex_lock(spx_factory.mutex);
472
473 /* Get free nodes, if any. */
474 if (!pj_list_empty(&spx_factory.codec_list)) {
475 codec = spx_factory.codec_list.next;
476 pj_list_erase(codec);
477 } else {
478 codec = pj_pool_zalloc(spx_factory.pool,
479 sizeof(pjmedia_codec));
480 PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM);
481 codec->op = &spx_op;
482 codec->factory = factory;
483 codec->codec_data = pj_pool_alloc(spx_factory.pool,
484 sizeof(struct spx_private));
485 }
486
487 pj_mutex_unlock(spx_factory.mutex);
488
489 spx = (struct spx_private*) codec->codec_data;
490 spx->enc = NULL;
491 spx->dec = NULL;
492
Benny Prijonoa837c302006-04-27 22:36:40 +0000493 if (id->clock_rate <= 8000)
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000494 spx->param_id = PARAM_NB;
Benny Prijonoa837c302006-04-27 22:36:40 +0000495 else if (id->clock_rate <= 16000)
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000496 spx->param_id = PARAM_WB;
497 else
498 spx->param_id = PARAM_UWB;
499
500 *p_codec = codec;
501 return PJ_SUCCESS;
502}
503
504/*
505 * Free codec.
506 */
507static pj_status_t spx_dealloc_codec( pjmedia_codec_factory *factory,
508 pjmedia_codec *codec )
509{
510 struct spx_private *spx;
511
512 PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
513 PJ_ASSERT_RETURN(factory == &spx_factory.base, PJ_EINVAL);
514
515 /* Close codec, if it's not closed. */
516 spx = (struct spx_private*) codec->codec_data;
517 if (spx->enc != NULL || spx->dec != NULL) {
518 spx_codec_close(codec);
519 }
520
521 /* Put in the free list. */
522 pj_mutex_lock(spx_factory.mutex);
523 pj_list_push_front(&spx_factory.codec_list, codec);
524 pj_mutex_unlock(spx_factory.mutex);
525
526 return PJ_SUCCESS;
527}
528
529/*
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000530 * Init codec.
531 */
532static pj_status_t spx_codec_init( pjmedia_codec *codec,
533 pj_pool_t *pool )
534{
535 PJ_UNUSED_ARG(codec);
536 PJ_UNUSED_ARG(pool);
537 return PJ_SUCCESS;
538}
539
540/*
541 * Open codec.
542 */
543static pj_status_t spx_codec_open( pjmedia_codec *codec,
544 pjmedia_codec_param *attr )
545{
546 struct spx_private *spx;
547 int id, tmp;
548
549 spx = (struct spx_private*) codec->codec_data;
550 id = spx->param_id;
551
552 /*
553 * Create and initialize encoder.
554 */
555 spx->enc = speex_encoder_init(spx_factory.speex_param[id].mode);
556 if (!spx->enc)
557 return PJMEDIA_CODEC_EFAILED;
558 speex_bits_init(&spx->enc_bits);
559
560 /* Set the quality*/
Benny Prijono7bd15132006-03-06 13:32:57 +0000561 if (spx_factory.speex_param[id].quality != -1) {
562 speex_encoder_ctl(spx->enc, SPEEX_SET_QUALITY,
563 &spx_factory.speex_param[id].quality);
564 }
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000565
566 /* Sampling rate. */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000567 tmp = attr->info.clock_rate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000568 speex_encoder_ctl(spx->enc, SPEEX_SET_SAMPLING_RATE,
569 &spx_factory.speex_param[id].clock_rate);
570
571 /* VAD */
Benny Prijono70c68912006-05-19 15:58:13 +0000572 tmp = (attr->setting.vad != 0);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000573 speex_encoder_ctl(spx->enc, SPEEX_SET_VAD, &tmp);
Benny Prijono70c68912006-05-19 15:58:13 +0000574 speex_encoder_ctl(spx->enc, SPEEX_SET_DTX, &tmp);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000575
576 /* Complexity */
Benny Prijono7bd15132006-03-06 13:32:57 +0000577 if (spx_factory.speex_param[id].complexity != -1) {
578 speex_encoder_ctl(spx->enc, SPEEX_SET_COMPLEXITY,
579 &spx_factory.speex_param[id].complexity);
580 }
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000581
582 /*
583 * Create and initialize decoder.
584 */
585 spx->dec = speex_decoder_init(spx_factory.speex_param[id].mode);
586 if (!spx->dec) {
587 spx_codec_close(codec);
588 return PJMEDIA_CODEC_EFAILED;
589 }
590 speex_bits_init(&spx->dec_bits);
591
592 /* Sampling rate. */
593 speex_decoder_ctl(spx->dec, SPEEX_SET_SAMPLING_RATE,
594 &spx_factory.speex_param[id].clock_rate);
595
596 /* PENH */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000597 tmp = attr->setting.penh;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000598 speex_decoder_ctl(spx->dec, SPEEX_SET_ENH, &tmp);
599
600 return PJ_SUCCESS;
601}
602
603/*
604 * Close codec.
605 */
606static pj_status_t spx_codec_close( pjmedia_codec *codec )
607{
608 struct spx_private *spx;
609
610 spx = (struct spx_private*) codec->codec_data;
611
612 /* Destroy encoder*/
613 if (spx->enc) {
614 speex_encoder_destroy( spx->enc );
615 spx->enc = NULL;
616 speex_bits_destroy( &spx->enc_bits );
617 }
618
619 /* Destroy decoder */
620 if (spx->dec) {
621 speex_decoder_destroy( spx->dec);
622 spx->dec = NULL;
623 speex_bits_destroy( &spx->dec_bits );
624 }
625
626 return PJ_SUCCESS;
627}
628
629
630/*
631 * Get frames in the packet.
632 */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000633static pj_status_t spx_codec_parse( pjmedia_codec *codec,
634 void *pkt,
635 pj_size_t pkt_size,
636 const pj_timestamp *ts,
637 unsigned *frame_cnt,
638 pjmedia_frame frames[])
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000639{
640 struct spx_private *spx;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000641 unsigned frame_size, samples_per_frame;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000642 unsigned count;
643
644 spx = (struct spx_private*) codec->codec_data;
645
Benny Prijono8befd9f2006-05-13 22:46:23 +0000646 frame_size = spx_factory.speex_param[spx->param_id].framesize;
647 samples_per_frame = spx_factory.speex_param[spx->param_id].samples_per_frame;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000648
649 /* Don't really know how to do this... */
650 count = 0;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000651 while (pkt_size >= frame_size && count < *frame_cnt) {
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000652 frames[count].buf = pkt;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000653 frames[count].size = frame_size;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000654 frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000655 frames[count].timestamp.u64 = ts->u64 + count * samples_per_frame;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000656
Benny Prijono8befd9f2006-05-13 22:46:23 +0000657 pkt_size -= frame_size;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000658 ++count;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000659 pkt = ((char*)pkt) + frame_size;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000660 }
661
Benny Prijono8befd9f2006-05-13 22:46:23 +0000662 /* Just in case speex has silence frame which size is less than normal
663 * frame size...
664 */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000665 if (pkt_size && count < *frame_cnt) {
666 frames[count].buf = pkt;
667 frames[count].size = pkt_size;
668 frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000669 frames[count].timestamp.u64 = ts->u64 + count * samples_per_frame;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000670 ++count;
671 }
672
673 *frame_cnt = count;
674 return PJ_SUCCESS;
675}
676
677/*
678 * Encode frame.
679 */
680static pj_status_t spx_codec_encode( pjmedia_codec *codec,
681 const struct pjmedia_frame *input,
682 unsigned output_buf_len,
683 struct pjmedia_frame *output)
684{
685 struct spx_private *spx;
Benny Prijono856ebc32006-03-05 11:51:10 +0000686 float tmp[642]; /* 20ms at 32KHz + 2 */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000687 pj_int16_t *samp_in;
Benny Prijono856ebc32006-03-05 11:51:10 +0000688 unsigned i, samp_count, sz;
Benny Prijono70c68912006-05-19 15:58:13 +0000689 int tx;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000690
691 spx = (struct spx_private*) codec->codec_data;
692
693 if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) {
694 output->size = 0;
695 output->buf = NULL;
696 output->timestamp = input->timestamp;
697 output->type = input->type;
698 return PJ_SUCCESS;
699 }
700
701 /* Copy frame to float buffer. */
702 samp_count = input->size / 2;
Benny Prijono856ebc32006-03-05 11:51:10 +0000703 pj_assert(samp_count <= PJ_ARRAY_SIZE(tmp));
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000704 samp_in = input->buf;
705 for (i=0; i<samp_count; ++i) {
706 tmp[i] = samp_in[i];
707 }
708
709 /* Flush all the bits in the struct so we can encode a new frame */
710 speex_bits_reset(&spx->enc_bits);
711
712 /* Encode the frame */
Benny Prijono70c68912006-05-19 15:58:13 +0000713 tx = speex_encode(spx->enc, tmp, &spx->enc_bits);
714
715 /* Check if we need not to transmit the frame (DTX) */
716 if (tx == 0) {
717 output->buf = NULL;
718 output->size = 0;
719 output->timestamp.u64 = input->timestamp.u64;
720 output->type = PJMEDIA_FRAME_TYPE_NONE;
721 return PJ_SUCCESS;
722 }
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000723
Benny Prijono856ebc32006-03-05 11:51:10 +0000724 /* Check size. */
725 sz = speex_bits_nbytes(&spx->enc_bits);
726 pj_assert(sz <= output_buf_len);
727
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000728 /* Copy the bits to an array of char that can be written */
729 output->size = speex_bits_write(&spx->enc_bits,
730 output->buf, output_buf_len);
731 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
732 output->timestamp = input->timestamp;
733
734 return PJ_SUCCESS;
735}
736
737/*
738 * Decode frame.
739 */
740static pj_status_t spx_codec_decode( pjmedia_codec *codec,
741 const struct pjmedia_frame *input,
742 unsigned output_buf_len,
743 struct pjmedia_frame *output)
744{
745 struct spx_private *spx;
Benny Prijono856ebc32006-03-05 11:51:10 +0000746 float tmp[642]; /* 20ms at 32KHz + 2 */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000747 pj_int16_t *dst_buf;
Benny Prijono856ebc32006-03-05 11:51:10 +0000748 unsigned i, count, sz;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000749
750 spx = (struct spx_private*) codec->codec_data;
751
752 if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) {
753 pj_memset(output->buf, 0, output_buf_len);
754 output->size = 320;
755 output->timestamp.u64 = input->timestamp.u64;
756 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
757 return PJ_SUCCESS;
758 }
759
760 /* Initialization of the structure that holds the bits */
761 speex_bits_init(&spx->dec_bits);
762
763 /* Copy the data into the bit-stream struct */
764 speex_bits_read_from(&spx->dec_bits, input->buf, input->size);
765
766 /* Decode the data */
767 speex_decode(spx->dec, &spx->dec_bits, tmp);
768
Benny Prijono856ebc32006-03-05 11:51:10 +0000769 /* Check size. */
770 sz = speex_bits_nbytes(&spx->enc_bits);
771 pj_assert(sz <= output_buf_len);
772
773 /* Copy from float to short samples. */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000774 count = spx_factory.speex_param[spx->param_id].clock_rate * 20 / 1000;
Benny Prijono856ebc32006-03-05 11:51:10 +0000775 pj_assert((count <= output_buf_len/2) && count <= PJ_ARRAY_SIZE(tmp));
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000776 dst_buf = output->buf;
777 for (i=0; i<count; ++i) {
778 dst_buf[i] = (pj_int16_t)tmp[i];
779 }
780 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
781 output->size = count * 2;
782 output->timestamp.u64 = input->timestamp.u64;
783
784
785 return PJ_SUCCESS;
786}
Benny Prijono4381efe2006-03-16 14:24:26 +0000787
Benny Prijono8befd9f2006-05-13 22:46:23 +0000788/*
789 * Recover lost frame.
790 */
791static pj_status_t spx_codec_recover(pjmedia_codec *codec,
792 unsigned output_buf_len,
793 struct pjmedia_frame *output)
794{
795 struct spx_private *spx;
796 float tmp[642]; /* 20ms at 32KHz + 2 */
797 pj_int16_t *dst_buf;
798 unsigned i, count;
799
800 spx = (struct spx_private*) codec->codec_data;
801
802 count = spx_factory.speex_param[spx->param_id].clock_rate * 20 / 1000;
803 pj_assert((count <= output_buf_len/2) && count <= PJ_ARRAY_SIZE(tmp));
804
805 /* Recover packet loss */
806 speex_decode(spx->dec, NULL, tmp);
807
808 /* Copy from float to short samples. */
809 dst_buf = output->buf;
810 for (i=0; i<count; ++i) {
811 dst_buf[i] = (pj_int16_t)tmp[i];
812 }
813 output->size = count * 2;
814
815 return PJ_SUCCESS;
816}
817
Benny Prijono4381efe2006-03-16 14:24:26 +0000818
819#endif /* PJMEDIA_HAS_SPEEX_CODEC */