mg_termination_t *megaco_choose_termination(megaco_profile_t *profile, const char *prefix)
{
mg_termination_type_t termtype;
+ switch_memory_pool_t *pool;
+ mg_termination_t *term = NULL;
+ char name[100];
+ int term_id;
/* Check the termination type by prefix */
if (strncasecmp(prefix, profile->rtp_termination_id_prefix, strlen(profile->rtp_termination_id_prefix)) == 0) {
termtype = MG_TERM_RTP;
+ term_id = mg_rtp_request_id(profile);
+ switch_snprintf(name, sizeof name, "%s/%d", profile->rtp_termination_id_prefix, term_id);
} else {
/* TODO Math: look through TDM channels */
return NULL;
}
+ switch_core_new_memory_pool(&pool);
+ term = switch_core_alloc(pool, sizeof *term);
+ term->pool = pool;
+ term->type = termtype;
+
+ if (termtype == MG_TERM_RTP) {
+ /* Fill in local address and reserve an rtp port */
+ term->u.rtp.local_addr = profile->my_ipaddr;
+ term->u.rtp.local_port = switch_rtp_request_port(term->u.rtp.local_addr);
+ term->u.rtp.codec = megaco_codec_str(profile->default_codec);
+ term->u.rtp.term_id = term_id;
+ term->name = switch_core_strdup(term->pool, name);
+ } else if (termtype == MG_TERM_TDM) {
+ /* XXX initialize tdm-specific fields */
+ }
+
+ switch_core_hash_insert_wrlock(profile->terminations, term->name, term, profile->terminations_rwlock);
+ return term;
}
mg_termination_t *megaco_find_termination(megaco_profile_t *profile, const char *name)
term->uuid = NULL;
}
+ if (term->type == MG_TERM_RTP && term->u.rtp.local_port != 0) {
+ switch_rtp_release_port(term->u.rtp.local_addr, term->u.rtp.local_port);
+ }
+
switch_core_hash_delete_wrlock(term->profile->terminations, term->name, term->profile->terminations_rwlock);
+ switch_core_destroy_memory_pool(&term->pool);
}
switch_status_t megaco_context_add_termination(mg_context_t *ctx, mg_termination_t *term)
switch_ivr_uuid_bridge(ctx->terminations[0]->uuid, ctx->terminations[1]->uuid);
}
+
+ return SWITCH_STATUS_SUCCESS;
}
}
megaco_termination_destroy(term);
+
+ return SWITCH_STATUS_SUCCESS;
}
switch_thread_rwlock_unlock(profile->contexts_rwlock);
}
+uint32_t mg_rtp_request_id(megaco_profile_t *profile)
+{
+ if (profile->rtpid_next >= MG_MAX_RTPID || profile->rtpid_next == 0) {
+ profile->rtpid_next = 1;
+ }
+
+ for (; profile->rtpid_next < MG_MAX_RTPID; profile->rtpid_next++) {
+ if ((profile->rtpid_bitmap[profile->rtpid_next % 8] & (1 << (profile->rtpid_next / 8))) == 0) {
+ profile->rtpid_bitmap[profile->rtpid_next % 8] |= 1 << (profile->rtpid_next / 8);
+ return profile->rtpid_next;
+ }
+ }
+
+ return 0;
+}
+
+void mg_rtp_release_id(megaco_profile_t *profile, uint32_t id)
+{
+ profile->rtpid_bitmap[id % 8] &= ~(1 << (id / 8));
+}
+
switch_status_t megaco_profile_start(const char *profilename)
{
switch_memory_pool_t *pool;
MEGACO_CODEC_G729,
MEGACO_CODEC_G723_1,
MEGACO_CODEC_ILBC,
+
+ /* Nothing below this line */
+ MEGACO_CODEC_INVALID = 0xFFFFFFFF
} megaco_codec_t;
typedef struct mg_peer_profile_s{
#define kCHAN_ID "chan"
typedef struct mg_termination_s {
+ switch_memory_pool_t *pool;
mg_termination_type_t type;
const char *name; /*!< Megaco Name */
const char *uuid; /*!< UUID of the associated FS channel, or NULL if it's not activated */
int rfc2833_pt; /*!< If the stream is using rfc2833 for dtmf events, this has to be set to its negotiated payload type */
int rate; /*!< Sampling rate */
const char *codec; /*!< Codec to use, using the freeswitch nomenclature. This could be "PCMU" for G711.U, "PCMA" for G711.A, or "G729" for g729 */
+ int term_id;
} rtp;
struct {
#define MG_CONTEXT_MODULO 16
#define MG_MAX_CONTEXTS 32768
+#define MG_MAX_RTPID 32768
struct megaco_profile_s {
uint8_t contexts_bitmap[MG_MAX_CONTEXTS/8]; /* Availability matrix, enough bits for a 32768 bitmap */
mg_context_t *contexts[MG_CONTEXT_MODULO];
+ uint8_t rtpid_bitmap[MG_MAX_CONTEXTS/8];
+ uint32_t rtpid_next;
switch_hash_t *terminations;
switch_thread_rwlock_t *terminations_rwlock;
};
+static inline const char *megaco_codec_str(megaco_codec_t codec)
+{
+ switch (codec) {
+ case MEGACO_CODEC_PCMU:
+ return "PCMU";
+ case MEGACO_CODEC_PCMA:
+ return "PCMA";
+ case MEGACO_CODEC_G729:
+ return "G729";
+ case MEGACO_CODEC_G723_1:
+ return "G723"; /* XXX double check this */
+ case MEGACO_CODEC_ILBC:
+ return "ILBC";
+ case MEGACO_CODEC_INVALID:
+ default:
+ return NULL;
+ }
+}
+
+static inline megaco_codec_t megaco_codec_parse(const char *codec) {
+ if (!strcasecmp(codec, "PCMU")) {
+ return MEGACO_CODEC_PCMU;
+ } else if (!strcasecmp(codec, "PCMA")) {
+ return MEGACO_CODEC_PCMA;
+ } else if (!strcasecmp(codec, "G729")) {
+ return MEGACO_CODEC_G729;
+ } else if (!strcasecmp(codec, "G723")) {
+ return MEGACO_CODEC_G723_1;
+ } else if (!strcasecmp(codec, "ILBC")) {
+ return MEGACO_CODEC_ILBC;
+ } else {
+ return MEGACO_CODEC_INVALID;
+ }
+}
+
megaco_profile_t *megaco_profile_locate(const char *name);
mg_peer_profile_t *megaco_peer_profile_locate(const char *name);
switch_status_t megaco_profile_start(const char *profilename);
switch_status_t megaco_profile_destroy(megaco_profile_t **profile);
+uint32_t mg_rtp_request_id(megaco_profile_t *profile);
+void mg_rtp_release_id(megaco_profile_t *profile, uint32_t id);
+
mg_context_t *megaco_get_context(megaco_profile_t *profile, uint32_t context_id);
mg_context_t *megaco_choose_context(megaco_profile_t *profile);
void megaco_release_context(mg_context_t *ctx);
+mg_termination_t *megaco_choose_termination(megaco_profile_t *profile, const char *prefix);
+mg_termination_t *megaco_find_termination(megaco_profile_t *profile, const char *name);
+void megaco_termination_destroy(mg_termination_t *term);
+
megaco_profile_t* megaco_get_profile_by_suId(SuId suId);
mg_context_t *megaco_find_context_by_suid(SuId suId, uint32_t context_id);