blob: 8689960f2c7a659dec35ff2c1a87763ba3537e43 [file] [log] [blame]
Benny Prijonoa4bf0212006-02-10 15:57:08 +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#include <pjmedia-codec/gsm.h>
20#include <pjmedia/codec.h>
21#include <pjmedia/errno.h>
22#include <pjmedia/endpoint.h>
Benny Prijonof04ffdd2006-02-21 00:11:18 +000023#include <pjmedia/port.h>
Benny Prijonoa4bf0212006-02-10 15:57:08 +000024#include <pj/assert.h>
25#include <pj/pool.h>
26#include <pj/string.h>
27#include <pj/os.h>
28#include "gsm/gsm.h"
29
Benny Prijono4381efe2006-03-16 14:24:26 +000030/*
31 * Only build this file if PJMEDIA_HAS_GSM_CODEC != 0
32 */
33#if defined(PJMEDIA_HAS_GSM_CODEC) && PJMEDIA_HAS_GSM_CODEC != 0
34
35
Benny Prijonoa4bf0212006-02-10 15:57:08 +000036/* Prototypes for GSM factory */
37static pj_status_t gsm_test_alloc( pjmedia_codec_factory *factory,
38 const pjmedia_codec_info *id );
39static pj_status_t gsm_default_attr( pjmedia_codec_factory *factory,
40 const pjmedia_codec_info *id,
41 pjmedia_codec_param *attr );
42static pj_status_t gsm_enum_codecs( pjmedia_codec_factory *factory,
43 unsigned *count,
44 pjmedia_codec_info codecs[]);
45static pj_status_t gsm_alloc_codec( pjmedia_codec_factory *factory,
46 const pjmedia_codec_info *id,
47 pjmedia_codec **p_codec);
48static pj_status_t gsm_dealloc_codec( pjmedia_codec_factory *factory,
49 pjmedia_codec *codec );
50
51/* Prototypes for GSM implementation. */
Benny Prijonoa4bf0212006-02-10 15:57:08 +000052static pj_status_t gsm_codec_init( pjmedia_codec *codec,
53 pj_pool_t *pool );
54static pj_status_t gsm_codec_open( pjmedia_codec *codec,
55 pjmedia_codec_param *attr );
56static pj_status_t gsm_codec_close( pjmedia_codec *codec );
Benny Prijono8befd9f2006-05-13 22:46:23 +000057static pj_status_t gsm_codec_parse( pjmedia_codec *codec,
58 void *pkt,
59 pj_size_t pkt_size,
60 const pj_timestamp *ts,
61 unsigned *frame_cnt,
62 pjmedia_frame frames[]);
Benny Prijonoa4bf0212006-02-10 15:57:08 +000063static pj_status_t gsm_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 gsm_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 GSM codec operations. */
73static pjmedia_codec_op gsm_op =
74{
Benny Prijonoa4bf0212006-02-10 15:57:08 +000075 &gsm_codec_init,
76 &gsm_codec_open,
77 &gsm_codec_close,
Benny Prijono8befd9f2006-05-13 22:46:23 +000078 &gsm_codec_parse,
Benny Prijonoa4bf0212006-02-10 15:57:08 +000079 &gsm_codec_encode,
80 &gsm_codec_decode
81};
82
83/* Definition for GSM codec factory operations. */
84static pjmedia_codec_factory_op gsm_factory_op =
85{
86 &gsm_test_alloc,
87 &gsm_default_attr,
88 &gsm_enum_codecs,
89 &gsm_alloc_codec,
90 &gsm_dealloc_codec
91};
92
93/* GSM factory */
94static struct gsm_codec_factory
95{
96 pjmedia_codec_factory base;
97 pjmedia_endpt *endpt;
98 pj_pool_t *pool;
99 pj_mutex_t *mutex;
100 pjmedia_codec codec_list;
101} gsm_codec_factory;
102
103/* GSM codec private data. */
Benny Prijonof93895c2006-03-05 11:50:53 +0000104struct gsm_data
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000105{
Benny Prijonof93895c2006-03-05 11:50:53 +0000106 void *encoder;
107 void *decoder;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000108};
109
110
111
112/*
113 * Initialize and register GSM codec factory to pjmedia endpoint.
114 */
115PJ_DEF(pj_status_t) pjmedia_codec_gsm_init( pjmedia_endpt *endpt )
116{
117 pjmedia_codec_mgr *codec_mgr;
118 pj_status_t status;
119
120 if (gsm_codec_factory.pool != NULL)
121 return PJ_SUCCESS;
122
123 /* Create GSM codec factory. */
124 gsm_codec_factory.base.op = &gsm_factory_op;
125 gsm_codec_factory.base.factory_data = NULL;
126 gsm_codec_factory.endpt = endpt;
127
128 gsm_codec_factory.pool = pjmedia_endpt_create_pool(endpt, "gsm", 4000,
129 4000);
130 if (!gsm_codec_factory.pool)
131 return PJ_ENOMEM;
132
133 pj_list_init(&gsm_codec_factory.codec_list);
134
135 /* Create mutex. */
136 status = pj_mutex_create_simple(gsm_codec_factory.pool, "gsm",
137 &gsm_codec_factory.mutex);
138 if (status != PJ_SUCCESS)
139 goto on_error;
140
141 /* Get the codec manager. */
142 codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
143 if (!codec_mgr) {
144 status = PJ_EINVALIDOP;
145 goto on_error;
146 }
147
148 /* Register codec factory to endpoint. */
149 status = pjmedia_codec_mgr_register_factory(codec_mgr,
150 &gsm_codec_factory.base);
151 if (status != PJ_SUCCESS)
152 goto on_error;
153
154 /* Done. */
155 return PJ_SUCCESS;
156
157on_error:
158 pj_pool_release(gsm_codec_factory.pool);
159 gsm_codec_factory.pool = NULL;
160 return status;
161}
162
163
164
165/*
166 * Unregister GSM codec factory from pjmedia endpoint and deinitialize
167 * the GSM codec library.
168 */
169PJ_DEF(pj_status_t) pjmedia_codec_gsm_deinit(void)
170{
171 pjmedia_codec_mgr *codec_mgr;
172 pj_status_t status;
173
174 if (gsm_codec_factory.pool == NULL)
175 return PJ_SUCCESS;
176
177 /* We don't want to deinit if there's outstanding codec. */
178 pj_mutex_lock(gsm_codec_factory.mutex);
179 if (!pj_list_empty(&gsm_codec_factory.codec_list)) {
180 pj_mutex_unlock(gsm_codec_factory.mutex);
181 return PJ_EBUSY;
182 }
183
184 /* Get the codec manager. */
185 codec_mgr = pjmedia_endpt_get_codec_mgr(gsm_codec_factory.endpt);
186 if (!codec_mgr) {
187 pj_pool_release(gsm_codec_factory.pool);
188 gsm_codec_factory.pool = NULL;
189 return PJ_EINVALIDOP;
190 }
191
192 /* Unregister GSM codec factory. */
193 status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
194 &gsm_codec_factory.base);
195
196 /* Destroy mutex. */
197 pj_mutex_destroy(gsm_codec_factory.mutex);
198
199 /* Destroy pool. */
200 pj_pool_release(gsm_codec_factory.pool);
201 gsm_codec_factory.pool = NULL;
202
203 return status;
204}
205
206/*
207 * Check if factory can allocate the specified codec.
208 */
209static pj_status_t gsm_test_alloc( pjmedia_codec_factory *factory,
210 const pjmedia_codec_info *info )
211{
212 PJ_UNUSED_ARG(factory);
213
214 /* Check payload type. */
215 if (info->pt != PJMEDIA_RTP_PT_GSM)
216 return PJMEDIA_CODEC_EUNSUP;
217
218 /* Ignore the rest, since it's static payload type. */
219
220 return PJ_SUCCESS;
221}
222
223/*
224 * Generate default attribute.
225 */
226static pj_status_t gsm_default_attr (pjmedia_codec_factory *factory,
227 const pjmedia_codec_info *id,
228 pjmedia_codec_param *attr )
229{
230 PJ_UNUSED_ARG(factory);
231 PJ_UNUSED_ARG(id);
232
233 pj_memset(attr, 0, sizeof(pjmedia_codec_param));
Benny Prijono8befd9f2006-05-13 22:46:23 +0000234 attr->info.clock_rate = 8000;
235 attr->info.channel_cnt = 1;
236 attr->info.avg_bps = 13200;
237 attr->info.pcm_bits_per_sample = 16;
238 attr->info.frm_ptime = 20;
239 attr->info.pt = PJMEDIA_RTP_PT_GSM;
240
241 attr->setting.frm_per_pkt = 1;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000242
243 /* Default all flag bits disabled. */
244
245 return PJ_SUCCESS;
246}
247
248/*
249 * Enum codecs supported by this factory (i.e. only GSM!).
250 */
251static pj_status_t gsm_enum_codecs(pjmedia_codec_factory *factory,
252 unsigned *count,
253 pjmedia_codec_info codecs[])
254{
255 PJ_UNUSED_ARG(factory);
256 PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
257
258 pj_memset(&codecs[0], 0, sizeof(pjmedia_codec_info));
259 codecs[0].encoding_name = pj_str("GSM");
260 codecs[0].pt = PJMEDIA_RTP_PT_GSM;
261 codecs[0].type = PJMEDIA_TYPE_AUDIO;
Benny Prijonoa837c302006-04-27 22:36:40 +0000262 codecs[0].clock_rate = 8000;
263 codecs[0].channel_cnt = 1;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000264
265 *count = 1;
266
267 return PJ_SUCCESS;
268}
269
270/*
271 * Allocate a new GSM codec instance.
272 */
273static pj_status_t gsm_alloc_codec( pjmedia_codec_factory *factory,
274 const pjmedia_codec_info *id,
275 pjmedia_codec **p_codec)
276{
277 pjmedia_codec *codec;
Benny Prijonof93895c2006-03-05 11:50:53 +0000278 struct gsm_data *gsm_data;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000279
280 PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
281 PJ_ASSERT_RETURN(factory == &gsm_codec_factory.base, PJ_EINVAL);
282
283
284 pj_mutex_lock(gsm_codec_factory.mutex);
285
286 /* Get free nodes, if any. */
287 if (!pj_list_empty(&gsm_codec_factory.codec_list)) {
288 codec = gsm_codec_factory.codec_list.next;
289 pj_list_erase(codec);
290 } else {
291 codec = pj_pool_zalloc(gsm_codec_factory.pool,
292 sizeof(pjmedia_codec));
293 PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM);
Benny Prijonof80b1bf2006-02-19 02:24:27 +0000294 codec->op = &gsm_op;
295 codec->factory = factory;
Benny Prijonof93895c2006-03-05 11:50:53 +0000296
297 gsm_data = pj_pool_zalloc(gsm_codec_factory.pool,
298 sizeof(struct gsm_data));
299 codec->codec_data = gsm_data;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000300 }
301
302 pj_mutex_unlock(gsm_codec_factory.mutex);
303
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000304 *p_codec = codec;
305 return PJ_SUCCESS;
306}
307
308/*
309 * Free codec.
310 */
311static pj_status_t gsm_dealloc_codec( pjmedia_codec_factory *factory,
312 pjmedia_codec *codec )
313{
Benny Prijonof93895c2006-03-05 11:50:53 +0000314 struct gsm_data *gsm_data;
315
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000316 PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
317 PJ_ASSERT_RETURN(factory == &gsm_codec_factory.base, PJ_EINVAL);
318
Benny Prijonof93895c2006-03-05 11:50:53 +0000319 gsm_data = codec->codec_data;
320
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000321 /* Close codec, if it's not closed. */
Benny Prijonof93895c2006-03-05 11:50:53 +0000322 gsm_codec_close(codec);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000323
324 /* Put in the free list. */
325 pj_mutex_lock(gsm_codec_factory.mutex);
326 pj_list_push_front(&gsm_codec_factory.codec_list, codec);
327 pj_mutex_unlock(gsm_codec_factory.mutex);
328
329 return PJ_SUCCESS;
330}
331
332/*
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000333 * Init codec.
334 */
335static pj_status_t gsm_codec_init( pjmedia_codec *codec,
336 pj_pool_t *pool )
337{
338 PJ_UNUSED_ARG(codec);
339 PJ_UNUSED_ARG(pool);
340 return PJ_SUCCESS;
341}
342
343/*
344 * Open codec.
345 */
346static pj_status_t gsm_codec_open( pjmedia_codec *codec,
347 pjmedia_codec_param *attr )
348{
Benny Prijonof93895c2006-03-05 11:50:53 +0000349 struct gsm_data *gsm_data = codec->codec_data;
350
351 pj_assert(gsm_data != NULL);
352 pj_assert(gsm_data->encoder == NULL && gsm_data->decoder == NULL);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000353
354 PJ_UNUSED_ARG(attr);
355
Benny Prijonof93895c2006-03-05 11:50:53 +0000356 gsm_data->encoder = gsm_create();
357 if (!gsm_data->encoder)
358 return PJMEDIA_CODEC_EFAILED;
359
360 gsm_data->decoder = gsm_create();
361 if (!gsm_data->decoder)
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000362 return PJMEDIA_CODEC_EFAILED;
363
364 return PJ_SUCCESS;
365}
366
367/*
368 * Close codec.
369 */
370static pj_status_t gsm_codec_close( pjmedia_codec *codec )
371{
Benny Prijonof93895c2006-03-05 11:50:53 +0000372 struct gsm_data *gsm_data = codec->codec_data;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000373
Benny Prijonof93895c2006-03-05 11:50:53 +0000374 pj_assert(gsm_data != NULL);
375
376 if (gsm_data->encoder) {
377 gsm_destroy(gsm_data->encoder);
378 gsm_data->encoder = NULL;
379 }
380 if (gsm_data->decoder) {
381 gsm_destroy(gsm_data->decoder);
382 gsm_data->decoder = NULL;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000383 }
384
385 return PJ_SUCCESS;
386}
387
388
389/*
390 * Get frames in the packet.
391 */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000392static pj_status_t gsm_codec_parse( pjmedia_codec *codec,
393 void *pkt,
394 pj_size_t pkt_size,
395 const pj_timestamp *ts,
396 unsigned *frame_cnt,
397 pjmedia_frame frames[])
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000398{
399 unsigned count = 0;
400
401 PJ_UNUSED_ARG(codec);
402
403 PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL);
404
405 while (pkt_size >= 33 && count < *frame_cnt) {
Benny Prijono8befd9f2006-05-13 22:46:23 +0000406 frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
407 frames[count].buf = pkt;
408 frames[count].size = 33;
409 frames[count].timestamp.u64 = ts->u64 + count * 160;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000410
411 pkt = ((char*)pkt) + 33;
412 pkt_size -= 33;
413
414 ++count;
415 }
416
417 *frame_cnt = count;
418 return PJ_SUCCESS;
419}
420
421/*
422 * Encode frame.
423 */
424static pj_status_t gsm_codec_encode( pjmedia_codec *codec,
425 const struct pjmedia_frame *input,
426 unsigned output_buf_len,
427 struct pjmedia_frame *output)
428{
Benny Prijonof93895c2006-03-05 11:50:53 +0000429 struct gsm_data *gsm_data = codec->codec_data;
430
431 pj_assert(gsm_data != NULL);
432 PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000433
434 if (output_buf_len < 33)
435 return PJMEDIA_CODEC_EFRMTOOSHORT;
436
437 if (input->size < 320)
438 return PJMEDIA_CODEC_EPCMTOOSHORT;
439
Benny Prijonof93895c2006-03-05 11:50:53 +0000440 gsm_encode(gsm_data->encoder, (short*)input->buf,
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000441 (unsigned char*)output->buf);
442
443 output->size = 33;
444 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
445
446 return PJ_SUCCESS;
447}
448
449/*
450 * Decode frame.
451 */
452static pj_status_t gsm_codec_decode( pjmedia_codec *codec,
453 const struct pjmedia_frame *input,
454 unsigned output_buf_len,
455 struct pjmedia_frame *output)
456{
Benny Prijonof93895c2006-03-05 11:50:53 +0000457 struct gsm_data *gsm_data = codec->codec_data;
458
459 pj_assert(gsm_data != NULL);
460 PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000461
462 if (output_buf_len < 320)
463 return PJMEDIA_CODEC_EPCMTOOSHORT;
464
465 if (input->size < 33)
466 return PJMEDIA_CODEC_EFRMTOOSHORT;
467
Benny Prijonof93895c2006-03-05 11:50:53 +0000468 gsm_decode(gsm_data->decoder,
Benny Prijonof80b1bf2006-02-19 02:24:27 +0000469 (unsigned char*)input->buf,
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000470 (short*)output->buf);
471
472 output->size = 320;
473 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
474
475 return PJ_SUCCESS;
476}
Benny Prijono4381efe2006-03-16 14:24:26 +0000477
478
479#endif /* PJMEDIA_HAS_GSM_CODEC */
480