1 /* $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 */
32 struct 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 */
40 static void sort_codecs(pjmedia_codec_mgr *mgr);
41 
42 
43 /*
44  * Duplicate codec parameter.
45  */
pjmedia_codec_param_clone(pj_pool_t * pool,const pjmedia_codec_param * src)46 PJ_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  */
pjmedia_codec_mgr_init(pjmedia_codec_mgr * mgr,pj_pool_factory * pf)79 PJ_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  */
pjmedia_codec_mgr_destroy(pjmedia_codec_mgr * mgr)106 PJ_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  */
pjmedia_codec_mgr_register_factory(pjmedia_codec_mgr * mgr,pjmedia_codec_factory * factory)147 PJ_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  */
pjmedia_codec_mgr_unregister_factory(pjmedia_codec_mgr * mgr,pjmedia_codec_factory * factory)207 PJ_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  */
pjmedia_codec_mgr_enum_codecs(pjmedia_codec_mgr * mgr,unsigned * count,pjmedia_codec_info codecs[],unsigned * prio)257 PJ_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  */
pjmedia_codec_mgr_get_codec_info(pjmedia_codec_mgr * mgr,unsigned pt,const pjmedia_codec_info ** p_info)291 PJ_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  */
pjmedia_codec_info_to_id(const pjmedia_codec_info * info,char * id,unsigned max_len)320 PJ_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  */
pjmedia_codec_mgr_find_codecs_by_id(pjmedia_codec_mgr * mgr,const pj_str_t * codec_id,unsigned * count,const pjmedia_codec_info * p_info[],unsigned prio[])348 PJ_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 */
swap_codec(pjmedia_codec_mgr * mgr,unsigned i,unsigned j)389 static 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 */
sort_codecs(pjmedia_codec_mgr * mgr)403 static 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  */
pjmedia_codec_mgr_set_codec_priority(pjmedia_codec_mgr * mgr,const pj_str_t * codec_id,pj_uint8_t prio)436 PJ_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  */
pjmedia_codec_mgr_alloc_codec(pjmedia_codec_mgr * mgr,const pjmedia_codec_info * info,pjmedia_codec ** p_codec)476 PJ_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  */
pjmedia_codec_mgr_get_default_param(pjmedia_codec_mgr * mgr,const pjmedia_codec_info * info,pjmedia_codec_param * param)514 PJ_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  */
pjmedia_codec_mgr_set_default_param(pjmedia_codec_mgr * mgr,const pjmedia_codec_info * info,const pjmedia_codec_param * param)580 PJ_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  */
pjmedia_codec_mgr_dealloc_codec(pjmedia_codec_mgr * mgr,pjmedia_codec * codec)656 PJ_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 
664