Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TEL-6252: Support for merging inbound and outbound codec #351

Open
wants to merge 1 commit into
base: telnyx/telephony/development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/include/switch_core_media.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ typedef enum {
SCMF_REJECT_IPV6,
SCMF_SRTP_HANGUP_ON_ERROR,
SCMF_SRTP_SKIP_EMPTY_MKI,
SCMF_MERGE_INBOUND_OUTBOUND_CODEC,
SCMF_MAX
} switch_core_media_flag_t;

Expand Down
7 changes: 7 additions & 0 deletions src/mod/endpoints/mod_sofia/sofia.c
Original file line number Diff line number Diff line change
Expand Up @@ -4912,6 +4912,7 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name)
profile->auto_restart = 1;
sofia_set_media_flag(profile, SCMF_AUTOFIX_TIMING);
sofia_set_media_flag(profile, SCMF_RTP_AUTOFLUSH_DURING_BRIDGE);
sofia_set_media_flag(profile, SCMF_MERGE_INBOUND_OUTBOUND_CODEC);
profile->contact_user = SOFIA_DEFAULT_CONTACT_USER;
sofia_set_pflag(profile, PFLAG_PASS_CALLEE_ID);
sofia_set_pflag(profile, PFLAG_ALLOW_UPDATE);
Expand Down Expand Up @@ -5853,6 +5854,12 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name)
} else {
sofia_clear_media_flag(profile, SCMF_SRTP_SKIP_EMPTY_MKI);
}
} else if (!strcasecmp(var, "sdp-merge-inbound-outbound-codec")) {
if (switch_true(val)) {
sofia_set_media_flag(profile, SCMF_MERGE_INBOUND_OUTBOUND_CODEC);
} else {
sofia_clear_media_flag(profile, SCMF_MERGE_INBOUND_OUTBOUND_CODEC);
}
} else if (!strcasecmp(var, "auth-calls")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_AUTH_CALLS);
Expand Down
112 changes: 109 additions & 3 deletions src/switch_core_media.c
Original file line number Diff line number Diff line change
Expand Up @@ -2676,6 +2676,86 @@ SWITCH_DECLARE(switch_core_media_params_t *) switch_core_media_get_mparams(switc
return smh->mparams;
}

static switch_bool_t is_codec_match(const char *lcodec, const char *rcodec)
{
char *lname, *rname, *lmod, *rmod, *lfmtp, *rfmtp, lbuf[256], rbuf[256];
uint32_t linterval = 0, lrate = 0, lbit = 0, lchannels = 0;
uint32_t rinterval = 0, rrate = 0, rbit = 0, rchannels = 0;

if (zstr(lcodec) || zstr(rcodec)) {
return SWITCH_FALSE;
}

switch_copy_string(lbuf, lcodec, sizeof(lbuf));
lname = switch_parse_codec_buf(lbuf, &linterval, &lrate, &lbit, &lchannels, &lmod, &lfmtp);

switch_copy_string(rbuf, rcodec, sizeof(rbuf));
rname = switch_parse_codec_buf(rbuf, &rinterval, &rrate, &rbit, &rchannels, &rmod, &rfmtp);

if (zstr(lname) || zstr(rname)) {
// Parsing failure
return SWITCH_FALSE;
}

if (!strcasecmp(lname, rname)) {
// If the codec don't specify or indicate interval, rate or bit
// then it will be treated as a wildcard comparison. Will only compare the value
// when both of the compared codec explicitly indicate their preferred value
if (((linterval != 0 && rinterval != 0) && linterval != rinterval)
|| ((lrate != 0 && rrate != 0) && lrate != rrate)
|| ((lbit != 0 && rbit != 0) && lbit != rbit)) {
return SWITCH_FALSE;
}
return SWITCH_TRUE;
}

return SWITCH_FALSE;
}

