blob: 280cfbddd97f42d72fd2ebb7a505a2bf21f4d36c [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 Prijono7bd15132006-03-06 13:32:57 +000040#define DEFAULT_QUALITY 4
41#define DEFAULT_COMPLEXITY -1
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 );
64static pj_status_t spx_codec_get_frames( pjmedia_codec *codec,
65 void *pkt,
66 pj_size_t pkt_size,
67 unsigned *frame_cnt,
68 pjmedia_frame frames[]);
69static pj_status_t spx_codec_encode( pjmedia_codec *codec,
70 const struct pjmedia_frame *input,
71 unsigned output_buf_len,
72 struct pjmedia_frame *output);
73static pj_status_t spx_codec_decode( pjmedia_codec *codec,
74 const struct pjmedia_frame *input,
75 unsigned output_buf_len,
76 struct pjmedia_frame *output);
77
78/* Definition for Speex codec operations. */
79static pjmedia_codec_op spx_op =
80{
Benny Prijonoeb30bf52006-03-04 20:43:52 +000081 &spx_codec_init,
82 &spx_codec_open,
83 &spx_codec_close,
84 &spx_codec_get_frames,
85 &spx_codec_encode,
86 &spx_codec_decode
87};
88
89/* Definition for Speex codec factory operations. */
90static pjmedia_codec_factory_op spx_factory_op =
91{
92 &spx_test_alloc,
93 &spx_default_attr,
94 &spx_enum_codecs,
95 &spx_alloc_codec,
96 &spx_dealloc_codec
97};
98
99/* Index to Speex parameter. */
100enum
101{
102 PARAM_NB, /* Index for narrowband parameter. */
103 PARAM_WB, /* Index for wideband parameter. */
104 PARAM_UWB, /* Index for ultra-wideband parameter */
105};
106
107/* Speex default parameter */
108struct speex_param
109{
Benny Prijono7bd15132006-03-06 13:32:57 +0000110 int enabled; /* Is this mode enabled? */
111 const SpeexMode *mode; /* Speex mode. */
112 int pt; /* Payload type. */
113 unsigned clock_rate; /* Default sampling rate to be used.*/
114 int quality; /* Default encoder quality. */
115 int complexity; /* Default encoder complexity. */
116 int samples_per_frame; /* Samples per frame. */
117 int framesize; /* Frame size for current mode. */
118 int bitrate; /* Bit rate for current mode. */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000119};
120
121/* Speex factory */
122static struct spx_factory
123{
124 pjmedia_codec_factory base;
125 pjmedia_endpt *endpt;
126 pj_pool_t *pool;
127 pj_mutex_t *mutex;
128 pjmedia_codec codec_list;
129 struct speex_param speex_param[3];
130
131} spx_factory;
132
133/* Speex codec private data. */
134struct spx_private
135{
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000136 int param_id; /**< Index to speex param. */
137
138 void *enc; /**< Encoder state. */
139 SpeexBits enc_bits; /**< Encoder bits. */
140 void *dec; /**< Decoder state. */
141 SpeexBits dec_bits; /**< Decoder bits. */
142};
143
144
145/*
146 * Get codec bitrate and frame size.
147 */
148static pj_status_t get_speex_info( struct speex_param *p )
149{
150 void *state;
151 int tmp;
152
153 /* Create temporary encoder */
154 state = speex_encoder_init(p->mode);
155 if (!state)
156 return PJMEDIA_CODEC_EFAILED;
157
158 /* Set the quality */
Benny Prijono7bd15132006-03-06 13:32:57 +0000159 if (p->quality != -1)
160 speex_encoder_ctl(state, SPEEX_SET_QUALITY, &p->quality);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000161
162 /* Sampling rate. */
163 speex_encoder_ctl(state, SPEEX_SET_SAMPLING_RATE, &p->clock_rate);
164
Benny Prijono7bd15132006-03-06 13:32:57 +0000165 /* VAD off to have max bitrate */
166 tmp = 0;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000167 speex_encoder_ctl(state, SPEEX_SET_VAD, &tmp);
168
169 /* Complexity. */
Benny Prijono7bd15132006-03-06 13:32:57 +0000170 if (p->complexity != -1)
171 speex_encoder_ctl(state, SPEEX_SET_COMPLEXITY, &p->complexity);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000172
173 /* Now get the frame size */
174 speex_encoder_ctl(state, SPEEX_GET_FRAME_SIZE, &p->samples_per_frame);
175
176 /* Now get the the averate bitrate */
177 speex_encoder_ctl(state, SPEEX_GET_BITRATE, &p->bitrate);
178
179 /* Calculate framesize. */
180 p->framesize = p->bitrate * 20 / 1000;
181
182 /* Destroy encoder. */
183 speex_encoder_destroy(state);
184
185 return PJ_SUCCESS;
186}
187
188/*
189 * Initialize and register Speex codec factory to pjmedia endpoint.
190 */
191PJ_DEF(pj_status_t) pjmedia_codec_speex_init( pjmedia_endpt *endpt,
192 unsigned options,
193 int quality,
194 int complexity )
195{
196 pjmedia_codec_mgr *codec_mgr;
197 unsigned i;
198 pj_status_t status;
199
200 if (spx_factory.pool != NULL) {
201 /* Already initialized. */
202 return PJ_SUCCESS;
203 }
204
205 /* Get defaults */
206 if (quality < 0) quality = DEFAULT_QUALITY;
207 if (complexity < 0) complexity = DEFAULT_COMPLEXITY;
208
209 /* Create Speex codec factory. */
210 spx_factory.base.op = &spx_factory_op;
211 spx_factory.base.factory_data = NULL;
212 spx_factory.endpt = endpt;
213
214 spx_factory.pool = pjmedia_endpt_create_pool(endpt, "speex",
215 4000, 4000);
216 if (!spx_factory.pool)
217 return PJ_ENOMEM;
218
219 pj_list_init(&spx_factory.codec_list);
220
221 /* Create mutex. */
222 status = pj_mutex_create_simple(spx_factory.pool, "speex",
223 &spx_factory.mutex);
224 if (status != PJ_SUCCESS)
225 goto on_error;
226
227 /* Initialize default Speex parameter. */
228 spx_factory.speex_param[PARAM_NB].enabled =
229 ((options & PJMEDIA_SPEEX_NO_NB) == 0);
Benny Prijonoa837c302006-04-27 22:36:40 +0000230 spx_factory.speex_param[PARAM_NB].pt = PJMEDIA_RTP_PT_SPEEX_NB;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000231 spx_factory.speex_param[PARAM_NB].mode = &speex_nb_mode;
232 spx_factory.speex_param[PARAM_NB].clock_rate = 8000;
233 spx_factory.speex_param[PARAM_NB].quality = quality;
234 spx_factory.speex_param[PARAM_NB].complexity = complexity;
235
236 spx_factory.speex_param[PARAM_WB].enabled =
237 ((options & PJMEDIA_SPEEX_NO_WB) == 0);
Benny Prijonoa837c302006-04-27 22:36:40 +0000238 spx_factory.speex_param[PARAM_WB].pt = PJMEDIA_RTP_PT_SPEEX_WB;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000239 spx_factory.speex_param[PARAM_WB].mode = &speex_wb_mode;
240 spx_factory.speex_param[PARAM_WB].clock_rate = 16000;
241 spx_factory.speex_param[PARAM_WB].quality = quality;
242 spx_factory.speex_param[PARAM_WB].complexity = complexity;
243
244 spx_factory.speex_param[PARAM_UWB].enabled =
245 ((options & PJMEDIA_SPEEX_NO_UWB) == 0);
Benny Prijonoa837c302006-04-27 22:36:40 +0000246 spx_factory.speex_param[PARAM_UWB].pt = PJMEDIA_RTP_PT_SPEEX_UWB;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000247 spx_factory.speex_param[PARAM_UWB].mode = &speex_uwb_mode;
248 spx_factory.speex_param[PARAM_UWB].clock_rate = 32000;
249 spx_factory.speex_param[PARAM_UWB].quality = quality;
250 spx_factory.speex_param[PARAM_UWB].complexity = complexity;
251
Benny Prijonof6aba4e2006-03-06 16:24:50 +0000252 /* Somehow quality <=4 is broken in linux. */
253 if (quality <= 4) {
254 PJ_LOG(4,(THIS_FILE, "Adjusting quality to 5 for uwb"));
255 spx_factory.speex_param[PARAM_UWB].quality = 5;
256 }
257
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000258 /* Get codec framesize and avg bitrate for each mode. */
259 for (i=0; i<PJ_ARRAY_SIZE(spx_factory.speex_param); ++i) {
260 status = get_speex_info(&spx_factory.speex_param[i]);
261 }
262
263 /* Get the codec manager. */
264 codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
265 if (!codec_mgr) {
266 status = PJ_EINVALIDOP;
267 goto on_error;
268 }
269
270 /* Register codec factory to endpoint. */
271 status = pjmedia_codec_mgr_register_factory(codec_mgr,
272 &spx_factory.base);
273 if (status != PJ_SUCCESS)
274 goto on_error;
275
276 /* Done. */
277 return PJ_SUCCESS;
278
279on_error:
280 pj_pool_release(spx_factory.pool);
281 spx_factory.pool = NULL;
282 return status;
283}
284
285
286/*
287 * Initialize with default settings.
288 */
289PJ_DEF(pj_status_t) pjmedia_codec_speex_init_default(pjmedia_endpt *endpt)
290{
291 return pjmedia_codec_speex_init(endpt, 0, -1, -1);
292}
293
294/*
295 * Unregister Speex codec factory from pjmedia endpoint and deinitialize
296 * the Speex codec library.
297 */
298PJ_DEF(pj_status_t) pjmedia_codec_speex_deinit(void)
299{
300 pjmedia_codec_mgr *codec_mgr;
301 pj_status_t status;
302
303 if (spx_factory.pool == NULL) {
304 /* Already deinitialized */
305 return PJ_SUCCESS;
306 }
307
308 /* We don't want to deinit if there's outstanding codec. */
309 pj_mutex_lock(spx_factory.mutex);
310 if (!pj_list_empty(&spx_factory.codec_list)) {
311 pj_mutex_unlock(spx_factory.mutex);
312 return PJ_EBUSY;
313 }
314
315 /* Get the codec manager. */
316 codec_mgr = pjmedia_endpt_get_codec_mgr(spx_factory.endpt);
317 if (!codec_mgr) {
318 pj_pool_release(spx_factory.pool);
319 spx_factory.pool = NULL;
320 return PJ_EINVALIDOP;
321 }
322
323 /* Unregister Speex codec factory. */
324 status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
325 &spx_factory.base);
326
327 /* Destroy mutex. */
328 pj_mutex_destroy(spx_factory.mutex);
329
330 /* Destroy pool. */
331 pj_pool_release(spx_factory.pool);
332 spx_factory.pool = NULL;
333
334 return status;
335}
336
337/*
338 * Check if factory can allocate the specified codec.
339 */
340static pj_status_t spx_test_alloc( pjmedia_codec_factory *factory,
341 const pjmedia_codec_info *info )
342{
343 const pj_str_t speex_tag = { "speex", 5};
344 unsigned i;
345
346 PJ_UNUSED_ARG(factory);
347
348 /* Type MUST be audio. */
349 if (info->type != PJMEDIA_TYPE_AUDIO)
350 return PJMEDIA_CODEC_EUNSUP;
351
352 /* Check encoding name. */
353 if (pj_stricmp(&info->encoding_name, &speex_tag) != 0)
354 return PJMEDIA_CODEC_EUNSUP;
355
356 /* Check clock-rate */
357 for (i=0; i<PJ_ARRAY_SIZE(spx_factory.speex_param); ++i) {
Benny Prijonoa837c302006-04-27 22:36:40 +0000358 if (info->clock_rate == spx_factory.speex_param[i].clock_rate) {
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000359 /* Okay, let's Speex! */
360 return PJ_SUCCESS;
361 }
362 }
363
364
365 /* Unsupported, or mode is disabled. */
366 return PJMEDIA_CODEC_EUNSUP;
367}
368
369/*
370 * Generate default attribute.
371 */
372static pj_status_t spx_default_attr (pjmedia_codec_factory *factory,
373 const pjmedia_codec_info *id,
374 pjmedia_codec_param *attr )
375{
376
377 PJ_ASSERT_RETURN(factory==&spx_factory.base, PJ_EINVAL);
378
379 pj_memset(attr, 0, sizeof(pjmedia_codec_param));
380 attr->pt = id->pt;
Benny Prijonoa837c302006-04-27 22:36:40 +0000381 attr->channel_cnt = 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000382
Benny Prijonoa837c302006-04-27 22:36:40 +0000383 if (id->clock_rate <= 8000) {
384 attr->clock_rate = spx_factory.speex_param[PARAM_NB].clock_rate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000385 attr->avg_bps = spx_factory.speex_param[PARAM_NB].bitrate;
386
Benny Prijonoa837c302006-04-27 22:36:40 +0000387 } else if (id->clock_rate <= 16000) {
388 attr->clock_rate = spx_factory.speex_param[PARAM_WB].clock_rate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000389 attr->avg_bps = spx_factory.speex_param[PARAM_WB].bitrate;
390
391 } else {
392 /* Wow.. somebody is doing ultra-wideband. Cool...! */
Benny Prijonoa837c302006-04-27 22:36:40 +0000393 attr->clock_rate = spx_factory.speex_param[PARAM_UWB].clock_rate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000394 attr->avg_bps = spx_factory.speex_param[PARAM_UWB].bitrate;
395 }
396
397 attr->pcm_bits_per_sample = 16;
398 attr->ptime = 20;
399 attr->pt = id->pt;
400
401 /* Default flags. */
Benny Prijonoa837c302006-04-27 22:36:40 +0000402 attr->cng = 1;
403 attr->concl = 1;
404 attr->hpf = 1;
405 attr->lpf =1 ;
406 attr->penh =1 ;
Benny Prijono7bd15132006-03-06 13:32:57 +0000407
408 /* Default, set VAD off as it caused voice chip off */
Benny Prijonoa837c302006-04-27 22:36:40 +0000409 attr->vad = 0;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000410
411 return PJ_SUCCESS;
412}
413
414/*
415 * Enum codecs supported by this factory (i.e. only Speex!).
416 */
417static pj_status_t spx_enum_codecs(pjmedia_codec_factory *factory,
418 unsigned *count,
419 pjmedia_codec_info codecs[])
420{
421 unsigned max;
422 int i; /* Must be signed */
423
424 PJ_UNUSED_ARG(factory);
425 PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
426
427 max = *count;
428 *count = 0;
429
430 /*
431 * We return three codecs here, and in this order:
432 * - ultra-wideband, wideband, and narrowband.
433 */
434 for (i=PJ_ARRAY_SIZE(spx_factory.speex_param)-1; i>=0 && *count<max; --i) {
435
436 if (!spx_factory.speex_param[i].enabled)
437 continue;
438
439 pj_memset(&codecs[*count], 0, sizeof(pjmedia_codec_info));
440 codecs[*count].encoding_name = pj_str("speex");
441 codecs[*count].pt = spx_factory.speex_param[i].pt;
442 codecs[*count].type = PJMEDIA_TYPE_AUDIO;
Benny Prijonoa837c302006-04-27 22:36:40 +0000443 codecs[*count].clock_rate = spx_factory.speex_param[i].clock_rate;
444 codecs[*count].channel_cnt = 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000445
446 ++*count;
447 }
448
449 return PJ_SUCCESS;
450}
451
452/*
453 * Allocate a new Speex codec instance.
454 */
455static pj_status_t spx_alloc_codec( pjmedia_codec_factory *factory,
456 const pjmedia_codec_info *id,
457 pjmedia_codec **p_codec)
458{
459 pjmedia_codec *codec;
460 struct spx_private *spx;
461
462 PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
463 PJ_ASSERT_RETURN(factory == &spx_factory.base, PJ_EINVAL);
464
465
466 pj_mutex_lock(spx_factory.mutex);
467
468 /* Get free nodes, if any. */
469 if (!pj_list_empty(&spx_factory.codec_list)) {
470 codec = spx_factory.codec_list.next;
471 pj_list_erase(codec);
472 } else {
473 codec = pj_pool_zalloc(spx_factory.pool,
474 sizeof(pjmedia_codec));
475 PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM);
476 codec->op = &spx_op;
477 codec->factory = factory;
478 codec->codec_data = pj_pool_alloc(spx_factory.pool,
479 sizeof(struct spx_private));
480 }
481
482 pj_mutex_unlock(spx_factory.mutex);
483
484 spx = (struct spx_private*) codec->codec_data;
485 spx->enc = NULL;
486 spx->dec = NULL;
487
Benny Prijonoa837c302006-04-27 22:36:40 +0000488 if (id->clock_rate <= 8000)
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000489 spx->param_id = PARAM_NB;
Benny Prijonoa837c302006-04-27 22:36:40 +0000490 else if (id->clock_rate <= 16000)
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000491 spx->param_id = PARAM_WB;
492 else
493 spx->param_id = PARAM_UWB;
494
495 *p_codec = codec;
496 return PJ_SUCCESS;
497}
498
499/*
500 * Free codec.
501 */
502static pj_status_t spx_dealloc_codec( pjmedia_codec_factory *factory,
503 pjmedia_codec *codec )
504{
505 struct spx_private *spx;
506
507 PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
508 PJ_ASSERT_RETURN(factory == &spx_factory.base, PJ_EINVAL);
509
510 /* Close codec, if it's not closed. */
511 spx = (struct spx_private*) codec->codec_data;
512 if (spx->enc != NULL || spx->dec != NULL) {
513 spx_codec_close(codec);
514 }
515
516 /* Put in the free list. */
517 pj_mutex_lock(spx_factory.mutex);
518 pj_list_push_front(&spx_factory.codec_list, codec);
519 pj_mutex_unlock(spx_factory.mutex);
520
521 return PJ_SUCCESS;
522}
523
524/*
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000525 * Init codec.
526 */
527static pj_status_t spx_codec_init( pjmedia_codec *codec,
528 pj_pool_t *pool )
529{
530 PJ_UNUSED_ARG(codec);
531 PJ_UNUSED_ARG(pool);
532 return PJ_SUCCESS;
533}
534
535/*
536 * Open codec.
537 */
538static pj_status_t spx_codec_open( pjmedia_codec *codec,
539 pjmedia_codec_param *attr )
540{
541 struct spx_private *spx;
542 int id, tmp;
543
544 spx = (struct spx_private*) codec->codec_data;
545 id = spx->param_id;
546
547 /*
548 * Create and initialize encoder.
549 */
550 spx->enc = speex_encoder_init(spx_factory.speex_param[id].mode);
551 if (!spx->enc)
552 return PJMEDIA_CODEC_EFAILED;
553 speex_bits_init(&spx->enc_bits);
554
555 /* Set the quality*/
Benny Prijono7bd15132006-03-06 13:32:57 +0000556 if (spx_factory.speex_param[id].quality != -1) {
557 speex_encoder_ctl(spx->enc, SPEEX_SET_QUALITY,
558 &spx_factory.speex_param[id].quality);
559 }
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000560
561 /* Sampling rate. */
Benny Prijonoa837c302006-04-27 22:36:40 +0000562 tmp = attr->clock_rate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000563 speex_encoder_ctl(spx->enc, SPEEX_SET_SAMPLING_RATE,
564 &spx_factory.speex_param[id].clock_rate);
565
566 /* VAD */
Benny Prijonoa837c302006-04-27 22:36:40 +0000567 tmp = attr->vad;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000568 speex_encoder_ctl(spx->enc, SPEEX_SET_VAD, &tmp);
569
570 /* Complexity */
Benny Prijono7bd15132006-03-06 13:32:57 +0000571 if (spx_factory.speex_param[id].complexity != -1) {
572 speex_encoder_ctl(spx->enc, SPEEX_SET_COMPLEXITY,
573 &spx_factory.speex_param[id].complexity);
574 }
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000575
576 /*
577 * Create and initialize decoder.
578 */
579 spx->dec = speex_decoder_init(spx_factory.speex_param[id].mode);
580 if (!spx->dec) {
581 spx_codec_close(codec);
582 return PJMEDIA_CODEC_EFAILED;
583 }
584 speex_bits_init(&spx->dec_bits);
585
586 /* Sampling rate. */
587 speex_decoder_ctl(spx->dec, SPEEX_SET_SAMPLING_RATE,
588 &spx_factory.speex_param[id].clock_rate);
589
590 /* PENH */
Benny Prijonoa837c302006-04-27 22:36:40 +0000591 tmp = attr->penh;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000592 speex_decoder_ctl(spx->dec, SPEEX_SET_ENH, &tmp);
593
594 return PJ_SUCCESS;
595}
596
597/*
598 * Close codec.
599 */
600static pj_status_t spx_codec_close( pjmedia_codec *codec )
601{
602 struct spx_private *spx;
603
604 spx = (struct spx_private*) codec->codec_data;
605
606 /* Destroy encoder*/
607 if (spx->enc) {
608 speex_encoder_destroy( spx->enc );
609 spx->enc = NULL;
610 speex_bits_destroy( &spx->enc_bits );
611 }
612
613 /* Destroy decoder */
614 if (spx->dec) {
615 speex_decoder_destroy( spx->dec);
616 spx->dec = NULL;
617 speex_bits_destroy( &spx->dec_bits );
618 }
619
620 return PJ_SUCCESS;
621}
622
623
624/*
625 * Get frames in the packet.
626 */
627static pj_status_t spx_codec_get_frames( pjmedia_codec *codec,
628 void *pkt,
629 pj_size_t pkt_size,
630 unsigned *frame_cnt,
631 pjmedia_frame frames[])
632{
633 struct spx_private *spx;
634 unsigned speex_frame_size;
635 unsigned count;
636
637 spx = (struct spx_private*) codec->codec_data;
638
639 speex_frame_size = spx_factory.speex_param[spx->param_id].framesize;
640
641 /* Don't really know how to do this... */
642 count = 0;
643 while (pkt_size >= speex_frame_size && count < *frame_cnt) {
644 frames[count].buf = pkt;
645 frames[count].size = speex_frame_size;
646 frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
647 frames[count].timestamp.u64 = 0;
648
649 pkt_size -= speex_frame_size;
650 ++count;
651 pkt = ((char*)pkt) + speex_frame_size;
652 }
653
654 if (pkt_size && count < *frame_cnt) {
655 frames[count].buf = pkt;
656 frames[count].size = pkt_size;
657 frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
658 frames[count].timestamp.u64 = 0;
659 ++count;
660 }
661
662 *frame_cnt = count;
663 return PJ_SUCCESS;
664}
665
666/*
667 * Encode frame.
668 */
669static pj_status_t spx_codec_encode( pjmedia_codec *codec,
670 const struct pjmedia_frame *input,
671 unsigned output_buf_len,
672 struct pjmedia_frame *output)
673{
674 struct spx_private *spx;
Benny Prijono856ebc32006-03-05 11:51:10 +0000675 float tmp[642]; /* 20ms at 32KHz + 2 */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000676 pj_int16_t *samp_in;
Benny Prijono856ebc32006-03-05 11:51:10 +0000677 unsigned i, samp_count, sz;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000678
679 spx = (struct spx_private*) codec->codec_data;
680
681 if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) {
682 output->size = 0;
683 output->buf = NULL;
684 output->timestamp = input->timestamp;
685 output->type = input->type;
686 return PJ_SUCCESS;
687 }
688
689 /* Copy frame to float buffer. */
690 samp_count = input->size / 2;
Benny Prijono856ebc32006-03-05 11:51:10 +0000691 pj_assert(samp_count <= PJ_ARRAY_SIZE(tmp));
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000692 samp_in = input->buf;
693 for (i=0; i<samp_count; ++i) {
694 tmp[i] = samp_in[i];
695 }
696
697 /* Flush all the bits in the struct so we can encode a new frame */
698 speex_bits_reset(&spx->enc_bits);
699
700 /* Encode the frame */
701 speex_encode(spx->enc, tmp, &spx->enc_bits);
702
Benny Prijono856ebc32006-03-05 11:51:10 +0000703 /* Check size. */
704 sz = speex_bits_nbytes(&spx->enc_bits);
705 pj_assert(sz <= output_buf_len);
706
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000707 /* Copy the bits to an array of char that can be written */
708 output->size = speex_bits_write(&spx->enc_bits,
709 output->buf, output_buf_len);
710 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
711 output->timestamp = input->timestamp;
712
713 return PJ_SUCCESS;
714}
715
716/*
717 * Decode frame.
718 */
719static pj_status_t spx_codec_decode( pjmedia_codec *codec,
720 const struct pjmedia_frame *input,
721 unsigned output_buf_len,
722 struct pjmedia_frame *output)
723{
724 struct spx_private *spx;
Benny Prijono856ebc32006-03-05 11:51:10 +0000725 float tmp[642]; /* 20ms at 32KHz + 2 */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000726 pj_int16_t *dst_buf;
Benny Prijono856ebc32006-03-05 11:51:10 +0000727 unsigned i, count, sz;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000728
729 spx = (struct spx_private*) codec->codec_data;
730
731 if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) {
732 pj_memset(output->buf, 0, output_buf_len);
733 output->size = 320;
734 output->timestamp.u64 = input->timestamp.u64;
735 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
736 return PJ_SUCCESS;
737 }
738
739 /* Initialization of the structure that holds the bits */
740 speex_bits_init(&spx->dec_bits);
741
742 /* Copy the data into the bit-stream struct */
743 speex_bits_read_from(&spx->dec_bits, input->buf, input->size);
744
745 /* Decode the data */
746 speex_decode(spx->dec, &spx->dec_bits, tmp);
747
Benny Prijono856ebc32006-03-05 11:51:10 +0000748 /* Check size. */
749 sz = speex_bits_nbytes(&spx->enc_bits);
750 pj_assert(sz <= output_buf_len);
751
752 /* Copy from float to short samples. */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000753 count = spx_factory.speex_param[spx->param_id].clock_rate * 20 / 1000;
Benny Prijono856ebc32006-03-05 11:51:10 +0000754 pj_assert((count <= output_buf_len/2) && count <= PJ_ARRAY_SIZE(tmp));
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000755 dst_buf = output->buf;
756 for (i=0; i<count; ++i) {
757 dst_buf[i] = (pj_int16_t)tmp[i];
758 }
759 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
760 output->size = count * 2;
761 output->timestamp.u64 = input->timestamp.u64;
762
763
764 return PJ_SUCCESS;
765}
Benny Prijono4381efe2006-03-16 14:24:26 +0000766
767
768#endif /* PJMEDIA_HAS_SPEEX_CODEC */