/* * Register a codec factory. */ PJ_DEF(pj_status_t) pjmedia_codec_mgr_register_factory( pjmedia_codec_mgr *mgr, pjmedia_codec_factory *factory) { pjmedia_codec_info info[PJMEDIA_CODEC_MGR_MAX_CODECS]; unsigned i, count; pj_status_t status; PJ_ASSERT_RETURN(mgr && factory, PJ_EINVAL); /* Since 2.0 we require codec factory to implement "destroy" op. Please * see: https://trac.pjsip.org/repos/ticket/1294 * * Really! Please do see it. */ PJ_ASSERT_RETURN(factory->op->destroy != NULL, PJ_ENOTSUP); /* Enum codecs */ count = PJ_ARRAY_SIZE(info); status = factory->op->enum_info(factory, &count, info); if (status != PJ_SUCCESS) return status; pj_mutex_lock(mgr->mutex); /* Check codec count */ if (count + mgr->codec_cnt > PJ_ARRAY_SIZE(mgr->codec_desc)) { pj_mutex_unlock(mgr->mutex); return PJ_ETOOMANY; } /* Save the codecs */ for (i=0; i<count; ++i) { pj_memcpy( &mgr->codec_desc[mgr->codec_cnt+i], &info[i], sizeof(pjmedia_codec_info)); mgr->codec_desc[mgr->codec_cnt+i].prio = PJMEDIA_CODEC_PRIO_NORMAL; mgr->codec_desc[mgr->codec_cnt+i].factory = factory; pjmedia_codec_info_to_id( &info[i], mgr->codec_desc[mgr->codec_cnt+i].id, sizeof(pjmedia_codec_id)); } /* Update count */ mgr->codec_cnt += count; /* Re-sort codec based on priorities */ sort_codecs(mgr); /* Add factory to the list */ pj_list_push_back(&mgr->factory_list, factory); pj_mutex_unlock(mgr->mutex); return PJ_SUCCESS; }
void list_codecs(pjmedia_endpt *ep) { unsigned int count = 100; int i; char buffer[100]; pjmedia_codec_mgr *mgr = 0; pjmedia_codec_info codec[20]; mgr = pjmedia_endpt_get_codec_mgr(ep); pjmedia_codec_mgr_enum_codecs(mgr, &count, codec, NULL); PJ_LOG(3, (__FILE__, "THERE ARE %d codecs", count)); for ( i = 0; i < count; i++ ) { pj_bzero(buffer, sizeof(buffer)); pjmedia_codec_info_to_id(&codec[i], buffer, sizeof(buffer)); PJ_LOG(3, (__FILE__, "Codec : %s", buffer)); } }
/* * Register a codec factory. */ PJ_DEF(pj_status_t) pjmedia_codec_mgr_register_factory( pjmedia_codec_mgr *mgr, pjmedia_codec_factory *factory) { pjmedia_codec_info info[PJMEDIA_CODEC_MGR_MAX_CODECS]; unsigned i, count; pj_status_t status; PJ_ASSERT_RETURN(mgr && factory, PJ_EINVAL); /* Enum codecs */ count = PJ_ARRAY_SIZE(info); status = factory->op->enum_info(factory, &count, info); if (status != PJ_SUCCESS) return status; /* Check codec count */ if (count + mgr->codec_cnt > PJ_ARRAY_SIZE(mgr->codec_desc)) return PJ_ETOOMANY; /* Save the codecs */ for (i=0; i<count; ++i) { pj_memcpy( &mgr->codec_desc[mgr->codec_cnt+i], &info[i], sizeof(pjmedia_codec_info)); mgr->codec_desc[mgr->codec_cnt+i].prio = PJMEDIA_CODEC_PRIO_NORMAL; mgr->codec_desc[mgr->codec_cnt+i].factory = factory; pjmedia_codec_info_to_id( &info[i], mgr->codec_desc[mgr->codec_cnt+i].id, sizeof(pjmedia_codec_id)); } /* Update count */ mgr->codec_cnt += count; /* Re-sort codec based on priorities */ sort_codecs(mgr); /* Add factory to the list */ pj_list_push_back(&mgr->factory_list, factory); return PJ_SUCCESS; }
/* * Internal function for collecting codec info and param from the SDP media. */ static pj_status_t get_audio_codec_info_param(pjmedia_stream_info *si, pj_pool_t *pool, pjmedia_codec_mgr *mgr, const pjmedia_sdp_media *local_m, const pjmedia_sdp_media *rem_m) { const pjmedia_sdp_attr *attr; pjmedia_sdp_rtpmap *rtpmap; unsigned i, fmti, pt = 0; pj_status_t status; /* Find the first codec which is not telephone-event */ for ( fmti = 0; fmti < local_m->desc.fmt_count; ++fmti ) { pjmedia_sdp_rtpmap r; if ( !pj_isdigit(*local_m->desc.fmt[fmti].ptr) ) return PJMEDIA_EINVALIDPT; pt = pj_strtoul(&local_m->desc.fmt[fmti]); if (pt < 96) { /* This is known static PT. Skip rtpmap checking because it is * optional. */ break; } attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, &local_m->desc.fmt[fmti]); if (attr == NULL) continue; status = pjmedia_sdp_attr_get_rtpmap(attr, &r); if (status != PJ_SUCCESS) continue; if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) != 0) break; } if ( fmti >= local_m->desc.fmt_count ) return PJMEDIA_EINVALIDPT; /* Get payload type for receiving direction */ si->rx_pt = pt; /* Get codec info. * For static payload types, get the info from codec manager. * For dynamic payload types, MUST get the rtpmap. */ if (pt < 96) { pj_bool_t has_rtpmap; rtpmap = NULL; has_rtpmap = PJ_TRUE; attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, &local_m->desc.fmt[fmti]); if (attr == NULL) { has_rtpmap = PJ_FALSE; } if (attr != NULL) { status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap); if (status != PJ_SUCCESS) has_rtpmap = PJ_FALSE; } /* Build codec format info: */ if (has_rtpmap) { si->fmt.type = si->type; si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]); pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name); si->fmt.clock_rate = rtpmap->clock_rate; #if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG != 0) /* The session info should have the actual clock rate, because * this info is used for calculationg buffer size, etc in stream */ if (si->fmt.pt == PJMEDIA_RTP_PT_G722) si->fmt.clock_rate = 16000; #endif /* For audio codecs, rtpmap parameters denotes the number of * channels. */ if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) { si->fmt.channel_cnt = (unsigned) pj_strtoul(&rtpmap->param); } else { si->fmt.channel_cnt = 1; } } else { const pjmedia_codec_info *p_info; status = pjmedia_codec_mgr_get_codec_info( mgr, pt, &p_info); if (status != PJ_SUCCESS) return status; pj_memcpy(&si->fmt, p_info, sizeof(pjmedia_codec_info)); } /* For static payload type, pt's are symetric */ si->tx_pt = pt; } else { pjmedia_codec_id codec_id; pj_str_t codec_id_st; const pjmedia_codec_info *p_info; attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, &local_m->desc.fmt[fmti]); if (attr == NULL) return PJMEDIA_EMISSINGRTPMAP; status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap); if (status != PJ_SUCCESS) return status; /* Build codec format info: */ si->fmt.type = si->type; si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]); si->fmt.encoding_name = rtpmap->enc_name; si->fmt.clock_rate = rtpmap->clock_rate; /* For audio codecs, rtpmap parameters denotes the number of * channels. */ if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) { si->fmt.channel_cnt = (unsigned) pj_strtoul(&rtpmap->param); } else { si->fmt.channel_cnt = 1; } /* Normalize the codec info from codec manager. Note that the * payload type will be resetted to its default (it might have * been rewritten by the SDP negotiator to match to the remote * offer), this is intentional as currently some components may * prefer (or even require) the default PT in codec info. */ pjmedia_codec_info_to_id(&si->fmt, codec_id, sizeof(codec_id)); i = 1; codec_id_st = pj_str(codec_id); status = pjmedia_codec_mgr_find_codecs_by_id(mgr, &codec_id_st, &i, &p_info, NULL); if (status != PJ_SUCCESS) return status; pj_memcpy(&si->fmt, p_info, sizeof(pjmedia_codec_info)); /* Determine payload type for outgoing channel, by finding * dynamic payload type in remote SDP that matches the answer. */ si->tx_pt = 0xFFFF; for (i=0; i<rem_m->desc.fmt_count; ++i) { if (pjmedia_sdp_neg_fmt_match(pool, (pjmedia_sdp_media*)local_m, fmti, (pjmedia_sdp_media*)rem_m, i, 0) == PJ_SUCCESS) { /* Found matched codec. */ si->tx_pt = pj_strtoul(&rem_m->desc.fmt[i]); break; } } if (si->tx_pt == 0xFFFF) return PJMEDIA_EMISSINGRTPMAP; } /* Now that we have codec info, get the codec param. */ si->param = PJ_POOL_ALLOC_T(pool, pjmedia_codec_param); status = pjmedia_codec_mgr_get_default_param(mgr, &si->fmt, si->param); /* Get remote fmtp for our encoder. */ pjmedia_stream_info_parse_fmtp(pool, rem_m, si->tx_pt, &si->param->setting.enc_fmtp); /* Get local fmtp for our decoder. */ pjmedia_stream_info_parse_fmtp(pool, local_m, si->rx_pt, &si->param->setting.dec_fmtp); if (!pj_stricmp2(&si->fmt.encoding_name, "opus")) { get_opus_channels_and_clock_rate(&si->param->setting.enc_fmtp, &si->param->setting.dec_fmtp, &si->fmt.channel_cnt, &si->fmt.clock_rate); } /* Get the remote ptime for our encoder. */ attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, "ptime", NULL); if (attr) { pj_str_t tmp_val = attr->value; unsigned frm_per_pkt; pj_strltrim(&tmp_val); /* Round up ptime when the specified is not multiple of frm_ptime */ frm_per_pkt = (pj_strtoul(&tmp_val) + si->param->info.frm_ptime/2) / si->param->info.frm_ptime; if (frm_per_pkt != 0) { si->param->setting.frm_per_pkt = (pj_uint8_t)frm_per_pkt; } } /* Get remote maxptime for our encoder. */ attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, "maxptime", NULL); if (attr) { pj_str_t tmp_val = attr->value; pj_strltrim(&tmp_val); si->tx_maxptime = pj_strtoul(&tmp_val); } /* When direction is NONE (it means SDP negotiation has failed) we don't * need to return a failure here, as returning failure will cause * the whole SDP to be rejected. See ticket #: * http:// * * Thanks Alain Totouom */ if (status != PJ_SUCCESS && si->dir != PJMEDIA_DIR_NONE) return status; /* Get incomming payload type for telephone-events */ si->rx_event_pt = -1; for (i=0; i<local_m->attr_count; ++i) { pjmedia_sdp_rtpmap r; attr = local_m->attr[i]; if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0) continue; if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS) continue; if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) { si->rx_event_pt = pj_strtoul(&r.pt); break; } } /* Get outgoing payload type for telephone-events */ si->tx_event_pt = -1; for (i=0; i<rem_m->attr_count; ++i) { pjmedia_sdp_rtpmap r; attr = rem_m->attr[i]; if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0) continue; if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS) continue; if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) { si->tx_event_pt = pj_strtoul(&r.pt); break; } } return PJ_SUCCESS; }
/* * Set default codec parameter. */ PJ_DEF(pj_status_t) pjmedia_codec_mgr_set_default_param( pjmedia_codec_mgr *mgr, const pjmedia_codec_info *info, const pjmedia_codec_param *param ) { unsigned i; pjmedia_codec_id codec_id; pj_pool_t *pool, *old_pool = NULL; struct pjmedia_codec_desc *codec_desc = NULL; pjmedia_codec_default_param *p; PJ_ASSERT_RETURN(mgr && info, PJ_EINVAL); if (!pjmedia_codec_info_to_id(info, (char*)&codec_id, sizeof(codec_id))) return PJ_EINVAL; pj_mutex_lock(mgr->mutex); /* Lookup codec desc */ for (i=0; i < mgr->codec_cnt; ++i) { if (pj_ansi_stricmp(codec_id, mgr->codec_desc[i].id) == 0) { codec_desc = &mgr->codec_desc[i]; break; } } /* Codec not found */ if (!codec_desc) { pj_mutex_unlock(mgr->mutex); return PJMEDIA_CODEC_EUNSUP; } /* If codec param is previously set, reset the codec param but release * the codec param pool later after the new param is set (ticket #1171). */ if (codec_desc->param) { pj_assert(codec_desc->param->pool); old_pool = codec_desc->param->pool; codec_desc->param = NULL; } /* When param is set to NULL, i.e: setting default codec param to library * default setting, just return PJ_SUCCESS. */ if (NULL == param) { pj_mutex_unlock(mgr->mutex); if (old_pool) pj_pool_release(old_pool); return PJ_SUCCESS; } /* Instantiate and initialize codec param */ pool = pj_pool_create(mgr->pf, (char*)codec_id, 256, 256, NULL); codec_desc->param = PJ_POOL_ZALLOC_T(pool, pjmedia_codec_default_param); p = codec_desc->param; p->pool = pool; p->param = PJ_POOL_ZALLOC_T(pool, pjmedia_codec_param); /* Update codec param */ pj_memcpy(p->param, param, sizeof(pjmedia_codec_param)); for (i = 0; i < param->setting.dec_fmtp.cnt; ++i) { pj_strdup(pool, &p->param->setting.dec_fmtp.param[i].name, ¶m->setting.dec_fmtp.param[i].name); pj_strdup(pool, &p->param->setting.dec_fmtp.param[i].val, ¶m->setting.dec_fmtp.param[i].val); } for (i = 0; i < param->setting.enc_fmtp.cnt; ++i) { pj_strdup(pool, &p->param->setting.enc_fmtp.param[i].name, ¶m->setting.enc_fmtp.param[i].name); pj_strdup(pool, &p->param->setting.enc_fmtp.param[i].val, ¶m->setting.enc_fmtp.param[i].val); } pj_mutex_unlock(mgr->mutex); if (old_pool) pj_pool_release(old_pool); return PJ_SUCCESS; }
/* * Get default codec parameter. */ PJ_DEF(pj_status_t) pjmedia_codec_mgr_get_default_param( pjmedia_codec_mgr *mgr, const pjmedia_codec_info *info, pjmedia_codec_param *param ) { pjmedia_codec_factory *factory; pj_status_t status; pjmedia_codec_id codec_id; struct pjmedia_codec_desc *codec_desc = NULL; unsigned i; PJ_ASSERT_RETURN(mgr && info && param, PJ_EINVAL); if (!pjmedia_codec_info_to_id(info, (char*)&codec_id, sizeof(codec_id))) return PJ_EINVAL; pj_mutex_lock(mgr->mutex); /* First, lookup default param in codec desc */ for (i=0; i < mgr->codec_cnt; ++i) { if (pj_ansi_stricmp(codec_id, mgr->codec_desc[i].id) == 0) { codec_desc = &mgr->codec_desc[i]; break; } } /* If we found the codec and its default param is set, return it */ if (codec_desc && codec_desc->param) { pj_assert(codec_desc->param->param); pj_memcpy(param, codec_desc->param->param, sizeof(pjmedia_codec_param)); pj_mutex_unlock(mgr->mutex); return PJ_SUCCESS; } /* Otherwise query the default param from codec factory */ factory = mgr->factory_list.next; while (factory != &mgr->factory_list) { if ( (*factory->op->test_alloc)(factory, info) == PJ_SUCCESS ) { status = (*factory->op->default_attr)(factory, info, param); if (status == PJ_SUCCESS) { /* Check for invalid max_bps. */ if (param->info.max_bps < param->info.avg_bps) param->info.max_bps = param->info.avg_bps; pj_mutex_unlock(mgr->mutex); return PJ_SUCCESS; } } factory = factory->next; } pj_mutex_unlock(mgr->mutex); return PJMEDIA_CODEC_EUNSUP; }