blob: a0c61b945559c77e3cf01b864947772d7205b2ef [file] [log] [blame]
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001/* $Id$ */
2/*
3 * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20#include <pjmedia-codec/speex.h>
21#include <pjmedia/codec.h>
22#include <pjmedia/errno.h>
23#include <pjmedia/endpoint.h>
24#include <pjmedia/port.h>
25#include <speex/speex.h>
26#include <pj/assert.h>
Benny Prijonof6aba4e2006-03-06 16:24:50 +000027#include <pj/log.h>
Benny Prijonoeb30bf52006-03-04 20:43:52 +000028#include <pj/pool.h>
29#include <pj/string.h>
30#include <pj/os.h>
31
Benny Prijono4381efe2006-03-16 14:24:26 +000032/*
33 * Only build this file if PJMEDIA_HAS_SPEEX_CODEC != 0
34 */
35#if defined(PJMEDIA_HAS_SPEEX_CODEC) && PJMEDIA_HAS_SPEEX_CODEC!=0
36
37
Benny Prijonof6aba4e2006-03-06 16:24:50 +000038#define THIS_FILE "speex_codec.c"
39
Benny Prijono70c68912006-05-19 15:58:13 +000040#define DEFAULT_QUALITY 10
41#define DEFAULT_COMPLEXITY 10
Benny Prijonoeb30bf52006-03-04 20:43:52 +000042
Benny Prijonoeb30bf52006-03-04 20:43:52 +000043/* Prototypes for Speex factory */
44static pj_status_t spx_test_alloc( pjmedia_codec_factory *factory,
45 const pjmedia_codec_info *id );
46static pj_status_t spx_default_attr( pjmedia_codec_factory *factory,
47 const pjmedia_codec_info *id,
48 pjmedia_codec_param *attr );
49static pj_status_t spx_enum_codecs( pjmedia_codec_factory *factory,
50 unsigned *count,
51 pjmedia_codec_info codecs[]);
52static pj_status_t spx_alloc_codec( pjmedia_codec_factory *factory,
53 const pjmedia_codec_info *id,
54 pjmedia_codec **p_codec);
55static pj_status_t spx_dealloc_codec( pjmedia_codec_factory *factory,
56 pjmedia_codec *codec );
57
58/* Prototypes for Speex implementation. */
Benny Prijonoeb30bf52006-03-04 20:43:52 +000059static pj_status_t spx_codec_init( pjmedia_codec *codec,
60 pj_pool_t *pool );
61static pj_status_t spx_codec_open( pjmedia_codec *codec,
Benny Prijono6865a3f2006-12-30 02:46:57 +000062 pjmedia_codec_param *attr );
Benny Prijonoeb30bf52006-03-04 20:43:52 +000063static pj_status_t spx_codec_close( pjmedia_codec *codec );
Benny Prijonob94a6ab2006-12-26 21:18:11 +000064static pj_status_t spx_codec_modify(pjmedia_codec *codec,
65 const pjmedia_codec_param *attr );
Benny Prijono8befd9f2006-05-13 22:46:23 +000066static pj_status_t spx_codec_parse( pjmedia_codec *codec,
67 void *pkt,
68 pj_size_t pkt_size,
69 const pj_timestamp *ts,
70 unsigned *frame_cnt,
71 pjmedia_frame frames[]);
Benny Prijonoeb30bf52006-03-04 20:43:52 +000072static pj_status_t spx_codec_encode( pjmedia_codec *codec,
73 const struct pjmedia_frame *input,
74 unsigned output_buf_len,
75 struct pjmedia_frame *output);
76static pj_status_t spx_codec_decode( pjmedia_codec *codec,
77 const struct pjmedia_frame *input,
78 unsigned output_buf_len,
79 struct pjmedia_frame *output);
Benny Prijono8befd9f2006-05-13 22:46:23 +000080static pj_status_t spx_codec_recover(pjmedia_codec *codec,
81 unsigned output_buf_len,
82 struct pjmedia_frame *output);
Benny Prijonoeb30bf52006-03-04 20:43:52 +000083
84/* Definition for Speex codec operations. */
85static pjmedia_codec_op spx_op =
86{
Benny Prijonoeb30bf52006-03-04 20:43:52 +000087 &spx_codec_init,
88 &spx_codec_open,
89 &spx_codec_close,
Benny Prijonob94a6ab2006-12-26 21:18:11 +000090 &spx_codec_modify,
Benny Prijono8befd9f2006-05-13 22:46:23 +000091 &spx_codec_parse,
Benny Prijonoeb30bf52006-03-04 20:43:52 +000092 &spx_codec_encode,
Benny Prijono8befd9f2006-05-13 22:46:23 +000093 &spx_codec_decode,
94 &spx_codec_recover
Benny Prijonoeb30bf52006-03-04 20:43:52 +000095};
96
97/* Definition for Speex codec factory operations. */
98static pjmedia_codec_factory_op spx_factory_op =
99{
100 &spx_test_alloc,
101 &spx_default_attr,
102 &spx_enum_codecs,
103 &spx_alloc_codec,
104 &spx_dealloc_codec
105};
106
107/* Index to Speex parameter. */
108enum
109{
110 PARAM_NB, /* Index for narrowband parameter. */
111 PARAM_WB, /* Index for wideband parameter. */
112 PARAM_UWB, /* Index for ultra-wideband parameter */
113};
114
115/* Speex default parameter */
116struct speex_param
117{
Benny Prijono7bd15132006-03-06 13:32:57 +0000118 int enabled; /* Is this mode enabled? */
119 const SpeexMode *mode; /* Speex mode. */
120 int pt; /* Payload type. */
121 unsigned clock_rate; /* Default sampling rate to be used.*/
122 int quality; /* Default encoder quality. */
123 int complexity; /* Default encoder complexity. */
124 int samples_per_frame; /* Samples per frame. */
125 int framesize; /* Frame size for current mode. */
126 int bitrate; /* Bit rate for current mode. */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000127};
128
129/* Speex factory */
130static struct spx_factory
131{
132 pjmedia_codec_factory base;
133 pjmedia_endpt *endpt;
134 pj_pool_t *pool;
135 pj_mutex_t *mutex;
136 pjmedia_codec codec_list;
137 struct speex_param speex_param[3];
138
139} spx_factory;
140
141/* Speex codec private data. */
142struct spx_private
143{
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000144 int param_id; /**< Index to speex param. */
145
146 void *enc; /**< Encoder state. */
147 SpeexBits enc_bits; /**< Encoder bits. */
148 void *dec; /**< Decoder state. */
149 SpeexBits dec_bits; /**< Decoder bits. */
150};
151
152
153/*
154 * Get codec bitrate and frame size.
155 */
156static pj_status_t get_speex_info( struct speex_param *p )
157{
158 void *state;
159 int tmp;
160
161 /* Create temporary encoder */
162 state = speex_encoder_init(p->mode);
163 if (!state)
164 return PJMEDIA_CODEC_EFAILED;
165
166 /* Set the quality */
Benny Prijono7bd15132006-03-06 13:32:57 +0000167 if (p->quality != -1)
168 speex_encoder_ctl(state, SPEEX_SET_QUALITY, &p->quality);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000169
170 /* Sampling rate. */
171 speex_encoder_ctl(state, SPEEX_SET_SAMPLING_RATE, &p->clock_rate);
172
Benny Prijono7bd15132006-03-06 13:32:57 +0000173 /* VAD off to have max bitrate */
174 tmp = 0;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000175 speex_encoder_ctl(state, SPEEX_SET_VAD, &tmp);
176
177 /* Complexity. */
Benny Prijono7bd15132006-03-06 13:32:57 +0000178 if (p->complexity != -1)
179 speex_encoder_ctl(state, SPEEX_SET_COMPLEXITY, &p->complexity);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000180
181 /* Now get the frame size */
182 speex_encoder_ctl(state, SPEEX_GET_FRAME_SIZE, &p->samples_per_frame);
183
184 /* Now get the the averate bitrate */
185 speex_encoder_ctl(state, SPEEX_GET_BITRATE, &p->bitrate);
186
187 /* Calculate framesize. */
188 p->framesize = p->bitrate * 20 / 1000;
189
190 /* Destroy encoder. */
191 speex_encoder_destroy(state);
192
193 return PJ_SUCCESS;
194}
195
196/*
197 * Initialize and register Speex codec factory to pjmedia endpoint.
198 */
199PJ_DEF(pj_status_t) pjmedia_codec_speex_init( pjmedia_endpt *endpt,
200 unsigned options,
201 int quality,
202 int complexity )
203{
204 pjmedia_codec_mgr *codec_mgr;
205 unsigned i;
206 pj_status_t status;
207
208 if (spx_factory.pool != NULL) {
209 /* Already initialized. */
210 return PJ_SUCCESS;
211 }
212
213 /* Get defaults */
Benny Prijonoa4cc47b2006-06-01 11:39:48 +0000214 if (quality <= 0) quality = DEFAULT_QUALITY;
215 if (complexity <= 0) complexity = DEFAULT_COMPLEXITY;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000216
217 /* Create Speex codec factory. */
218 spx_factory.base.op = &spx_factory_op;
219 spx_factory.base.factory_data = NULL;
220 spx_factory.endpt = endpt;
221
222 spx_factory.pool = pjmedia_endpt_create_pool(endpt, "speex",
223 4000, 4000);
224 if (!spx_factory.pool)
225 return PJ_ENOMEM;
226
227 pj_list_init(&spx_factory.codec_list);
228
229 /* Create mutex. */
230 status = pj_mutex_create_simple(spx_factory.pool, "speex",
231 &spx_factory.mutex);
232 if (status != PJ_SUCCESS)
233 goto on_error;
234
235 /* Initialize default Speex parameter. */
236 spx_factory.speex_param[PARAM_NB].enabled =
237 ((options & PJMEDIA_SPEEX_NO_NB) == 0);
Benny Prijonoa837c302006-04-27 22:36:40 +0000238 spx_factory.speex_param[PARAM_NB].pt = PJMEDIA_RTP_PT_SPEEX_NB;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000239 spx_factory.speex_param[PARAM_NB].mode = &speex_nb_mode;
240 spx_factory.speex_param[PARAM_NB].clock_rate = 8000;
241 spx_factory.speex_param[PARAM_NB].quality = quality;
242 spx_factory.speex_param[PARAM_NB].complexity = complexity;
243
244 spx_factory.speex_param[PARAM_WB].enabled =
245 ((options & PJMEDIA_SPEEX_NO_WB) == 0);
Benny Prijonoa837c302006-04-27 22:36:40 +0000246 spx_factory.speex_param[PARAM_WB].pt = PJMEDIA_RTP_PT_SPEEX_WB;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000247 spx_factory.speex_param[PARAM_WB].mode = &speex_wb_mode;
248 spx_factory.speex_param[PARAM_WB].clock_rate = 16000;
249 spx_factory.speex_param[PARAM_WB].quality = quality;
250 spx_factory.speex_param[PARAM_WB].complexity = complexity;
251
252 spx_factory.speex_param[PARAM_UWB].enabled =
253 ((options & PJMEDIA_SPEEX_NO_UWB) == 0);
Benny Prijonoa837c302006-04-27 22:36:40 +0000254 spx_factory.speex_param[PARAM_UWB].pt = PJMEDIA_RTP_PT_SPEEX_UWB;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000255 spx_factory.speex_param[PARAM_UWB].mode = &speex_uwb_mode;
256 spx_factory.speex_param[PARAM_UWB].clock_rate = 32000;
257 spx_factory.speex_param[PARAM_UWB].quality = quality;
258 spx_factory.speex_param[PARAM_UWB].complexity = complexity;
259
Benny Prijonof6aba4e2006-03-06 16:24:50 +0000260 /* Somehow quality <=4 is broken in linux. */
Benny Prijono70c68912006-05-19 15:58:13 +0000261 if (quality <= 4 && quality >= 0) {
262 PJ_LOG(5,(THIS_FILE, "Adjusting quality to 5 for uwb"));
Benny Prijonof6aba4e2006-03-06 16:24:50 +0000263 spx_factory.speex_param[PARAM_UWB].quality = 5;
264 }
265
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000266 /* Get codec framesize and avg bitrate for each mode. */
267 for (i=0; i<PJ_ARRAY_SIZE(spx_factory.speex_param); ++i) {
268 status = get_speex_info(&spx_factory.speex_param[i]);
269 }
270
271 /* Get the codec manager. */
272 codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
273 if (!codec_mgr) {
274 status = PJ_EINVALIDOP;
275 goto on_error;
276 }
277
278 /* Register codec factory to endpoint. */
279 status = pjmedia_codec_mgr_register_factory(codec_mgr,
280 &spx_factory.base);
281 if (status != PJ_SUCCESS)
282 goto on_error;
283
284 /* Done. */
285 return PJ_SUCCESS;
286
287on_error:
288 pj_pool_release(spx_factory.pool);
289 spx_factory.pool = NULL;
290 return status;
291}
292
293
294/*
295 * Initialize with default settings.
296 */
297PJ_DEF(pj_status_t) pjmedia_codec_speex_init_default(pjmedia_endpt *endpt)
298{
299 return pjmedia_codec_speex_init(endpt, 0, -1, -1);
300}
301
302/*
303 * Unregister Speex codec factory from pjmedia endpoint and deinitialize
304 * the Speex codec library.
305 */
306PJ_DEF(pj_status_t) pjmedia_codec_speex_deinit(void)
307{
308 pjmedia_codec_mgr *codec_mgr;
309 pj_status_t status;
310
311 if (spx_factory.pool == NULL) {
312 /* Already deinitialized */
313 return PJ_SUCCESS;
314 }
315
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000316 pj_mutex_lock(spx_factory.mutex);
Benny Prijonoab082f32006-11-19 20:28:34 +0000317
318 /* We don't want to deinit if there's outstanding codec. */
319 /* This is silly, as we'll always have codec in the list if
320 we ever allocate a codec! A better behavior maybe is to
321 deallocate all codecs in the list.
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000322 if (!pj_list_empty(&spx_factory.codec_list)) {
323 pj_mutex_unlock(spx_factory.mutex);
324 return PJ_EBUSY;
325 }
Benny Prijonoab082f32006-11-19 20:28:34 +0000326 */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000327
328 /* Get the codec manager. */
329 codec_mgr = pjmedia_endpt_get_codec_mgr(spx_factory.endpt);
330 if (!codec_mgr) {
331 pj_pool_release(spx_factory.pool);
332 spx_factory.pool = NULL;
333 return PJ_EINVALIDOP;
334 }
335
336 /* Unregister Speex codec factory. */
337 status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
338 &spx_factory.base);
339
340 /* Destroy mutex. */
341 pj_mutex_destroy(spx_factory.mutex);
342
343 /* Destroy pool. */
344 pj_pool_release(spx_factory.pool);
345 spx_factory.pool = NULL;
346
347 return status;
348}
349
350/*
351 * Check if factory can allocate the specified codec.
352 */
353static pj_status_t spx_test_alloc( pjmedia_codec_factory *factory,
354 const pjmedia_codec_info *info )
355{
356 const pj_str_t speex_tag = { "speex", 5};
357 unsigned i;
358
359 PJ_UNUSED_ARG(factory);
360
361 /* Type MUST be audio. */
362 if (info->type != PJMEDIA_TYPE_AUDIO)
363 return PJMEDIA_CODEC_EUNSUP;
364
365 /* Check encoding name. */
366 if (pj_stricmp(&info->encoding_name, &speex_tag) != 0)
367 return PJMEDIA_CODEC_EUNSUP;
368
369 /* Check clock-rate */
370 for (i=0; i<PJ_ARRAY_SIZE(spx_factory.speex_param); ++i) {
Benny Prijonoa837c302006-04-27 22:36:40 +0000371 if (info->clock_rate == spx_factory.speex_param[i].clock_rate) {
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000372 /* Okay, let's Speex! */
373 return PJ_SUCCESS;
374 }
375 }
376
377
378 /* Unsupported, or mode is disabled. */
379 return PJMEDIA_CODEC_EUNSUP;
380}
381
382/*
383 * Generate default attribute.
384 */
385static pj_status_t spx_default_attr (pjmedia_codec_factory *factory,
386 const pjmedia_codec_info *id,
387 pjmedia_codec_param *attr )
388{
389
390 PJ_ASSERT_RETURN(factory==&spx_factory.base, PJ_EINVAL);
391
Benny Prijonoac623b32006-07-03 15:19:31 +0000392 pj_bzero(attr, sizeof(pjmedia_codec_param));
Benny Prijono8befd9f2006-05-13 22:46:23 +0000393 attr->info.pt = (pj_uint8_t)id->pt;
394 attr->info.channel_cnt = 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000395
Benny Prijonoa837c302006-04-27 22:36:40 +0000396 if (id->clock_rate <= 8000) {
Benny Prijono8befd9f2006-05-13 22:46:23 +0000397 attr->info.clock_rate = spx_factory.speex_param[PARAM_NB].clock_rate;
398 attr->info.avg_bps = spx_factory.speex_param[PARAM_NB].bitrate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000399
Benny Prijonoa837c302006-04-27 22:36:40 +0000400 } else if (id->clock_rate <= 16000) {
Benny Prijono8befd9f2006-05-13 22:46:23 +0000401 attr->info.clock_rate = spx_factory.speex_param[PARAM_WB].clock_rate;
402 attr->info.avg_bps = spx_factory.speex_param[PARAM_WB].bitrate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000403
404 } else {
405 /* Wow.. somebody is doing ultra-wideband. Cool...! */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000406 attr->info.clock_rate = spx_factory.speex_param[PARAM_UWB].clock_rate;
407 attr->info.avg_bps = spx_factory.speex_param[PARAM_UWB].bitrate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000408 }
409
Benny Prijono8befd9f2006-05-13 22:46:23 +0000410 attr->info.pcm_bits_per_sample = 16;
411 attr->info.frm_ptime = 20;
412 attr->info.pt = (pj_uint8_t)id->pt;
413
414 attr->setting.frm_per_pkt = 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000415
416 /* Default flags. */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000417 attr->setting.cng = 1;
418 attr->setting.plc = 1;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000419 attr->setting.penh =1 ;
Benny Prijono70c68912006-05-19 15:58:13 +0000420 attr->setting.vad = 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000421
422 return PJ_SUCCESS;
423}
424
425/*
426 * Enum codecs supported by this factory (i.e. only Speex!).
427 */
428static pj_status_t spx_enum_codecs(pjmedia_codec_factory *factory,
429 unsigned *count,
430 pjmedia_codec_info codecs[])
431{
432 unsigned max;
433 int i; /* Must be signed */
434
435 PJ_UNUSED_ARG(factory);
436 PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
437
438 max = *count;
439 *count = 0;
440
441 /*
442 * We return three codecs here, and in this order:
443 * - ultra-wideband, wideband, and narrowband.
444 */
445 for (i=PJ_ARRAY_SIZE(spx_factory.speex_param)-1; i>=0 && *count<max; --i) {
446
447 if (!spx_factory.speex_param[i].enabled)
448 continue;
449
Benny Prijonoac623b32006-07-03 15:19:31 +0000450 pj_bzero(&codecs[*count], sizeof(pjmedia_codec_info));
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000451 codecs[*count].encoding_name = pj_str("speex");
452 codecs[*count].pt = spx_factory.speex_param[i].pt;
453 codecs[*count].type = PJMEDIA_TYPE_AUDIO;
Benny Prijonoa837c302006-04-27 22:36:40 +0000454 codecs[*count].clock_rate = spx_factory.speex_param[i].clock_rate;
455 codecs[*count].channel_cnt = 1;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000456
457 ++*count;
458 }
459
460 return PJ_SUCCESS;
461}
462
463/*
464 * Allocate a new Speex codec instance.
465 */
466static pj_status_t spx_alloc_codec( pjmedia_codec_factory *factory,
467 const pjmedia_codec_info *id,
468 pjmedia_codec **p_codec)
469{
470 pjmedia_codec *codec;
471 struct spx_private *spx;
472
473 PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
474 PJ_ASSERT_RETURN(factory == &spx_factory.base, PJ_EINVAL);
475
476
477 pj_mutex_lock(spx_factory.mutex);
478
479 /* Get free nodes, if any. */
480 if (!pj_list_empty(&spx_factory.codec_list)) {
481 codec = spx_factory.codec_list.next;
482 pj_list_erase(codec);
483 } else {
484 codec = pj_pool_zalloc(spx_factory.pool,
485 sizeof(pjmedia_codec));
486 PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM);
487 codec->op = &spx_op;
488 codec->factory = factory;
489 codec->codec_data = pj_pool_alloc(spx_factory.pool,
490 sizeof(struct spx_private));
491 }
492
493 pj_mutex_unlock(spx_factory.mutex);
494
495 spx = (struct spx_private*) codec->codec_data;
496 spx->enc = NULL;
497 spx->dec = NULL;
498
Benny Prijonoa837c302006-04-27 22:36:40 +0000499 if (id->clock_rate <= 8000)
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000500 spx->param_id = PARAM_NB;
Benny Prijonoa837c302006-04-27 22:36:40 +0000501 else if (id->clock_rate <= 16000)
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000502 spx->param_id = PARAM_WB;
503 else
504 spx->param_id = PARAM_UWB;
505
506 *p_codec = codec;
507 return PJ_SUCCESS;
508}
509
510/*
511 * Free codec.
512 */
513static pj_status_t spx_dealloc_codec( pjmedia_codec_factory *factory,
514 pjmedia_codec *codec )
515{
516 struct spx_private *spx;
517
518 PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
519 PJ_ASSERT_RETURN(factory == &spx_factory.base, PJ_EINVAL);
520
521 /* Close codec, if it's not closed. */
522 spx = (struct spx_private*) codec->codec_data;
523 if (spx->enc != NULL || spx->dec != NULL) {
524 spx_codec_close(codec);
525 }
526
527 /* Put in the free list. */
528 pj_mutex_lock(spx_factory.mutex);
529 pj_list_push_front(&spx_factory.codec_list, codec);
530 pj_mutex_unlock(spx_factory.mutex);
531
532 return PJ_SUCCESS;
533}
534
535/*
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000536 * Init codec.
537 */
538static pj_status_t spx_codec_init( pjmedia_codec *codec,
539 pj_pool_t *pool )
540{
541 PJ_UNUSED_ARG(codec);
542 PJ_UNUSED_ARG(pool);
543 return PJ_SUCCESS;
544}
545
546/*
547 * Open codec.
548 */
549static pj_status_t spx_codec_open( pjmedia_codec *codec,
Benny Prijono6865a3f2006-12-30 02:46:57 +0000550 pjmedia_codec_param *attr )
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000551{
552 struct spx_private *spx;
553 int id, tmp;
554
555 spx = (struct spx_private*) codec->codec_data;
556 id = spx->param_id;
557
Benny Prijonob94a6ab2006-12-26 21:18:11 +0000558 /* Only supports one frame per packet */
559 PJ_ASSERT_RETURN(attr->setting.frm_per_pkt==1, PJ_EINVAL);
560
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000561 /*
562 * Create and initialize encoder.
563 */
564 spx->enc = speex_encoder_init(spx_factory.speex_param[id].mode);
565 if (!spx->enc)
566 return PJMEDIA_CODEC_EFAILED;
567 speex_bits_init(&spx->enc_bits);
568
569 /* Set the quality*/
Benny Prijono7bd15132006-03-06 13:32:57 +0000570 if (spx_factory.speex_param[id].quality != -1) {
571 speex_encoder_ctl(spx->enc, SPEEX_SET_QUALITY,
572 &spx_factory.speex_param[id].quality);
573 }
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000574
575 /* Sampling rate. */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000576 tmp = attr->info.clock_rate;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000577 speex_encoder_ctl(spx->enc, SPEEX_SET_SAMPLING_RATE,
578 &spx_factory.speex_param[id].clock_rate);
579
580 /* VAD */
Benny Prijono70c68912006-05-19 15:58:13 +0000581 tmp = (attr->setting.vad != 0);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000582 speex_encoder_ctl(spx->enc, SPEEX_SET_VAD, &tmp);
Benny Prijono70c68912006-05-19 15:58:13 +0000583 speex_encoder_ctl(spx->enc, SPEEX_SET_DTX, &tmp);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000584
585 /* Complexity */
Benny Prijono7bd15132006-03-06 13:32:57 +0000586 if (spx_factory.speex_param[id].complexity != -1) {
587 speex_encoder_ctl(spx->enc, SPEEX_SET_COMPLEXITY,
588 &spx_factory.speex_param[id].complexity);
589 }
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000590
591 /*
592 * Create and initialize decoder.
593 */
594 spx->dec = speex_decoder_init(spx_factory.speex_param[id].mode);
595 if (!spx->dec) {
596 spx_codec_close(codec);
597 return PJMEDIA_CODEC_EFAILED;
598 }
599 speex_bits_init(&spx->dec_bits);
600
601 /* Sampling rate. */
602 speex_decoder_ctl(spx->dec, SPEEX_SET_SAMPLING_RATE,
603 &spx_factory.speex_param[id].clock_rate);
604
605 /* PENH */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000606 tmp = attr->setting.penh;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000607 speex_decoder_ctl(spx->dec, SPEEX_SET_ENH, &tmp);
608
609 return PJ_SUCCESS;
610}
611
612/*
613 * Close codec.
614 */
615static pj_status_t spx_codec_close( pjmedia_codec *codec )
616{
617 struct spx_private *spx;
618
619 spx = (struct spx_private*) codec->codec_data;
620
621 /* Destroy encoder*/
622 if (spx->enc) {
623 speex_encoder_destroy( spx->enc );
624 spx->enc = NULL;
625 speex_bits_destroy( &spx->enc_bits );
626 }
627
628 /* Destroy decoder */
629 if (spx->dec) {
630 speex_decoder_destroy( spx->dec);
631 spx->dec = NULL;
632 speex_bits_destroy( &spx->dec_bits );
633 }
634
635 return PJ_SUCCESS;
636}
637
638
639/*
Benny Prijonob94a6ab2006-12-26 21:18:11 +0000640 * Modify codec settings.
641 */
642static pj_status_t spx_codec_modify(pjmedia_codec *codec,
643 const pjmedia_codec_param *attr )
644{
645 struct spx_private *spx;
646 int tmp;
647
648 spx = (struct spx_private*) codec->codec_data;
649
650 /* Only supports one frame per packet */
651 PJ_ASSERT_RETURN(attr->setting.frm_per_pkt==1, PJ_EINVAL);
652
653 /* VAD */
654 tmp = (attr->setting.vad != 0);
655 speex_encoder_ctl(spx->enc, SPEEX_SET_VAD, &tmp);
656 speex_encoder_ctl(spx->enc, SPEEX_SET_DTX, &tmp);
657
658 /* PENH */
659 tmp = attr->setting.penh;
660 speex_decoder_ctl(spx->dec, SPEEX_SET_ENH, &tmp);
661
662 return PJ_SUCCESS;
663}
664
665
666/*
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000667 * Get frames in the packet.
668 */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000669static pj_status_t spx_codec_parse( pjmedia_codec *codec,
670 void *pkt,
671 pj_size_t pkt_size,
672 const pj_timestamp *ts,
673 unsigned *frame_cnt,
674 pjmedia_frame frames[])
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000675{
676 struct spx_private *spx;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000677 unsigned frame_size, samples_per_frame;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000678 unsigned count;
679
680 spx = (struct spx_private*) codec->codec_data;
681
Benny Prijono8befd9f2006-05-13 22:46:23 +0000682 frame_size = spx_factory.speex_param[spx->param_id].framesize;
683 samples_per_frame = spx_factory.speex_param[spx->param_id].samples_per_frame;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000684
685 /* Don't really know how to do this... */
686 count = 0;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000687 while (pkt_size >= frame_size && count < *frame_cnt) {
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000688 frames[count].buf = pkt;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000689 frames[count].size = frame_size;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000690 frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000691 frames[count].timestamp.u64 = ts->u64 + count * samples_per_frame;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000692
Benny Prijono8befd9f2006-05-13 22:46:23 +0000693 pkt_size -= frame_size;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000694 ++count;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000695 pkt = ((char*)pkt) + frame_size;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000696 }
697
Benny Prijono8befd9f2006-05-13 22:46:23 +0000698 /* Just in case speex has silence frame which size is less than normal
699 * frame size...
700 */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000701 if (pkt_size && count < *frame_cnt) {
702 frames[count].buf = pkt;
703 frames[count].size = pkt_size;
704 frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000705 frames[count].timestamp.u64 = ts->u64 + count * samples_per_frame;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000706 ++count;
707 }
708
709 *frame_cnt = count;
710 return PJ_SUCCESS;
711}
712
713/*
714 * Encode frame.
715 */
716static pj_status_t spx_codec_encode( pjmedia_codec *codec,
717 const struct pjmedia_frame *input,
718 unsigned output_buf_len,
719 struct pjmedia_frame *output)
720{
721 struct spx_private *spx;
Benny Prijonodc498ca2006-07-26 17:04:54 +0000722 unsigned sz;
Benny Prijono70c68912006-05-19 15:58:13 +0000723 int tx;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000724
725 spx = (struct spx_private*) codec->codec_data;
726
727 if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) {
728 output->size = 0;
729 output->buf = NULL;
730 output->timestamp = input->timestamp;
731 output->type = input->type;
732 return PJ_SUCCESS;
733 }
734
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000735 /* Flush all the bits in the struct so we can encode a new frame */
736 speex_bits_reset(&spx->enc_bits);
737
738 /* Encode the frame */
Benny Prijonodc498ca2006-07-26 17:04:54 +0000739 tx = speex_encode_int(spx->enc, input->buf, &spx->enc_bits);
Benny Prijono70c68912006-05-19 15:58:13 +0000740
741 /* Check if we need not to transmit the frame (DTX) */
742 if (tx == 0) {
743 output->buf = NULL;
744 output->size = 0;
745 output->timestamp.u64 = input->timestamp.u64;
746 output->type = PJMEDIA_FRAME_TYPE_NONE;
747 return PJ_SUCCESS;
748 }
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000749
Benny Prijono856ebc32006-03-05 11:51:10 +0000750 /* Check size. */
751 sz = speex_bits_nbytes(&spx->enc_bits);
752 pj_assert(sz <= output_buf_len);
753
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000754 /* Copy the bits to an array of char that can be written */
755 output->size = speex_bits_write(&spx->enc_bits,
756 output->buf, output_buf_len);
757 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
758 output->timestamp = input->timestamp;
759
760 return PJ_SUCCESS;
761}
762
763/*
764 * Decode frame.
765 */
766static pj_status_t spx_codec_decode( pjmedia_codec *codec,
767 const struct pjmedia_frame *input,
768 unsigned output_buf_len,
769 struct pjmedia_frame *output)
770{
771 struct spx_private *spx;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000772
773 spx = (struct spx_private*) codec->codec_data;
774
775 if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) {
Benny Prijonoac623b32006-07-03 15:19:31 +0000776 pj_bzero(output->buf, output_buf_len);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000777 output->size = 320;
778 output->timestamp.u64 = input->timestamp.u64;
779 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
780 return PJ_SUCCESS;
781 }
782
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000783 /* Copy the data into the bit-stream struct */
784 speex_bits_read_from(&spx->dec_bits, input->buf, input->size);
785
786 /* Decode the data */
Benny Prijonodc498ca2006-07-26 17:04:54 +0000787 speex_decode_int(spx->dec, &spx->dec_bits, output->buf);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000788
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000789 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
Benny Prijonodc498ca2006-07-26 17:04:54 +0000790 output->size = speex_bits_nbytes(&spx->dec_bits);
791 pj_assert(output->size <= (int)output_buf_len);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000792 output->timestamp.u64 = input->timestamp.u64;
793
794
795 return PJ_SUCCESS;
796}
Benny Prijono4381efe2006-03-16 14:24:26 +0000797
Benny Prijono8befd9f2006-05-13 22:46:23 +0000798/*
799 * Recover lost frame.
800 */
801static pj_status_t spx_codec_recover(pjmedia_codec *codec,
802 unsigned output_buf_len,
803 struct pjmedia_frame *output)
804{
805 struct spx_private *spx;
Benny Prijonodc498ca2006-07-26 17:04:54 +0000806 unsigned count;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000807
Benny Prijono60281662006-06-22 18:39:33 +0000808 /* output_buf_len is unreferenced when building in Release mode */
809 PJ_UNUSED_ARG(output_buf_len);
810
Benny Prijono8befd9f2006-05-13 22:46:23 +0000811 spx = (struct spx_private*) codec->codec_data;
812
813 count = spx_factory.speex_param[spx->param_id].clock_rate * 20 / 1000;
Benny Prijonodc498ca2006-07-26 17:04:54 +0000814 pj_assert(count <= output_buf_len/2);
Benny Prijono8befd9f2006-05-13 22:46:23 +0000815
816 /* Recover packet loss */
Benny Prijonodc498ca2006-07-26 17:04:54 +0000817 speex_decode_int(spx->dec, NULL, output->buf);
Benny Prijono8befd9f2006-05-13 22:46:23 +0000818
Benny Prijono8befd9f2006-05-13 22:46:23 +0000819 output->size = count * 2;
820
821 return PJ_SUCCESS;
822}
823
Benny Prijono4381efe2006-03-16 14:24:26 +0000824
825#endif /* PJMEDIA_HAS_SPEEX_CODEC */