blob: 34f538b20469ec906031564db41d761e1b86a2f4 [file] [log] [blame]
Benny Prijonoa4bf0212006-02-10 15:57:08 +00001/* $Id$ */
2/*
Benny Prijonoa771a512007-02-19 01:13:53 +00003 * Copyright (C)2003-2007 Benny Prijono <benny@prijono.org>
Benny Prijonoa4bf0212006-02-10 15:57:08 +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#include <pjmedia-codec/gsm.h>
20#include <pjmedia/codec.h>
21#include <pjmedia/errno.h>
22#include <pjmedia/endpoint.h>
Benny Prijono70c68912006-05-19 15:58:13 +000023#include <pjmedia/plc.h>
Benny Prijonof04ffdd2006-02-21 00:11:18 +000024#include <pjmedia/port.h>
Benny Prijono70c68912006-05-19 15:58:13 +000025#include <pjmedia/silencedet.h>
Benny Prijonoa4bf0212006-02-10 15:57:08 +000026#include <pj/assert.h>
27#include <pj/pool.h>
28#include <pj/string.h>
29#include <pj/os.h>
Benny Prijono7f1c90f2007-04-07 12:29:46 +000030#include "../../third_party/gsm/inc/gsm.h"
Benny Prijonoa4bf0212006-02-10 15:57:08 +000031
Benny Prijono4381efe2006-03-16 14:24:26 +000032/*
33 * Only build this file if PJMEDIA_HAS_GSM_CODEC != 0
34 */
35#if defined(PJMEDIA_HAS_GSM_CODEC) && PJMEDIA_HAS_GSM_CODEC != 0
36
37
Benny Prijonoa4bf0212006-02-10 15:57:08 +000038/* Prototypes for GSM factory */
39static pj_status_t gsm_test_alloc( pjmedia_codec_factory *factory,
40 const pjmedia_codec_info *id );
41static pj_status_t gsm_default_attr( pjmedia_codec_factory *factory,
42 const pjmedia_codec_info *id,
43 pjmedia_codec_param *attr );
44static pj_status_t gsm_enum_codecs( pjmedia_codec_factory *factory,
45 unsigned *count,
46 pjmedia_codec_info codecs[]);
47static pj_status_t gsm_alloc_codec( pjmedia_codec_factory *factory,
48 const pjmedia_codec_info *id,
49 pjmedia_codec **p_codec);
50static pj_status_t gsm_dealloc_codec( pjmedia_codec_factory *factory,
51 pjmedia_codec *codec );
52
53/* Prototypes for GSM implementation. */
Benny Prijonoa4bf0212006-02-10 15:57:08 +000054static pj_status_t gsm_codec_init( pjmedia_codec *codec,
55 pj_pool_t *pool );
56static pj_status_t gsm_codec_open( pjmedia_codec *codec,
Benny Prijono6865a3f2006-12-30 02:46:57 +000057 pjmedia_codec_param *attr );
Benny Prijonoa4bf0212006-02-10 15:57:08 +000058static pj_status_t gsm_codec_close( pjmedia_codec *codec );
Benny Prijonob94a6ab2006-12-26 21:18:11 +000059static pj_status_t gsm_codec_modify(pjmedia_codec *codec,
60 const pjmedia_codec_param *attr );
Benny Prijono8befd9f2006-05-13 22:46:23 +000061static pj_status_t gsm_codec_parse( pjmedia_codec *codec,
62 void *pkt,
63 pj_size_t pkt_size,
64 const pj_timestamp *ts,
65 unsigned *frame_cnt,
66 pjmedia_frame frames[]);
Benny Prijonoa4bf0212006-02-10 15:57:08 +000067static pj_status_t gsm_codec_encode( pjmedia_codec *codec,
68 const struct pjmedia_frame *input,
69 unsigned output_buf_len,
70 struct pjmedia_frame *output);
71static pj_status_t gsm_codec_decode( pjmedia_codec *codec,
72 const struct pjmedia_frame *input,
73 unsigned output_buf_len,
74 struct pjmedia_frame *output);
Benny Prijono70c68912006-05-19 15:58:13 +000075static pj_status_t gsm_codec_recover(pjmedia_codec *codec,
76 unsigned output_buf_len,
77 struct pjmedia_frame *output);
Benny Prijonoa4bf0212006-02-10 15:57:08 +000078
79/* Definition for GSM codec operations. */
80static pjmedia_codec_op gsm_op =
81{
Benny Prijonoa4bf0212006-02-10 15:57:08 +000082 &gsm_codec_init,
83 &gsm_codec_open,
84 &gsm_codec_close,
Benny Prijonob94a6ab2006-12-26 21:18:11 +000085 &gsm_codec_modify,
Benny Prijono8befd9f2006-05-13 22:46:23 +000086 &gsm_codec_parse,
Benny Prijonoa4bf0212006-02-10 15:57:08 +000087 &gsm_codec_encode,
Benny Prijono70c68912006-05-19 15:58:13 +000088 &gsm_codec_decode,
89 &gsm_codec_recover
Benny Prijonoa4bf0212006-02-10 15:57:08 +000090};
91
92/* Definition for GSM codec factory operations. */
93static pjmedia_codec_factory_op gsm_factory_op =
94{
95 &gsm_test_alloc,
96 &gsm_default_attr,
97 &gsm_enum_codecs,
98 &gsm_alloc_codec,
99 &gsm_dealloc_codec
100};
101
102/* GSM factory */
103static struct gsm_codec_factory
104{
105 pjmedia_codec_factory base;
106 pjmedia_endpt *endpt;
107 pj_pool_t *pool;
108 pj_mutex_t *mutex;
109 pjmedia_codec codec_list;
110} gsm_codec_factory;
111
Benny Prijono70c68912006-05-19 15:58:13 +0000112
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000113/* GSM codec private data. */
Benny Prijonof93895c2006-03-05 11:50:53 +0000114struct gsm_data
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000115{
Benny Prijono70c68912006-05-19 15:58:13 +0000116 void *encoder;
117 void *decoder;
118 pj_bool_t plc_enabled;
119 pjmedia_plc *plc;
120 pj_bool_t vad_enabled;
121 pjmedia_silence_det *vad;
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000122 pj_timestamp last_tx;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000123};
124
125
126
127/*
128 * Initialize and register GSM codec factory to pjmedia endpoint.
129 */
130PJ_DEF(pj_status_t) pjmedia_codec_gsm_init( pjmedia_endpt *endpt )
131{
132 pjmedia_codec_mgr *codec_mgr;
133 pj_status_t status;
134
135 if (gsm_codec_factory.pool != NULL)
136 return PJ_SUCCESS;
137
138 /* Create GSM codec factory. */
139 gsm_codec_factory.base.op = &gsm_factory_op;
140 gsm_codec_factory.base.factory_data = NULL;
141 gsm_codec_factory.endpt = endpt;
142
143 gsm_codec_factory.pool = pjmedia_endpt_create_pool(endpt, "gsm", 4000,
144 4000);
145 if (!gsm_codec_factory.pool)
146 return PJ_ENOMEM;
147
148 pj_list_init(&gsm_codec_factory.codec_list);
149
150 /* Create mutex. */
151 status = pj_mutex_create_simple(gsm_codec_factory.pool, "gsm",
152 &gsm_codec_factory.mutex);
153 if (status != PJ_SUCCESS)
154 goto on_error;
155
156 /* Get the codec manager. */
157 codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
158 if (!codec_mgr) {
159 status = PJ_EINVALIDOP;
160 goto on_error;
161 }
162
163 /* Register codec factory to endpoint. */
164 status = pjmedia_codec_mgr_register_factory(codec_mgr,
165 &gsm_codec_factory.base);
166 if (status != PJ_SUCCESS)
167 goto on_error;
168
169 /* Done. */
170 return PJ_SUCCESS;
171
172on_error:
173 pj_pool_release(gsm_codec_factory.pool);
174 gsm_codec_factory.pool = NULL;
175 return status;
176}
177
178
179
180/*
181 * Unregister GSM codec factory from pjmedia endpoint and deinitialize
182 * the GSM codec library.
183 */
184PJ_DEF(pj_status_t) pjmedia_codec_gsm_deinit(void)
185{
186 pjmedia_codec_mgr *codec_mgr;
187 pj_status_t status;
188
189 if (gsm_codec_factory.pool == NULL)
190 return PJ_SUCCESS;
191
192 /* We don't want to deinit if there's outstanding codec. */
Benny Prijonod7036a12007-03-15 10:12:57 +0000193 /* This is silly, as we'll always have codec in the list if
194 we ever allocate a codec! A better behavior maybe is to
195 deallocate all codecs in the list.
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000196 pj_mutex_lock(gsm_codec_factory.mutex);
197 if (!pj_list_empty(&gsm_codec_factory.codec_list)) {
198 pj_mutex_unlock(gsm_codec_factory.mutex);
199 return PJ_EBUSY;
200 }
Benny Prijonod7036a12007-03-15 10:12:57 +0000201 */
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000202
203 /* Get the codec manager. */
204 codec_mgr = pjmedia_endpt_get_codec_mgr(gsm_codec_factory.endpt);
205 if (!codec_mgr) {
206 pj_pool_release(gsm_codec_factory.pool);
207 gsm_codec_factory.pool = NULL;
208 return PJ_EINVALIDOP;
209 }
210
211 /* Unregister GSM codec factory. */
212 status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
213 &gsm_codec_factory.base);
214
215 /* Destroy mutex. */
216 pj_mutex_destroy(gsm_codec_factory.mutex);
217
218 /* Destroy pool. */
219 pj_pool_release(gsm_codec_factory.pool);
220 gsm_codec_factory.pool = NULL;
221
222 return status;
223}
224
225/*
226 * Check if factory can allocate the specified codec.
227 */
228static pj_status_t gsm_test_alloc( pjmedia_codec_factory *factory,
229 const pjmedia_codec_info *info )
230{
231 PJ_UNUSED_ARG(factory);
232
233 /* Check payload type. */
234 if (info->pt != PJMEDIA_RTP_PT_GSM)
235 return PJMEDIA_CODEC_EUNSUP;
236
237 /* Ignore the rest, since it's static payload type. */
238
239 return PJ_SUCCESS;
240}
241
242/*
243 * Generate default attribute.
244 */
245static pj_status_t gsm_default_attr (pjmedia_codec_factory *factory,
246 const pjmedia_codec_info *id,
247 pjmedia_codec_param *attr )
248{
249 PJ_UNUSED_ARG(factory);
250 PJ_UNUSED_ARG(id);
251
Benny Prijonoac623b32006-07-03 15:19:31 +0000252 pj_bzero(attr, sizeof(pjmedia_codec_param));
Benny Prijono8befd9f2006-05-13 22:46:23 +0000253 attr->info.clock_rate = 8000;
254 attr->info.channel_cnt = 1;
255 attr->info.avg_bps = 13200;
256 attr->info.pcm_bits_per_sample = 16;
257 attr->info.frm_ptime = 20;
258 attr->info.pt = PJMEDIA_RTP_PT_GSM;
259
260 attr->setting.frm_per_pkt = 1;
Benny Prijono70c68912006-05-19 15:58:13 +0000261 attr->setting.vad = 1;
262 attr->setting.plc = 1;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000263
Benny Prijono70c68912006-05-19 15:58:13 +0000264 /* Default all other flag bits disabled. */
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000265
266 return PJ_SUCCESS;
267}
268
269/*
270 * Enum codecs supported by this factory (i.e. only GSM!).
271 */
272static pj_status_t gsm_enum_codecs(pjmedia_codec_factory *factory,
273 unsigned *count,
274 pjmedia_codec_info codecs[])
275{
276 PJ_UNUSED_ARG(factory);
277 PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
278
Benny Prijonoac623b32006-07-03 15:19:31 +0000279 pj_bzero(&codecs[0], sizeof(pjmedia_codec_info));
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000280 codecs[0].encoding_name = pj_str("GSM");
281 codecs[0].pt = PJMEDIA_RTP_PT_GSM;
282 codecs[0].type = PJMEDIA_TYPE_AUDIO;
Benny Prijonoa837c302006-04-27 22:36:40 +0000283 codecs[0].clock_rate = 8000;
284 codecs[0].channel_cnt = 1;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000285
286 *count = 1;
287
288 return PJ_SUCCESS;
289}
290
291/*
292 * Allocate a new GSM codec instance.
293 */
294static pj_status_t gsm_alloc_codec( pjmedia_codec_factory *factory,
295 const pjmedia_codec_info *id,
296 pjmedia_codec **p_codec)
297{
298 pjmedia_codec *codec;
Benny Prijonof93895c2006-03-05 11:50:53 +0000299 struct gsm_data *gsm_data;
Benny Prijono70c68912006-05-19 15:58:13 +0000300 pj_status_t status;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000301
302 PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
303 PJ_ASSERT_RETURN(factory == &gsm_codec_factory.base, PJ_EINVAL);
304
305
306 pj_mutex_lock(gsm_codec_factory.mutex);
307
308 /* Get free nodes, if any. */
309 if (!pj_list_empty(&gsm_codec_factory.codec_list)) {
310 codec = gsm_codec_factory.codec_list.next;
311 pj_list_erase(codec);
312 } else {
313 codec = pj_pool_zalloc(gsm_codec_factory.pool,
314 sizeof(pjmedia_codec));
315 PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM);
Benny Prijonof80b1bf2006-02-19 02:24:27 +0000316 codec->op = &gsm_op;
317 codec->factory = factory;
Benny Prijonof93895c2006-03-05 11:50:53 +0000318
319 gsm_data = pj_pool_zalloc(gsm_codec_factory.pool,
320 sizeof(struct gsm_data));
321 codec->codec_data = gsm_data;
Benny Prijono70c68912006-05-19 15:58:13 +0000322
323 /* Create PLC */
324 status = pjmedia_plc_create(gsm_codec_factory.pool, 8000,
325 160, 0, &gsm_data->plc);
326 if (status != PJ_SUCCESS) {
327 pj_mutex_unlock(gsm_codec_factory.mutex);
328 return status;
329 }
330
331 /* Create silence detector */
332 status = pjmedia_silence_det_create(gsm_codec_factory.pool,
333 8000, 160,
334 &gsm_data->vad);
335 if (status != PJ_SUCCESS) {
336 pj_mutex_unlock(gsm_codec_factory.mutex);
337 return status;
338 }
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000339 }
340
341 pj_mutex_unlock(gsm_codec_factory.mutex);
342
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000343 *p_codec = codec;
344 return PJ_SUCCESS;
345}
346
347/*
348 * Free codec.
349 */
350static pj_status_t gsm_dealloc_codec( pjmedia_codec_factory *factory,
351 pjmedia_codec *codec )
352{
Benny Prijonof93895c2006-03-05 11:50:53 +0000353 struct gsm_data *gsm_data;
Benny Prijono87445bc2006-07-26 11:23:07 +0000354 pj_int16_t frame[160];
355 int i;
Benny Prijonof93895c2006-03-05 11:50:53 +0000356
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000357 PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
358 PJ_ASSERT_RETURN(factory == &gsm_codec_factory.base, PJ_EINVAL);
359
Benny Prijonof93895c2006-03-05 11:50:53 +0000360 gsm_data = codec->codec_data;
361
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000362 /* Close codec, if it's not closed. */
Benny Prijonof93895c2006-03-05 11:50:53 +0000363 gsm_codec_close(codec);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000364
Benny Prijono87445bc2006-07-26 11:23:07 +0000365 /* Clear left samples in the PLC, since codec+plc will be reused
366 * next time.
367 */
368 for (i=0; i<2; ++i) {
369 pjmedia_zero_samples(frame, PJ_ARRAY_SIZE(frame));
370 pjmedia_plc_save(gsm_data->plc, frame);
371 }
372
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000373 /* Re-init silence_period */
374 pj_set_timestamp32(&gsm_data->last_tx, 0, 0);
375
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000376 /* Put in the free list. */
377 pj_mutex_lock(gsm_codec_factory.mutex);
378 pj_list_push_front(&gsm_codec_factory.codec_list, codec);
379 pj_mutex_unlock(gsm_codec_factory.mutex);
380
381 return PJ_SUCCESS;
382}
383
384/*
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000385 * Init codec.
386 */
387static pj_status_t gsm_codec_init( pjmedia_codec *codec,
388 pj_pool_t *pool )
389{
390 PJ_UNUSED_ARG(codec);
391 PJ_UNUSED_ARG(pool);
392 return PJ_SUCCESS;
393}
394
395/*
396 * Open codec.
397 */
398static pj_status_t gsm_codec_open( pjmedia_codec *codec,
Benny Prijono6865a3f2006-12-30 02:46:57 +0000399 pjmedia_codec_param *attr )
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000400{
Benny Prijonof93895c2006-03-05 11:50:53 +0000401 struct gsm_data *gsm_data = codec->codec_data;
402
403 pj_assert(gsm_data != NULL);
404 pj_assert(gsm_data->encoder == NULL && gsm_data->decoder == NULL);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000405
Benny Prijonof93895c2006-03-05 11:50:53 +0000406 gsm_data->encoder = gsm_create();
407 if (!gsm_data->encoder)
408 return PJMEDIA_CODEC_EFAILED;
409
410 gsm_data->decoder = gsm_create();
411 if (!gsm_data->decoder)
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000412 return PJMEDIA_CODEC_EFAILED;
413
Benny Prijono70c68912006-05-19 15:58:13 +0000414 gsm_data->vad_enabled = (attr->setting.vad != 0);
415 gsm_data->plc_enabled = (attr->setting.plc != 0);
416
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000417 return PJ_SUCCESS;
418}
419
420/*
421 * Close codec.
422 */
423static pj_status_t gsm_codec_close( pjmedia_codec *codec )
424{
Benny Prijonof93895c2006-03-05 11:50:53 +0000425 struct gsm_data *gsm_data = codec->codec_data;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000426
Benny Prijonof93895c2006-03-05 11:50:53 +0000427 pj_assert(gsm_data != NULL);
428
429 if (gsm_data->encoder) {
430 gsm_destroy(gsm_data->encoder);
431 gsm_data->encoder = NULL;
432 }
433 if (gsm_data->decoder) {
434 gsm_destroy(gsm_data->decoder);
435 gsm_data->decoder = NULL;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000436 }
437
438 return PJ_SUCCESS;
439}
440
441
442/*
Benny Prijonob94a6ab2006-12-26 21:18:11 +0000443 * Modify codec settings.
444 */
445static pj_status_t gsm_codec_modify(pjmedia_codec *codec,
446 const pjmedia_codec_param *attr )
447{
448 struct gsm_data *gsm_data = codec->codec_data;
449
450 pj_assert(gsm_data != NULL);
Benny Prijono6865a3f2006-12-30 02:46:57 +0000451 pj_assert(gsm_data->encoder != NULL && gsm_data->decoder != NULL);
Benny Prijonob94a6ab2006-12-26 21:18:11 +0000452
453 gsm_data->vad_enabled = (attr->setting.vad != 0);
454 gsm_data->plc_enabled = (attr->setting.plc != 0);
455
456 return PJ_SUCCESS;
457}
458
459
460/*
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000461 * Get frames in the packet.
462 */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000463static pj_status_t gsm_codec_parse( pjmedia_codec *codec,
464 void *pkt,
465 pj_size_t pkt_size,
466 const pj_timestamp *ts,
467 unsigned *frame_cnt,
468 pjmedia_frame frames[])
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000469{
470 unsigned count = 0;
471
472 PJ_UNUSED_ARG(codec);
473
474 PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL);
475
476 while (pkt_size >= 33 && count < *frame_cnt) {
Benny Prijono8befd9f2006-05-13 22:46:23 +0000477 frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
478 frames[count].buf = pkt;
479 frames[count].size = 33;
480 frames[count].timestamp.u64 = ts->u64 + count * 160;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000481
482 pkt = ((char*)pkt) + 33;
483 pkt_size -= 33;
484
485 ++count;
486 }
487
488 *frame_cnt = count;
489 return PJ_SUCCESS;
490}
491
492/*
493 * Encode frame.
494 */
495static pj_status_t gsm_codec_encode( pjmedia_codec *codec,
496 const struct pjmedia_frame *input,
497 unsigned output_buf_len,
498 struct pjmedia_frame *output)
499{
Benny Prijonof93895c2006-03-05 11:50:53 +0000500 struct gsm_data *gsm_data = codec->codec_data;
501
502 pj_assert(gsm_data != NULL);
503 PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000504
505 if (output_buf_len < 33)
506 return PJMEDIA_CODEC_EFRMTOOSHORT;
507
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000508 PJ_ASSERT_RETURN(input->size==320, PJMEDIA_CODEC_EPCMFRMINLEN);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000509
Benny Prijono70c68912006-05-19 15:58:13 +0000510 /* Detect silence */
511 if (gsm_data->vad_enabled) {
512 pj_bool_t is_silence;
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000513 pj_int32_t silence_duration;
514
515 silence_duration = pj_timestamp_diff32(&gsm_data->last_tx,
516 &input->timestamp);
Benny Prijono70c68912006-05-19 15:58:13 +0000517
518 is_silence = pjmedia_silence_det_detect(gsm_data->vad,
519 input->buf,
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000520 (input->size >> 1),
Benny Prijono70c68912006-05-19 15:58:13 +0000521 NULL);
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000522 if (is_silence &&
523 PJMEDIA_CODEC_MAX_SILENCE_PERIOD != -1 &&
524 silence_duration < PJMEDIA_CODEC_MAX_SILENCE_PERIOD)
525 {
Benny Prijono70c68912006-05-19 15:58:13 +0000526 output->type = PJMEDIA_FRAME_TYPE_NONE;
527 output->buf = NULL;
528 output->size = 0;
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000529 output->timestamp = input->timestamp;
Benny Prijono70c68912006-05-19 15:58:13 +0000530 return PJ_SUCCESS;
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000531 } else {
532 gsm_data->last_tx = input->timestamp;
Benny Prijono70c68912006-05-19 15:58:13 +0000533 }
534 }
535
536 /* Encode */
Benny Prijonof93895c2006-03-05 11:50:53 +0000537 gsm_encode(gsm_data->encoder, (short*)input->buf,
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000538 (unsigned char*)output->buf);
539
540 output->size = 33;
541 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
542
543 return PJ_SUCCESS;
544}
545
546/*
547 * Decode frame.
548 */
549static pj_status_t gsm_codec_decode( pjmedia_codec *codec,
550 const struct pjmedia_frame *input,
551 unsigned output_buf_len,
552 struct pjmedia_frame *output)
553{
Benny Prijonof93895c2006-03-05 11:50:53 +0000554 struct gsm_data *gsm_data = codec->codec_data;
555
556 pj_assert(gsm_data != NULL);
557 PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000558
559 if (output_buf_len < 320)
560 return PJMEDIA_CODEC_EPCMTOOSHORT;
561
562 if (input->size < 33)
563 return PJMEDIA_CODEC_EFRMTOOSHORT;
564
Benny Prijonof93895c2006-03-05 11:50:53 +0000565 gsm_decode(gsm_data->decoder,
Benny Prijonof80b1bf2006-02-19 02:24:27 +0000566 (unsigned char*)input->buf,
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000567 (short*)output->buf);
568
569 output->size = 320;
570 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
571
Benny Prijono70c68912006-05-19 15:58:13 +0000572 if (gsm_data->plc_enabled)
573 pjmedia_plc_save( gsm_data->plc, output->buf);
574
575 return PJ_SUCCESS;
576}
577
578
579/*
580 * Recover lost frame.
581 */
582static pj_status_t gsm_codec_recover(pjmedia_codec *codec,
583 unsigned output_buf_len,
584 struct pjmedia_frame *output)
585{
586 struct gsm_data *gsm_data = codec->codec_data;
587
588 PJ_ASSERT_RETURN(gsm_data->plc_enabled, PJ_EINVALIDOP);
589
590 PJ_ASSERT_RETURN(output_buf_len >= 320, PJMEDIA_CODEC_EPCMTOOSHORT);
591
592 pjmedia_plc_generate(gsm_data->plc, output->buf);
593 output->size = 320;
594
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000595 return PJ_SUCCESS;
596}
Benny Prijono4381efe2006-03-16 14:24:26 +0000597
598
599#endif /* PJMEDIA_HAS_GSM_CODEC */
600