blob: 0e8f5e38be4f83815e7158fb9b3c1e47081565f6 [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. */
Nanang Izzuddine4b4b7d2008-06-06 12:15:23 +0000124 int max_bitrate; /* Max bit rate for current mode. */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000125};
126
127/* Speex factory */
128static struct spx_factory
129{
130 pjmedia_codec_factory base;
131 pjmedia_endpt *endpt;
132 pj_pool_t *pool;
133 pj_mutex_t *mutex;
134 pjmedia_codec codec_list;
135 struct speex_param speex_param[3];
136
137} spx_factory;
138
139/* Speex codec private data. */
140struct spx_private
141{
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000142 int param_id; /**< Index to speex param. */
143
144 void *enc; /**< Encoder state. */
145 SpeexBits enc_bits; /**< Encoder bits. */
146 void *dec; /**< Decoder state. */
147 SpeexBits dec_bits; /**< Decoder bits. */
148};
149
150
151/*
152 * Get codec bitrate and frame size.
153 */
154static pj_status_t get_speex_info( struct speex_param *p )
155{
156 void *state;
157 int tmp;
158
159 /* Create temporary encoder */
160 state = speex_encoder_init(p->mode);
161 if (!state)
162 return PJMEDIA_CODEC_EFAILED;
163
Nanang Izzuddine4b4b7d2008-06-06 12:15:23 +0000164 /* Set the quality */
165 if (p->quality != -1)
166 speex_encoder_ctl(state, SPEEX_SET_QUALITY, &p->quality);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000167
168 /* Sampling rate. */
169 speex_encoder_ctl(state, SPEEX_SET_SAMPLING_RATE, &p->clock_rate);
170
Benny Prijono7bd15132006-03-06 13:32:57 +0000171 /* VAD off to have max bitrate */
172 tmp = 0;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000173 speex_encoder_ctl(state, SPEEX_SET_VAD, &tmp);
174
175 /* Complexity. */
Benny Prijono7bd15132006-03-06 13:32:57 +0000176 if (p->complexity != -1)
177 speex_encoder_ctl(state, SPEEX_SET_COMPLEXITY, &p->complexity);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000178
179 /* Now get the frame size */
180 speex_encoder_ctl(state, SPEEX_GET_FRAME_SIZE, &p->samples_per_frame);
181
Nanang Izzuddine4b4b7d2008-06-06 12:15:23 +0000182 /* Now get the average bitrate */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000183 speex_encoder_ctl(state, SPEEX_GET_BITRATE, &p->bitrate);
184
185 /* Calculate framesize. */
186 p->framesize = p->bitrate * 20 / 1000;
187
Nanang Izzuddine4b4b7d2008-06-06 12:15:23 +0000188 /* Now get the maximum bitrate by using maximum quality (=10) */
189 tmp = 10;
190 speex_encoder_ctl(state, SPEEX_SET_QUALITY, &tmp);
191 speex_encoder_ctl(state, SPEEX_GET_BITRATE, &p->max_bitrate);
192
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000193 /* Destroy encoder. */
194 speex_encoder_destroy(state);
195
196 return PJ_SUCCESS;
197}
198
199/*
200 * Initialize and register Speex codec factory to pjmedia endpoint.
201 */
202PJ_DEF(pj_status_t) pjmedia_codec_speex_init( pjmedia_endpt *endpt,
203 unsigned options,
204 int quality,
205 int complexity )
206{
207 pjmedia_codec_mgr *codec_mgr;
208 unsigned i;
209 pj_status_t status;
210
211 if (spx_factory.pool != NULL) {
212 /* Already initialized. */
213 return PJ_SUCCESS;
214 }
215
216 /* Get defaults */
Nanang Izzuddin829ac022008-05-27 00:24:26 +0000217 if (quality < 0) quality = PJMEDIA_CODEC_SPEEX_DEFAULT_QUALITY;
218 if (complexity < 0) complexity = PJMEDIA_CODEC_SPEEX_DEFAULT_COMPLEXITY;
219
220 /* Validate quality & complexity */
221 PJ_ASSERT_RETURN(quality >= 0 && quality <= 10, PJ_EINVAL);
222 PJ_ASSERT_RETURN(complexity >= 1 && complexity <= 10, PJ_EINVAL);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000223
224 /* Create Speex codec factory. */
225 spx_factory.base.op = &spx_factory_op;
226 spx_factory.base.factory_data = NULL;
227 spx_factory.endpt = endpt;
228
229 spx_factory.pool = pjmedia_endpt_create_pool(endpt, "speex",
230 4000, 4000);
231 if (!spx_factory.pool)
232 return PJ_ENOMEM;
233
234 pj_list_init(&spx_factory.codec_list);
235
236 /* Create mutex. */
237 status = pj_mutex_create_simple(spx_factory.pool, "speex",
238 &spx_factory.mutex);
239 if (status != PJ_SUCCESS)
240 goto on_error;
241
242 /* Initialize default Speex parameter. */
243 spx_factory.speex_param[PARAM_NB].enabled =
244 ((options & PJMEDIA_SPEEX_NO_NB) == 0);
Benny Prijonoa837c302006-04-27 22:36:40 +0000245 spx_factory.speex_param[PARAM_NB].pt = PJMEDIA_RTP_PT_SPEEX_NB;
Nanang Izzuddine4b4b7d2008-06-06 12:15:23 +0000246 spx_factory.speex_param[PARAM_NB].mode = speex_lib_get_mode(SPEEX_MODEID_NB);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000247 spx_factory.speex_param[PARAM_NB].clock_rate = 8000;
248 spx_factory.speex_param[PARAM_NB].quality = quality;
249 spx_factory.speex_param[PARAM_NB].complexity = complexity;
250
251 spx_factory.speex_param[PARAM_WB].enabled =
252 ((options & PJMEDIA_SPEEX_NO_WB) == 0);
Benny Prijonoa837c302006-04-27 22:36:40 +0000253 spx_factory.speex_param[PARAM_WB].pt = PJMEDIA_RTP_PT_SPEEX_WB;
Nanang Izzuddine4b4b7d2008-06-06 12:15:23 +0000254 spx_factory.speex_param[PARAM_WB].mode = speex_lib_get_mode(SPEEX_MODEID_WB);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000255 spx_factory.speex_param[PARAM_WB].clock_rate = 16000;
256 spx_factory.speex_param[PARAM_WB].quality = quality;
257 spx_factory.speex_param[PARAM_WB].complexity = complexity;
258
259 spx_factory.speex_param[PARAM_UWB].enabled =
260 ((options & PJMEDIA_SPEEX_NO_UWB) == 0);
Benny Prijonoa837c302006-04-27 22:36:40 +0000261 spx_factory.speex_param[PARAM_UWB].pt = PJMEDIA_RTP_PT_SPEEX_UWB;
Nanang Izzuddine4b4b7d2008-06-06 12:15:23 +0000262 spx_factory.speex_param[PARAM_UWB].mode = speex_lib_get_mode(SPEEX_MODEID_UWB);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000263 spx_factory.speex_param[PARAM_UWB].clock_rate = 32000;
264 spx_factory.speex_param[PARAM_UWB].quality = quality;
265 spx_factory.speex_param[PARAM_UWB].complexity = complexity;
266
Benny Prijonof6aba4e2006-03-06 16:24:50 +0000267 /* Somehow quality <=4 is broken in linux. */
Benny Prijono70c68912006-05-19 15:58:13 +0000268 if (quality <= 4 && quality >= 0) {
269 PJ_LOG(5,(THIS_FILE, "Adjusting quality to 5 for uwb"));
Benny Prijonof6aba4e2006-03-06 16:24:50 +0000270 spx_factory.speex_param[PARAM_UWB].quality = 5;
271 }
272
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000273 /* Get codec framesize and avg bitrate for each mode. */
274 for (i=0; i<PJ_ARRAY_SIZE(spx_factory.speex_param); ++i) {
275 status = get_speex_info(&spx_factory.speex_param[i]);
276 }
277
278 /* Get the codec manager. */
279 codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
280 if (!codec_mgr) {
281 status = PJ_EINVALIDOP;
282 goto on_error;
283 }
284
285 /* Register codec factory to endpoint. */
286 status = pjmedia_codec_mgr_register_factory(codec_mgr,
287 &spx_factory.base);
288 if (status != PJ_SUCCESS)
289 goto on_error;
290
291 /* Done. */
292 return PJ_SUCCESS;
293
294on_error:
295 pj_pool_release(spx_factory.pool);
296 spx_factory.pool = NULL;
297 return status;
298}
299
300
301/*
302 * Initialize with default settings.
303 */
304PJ_DEF(pj_status_t) pjmedia_codec_speex_init_default(pjmedia_endpt *endpt)
305{
306 return pjmedia_codec_speex_init(endpt, 0, -1, -1);
307}
308
309/*
Nanang Izzuddin829ac022008-05-27 00:24:26 +0000310 * Change the settings of Speex codec.
311 */
312PJ_DEF(pj_status_t) pjmedia_codec_speex_set_param(unsigned clock_rate,
313 int quality,
314 int complexity)
315{
316 unsigned i;
317
318 /* Get defaults */
319 if (quality < 0) quality = PJMEDIA_CODEC_SPEEX_DEFAULT_QUALITY;
320 if (complexity < 0) complexity = PJMEDIA_CODEC_SPEEX_DEFAULT_COMPLEXITY;
321
322 /* Validate quality & complexity */
323 PJ_ASSERT_RETURN(quality >= 0 && quality <= 10, PJ_EINVAL);
324 PJ_ASSERT_RETURN(complexity >= 1 && complexity <= 10, PJ_EINVAL);
325
326 /* Apply the settings */
327 for (i=0; i<PJ_ARRAY_SIZE(spx_factory.speex_param); ++i) {
328 if (spx_factory.speex_param[i].clock_rate == clock_rate) {
329 pj_status_t status;
330
331 spx_factory.speex_param[i].quality = quality;
332 spx_factory.speex_param[i].complexity = complexity;
333
334 /* Somehow quality<=4 is broken in linux. */
335 if (i == PARAM_UWB && quality <= 4 && quality >= 0) {
336 PJ_LOG(5,(THIS_FILE, "Adjusting quality to 5 for uwb"));
337 spx_factory.speex_param[PARAM_UWB].quality = 5;
338 }
339
340 status = get_speex_info(&spx_factory.speex_param[i]);
341
342 return status;
343 }
344 }
345
346 return PJ_EINVAL;
347}
348
349/*
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000350 * Unregister Speex codec factory from pjmedia endpoint and deinitialize
351 * the Speex codec library.
352 */
353PJ_DEF(pj_status_t) pjmedia_codec_speex_deinit(void)
354{
355 pjmedia_codec_mgr *codec_mgr;
356 pj_status_t status;
357
358 if (spx_factory.pool == NULL) {
359 /* Already deinitialized */
360 return PJ_SUCCESS;
361 }
362
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000363 pj_mutex_lock(spx_factory.mutex);
Benny Prijonoab082f32006-11-19 20:28:34 +0000364
365 /* We don't want to deinit if there's outstanding codec. */
366 /* This is silly, as we'll always have codec in the list if
367 we ever allocate a codec! A better behavior maybe is to
368 deallocate all codecs in the list.
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000369 if (!pj_list_empty(&spx_factory.codec_list)) {
370 pj_mutex_unlock(spx_factory.mutex);
371 return PJ_EBUSY;
372 }
Benny Prijonoab082f32006-11-19 20:28:34 +0000373 */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000374
375 /* Get the codec manager. */
376 codec_mgr = pjmedia_endpt_get_codec_mgr(spx_factory.endpt);
377 if (!codec_mgr) {
378 pj_pool_release(spx_factory.pool);
379 spx_factory.pool = NULL;
380 return PJ_EINVALIDOP;
381 }
382
383 /* Unregister Speex codec factory. */
384 status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
385 &spx_factory.base);
386
387 /* Destroy mutex. */
388 pj_mutex_destroy(spx_factory.mutex);
389
390 /* Destroy pool. */
391 pj_pool_release(spx_factory.pool);
392 spx_factory.pool = NULL;
393
394 return status;
395}
396
397/*
398 * Check if factory can allocate the specified codec.
399 */
400static pj_status_t spx_test_alloc( pjmedia_codec_factory *factory,
401 const pjmedia_codec_info *info )
402{
403 const pj_str_t speex_tag = { "speex", 5};
404 unsigned i;
405
406 PJ_UNUSED_ARG(factory);
407
408 /* Type MUST be audio. */
409 if (info->type != PJMEDIA_TYPE_AUDIO)
410 return PJMEDIA_CODEC_EUNSUP;
411
412 /* Check encoding name. */
413 if (pj_stricmp(&info->encoding_name, &speex_tag) != 0)
414 return PJMEDIA_CODEC_EUNSUP;
415
416 /* Check clock-rate */
417 for (i=0; i<PJ_ARRAY_SIZE(spx_factory.speex_param); ++i) {
Benny Prijonoa837c302006-04-27 22:36:40 +0000418 if (info->clock_rate == spx_factory.speex_param[i].clock_rate) {
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000419 /* Okay, let's Speex! */
420 return PJ_SUCCESS;
421 }
422 }
423
424
425 /* Unsupported, or mode is disabled. */
426 return PJMEDIA_CODEC_EUNSUP;
427}
428
429/*
430 * Generate default attribute.
431 */
432static pj_status_t spx_default_attr (pjmedia_codec_factory *factory,
433 const pjmedia_codec_info *id,
434 pjmedia_codec_param *attr )
435{
436
437 PJ_ASSERT_RETURN(factory==&spx_factory.base, PJ_EINVAL);
438
Benny Prijonoac623b32006-07-03 15:19:31 +0000439 pj_bzero(attr, sizeof(pjmedia_codec_param));
Benny Prijono8befd9f2006-05-13 22:46:23 +0000440 attr->info.pt = (pj_uint8_t)id->pt;
441 attr->info.channel_cnt = 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000442
Benny Prijonoa837c302006-04-27 22:36:40 +0000443 if (id->clock_rate <= 8000) {
Benny Prijono8befd9f2006-05-13 22:46:23 +0000444 attr->info.clock_rate = spx_factory.speex_param[PARAM_NB].clock_rate;
445 attr->info.avg_bps = spx_factory.speex_param[PARAM_NB].bitrate;
Nanang Izzuddine4b4b7d2008-06-06 12:15:23 +0000446 attr->info.max_bps = spx_factory.speex_param[PARAM_NB].max_bitrate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000447
Benny Prijonoa837c302006-04-27 22:36:40 +0000448 } else if (id->clock_rate <= 16000) {
Benny Prijono8befd9f2006-05-13 22:46:23 +0000449 attr->info.clock_rate = spx_factory.speex_param[PARAM_WB].clock_rate;
450 attr->info.avg_bps = spx_factory.speex_param[PARAM_WB].bitrate;
Nanang Izzuddine4b4b7d2008-06-06 12:15:23 +0000451 attr->info.max_bps = spx_factory.speex_param[PARAM_WB].max_bitrate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000452
453 } else {
454 /* Wow.. somebody is doing ultra-wideband. Cool...! */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000455 attr->info.clock_rate = spx_factory.speex_param[PARAM_UWB].clock_rate;
456 attr->info.avg_bps = spx_factory.speex_param[PARAM_UWB].bitrate;
Nanang Izzuddine4b4b7d2008-06-06 12:15:23 +0000457 attr->info.max_bps = spx_factory.speex_param[PARAM_UWB].max_bitrate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000458 }
459
Benny Prijono8befd9f2006-05-13 22:46:23 +0000460 attr->info.pcm_bits_per_sample = 16;
461 attr->info.frm_ptime = 20;
462 attr->info.pt = (pj_uint8_t)id->pt;
463
464 attr->setting.frm_per_pkt = 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000465
466 /* Default flags. */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000467 attr->setting.cng = 1;
468 attr->setting.plc = 1;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000469 attr->setting.penh =1 ;
Benny Prijono70c68912006-05-19 15:58:13 +0000470 attr->setting.vad = 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000471
472 return PJ_SUCCESS;
473}
474
475/*
476 * Enum codecs supported by this factory (i.e. only Speex!).
477 */
478static pj_status_t spx_enum_codecs(pjmedia_codec_factory *factory,
479 unsigned *count,
480 pjmedia_codec_info codecs[])
481{
482 unsigned max;
483 int i; /* Must be signed */
484
485 PJ_UNUSED_ARG(factory);
486 PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
487
488 max = *count;
489 *count = 0;
490
491 /*
492 * We return three codecs here, and in this order:
493 * - ultra-wideband, wideband, and narrowband.
494 */
495 for (i=PJ_ARRAY_SIZE(spx_factory.speex_param)-1; i>=0 && *count<max; --i) {
496
497 if (!spx_factory.speex_param[i].enabled)
498 continue;
499
Benny Prijonoac623b32006-07-03 15:19:31 +0000500 pj_bzero(&codecs[*count], sizeof(pjmedia_codec_info));
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000501 codecs[*count].encoding_name = pj_str("speex");
502 codecs[*count].pt = spx_factory.speex_param[i].pt;
503 codecs[*count].type = PJMEDIA_TYPE_AUDIO;
Benny Prijonoa837c302006-04-27 22:36:40 +0000504 codecs[*count].clock_rate = spx_factory.speex_param[i].clock_rate;
505 codecs[*count].channel_cnt = 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000506
507 ++*count;
508 }
509
510 return PJ_SUCCESS;
511}
512
513/*
514 * Allocate a new Speex codec instance.
515 */
516static pj_status_t spx_alloc_codec( pjmedia_codec_factory *factory,
517 const pjmedia_codec_info *id,
518 pjmedia_codec **p_codec)
519{
520 pjmedia_codec *codec;
521 struct spx_private *spx;
522
523 PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
524 PJ_ASSERT_RETURN(factory == &spx_factory.base, PJ_EINVAL);
525
526
527 pj_mutex_lock(spx_factory.mutex);
528
529 /* Get free nodes, if any. */
530 if (!pj_list_empty(&spx_factory.codec_list)) {
531 codec = spx_factory.codec_list.next;
532 pj_list_erase(codec);
533 } else {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000534 codec = PJ_POOL_ZALLOC_T(spx_factory.pool, pjmedia_codec);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000535 PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM);
536 codec->op = &spx_op;
537 codec->factory = factory;
538 codec->codec_data = pj_pool_alloc(spx_factory.pool,
539 sizeof(struct spx_private));
540 }
541
542 pj_mutex_unlock(spx_factory.mutex);
543
544 spx = (struct spx_private*) codec->codec_data;
545 spx->enc = NULL;
546 spx->dec = NULL;
547
Benny Prijonoa837c302006-04-27 22:36:40 +0000548 if (id->clock_rate <= 8000)
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000549 spx->param_id = PARAM_NB;
Benny Prijonoa837c302006-04-27 22:36:40 +0000550 else if (id->clock_rate <= 16000)
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000551 spx->param_id = PARAM_WB;
552 else
553 spx->param_id = PARAM_UWB;
554
555 *p_codec = codec;
556 return PJ_SUCCESS;
557}
558
559/*
560 * Free codec.
561 */
562static pj_status_t spx_dealloc_codec( pjmedia_codec_factory *factory,
563 pjmedia_codec *codec )
564{
565 struct spx_private *spx;
566
567 PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
568 PJ_ASSERT_RETURN(factory == &spx_factory.base, PJ_EINVAL);
569
570 /* Close codec, if it's not closed. */
571 spx = (struct spx_private*) codec->codec_data;
572 if (spx->enc != NULL || spx->dec != NULL) {
573 spx_codec_close(codec);
574 }
575
576 /* Put in the free list. */
577 pj_mutex_lock(spx_factory.mutex);
578 pj_list_push_front(&spx_factory.codec_list, codec);
579 pj_mutex_unlock(spx_factory.mutex);
580
581 return PJ_SUCCESS;
582}
583
584/*
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000585 * Init codec.
586 */
587static pj_status_t spx_codec_init( pjmedia_codec *codec,
588 pj_pool_t *pool )
589{
590 PJ_UNUSED_ARG(codec);
591 PJ_UNUSED_ARG(pool);
592 return PJ_SUCCESS;
593}
594
595/*
596 * Open codec.
597 */
598static pj_status_t spx_codec_open( pjmedia_codec *codec,
Benny Prijono6865a3f2006-12-30 02:46:57 +0000599 pjmedia_codec_param *attr )
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000600{
601 struct spx_private *spx;
602 int id, tmp;
603
604 spx = (struct spx_private*) codec->codec_data;
605 id = spx->param_id;
606
607 /*
608 * Create and initialize encoder.
609 */
610 spx->enc = speex_encoder_init(spx_factory.speex_param[id].mode);
611 if (!spx->enc)
612 return PJMEDIA_CODEC_EFAILED;
613 speex_bits_init(&spx->enc_bits);
614
615 /* Set the quality*/
Benny Prijono7bd15132006-03-06 13:32:57 +0000616 if (spx_factory.speex_param[id].quality != -1) {
617 speex_encoder_ctl(spx->enc, SPEEX_SET_QUALITY,
618 &spx_factory.speex_param[id].quality);
619 }
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000620
621 /* Sampling rate. */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000622 tmp = attr->info.clock_rate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000623 speex_encoder_ctl(spx->enc, SPEEX_SET_SAMPLING_RATE,
624 &spx_factory.speex_param[id].clock_rate);
625
626 /* VAD */
Benny Prijono70c68912006-05-19 15:58:13 +0000627 tmp = (attr->setting.vad != 0);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000628 speex_encoder_ctl(spx->enc, SPEEX_SET_VAD, &tmp);
Benny Prijono70c68912006-05-19 15:58:13 +0000629 speex_encoder_ctl(spx->enc, SPEEX_SET_DTX, &tmp);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000630
631 /* Complexity */
Benny Prijono7bd15132006-03-06 13:32:57 +0000632 if (spx_factory.speex_param[id].complexity != -1) {
633 speex_encoder_ctl(spx->enc, SPEEX_SET_COMPLEXITY,
634 &spx_factory.speex_param[id].complexity);
635 }
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000636
637 /*
638 * Create and initialize decoder.
639 */
640 spx->dec = speex_decoder_init(spx_factory.speex_param[id].mode);
641 if (!spx->dec) {
642 spx_codec_close(codec);
643 return PJMEDIA_CODEC_EFAILED;
644 }
645 speex_bits_init(&spx->dec_bits);
646
647 /* Sampling rate. */
648 speex_decoder_ctl(spx->dec, SPEEX_SET_SAMPLING_RATE,
649 &spx_factory.speex_param[id].clock_rate);
650
651 /* PENH */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000652 tmp = attr->setting.penh;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000653 speex_decoder_ctl(spx->dec, SPEEX_SET_ENH, &tmp);
654
655 return PJ_SUCCESS;
656}
657
658/*
659 * Close codec.
660 */
661static pj_status_t spx_codec_close( pjmedia_codec *codec )
662{
663 struct spx_private *spx;
664
665 spx = (struct spx_private*) codec->codec_data;
666
667 /* Destroy encoder*/
668 if (spx->enc) {
669 speex_encoder_destroy( spx->enc );
670 spx->enc = NULL;
671 speex_bits_destroy( &spx->enc_bits );
672 }
673
674 /* Destroy decoder */
675 if (spx->dec) {
676 speex_decoder_destroy( spx->dec);
677 spx->dec = NULL;
678 speex_bits_destroy( &spx->dec_bits );
679 }
680
681 return PJ_SUCCESS;
682}
683
684
685/*
Benny Prijonob94a6ab2006-12-26 21:18:11 +0000686 * Modify codec settings.
687 */
688static pj_status_t spx_codec_modify(pjmedia_codec *codec,
689 const pjmedia_codec_param *attr )
690{
691 struct spx_private *spx;
692 int tmp;
693
694 spx = (struct spx_private*) codec->codec_data;
695
Benny Prijonob94a6ab2006-12-26 21:18:11 +0000696 /* VAD */
697 tmp = (attr->setting.vad != 0);
698 speex_encoder_ctl(spx->enc, SPEEX_SET_VAD, &tmp);
699 speex_encoder_ctl(spx->enc, SPEEX_SET_DTX, &tmp);
700
701 /* PENH */
702 tmp = attr->setting.penh;
703 speex_decoder_ctl(spx->dec, SPEEX_SET_ENH, &tmp);
704
705 return PJ_SUCCESS;
706}
707
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000708#if 0
709# define TRACE__(args) PJ_LOG(5,args)
710#else
711# define TRACE__(args)
712#endif
713
714#undef THIS_FUNC
715#define THIS_FUNC "speex_get_next_frame"
716
717#define NB_SUBMODES 16
718#define NB_SUBMODE_BITS 4
719
720#define SB_SUBMODES 8
721#define SB_SUBMODE_BITS 3
722
723/* This function will iterate frames & submodes in the Speex bits.
724 * Returns 0 if a frame found, otherwise returns -1.
725 */
726int speex_get_next_frame(SpeexBits *bits)
727{
728 static const int inband_skip_table[NB_SUBMODES] =
729 {1, 1, 4, 4, 4, 4, 4, 4, 8, 8, 16, 16, 32, 32, 64, 64 };
730 static const int wb_skip_table[SB_SUBMODES] =
731 {SB_SUBMODE_BITS+1, 36, 112, 192, 352, -1, -1, -1};
732
733 unsigned submode;
734 unsigned nb_count = 0;
735
736 while (speex_bits_remaining(bits) >= 5) {
737 unsigned wb_count = 0;
738 unsigned bit_ptr = bits->bitPtr;
739 unsigned char_ptr = bits->charPtr;
740
741 /* WB frame */
742 while ((speex_bits_remaining(bits) >= 4)
743 && speex_bits_unpack_unsigned(bits, 1))
744 {
745 int advance;
746
747 submode = speex_bits_unpack_unsigned(bits, 3);
748 advance = wb_skip_table[submode];
749 if (advance < 0) {
750 TRACE__((THIS_FUNC, "Invalid mode encountered. "
751 "The stream is corrupted."));
752 return -1;
753 }
754 TRACE__((THIS_FUNC, "WB layer skipped: %d bits", advance));
755 advance -= (SB_SUBMODE_BITS+1);
756 speex_bits_advance(bits, advance);
757
758 bit_ptr = bits->bitPtr;
759 char_ptr = bits->charPtr;
760
761 /* Consecutive subband frames may not exceed 2 frames */
762 if (++wb_count > 2)
763 return -1;
764 }
765
766 /* End of bits, return the frame */
767 if (speex_bits_remaining(bits) < 4) {
768 TRACE__((THIS_FUNC, "End of stream"));
769 return 0;
770 }
771
772 /* Stop iteration, return the frame */
773 if (nb_count > 0) {
774 bits->bitPtr = bit_ptr;
775 bits->charPtr = char_ptr;
776 return 0;
777 }
778
779 /* Get control bits */
780 submode = speex_bits_unpack_unsigned(bits, 4);
781 TRACE__((THIS_FUNC, "Control bits: %d at %d",
782 submode, bits->charPtr*8+bits->bitPtr));
783
784 if (submode == 15) {
785 TRACE__((THIS_FUNC, "Found submode: terminator"));
786 return 0;
787 } else if (submode == 14) {
788 /* in-band signal; next 4 bits contain signal id */
789 submode = speex_bits_unpack_unsigned(bits, 4);
790 TRACE__((THIS_FUNC, "Found submode: in-band %d bits",
791 inband_skip_table[submode]));
792 speex_bits_advance(bits, inband_skip_table[submode]);
793 } else if (submode == 13) {
794 /* user in-band; next 5 bits contain msg len */
795 submode = speex_bits_unpack_unsigned(bits, 5);
796 TRACE__((THIS_FUNC, "Found submode: user-band %d bytes", submode));
797 speex_bits_advance(bits, submode * 8);
798 } else if (submode > 8) {
799 TRACE__((THIS_FUNC, "Unknown sub-mode %d", submode));
800 return 0;
801 } else {
802 /* NB frame */
803 unsigned int advance = submode;
804 speex_mode_query(&speex_nb_mode, SPEEX_SUBMODE_BITS_PER_FRAME, &advance);
805 if (advance < 0) {
806 TRACE__((THIS_FUNC, "Invalid mode encountered. "
807 "The stream is corrupted."));
808 return -1;
809 }
810 TRACE__((THIS_FUNC, "Submode %d: %d bits", submode, advance));
811 advance -= (NB_SUBMODE_BITS+1);
812 speex_bits_advance(bits, advance);
813
814 ++nb_count;
815 }
816 }
817
818 return 0;
819}
820
Benny Prijonob94a6ab2006-12-26 21:18:11 +0000821
822/*
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000823 * Get frames in the packet.
824 */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000825static pj_status_t spx_codec_parse( pjmedia_codec *codec,
826 void *pkt,
827 pj_size_t pkt_size,
828 const pj_timestamp *ts,
829 unsigned *frame_cnt,
830 pjmedia_frame frames[])
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000831{
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000832 struct spx_private *spx = (struct spx_private*) codec->codec_data;
833 unsigned samples_per_frame;
834 unsigned count = 0;
835 int char_ptr = 0;
836 int bit_ptr = 0;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000837
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000838 samples_per_frame=spx_factory.speex_param[spx->param_id].samples_per_frame;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000839
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000840 /* Copy the data into the speex bit-stream */
841 speex_bits_read_from(&spx->dec_bits, (char*)pkt, pkt_size);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000842
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000843 while (speex_get_next_frame(&spx->dec_bits) == 0 &&
844 spx->dec_bits.charPtr != char_ptr)
845 {
846 frames[count].buf = (char*)pkt + char_ptr;
847 /* Bit info contains start bit offset of the frame */
848 frames[count].bit_info = bit_ptr;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000849 frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000850 frames[count].timestamp.u64 = ts->u64 + count * samples_per_frame;
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000851 frames[count].size = spx->dec_bits.charPtr - char_ptr;
852 if (spx->dec_bits.bitPtr)
853 ++frames[count].size;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000854
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000855 bit_ptr = spx->dec_bits.bitPtr;
856 char_ptr = spx->dec_bits.charPtr;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000857
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000858 ++count;
859 }
860
861 *frame_cnt = count;
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000862
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000863 return PJ_SUCCESS;
864}
865
866/*
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000867 * Encode frames.
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000868 */
869static pj_status_t spx_codec_encode( pjmedia_codec *codec,
870 const struct pjmedia_frame *input,
871 unsigned output_buf_len,
872 struct pjmedia_frame *output)
873{
874 struct spx_private *spx;
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000875 unsigned samples_per_frame;
876 int tx = 0;
877 spx_int16_t *pcm_in = (spx_int16_t*)input->buf;
878 unsigned nsamples;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000879
880 spx = (struct spx_private*) codec->codec_data;
881
882 if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) {
883 output->size = 0;
884 output->buf = NULL;
885 output->timestamp = input->timestamp;
886 output->type = input->type;
887 return PJ_SUCCESS;
888 }
889
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000890 nsamples = input->size >> 1;
891 samples_per_frame=spx_factory.speex_param[spx->param_id].samples_per_frame;
892
893 PJ_ASSERT_RETURN(nsamples % samples_per_frame == 0,
894 PJMEDIA_CODEC_EPCMFRMINLEN);
895
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000896 /* Flush all the bits in the struct so we can encode a new frame */
897 speex_bits_reset(&spx->enc_bits);
898
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000899 /* Encode the frames */
900 while (nsamples >= samples_per_frame) {
901 tx += speex_encode_int(spx->enc, pcm_in, &spx->enc_bits);
902 pcm_in += samples_per_frame;
903 nsamples -= samples_per_frame;
904 }
Benny Prijono70c68912006-05-19 15:58:13 +0000905
906 /* Check if we need not to transmit the frame (DTX) */
907 if (tx == 0) {
908 output->buf = NULL;
909 output->size = 0;
910 output->timestamp.u64 = input->timestamp.u64;
911 output->type = PJMEDIA_FRAME_TYPE_NONE;
912 return PJ_SUCCESS;
913 }
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000914
Benny Prijono856ebc32006-03-05 11:51:10 +0000915 /* Check size. */
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000916 pj_assert(speex_bits_nbytes(&spx->enc_bits) <= (int)output_buf_len);
Benny Prijono856ebc32006-03-05 11:51:10 +0000917
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000918 /* Copy the bits to an array of char that can be written */
919 output->size = speex_bits_write(&spx->enc_bits,
Benny Prijonoa1e69682007-05-11 15:14:34 +0000920 (char*)output->buf, output_buf_len);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000921 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
922 output->timestamp = input->timestamp;
923
924 return PJ_SUCCESS;
925}
926
927/*
928 * Decode frame.
929 */
930static pj_status_t spx_codec_decode( pjmedia_codec *codec,
931 const struct pjmedia_frame *input,
932 unsigned output_buf_len,
933 struct pjmedia_frame *output)
934{
935 struct spx_private *spx;
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000936 unsigned samples_per_frame;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000937
938 spx = (struct spx_private*) codec->codec_data;
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000939 samples_per_frame=spx_factory.speex_param[spx->param_id].samples_per_frame;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000940
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000941 PJ_ASSERT_RETURN(output_buf_len >= samples_per_frame << 1,
942 PJMEDIA_CODEC_EPCMTOOSHORT);
Benny Prijono7a170682008-01-11 08:52:18 +0000943
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000944 if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) {
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000945 pjmedia_zero_samples((pj_int16_t*)output->buf, samples_per_frame);
946 output->size = samples_per_frame << 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000947 output->timestamp.u64 = input->timestamp.u64;
948 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
949 return PJ_SUCCESS;
950 }
951
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000952 /* Copy the data into the bit-stream struct */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000953 speex_bits_read_from(&spx->dec_bits, (char*)input->buf, input->size);
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000954
955 /* Set Speex dec_bits pointer to the start bit of the frame */
956 speex_bits_advance(&spx->dec_bits, input->bit_info);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000957
958 /* Decode the data */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000959 speex_decode_int(spx->dec, &spx->dec_bits, (spx_int16_t*)output->buf);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000960
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000961 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000962 output->size = samples_per_frame << 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000963 output->timestamp.u64 = input->timestamp.u64;
964
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000965 return PJ_SUCCESS;
966}
Benny Prijono4381efe2006-03-16 14:24:26 +0000967
Benny Prijono8befd9f2006-05-13 22:46:23 +0000968/*
969 * Recover lost frame.
970 */
971static pj_status_t spx_codec_recover(pjmedia_codec *codec,
972 unsigned output_buf_len,
973 struct pjmedia_frame *output)
974{
975 struct spx_private *spx;
Benny Prijonodc498ca2006-07-26 17:04:54 +0000976 unsigned count;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000977
Benny Prijono60281662006-06-22 18:39:33 +0000978 /* output_buf_len is unreferenced when building in Release mode */
979 PJ_UNUSED_ARG(output_buf_len);
980
Benny Prijono8befd9f2006-05-13 22:46:23 +0000981 spx = (struct spx_private*) codec->codec_data;
982
983 count = spx_factory.speex_param[spx->param_id].clock_rate * 20 / 1000;
Benny Prijonodc498ca2006-07-26 17:04:54 +0000984 pj_assert(count <= output_buf_len/2);
Benny Prijono8befd9f2006-05-13 22:46:23 +0000985
986 /* Recover packet loss */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000987 speex_decode_int(spx->dec, NULL, (spx_int16_t*) output->buf);
Benny Prijono8befd9f2006-05-13 22:46:23 +0000988
Benny Prijono8befd9f2006-05-13 22:46:23 +0000989 output->size = count * 2;
990
991 return PJ_SUCCESS;
992}
993
Benny Prijono4381efe2006-03-16 14:24:26 +0000994
995#endif /* PJMEDIA_HAS_SPEEX_CODEC */