diff --git a/libs/freetdm/mod_freetdm/tdm.c b/libs/freetdm/mod_freetdm/tdm.c index 61a145ed3b..a3369f1f7b 100644 --- a/libs/freetdm/mod_freetdm/tdm.c +++ b/libs/freetdm/mod_freetdm/tdm.c @@ -112,7 +112,7 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi ctdm_private_t *tech_pvt = NULL; if (zstr(szchanid) || zstr(szspanid)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Both "kSPAN_ID" and "kCHAN_ID" have to be set.\n"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Both ["kSPAN_ID"] and ["kCHAN_ID"] have to be set.\n"); goto fail; } diff --git a/src/mod/endpoints/mod_media_gateway/media_gateway.c b/src/mod/endpoints/mod_media_gateway/media_gateway.c index 769bdc5465..b596cdb819 100644 --- a/src/mod/endpoints/mod_media_gateway/media_gateway.c +++ b/src/mod/endpoints/mod_media_gateway/media_gateway.c @@ -55,6 +55,8 @@ megaco_profile_t* megaco_get_profile_by_suId(SuId suId) const void *var; /*iterate through profile list to get requested suID profile */ + + switch_thread_rwlock_rdlock(megaco_globals.profile_rwlock); for (hi = switch_hash_first(NULL, megaco_globals.profile_hash); hi; hi = switch_hash_next(hi)) { switch_hash_this(hi, &var, NULL, &val); profile = (megaco_profile_t *) val; @@ -66,13 +68,172 @@ megaco_profile_t* megaco_get_profile_by_suId(SuId suId) } if(!found){ + profile = NULL; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, " Not able to find profile associated with suId[%d]\n",suId); - return NULL; } + + switch_thread_rwlock_unlock(megaco_globals.profile_rwlock); return profile; } +/* + * Creates a freeswitch channel for the specified termination. + * The channel will be parked until future actions are taken + */ +switch_status_t megaco_activate_termination(mg_termination_t *term) +{ + switch_event_t *var_event = NULL; + switch_core_session_t *session = NULL; + switch_status_t status = SWITCH_STATUS_SUCCESS; + char dialstring[100]; + switch_call_cause_t cause; + + if (!zstr(term->uuid)) { + /* A UUID is present, check if the channel still exists */ + switch_core_session_t *session; + if ((session = switch_core_session_locate(term->uuid))) { + switch_core_session_rwunlock(session); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Channel [%s] already exists for termination [%s]\n", term->uuid, term->name); + return SWITCH_STATUS_SUCCESS; + } + + /* The referenced channel doesn't exist anymore, clear it */ + term->uuid = NULL; + } + + switch_event_create(&var_event, SWITCH_EVENT_CLONE); + + if (term->type == MG_TERM_RTP) { + switch_snprintf(dialstring, sizeof dialstring, "rtp/%s", term->name); + + switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, kLOCALADDR, term->u.rtp.local_addr); + switch_event_add_header(var_event, SWITCH_STACK_BOTTOM, kLOCALPORT, "%d", term->u.rtp.local_port); + switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, kREMOTEADDR, term->u.rtp.remote_addr); + switch_event_add_header(var_event, SWITCH_STACK_BOTTOM, kREMOTEPORT, "%d", term->u.rtp.remote_port); + + switch_event_add_header(var_event, SWITCH_STACK_BOTTOM, kPTIME, "%d", term->u.rtp.ptime); + switch_event_add_header(var_event, SWITCH_STACK_BOTTOM, kPT, "%d", term->u.rtp.pt); + switch_event_add_header(var_event, SWITCH_STACK_BOTTOM, kRFC2833PT, "%d", term->u.rtp.rfc2833_pt); + switch_event_add_header(var_event, SWITCH_STACK_BOTTOM, kRATE, "%d", term->u.rtp.rate); + switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, kCODEC, term->u.rtp.codec); + } else if (term->type == MG_TERM_TDM) { + switch_snprintf(dialstring, sizeof dialstring, "tdm/%s", term->name); + + switch_event_add_header(var_event, SWITCH_STACK_BOTTOM, kSPAN_ID, "%d", term->u.tdm.span); + switch_event_add_header(var_event, SWITCH_STACK_BOTTOM, kCHAN_ID, "%d", term->u.tdm.channel); + } + + /* Set common variables on the channel */ + switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, SWITCH_PARK_AFTER_BRIDGE_VARIABLE, "true"); + + if (switch_ivr_originate(NULL, &session, &cause, dialstring, 0, NULL, NULL, NULL, NULL, var_event, 0, NULL) != SWITCH_CAUSE_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to instanciate termination [%s]: %s\n", term->name, switch_channel_cause2str(cause)); + status = SWITCH_STATUS_FALSE; + goto done; + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Termination [%s] successfully instanciated as [%s] [%s]\n", term->name, dialstring, switch_core_session_get_uuid(session)); + +done: + if (session) { + switch_core_session_rwunlock(session); + } + switch_event_destroy(&var_event); +} + +mg_termination_t *megaco_choose_termination(megaco_profile_t *profile, const char *prefix) +{ + mg_termination_type_t termtype; + + /* 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; + } else { + /* TODO Math: look through TDM channels */ + return NULL; + } + + +} + +mg_termination_t *megaco_find_termination(megaco_profile_t *profile, const char *name) +{ + mg_termination_t *term = switch_core_hash_find_rdlock(profile->terminations, name, profile->terminations_rwlock); + return term; + +} + +void megaco_termination_destroy(mg_termination_t *term) +{ + /* Lookup the FS session and hang it up */ + switch_core_session_t *session; + switch_channel_t *channel; + + if ((session = switch_core_session_locate(term->uuid))) { + channel = switch_core_session_get_channel(session); + switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); + switch_core_session_rwunlock(session); + term->uuid = NULL; + } + + switch_core_hash_delete_wrlock(term->profile->terminations, term->name, term->profile->terminations_rwlock); +} + +switch_status_t megaco_context_add_termination(mg_context_t *ctx, mg_termination_t *term) +{ + + switch_assert(ctx != NULL); + switch_assert(term != NULL); + + /* Check if the current context has existing terminations */ + if (ctx->terminations[0] && ctx->terminations[1]) { + /* Context is full */ + return SWITCH_STATUS_FALSE; + } + + if (ctx->terminations[0]) { + ctx->terminations[1] = term; + } else if (ctx->terminations[1]) { + ctx->terminations[0] = term; + } else { + ctx->terminations[0] = term; + } + + if (ctx->terminations[0] && ctx->terminations[1]) { + if (zstr(ctx->terminations[0]->uuid)) { + megaco_activate_termination(ctx->terminations[0]); + } + if (zstr(ctx->terminations[1]->uuid)) { + megaco_activate_termination(ctx->terminations[1]); + } + + switch_ivr_uuid_bridge(ctx->terminations[0]->uuid, ctx->terminations[1]->uuid); + } +} + + +switch_status_t megaco_context_sub_termination(mg_context_t *ctx, mg_termination_t *term) +{ + switch_assert(ctx != NULL); + switch_assert(term != NULL); + + /* Channels will automatically go to park once the bridge ends */ + if (ctx->terminations[0] == term) { + ctx->terminations[0] = NULL; + } else if (ctx->terminations[1] == term) { + ctx->terminations[1] = NULL; + } + + megaco_termination_destroy(term); +} + + +switch_status_t megaco_context_move_termination(mg_context_t *dst, mg_termination_t *term) +{ + +} + mg_context_t *megaco_find_context_by_suid(SuId suId, uint32_t context_id) { mg_context_t *result = NULL; @@ -87,20 +248,7 @@ mg_context_t *megaco_find_context_by_suid(SuId suId, uint32_t context_id) return NULL; } - switch_thread_rwlock_rdlock(profile->contexts_rwlock); - - /* Context exists */ - if (profile->contexts_bitmap[context_id % 8] & (1 << (context_id / 8))) { - for (result = profile->contexts[context_id % MG_CONTEXT_MODULO]; result; result = result->next) { - if (result->context_id == context_id) { - break; - } - } - } - - switch_thread_rwlock_unlock(profile->contexts_rwlock); - - return result; + return megaco_get_context(profile, context_id); } mg_context_t *megaco_get_context(megaco_profile_t *profile, uint32_t context_id) @@ -213,8 +361,9 @@ switch_status_t megaco_profile_start(const char *profilename) switch_thread_rwlock_create(&profile->rwlock, pool); switch_thread_rwlock_create(&profile->contexts_rwlock, pool); + switch_thread_rwlock_create(&profile->terminations_rwlock, pool); -// switch_core_hash_init(&profile->contexts_hash, pool); + switch_core_hash_init(&profile->terminations, pool); if (SWITCH_STATUS_SUCCESS != config_profile(profile, SWITCH_FALSE)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error configuring profile %s\n", profile->name); @@ -235,7 +384,7 @@ switch_status_t megaco_profile_start(const char *profilename) fail: switch_core_destroy_memory_pool(&pool); return SWITCH_STATUS_FALSE; -} +} switch_status_t megaco_profile_destroy(megaco_profile_t **profile) diff --git a/src/mod/endpoints/mod_media_gateway/mod_media_gateway.h b/src/mod/endpoints/mod_media_gateway/mod_media_gateway.h index f1da14a4f2..1fe81c8825 100644 --- a/src/mod/endpoints/mod_media_gateway/mod_media_gateway.h +++ b/src/mod/endpoints/mod_media_gateway/mod_media_gateway.h @@ -64,25 +64,46 @@ typedef enum { typedef struct megaco_profile_s megaco_profile_t; typedef struct mg_context_s mg_context_t; +/* RTP parameters understood by the controllable channel */ +#define kLOCALADDR "local_addr" +#define kLOCALPORT "local_port" +#define kREMOTEADDR "remote_addr" +#define kREMOTEPORT "remote_port" +#define kCODEC "codec" +#define kPTIME "ptime" +#define kPT "pt" +#define kRFC2833PT "rfc2833_pt" +#define kMODE "mode" +#define kRATE "rate" + +/* TDM parameters understood by the controllable channel */ +#define kSPAN_ID "span" +#define kCHAN_ID "chan" + typedef struct mg_termination_s { mg_termination_type_t type; - const char *uuid; - mg_context_t *context; + const char *name; /*!< Megaco Name */ + const char *uuid; /*!< UUID of the associated FS channel, or NULL if it's not activated */ + mg_context_t *context; /*!< Context in which this termination is connected, or NULL */ + megaco_profile_t *profile; /*!< Parent MG profile */ union { struct { - const char *codec; - int ptime; - const char *remote_address; - switch_port_t remote_port; + /* The RTP termination will automatically operate as "sendonly" or "recvonly" as soon as + * one of the network addresses are NULL */ + const char *local_addr; switch_port_t local_port; - CmSdpInfoSet *local_sdp; - CmSdpInfoSet *remote_sdp; - - unsigned mode:2; - unsigned :0; + const char *remote_addr; + switch_port_t remote_port; + + int ptime; + int pt; + int rfc2833_pt; + int rate; + const char *codec; } rtp; + struct { int span; int channel; @@ -93,7 +114,7 @@ typedef struct mg_termination_s { struct mg_context_s { uint32_t context_id; - mg_termination_t terminations[MG_CONTEXT_MAX_TERMS]; + mg_termination_t *terminations[MG_CONTEXT_MAX_TERMS]; megaco_profile_t *profile; mg_context_t *next; switch_memory_pool_t *pool; @@ -102,6 +123,7 @@ struct mg_context_s { #define MG_CONTEXT_MODULO 16 #define MG_MAX_CONTEXTS 32768 + struct megaco_profile_s { char *name; switch_memory_pool_t *pool; @@ -125,6 +147,9 @@ struct megaco_profile_s { uint32_t next_context_id; uint8_t contexts_bitmap[MG_MAX_CONTEXTS/8]; /* Availability matrix, enough bits for a 32768 bitmap */ mg_context_t *contexts[MG_CONTEXT_MODULO]; + + switch_hash_t *terminations; + switch_thread_rwlock_t *terminations_rwlock; }; diff --git a/src/mod/endpoints/mod_sofia/rtp.c b/src/mod/endpoints/mod_sofia/rtp.c index 679a9a0e22..a464beb1be 100644 --- a/src/mod/endpoints/mod_sofia/rtp.c +++ b/src/mod/endpoints/mod_sofia/rtp.c @@ -75,7 +75,11 @@ typedef struct { switch_port_t remote_port; switch_payload_t agreed_pt; /*XXX*/ sofia_dtmf_t dtmf_type; - + enum { + RTP_SENDONLY, + RTP_RECVONLY, + RTP_SENDRECV + } mode; } crtp_private_t; static switch_status_t channel_on_init(switch_core_session_t *session); @@ -128,8 +132,10 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi switch_channel_t *channel; char name[128]; crtp_private_t *tech_pvt = NULL; + switch_caller_profile_t *caller_profile; const char *err; + const char *local_addr = switch_event_get_header_nil(var_event, kLOCALADDR), *szlocal_port = switch_event_get_header_nil(var_event, kLOCALPORT), @@ -150,8 +156,16 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi //rfc2833_pt = !zstr(szrfc2833_pt) ? atoi(szrfc2833_pt) : 0, rate = !zstr(szrate) ? atoi(szrate) : 8000, pt = !zstr(szpt) ? atoi(szpt) : 0; - - + + if ( + ((zstr(remote_addr) || remote_port == 0) && (zstr(local_addr) || local_port == 0)) || + zstr(codec) || + zstr(szpt)) { + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing required arguments\n"); + goto fail; + } + if (!(*new_session = switch_core_session_request(crtp.endpoint_interface, SWITCH_CALL_DIRECTION_OUTBOUND, 0, pool))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't request session.\n"); @@ -171,10 +185,21 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi tech_pvt->agreed_pt = pt; tech_pvt->dtmf_type = DTMF_2833; /* XXX */ + if (zstr(local_addr) || local_port == 0) { + tech_pvt->mode = RTP_SENDONLY; + } else if (zstr(remote_addr) || remote_port == 0) { + tech_pvt->mode = RTP_SENDRECV; + } else { + + } + switch_core_session_set_private(*new_session, tech_pvt); + caller_profile = switch_caller_profile_clone(*new_session, outbound_profile); + switch_channel_set_caller_profile(channel, caller_profile); - snprintf(name, sizeof(name), "rtp/ctrl"); + + snprintf(name, sizeof(name), "rtp/%s", outbound_profile->destination_number); switch_channel_set_name(channel, name); switch_channel_set_state(channel, CS_INIT); @@ -251,7 +276,7 @@ static switch_status_t channel_on_init(switch_core_session_t *session) switch_channel_t *channel = switch_core_session_get_channel(session); - switch_channel_set_state(channel, CS_ROUTING); + switch_channel_set_state(channel, CS_CONSUME_MEDIA); return SWITCH_STATUS_SUCCESS; } @@ -290,7 +315,7 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch if (!tech_pvt->rtp_session) { goto cng; } - + if (switch_rtp_has_dtmf(tech_pvt->rtp_session)) { switch_dtmf_t dtmf = { 0 }; switch_rtp_dequeue_dtmf(tech_pvt->rtp_session, &dtmf); @@ -319,7 +344,7 @@ static switch_status_t channel_write_frame(switch_core_session_t *session, switc { crtp_private_t *tech_pvt; switch_channel_t *channel; - int frames = 0, bytes = 0, samples = 0; + //int frames = 0, bytes = 0, samples = 0; channel = switch_core_session_get_channel(session); assert(channel != NULL); @@ -328,6 +353,7 @@ static switch_status_t channel_write_frame(switch_core_session_t *session, switc assert(tech_pvt != NULL); +#if 0 if (!switch_test_flag(frame, SFF_CNG) && !switch_test_flag(frame, SFF_PROXY_PACKET)) { if (tech_pvt->read_codec.implementation->encoded_bytes_per_packet) { bytes = tech_pvt->read_codec.implementation->encoded_bytes_per_packet; @@ -339,6 +365,8 @@ static switch_status_t channel_write_frame(switch_core_session_t *session, switc } tech_pvt->timestamp_send += samples; +#endif + switch_rtp_write_frame(tech_pvt->rtp_session, frame); return SWITCH_STATUS_SUCCESS; @@ -369,6 +397,120 @@ static switch_status_t channel_send_dtmf(switch_core_session_t *session, const s static switch_status_t channel_receive_message(switch_core_session_t *session, switch_core_session_message_t *msg) { + crtp_private_t *tech_pvt = NULL; + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + switch (msg->message_id) { + case SWITCH_MESSAGE_INDICATE_DEBUG_AUDIO: + { + if (switch_rtp_ready(tech_pvt->rtp_session) && !zstr(msg->string_array_arg[0]) && !zstr(msg->string_array_arg[1])) { + int32_t flags = 0; + if (!strcasecmp(msg->string_array_arg[0], "read")) { + flags |= SWITCH_RTP_FLAG_DEBUG_RTP_READ; + } else if (!strcasecmp(msg->string_array_arg[0], "write")) { + flags |= SWITCH_RTP_FLAG_DEBUG_RTP_WRITE; + } else if (!strcasecmp(msg->string_array_arg[0], "both")) { + flags |= SWITCH_RTP_FLAG_DEBUG_RTP_READ | SWITCH_RTP_FLAG_DEBUG_RTP_WRITE; + } + + if (flags) { + if (switch_true(msg->string_array_arg[1])) { + switch_rtp_set_flag(tech_pvt->rtp_session, flags); + } else { + switch_rtp_clear_flag(tech_pvt->rtp_session, flags); + } + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Invalid Options\n"); + } + } + break; + } + case SWITCH_MESSAGE_INDICATE_AUDIO_SYNC: + if (switch_rtp_ready(tech_pvt->rtp_session)) { + rtp_flush_read_buffer(tech_pvt->rtp_session, SWITCH_RTP_FLUSH_ONCE); + } + break; + case SWITCH_MESSAGE_INDICATE_JITTER_BUFFER: + { + if (switch_rtp_ready(tech_pvt->rtp_session)) { + int len = 0, maxlen = 0, qlen = 0, maxqlen = 50, max_drift = 0; + + if (msg->string_arg) { + char *p, *q; + const char *s; + + if (!strcasecmp(msg->string_arg, "pause")) { + switch_rtp_pause_jitter_buffer(tech_pvt->rtp_session, SWITCH_TRUE); + goto end; + } else if (!strcasecmp(msg->string_arg, "resume")) { + switch_rtp_pause_jitter_buffer(tech_pvt->rtp_session, SWITCH_FALSE); + goto end; + } else if (!strncasecmp(msg->string_arg, "debug:", 6)) { + s = msg->string_arg + 6; + if (s && !strcmp(s, "off")) { + s = NULL; + } + switch_rtp_debug_jitter_buffer(tech_pvt->rtp_session, s); + goto end; + } + + + if ((len = atoi(msg->string_arg))) { + qlen = len / (tech_pvt->read_codec.implementation->microseconds_per_packet / 1000); + if (qlen < 1) { + qlen = 3; + } + } + + if (qlen) { + if ((p = strchr(msg->string_arg, ':'))) { + p++; + maxlen = atol(p); + if ((q = strchr(p, ':'))) { + q++; + max_drift = abs(atol(q)); + } + } + } + + + if (maxlen) { + maxqlen = maxlen / (tech_pvt->read_codec.implementation->microseconds_per_packet / 1000); + } + } + + if (qlen) { + if (maxqlen < qlen) { + maxqlen = qlen * 5; + } + if (switch_rtp_activate_jitter_buffer(tech_pvt->rtp_session, qlen, maxqlen, + tech_pvt->read_codec.implementation->samples_per_packet, + tech_pvt->read_codec.implementation->samples_per_second, max_drift) == SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), + SWITCH_LOG_DEBUG, "Setting Jitterbuffer to %dms (%d frames) (%d max frames) (%d max drift)\n", + len, qlen, maxqlen, max_drift); + switch_channel_set_flag(tech_pvt->channel, CF_JITTERBUFFER); + if (!switch_false(switch_channel_get_variable(tech_pvt->channel, "sip_jitter_buffer_plc"))) { + switch_channel_set_flag(tech_pvt->channel, CF_JITTERBUFFER_PLC); + } + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), + SWITCH_LOG_WARNING, "Error Setting Jitterbuffer to %dms (%d frames)\n", len, qlen); + } + + } else { + switch_rtp_deactivate_jitter_buffer(tech_pvt->rtp_session); + } + } + } + break; + + default: + break; + } +end: return SWITCH_STATUS_SUCCESS; }