blob: 08c45b2db143e480e96087fbb6f2d4e7d08e8d40 [file] [log] [blame]
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijonoeb30bf52006-03-04 20:43:52 +00005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21#include <pjmedia-codec/speex.h>
22#include <pjmedia/codec.h>
23#include <pjmedia/errno.h>
24#include <pjmedia/endpoint.h>
25#include <pjmedia/port.h>
26#include <speex/speex.h>
27#include <pj/assert.h>
Benny Prijonof6aba4e2006-03-06 16:24:50 +000028#include <pj/log.h>
Benny Prijonoeb30bf52006-03-04 20:43:52 +000029#include <pj/pool.h>
30#include <pj/string.h>
31#include <pj/os.h>
32
Benny Prijono4381efe2006-03-16 14:24:26 +000033/*
34 * Only build this file if PJMEDIA_HAS_SPEEX_CODEC != 0
35 */
36#if defined(PJMEDIA_HAS_SPEEX_CODEC) && PJMEDIA_HAS_SPEEX_CODEC!=0
37
38
Benny Prijonof6aba4e2006-03-06 16:24:50 +000039#define THIS_FILE "speex_codec.c"
40
Benny Prijonoeb30bf52006-03-04 20:43:52 +000041/* Prototypes for Speex factory */
42static pj_status_t spx_test_alloc( pjmedia_codec_factory *factory,
43 const pjmedia_codec_info *id );
44static pj_status_t spx_default_attr( pjmedia_codec_factory *factory,
45 const pjmedia_codec_info *id,
46 pjmedia_codec_param *attr );
47static pj_status_t spx_enum_codecs( pjmedia_codec_factory *factory,
48 unsigned *count,
49 pjmedia_codec_info codecs[]);
50static pj_status_t spx_alloc_codec( pjmedia_codec_factory *factory,
51 const pjmedia_codec_info *id,
52 pjmedia_codec **p_codec);
53static pj_status_t spx_dealloc_codec( pjmedia_codec_factory *factory,
54 pjmedia_codec *codec );
55
56/* Prototypes for Speex implementation. */
Benny Prijonoeb30bf52006-03-04 20:43:52 +000057static pj_status_t spx_codec_init( pjmedia_codec *codec,
58 pj_pool_t *pool );
59static pj_status_t spx_codec_open( pjmedia_codec *codec,
Benny Prijono6865a3f2006-12-30 02:46:57 +000060 pjmedia_codec_param *attr );
Benny Prijonoeb30bf52006-03-04 20:43:52 +000061static pj_status_t spx_codec_close( pjmedia_codec *codec );
Benny Prijonob94a6ab2006-12-26 21:18:11 +000062static pj_status_t spx_codec_modify(pjmedia_codec *codec,
63 const pjmedia_codec_param *attr );
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 Prijonob94a6ab2006-12-26 21:18:11 +000088 &spx_codec_modify,
Benny Prijono8befd9f2006-05-13 22:46:23 +000089 &spx_codec_parse,
Benny Prijonoeb30bf52006-03-04 20:43:52 +000090 &spx_codec_encode,
Benny Prijono8befd9f2006-05-13 22:46:23 +000091 &spx_codec_decode,
92 &spx_codec_recover
Benny Prijonoeb30bf52006-03-04 20:43:52 +000093};
94
95/* Definition for Speex codec factory operations. */
96static pjmedia_codec_factory_op spx_factory_op =
97{
98 &spx_test_alloc,
99 &spx_default_attr,
100 &spx_enum_codecs,
101 &spx_alloc_codec,
102 &spx_dealloc_codec
103};
104
105/* Index to Speex parameter. */
106enum
107{
108 PARAM_NB, /* Index for narrowband parameter. */
109 PARAM_WB, /* Index for wideband parameter. */
110 PARAM_UWB, /* Index for ultra-wideband parameter */
111};
112
113/* Speex default parameter */
114struct speex_param
115{
Benny Prijono7bd15132006-03-06 13:32:57 +0000116 int enabled; /* Is this mode enabled? */
117 const SpeexMode *mode; /* Speex mode. */
118 int pt; /* Payload type. */
119 unsigned clock_rate; /* Default sampling rate to be used.*/
120 int quality; /* Default encoder quality. */
121 int complexity; /* Default encoder complexity. */
122 int samples_per_frame; /* Samples per frame. */
123 int framesize; /* Frame size for current mode. */
124 int bitrate; /* Bit rate for current mode. */
Nanang Izzuddine4b4b7d2008-06-06 12:15:23 +0000125 int max_bitrate; /* Max bit rate for current mode. */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000126};
127
128/* Speex factory */
129static struct spx_factory
130{
131 pjmedia_codec_factory base;
132 pjmedia_endpt *endpt;
133 pj_pool_t *pool;
134 pj_mutex_t *mutex;
135 pjmedia_codec codec_list;
136 struct speex_param speex_param[3];
137
138} spx_factory;
139
140/* Speex codec private data. */
141struct spx_private
142{
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000143 int param_id; /**< Index to speex param. */
144
145 void *enc; /**< Encoder state. */
146 SpeexBits enc_bits; /**< Encoder bits. */
147 void *dec; /**< Decoder state. */
148 SpeexBits dec_bits; /**< Decoder bits. */
149};
150
151
152/*
153 * Get codec bitrate and frame size.
154 */
155static pj_status_t get_speex_info( struct speex_param *p )
156{
157 void *state;
158 int tmp;
159
160 /* Create temporary encoder */
161 state = speex_encoder_init(p->mode);
162 if (!state)
163 return PJMEDIA_CODEC_EFAILED;
164
Nanang Izzuddine4b4b7d2008-06-06 12:15:23 +0000165 /* Set the quality */
166 if (p->quality != -1)
167 speex_encoder_ctl(state, SPEEX_SET_QUALITY, &p->quality);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000168
169 /* Sampling rate. */
170 speex_encoder_ctl(state, SPEEX_SET_SAMPLING_RATE, &p->clock_rate);
171
Benny Prijono7bd15132006-03-06 13:32:57 +0000172 /* VAD off to have max bitrate */
173 tmp = 0;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000174 speex_encoder_ctl(state, SPEEX_SET_VAD, &tmp);
175
176 /* Complexity. */
Benny Prijono7bd15132006-03-06 13:32:57 +0000177 if (p->complexity != -1)
178 speex_encoder_ctl(state, SPEEX_SET_COMPLEXITY, &p->complexity);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000179
180 /* Now get the frame size */
181 speex_encoder_ctl(state, SPEEX_GET_FRAME_SIZE, &p->samples_per_frame);
182
Nanang Izzuddine4b4b7d2008-06-06 12:15:23 +0000183 /* Now get the average bitrate */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000184 speex_encoder_ctl(state, SPEEX_GET_BITRATE, &p->bitrate);
185
186 /* Calculate framesize. */
187 p->framesize = p->bitrate * 20 / 1000;
188
Nanang Izzuddine4b4b7d2008-06-06 12:15:23 +0000189 /* Now get the maximum bitrate by using maximum quality (=10) */
190 tmp = 10;
191 speex_encoder_ctl(state, SPEEX_SET_QUALITY, &tmp);
192 speex_encoder_ctl(state, SPEEX_GET_BITRATE, &p->max_bitrate);
193
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000194 /* Destroy encoder. */
195 speex_encoder_destroy(state);
196
197 return PJ_SUCCESS;
198}
199
200/*
201 * Initialize and register Speex codec factory to pjmedia endpoint.
202 */
203PJ_DEF(pj_status_t) pjmedia_codec_speex_init( pjmedia_endpt *endpt,
204 unsigned options,
205 int quality,
206 int complexity )
207{
208 pjmedia_codec_mgr *codec_mgr;
209 unsigned i;
210 pj_status_t status;
211
212 if (spx_factory.pool != NULL) {
213 /* Already initialized. */
214 return PJ_SUCCESS;
215 }
216
217 /* Get defaults */
Nanang Izzuddin829ac022008-05-27 00:24:26 +0000218 if (quality < 0) quality = PJMEDIA_CODEC_SPEEX_DEFAULT_QUALITY;
219 if (complexity < 0) complexity = PJMEDIA_CODEC_SPEEX_DEFAULT_COMPLEXITY;
220
221 /* Validate quality & complexity */
222 PJ_ASSERT_RETURN(quality >= 0 && quality <= 10, PJ_EINVAL);
223 PJ_ASSERT_RETURN(complexity >= 1 && complexity <= 10, PJ_EINVAL);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000224
225 /* Create Speex codec factory. */
226 spx_factory.base.op = &spx_factory_op;
227 spx_factory.base.factory_data = NULL;
228 spx_factory.endpt = endpt;
229
230 spx_factory.pool = pjmedia_endpt_create_pool(endpt, "speex",
231 4000, 4000);
232 if (!spx_factory.pool)
233 return PJ_ENOMEM;
234
235 pj_list_init(&spx_factory.codec_list);
236
237 /* Create mutex. */
238 status = pj_mutex_create_simple(spx_factory.pool, "speex",
239 &spx_factory.mutex);
240 if (status != PJ_SUCCESS)
241 goto on_error;
242
243 /* Initialize default Speex parameter. */
244 spx_factory.speex_param[PARAM_NB].enabled =
245 ((options & PJMEDIA_SPEEX_NO_NB) == 0);
Benny Prijonoa837c302006-04-27 22:36:40 +0000246 spx_factory.speex_param[PARAM_NB].pt = PJMEDIA_RTP_PT_SPEEX_NB;
Nanang Izzuddine4b4b7d2008-06-06 12:15:23 +0000247 spx_factory.speex_param[PARAM_NB].mode = speex_lib_get_mode(SPEEX_MODEID_NB);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000248 spx_factory.speex_param[PARAM_NB].clock_rate = 8000;
249 spx_factory.speex_param[PARAM_NB].quality = quality;
250 spx_factory.speex_param[PARAM_NB].complexity = complexity;
251
252 spx_factory.speex_param[PARAM_WB].enabled =
253 ((options & PJMEDIA_SPEEX_NO_WB) == 0);
Benny Prijonoa837c302006-04-27 22:36:40 +0000254 spx_factory.speex_param[PARAM_WB].pt = PJMEDIA_RTP_PT_SPEEX_WB;
Nanang Izzuddine4b4b7d2008-06-06 12:15:23 +0000255 spx_factory.speex_param[PARAM_WB].mode = speex_lib_get_mode(SPEEX_MODEID_WB);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000256 spx_factory.speex_param[PARAM_WB].clock_rate = 16000;
257 spx_factory.speex_param[PARAM_WB].quality = quality;
258 spx_factory.speex_param[PARAM_WB].complexity = complexity;
259
260 spx_factory.speex_param[PARAM_UWB].enabled =
261 ((options & PJMEDIA_SPEEX_NO_UWB) == 0);
Benny Prijonoa837c302006-04-27 22:36:40 +0000262 spx_factory.speex_param[PARAM_UWB].pt = PJMEDIA_RTP_PT_SPEEX_UWB;
Nanang Izzuddine4b4b7d2008-06-06 12:15:23 +0000263 spx_factory.speex_param[PARAM_UWB].mode = speex_lib_get_mode(SPEEX_MODEID_UWB);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000264 spx_factory.speex_param[PARAM_UWB].clock_rate = 32000;
265 spx_factory.speex_param[PARAM_UWB].quality = quality;
266 spx_factory.speex_param[PARAM_UWB].complexity = complexity;
267
Benny Prijonof6aba4e2006-03-06 16:24:50 +0000268 /* Somehow quality <=4 is broken in linux. */
Benny Prijono70c68912006-05-19 15:58:13 +0000269 if (quality <= 4 && quality >= 0) {
270 PJ_LOG(5,(THIS_FILE, "Adjusting quality to 5 for uwb"));
Benny Prijonof6aba4e2006-03-06 16:24:50 +0000271 spx_factory.speex_param[PARAM_UWB].quality = 5;
272 }
273
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000274 /* Get codec framesize and avg bitrate for each mode. */
275 for (i=0; i<PJ_ARRAY_SIZE(spx_factory.speex_param); ++i) {
276 status = get_speex_info(&spx_factory.speex_param[i]);
277 }
278
279 /* Get the codec manager. */
280 codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
281 if (!codec_mgr) {
282 status = PJ_EINVALIDOP;
283 goto on_error;
284 }
285
286 /* Register codec factory to endpoint. */
287 status = pjmedia_codec_mgr_register_factory(codec_mgr,
288 &spx_factory.base);
289 if (status != PJ_SUCCESS)
290 goto on_error;
291
292 /* Done. */
293 return PJ_SUCCESS;
294
295on_error:
296 pj_pool_release(spx_factory.pool);
297 spx_factory.pool = NULL;
298 return status;
299}
300
301
302/*
303 * Initialize with default settings.
304 */
305PJ_DEF(pj_status_t) pjmedia_codec_speex_init_default(pjmedia_endpt *endpt)
306{
307 return pjmedia_codec_speex_init(endpt, 0, -1, -1);
308}
309
310/*
Nanang Izzuddin829ac022008-05-27 00:24:26 +0000311 * Change the settings of Speex codec.
312 */
313PJ_DEF(pj_status_t) pjmedia_codec_speex_set_param(unsigned clock_rate,
314 int quality,
315 int complexity)
316{
317 unsigned i;
318
319 /* Get defaults */
320 if (quality < 0) quality = PJMEDIA_CODEC_SPEEX_DEFAULT_QUALITY;
321 if (complexity < 0) complexity = PJMEDIA_CODEC_SPEEX_DEFAULT_COMPLEXITY;
322
323 /* Validate quality & complexity */
324 PJ_ASSERT_RETURN(quality >= 0 && quality <= 10, PJ_EINVAL);
325 PJ_ASSERT_RETURN(complexity >= 1 && complexity <= 10, PJ_EINVAL);
326
327 /* Apply the settings */
328 for (i=0; i<PJ_ARRAY_SIZE(spx_factory.speex_param); ++i) {
329 if (spx_factory.speex_param[i].clock_rate == clock_rate) {
330 pj_status_t status;
331
332 spx_factory.speex_param[i].quality = quality;
333 spx_factory.speex_param[i].complexity = complexity;
334
335 /* Somehow quality<=4 is broken in linux. */
336 if (i == PARAM_UWB && quality <= 4 && quality >= 0) {
337 PJ_LOG(5,(THIS_FILE, "Adjusting quality to 5 for uwb"));
338 spx_factory.speex_param[PARAM_UWB].quality = 5;
339 }
340
341 status = get_speex_info(&spx_factory.speex_param[i]);
342
343 return status;
344 }
345 }
346
347 return PJ_EINVAL;
348}
349
350/*
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000351 * Unregister Speex codec factory from pjmedia endpoint and deinitialize
352 * the Speex codec library.
353 */
354PJ_DEF(pj_status_t) pjmedia_codec_speex_deinit(void)
355{
356 pjmedia_codec_mgr *codec_mgr;
357 pj_status_t status;
358
359 if (spx_factory.pool == NULL) {
360 /* Already deinitialized */
361 return PJ_SUCCESS;
362 }
363
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000364 pj_mutex_lock(spx_factory.mutex);
Benny Prijonoab082f32006-11-19 20:28:34 +0000365
366 /* We don't want to deinit if there's outstanding codec. */
367 /* This is silly, as we'll always have codec in the list if
368 we ever allocate a codec! A better behavior maybe is to
369 deallocate all codecs in the list.
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000370 if (!pj_list_empty(&spx_factory.codec_list)) {
371 pj_mutex_unlock(spx_factory.mutex);
372 return PJ_EBUSY;
373 }
Benny Prijonoab082f32006-11-19 20:28:34 +0000374 */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000375
376 /* Get the codec manager. */
377 codec_mgr = pjmedia_endpt_get_codec_mgr(spx_factory.endpt);
378 if (!codec_mgr) {
379 pj_pool_release(spx_factory.pool);
380 spx_factory.pool = NULL;
381 return PJ_EINVALIDOP;
382 }
383
384 /* Unregister Speex codec factory. */
385 status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
386 &spx_factory.base);
387
388 /* Destroy mutex. */
389 pj_mutex_destroy(spx_factory.mutex);
390
391 /* Destroy pool. */
392 pj_pool_release(spx_factory.pool);
393 spx_factory.pool = NULL;
394
395 return status;
396}
397
398/*
399 * Check if factory can allocate the specified codec.
400 */
401static pj_status_t spx_test_alloc( pjmedia_codec_factory *factory,
402 const pjmedia_codec_info *info )
403{
404 const pj_str_t speex_tag = { "speex", 5};
405 unsigned i;
406
407 PJ_UNUSED_ARG(factory);
408
409 /* Type MUST be audio. */
410 if (info->type != PJMEDIA_TYPE_AUDIO)
411 return PJMEDIA_CODEC_EUNSUP;
412
413 /* Check encoding name. */
414 if (pj_stricmp(&info->encoding_name, &speex_tag) != 0)
415 return PJMEDIA_CODEC_EUNSUP;
416
417 /* Check clock-rate */
418 for (i=0; i<PJ_ARRAY_SIZE(spx_factory.speex_param); ++i) {
Benny Prijonoa837c302006-04-27 22:36:40 +0000419 if (info->clock_rate == spx_factory.speex_param[i].clock_rate) {
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000420 /* Okay, let's Speex! */
421 return PJ_SUCCESS;
422 }
423 }
424
425
426 /* Unsupported, or mode is disabled. */
427 return PJMEDIA_CODEC_EUNSUP;
428}
429
430/*
431 * Generate default attribute.
432 */
433static pj_status_t spx_default_attr (pjmedia_codec_factory *factory,
434 const pjmedia_codec_info *id,
435 pjmedia_codec_param *attr )
436{
437
438 PJ_ASSERT_RETURN(factory==&spx_factory.base, PJ_EINVAL);
439
Benny Prijonoac623b32006-07-03 15:19:31 +0000440 pj_bzero(attr, sizeof(pjmedia_codec_param));
Benny Prijono8befd9f2006-05-13 22:46:23 +0000441 attr->info.pt = (pj_uint8_t)id->pt;
442 attr->info.channel_cnt = 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000443
Benny Prijonoa837c302006-04-27 22:36:40 +0000444 if (id->clock_rate <= 8000) {
Benny Prijono8befd9f2006-05-13 22:46:23 +0000445 attr->info.clock_rate = spx_factory.speex_param[PARAM_NB].clock_rate;
446 attr->info.avg_bps = spx_factory.speex_param[PARAM_NB].bitrate;
Nanang Izzuddine4b4b7d2008-06-06 12:15:23 +0000447 attr->info.max_bps = spx_factory.speex_param[PARAM_NB].max_bitrate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000448
Benny Prijonoa837c302006-04-27 22:36:40 +0000449 } else if (id->clock_rate <= 16000) {
Benny Prijono8befd9f2006-05-13 22:46:23 +0000450 attr->info.clock_rate = spx_factory.speex_param[PARAM_WB].clock_rate;
451 attr->info.avg_bps = spx_factory.speex_param[PARAM_WB].bitrate;
Nanang Izzuddine4b4b7d2008-06-06 12:15:23 +0000452 attr->info.max_bps = spx_factory.speex_param[PARAM_WB].max_bitrate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000453
454 } else {
455 /* Wow.. somebody is doing ultra-wideband. Cool...! */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000456 attr->info.clock_rate = spx_factory.speex_param[PARAM_UWB].clock_rate;
457 attr->info.avg_bps = spx_factory.speex_param[PARAM_UWB].bitrate;
Nanang Izzuddine4b4b7d2008-06-06 12:15:23 +0000458 attr->info.max_bps = spx_factory.speex_param[PARAM_UWB].max_bitrate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000459 }
460
Benny Prijono8befd9f2006-05-13 22:46:23 +0000461 attr->info.pcm_bits_per_sample = 16;
462 attr->info.frm_ptime = 20;
463 attr->info.pt = (pj_uint8_t)id->pt;
464
465 attr->setting.frm_per_pkt = 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000466
467 /* Default flags. */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000468 attr->setting.cng = 1;
469 attr->setting.plc = 1;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000470 attr->setting.penh =1 ;
Benny Prijono70c68912006-05-19 15:58:13 +0000471 attr->setting.vad = 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000472
473 return PJ_SUCCESS;
474}
475
476/*
477 * Enum codecs supported by this factory (i.e. only Speex!).
478 */
479static pj_status_t spx_enum_codecs(pjmedia_codec_factory *factory,
480 unsigned *count,
481 pjmedia_codec_info codecs[])
482{
483 unsigned max;
484 int i; /* Must be signed */
485
486 PJ_UNUSED_ARG(factory);
487 PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
488
489 max = *count;
490 *count = 0;
491
492 /*
493 * We return three codecs here, and in this order:
494 * - ultra-wideband, wideband, and narrowband.
495 */
496 for (i=PJ_ARRAY_SIZE(spx_factory.speex_param)-1; i>=0 && *count<max; --i) {
497
498 if (!spx_factory.speex_param[i].enabled)
499 continue;
500
Benny Prijonoac623b32006-07-03 15:19:31 +0000501 pj_bzero(&codecs[*count], sizeof(pjmedia_codec_info));
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000502 codecs[*count].encoding_name = pj_str("speex");
503 codecs[*count].pt = spx_factory.speex_param[i].pt;
504 codecs[*count].type = PJMEDIA_TYPE_AUDIO;
Benny Prijonoa837c302006-04-27 22:36:40 +0000505 codecs[*count].clock_rate = spx_factory.speex_param[i].clock_rate;
506 codecs[*count].channel_cnt = 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000507
508 ++*count;
509 }
510
511 return PJ_SUCCESS;
512}
513
514/*
515 * Allocate a new Speex codec instance.
516 */
517static pj_status_t spx_alloc_codec( pjmedia_codec_factory *factory,
518 const pjmedia_codec_info *id,
519 pjmedia_codec **p_codec)
520{
521 pjmedia_codec *codec;
522 struct spx_private *spx;
523
524 PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
525 PJ_ASSERT_RETURN(factory == &spx_factory.base, PJ_EINVAL);
526
527
528 pj_mutex_lock(spx_factory.mutex);
529
530 /* Get free nodes, if any. */
531 if (!pj_list_empty(&spx_factory.codec_list)) {
532 codec = spx_factory.codec_list.next;
533 pj_list_erase(codec);
534 } else {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000535 codec = PJ_POOL_ZALLOC_T(spx_factory.pool, pjmedia_codec);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000536 PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM);
537 codec->op = &spx_op;
538 codec->factory = factory;
539 codec->codec_data = pj_pool_alloc(spx_factory.pool,
540 sizeof(struct spx_private));
541 }
542
543 pj_mutex_unlock(spx_factory.mutex);
544
545 spx = (struct spx_private*) codec->codec_data;
546 spx->enc = NULL;
547 spx->dec = NULL;
548
Benny Prijonoa837c302006-04-27 22:36:40 +0000549 if (id->clock_rate <= 8000)
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000550 spx->param_id = PARAM_NB;
Benny Prijonoa837c302006-04-27 22:36:40 +0000551 else if (id->clock_rate <= 16000)
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000552 spx->param_id = PARAM_WB;
553 else
554 spx->param_id = PARAM_UWB;
555
556 *p_codec = codec;
557 return PJ_SUCCESS;
558}
559
560/*
561 * Free codec.
562 */
563static pj_status_t spx_dealloc_codec( pjmedia_codec_factory *factory,
564 pjmedia_codec *codec )
565{
566 struct spx_private *spx;
567
568 PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
569 PJ_ASSERT_RETURN(factory == &spx_factory.base, PJ_EINVAL);
570
571 /* Close codec, if it's not closed. */
572 spx = (struct spx_private*) codec->codec_data;
573 if (spx->enc != NULL || spx->dec != NULL) {
574 spx_codec_close(codec);
575 }
576
577 /* Put in the free list. */
578 pj_mutex_lock(spx_factory.mutex);
579 pj_list_push_front(&spx_factory.codec_list, codec);
580 pj_mutex_unlock(spx_factory.mutex);
581
582 return PJ_SUCCESS;
583}
584
585/*
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000586 * Init codec.
587 */
588static pj_status_t spx_codec_init( pjmedia_codec *codec,
589 pj_pool_t *pool )
590{
591 PJ_UNUSED_ARG(codec);
592 PJ_UNUSED_ARG(pool);
593 return PJ_SUCCESS;
594}
595
596/*
597 * Open codec.
598 */
599static pj_status_t spx_codec_open( pjmedia_codec *codec,
Benny Prijono6865a3f2006-12-30 02:46:57 +0000600 pjmedia_codec_param *attr )
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000601{
602 struct spx_private *spx;
603 int id, tmp;
604
605 spx = (struct spx_private*) codec->codec_data;
606 id = spx->param_id;
607
608 /*
609 * Create and initialize encoder.
610 */
611 spx->enc = speex_encoder_init(spx_factory.speex_param[id].mode);
612 if (!spx->enc)
613 return PJMEDIA_CODEC_EFAILED;
614 speex_bits_init(&spx->enc_bits);
615
616 /* Set the quality*/
Benny Prijono7bd15132006-03-06 13:32:57 +0000617 if (spx_factory.speex_param[id].quality != -1) {
618 speex_encoder_ctl(spx->enc, SPEEX_SET_QUALITY,
619 &spx_factory.speex_param[id].quality);
620 }
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000621
622 /* Sampling rate. */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000623 tmp = attr->info.clock_rate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000624 speex_encoder_ctl(spx->enc, SPEEX_SET_SAMPLING_RATE,
625 &spx_factory.speex_param[id].clock_rate);
626
627 /* VAD */
Benny Prijono70c68912006-05-19 15:58:13 +0000628 tmp = (attr->setting.vad != 0);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000629 speex_encoder_ctl(spx->enc, SPEEX_SET_VAD, &tmp);
Benny Prijono70c68912006-05-19 15:58:13 +0000630 speex_encoder_ctl(spx->enc, SPEEX_SET_DTX, &tmp);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000631
632 /* Complexity */
Benny Prijono7bd15132006-03-06 13:32:57 +0000633 if (spx_factory.speex_param[id].complexity != -1) {
634 speex_encoder_ctl(spx->enc, SPEEX_SET_COMPLEXITY,
635 &spx_factory.speex_param[id].complexity);
636 }
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000637
638 /*
639 * Create and initialize decoder.
640 */
641 spx->dec = speex_decoder_init(spx_factory.speex_param[id].mode);
642 if (!spx->dec) {
643 spx_codec_close(codec);
644 return PJMEDIA_CODEC_EFAILED;
645 }
646 speex_bits_init(&spx->dec_bits);
647
648 /* Sampling rate. */
649 speex_decoder_ctl(spx->dec, SPEEX_SET_SAMPLING_RATE,
650 &spx_factory.speex_param[id].clock_rate);
651
652 /* PENH */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000653 tmp = attr->setting.penh;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000654 speex_decoder_ctl(spx->dec, SPEEX_SET_ENH, &tmp);
655
656 return PJ_SUCCESS;
657}
658
659/*
660 * Close codec.
661 */
662static pj_status_t spx_codec_close( pjmedia_codec *codec )
663{
664 struct spx_private *spx;
665
666 spx = (struct spx_private*) codec->codec_data;
667
668 /* Destroy encoder*/
669 if (spx->enc) {
670 speex_encoder_destroy( spx->enc );
671 spx->enc = NULL;
672 speex_bits_destroy( &spx->enc_bits );
673 }
674
675 /* Destroy decoder */
676 if (spx->dec) {
677 speex_decoder_destroy( spx->dec);
678 spx->dec = NULL;
679 speex_bits_destroy( &spx->dec_bits );
680 }
681
682 return PJ_SUCCESS;
683}
684
685
686/*
Benny Prijonob94a6ab2006-12-26 21:18:11 +0000687 * Modify codec settings.
688 */
689static pj_status_t spx_codec_modify(pjmedia_codec *codec,
690 const pjmedia_codec_param *attr )
691{
692 struct spx_private *spx;
693 int tmp;
694
695 spx = (struct spx_private*) codec->codec_data;
696
Benny Prijonob94a6ab2006-12-26 21:18:11 +0000697 /* VAD */
698 tmp = (attr->setting.vad != 0);
699 speex_encoder_ctl(spx->enc, SPEEX_SET_VAD, &tmp);
700 speex_encoder_ctl(spx->enc, SPEEX_SET_DTX, &tmp);
701
702 /* PENH */
703 tmp = attr->setting.penh;
704 speex_decoder_ctl(spx->dec, SPEEX_SET_ENH, &tmp);
705
706 return PJ_SUCCESS;
707}
708
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000709#if 0
710# define TRACE__(args) PJ_LOG(5,args)
711#else
712# define TRACE__(args)
713#endif
714
715#undef THIS_FUNC
716#define THIS_FUNC "speex_get_next_frame"
717
718#define NB_SUBMODES 16
719#define NB_SUBMODE_BITS 4
720
721#define SB_SUBMODES 8
722#define SB_SUBMODE_BITS 3
723
724/* This function will iterate frames & submodes in the Speex bits.
725 * Returns 0 if a frame found, otherwise returns -1.
726 */
727int speex_get_next_frame(SpeexBits *bits)
728{
729 static const int inband_skip_table[NB_SUBMODES] =
730 {1, 1, 4, 4, 4, 4, 4, 4, 8, 8, 16, 16, 32, 32, 64, 64 };
731 static const int wb_skip_table[SB_SUBMODES] =
732 {SB_SUBMODE_BITS+1, 36, 112, 192, 352, -1, -1, -1};
733
734 unsigned submode;
735 unsigned nb_count = 0;
736
737 while (speex_bits_remaining(bits) >= 5) {
738 unsigned wb_count = 0;
739 unsigned bit_ptr = bits->bitPtr;
740 unsigned char_ptr = bits->charPtr;
741
742 /* WB frame */
743 while ((speex_bits_remaining(bits) >= 4)
744 && speex_bits_unpack_unsigned(bits, 1))
745 {
746 int advance;
747
748 submode = speex_bits_unpack_unsigned(bits, 3);
749 advance = wb_skip_table[submode];
750 if (advance < 0) {
751 TRACE__((THIS_FUNC, "Invalid mode encountered. "
752 "The stream is corrupted."));
753 return -1;
754 }
755 TRACE__((THIS_FUNC, "WB layer skipped: %d bits", advance));
756 advance -= (SB_SUBMODE_BITS+1);
757 speex_bits_advance(bits, advance);
758
759 bit_ptr = bits->bitPtr;
760 char_ptr = bits->charPtr;
761
762 /* Consecutive subband frames may not exceed 2 frames */
763 if (++wb_count > 2)
764 return -1;
765 }
766
767 /* End of bits, return the frame */
768 if (speex_bits_remaining(bits) < 4) {
769 TRACE__((THIS_FUNC, "End of stream"));
770 return 0;
771 }
772
773 /* Stop iteration, return the frame */
774 if (nb_count > 0) {
775 bits->bitPtr = bit_ptr;
776 bits->charPtr = char_ptr;
777 return 0;
778 }
779
780 /* Get control bits */
781 submode = speex_bits_unpack_unsigned(bits, 4);
782 TRACE__((THIS_FUNC, "Control bits: %d at %d",
783 submode, bits->charPtr*8+bits->bitPtr));
784
785 if (submode == 15) {
786 TRACE__((THIS_FUNC, "Found submode: terminator"));
Nanang Izzuddine46495b2008-08-27 17:57:32 +0000787 return -1;
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000788 } else if (submode == 14) {
789 /* in-band signal; next 4 bits contain signal id */
790 submode = speex_bits_unpack_unsigned(bits, 4);
791 TRACE__((THIS_FUNC, "Found submode: in-band %d bits",
792 inband_skip_table[submode]));
793 speex_bits_advance(bits, inband_skip_table[submode]);
794 } else if (submode == 13) {
795 /* user in-band; next 5 bits contain msg len */
796 submode = speex_bits_unpack_unsigned(bits, 5);
797 TRACE__((THIS_FUNC, "Found submode: user-band %d bytes", submode));
798 speex_bits_advance(bits, submode * 8);
799 } else if (submode > 8) {
800 TRACE__((THIS_FUNC, "Unknown sub-mode %d", submode));
Nanang Izzuddine46495b2008-08-27 17:57:32 +0000801 return -1;
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000802 } else {
803 /* NB frame */
804 unsigned int advance = submode;
805 speex_mode_query(&speex_nb_mode, SPEEX_SUBMODE_BITS_PER_FRAME, &advance);
806 if (advance < 0) {
807 TRACE__((THIS_FUNC, "Invalid mode encountered. "
808 "The stream is corrupted."));
809 return -1;
810 }
811 TRACE__((THIS_FUNC, "Submode %d: %d bits", submode, advance));
812 advance -= (NB_SUBMODE_BITS+1);
813 speex_bits_advance(bits, advance);
814
815 ++nb_count;
816 }
817 }
818
819 return 0;
820}
821
Benny Prijonob94a6ab2006-12-26 21:18:11 +0000822
823/*
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000824 * Get frames in the packet.
825 */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000826static pj_status_t spx_codec_parse( pjmedia_codec *codec,
827 void *pkt,
828 pj_size_t pkt_size,
829 const pj_timestamp *ts,
830 unsigned *frame_cnt,
831 pjmedia_frame frames[])
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000832{
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000833 struct spx_private *spx = (struct spx_private*) codec->codec_data;
834 unsigned samples_per_frame;
835 unsigned count = 0;
836 int char_ptr = 0;
837 int bit_ptr = 0;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000838
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000839 samples_per_frame=spx_factory.speex_param[spx->param_id].samples_per_frame;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000840
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000841 /* Copy the data into the speex bit-stream */
842 speex_bits_read_from(&spx->dec_bits, (char*)pkt, pkt_size);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000843
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000844 while (speex_get_next_frame(&spx->dec_bits) == 0 &&
845 spx->dec_bits.charPtr != char_ptr)
846 {
847 frames[count].buf = (char*)pkt + char_ptr;
848 /* Bit info contains start bit offset of the frame */
849 frames[count].bit_info = bit_ptr;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000850 frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000851 frames[count].timestamp.u64 = ts->u64 + count * samples_per_frame;
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000852 frames[count].size = spx->dec_bits.charPtr - char_ptr;
853 if (spx->dec_bits.bitPtr)
854 ++frames[count].size;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000855
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000856 bit_ptr = spx->dec_bits.bitPtr;
857 char_ptr = spx->dec_bits.charPtr;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000858
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000859 ++count;
860 }
861
862 *frame_cnt = count;
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000863
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000864 return PJ_SUCCESS;
865}
866
867/*
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000868 * Encode frames.
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000869 */
870static pj_status_t spx_codec_encode( pjmedia_codec *codec,
871 const struct pjmedia_frame *input,
872 unsigned output_buf_len,
873 struct pjmedia_frame *output)
874{
875 struct spx_private *spx;
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000876 unsigned samples_per_frame;
877 int tx = 0;
878 spx_int16_t *pcm_in = (spx_int16_t*)input->buf;
879 unsigned nsamples;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000880
881 spx = (struct spx_private*) codec->codec_data;
882
883 if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) {
884 output->size = 0;
885 output->buf = NULL;
886 output->timestamp = input->timestamp;
887 output->type = input->type;
888 return PJ_SUCCESS;
889 }
890
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000891 nsamples = input->size >> 1;
892 samples_per_frame=spx_factory.speex_param[spx->param_id].samples_per_frame;
893
894 PJ_ASSERT_RETURN(nsamples % samples_per_frame == 0,
895 PJMEDIA_CODEC_EPCMFRMINLEN);
896
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000897 /* Flush all the bits in the struct so we can encode a new frame */
898 speex_bits_reset(&spx->enc_bits);
899
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000900 /* Encode the frames */
901 while (nsamples >= samples_per_frame) {
902 tx += speex_encode_int(spx->enc, pcm_in, &spx->enc_bits);
903 pcm_in += samples_per_frame;
904 nsamples -= samples_per_frame;
905 }
Benny Prijono70c68912006-05-19 15:58:13 +0000906
907 /* Check if we need not to transmit the frame (DTX) */
908 if (tx == 0) {
909 output->buf = NULL;
910 output->size = 0;
911 output->timestamp.u64 = input->timestamp.u64;
912 output->type = PJMEDIA_FRAME_TYPE_NONE;
913 return PJ_SUCCESS;
914 }
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000915
Benny Prijono856ebc32006-03-05 11:51:10 +0000916 /* Check size. */
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000917 pj_assert(speex_bits_nbytes(&spx->enc_bits) <= (int)output_buf_len);
Benny Prijono856ebc32006-03-05 11:51:10 +0000918
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000919 /* Copy the bits to an array of char that can be written */
920 output->size = speex_bits_write(&spx->enc_bits,
Benny Prijonoa1e69682007-05-11 15:14:34 +0000921 (char*)output->buf, output_buf_len);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000922 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
923 output->timestamp = input->timestamp;
924
925 return PJ_SUCCESS;
926}
927
928/*
929 * Decode frame.
930 */
931static pj_status_t spx_codec_decode( pjmedia_codec *codec,
932 const struct pjmedia_frame *input,
933 unsigned output_buf_len,
934 struct pjmedia_frame *output)
935{
936 struct spx_private *spx;
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000937 unsigned samples_per_frame;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000938
939 spx = (struct spx_private*) codec->codec_data;
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000940 samples_per_frame=spx_factory.speex_param[spx->param_id].samples_per_frame;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000941
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000942 PJ_ASSERT_RETURN(output_buf_len >= samples_per_frame << 1,
943 PJMEDIA_CODEC_EPCMTOOSHORT);
Benny Prijono7a170682008-01-11 08:52:18 +0000944
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000945 if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) {
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000946 pjmedia_zero_samples((pj_int16_t*)output->buf, samples_per_frame);
947 output->size = samples_per_frame << 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000948 output->timestamp.u64 = input->timestamp.u64;
949 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
950 return PJ_SUCCESS;
951 }
952
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000953 /* Copy the data into the bit-stream struct */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000954 speex_bits_read_from(&spx->dec_bits, (char*)input->buf, input->size);
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000955
956 /* Set Speex dec_bits pointer to the start bit of the frame */
957 speex_bits_advance(&spx->dec_bits, input->bit_info);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000958
959 /* Decode the data */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000960 speex_decode_int(spx->dec, &spx->dec_bits, (spx_int16_t*)output->buf);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000961
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000962 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000963 output->size = samples_per_frame << 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000964 output->timestamp.u64 = input->timestamp.u64;
965
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000966 return PJ_SUCCESS;
967}
Benny Prijono4381efe2006-03-16 14:24:26 +0000968
Benny Prijono8befd9f2006-05-13 22:46:23 +0000969/*
970 * Recover lost frame.
971 */
972static pj_status_t spx_codec_recover(pjmedia_codec *codec,
973 unsigned output_buf_len,
974 struct pjmedia_frame *output)
975{
976 struct spx_private *spx;
Benny Prijonodc498ca2006-07-26 17:04:54 +0000977 unsigned count;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000978
Benny Prijono60281662006-06-22 18:39:33 +0000979 /* output_buf_len is unreferenced when building in Release mode */
980 PJ_UNUSED_ARG(output_buf_len);
981
Benny Prijono8befd9f2006-05-13 22:46:23 +0000982 spx = (struct spx_private*) codec->codec_data;
983
984 count = spx_factory.speex_param[spx->param_id].clock_rate * 20 / 1000;
Benny Prijonodc498ca2006-07-26 17:04:54 +0000985 pj_assert(count <= output_buf_len/2);
Benny Prijono8befd9f2006-05-13 22:46:23 +0000986
987 /* Recover packet loss */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000988 speex_decode_int(spx->dec, NULL, (spx_int16_t*) output->buf);
Benny Prijono8befd9f2006-05-13 22:46:23 +0000989
Benny Prijono8befd9f2006-05-13 22:46:23 +0000990 output->size = count * 2;
991
992 return PJ_SUCCESS;
993}
994
Benny Prijono4381efe2006-03-16 14:24:26 +0000995
996#endif /* PJMEDIA_HAS_SPEEX_CODEC */