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