blob: 8625e72b94543bbcca70a919c959fcf84bd93cfb [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 */
Benny Prijonoa4cc47b2006-06-01 11:39:48 +0000211 if (quality <= 0) quality = DEFAULT_QUALITY;
212 if (complexity <= 0) complexity = DEFAULT_COMPLEXITY;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000213
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
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000313 pj_mutex_lock(spx_factory.mutex);
Benny Prijonoab082f32006-11-19 20:28:34 +0000314
315 /* We don't want to deinit if there's outstanding codec. */
316 /* This is silly, as we'll always have codec in the list if
317 we ever allocate a codec! A better behavior maybe is to
318 deallocate all codecs in the list.
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000319 if (!pj_list_empty(&spx_factory.codec_list)) {
320 pj_mutex_unlock(spx_factory.mutex);
321 return PJ_EBUSY;
322 }
Benny Prijonoab082f32006-11-19 20:28:34 +0000323 */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000324
325 /* Get the codec manager. */
326 codec_mgr = pjmedia_endpt_get_codec_mgr(spx_factory.endpt);
327 if (!codec_mgr) {
328 pj_pool_release(spx_factory.pool);
329 spx_factory.pool = NULL;
330 return PJ_EINVALIDOP;
331 }
332
333 /* Unregister Speex codec factory. */
334 status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
335 &spx_factory.base);
336
337 /* Destroy mutex. */
338 pj_mutex_destroy(spx_factory.mutex);
339
340 /* Destroy pool. */
341 pj_pool_release(spx_factory.pool);
342 spx_factory.pool = NULL;
343
344 return status;
345}
346
347/*
348 * Check if factory can allocate the specified codec.
349 */
350static pj_status_t spx_test_alloc( pjmedia_codec_factory *factory,
351 const pjmedia_codec_info *info )
352{
353 const pj_str_t speex_tag = { "speex", 5};
354 unsigned i;
355
356 PJ_UNUSED_ARG(factory);
357
358 /* Type MUST be audio. */
359 if (info->type != PJMEDIA_TYPE_AUDIO)
360 return PJMEDIA_CODEC_EUNSUP;
361
362 /* Check encoding name. */
363 if (pj_stricmp(&info->encoding_name, &speex_tag) != 0)
364 return PJMEDIA_CODEC_EUNSUP;
365
366 /* Check clock-rate */
367 for (i=0; i<PJ_ARRAY_SIZE(spx_factory.speex_param); ++i) {
Benny Prijonoa837c302006-04-27 22:36:40 +0000368 if (info->clock_rate == spx_factory.speex_param[i].clock_rate) {
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000369 /* Okay, let's Speex! */
370 return PJ_SUCCESS;
371 }
372 }
373
374
375 /* Unsupported, or mode is disabled. */
376 return PJMEDIA_CODEC_EUNSUP;
377}
378
379/*
380 * Generate default attribute.
381 */
382static pj_status_t spx_default_attr (pjmedia_codec_factory *factory,
383 const pjmedia_codec_info *id,
384 pjmedia_codec_param *attr )
385{
386
387 PJ_ASSERT_RETURN(factory==&spx_factory.base, PJ_EINVAL);
388
Benny Prijonoac623b32006-07-03 15:19:31 +0000389 pj_bzero(attr, sizeof(pjmedia_codec_param));
Benny Prijono8befd9f2006-05-13 22:46:23 +0000390 attr->info.pt = (pj_uint8_t)id->pt;
391 attr->info.channel_cnt = 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000392
Benny Prijonoa837c302006-04-27 22:36:40 +0000393 if (id->clock_rate <= 8000) {
Benny Prijono8befd9f2006-05-13 22:46:23 +0000394 attr->info.clock_rate = spx_factory.speex_param[PARAM_NB].clock_rate;
395 attr->info.avg_bps = spx_factory.speex_param[PARAM_NB].bitrate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000396
Benny Prijonoa837c302006-04-27 22:36:40 +0000397 } else if (id->clock_rate <= 16000) {
Benny Prijono8befd9f2006-05-13 22:46:23 +0000398 attr->info.clock_rate = spx_factory.speex_param[PARAM_WB].clock_rate;
399 attr->info.avg_bps = spx_factory.speex_param[PARAM_WB].bitrate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000400
401 } else {
402 /* Wow.. somebody is doing ultra-wideband. Cool...! */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000403 attr->info.clock_rate = spx_factory.speex_param[PARAM_UWB].clock_rate;
404 attr->info.avg_bps = spx_factory.speex_param[PARAM_UWB].bitrate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000405 }
406
Benny Prijono8befd9f2006-05-13 22:46:23 +0000407 attr->info.pcm_bits_per_sample = 16;
408 attr->info.frm_ptime = 20;
409 attr->info.pt = (pj_uint8_t)id->pt;
410
411 attr->setting.frm_per_pkt = 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000412
413 /* Default flags. */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000414 attr->setting.cng = 1;
415 attr->setting.plc = 1;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000416 attr->setting.penh =1 ;
Benny Prijono70c68912006-05-19 15:58:13 +0000417 attr->setting.vad = 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000418
419 return PJ_SUCCESS;
420}
421
422/*
423 * Enum codecs supported by this factory (i.e. only Speex!).
424 */
425static pj_status_t spx_enum_codecs(pjmedia_codec_factory *factory,
426 unsigned *count,
427 pjmedia_codec_info codecs[])
428{
429 unsigned max;
430 int i; /* Must be signed */
431
432 PJ_UNUSED_ARG(factory);
433 PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
434
435 max = *count;
436 *count = 0;
437
438 /*
439 * We return three codecs here, and in this order:
440 * - ultra-wideband, wideband, and narrowband.
441 */
442 for (i=PJ_ARRAY_SIZE(spx_factory.speex_param)-1; i>=0 && *count<max; --i) {
443
444 if (!spx_factory.speex_param[i].enabled)
445 continue;
446
Benny Prijonoac623b32006-07-03 15:19:31 +0000447 pj_bzero(&codecs[*count], sizeof(pjmedia_codec_info));
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000448 codecs[*count].encoding_name = pj_str("speex");
449 codecs[*count].pt = spx_factory.speex_param[i].pt;
450 codecs[*count].type = PJMEDIA_TYPE_AUDIO;
Benny Prijonoa837c302006-04-27 22:36:40 +0000451 codecs[*count].clock_rate = spx_factory.speex_param[i].clock_rate;
452 codecs[*count].channel_cnt = 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000453
454 ++*count;
455 }
456
457 return PJ_SUCCESS;
458}
459
460/*
461 * Allocate a new Speex codec instance.
462 */
463static pj_status_t spx_alloc_codec( pjmedia_codec_factory *factory,
464 const pjmedia_codec_info *id,
465 pjmedia_codec **p_codec)
466{
467 pjmedia_codec *codec;
468 struct spx_private *spx;
469
470 PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
471 PJ_ASSERT_RETURN(factory == &spx_factory.base, PJ_EINVAL);
472
473
474 pj_mutex_lock(spx_factory.mutex);
475
476 /* Get free nodes, if any. */
477 if (!pj_list_empty(&spx_factory.codec_list)) {
478 codec = spx_factory.codec_list.next;
479 pj_list_erase(codec);
480 } else {
481 codec = pj_pool_zalloc(spx_factory.pool,
482 sizeof(pjmedia_codec));
483 PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM);
484 codec->op = &spx_op;
485 codec->factory = factory;
486 codec->codec_data = pj_pool_alloc(spx_factory.pool,
487 sizeof(struct spx_private));
488 }
489
490 pj_mutex_unlock(spx_factory.mutex);
491
492 spx = (struct spx_private*) codec->codec_data;
493 spx->enc = NULL;
494 spx->dec = NULL;
495
Benny Prijonoa837c302006-04-27 22:36:40 +0000496 if (id->clock_rate <= 8000)
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000497 spx->param_id = PARAM_NB;
Benny Prijonoa837c302006-04-27 22:36:40 +0000498 else if (id->clock_rate <= 16000)
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000499 spx->param_id = PARAM_WB;
500 else
501 spx->param_id = PARAM_UWB;
502
503 *p_codec = codec;
504 return PJ_SUCCESS;
505}
506
507/*
508 * Free codec.
509 */
510static pj_status_t spx_dealloc_codec( pjmedia_codec_factory *factory,
511 pjmedia_codec *codec )
512{
513 struct spx_private *spx;
514
515 PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
516 PJ_ASSERT_RETURN(factory == &spx_factory.base, PJ_EINVAL);
517
518 /* Close codec, if it's not closed. */
519 spx = (struct spx_private*) codec->codec_data;
520 if (spx->enc != NULL || spx->dec != NULL) {
521 spx_codec_close(codec);
522 }
523
524 /* Put in the free list. */
525 pj_mutex_lock(spx_factory.mutex);
526 pj_list_push_front(&spx_factory.codec_list, codec);
527 pj_mutex_unlock(spx_factory.mutex);
528
529 return PJ_SUCCESS;
530}
531
532/*
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000533 * Init codec.
534 */
535static pj_status_t spx_codec_init( pjmedia_codec *codec,
536 pj_pool_t *pool )
537{
538 PJ_UNUSED_ARG(codec);
539 PJ_UNUSED_ARG(pool);
540 return PJ_SUCCESS;
541}
542
543/*
544 * Open codec.
545 */
546static pj_status_t spx_codec_open( pjmedia_codec *codec,
547 pjmedia_codec_param *attr )
548{
549 struct spx_private *spx;
550 int id, tmp;
551
552 spx = (struct spx_private*) codec->codec_data;
553 id = spx->param_id;
554
555 /*
556 * Create and initialize encoder.
557 */
558 spx->enc = speex_encoder_init(spx_factory.speex_param[id].mode);
559 if (!spx->enc)
560 return PJMEDIA_CODEC_EFAILED;
561 speex_bits_init(&spx->enc_bits);
562
563 /* Set the quality*/
Benny Prijono7bd15132006-03-06 13:32:57 +0000564 if (spx_factory.speex_param[id].quality != -1) {
565 speex_encoder_ctl(spx->enc, SPEEX_SET_QUALITY,
566 &spx_factory.speex_param[id].quality);
567 }
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000568
569 /* Sampling rate. */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000570 tmp = attr->info.clock_rate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000571 speex_encoder_ctl(spx->enc, SPEEX_SET_SAMPLING_RATE,
572 &spx_factory.speex_param[id].clock_rate);
573
574 /* VAD */
Benny Prijono70c68912006-05-19 15:58:13 +0000575 tmp = (attr->setting.vad != 0);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000576 speex_encoder_ctl(spx->enc, SPEEX_SET_VAD, &tmp);
Benny Prijono70c68912006-05-19 15:58:13 +0000577 speex_encoder_ctl(spx->enc, SPEEX_SET_DTX, &tmp);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000578
579 /* Complexity */
Benny Prijono7bd15132006-03-06 13:32:57 +0000580 if (spx_factory.speex_param[id].complexity != -1) {
581 speex_encoder_ctl(spx->enc, SPEEX_SET_COMPLEXITY,
582 &spx_factory.speex_param[id].complexity);
583 }
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000584
585 /*
586 * Create and initialize decoder.
587 */
588 spx->dec = speex_decoder_init(spx_factory.speex_param[id].mode);
589 if (!spx->dec) {
590 spx_codec_close(codec);
591 return PJMEDIA_CODEC_EFAILED;
592 }
593 speex_bits_init(&spx->dec_bits);
594
595 /* Sampling rate. */
596 speex_decoder_ctl(spx->dec, SPEEX_SET_SAMPLING_RATE,
597 &spx_factory.speex_param[id].clock_rate);
598
599 /* PENH */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000600 tmp = attr->setting.penh;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000601 speex_decoder_ctl(spx->dec, SPEEX_SET_ENH, &tmp);
602
603 return PJ_SUCCESS;
604}
605
606/*
607 * Close codec.
608 */
609static pj_status_t spx_codec_close( pjmedia_codec *codec )
610{
611 struct spx_private *spx;
612
613 spx = (struct spx_private*) codec->codec_data;
614
615 /* Destroy encoder*/
616 if (spx->enc) {
617 speex_encoder_destroy( spx->enc );
618 spx->enc = NULL;
619 speex_bits_destroy( &spx->enc_bits );
620 }
621
622 /* Destroy decoder */
623 if (spx->dec) {
624 speex_decoder_destroy( spx->dec);
625 spx->dec = NULL;
626 speex_bits_destroy( &spx->dec_bits );
627 }
628
629 return PJ_SUCCESS;
630}
631
632
633/*
634 * Get frames in the packet.
635 */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000636static pj_status_t spx_codec_parse( pjmedia_codec *codec,
637 void *pkt,
638 pj_size_t pkt_size,
639 const pj_timestamp *ts,
640 unsigned *frame_cnt,
641 pjmedia_frame frames[])
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000642{
643 struct spx_private *spx;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000644 unsigned frame_size, samples_per_frame;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000645 unsigned count;
646
647 spx = (struct spx_private*) codec->codec_data;
648
Benny Prijono8befd9f2006-05-13 22:46:23 +0000649 frame_size = spx_factory.speex_param[spx->param_id].framesize;
650 samples_per_frame = spx_factory.speex_param[spx->param_id].samples_per_frame;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000651
652 /* Don't really know how to do this... */
653 count = 0;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000654 while (pkt_size >= frame_size && count < *frame_cnt) {
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000655 frames[count].buf = pkt;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000656 frames[count].size = frame_size;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000657 frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000658 frames[count].timestamp.u64 = ts->u64 + count * samples_per_frame;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000659
Benny Prijono8befd9f2006-05-13 22:46:23 +0000660 pkt_size -= frame_size;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000661 ++count;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000662 pkt = ((char*)pkt) + frame_size;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000663 }
664
Benny Prijono8befd9f2006-05-13 22:46:23 +0000665 /* Just in case speex has silence frame which size is less than normal
666 * frame size...
667 */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000668 if (pkt_size && count < *frame_cnt) {
669 frames[count].buf = pkt;
670 frames[count].size = pkt_size;
671 frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000672 frames[count].timestamp.u64 = ts->u64 + count * samples_per_frame;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000673 ++count;
674 }
675
676 *frame_cnt = count;
677 return PJ_SUCCESS;
678}
679
680/*
681 * Encode frame.
682 */
683static pj_status_t spx_codec_encode( pjmedia_codec *codec,
684 const struct pjmedia_frame *input,
685 unsigned output_buf_len,
686 struct pjmedia_frame *output)
687{
688 struct spx_private *spx;
Benny Prijonodc498ca2006-07-26 17:04:54 +0000689 unsigned sz;
Benny Prijono70c68912006-05-19 15:58:13 +0000690 int tx;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000691
692 spx = (struct spx_private*) codec->codec_data;
693
694 if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) {
695 output->size = 0;
696 output->buf = NULL;
697 output->timestamp = input->timestamp;
698 output->type = input->type;
699 return PJ_SUCCESS;
700 }
701
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000702 /* Flush all the bits in the struct so we can encode a new frame */
703 speex_bits_reset(&spx->enc_bits);
704
705 /* Encode the frame */
Benny Prijonodc498ca2006-07-26 17:04:54 +0000706 tx = speex_encode_int(spx->enc, input->buf, &spx->enc_bits);
Benny Prijono70c68912006-05-19 15:58:13 +0000707
708 /* Check if we need not to transmit the frame (DTX) */
709 if (tx == 0) {
710 output->buf = NULL;
711 output->size = 0;
712 output->timestamp.u64 = input->timestamp.u64;
713 output->type = PJMEDIA_FRAME_TYPE_NONE;
714 return PJ_SUCCESS;
715 }
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000716
Benny Prijono856ebc32006-03-05 11:51:10 +0000717 /* Check size. */
718 sz = speex_bits_nbytes(&spx->enc_bits);
719 pj_assert(sz <= output_buf_len);
720
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000721 /* Copy the bits to an array of char that can be written */
722 output->size = speex_bits_write(&spx->enc_bits,
723 output->buf, output_buf_len);
724 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
725 output->timestamp = input->timestamp;
726
727 return PJ_SUCCESS;
728}
729
730/*
731 * Decode frame.
732 */
733static pj_status_t spx_codec_decode( pjmedia_codec *codec,
734 const struct pjmedia_frame *input,
735 unsigned output_buf_len,
736 struct pjmedia_frame *output)
737{
738 struct spx_private *spx;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000739
740 spx = (struct spx_private*) codec->codec_data;
741
742 if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) {
Benny Prijonoac623b32006-07-03 15:19:31 +0000743 pj_bzero(output->buf, output_buf_len);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000744 output->size = 320;
745 output->timestamp.u64 = input->timestamp.u64;
746 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
747 return PJ_SUCCESS;
748 }
749
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000750 /* Copy the data into the bit-stream struct */
751 speex_bits_read_from(&spx->dec_bits, input->buf, input->size);
752
753 /* Decode the data */
Benny Prijonodc498ca2006-07-26 17:04:54 +0000754 speex_decode_int(spx->dec, &spx->dec_bits, output->buf);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000755
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000756 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
Benny Prijonodc498ca2006-07-26 17:04:54 +0000757 output->size = speex_bits_nbytes(&spx->dec_bits);
758 pj_assert(output->size <= (int)output_buf_len);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000759 output->timestamp.u64 = input->timestamp.u64;
760
761
762 return PJ_SUCCESS;
763}
Benny Prijono4381efe2006-03-16 14:24:26 +0000764
Benny Prijono8befd9f2006-05-13 22:46:23 +0000765/*
766 * Recover lost frame.
767 */
768static pj_status_t spx_codec_recover(pjmedia_codec *codec,
769 unsigned output_buf_len,
770 struct pjmedia_frame *output)
771{
772 struct spx_private *spx;
Benny Prijonodc498ca2006-07-26 17:04:54 +0000773 unsigned count;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000774
Benny Prijono60281662006-06-22 18:39:33 +0000775 /* output_buf_len is unreferenced when building in Release mode */
776 PJ_UNUSED_ARG(output_buf_len);
777
Benny Prijono8befd9f2006-05-13 22:46:23 +0000778 spx = (struct spx_private*) codec->codec_data;
779
780 count = spx_factory.speex_param[spx->param_id].clock_rate * 20 / 1000;
Benny Prijonodc498ca2006-07-26 17:04:54 +0000781 pj_assert(count <= output_buf_len/2);
Benny Prijono8befd9f2006-05-13 22:46:23 +0000782
783 /* Recover packet loss */
Benny Prijonodc498ca2006-07-26 17:04:54 +0000784 speex_decode_int(spx->dec, NULL, output->buf);
Benny Prijono8befd9f2006-05-13 22:46:23 +0000785
Benny Prijono8befd9f2006-05-13 22:46:23 +0000786 output->size = count * 2;
787
788 return PJ_SUCCESS;
789}
790
Benny Prijono4381efe2006-03-16 14:24:26 +0000791
792#endif /* PJMEDIA_HAS_SPEEX_CODEC */