blob: 590f4061b10be34168aae694efe0aa18f8375e55 [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>
27#include <pj/pool.h>
28#include <pj/string.h>
29#include <pj/os.h>
30
Benny Prijono7bd15132006-03-06 13:32:57 +000031#define DEFAULT_QUALITY 4
32#define DEFAULT_COMPLEXITY -1
Benny Prijonoeb30bf52006-03-04 20:43:52 +000033
34
35/* Prototypes for Speex factory */
36static pj_status_t spx_test_alloc( pjmedia_codec_factory *factory,
37 const pjmedia_codec_info *id );
38static pj_status_t spx_default_attr( pjmedia_codec_factory *factory,
39 const pjmedia_codec_info *id,
40 pjmedia_codec_param *attr );
41static pj_status_t spx_enum_codecs( pjmedia_codec_factory *factory,
42 unsigned *count,
43 pjmedia_codec_info codecs[]);
44static pj_status_t spx_alloc_codec( pjmedia_codec_factory *factory,
45 const pjmedia_codec_info *id,
46 pjmedia_codec **p_codec);
47static pj_status_t spx_dealloc_codec( pjmedia_codec_factory *factory,
48 pjmedia_codec *codec );
49
50/* Prototypes for Speex implementation. */
51static pj_status_t spx_codec_default_attr(pjmedia_codec *codec,
52 pjmedia_codec_param *attr);
53static pj_status_t spx_codec_init( pjmedia_codec *codec,
54 pj_pool_t *pool );
55static pj_status_t spx_codec_open( pjmedia_codec *codec,
56 pjmedia_codec_param *attr );
57static pj_status_t spx_codec_close( pjmedia_codec *codec );
58static pj_status_t spx_codec_get_frames( pjmedia_codec *codec,
59 void *pkt,
60 pj_size_t pkt_size,
61 unsigned *frame_cnt,
62 pjmedia_frame frames[]);
63static pj_status_t spx_codec_encode( pjmedia_codec *codec,
64 const struct pjmedia_frame *input,
65 unsigned output_buf_len,
66 struct pjmedia_frame *output);
67static pj_status_t spx_codec_decode( pjmedia_codec *codec,
68 const struct pjmedia_frame *input,
69 unsigned output_buf_len,
70 struct pjmedia_frame *output);
71
72/* Definition for Speex codec operations. */
73static pjmedia_codec_op spx_op =
74{
75 &spx_codec_default_attr,
76 &spx_codec_init,
77 &spx_codec_open,
78 &spx_codec_close,
79 &spx_codec_get_frames,
80 &spx_codec_encode,
81 &spx_codec_decode
82};
83
84/* Definition for Speex codec factory operations. */
85static pjmedia_codec_factory_op spx_factory_op =
86{
87 &spx_test_alloc,
88 &spx_default_attr,
89 &spx_enum_codecs,
90 &spx_alloc_codec,
91 &spx_dealloc_codec
92};
93
94/* Index to Speex parameter. */
95enum
96{
97 PARAM_NB, /* Index for narrowband parameter. */
98 PARAM_WB, /* Index for wideband parameter. */
99 PARAM_UWB, /* Index for ultra-wideband parameter */
100};
101
102/* Speex default parameter */
103struct speex_param
104{
Benny Prijono7bd15132006-03-06 13:32:57 +0000105 int enabled; /* Is this mode enabled? */
106 const SpeexMode *mode; /* Speex mode. */
107 int pt; /* Payload type. */
108 unsigned clock_rate; /* Default sampling rate to be used.*/
109 int quality; /* Default encoder quality. */
110 int complexity; /* Default encoder complexity. */
111 int samples_per_frame; /* Samples per frame. */
112 int framesize; /* Frame size for current mode. */
113 int bitrate; /* Bit rate for current mode. */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000114};
115
116/* Speex factory */
117static struct spx_factory
118{
119 pjmedia_codec_factory base;
120 pjmedia_endpt *endpt;
121 pj_pool_t *pool;
122 pj_mutex_t *mutex;
123 pjmedia_codec codec_list;
124 struct speex_param speex_param[3];
125
126} spx_factory;
127
128/* Speex codec private data. */
129struct spx_private
130{
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000131 int param_id; /**< Index to speex param. */
132
133 void *enc; /**< Encoder state. */
134 SpeexBits enc_bits; /**< Encoder bits. */
135 void *dec; /**< Decoder state. */
136 SpeexBits dec_bits; /**< Decoder bits. */
137};
138
139
140/*
141 * Get codec bitrate and frame size.
142 */
143static pj_status_t get_speex_info( struct speex_param *p )
144{
145 void *state;
146 int tmp;
147
148 /* Create temporary encoder */
149 state = speex_encoder_init(p->mode);
150 if (!state)
151 return PJMEDIA_CODEC_EFAILED;
152
153 /* Set the quality */
Benny Prijono7bd15132006-03-06 13:32:57 +0000154 if (p->quality != -1)
155 speex_encoder_ctl(state, SPEEX_SET_QUALITY, &p->quality);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000156
157 /* Sampling rate. */
158 speex_encoder_ctl(state, SPEEX_SET_SAMPLING_RATE, &p->clock_rate);
159
Benny Prijono7bd15132006-03-06 13:32:57 +0000160 /* VAD off to have max bitrate */
161 tmp = 0;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000162 speex_encoder_ctl(state, SPEEX_SET_VAD, &tmp);
163
164 /* Complexity. */
Benny Prijono7bd15132006-03-06 13:32:57 +0000165 if (p->complexity != -1)
166 speex_encoder_ctl(state, SPEEX_SET_COMPLEXITY, &p->complexity);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000167
168 /* Now get the frame size */
169 speex_encoder_ctl(state, SPEEX_GET_FRAME_SIZE, &p->samples_per_frame);
170
171 /* Now get the the averate bitrate */
172 speex_encoder_ctl(state, SPEEX_GET_BITRATE, &p->bitrate);
173
174 /* Calculate framesize. */
175 p->framesize = p->bitrate * 20 / 1000;
176
177 /* Destroy encoder. */
178 speex_encoder_destroy(state);
179
180 return PJ_SUCCESS;
181}
182
183/*
184 * Initialize and register Speex codec factory to pjmedia endpoint.
185 */
186PJ_DEF(pj_status_t) pjmedia_codec_speex_init( pjmedia_endpt *endpt,
187 unsigned options,
188 int quality,
189 int complexity )
190{
191 pjmedia_codec_mgr *codec_mgr;
192 unsigned i;
193 pj_status_t status;
194
195 if (spx_factory.pool != NULL) {
196 /* Already initialized. */
197 return PJ_SUCCESS;
198 }
199
200 /* Get defaults */
201 if (quality < 0) quality = DEFAULT_QUALITY;
202 if (complexity < 0) complexity = DEFAULT_COMPLEXITY;
203
204 /* Create Speex codec factory. */
205 spx_factory.base.op = &spx_factory_op;
206 spx_factory.base.factory_data = NULL;
207 spx_factory.endpt = endpt;
208
209 spx_factory.pool = pjmedia_endpt_create_pool(endpt, "speex",
210 4000, 4000);
211 if (!spx_factory.pool)
212 return PJ_ENOMEM;
213
214 pj_list_init(&spx_factory.codec_list);
215
216 /* Create mutex. */
217 status = pj_mutex_create_simple(spx_factory.pool, "speex",
218 &spx_factory.mutex);
219 if (status != PJ_SUCCESS)
220 goto on_error;
221
222 /* Initialize default Speex parameter. */
223 spx_factory.speex_param[PARAM_NB].enabled =
224 ((options & PJMEDIA_SPEEX_NO_NB) == 0);
225 spx_factory.speex_param[PARAM_NB].pt = 102;
226 spx_factory.speex_param[PARAM_NB].mode = &speex_nb_mode;
227 spx_factory.speex_param[PARAM_NB].clock_rate = 8000;
228 spx_factory.speex_param[PARAM_NB].quality = quality;
229 spx_factory.speex_param[PARAM_NB].complexity = complexity;
230
231 spx_factory.speex_param[PARAM_WB].enabled =
232 ((options & PJMEDIA_SPEEX_NO_WB) == 0);
233 spx_factory.speex_param[PARAM_WB].pt = 103;
234 spx_factory.speex_param[PARAM_WB].mode = &speex_wb_mode;
235 spx_factory.speex_param[PARAM_WB].clock_rate = 16000;
236 spx_factory.speex_param[PARAM_WB].quality = quality;
237 spx_factory.speex_param[PARAM_WB].complexity = complexity;
238
239 spx_factory.speex_param[PARAM_UWB].enabled =
240 ((options & PJMEDIA_SPEEX_NO_UWB) == 0);
241 spx_factory.speex_param[PARAM_UWB].pt = 104;
242 spx_factory.speex_param[PARAM_UWB].mode = &speex_uwb_mode;
243 spx_factory.speex_param[PARAM_UWB].clock_rate = 32000;
244 spx_factory.speex_param[PARAM_UWB].quality = quality;
245 spx_factory.speex_param[PARAM_UWB].complexity = complexity;
246
247 /* Get codec framesize and avg bitrate for each mode. */
248 for (i=0; i<PJ_ARRAY_SIZE(spx_factory.speex_param); ++i) {
249 status = get_speex_info(&spx_factory.speex_param[i]);
250 }
251
252 /* Get the codec manager. */
253 codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
254 if (!codec_mgr) {
255 status = PJ_EINVALIDOP;
256 goto on_error;
257 }
258
259 /* Register codec factory to endpoint. */
260 status = pjmedia_codec_mgr_register_factory(codec_mgr,
261 &spx_factory.base);
262 if (status != PJ_SUCCESS)
263 goto on_error;
264
265 /* Done. */
266 return PJ_SUCCESS;
267
268on_error:
269 pj_pool_release(spx_factory.pool);
270 spx_factory.pool = NULL;
271 return status;
272}
273
274
275/*
276 * Initialize with default settings.
277 */
278PJ_DEF(pj_status_t) pjmedia_codec_speex_init_default(pjmedia_endpt *endpt)
279{
280 return pjmedia_codec_speex_init(endpt, 0, -1, -1);
281}
282
283/*
284 * Unregister Speex codec factory from pjmedia endpoint and deinitialize
285 * the Speex codec library.
286 */
287PJ_DEF(pj_status_t) pjmedia_codec_speex_deinit(void)
288{
289 pjmedia_codec_mgr *codec_mgr;
290 pj_status_t status;
291
292 if (spx_factory.pool == NULL) {
293 /* Already deinitialized */
294 return PJ_SUCCESS;
295 }
296
297 /* We don't want to deinit if there's outstanding codec. */
298 pj_mutex_lock(spx_factory.mutex);
299 if (!pj_list_empty(&spx_factory.codec_list)) {
300 pj_mutex_unlock(spx_factory.mutex);
301 return PJ_EBUSY;
302 }
303
304 /* Get the codec manager. */
305 codec_mgr = pjmedia_endpt_get_codec_mgr(spx_factory.endpt);
306 if (!codec_mgr) {
307 pj_pool_release(spx_factory.pool);
308 spx_factory.pool = NULL;
309 return PJ_EINVALIDOP;
310 }
311
312 /* Unregister Speex codec factory. */
313 status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
314 &spx_factory.base);
315
316 /* Destroy mutex. */
317 pj_mutex_destroy(spx_factory.mutex);
318
319 /* Destroy pool. */
320 pj_pool_release(spx_factory.pool);
321 spx_factory.pool = NULL;
322
323 return status;
324}
325
326/*
327 * Check if factory can allocate the specified codec.
328 */
329static pj_status_t spx_test_alloc( pjmedia_codec_factory *factory,
330 const pjmedia_codec_info *info )
331{
332 const pj_str_t speex_tag = { "speex", 5};
333 unsigned i;
334
335 PJ_UNUSED_ARG(factory);
336
337 /* Type MUST be audio. */
338 if (info->type != PJMEDIA_TYPE_AUDIO)
339 return PJMEDIA_CODEC_EUNSUP;
340
341 /* Check encoding name. */
342 if (pj_stricmp(&info->encoding_name, &speex_tag) != 0)
343 return PJMEDIA_CODEC_EUNSUP;
344
345 /* Check clock-rate */
346 for (i=0; i<PJ_ARRAY_SIZE(spx_factory.speex_param); ++i) {
347 if (info->sample_rate == spx_factory.speex_param[i].clock_rate) {
348 /* Okay, let's Speex! */
349 return PJ_SUCCESS;
350 }
351 }
352
353
354 /* Unsupported, or mode is disabled. */
355 return PJMEDIA_CODEC_EUNSUP;
356}
357
358/*
359 * Generate default attribute.
360 */
361static pj_status_t spx_default_attr (pjmedia_codec_factory *factory,
362 const pjmedia_codec_info *id,
363 pjmedia_codec_param *attr )
364{
365
366 PJ_ASSERT_RETURN(factory==&spx_factory.base, PJ_EINVAL);
367
368 pj_memset(attr, 0, sizeof(pjmedia_codec_param));
369 attr->pt = id->pt;
370
371 if (id->sample_rate <= 8000) {
372 attr->sample_rate = spx_factory.speex_param[PARAM_NB].clock_rate;
373 attr->avg_bps = spx_factory.speex_param[PARAM_NB].bitrate;
374
375 } else if (id->sample_rate <= 16000) {
376 attr->sample_rate = spx_factory.speex_param[PARAM_WB].clock_rate;
377 attr->avg_bps = spx_factory.speex_param[PARAM_WB].bitrate;
378
379 } else {
380 /* Wow.. somebody is doing ultra-wideband. Cool...! */
381 attr->sample_rate = spx_factory.speex_param[PARAM_UWB].clock_rate;
382 attr->avg_bps = spx_factory.speex_param[PARAM_UWB].bitrate;
383 }
384
385 attr->pcm_bits_per_sample = 16;
386 attr->ptime = 20;
387 attr->pt = id->pt;
388
389 /* Default flags. */
390 attr->cng_enabled = 1;
391 attr->concl_enabled = 1;
392 attr->hpf_enabled = 1;
393 attr->lpf_enabled =1 ;
394 attr->penh_enabled =1 ;
Benny Prijono7bd15132006-03-06 13:32:57 +0000395
396 /* Default, set VAD off as it caused voice chip off */
397 attr->vad_enabled = 0;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000398
399 return PJ_SUCCESS;
400}
401
402/*
403 * Enum codecs supported by this factory (i.e. only Speex!).
404 */
405static pj_status_t spx_enum_codecs(pjmedia_codec_factory *factory,
406 unsigned *count,
407 pjmedia_codec_info codecs[])
408{
409 unsigned max;
410 int i; /* Must be signed */
411
412 PJ_UNUSED_ARG(factory);
413 PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
414
415 max = *count;
416 *count = 0;
417
418 /*
419 * We return three codecs here, and in this order:
420 * - ultra-wideband, wideband, and narrowband.
421 */
422 for (i=PJ_ARRAY_SIZE(spx_factory.speex_param)-1; i>=0 && *count<max; --i) {
423
424 if (!spx_factory.speex_param[i].enabled)
425 continue;
426
427 pj_memset(&codecs[*count], 0, sizeof(pjmedia_codec_info));
428 codecs[*count].encoding_name = pj_str("speex");
429 codecs[*count].pt = spx_factory.speex_param[i].pt;
430 codecs[*count].type = PJMEDIA_TYPE_AUDIO;
431 codecs[*count].sample_rate = spx_factory.speex_param[i].clock_rate;
432
433 ++*count;
434 }
435
436 return PJ_SUCCESS;
437}
438
439/*
440 * Allocate a new Speex codec instance.
441 */
442static pj_status_t spx_alloc_codec( pjmedia_codec_factory *factory,
443 const pjmedia_codec_info *id,
444 pjmedia_codec **p_codec)
445{
446 pjmedia_codec *codec;
447 struct spx_private *spx;
448
449 PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
450 PJ_ASSERT_RETURN(factory == &spx_factory.base, PJ_EINVAL);
451
452
453 pj_mutex_lock(spx_factory.mutex);
454
455 /* Get free nodes, if any. */
456 if (!pj_list_empty(&spx_factory.codec_list)) {
457 codec = spx_factory.codec_list.next;
458 pj_list_erase(codec);
459 } else {
460 codec = pj_pool_zalloc(spx_factory.pool,
461 sizeof(pjmedia_codec));
462 PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM);
463 codec->op = &spx_op;
464 codec->factory = factory;
465 codec->codec_data = pj_pool_alloc(spx_factory.pool,
466 sizeof(struct spx_private));
467 }
468
469 pj_mutex_unlock(spx_factory.mutex);
470
471 spx = (struct spx_private*) codec->codec_data;
472 spx->enc = NULL;
473 spx->dec = NULL;
474
475 if (id->sample_rate <= 8000)
476 spx->param_id = PARAM_NB;
477 else if (id->sample_rate <= 16000)
478 spx->param_id = PARAM_WB;
479 else
480 spx->param_id = PARAM_UWB;
481
482 *p_codec = codec;
483 return PJ_SUCCESS;
484}
485
486/*
487 * Free codec.
488 */
489static pj_status_t spx_dealloc_codec( pjmedia_codec_factory *factory,
490 pjmedia_codec *codec )
491{
492 struct spx_private *spx;
493
494 PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
495 PJ_ASSERT_RETURN(factory == &spx_factory.base, PJ_EINVAL);
496
497 /* Close codec, if it's not closed. */
498 spx = (struct spx_private*) codec->codec_data;
499 if (spx->enc != NULL || spx->dec != NULL) {
500 spx_codec_close(codec);
501 }
502
503 /* Put in the free list. */
504 pj_mutex_lock(spx_factory.mutex);
505 pj_list_push_front(&spx_factory.codec_list, codec);
506 pj_mutex_unlock(spx_factory.mutex);
507
508 return PJ_SUCCESS;
509}
510
511/*
512 * Get codec default attributes.
513 */
514static pj_status_t spx_codec_default_attr( pjmedia_codec *codec,
515 pjmedia_codec_param *attr)
516{
517 struct spx_private *spx;
518 pjmedia_codec_info info;
519
520 spx = (struct spx_private*) codec->codec_data;
521
522 info.encoding_name = pj_str("speex");
523 info.pt = 200; /* Don't care */
524 info.sample_rate = spx_factory.speex_param[spx->param_id].clock_rate;
525 info.type = PJMEDIA_TYPE_AUDIO;
526
527 return spx_default_attr( codec->factory, &info, attr);
528}
529
530/*
531 * Init codec.
532 */
533static pj_status_t spx_codec_init( pjmedia_codec *codec,
534 pj_pool_t *pool )
535{
536 PJ_UNUSED_ARG(codec);
537 PJ_UNUSED_ARG(pool);
538 return PJ_SUCCESS;
539}
540
541/*
542 * Open codec.
543 */
544static pj_status_t spx_codec_open( pjmedia_codec *codec,
545 pjmedia_codec_param *attr )
546{
547 struct spx_private *spx;
548 int id, tmp;
549
550 spx = (struct spx_private*) codec->codec_data;
551 id = spx->param_id;
552
553 /*
554 * Create and initialize encoder.
555 */
556 spx->enc = speex_encoder_init(spx_factory.speex_param[id].mode);
557 if (!spx->enc)
558 return PJMEDIA_CODEC_EFAILED;
559 speex_bits_init(&spx->enc_bits);
560
561 /* Set the quality*/
Benny Prijono7bd15132006-03-06 13:32:57 +0000562 if (spx_factory.speex_param[id].quality != -1) {
563 speex_encoder_ctl(spx->enc, SPEEX_SET_QUALITY,
564 &spx_factory.speex_param[id].quality);
565 }
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000566
567 /* Sampling rate. */
568 tmp = attr->sample_rate;
569 speex_encoder_ctl(spx->enc, SPEEX_SET_SAMPLING_RATE,
570 &spx_factory.speex_param[id].clock_rate);
571
572 /* VAD */
573 tmp = attr->vad_enabled;
574 speex_encoder_ctl(spx->enc, SPEEX_SET_VAD, &tmp);
575
576 /* Complexity */
Benny Prijono7bd15132006-03-06 13:32:57 +0000577 if (spx_factory.speex_param[id].complexity != -1) {
578 speex_encoder_ctl(spx->enc, SPEEX_SET_COMPLEXITY,
579 &spx_factory.speex_param[id].complexity);
580 }
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000581
582 /*
583 * Create and initialize decoder.
584 */
585 spx->dec = speex_decoder_init(spx_factory.speex_param[id].mode);
586 if (!spx->dec) {
587 spx_codec_close(codec);
588 return PJMEDIA_CODEC_EFAILED;
589 }
590 speex_bits_init(&spx->dec_bits);
591
592 /* Sampling rate. */
593 speex_decoder_ctl(spx->dec, SPEEX_SET_SAMPLING_RATE,
594 &spx_factory.speex_param[id].clock_rate);
595
596 /* PENH */
597 tmp = attr->penh_enabled;
598 speex_decoder_ctl(spx->dec, SPEEX_SET_ENH, &tmp);
599
600 return PJ_SUCCESS;
601}
602
603/*
604 * Close codec.
605 */
606static pj_status_t spx_codec_close( pjmedia_codec *codec )
607{
608 struct spx_private *spx;
609
610 spx = (struct spx_private*) codec->codec_data;
611
612 /* Destroy encoder*/
613 if (spx->enc) {
614 speex_encoder_destroy( spx->enc );
615 spx->enc = NULL;
616 speex_bits_destroy( &spx->enc_bits );
617 }
618
619 /* Destroy decoder */
620 if (spx->dec) {
621 speex_decoder_destroy( spx->dec);
622 spx->dec = NULL;
623 speex_bits_destroy( &spx->dec_bits );
624 }
625
626 return PJ_SUCCESS;
627}
628
629
630/*
631 * Get frames in the packet.
632 */
633static pj_status_t spx_codec_get_frames( pjmedia_codec *codec,
634 void *pkt,
635 pj_size_t pkt_size,
636 unsigned *frame_cnt,
637 pjmedia_frame frames[])
638{
639 struct spx_private *spx;
640 unsigned speex_frame_size;
641 unsigned count;
642
643 spx = (struct spx_private*) codec->codec_data;
644
645 speex_frame_size = spx_factory.speex_param[spx->param_id].framesize;
646
647 /* Don't really know how to do this... */
648 count = 0;
649 while (pkt_size >= speex_frame_size && count < *frame_cnt) {
650 frames[count].buf = pkt;
651 frames[count].size = speex_frame_size;
652 frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
653 frames[count].timestamp.u64 = 0;
654
655 pkt_size -= speex_frame_size;
656 ++count;
657 pkt = ((char*)pkt) + speex_frame_size;
658 }
659
660 if (pkt_size && count < *frame_cnt) {
661 frames[count].buf = pkt;
662 frames[count].size = pkt_size;
663 frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
664 frames[count].timestamp.u64 = 0;
665 ++count;
666 }
667
668 *frame_cnt = count;
669 return PJ_SUCCESS;
670}
671
672/*
673 * Encode frame.
674 */
675static pj_status_t spx_codec_encode( pjmedia_codec *codec,
676 const struct pjmedia_frame *input,
677 unsigned output_buf_len,
678 struct pjmedia_frame *output)
679{
680 struct spx_private *spx;
Benny Prijono856ebc32006-03-05 11:51:10 +0000681 float tmp[642]; /* 20ms at 32KHz + 2 */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000682 pj_int16_t *samp_in;
Benny Prijono856ebc32006-03-05 11:51:10 +0000683 unsigned i, samp_count, sz;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000684
685 spx = (struct spx_private*) codec->codec_data;
686
687 if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) {
688 output->size = 0;
689 output->buf = NULL;
690 output->timestamp = input->timestamp;
691 output->type = input->type;
692 return PJ_SUCCESS;
693 }
694
695 /* Copy frame to float buffer. */
696 samp_count = input->size / 2;
Benny Prijono856ebc32006-03-05 11:51:10 +0000697 pj_assert(samp_count <= PJ_ARRAY_SIZE(tmp));
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000698 samp_in = input->buf;
699 for (i=0; i<samp_count; ++i) {
700 tmp[i] = samp_in[i];
701 }
702
703 /* Flush all the bits in the struct so we can encode a new frame */
704 speex_bits_reset(&spx->enc_bits);
705
706 /* Encode the frame */
707 speex_encode(spx->enc, tmp, &spx->enc_bits);
708
Benny Prijono856ebc32006-03-05 11:51:10 +0000709 /* Check size. */
710 sz = speex_bits_nbytes(&spx->enc_bits);
711 pj_assert(sz <= output_buf_len);
712
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000713 /* Copy the bits to an array of char that can be written */
714 output->size = speex_bits_write(&spx->enc_bits,
715 output->buf, output_buf_len);
716 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
717 output->timestamp = input->timestamp;
718
719 return PJ_SUCCESS;
720}
721
722/*
723 * Decode frame.
724 */
725static pj_status_t spx_codec_decode( pjmedia_codec *codec,
726 const struct pjmedia_frame *input,
727 unsigned output_buf_len,
728 struct pjmedia_frame *output)
729{
730 struct spx_private *spx;
Benny Prijono856ebc32006-03-05 11:51:10 +0000731 float tmp[642]; /* 20ms at 32KHz + 2 */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000732 pj_int16_t *dst_buf;
Benny Prijono856ebc32006-03-05 11:51:10 +0000733 unsigned i, count, sz;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000734
735 spx = (struct spx_private*) codec->codec_data;
736
737 if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) {
738 pj_memset(output->buf, 0, output_buf_len);
739 output->size = 320;
740 output->timestamp.u64 = input->timestamp.u64;
741 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
742 return PJ_SUCCESS;
743 }
744
745 /* Initialization of the structure that holds the bits */
746 speex_bits_init(&spx->dec_bits);
747
748 /* Copy the data into the bit-stream struct */
749 speex_bits_read_from(&spx->dec_bits, input->buf, input->size);
750
751 /* Decode the data */
752 speex_decode(spx->dec, &spx->dec_bits, tmp);
753
Benny Prijono856ebc32006-03-05 11:51:10 +0000754 /* Check size. */
755 sz = speex_bits_nbytes(&spx->enc_bits);
756 pj_assert(sz <= output_buf_len);
757
758 /* Copy from float to short samples. */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000759 count = spx_factory.speex_param[spx->param_id].clock_rate * 20 / 1000;
Benny Prijono856ebc32006-03-05 11:51:10 +0000760 pj_assert((count <= output_buf_len/2) && count <= PJ_ARRAY_SIZE(tmp));
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000761 dst_buf = output->buf;
762 for (i=0; i<count; ++i) {
763 dst_buf[i] = (pj_int16_t)tmp[i];
764 }
765 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
766 output->size = count * 2;
767 output->timestamp.u64 = input->timestamp.u64;
768
769
770 return PJ_SUCCESS;
771}