static void merge_codec_string(switch_core_session_t *session, const char *preferred_codecs, const char *codecs, char **codec_lists)
{
char *tmp_prefer_codecs[SWITCH_MAX_CODECS];
char *tmp_codecs[SWITCH_MAX_CODECS];
int pref_codecs_count = 0, codecs_count = 0, i = 0, j = 0, k = 0, l = 0;
switch_stream_handle_t stream = { 0 };

if (zstr(codecs) || zstr(preferred_codecs) || !codec_lists) {
return;
}

if (!strcasecmp(codecs, preferred_codecs)) {
return;
}

codecs_count = switch_separate_string(switch_core_session_strdup(session, codecs), ',', tmp_codecs, SWITCH_MAX_CODECS);
pref_codecs_count = switch_separate_string(switch_core_session_strdup(session, preferred_codecs), ',', tmp_prefer_codecs, SWITCH_MAX_CODECS);

for (i = 0; i < pref_codecs_count; ++i) {
for (j = k; j < codecs_count; ++j) {
if (is_codec_match(tmp_codecs[j], tmp_prefer_codecs[i])) {
int last_shift = k++;
if (last_shift != j) {
// Shift the found codec to the top list
for (l = j; l > last_shift; --l) {
char *tmp = tmp_codecs[l];
tmp_codecs[l] = tmp_codecs[l-1];
tmp_codecs[l-1] = tmp;
}
}
}
}
}

if (k > 0) {
SWITCH_STANDARD_STREAM(stream);
for (j = 0; j < codecs_count; ++j) {
stream.write_function(&stream, "%s%s", (j > 0 ? "," : ""), tmp_codecs[j]);
}

*codec_lists = stream.data;
}
}

SWITCH_DECLARE(int) switch_core_media_retrieve_codecs(switch_core_session_t *session, const switch_codec_implementation_t **array)
{
const char *abs, *codec_string = NULL;
Expand Down Expand Up @@ -2729,7 +2809,20 @@ SWITCH_DECLARE(int) switch_core_media_retrieve_codecs(switch_core_session_t *ses
codec_string = "PCMU@20i,PCMA@20i,speex@20i";
}

tmp_codec_string = switch_core_session_strdup(smh->session, codec_string);
val = switch_channel_get_variable_dup(session->channel, "merge_inbound_outbound_codecs", SWITCH_FALSE, -1);
if (ocodec && (!(zstr(val) && switch_true(val)) || (zstr(val) && smh->media_flags[SCMF_MERGE_INBOUND_OUTBOUND_CODEC]))) {
char *merge_codec = NULL;
merge_codec_string(session, ocodec, codec_string, &merge_codec);
if (merge_codec) {
tmp_codec_string = switch_core_session_strdup(smh->session, merge_codec);
switch_safe_free(merge_codec);
} else {
tmp_codec_string = switch_core_session_strdup(smh->session, codec_string);
}
} else {
tmp_codec_string = switch_core_session_strdup(smh->session, codec_string);
}

codec_order_last = switch_separate_string(tmp_codec_string, ',', codec_order, SWITCH_MAX_CODECS);
return switch_loadable_module_get_codecs_sorted(array, fmtp, SWITCH_MAX_CODECS, codec_order, codec_order_last);
}
Expand Down Expand Up @@ -2796,8 +2889,21 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_prepare_codecs(switch_core_ses
codec_string = "PCMU@20i,PCMA@20i,speex@20i";
}

tmp_codec_string = switch_core_session_strdup(smh->session, codec_string);
switch_channel_set_variable(session->channel, "rtp_use_codec_string", codec_string);
val = switch_channel_get_variable_dup(session->channel, "merge_inbound_outbound_codecs", SWITCH_FALSE, -1);
if (ocodec && ((!zstr(val) && switch_true(val)) || (zstr(val) && smh->media_flags[SCMF_MERGE_INBOUND_OUTBOUND_CODEC]))) {
char *merge_codec = NULL;
merge_codec_string(session, ocodec, codec_string, &merge_codec);
if (merge_codec) {
tmp_codec_string = switch_core_session_strdup(smh->session, merge_codec);
switch_safe_free(merge_codec);
} else {
tmp_codec_string = switch_core_session_strdup(smh->session, codec_string);
}
} else {
tmp_codec_string = switch_core_session_strdup(smh->session, codec_string);
}
switch_channel_set_variable(session->channel, "rtp_use_codec_string", tmp_codec_string);

smh->codec_order_last = switch_separate_string(tmp_codec_string, ',', smh->codec_order, SWITCH_MAX_CODECS);
smh->mparams->num_codecs = switch_loadable_module_get_codecs_sorted(smh->codecs, smh->fmtp, SWITCH_MAX_CODECS, smh->codec_order, smh->codec_order_last);
return SWITCH_STATUS_SUCCESS;
Expand Down