forked from Mirrors/freeswitch
add/sub done but untested, mod remains. this commit also adds a message handler inside the rtp endpoint so it can properly flush streams, setup jitter buffering, and honor uuid_debug_audio requests
This commit is contained in:
parent
c6a75aa4ee
commit
909d464abd
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user