blob: dc2c74004534c2286859eed54eeea0078508a572 [file] [log] [blame]
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001/* $Id$ */
2/*
Benny Prijonoa771a512007-02-19 01:13:53 +00003 * Copyright (C)2003-2007 Benny Prijono <benny@prijono.org>
Benny Prijonoeb30bf52006-03-04 20:43:52 +00004 *
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 Prijonoeb30bf52006-03-04 20:43:52 +000040/* Prototypes for Speex factory */
41static pj_status_t spx_test_alloc( pjmedia_codec_factory *factory,
42 const pjmedia_codec_info *id );
43static pj_status_t spx_default_attr( pjmedia_codec_factory *factory,
44 const pjmedia_codec_info *id,
45 pjmedia_codec_param *attr );
46static pj_status_t spx_enum_codecs( pjmedia_codec_factory *factory,
47 unsigned *count,
48 pjmedia_codec_info codecs[]);
49static pj_status_t spx_alloc_codec( pjmedia_codec_factory *factory,
50 const pjmedia_codec_info *id,
51 pjmedia_codec **p_codec);
52static pj_status_t spx_dealloc_codec( pjmedia_codec_factory *factory,
53 pjmedia_codec *codec );
54
55/* Prototypes for Speex implementation. */
Benny Prijonoeb30bf52006-03-04 20:43:52 +000056static pj_status_t spx_codec_init( pjmedia_codec *codec,
57 pj_pool_t *pool );
58static pj_status_t spx_codec_open( pjmedia_codec *codec,
Benny Prijono6865a3f2006-12-30 02:46:57 +000059 pjmedia_codec_param *attr );
Benny Prijonoeb30bf52006-03-04 20:43:52 +000060static pj_status_t spx_codec_close( pjmedia_codec *codec );
Benny Prijonob94a6ab2006-12-26 21:18:11 +000061static pj_status_t spx_codec_modify(pjmedia_codec *codec,
62 const pjmedia_codec_param *attr );
Benny Prijono8befd9f2006-05-13 22:46:23 +000063static pj_status_t spx_codec_parse( pjmedia_codec *codec,
64 void *pkt,
65 pj_size_t pkt_size,
66 const pj_timestamp *ts,
67 unsigned *frame_cnt,
68 pjmedia_frame frames[]);
Benny Prijonoeb30bf52006-03-04 20:43:52 +000069static 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);
Benny Prijono8befd9f2006-05-13 22:46:23 +000077static pj_status_t spx_codec_recover(pjmedia_codec *codec,
78 unsigned output_buf_len,
79 struct pjmedia_frame *output);
Benny Prijonoeb30bf52006-03-04 20:43:52 +000080
81/* Definition for Speex codec operations. */
82static pjmedia_codec_op spx_op =
83{
Benny Prijonoeb30bf52006-03-04 20:43:52 +000084 &spx_codec_init,
85 &spx_codec_open,
86 &spx_codec_close,
Benny Prijonob94a6ab2006-12-26 21:18:11 +000087 &spx_codec_modify,
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 */
Nanang Izzuddin829ac022008-05-27 00:24:26 +0000211 if (quality < 0) quality = PJMEDIA_CODEC_SPEEX_DEFAULT_QUALITY;
212 if (complexity < 0) complexity = PJMEDIA_CODEC_SPEEX_DEFAULT_COMPLEXITY;
213
214 /* Validate quality & complexity */
215 PJ_ASSERT_RETURN(quality >= 0 && quality <= 10, PJ_EINVAL);
216 PJ_ASSERT_RETURN(complexity >= 1 && complexity <= 10, PJ_EINVAL);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000217
218 /* Create Speex codec factory. */
219 spx_factory.base.op = &spx_factory_op;
220 spx_factory.base.factory_data = NULL;
221 spx_factory.endpt = endpt;
222
223 spx_factory.pool = pjmedia_endpt_create_pool(endpt, "speex",
224 4000, 4000);
225 if (!spx_factory.pool)
226 return PJ_ENOMEM;
227
228 pj_list_init(&spx_factory.codec_list);
229
230 /* Create mutex. */
231 status = pj_mutex_create_simple(spx_factory.pool, "speex",
232 &spx_factory.mutex);
233 if (status != PJ_SUCCESS)
234 goto on_error;
235
236 /* Initialize default Speex parameter. */
237 spx_factory.speex_param[PARAM_NB].enabled =
238 ((options & PJMEDIA_SPEEX_NO_NB) == 0);
Benny Prijonoa837c302006-04-27 22:36:40 +0000239 spx_factory.speex_param[PARAM_NB].pt = PJMEDIA_RTP_PT_SPEEX_NB;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000240 spx_factory.speex_param[PARAM_NB].mode = &speex_nb_mode;
241 spx_factory.speex_param[PARAM_NB].clock_rate = 8000;
242 spx_factory.speex_param[PARAM_NB].quality = quality;
243 spx_factory.speex_param[PARAM_NB].complexity = complexity;
244
245 spx_factory.speex_param[PARAM_WB].enabled =
246 ((options & PJMEDIA_SPEEX_NO_WB) == 0);
Benny Prijonoa837c302006-04-27 22:36:40 +0000247 spx_factory.speex_param[PARAM_WB].pt = PJMEDIA_RTP_PT_SPEEX_WB;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000248 spx_factory.speex_param[PARAM_WB].mode = &speex_wb_mode;
249 spx_factory.speex_param[PARAM_WB].clock_rate = 16000;
250 spx_factory.speex_param[PARAM_WB].quality = quality;
251 spx_factory.speex_param[PARAM_WB].complexity = complexity;
252
253 spx_factory.speex_param[PARAM_UWB].enabled =
254 ((options & PJMEDIA_SPEEX_NO_UWB) == 0);
Benny Prijonoa837c302006-04-27 22:36:40 +0000255 spx_factory.speex_param[PARAM_UWB].pt = PJMEDIA_RTP_PT_SPEEX_UWB;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000256 spx_factory.speex_param[PARAM_UWB].mode = &speex_uwb_mode;
257 spx_factory.speex_param[PARAM_UWB].clock_rate = 32000;
258 spx_factory.speex_param[PARAM_UWB].quality = quality;
259 spx_factory.speex_param[PARAM_UWB].complexity = complexity;
260
Benny Prijonof6aba4e2006-03-06 16:24:50 +0000261 /* Somehow quality <=4 is broken in linux. */
Benny Prijono70c68912006-05-19 15:58:13 +0000262 if (quality <= 4 && quality >= 0) {
263 PJ_LOG(5,(THIS_FILE, "Adjusting quality to 5 for uwb"));
Benny Prijonof6aba4e2006-03-06 16:24:50 +0000264 spx_factory.speex_param[PARAM_UWB].quality = 5;
265 }
266
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000267 /* Get codec framesize and avg bitrate for each mode. */
268 for (i=0; i<PJ_ARRAY_SIZE(spx_factory.speex_param); ++i) {
269 status = get_speex_info(&spx_factory.speex_param[i]);
270 }
271
272 /* Get the codec manager. */
273 codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
274 if (!codec_mgr) {
275 status = PJ_EINVALIDOP;
276 goto on_error;
277 }
278
279 /* Register codec factory to endpoint. */
280 status = pjmedia_codec_mgr_register_factory(codec_mgr,
281 &spx_factory.base);
282 if (status != PJ_SUCCESS)
283 goto on_error;
284
285 /* Done. */
286 return PJ_SUCCESS;
287
288on_error:
289 pj_pool_release(spx_factory.pool);
290 spx_factory.pool = NULL;
291 return status;
292}
293
294
295/*
296 * Initialize with default settings.
297 */
298PJ_DEF(pj_status_t) pjmedia_codec_speex_init_default(pjmedia_endpt *endpt)
299{
300 return pjmedia_codec_speex_init(endpt, 0, -1, -1);
301}
302
303/*
Nanang Izzuddin829ac022008-05-27 00:24:26 +0000304 * Change the settings of Speex codec.
305 */
306PJ_DEF(pj_status_t) pjmedia_codec_speex_set_param(unsigned clock_rate,
307 int quality,
308 int complexity)
309{
310 unsigned i;
311
312 /* Get defaults */
313 if (quality < 0) quality = PJMEDIA_CODEC_SPEEX_DEFAULT_QUALITY;
314 if (complexity < 0) complexity = PJMEDIA_CODEC_SPEEX_DEFAULT_COMPLEXITY;
315
316 /* Validate quality & complexity */
317 PJ_ASSERT_RETURN(quality >= 0 && quality <= 10, PJ_EINVAL);
318 PJ_ASSERT_RETURN(complexity >= 1 && complexity <= 10, PJ_EINVAL);
319
320 /* Apply the settings */
321 for (i=0; i<PJ_ARRAY_SIZE(spx_factory.speex_param); ++i) {
322 if (spx_factory.speex_param[i].clock_rate == clock_rate) {
323 pj_status_t status;
324
325 spx_factory.speex_param[i].quality = quality;
326 spx_factory.speex_param[i].complexity = complexity;
327
328 /* Somehow quality<=4 is broken in linux. */
329 if (i == PARAM_UWB && quality <= 4 && quality >= 0) {
330 PJ_LOG(5,(THIS_FILE, "Adjusting quality to 5 for uwb"));
331 spx_factory.speex_param[PARAM_UWB].quality = 5;
332 }
333
334 status = get_speex_info(&spx_factory.speex_param[i]);
335
336 return status;
337 }
338 }
339
340 return PJ_EINVAL;
341}
342
343/*
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000344 * Unregister Speex codec factory from pjmedia endpoint and deinitialize
345 * the Speex codec library.
346 */
347PJ_DEF(pj_status_t) pjmedia_codec_speex_deinit(void)
348{
349 pjmedia_codec_mgr *codec_mgr;
350 pj_status_t status;
351
352 if (spx_factory.pool == NULL) {
353 /* Already deinitialized */
354 return PJ_SUCCESS;
355 }
356
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000357 pj_mutex_lock(spx_factory.mutex);
Benny Prijonoab082f32006-11-19 20:28:34 +0000358
359 /* We don't want to deinit if there's outstanding codec. */
360 /* This is silly, as we'll always have codec in the list if
361 we ever allocate a codec! A better behavior maybe is to
362 deallocate all codecs in the list.
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000363 if (!pj_list_empty(&spx_factory.codec_list)) {
364 pj_mutex_unlock(spx_factory.mutex);
365 return PJ_EBUSY;
366 }
Benny Prijonoab082f32006-11-19 20:28:34 +0000367 */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000368
369 /* Get the codec manager. */
370 codec_mgr = pjmedia_endpt_get_codec_mgr(spx_factory.endpt);
371 if (!codec_mgr) {
372 pj_pool_release(spx_factory.pool);
373 spx_factory.pool = NULL;
374 return PJ_EINVALIDOP;
375 }
376
377 /* Unregister Speex codec factory. */
378 status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
379 &spx_factory.base);
380
381 /* Destroy mutex. */
382 pj_mutex_destroy(spx_factory.mutex);
383
384 /* Destroy pool. */
385 pj_pool_release(spx_factory.pool);
386 spx_factory.pool = NULL;
387
388 return status;
389}
390
391/*
392 * Check if factory can allocate the specified codec.
393 */
394static pj_status_t spx_test_alloc( pjmedia_codec_factory *factory,
395 const pjmedia_codec_info *info )
396{
397 const pj_str_t speex_tag = { "speex", 5};
398 unsigned i;
399
400 PJ_UNUSED_ARG(factory);
401
402 /* Type MUST be audio. */
403 if (info->type != PJMEDIA_TYPE_AUDIO)
404 return PJMEDIA_CODEC_EUNSUP;
405
406 /* Check encoding name. */
407 if (pj_stricmp(&info->encoding_name, &speex_tag) != 0)
408 return PJMEDIA_CODEC_EUNSUP;
409
410 /* Check clock-rate */
411 for (i=0; i<PJ_ARRAY_SIZE(spx_factory.speex_param); ++i) {
Benny Prijonoa837c302006-04-27 22:36:40 +0000412 if (info->clock_rate == spx_factory.speex_param[i].clock_rate) {
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000413 /* Okay, let's Speex! */
414 return PJ_SUCCESS;
415 }
416 }
417
418
419 /* Unsupported, or mode is disabled. */
420 return PJMEDIA_CODEC_EUNSUP;
421}
422
423/*
424 * Generate default attribute.
425 */
426static pj_status_t spx_default_attr (pjmedia_codec_factory *factory,
427 const pjmedia_codec_info *id,
428 pjmedia_codec_param *attr )
429{
430
431 PJ_ASSERT_RETURN(factory==&spx_factory.base, PJ_EINVAL);
432
Benny Prijonoac623b32006-07-03 15:19:31 +0000433 pj_bzero(attr, sizeof(pjmedia_codec_param));
Benny Prijono8befd9f2006-05-13 22:46:23 +0000434 attr->info.pt = (pj_uint8_t)id->pt;
435 attr->info.channel_cnt = 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000436
Benny Prijonoa837c302006-04-27 22:36:40 +0000437 if (id->clock_rate <= 8000) {
Benny Prijono8befd9f2006-05-13 22:46:23 +0000438 attr->info.clock_rate = spx_factory.speex_param[PARAM_NB].clock_rate;
439 attr->info.avg_bps = spx_factory.speex_param[PARAM_NB].bitrate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000440
Benny Prijonoa837c302006-04-27 22:36:40 +0000441 } else if (id->clock_rate <= 16000) {
Benny Prijono8befd9f2006-05-13 22:46:23 +0000442 attr->info.clock_rate = spx_factory.speex_param[PARAM_WB].clock_rate;
443 attr->info.avg_bps = spx_factory.speex_param[PARAM_WB].bitrate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000444
445 } else {
446 /* Wow.. somebody is doing ultra-wideband. Cool...! */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000447 attr->info.clock_rate = spx_factory.speex_param[PARAM_UWB].clock_rate;
448 attr->info.avg_bps = spx_factory.speex_param[PARAM_UWB].bitrate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000449 }
450
Benny Prijono8befd9f2006-05-13 22:46:23 +0000451 attr->info.pcm_bits_per_sample = 16;
452 attr->info.frm_ptime = 20;
453 attr->info.pt = (pj_uint8_t)id->pt;
454
455 attr->setting.frm_per_pkt = 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000456
457 /* Default flags. */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000458 attr->setting.cng = 1;
459 attr->setting.plc = 1;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000460 attr->setting.penh =1 ;
Benny Prijono70c68912006-05-19 15:58:13 +0000461 attr->setting.vad = 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000462
463 return PJ_SUCCESS;
464}
465
466/*
467 * Enum codecs supported by this factory (i.e. only Speex!).
468 */
469static pj_status_t spx_enum_codecs(pjmedia_codec_factory *factory,
470 unsigned *count,
471 pjmedia_codec_info codecs[])
472{
473 unsigned max;
474 int i; /* Must be signed */
475
476 PJ_UNUSED_ARG(factory);
477 PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
478
479 max = *count;
480 *count = 0;
481
482 /*
483 * We return three codecs here, and in this order:
484 * - ultra-wideband, wideband, and narrowband.
485 */
486 for (i=PJ_ARRAY_SIZE(spx_factory.speex_param)-1; i>=0 && *count<max; --i) {
487
488 if (!spx_factory.speex_param[i].enabled)
489 continue;
490
Benny Prijonoac623b32006-07-03 15:19:31 +0000491 pj_bzero(&codecs[*count], sizeof(pjmedia_codec_info));
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000492 codecs[*count].encoding_name = pj_str("speex");
493 codecs[*count].pt = spx_factory.speex_param[i].pt;
494 codecs[*count].type = PJMEDIA_TYPE_AUDIO;
Benny Prijonoa837c302006-04-27 22:36:40 +0000495 codecs[*count].clock_rate = spx_factory.speex_param[i].clock_rate;
496 codecs[*count].channel_cnt = 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000497
498 ++*count;
499 }
500
501 return PJ_SUCCESS;
502}
503
504/*
505 * Allocate a new Speex codec instance.
506 */
507static pj_status_t spx_alloc_codec( pjmedia_codec_factory *factory,
508 const pjmedia_codec_info *id,
509 pjmedia_codec **p_codec)
510{
511 pjmedia_codec *codec;
512 struct spx_private *spx;
513
514 PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
515 PJ_ASSERT_RETURN(factory == &spx_factory.base, PJ_EINVAL);
516
517
518 pj_mutex_lock(spx_factory.mutex);
519
520 /* Get free nodes, if any. */
521 if (!pj_list_empty(&spx_factory.codec_list)) {
522 codec = spx_factory.codec_list.next;
523 pj_list_erase(codec);
524 } else {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000525 codec = PJ_POOL_ZALLOC_T(spx_factory.pool, pjmedia_codec);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000526 PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM);
527 codec->op = &spx_op;
528 codec->factory = factory;
529 codec->codec_data = pj_pool_alloc(spx_factory.pool,
530 sizeof(struct spx_private));
531 }
532
533 pj_mutex_unlock(spx_factory.mutex);
534
535 spx = (struct spx_private*) codec->codec_data;
536 spx->enc = NULL;
537 spx->dec = NULL;
538
Benny Prijonoa837c302006-04-27 22:36:40 +0000539 if (id->clock_rate <= 8000)
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000540 spx->param_id = PARAM_NB;
Benny Prijonoa837c302006-04-27 22:36:40 +0000541 else if (id->clock_rate <= 16000)
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000542 spx->param_id = PARAM_WB;
543 else
544 spx->param_id = PARAM_UWB;
545
546 *p_codec = codec;
547 return PJ_SUCCESS;
548}
549
550/*
551 * Free codec.
552 */
553static pj_status_t spx_dealloc_codec( pjmedia_codec_factory *factory,
554 pjmedia_codec *codec )
555{
556 struct spx_private *spx;
557
558 PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
559 PJ_ASSERT_RETURN(factory == &spx_factory.base, PJ_EINVAL);
560
561 /* Close codec, if it's not closed. */
562 spx = (struct spx_private*) codec->codec_data;
563 if (spx->enc != NULL || spx->dec != NULL) {
564 spx_codec_close(codec);
565 }
566
567 /* Put in the free list. */
568 pj_mutex_lock(spx_factory.mutex);
569 pj_list_push_front(&spx_factory.codec_list, codec);
570 pj_mutex_unlock(spx_factory.mutex);
571
572 return PJ_SUCCESS;
573}
574
575/*
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000576 * Init codec.
577 */
578static pj_status_t spx_codec_init( pjmedia_codec *codec,
579 pj_pool_t *pool )
580{
581 PJ_UNUSED_ARG(codec);
582 PJ_UNUSED_ARG(pool);
583 return PJ_SUCCESS;
584}
585
586/*
587 * Open codec.
588 */
589static pj_status_t spx_codec_open( pjmedia_codec *codec,
Benny Prijono6865a3f2006-12-30 02:46:57 +0000590 pjmedia_codec_param *attr )
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000591{
592 struct spx_private *spx;
593 int id, tmp;
594
595 spx = (struct spx_private*) codec->codec_data;
596 id = spx->param_id;
597
Benny Prijonob94a6ab2006-12-26 21:18:11 +0000598 /* Only supports one frame per packet */
599 PJ_ASSERT_RETURN(attr->setting.frm_per_pkt==1, PJ_EINVAL);
600
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000601 /*
602 * Create and initialize encoder.
603 */
604 spx->enc = speex_encoder_init(spx_factory.speex_param[id].mode);
605 if (!spx->enc)
606 return PJMEDIA_CODEC_EFAILED;
607 speex_bits_init(&spx->enc_bits);
608
609 /* Set the quality*/
Benny Prijono7bd15132006-03-06 13:32:57 +0000610 if (spx_factory.speex_param[id].quality != -1) {
611 speex_encoder_ctl(spx->enc, SPEEX_SET_QUALITY,
612 &spx_factory.speex_param[id].quality);
613 }
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000614
615 /* Sampling rate. */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000616 tmp = attr->info.clock_rate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000617 speex_encoder_ctl(spx->enc, SPEEX_SET_SAMPLING_RATE,
618 &spx_factory.speex_param[id].clock_rate);
619
620 /* VAD */
Benny Prijono70c68912006-05-19 15:58:13 +0000621 tmp = (attr->setting.vad != 0);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000622 speex_encoder_ctl(spx->enc, SPEEX_SET_VAD, &tmp);
Benny Prijono70c68912006-05-19 15:58:13 +0000623 speex_encoder_ctl(spx->enc, SPEEX_SET_DTX, &tmp);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000624
625 /* Complexity */
Benny Prijono7bd15132006-03-06 13:32:57 +0000626 if (spx_factory.speex_param[id].complexity != -1) {
627 speex_encoder_ctl(spx->enc, SPEEX_SET_COMPLEXITY,
628 &spx_factory.speex_param[id].complexity);
629 }
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000630
631 /*
632 * Create and initialize decoder.
633 */
634 spx->dec = speex_decoder_init(spx_factory.speex_param[id].mode);
635 if (!spx->dec) {
636 spx_codec_close(codec);
637 return PJMEDIA_CODEC_EFAILED;
638 }
639 speex_bits_init(&spx->dec_bits);
640
641 /* Sampling rate. */
642 speex_decoder_ctl(spx->dec, SPEEX_SET_SAMPLING_RATE,
643 &spx_factory.speex_param[id].clock_rate);
644
645 /* PENH */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000646 tmp = attr->setting.penh;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000647 speex_decoder_ctl(spx->dec, SPEEX_SET_ENH, &tmp);
648
649 return PJ_SUCCESS;
650}
651
652/*
653 * Close codec.
654 */
655static pj_status_t spx_codec_close( pjmedia_codec *codec )
656{
657 struct spx_private *spx;
658
659 spx = (struct spx_private*) codec->codec_data;
660
661 /* Destroy encoder*/
662 if (spx->enc) {
663 speex_encoder_destroy( spx->enc );
664 spx->enc = NULL;
665 speex_bits_destroy( &spx->enc_bits );
666 }
667
668 /* Destroy decoder */
669 if (spx->dec) {
670 speex_decoder_destroy( spx->dec);
671 spx->dec = NULL;
672 speex_bits_destroy( &spx->dec_bits );
673 }
674
675 return PJ_SUCCESS;
676}
677
678
679/*
Benny Prijonob94a6ab2006-12-26 21:18:11 +0000680 * Modify codec settings.
681 */
682static pj_status_t spx_codec_modify(pjmedia_codec *codec,
683 const pjmedia_codec_param *attr )
684{
685 struct spx_private *spx;
686 int tmp;
687
688 spx = (struct spx_private*) codec->codec_data;
689
690 /* Only supports one frame per packet */
691 PJ_ASSERT_RETURN(attr->setting.frm_per_pkt==1, PJ_EINVAL);
692
693 /* VAD */
694 tmp = (attr->setting.vad != 0);
695 speex_encoder_ctl(spx->enc, SPEEX_SET_VAD, &tmp);
696 speex_encoder_ctl(spx->enc, SPEEX_SET_DTX, &tmp);
697
698 /* PENH */
699 tmp = attr->setting.penh;
700 speex_decoder_ctl(spx->dec, SPEEX_SET_ENH, &tmp);
701
702 return PJ_SUCCESS;
703}
704
705
706/*
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000707 * Get frames in the packet.
708 */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000709static pj_status_t spx_codec_parse( pjmedia_codec *codec,
710 void *pkt,
711 pj_size_t pkt_size,
712 const pj_timestamp *ts,
713 unsigned *frame_cnt,
714 pjmedia_frame frames[])
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000715{
716 struct spx_private *spx;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000717 unsigned frame_size, samples_per_frame;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000718 unsigned count;
719
720 spx = (struct spx_private*) codec->codec_data;
721
Benny Prijono8befd9f2006-05-13 22:46:23 +0000722 frame_size = spx_factory.speex_param[spx->param_id].framesize;
723 samples_per_frame = spx_factory.speex_param[spx->param_id].samples_per_frame;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000724
725 /* Don't really know how to do this... */
726 count = 0;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000727 while (pkt_size >= frame_size && count < *frame_cnt) {
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000728 frames[count].buf = pkt;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000729 frames[count].size = frame_size;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000730 frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000731 frames[count].timestamp.u64 = ts->u64 + count * samples_per_frame;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000732
Benny Prijono8befd9f2006-05-13 22:46:23 +0000733 pkt_size -= frame_size;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000734 ++count;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000735 pkt = ((char*)pkt) + frame_size;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000736 }
737
Benny Prijono8befd9f2006-05-13 22:46:23 +0000738 /* Just in case speex has silence frame which size is less than normal
739 * frame size...
740 */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000741 if (pkt_size && count < *frame_cnt) {
742 frames[count].buf = pkt;
743 frames[count].size = pkt_size;
744 frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000745 frames[count].timestamp.u64 = ts->u64 + count * samples_per_frame;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000746 ++count;
747 }
748
749 *frame_cnt = count;
750 return PJ_SUCCESS;
751}
752
753/*
754 * Encode frame.
755 */
756static pj_status_t spx_codec_encode( pjmedia_codec *codec,
757 const struct pjmedia_frame *input,
758 unsigned output_buf_len,
759 struct pjmedia_frame *output)
760{
761 struct spx_private *spx;
Benny Prijonodc498ca2006-07-26 17:04:54 +0000762 unsigned sz;
Benny Prijono70c68912006-05-19 15:58:13 +0000763 int tx;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000764
765 spx = (struct spx_private*) codec->codec_data;
766
767 if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) {
768 output->size = 0;
769 output->buf = NULL;
770 output->timestamp = input->timestamp;
771 output->type = input->type;
772 return PJ_SUCCESS;
773 }
774
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000775 /* Flush all the bits in the struct so we can encode a new frame */
776 speex_bits_reset(&spx->enc_bits);
777
778 /* Encode the frame */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000779 tx = speex_encode_int(spx->enc, (spx_int16_t*)input->buf,
780 &spx->enc_bits);
Benny Prijono70c68912006-05-19 15:58:13 +0000781
782 /* Check if we need not to transmit the frame (DTX) */
783 if (tx == 0) {
784 output->buf = NULL;
785 output->size = 0;
786 output->timestamp.u64 = input->timestamp.u64;
787 output->type = PJMEDIA_FRAME_TYPE_NONE;
788 return PJ_SUCCESS;
789 }
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000790
Benny Prijono856ebc32006-03-05 11:51:10 +0000791 /* Check size. */
792 sz = speex_bits_nbytes(&spx->enc_bits);
793 pj_assert(sz <= output_buf_len);
794
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000795 /* Copy the bits to an array of char that can be written */
796 output->size = speex_bits_write(&spx->enc_bits,
Benny Prijonoa1e69682007-05-11 15:14:34 +0000797 (char*)output->buf, output_buf_len);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000798 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
799 output->timestamp = input->timestamp;
800
801 return PJ_SUCCESS;
802}
803
804/*
805 * Decode frame.
806 */
807static pj_status_t spx_codec_decode( pjmedia_codec *codec,
808 const struct pjmedia_frame *input,
809 unsigned output_buf_len,
810 struct pjmedia_frame *output)
811{
812 struct spx_private *spx;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000813
814 spx = (struct spx_private*) codec->codec_data;
815
Benny Prijono7a170682008-01-11 08:52:18 +0000816 PJ_ASSERT_RETURN(output_buf_len >= 320, PJMEDIA_CODEC_EPCMTOOSHORT);
817
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000818 if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) {
Benny Prijono7a170682008-01-11 08:52:18 +0000819 pjmedia_zero_samples((pj_int16_t*)output->buf, 160);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000820 output->size = 320;
821 output->timestamp.u64 = input->timestamp.u64;
822 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
823 return PJ_SUCCESS;
824 }
825
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000826 /* Copy the data into the bit-stream struct */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000827 speex_bits_read_from(&spx->dec_bits, (char*)input->buf, input->size);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000828
829 /* Decode the data */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000830 speex_decode_int(spx->dec, &spx->dec_bits, (spx_int16_t*)output->buf);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000831
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000832 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
Benny Prijono7a170682008-01-11 08:52:18 +0000833 output->size = 320;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000834 output->timestamp.u64 = input->timestamp.u64;
835
836
837 return PJ_SUCCESS;
838}
Benny Prijono4381efe2006-03-16 14:24:26 +0000839
Benny Prijono8befd9f2006-05-13 22:46:23 +0000840/*
841 * Recover lost frame.
842 */
843static pj_status_t spx_codec_recover(pjmedia_codec *codec,
844 unsigned output_buf_len,
845 struct pjmedia_frame *output)
846{
847 struct spx_private *spx;
Benny Prijonodc498ca2006-07-26 17:04:54 +0000848 unsigned count;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000849
Benny Prijono60281662006-06-22 18:39:33 +0000850 /* output_buf_len is unreferenced when building in Release mode */
851 PJ_UNUSED_ARG(output_buf_len);
852
Benny Prijono8befd9f2006-05-13 22:46:23 +0000853 spx = (struct spx_private*) codec->codec_data;
854
855 count = spx_factory.speex_param[spx->param_id].clock_rate * 20 / 1000;
Benny Prijonodc498ca2006-07-26 17:04:54 +0000856 pj_assert(count <= output_buf_len/2);
Benny Prijono8befd9f2006-05-13 22:46:23 +0000857
858 /* Recover packet loss */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000859 speex_decode_int(spx->dec, NULL, (spx_int16_t*) output->buf);
Benny Prijono8befd9f2006-05-13 22:46:23 +0000860
Benny Prijono8befd9f2006-05-13 22:46:23 +0000861 output->size = count * 2;
862
863 return PJ_SUCCESS;
864}
865
Benny Prijono4381efe2006-03-16 14:24:26 +0000866
867#endif /* PJMEDIA_HAS_SPEEX_CODEC */