blob: a79421e55ccca188aae214c00c4fa1aa86ad4c0a [file] [log] [blame]
Alexandre Lision8af73cb2013-12-10 14:11:20 -05001/* $Id$ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include <pjmedia/codec.h>
21#include <pjmedia/errno.h>
22#include <pj/array.h>
23#include <pj/assert.h>
24#include <pj/log.h>
25#include <pj/string.h>
26
27#define THIS_FILE "codec.c"
28
29
30
31/* Definition of default codecs parameters */
32struct pjmedia_codec_default_param
33{
34 pj_pool_t *pool;
35 pjmedia_codec_param *param;
36};
37
38
39/* Sort codecs in codec manager based on priorities */
40static void sort_codecs(pjmedia_codec_mgr *mgr);
41
42
43/*
44 * Duplicate codec parameter.
45 */
46PJ_DEF(pjmedia_codec_param*) pjmedia_codec_param_clone(
47 pj_pool_t *pool,
48 const pjmedia_codec_param *src)
49{
50 pjmedia_codec_param *p;
51 unsigned i;
52
53 PJ_ASSERT_RETURN(pool && src, NULL);
54
55 p = PJ_POOL_ZALLOC_T(pool, pjmedia_codec_param);
56
57 /* Update codec param */
58 pj_memcpy(p, src, sizeof(pjmedia_codec_param));
59 for (i = 0; i < src->setting.dec_fmtp.cnt; ++i) {
60 pj_strdup(pool, &p->setting.dec_fmtp.param[i].name,
61 &src->setting.dec_fmtp.param[i].name);
62 pj_strdup(pool, &p->setting.dec_fmtp.param[i].val,
63 &src->setting.dec_fmtp.param[i].val);
64 }
65 for (i = 0; i < src->setting.enc_fmtp.cnt; ++i) {
66 pj_strdup(pool, &p->setting.enc_fmtp.param[i].name,
67 &src->setting.enc_fmtp.param[i].name);
68 pj_strdup(pool, &p->setting.enc_fmtp.param[i].val,
69 &src->setting.enc_fmtp.param[i].val);
70 }
71
72 return p;
73}
74
75
76/*
77 * Initialize codec manager.
78 */
79PJ_DEF(pj_status_t) pjmedia_codec_mgr_init (pjmedia_codec_mgr *mgr,
80 pj_pool_factory *pf)
81{
82 pj_status_t status;
83
84 PJ_ASSERT_RETURN(mgr && pf, PJ_EINVAL);
85
86 /* Init codec manager */
87 pj_bzero(mgr, sizeof(pjmedia_codec_mgr));
88 mgr->pf = pf;
89 pj_list_init (&mgr->factory_list);
90 mgr->codec_cnt = 0;
91
92 /* Create pool */
93 mgr->pool = pj_pool_create(mgr->pf, "codec-mgr", 256, 256, NULL);
94
95 /* Create mutex */
96 status = pj_mutex_create_recursive(mgr->pool, "codec-mgr", &mgr->mutex);
97 if (status != PJ_SUCCESS)
98 return status;
99
100 return PJ_SUCCESS;
101}
102
103/*
104 * Initialize codec manager.
105 */
106PJ_DEF(pj_status_t) pjmedia_codec_mgr_destroy (pjmedia_codec_mgr *mgr)
107{
108 pjmedia_codec_factory *factory;
109 unsigned i;
110
111 PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
112
113 /* Destroy all factories in the list */
114 factory = mgr->factory_list.next;
115 while (factory != &mgr->factory_list) {
116 pjmedia_codec_factory *next = factory->next;
117 (*factory->op->destroy)();
118 factory = next;
119 }
120
121 /* Cleanup all pools of all codec default params */
122 for (i=0; i<mgr->codec_cnt; ++i) {
123 if (mgr->codec_desc[i].param) {
124 pj_assert(mgr->codec_desc[i].param->pool);
125 pj_pool_release(mgr->codec_desc[i].param->pool);
126 }
127 }
128
129 /* Destroy mutex */
130 if (mgr->mutex)
131 pj_mutex_destroy(mgr->mutex);
132
133 /* Release pool */
134 if (mgr->pool)
135 pj_pool_release(mgr->pool);
136
137 /* Just for safety, set codec manager states to zero */
138 pj_bzero(mgr, sizeof(pjmedia_codec_mgr));
139
140 return PJ_SUCCESS;
141}
142
143
144/*
145 * Register a codec factory.
146 */
147PJ_DEF(pj_status_t) pjmedia_codec_mgr_register_factory( pjmedia_codec_mgr *mgr,
148 pjmedia_codec_factory *factory)
149{
150 pjmedia_codec_info info[PJMEDIA_CODEC_MGR_MAX_CODECS];
151 unsigned i, count;
152 pj_status_t status;
153
154 PJ_ASSERT_RETURN(mgr && factory, PJ_EINVAL);
155
156 /* Since 2.0 we require codec factory to implement "destroy" op. Please
157 * see: https://trac.pjsip.org/repos/ticket/1294
158 *
159 * Really! Please do see it.
160 */
161 PJ_ASSERT_RETURN(factory->op->destroy != NULL, PJ_ENOTSUP);
162
163 /* Enum codecs */
164 count = PJ_ARRAY_SIZE(info);
165 status = factory->op->enum_info(factory, &count, info);
166 if (status != PJ_SUCCESS)
167 return status;
168
169 pj_mutex_lock(mgr->mutex);
170
171 /* Check codec count */
172 if (count + mgr->codec_cnt > PJ_ARRAY_SIZE(mgr->codec_desc)) {
173 pj_mutex_unlock(mgr->mutex);
174 return PJ_ETOOMANY;
175 }
176
177
178 /* Save the codecs */
179 for (i=0; i<count; ++i) {
180 pj_memcpy( &mgr->codec_desc[mgr->codec_cnt+i],
181 &info[i], sizeof(pjmedia_codec_info));
182 mgr->codec_desc[mgr->codec_cnt+i].prio = PJMEDIA_CODEC_PRIO_NORMAL;
183 mgr->codec_desc[mgr->codec_cnt+i].factory = factory;
184 pjmedia_codec_info_to_id( &info[i],
185 mgr->codec_desc[mgr->codec_cnt+i].id,
186 sizeof(pjmedia_codec_id));
187 }
188
189 /* Update count */
190 mgr->codec_cnt += count;
191
192 /* Re-sort codec based on priorities */
193 sort_codecs(mgr);
194
195 /* Add factory to the list */
196 pj_list_push_back(&mgr->factory_list, factory);
197
198 pj_mutex_unlock(mgr->mutex);
199
200 return PJ_SUCCESS;
201}
202
203
204/*
205 * Unregister a codec factory.
206 */
207PJ_DEF(pj_status_t) pjmedia_codec_mgr_unregister_factory(
208 pjmedia_codec_mgr *mgr,
209 pjmedia_codec_factory *factory)
210{
211 unsigned i;
212 PJ_ASSERT_RETURN(mgr && factory, PJ_EINVAL);
213
214 pj_mutex_lock(mgr->mutex);
215
216 /* Factory must be registered. */
217 if (pj_list_find_node(&mgr->factory_list, factory) != factory) {
218 pj_mutex_unlock(mgr->mutex);
219 return PJ_ENOTFOUND;
220 }
221
222 /* Erase factory from the factory list */
223 pj_list_erase(factory);
224
225
226 /* Remove all supported codecs from the codec manager that were created
227 * by the specified factory.
228 */
229 for (i=0; i<mgr->codec_cnt; ) {
230
231 if (mgr->codec_desc[i].factory == factory) {
232 /* Release pool of codec default param */
233 if (mgr->codec_desc[i].param) {
234 pj_assert(mgr->codec_desc[i].param->pool);
235 pj_pool_release(mgr->codec_desc[i].param->pool);
236 }
237
238 /* Remove the codec from array of codec descriptions */
239 pj_array_erase(mgr->codec_desc, sizeof(mgr->codec_desc[0]),
240 mgr->codec_cnt, i);
241 --mgr->codec_cnt;
242
243 } else {
244 ++i;
245 }
246 }
247
248 pj_mutex_unlock(mgr->mutex);
249
250 return PJ_SUCCESS;
251}
252
253
254/*
255 * Enum all codecs.
256 */
257PJ_DEF(pj_status_t) pjmedia_codec_mgr_enum_codecs(pjmedia_codec_mgr *mgr,
258 unsigned *count,
259 pjmedia_codec_info codecs[],
260 unsigned *prio)
261{
262 unsigned i;
263
264 PJ_ASSERT_RETURN(mgr && count && codecs, PJ_EINVAL);
265
266 pj_mutex_lock(mgr->mutex);
267
268 if (*count > mgr->codec_cnt)
269 *count = mgr->codec_cnt;
270
271 for (i=0; i<*count; ++i) {
272 pj_memcpy(&codecs[i],
273 &mgr->codec_desc[i].info,
274 sizeof(pjmedia_codec_info));
275 }
276
277 if (prio) {
278 for (i=0; i < *count; ++i)
279 prio[i] = mgr->codec_desc[i].prio;
280 }
281
282 pj_mutex_unlock(mgr->mutex);
283
284 return PJ_SUCCESS;
285}
286
287
288/*
289 * Get codec info for static payload type.
290 */
291PJ_DEF(pj_status_t) pjmedia_codec_mgr_get_codec_info( pjmedia_codec_mgr *mgr,
292 unsigned pt,
293 const pjmedia_codec_info **p_info)
294{
295 unsigned i;
296
297 PJ_ASSERT_RETURN(mgr && p_info && pt>=0 && pt < 96, PJ_EINVAL);
298
299 pj_mutex_lock(mgr->mutex);
300
301 for (i=0; i<mgr->codec_cnt; ++i) {
302 if (mgr->codec_desc[i].info.pt == pt) {
303 *p_info = &mgr->codec_desc[i].info;
304
305 pj_mutex_unlock(mgr->mutex);
306 return PJ_SUCCESS;
307 }
308 }
309
310 pj_mutex_unlock(mgr->mutex);
311
312 return PJMEDIA_CODEC_EUNSUP;
313}
314
315
316/*
317 * Convert codec info struct into a unique codec identifier.
318 * A codec identifier looks something like "L16/44100/2".
319 */
320PJ_DEF(char*) pjmedia_codec_info_to_id( const pjmedia_codec_info *info,
321 char *id, unsigned max_len )
322{
323 int len;
324
325 PJ_ASSERT_RETURN(info && id && max_len, NULL);
326
327 len = pj_ansi_snprintf(id, max_len, "%.*s/%u/%u",
328 (int)info->encoding_name.slen,
329 info->encoding_name.ptr,
330 info->clock_rate,
331 info->channel_cnt);
332
333 if (len < 1 || len >= (int)max_len) {
334 id[0] = '\0';
335 return NULL;
336 }
337
338 return id;
339}
340
341
342/*
343 * Find codecs by the unique codec identifier. This function will find
344 * all codecs that match the codec identifier prefix. For example, if
345 * "L16" is specified, then it will find "L16/8000/1", "L16/16000/1",
346 * and so on, up to the maximum count specified in the argument.
347 */
348PJ_DEF(pj_status_t) pjmedia_codec_mgr_find_codecs_by_id( pjmedia_codec_mgr *mgr,
349 const pj_str_t *codec_id,
350 unsigned *count,
351 const pjmedia_codec_info *p_info[],
352 unsigned prio[])
353{
354 unsigned i, found = 0;
355
356 PJ_ASSERT_RETURN(mgr && codec_id && count && *count, PJ_EINVAL);
357
358 pj_mutex_lock(mgr->mutex);
359
360 for (i=0; i<mgr->codec_cnt; ++i) {
361
362 if (codec_id->slen == 0 ||
363 pj_strnicmp2(codec_id, mgr->codec_desc[i].id,
364 codec_id->slen) == 0)
365 {
366
367 if (p_info)
368 p_info[found] = &mgr->codec_desc[i].info;
369 if (prio)
370 prio[found] = mgr->codec_desc[i].prio;
371
372 ++found;
373
374 if (found >= *count)
375 break;
376 }
377
378 }
379
380 pj_mutex_unlock(mgr->mutex);
381
382 *count = found;
383
384 return found ? PJ_SUCCESS : PJ_ENOTFOUND;
385}
386
387
388/* Swap two codecs positions in codec manager */
389static void swap_codec(pjmedia_codec_mgr *mgr, unsigned i, unsigned j)
390{
391 struct pjmedia_codec_desc tmp;
392
393 pj_memcpy(&tmp, &mgr->codec_desc[i], sizeof(struct pjmedia_codec_desc));
394
395 pj_memcpy(&mgr->codec_desc[i], &mgr->codec_desc[j],
396 sizeof(struct pjmedia_codec_desc));
397
398 pj_memcpy(&mgr->codec_desc[j], &tmp, sizeof(struct pjmedia_codec_desc));
399}
400
401
402/* Sort codecs in codec manager based on priorities */
403static void sort_codecs(pjmedia_codec_mgr *mgr)
404{
405 unsigned i;
406
407 /* Re-sort */
408 for (i=0; i<mgr->codec_cnt; ++i) {
409 unsigned j, max;
410
411 for (max=i, j=i+1; j<mgr->codec_cnt; ++j) {
412 if (mgr->codec_desc[j].prio > mgr->codec_desc[max].prio)
413 max = j;
414 }
415
416 if (max != i)
417 swap_codec(mgr, i, max);
418 }
419
420 /* Change PJMEDIA_CODEC_PRIO_HIGHEST codecs to NEXT_HIGHER */
421 for (i=0; i<mgr->codec_cnt; ++i) {
422 if (mgr->codec_desc[i].prio == PJMEDIA_CODEC_PRIO_HIGHEST)
423 mgr->codec_desc[i].prio = PJMEDIA_CODEC_PRIO_NEXT_HIGHER;
424 else
425 break;
426 }
427}
428
429
430/**
431 * Set codec priority. The codec priority determines the order of
432 * the codec in the SDP created by the endpoint. If more than one codecs
433 * are found with the same codec_id prefix, then the function sets the
434 * priorities of all those codecs.
435 */
436PJ_DEF(pj_status_t) pjmedia_codec_mgr_set_codec_priority(
437 pjmedia_codec_mgr *mgr,
438 const pj_str_t *codec_id,
439 pj_uint8_t prio)
440{
441 unsigned i, found = 0;
442
443 PJ_ASSERT_RETURN(mgr && codec_id, PJ_EINVAL);
444
445 pj_mutex_lock(mgr->mutex);
446
447 /* Update the priorities of affected codecs */
448 for (i=0; i<mgr->codec_cnt; ++i)
449 {
450 if (codec_id->slen == 0 ||
451 pj_strnicmp2(codec_id, mgr->codec_desc[i].id,
452 codec_id->slen) == 0)
453 {
454 mgr->codec_desc[i].prio = (pjmedia_codec_priority) prio;
455 ++found;
456 }
457 }
458
459 if (!found) {
460 pj_mutex_unlock(mgr->mutex);
461 return PJ_ENOTFOUND;
462 }
463
464 /* Re-sort codecs */
465 sort_codecs(mgr);
466
467 pj_mutex_unlock(mgr->mutex);
468
469 return PJ_SUCCESS;
470}
471
472
473/*
474 * Allocate one codec.
475 */
476PJ_DEF(pj_status_t) pjmedia_codec_mgr_alloc_codec(pjmedia_codec_mgr *mgr,
477 const pjmedia_codec_info *info,
478 pjmedia_codec **p_codec)
479{
480 pjmedia_codec_factory *factory;
481 pj_status_t status;
482
483 PJ_ASSERT_RETURN(mgr && info && p_codec, PJ_EINVAL);
484
485 *p_codec = NULL;
486
487 pj_mutex_lock(mgr->mutex);
488
489 factory = mgr->factory_list.next;
490 while (factory != &mgr->factory_list) {
491
492 if ( (*factory->op->test_alloc)(factory, info) == PJ_SUCCESS ) {
493
494 status = (*factory->op->alloc_codec)(factory, info, p_codec);
495 if (status == PJ_SUCCESS) {
496 pj_mutex_unlock(mgr->mutex);
497 return PJ_SUCCESS;
498 }
499
500 }
501
502 factory = factory->next;
503 }
504
505 pj_mutex_unlock(mgr->mutex);
506
507 return PJMEDIA_CODEC_EUNSUP;
508}
509
510
511/*
512 * Get default codec parameter.
513 */
514PJ_DEF(pj_status_t) pjmedia_codec_mgr_get_default_param( pjmedia_codec_mgr *mgr,
515 const pjmedia_codec_info *info,
516 pjmedia_codec_param *param )
517{
518 pjmedia_codec_factory *factory;
519 pj_status_t status;
520 pjmedia_codec_id codec_id;
521 struct pjmedia_codec_desc *codec_desc = NULL;
522 unsigned i;
523
524 PJ_ASSERT_RETURN(mgr && info && param, PJ_EINVAL);
525
526 if (!pjmedia_codec_info_to_id(info, (char*)&codec_id, sizeof(codec_id)))
527 return PJ_EINVAL;
528
529 pj_mutex_lock(mgr->mutex);
530
531 /* First, lookup default param in codec desc */
532 for (i=0; i < mgr->codec_cnt; ++i) {
533 if (pj_ansi_stricmp(codec_id, mgr->codec_desc[i].id) == 0) {
534 codec_desc = &mgr->codec_desc[i];
535 break;
536 }
537 }
538
539 /* If we found the codec and its default param is set, return it */
540 if (codec_desc && codec_desc->param) {
541 pj_assert(codec_desc->param->param);
542 pj_memcpy(param, codec_desc->param->param,
543 sizeof(pjmedia_codec_param));
544
545 pj_mutex_unlock(mgr->mutex);
546 return PJ_SUCCESS;
547 }
548
549 /* Otherwise query the default param from codec factory */
550 factory = mgr->factory_list.next;
551 while (factory != &mgr->factory_list) {
552
553 if ( (*factory->op->test_alloc)(factory, info) == PJ_SUCCESS ) {
554
555 status = (*factory->op->default_attr)(factory, info, param);
556 if (status == PJ_SUCCESS) {
557 /* Check for invalid max_bps. */
558 if (param->info.max_bps < param->info.avg_bps)
559 param->info.max_bps = param->info.avg_bps;
560
561 pj_mutex_unlock(mgr->mutex);
562 return PJ_SUCCESS;
563 }
564
565 }
566
567 factory = factory->next;
568 }
569
570 pj_mutex_unlock(mgr->mutex);
571
572
573 return PJMEDIA_CODEC_EUNSUP;
574}
575
576
577/*
578 * Set default codec parameter.
579 */
580PJ_DEF(pj_status_t) pjmedia_codec_mgr_set_default_param(
581 pjmedia_codec_mgr *mgr,
582 const pjmedia_codec_info *info,
583 const pjmedia_codec_param *param )
584{
585 unsigned i;
586 pjmedia_codec_id codec_id;
587 pj_pool_t *pool, *old_pool = NULL;
588 struct pjmedia_codec_desc *codec_desc = NULL;
589 pjmedia_codec_default_param *p;
590
591 PJ_ASSERT_RETURN(mgr && info, PJ_EINVAL);
592
593 if (!pjmedia_codec_info_to_id(info, (char*)&codec_id, sizeof(codec_id)))
594 return PJ_EINVAL;
595
596 pj_mutex_lock(mgr->mutex);
597
598 /* Lookup codec desc */
599 for (i=0; i < mgr->codec_cnt; ++i) {
600 if (pj_ansi_stricmp(codec_id, mgr->codec_desc[i].id) == 0) {
601 codec_desc = &mgr->codec_desc[i];
602 break;
603 }
604 }
605
606 /* Codec not found */
607 if (!codec_desc) {
608 pj_mutex_unlock(mgr->mutex);
609 return PJMEDIA_CODEC_EUNSUP;
610 }
611
612 /* If codec param is previously set, reset the codec param but release
613 * the codec param pool later after the new param is set (ticket #1171).
614 */
615 if (codec_desc->param) {
616 pj_assert(codec_desc->param->pool);
617 old_pool = codec_desc->param->pool;
618 codec_desc->param = NULL;
619 }
620
621 /* When param is set to NULL, i.e: setting default codec param to library
622 * default setting, just return PJ_SUCCESS.
623 */
624 if (NULL == param) {
625 pj_mutex_unlock(mgr->mutex);
626 if (old_pool)
627 pj_pool_release(old_pool);
628 return PJ_SUCCESS;
629 }
630
631 /* Instantiate and initialize codec param */
632 pool = pj_pool_create(mgr->pf, (char*)codec_id, 256, 256, NULL);
633 codec_desc->param = PJ_POOL_ZALLOC_T(pool, pjmedia_codec_default_param);
634 p = codec_desc->param;
635 p->pool = pool;
636
637 /* Update codec param */
638 p->param = pjmedia_codec_param_clone(pool, param);
639 if (!p->param) {
640 pj_mutex_unlock(mgr->mutex);
641 return PJ_EINVAL;
642 }
643
644 pj_mutex_unlock(mgr->mutex);
645
646 if (old_pool)
647 pj_pool_release(old_pool);
648
649 return PJ_SUCCESS;
650}
651
652
653/*
654 * Dealloc codec.
655 */
656PJ_DEF(pj_status_t) pjmedia_codec_mgr_dealloc_codec(pjmedia_codec_mgr *mgr,
657 pjmedia_codec *codec)
658{
659 PJ_ASSERT_RETURN(mgr && codec, PJ_EINVAL);
660
661 return (*codec->factory->op->dealloc_codec)(codec->factory, codec);
662}
663