blob: 46c49f08363bc4bd78eb33b1b270223569658e60 [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
30/* Prototypes for GSM factory */
31static pj_status_t gsm_test_alloc( pjmedia_codec_factory *factory,
32 const pjmedia_codec_info *id );
33static pj_status_t gsm_default_attr( pjmedia_codec_factory *factory,
34 const pjmedia_codec_info *id,
35 pjmedia_codec_param *attr );
36static pj_status_t gsm_enum_codecs( pjmedia_codec_factory *factory,
37 unsigned *count,
38 pjmedia_codec_info codecs[]);
39static pj_status_t gsm_alloc_codec( pjmedia_codec_factory *factory,
40 const pjmedia_codec_info *id,
41 pjmedia_codec **p_codec);
42static pj_status_t gsm_dealloc_codec( pjmedia_codec_factory *factory,
43 pjmedia_codec *codec );
44
45/* Prototypes for GSM implementation. */
46static pj_status_t gsm_codec_default_attr(pjmedia_codec *codec,
47 pjmedia_codec_param *attr);
48static pj_status_t gsm_codec_init( pjmedia_codec *codec,
49 pj_pool_t *pool );
50static pj_status_t gsm_codec_open( pjmedia_codec *codec,
51 pjmedia_codec_param *attr );
52static pj_status_t gsm_codec_close( pjmedia_codec *codec );
53static pj_status_t gsm_codec_get_frames( pjmedia_codec *codec,
54 void *pkt,
55 pj_size_t pkt_size,
56 unsigned *frame_cnt,
57 pjmedia_frame frames[]);
58static pj_status_t gsm_codec_encode( pjmedia_codec *codec,
59 const struct pjmedia_frame *input,
60 unsigned output_buf_len,
61 struct pjmedia_frame *output);
62static pj_status_t gsm_codec_decode( pjmedia_codec *codec,
63 const struct pjmedia_frame *input,
64 unsigned output_buf_len,
65 struct pjmedia_frame *output);
66
67/* Definition for GSM codec operations. */
68static pjmedia_codec_op gsm_op =
69{
70 &gsm_codec_default_attr,
71 &gsm_codec_init,
72 &gsm_codec_open,
73 &gsm_codec_close,
74 &gsm_codec_get_frames,
75 &gsm_codec_encode,
76 &gsm_codec_decode
77};
78
79/* Definition for GSM codec factory operations. */
80static pjmedia_codec_factory_op gsm_factory_op =
81{
82 &gsm_test_alloc,
83 &gsm_default_attr,
84 &gsm_enum_codecs,
85 &gsm_alloc_codec,
86 &gsm_dealloc_codec
87};
88
89/* GSM factory */
90static struct gsm_codec_factory
91{
92 pjmedia_codec_factory base;
93 pjmedia_endpt *endpt;
94 pj_pool_t *pool;
95 pj_mutex_t *mutex;
96 pjmedia_codec codec_list;
97} gsm_codec_factory;
98
99/* GSM codec private data. */
Benny Prijonof93895c2006-03-05 11:50:53 +0000100struct gsm_data
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000101{
Benny Prijonof93895c2006-03-05 11:50:53 +0000102 void *encoder;
103 void *decoder;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000104};
105
106
107
108/*
109 * Initialize and register GSM codec factory to pjmedia endpoint.
110 */
111PJ_DEF(pj_status_t) pjmedia_codec_gsm_init( pjmedia_endpt *endpt )
112{
113 pjmedia_codec_mgr *codec_mgr;
114 pj_status_t status;
115
116 if (gsm_codec_factory.pool != NULL)
117 return PJ_SUCCESS;
118
119 /* Create GSM codec factory. */
120 gsm_codec_factory.base.op = &gsm_factory_op;
121 gsm_codec_factory.base.factory_data = NULL;
122 gsm_codec_factory.endpt = endpt;
123
124 gsm_codec_factory.pool = pjmedia_endpt_create_pool(endpt, "gsm", 4000,
125 4000);
126 if (!gsm_codec_factory.pool)
127 return PJ_ENOMEM;
128
129 pj_list_init(&gsm_codec_factory.codec_list);
130
131 /* Create mutex. */
132 status = pj_mutex_create_simple(gsm_codec_factory.pool, "gsm",
133 &gsm_codec_factory.mutex);
134 if (status != PJ_SUCCESS)
135 goto on_error;
136
137 /* Get the codec manager. */
138 codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
139 if (!codec_mgr) {
140 status = PJ_EINVALIDOP;
141 goto on_error;
142 }
143
144 /* Register codec factory to endpoint. */
145 status = pjmedia_codec_mgr_register_factory(codec_mgr,
146 &gsm_codec_factory.base);
147 if (status != PJ_SUCCESS)
148 goto on_error;
149
150 /* Done. */
151 return PJ_SUCCESS;
152
153on_error:
154 pj_pool_release(gsm_codec_factory.pool);
155 gsm_codec_factory.pool = NULL;
156 return status;
157}
158
159
160
161/*
162 * Unregister GSM codec factory from pjmedia endpoint and deinitialize
163 * the GSM codec library.
164 */
165PJ_DEF(pj_status_t) pjmedia_codec_gsm_deinit(void)
166{
167 pjmedia_codec_mgr *codec_mgr;
168 pj_status_t status;
169
170 if (gsm_codec_factory.pool == NULL)
171 return PJ_SUCCESS;
172
173 /* We don't want to deinit if there's outstanding codec. */
174 pj_mutex_lock(gsm_codec_factory.mutex);
175 if (!pj_list_empty(&gsm_codec_factory.codec_list)) {
176 pj_mutex_unlock(gsm_codec_factory.mutex);
177 return PJ_EBUSY;
178 }
179
180 /* Get the codec manager. */
181 codec_mgr = pjmedia_endpt_get_codec_mgr(gsm_codec_factory.endpt);
182 if (!codec_mgr) {
183 pj_pool_release(gsm_codec_factory.pool);
184 gsm_codec_factory.pool = NULL;
185 return PJ_EINVALIDOP;
186 }
187
188 /* Unregister GSM codec factory. */
189 status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
190 &gsm_codec_factory.base);
191
192 /* Destroy mutex. */
193 pj_mutex_destroy(gsm_codec_factory.mutex);
194
195 /* Destroy pool. */
196 pj_pool_release(gsm_codec_factory.pool);
197 gsm_codec_factory.pool = NULL;
198
199 return status;
200}
201
202/*
203 * Check if factory can allocate the specified codec.
204 */
205static pj_status_t gsm_test_alloc( pjmedia_codec_factory *factory,
206 const pjmedia_codec_info *info )
207{
208 PJ_UNUSED_ARG(factory);
209
210 /* Check payload type. */
211 if (info->pt != PJMEDIA_RTP_PT_GSM)
212 return PJMEDIA_CODEC_EUNSUP;
213
214 /* Ignore the rest, since it's static payload type. */
215
216 return PJ_SUCCESS;
217}
218
219/*
220 * Generate default attribute.
221 */
222static pj_status_t gsm_default_attr (pjmedia_codec_factory *factory,
223 const pjmedia_codec_info *id,
224 pjmedia_codec_param *attr )
225{
226 PJ_UNUSED_ARG(factory);
227 PJ_UNUSED_ARG(id);
228
229 pj_memset(attr, 0, sizeof(pjmedia_codec_param));
230 attr->sample_rate = 8000;
231 attr->avg_bps = 13200;
232 attr->pcm_bits_per_sample = 16;
233 attr->ptime = 20;
234 attr->pt = PJMEDIA_RTP_PT_GSM;
235
236 /* Default all flag bits disabled. */
237
238 return PJ_SUCCESS;
239}
240
241/*
242 * Enum codecs supported by this factory (i.e. only GSM!).
243 */
244static pj_status_t gsm_enum_codecs(pjmedia_codec_factory *factory,
245 unsigned *count,
246 pjmedia_codec_info codecs[])
247{
248 PJ_UNUSED_ARG(factory);
249 PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
250
251 pj_memset(&codecs[0], 0, sizeof(pjmedia_codec_info));
252 codecs[0].encoding_name = pj_str("GSM");
253 codecs[0].pt = PJMEDIA_RTP_PT_GSM;
254 codecs[0].type = PJMEDIA_TYPE_AUDIO;
255 codecs[0].sample_rate = 8000;
256
257 *count = 1;
258
259 return PJ_SUCCESS;
260}
261
262/*
263 * Allocate a new GSM codec instance.
264 */
265static pj_status_t gsm_alloc_codec( pjmedia_codec_factory *factory,
266 const pjmedia_codec_info *id,
267 pjmedia_codec **p_codec)
268{
269 pjmedia_codec *codec;
Benny Prijonof93895c2006-03-05 11:50:53 +0000270 struct gsm_data *gsm_data;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000271
272 PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
273 PJ_ASSERT_RETURN(factory == &gsm_codec_factory.base, PJ_EINVAL);
274
275
276 pj_mutex_lock(gsm_codec_factory.mutex);
277
278 /* Get free nodes, if any. */
279 if (!pj_list_empty(&gsm_codec_factory.codec_list)) {
280 codec = gsm_codec_factory.codec_list.next;
281 pj_list_erase(codec);
282 } else {
283 codec = pj_pool_zalloc(gsm_codec_factory.pool,
284 sizeof(pjmedia_codec));
285 PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM);
Benny Prijonof80b1bf2006-02-19 02:24:27 +0000286 codec->op = &gsm_op;
287 codec->factory = factory;
Benny Prijonof93895c2006-03-05 11:50:53 +0000288
289 gsm_data = pj_pool_zalloc(gsm_codec_factory.pool,
290 sizeof(struct gsm_data));
291 codec->codec_data = gsm_data;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000292 }
293
294 pj_mutex_unlock(gsm_codec_factory.mutex);
295
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000296 *p_codec = codec;
297 return PJ_SUCCESS;
298}
299
300/*
301 * Free codec.
302 */
303static pj_status_t gsm_dealloc_codec( pjmedia_codec_factory *factory,
304 pjmedia_codec *codec )
305{
Benny Prijonof93895c2006-03-05 11:50:53 +0000306 struct gsm_data *gsm_data;
307
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000308 PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
309 PJ_ASSERT_RETURN(factory == &gsm_codec_factory.base, PJ_EINVAL);
310
Benny Prijonof93895c2006-03-05 11:50:53 +0000311 gsm_data = codec->codec_data;
312
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000313 /* Close codec, if it's not closed. */
Benny Prijonof93895c2006-03-05 11:50:53 +0000314 gsm_codec_close(codec);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000315
316 /* Put in the free list. */
317 pj_mutex_lock(gsm_codec_factory.mutex);
318 pj_list_push_front(&gsm_codec_factory.codec_list, codec);
319 pj_mutex_unlock(gsm_codec_factory.mutex);
320
321 return PJ_SUCCESS;
322}
323
324/*
325 * Get codec default attributes.
326 */
327static pj_status_t gsm_codec_default_attr( pjmedia_codec *codec,
328 pjmedia_codec_param *attr)
329{
330 return gsm_default_attr( codec->factory, NULL, attr);
331}
332
333/*
334 * Init codec.
335 */
336static pj_status_t gsm_codec_init( pjmedia_codec *codec,
337 pj_pool_t *pool )
338{
339 PJ_UNUSED_ARG(codec);
340 PJ_UNUSED_ARG(pool);
341 return PJ_SUCCESS;
342}
343
344/*
345 * Open codec.
346 */
347static pj_status_t gsm_codec_open( pjmedia_codec *codec,
348 pjmedia_codec_param *attr )
349{
Benny Prijonof93895c2006-03-05 11:50:53 +0000350 struct gsm_data *gsm_data = codec->codec_data;
351
352 pj_assert(gsm_data != NULL);
353 pj_assert(gsm_data->encoder == NULL && gsm_data->decoder == NULL);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000354
355 PJ_UNUSED_ARG(attr);
356
Benny Prijonof93895c2006-03-05 11:50:53 +0000357 gsm_data->encoder = gsm_create();
358 if (!gsm_data->encoder)
359 return PJMEDIA_CODEC_EFAILED;
360
361 gsm_data->decoder = gsm_create();
362 if (!gsm_data->decoder)
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000363 return PJMEDIA_CODEC_EFAILED;
364
365 return PJ_SUCCESS;
366}
367
368/*
369 * Close codec.
370 */
371static pj_status_t gsm_codec_close( pjmedia_codec *codec )
372{
Benny Prijonof93895c2006-03-05 11:50:53 +0000373 struct gsm_data *gsm_data = codec->codec_data;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000374
Benny Prijonof93895c2006-03-05 11:50:53 +0000375 pj_assert(gsm_data != NULL);
376
377 if (gsm_data->encoder) {
378 gsm_destroy(gsm_data->encoder);
379 gsm_data->encoder = NULL;
380 }
381 if (gsm_data->decoder) {
382 gsm_destroy(gsm_data->decoder);
383 gsm_data->decoder = NULL;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000384 }
385
386 return PJ_SUCCESS;
387}
388
389
390/*
391 * Get frames in the packet.
392 */
393static pj_status_t gsm_codec_get_frames( pjmedia_codec *codec,
394 void *pkt,
395 pj_size_t pkt_size,
396 unsigned *frame_cnt,
397 pjmedia_frame frames[])
398{
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) {
406 frames[0].type = PJMEDIA_FRAME_TYPE_AUDIO;
407 frames[0].buf = pkt;
408 frames[0].size = 33;
409
410 pkt = ((char*)pkt) + 33;
411 pkt_size -= 33;
412
413 ++count;
414 }
415
416 *frame_cnt = count;
417 return PJ_SUCCESS;
418}
419
420/*
421 * Encode frame.
422 */
423static pj_status_t gsm_codec_encode( pjmedia_codec *codec,
424 const struct pjmedia_frame *input,
425 unsigned output_buf_len,
426 struct pjmedia_frame *output)
427{
Benny Prijonof93895c2006-03-05 11:50:53 +0000428 struct gsm_data *gsm_data = codec->codec_data;
429
430 pj_assert(gsm_data != NULL);
431 PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000432
433 if (output_buf_len < 33)
434 return PJMEDIA_CODEC_EFRMTOOSHORT;
435
436 if (input->size < 320)
437 return PJMEDIA_CODEC_EPCMTOOSHORT;
438
Benny Prijonof93895c2006-03-05 11:50:53 +0000439 gsm_encode(gsm_data->encoder, (short*)input->buf,
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000440 (unsigned char*)output->buf);
441
442 output->size = 33;
443 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
444
445 return PJ_SUCCESS;
446}
447
448/*
449 * Decode frame.
450 */
451static pj_status_t gsm_codec_decode( pjmedia_codec *codec,
452 const struct pjmedia_frame *input,
453 unsigned output_buf_len,
454 struct pjmedia_frame *output)
455{
Benny Prijonof93895c2006-03-05 11:50:53 +0000456 struct gsm_data *gsm_data = codec->codec_data;
457
458 pj_assert(gsm_data != NULL);
459 PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000460
461 if (output_buf_len < 320)
462 return PJMEDIA_CODEC_EPCMTOOSHORT;
463
464 if (input->size < 33)
465 return PJMEDIA_CODEC_EFRMTOOSHORT;
466
Benny Prijonof93895c2006-03-05 11:50:53 +0000467 gsm_decode(gsm_data->decoder,
Benny Prijonof80b1bf2006-02-19 02:24:27 +0000468 (unsigned char*)input->buf,
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000469 (short*)output->buf);
470
471 output->size = 320;
472 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
473
474 return PJ_SUCCESS;
475}