summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--channels/chan_iax2.c268
-rw-r--r--channels/iax2/codec_pref.c369
-rw-r--r--channels/iax2/format_compatibility.c73
-rw-r--r--channels/iax2/include/codec_pref.h57
-rw-r--r--channels/iax2/include/format_compatibility.h14
-rw-r--r--include/asterisk/format_compatibility.h2
-rw-r--r--main/format_compatibility.c2
7 files changed, 532 insertions, 253 deletions
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index 831f2c266..630a66342 100644
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -285,7 +285,7 @@ static int nochecksums = 0;
/* Sample over last 100 units to determine historic jitter */
#define GAMMA (0.01)
-static struct iax2_codec_pref prefs;
+static struct iax2_codec_pref prefs_global;
static const char tdesc[] = "Inter Asterisk eXchange Driver (Ver 2)";
@@ -353,22 +353,22 @@ static int (*iax2_regfunk)(const char *username, int onoff) = NULL;
#define IAX_CAPABILITY_FULLBANDWIDTH 0xFFFF
/* T1, maybe ISDN */
#define IAX_CAPABILITY_MEDBANDWIDTH (IAX_CAPABILITY_FULLBANDWIDTH & \
- ~ast_format_compatibility_format2bitfield(ast_format_slin) & \
- ~ast_format_compatibility_format2bitfield(ast_format_slin16) & \
- ~ast_format_compatibility_format2bitfield(ast_format_siren7) & \
- ~ast_format_compatibility_format2bitfield(ast_format_siren14) & \
- ~ast_format_compatibility_format2bitfield(ast_format_g719) & \
- ~ast_format_compatibility_format2bitfield(ast_format_ulaw) & \
- ~ast_format_compatibility_format2bitfield(ast_format_alaw) & \
- ~ast_format_compatibility_format2bitfield(ast_format_g722))
+ ~AST_FORMAT_SLIN & \
+ ~AST_FORMAT_SLIN16 & \
+ ~AST_FORMAT_SIREN7 & \
+ ~AST_FORMAT_SIREN14 & \
+ ~AST_FORMAT_G719 & \
+ ~AST_FORMAT_ULAW & \
+ ~AST_FORMAT_ALAW & \
+ ~AST_FORMAT_G722)
/* A modem */
#define IAX_CAPABILITY_LOWBANDWIDTH (IAX_CAPABILITY_MEDBANDWIDTH & \
- ~ast_format_compatibility_format2bitfield(ast_format_g726) & \
- ~ast_format_compatibility_format2bitfield(ast_format_g726_aal2) & \
- ~ast_format_compatibility_format2bitfield(ast_format_adpcm))
+ ~AST_FORMAT_G726 & \
+ ~AST_FORMAT_G726_AAL2 & \
+ ~AST_FORMAT_ADPCM)
#define IAX_CAPABILITY_LOWFREE (IAX_CAPABILITY_LOWBANDWIDTH & \
- ~ast_format_compatibility_format2bitfield(ast_format_g723))
+ ~AST_FORMAT_G723)
#define DEFAULT_MAXMS 2000 /* Must be faster than 2 seconds by default */
@@ -1820,16 +1820,20 @@ static struct ast_format *codec_choose_from_prefs(struct iax2_codec_pref *pref,
int x;
struct ast_format *found_format = NULL;
- for (x = 0; x < IAX2_CODEC_PREF_SIZE; x++) {
+ for (x = 0; x < ARRAY_LEN(pref->order); ++x) {
struct ast_format *pref_format;
- uint64_t pref_as_bitfield = iax2_codec_pref_order_value_to_format_bitfield(pref->order[x]);
+ uint64_t pref_bitfield;
- if (!pref_as_bitfield) {
+ pref_bitfield = iax2_codec_pref_order_value_to_format_bitfield(pref->order[x]);
+ if (!pref_bitfield) {
break;
}
- pref_format = ast_format_compatibility_bitfield2format(pref_as_bitfield);
-
+ pref_format = ast_format_compatibility_bitfield2format(pref_bitfield);
+ if (!pref_format) {
+ /* The bitfield is not associated with any format. */
+ continue;
+ }
found_format = ast_format_cap_get_compatible_format(cap, pref_format);
if (found_format) {
break;
@@ -1867,61 +1871,6 @@ static iax2_format iax2_codec_choose(struct iax2_codec_pref *pref, iax2_format f
return format;
}
-static iax2_format iax2_best_codec(iax2_format formats)
-{
- /* This just our opinion, expressed in code. We are asked to choose
- the best codec to use, given no information */
- static const iax2_format prefs[] =
- {
- /*! Okay, ulaw is used by all telephony equipment, so start with it */
- AST_FORMAT_ULAW,
- /*! Unless of course, you're a silly European, so then prefer ALAW */
- AST_FORMAT_ALAW,
- AST_FORMAT_G719,
- AST_FORMAT_SIREN14,
- AST_FORMAT_SIREN7,
- AST_FORMAT_TESTLAW,
- /*! G.722 is better then all below, but not as common as the above... so give ulaw and alaw priority */
- AST_FORMAT_G722,
- /*! Okay, well, signed linear is easy to translate into other stuff */
- AST_FORMAT_SLIN16,
- AST_FORMAT_SLIN,
- /*! G.726 is standard ADPCM, in RFC3551 packing order */
- AST_FORMAT_G726,
- /*! G.726 is standard ADPCM, in AAL2 packing order */
- AST_FORMAT_G726_AAL2,
- /*! ADPCM has great sound quality and is still pretty easy to translate */
- AST_FORMAT_ADPCM,
- /*! Okay, we're down to vocoders now, so pick GSM because it's small and easier to
- translate and sounds pretty good */
- AST_FORMAT_GSM,
- /*! iLBC is not too bad */
- AST_FORMAT_ILBC,
- /*! Speex is free, but computationally more expensive than GSM */
- AST_FORMAT_SPEEX16,
- AST_FORMAT_SPEEX,
- /*! Opus */
- AST_FORMAT_OPUS,
- /*! Ick, LPC10 sounds terrible, but at least we have code for it, if you're tacky enough
- to use it */
- AST_FORMAT_LPC10,
- /*! G.729a is faster than 723 and slightly less expensive */
- AST_FORMAT_G729,
- /*! Down to G.723.1 which is proprietary but at least designed for voice */
- AST_FORMAT_G723,
- };
- int x;
-
- /* Find the first preferred codec in the format given */
- for (x = 0; x < ARRAY_LEN(prefs); x++) {
- if (formats & prefs[x]) {
- return prefs[x];
- }
- }
-
- return 0;
-}
-
const char *iax2_getformatname(iax2_format format)
{
struct ast_format *tmpfmt;
@@ -1951,33 +1900,24 @@ static const char *iax2_getformatname_multiple(iax2_format format, struct ast_st
static int iax2_parse_allow_disallow(struct iax2_codec_pref *pref, iax2_format *formats, const char *list, int allowing)
{
int res, i;
- struct ast_format_cap *cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
-
- if (!cap) {
- return 1;
- }
+ struct ast_format_cap *cap;
/* We want to add the formats to the cap in the preferred order */
- for (i = 0; i < IAX2_CODEC_PREF_SIZE; i++) {
- uint64_t pref_as_bitfield = iax2_codec_pref_order_value_to_format_bitfield(pref->order[i]);
-
- if (!pref_as_bitfield) {
- break;
- }
-
- if (iax2_format_compatibility_bitfield2cap(pref_as_bitfield, cap)) {
- ao2_ref(cap, -1);
- return 1;
- }
+ cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+ if (!cap || iax2_codec_pref_to_cap(pref, cap)) {
+ ao2_cleanup(cap);
+ return 1;
}
res = ast_format_cap_update_by_allow_disallow(cap, list, allowing);
- *formats = iax2_format_compatibility_cap2bitfield(cap);
+ /* Adjust formats bitfield and pref list to match. */
+ *formats = iax2_format_compatibility_cap2bitfield(cap);
iax2_codec_pref_remove_missing(pref, *formats);
for (i = 0; i < ast_format_cap_count(cap); i++) {
struct ast_format *fmt = ast_format_cap_get_format(cap, i);
+
iax2_codec_pref_append(pref, fmt, ast_format_cap_get_format_framing(cap, fmt));
ao2_ref(fmt, -1);
}
@@ -2283,7 +2223,7 @@ static struct chan_iax2_pvt *new_iax(struct ast_sockaddr *addr, const char *host
return NULL;
}
- tmp->prefs = prefs;
+ tmp->prefs = prefs_global;
tmp->pingid = -1;
tmp->lagid = -1;
tmp->autoid = -1;
@@ -3844,7 +3784,7 @@ static char *handle_cli_iax2_show_peer(struct ast_cli_entry *e, int cmd, struct
char status[30];
char cbuf[256];
struct iax2_peer *peer;
- struct ast_str *codec_buf = ast_str_alloca(64);
+ struct ast_str *codec_buf = ast_str_alloca(256);
struct ast_str *encmethods = ast_str_alloca(256);
int load_realtime = 0;
@@ -3895,17 +3835,15 @@ static char *handle_cli_iax2_show_peer(struct ast_cli_entry *e, int cmd, struct
ast_cli(a->fd, " Addr->IP : %s Port %s\n", str_addr ? str_addr : "(Unspecified)", str_port);
ast_cli(a->fd, " Defaddr->IP : %s Port %s\n", str_defaddr, str_defport);
ast_cli(a->fd, " Username : %s\n", peer->username);
- ast_cli(a->fd, " Codecs : ");
- ast_cli(a->fd, "%s\n", iax2_getformatname_multiple(peer->capability, &codec_buf));
+ ast_cli(a->fd, " Codecs : %s\n", iax2_getformatname_multiple(peer->capability, &codec_buf));
- ast_cli(a->fd, " Codec Order : ");
- if (iax2_codec_pref_string(&peer->prefs, cbuf, sizeof(cbuf)) != -1) {
- ast_cli(a->fd, "%s\n", cbuf);
+ if (iax2_codec_pref_string(&peer->prefs, cbuf, sizeof(cbuf)) < 0) {
+ strcpy(cbuf, "Error"); /* Safe */
}
+ ast_cli(a->fd, " Codec Order : %s\n", cbuf);
- ast_cli(a->fd, " Status : ");
peer_status(peer, status, sizeof(status));
- ast_cli(a->fd, "%s\n",status);
+ ast_cli(a->fd, " Status : %s\n", status);
ast_cli(a->fd, " Qualify : every %dms when OK, every %dms when UNREACHABLE (sample smoothing %s)\n", peer->pokefreqok, peer->pokefreqnotok, peer->smoothing ? "On" : "Off");
ast_cli(a->fd, "\n");
peer_unref(peer);
@@ -4654,6 +4592,7 @@ static void realtime_update_peer(const char *peername, struct ast_sockaddr *sock
struct create_addr_info {
iax2_format capability;
uint64_t flags;
+ struct iax2_codec_pref prefs;
int maxtime;
int encmethods;
int found;
@@ -4663,7 +4602,6 @@ struct create_addr_info {
char secret[80];
char outkey[80];
char timezone[80];
- char prefs[32];
char cid_num[80];
char cid_name[80];
char context[AST_MAX_CONTEXT];
@@ -4676,7 +4614,6 @@ static int create_addr(const char *peername, struct ast_channel *c, struct ast_s
{
struct iax2_peer *peer;
int res = -1;
- struct iax2_codec_pref ourprefs;
ast_clear_flag64(cai, IAX_SENDANI | IAX_TRUNK);
cai->sockfd = defaultsockfd;
@@ -4697,20 +4634,24 @@ static int create_addr(const char *peername, struct ast_channel *c, struct ast_s
}
ast_sockaddr_copy(addr, &peer_addr);
- /* use global iax prefs for unknown peer/user */
- /* But move the calling channel's native codec to the top of the preference list */
- memcpy(&ourprefs, &prefs, sizeof(ourprefs));
+ /*
+ * Use The global iax prefs for unknown peer/user.
+ * However, move the calling channel's native codec to
+ * the top of the preference list.
+ */
+ cai->prefs = prefs_global;
if (c) {
int i;
for (i = 0; i < ast_format_cap_count(ast_channel_nativeformats(c)); i++) {
struct ast_format *format = ast_format_cap_get_format(
ast_channel_nativeformats(c), i);
- iax2_codec_pref_prepend(&ourprefs, format, ast_format_cap_get_format_framing(ast_channel_nativeformats(c), format), 1);
+ iax2_codec_pref_prepend(&cai->prefs, format,
+ ast_format_cap_get_format_framing(ast_channel_nativeformats(c), format),
+ 1);
ao2_ref(format, -1);
}
}
- iax2_codec_pref_convert(&ourprefs, cai->prefs, sizeof(cai->prefs), 1);
return 0;
}
@@ -4731,7 +4672,7 @@ static int create_addr(const char *peername, struct ast_channel *c, struct ast_s
cai->encmethods = peer->encmethods;
cai->sockfd = peer->sockfd;
cai->adsi = peer->adsi;
- memcpy(&ourprefs, &peer->prefs, sizeof(ourprefs));
+ cai->prefs = peer->prefs;
/* Move the calling channel's native codec to the top of the preference list */
if (c) {
int i;
@@ -4739,11 +4680,12 @@ static int create_addr(const char *peername, struct ast_channel *c, struct ast_s
for (i = 0; i < ast_format_cap_count(ast_channel_nativeformats(c)); i++) {
struct ast_format *tmpfmt = ast_format_cap_get_format(
ast_channel_nativeformats(c), i);
- iax2_codec_pref_prepend(&ourprefs, tmpfmt, ast_format_cap_get_format_framing(ast_channel_nativeformats(c), tmpfmt), 1);
+ iax2_codec_pref_prepend(&cai->prefs, tmpfmt,
+ ast_format_cap_get_format_framing(ast_channel_nativeformats(c), tmpfmt),
+ 1);
ao2_ref(tmpfmt, -1);
}
}
- iax2_codec_pref_convert(&ourprefs, cai->prefs, sizeof(cai->prefs), 1);
ast_copy_string(cai->context, peer->context, sizeof(cai->context));
ast_copy_string(cai->peercontext, peer->peercontext, sizeof(cai->peercontext));
ast_copy_string(cai->username, peer->username, sizeof(cai->username));
@@ -5150,6 +5092,7 @@ static int iax2_call(struct ast_channel *c, const char *dest, int timeout)
unsigned char osp_block_index;
unsigned int osp_block_length;
unsigned char osp_buffer[256];
+ char encoded_prefs[32];
iax2_format iax2_tmpfmt;
if ((ast_channel_state(c) != AST_STATE_DOWN) && (ast_channel_state(c) != AST_STATE_RESERVED)) {
@@ -5218,7 +5161,8 @@ static int iax2_call(struct ast_channel *c, const char *dest, int timeout)
}
/* WARNING: this breaks down at 190 bits! */
- iax_ie_append_str(&ied, IAX_IE_CODEC_PREFS, cai.prefs);
+ iax2_codec_pref_convert(&cai.prefs, encoded_prefs, sizeof(encoded_prefs), 1);
+ iax_ie_append_str(&ied, IAX_IE_CODEC_PREFS, encoded_prefs);
if (l) {
iax_ie_append_str(&ied, IAX_IE_CALLING_NUMBER, l);
@@ -5877,7 +5821,9 @@ static int iax2_getpeertrunk(struct ast_sockaddr addr)
}
/*! \brief Create new call, interface with the PBX core */
-static struct ast_channel *ast_iax2_new(int callno, int state, iax2_format capability, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, unsigned int cachable)
+static struct ast_channel *ast_iax2_new(int callno, int state, iax2_format capability,
+ struct iax2_codec_pref *prefs, const struct ast_assigned_ids *assignedids,
+ const struct ast_channel *requestor, unsigned int cachable)
{
struct ast_channel *tmp = NULL;
struct chan_iax2_pvt *i;
@@ -5893,10 +5839,22 @@ static struct ast_channel *ast_iax2_new(int callno, int state, iax2_format capab
return NULL;
}
+ if (!capability) {
+ ast_log(LOG_WARNING, "No formats specified for call to: IAX2/%s-%d\n",
+ i->host, i->callno);
+ return NULL;
+ }
native = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
if (!native) {
return NULL;
}
+ if (iax2_codec_pref_best_bitfield2cap(capability, prefs, native)
+ || !ast_format_cap_count(native)) {
+ ast_log(LOG_WARNING, "No requested formats available for call to: IAX2/%s-%d\n",
+ i->host, i->callno);
+ ao2_ref(native, -1);
+ return NULL;
+ }
if (!ast_strlen_zero(i->peer)) {
peer_name = ast_strdupa(i->peer);
@@ -5910,13 +5868,17 @@ static struct ast_channel *ast_iax2_new(int callno, int state, iax2_format capab
if (!ast_strlen_zero(peer_name)) {
peer = find_peer(peer_name, 1);
if (peer && peer->endpoint) {
- tmp = ast_channel_alloc_with_endpoint(1, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, assignedids, requestor, i->amaflags, peer->endpoint, "IAX2/%s-%d", i->host, i->callno);
+ tmp = ast_channel_alloc_with_endpoint(1, state, i->cid_num, i->cid_name,
+ i->accountcode, i->exten, i->context, assignedids, requestor,
+ i->amaflags, peer->endpoint, "IAX2/%s-%d", i->host, i->callno);
}
ao2_cleanup(peer);
}
if (!tmp) {
- tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, assignedids, requestor, i->amaflags, "IAX2/%s-%d", i->host, i->callno);
+ tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, i->accountcode,
+ i->exten, i->context, assignedids, requestor, i->amaflags, "IAX2/%s-%d",
+ i->host, i->callno);
}
ast_mutex_lock(&iaxsl[callno]);
@@ -5945,9 +5907,8 @@ static struct ast_channel *ast_iax2_new(int callno, int state, iax2_format capab
ast_channel_tech_set(tmp, &iax2_tech);
/* We can support any format by default, until we get restricted */
- iax2_format_compatibility_bitfield2cap(capability, native);
ast_channel_nativeformats_set(tmp, native);
- tmpfmt = ast_format_cap_get_format(ast_channel_nativeformats(tmp), 0);
+ tmpfmt = ast_format_cap_get_format(native, 0);
ast_channel_set_readformat(tmp, tmpfmt);
ast_channel_set_rawreadformat(tmp, tmpfmt);
@@ -6046,8 +6007,10 @@ static struct ast_channel *ast_iax2_new(int callno, int state, iax2_format capab
if (state != AST_STATE_DOWN) {
if (ast_pbx_start(tmp)) {
ast_log(LOG_WARNING, "Unable to start PBX on %s\n", ast_channel_name(tmp));
+ /* unlock and relock iaxsl[callno] to preserve locking order */
+ ast_mutex_unlock(&iaxsl[callno]);
ast_hangup(tmp);
- i->owner = NULL;
+ ast_mutex_lock(&iaxsl[callno]);
return NULL;
}
}
@@ -7907,8 +7870,10 @@ static int check_access(int callno, struct ast_sockaddr *addr, struct iax_ies *i
/* Use provided preferences until told otherwise for actual preferences */
if (ies->codec_prefs) {
iax2_codec_pref_convert(&iaxs[callno]->rprefs, ies->codec_prefs, 32, 0);
- iax2_codec_pref_convert(&iaxs[callno]->prefs, ies->codec_prefs, 32, 0);
+ } else {
+ memset(&iaxs[callno]->rprefs, 0, sizeof(iaxs[callno]->rprefs));
}
+ iaxs[callno]->prefs = iaxs[callno]->rprefs;
if (!gotcapability) {
iaxs[callno]->peercapability = iaxs[callno]->peerformat;
@@ -10556,8 +10521,9 @@ static int socket_process_helper(struct iax2_thread *thread)
(f.frametype == AST_FRAME_IAX)) {
if (ast_test_flag64(iaxs[fr->callno], IAX_DELAYPBXSTART)) {
ast_clear_flag64(iaxs[fr->callno], IAX_DELAYPBXSTART);
- if (!ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->chosenformat, NULL, NULL,
- ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_AUTHENTICATED))) {
+ if (!ast_iax2_new(fr->callno, AST_STATE_RING,
+ iaxs[fr->callno]->chosenformat, &iaxs[fr->callno]->rprefs, NULL, NULL,
+ ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_AUTHENTICATED))) {
ast_variables_destroy(ies.vars);
ast_mutex_unlock(&iaxsl[fr->callno]);
return 1;
@@ -10893,7 +10859,7 @@ static int socket_process_helper(struct iax2_thread *thread)
if(ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOPREFS)) {
using_prefs = ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOCAP) ? "reqonly" : "disabled";
memset(&pref, 0, sizeof(pref));
- format = iax2_best_codec(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability);
+ format = iax2_format_compatibility_best(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability);
strcpy(caller_pref_buf,"disabled");
strcpy(host_pref_buf,"disabled");
} else {
@@ -10909,7 +10875,7 @@ static int socket_process_helper(struct iax2_thread *thread)
}
format = iax2_codec_choose(&pref, iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability);
} else /* if no codec_prefs IE do it the old way */
- format = iax2_best_codec(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability);
+ format = iax2_format_compatibility_best(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability);
}
}
@@ -11111,7 +11077,9 @@ static int socket_process_helper(struct iax2_thread *thread)
struct ast_str *cap_buf = ast_str_alloca(64);
/* Switch us to use a compatible format */
- iax2_format_compatibility_bitfield2cap(iaxs[fr->callno]->peerformat, native);
+ iax2_codec_pref_best_bitfield2cap(
+ iaxs[fr->callno]->peerformat, &iaxs[fr->callno]->rprefs,
+ native);
ast_channel_nativeformats_set(iaxs[fr->callno]->owner, native);
ast_verb(3, "Format for call is %s\n", ast_format_cap_get_names(ast_channel_nativeformats(iaxs[fr->callno]->owner), &cap_buf));
@@ -11351,8 +11319,9 @@ static int socket_process_helper(struct iax2_thread *thread)
if(ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOPREFS)) {
using_prefs = ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOCAP) ? "reqonly" : "disabled";
memset(&pref, 0, sizeof(pref));
- format = ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOCAP) ?
- iaxs[fr->callno]->peerformat : iax2_best_codec(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability);
+ format = ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOCAP)
+ ? iaxs[fr->callno]->peerformat
+ : iax2_format_compatibility_best(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability);
strcpy(caller_pref_buf,"disabled");
strcpy(host_pref_buf,"disabled");
} else {
@@ -11368,7 +11337,7 @@ static int socket_process_helper(struct iax2_thread *thread)
}
format = iax2_codec_choose(&pref, iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability);
} else /* if no codec_prefs IE do it the old way */
- format = iax2_best_codec(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability);
+ format = iax2_format_compatibility_best(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability);
}
}
if (!format) {
@@ -11429,9 +11398,11 @@ static int socket_process_helper(struct iax2_thread *thread)
using_prefs);
ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
- if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, format, NULL, NULL, 1)))
+ c = ast_iax2_new(fr->callno, AST_STATE_RING, format,
+ &iaxs[fr->callno]->rprefs, NULL, NULL, 1);
+ if (!c) {
iax2_destroy(fr->callno);
- else if (ies.vars) {
+ } else if (ies.vars) {
struct ast_datastore *variablestore;
struct ast_variable *var, *prev = NULL;
AST_LIST_HEAD(, ast_var_t) *varlist;
@@ -11503,9 +11474,12 @@ immediatedial:
iax2_getformatname_multiple(iaxs[fr->callno]->peerformat, &cap_buf));
ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
send_command(iaxs[fr->callno], AST_FRAME_CONTROL, AST_CONTROL_PROGRESS, 0, NULL, 0, -1);
- if (!(c = ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->peerformat, NULL, NULL, 1)))
+ c = ast_iax2_new(fr->callno, AST_STATE_RING,
+ iaxs[fr->callno]->peerformat, &iaxs[fr->callno]->rprefs,
+ NULL, NULL, 1);
+ if (!c) {
iax2_destroy(fr->callno);
- else if (ies.vars) {
+ } else if (ies.vars) {
struct ast_datastore *variablestore;
struct ast_variable *var, *prev = NULL;
AST_LIST_HEAD(, ast_var_t) *varlist;
@@ -12528,7 +12502,8 @@ static struct ast_channel *iax2_request(const char *type, struct ast_format_cap
ast_string_field_set(iaxs[callno], host, pds.peer);
}
- c = ast_iax2_new(callno, AST_STATE_DOWN, cai.capability, assignedids, requestor, cai.found);
+ c = ast_iax2_new(callno, AST_STATE_DOWN, cai.capability, &cai.prefs, assignedids,
+ requestor, cai.found);
ast_mutex_unlock(&iaxsl[callno]);
@@ -12840,7 +12815,7 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, st
ast_sockaddr_set_port(&peer->addr, IAX_DEFAULT_PORTNO);
peer->expiry = min_reg_expire;
}
- peer->prefs = prefs;
+ peer->prefs = prefs_global;
peer->capability = iax2_capability;
peer->smoothing = 0;
peer->pokefreqok = DEFAULT_FREQ_OK;
@@ -13157,7 +13132,7 @@ static struct iax2_user *build_user(const char *name, struct ast_variable *v, st
}
user->maxauthreq = maxauthreq;
user->curauthreq = oldcurauthreq;
- user->prefs = prefs;
+ user->prefs = prefs_global;
user->capability = iax2_capability;
user->encmethods = iax2_encryption;
user->adsi = adsi;
@@ -13470,7 +13445,7 @@ static void set_config_destroy(void)
static int set_config(const char *config_file, int reload, int forced)
{
struct ast_config *cfg, *ucfg;
- iax2_format capability = iax2_capability;
+ iax2_format capability;
struct ast_variable *v;
char *cat;
const char *utype;
@@ -13485,9 +13460,7 @@ static int set_config(const char *config_file, int reload, int forced)
struct ast_netsock *ns;
struct ast_flags config_flags = { (reload && !forced) ? CONFIG_FLAG_FILEUNCHANGED : 0 };
struct ast_sockaddr bindaddr;
-#if 0
- static unsigned short int last_port=0;
-#endif
+ struct iax2_codec_pref prefs_new;
cfg = ast_config_load(config_file, config_flags);
@@ -13528,8 +13501,8 @@ static int set_config(const char *config_file, int reload, int forced)
ast_sockaddr_parse(&bindaddr, "0.0.0.0:0", 0);
- /* Reset global codec prefs */
- memset(&prefs, 0 , sizeof(struct iax2_codec_pref));
+ /* Setup new codec prefs */
+ capability = iax2_codec_pref_from_bitfield(&prefs_new, IAX_CAPABILITY_FULLBANDWIDTH);
/* Reset Global Flags */
memset(&globalflags, 0, sizeof(globalflags));
@@ -13754,17 +13727,21 @@ static int set_config(const char *config_file, int reload, int forced)
}
} else if (!strcasecmp(v->name, "bandwidth")) {
if (!strcasecmp(v->value, "low")) {
- capability = IAX_CAPABILITY_LOWBANDWIDTH;
+ capability = iax2_codec_pref_from_bitfield(&prefs_new,
+ IAX_CAPABILITY_LOWBANDWIDTH);
} else if (!strcasecmp(v->value, "medium")) {
- capability = IAX_CAPABILITY_MEDBANDWIDTH;
+ capability = iax2_codec_pref_from_bitfield(&prefs_new,
+ IAX_CAPABILITY_MEDBANDWIDTH);
} else if (!strcasecmp(v->value, "high")) {
- capability = IAX_CAPABILITY_FULLBANDWIDTH;
- } else
+ capability = iax2_codec_pref_from_bitfield(&prefs_new,
+ IAX_CAPABILITY_FULLBANDWIDTH);
+ } else {
ast_log(LOG_WARNING, "bandwidth must be either low, medium, or high\n");
+ }
} else if (!strcasecmp(v->name, "allow")) {
- iax2_parse_allow_disallow(&prefs, &capability, v->value, 1);
+ iax2_parse_allow_disallow(&prefs_new, &capability, v->value, 1);
} else if (!strcasecmp(v->name, "disallow")) {
- iax2_parse_allow_disallow(&prefs, &capability, v->value, 0);
+ iax2_parse_allow_disallow(&prefs_new, &capability, v->value, 0);
} else if (!strcasecmp(v->name, "register")) {
iax2_register(v->value, v->lineno);
} else if (!strcasecmp(v->name, "iaxcompat")) {
@@ -13882,6 +13859,7 @@ static int set_config(const char *config_file, int reload, int forced)
min_reg_expire, max_reg_expire, max_reg_expire);
min_reg_expire = max_reg_expire;
}
+ prefs_global = prefs_new;
iax2_capability = capability;
if (ucfg) {
@@ -14408,7 +14386,7 @@ static int function_iaxpeer(struct ast_channel *chan, const char *cmd, char *dat
} else if (!strcasecmp(colname, "callerid_num")) {
ast_copy_string(buf, peer->cid_num, len);
} else if (!strcasecmp(colname, "codecs")) {
- struct ast_str *codec_buf;
+ struct ast_str *codec_buf = ast_str_alloca(256);
iax2_getformatname_multiple(peer->capability, &codec_buf);
ast_copy_string(buf, ast_str_buffer(codec_buf), len);
diff --git a/channels/iax2/codec_pref.c b/channels/iax2/codec_pref.c
index 903dca4cc..85167c368 100644
--- a/channels/iax2/codec_pref.c
+++ b/channels/iax2/codec_pref.c
@@ -38,6 +38,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/format_compatibility.h"
#include "asterisk/format_cache.h"
#include "asterisk/format_cap.h"
+#include "asterisk/utils.h"
#include "include/codec_pref.h"
#include "include/format_compatibility.h"
@@ -48,7 +49,8 @@ void iax2_codec_pref_convert(struct iax2_codec_pref *pref, char *buf, size_t siz
int x;
if (right) {
- for (x = 0; x < IAX2_CODEC_PREF_SIZE && x < size; x++) {
+ --size;/* Save room for the nul string terminator. */
+ for (x = 0; x < ARRAY_LEN(pref->order) && x < size; ++x) {
if (!pref->order[x]) {
break;
}
@@ -58,24 +60,29 @@ void iax2_codec_pref_convert(struct iax2_codec_pref *pref, char *buf, size_t siz
buf[x] = '\0';
} else {
- for (x = 0; x < IAX2_CODEC_PREF_SIZE && x < size; x++) {
+ for (x = 0; x < ARRAY_LEN(pref->order) && x < size; ++x) {
if (buf[x] == '\0') {
break;
}
pref->order[x] = buf[x] - differential;
+ pref->framing[x] = 0;
}
- if (x < size) {
+ if (x < ARRAY_LEN(pref->order)) {
pref->order[x] = 0;
+ pref->framing[x] = 0;
}
}
}
struct ast_format *iax2_codec_pref_index(struct iax2_codec_pref *pref, int idx, struct ast_format **result)
{
- if ((idx >= 0) && (idx < sizeof(pref->order)) && pref->order[idx]) {
- *result = ast_format_compatibility_bitfield2format(pref->order[idx]);
+ if (0 <= idx && idx < ARRAY_LEN(pref->order) && pref->order[idx]) {
+ uint64_t pref_bitfield;
+
+ pref_bitfield = iax2_codec_pref_order_value_to_format_bitfield(pref->order[idx]);
+ *result = ast_format_compatibility_bitfield2format(pref_bitfield);
} else {
*result = NULL;
}
@@ -83,50 +90,117 @@ struct ast_format *iax2_codec_pref_index(struct iax2_codec_pref *pref, int idx,
return *result;
}
-void iax2_codec_pref_to_cap(struct iax2_codec_pref *pref, struct ast_format_cap *cap)
+int iax2_codec_pref_to_cap(struct iax2_codec_pref *pref, struct ast_format_cap *cap)
{
int idx;
- for (idx = 0; idx < sizeof(pref->order); idx++) {
- if (!pref->order[idx]) {
+ for (idx = 0; idx < ARRAY_LEN(pref->order); ++idx) {
+ uint64_t pref_bitfield;
+ struct ast_format *pref_format;
+
+ pref_bitfield = iax2_codec_pref_order_value_to_format_bitfield(pref->order[idx]);
+ if (!pref_bitfield) {
+ break;
+ }
+
+ pref_format = ast_format_compatibility_bitfield2format(pref_bitfield);
+ if (pref_format && ast_format_cap_append(cap, pref_format, pref->framing[idx])) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int iax2_codec_pref_best_bitfield2cap(uint64_t bitfield, struct iax2_codec_pref *prefs, struct ast_format_cap *cap)
+{
+ uint64_t best_bitfield;
+ struct ast_format *format;
+
+ /* Add any user preferred codecs first. */
+ if (prefs) {
+ int idx;
+
+ for (idx = 0; bitfield && idx < ARRAY_LEN(prefs->order); ++idx) {
+ best_bitfield = iax2_codec_pref_order_value_to_format_bitfield(prefs->order[idx]);
+ if (!best_bitfield) {
+ break;
+ }
+
+ if (best_bitfield & bitfield) {
+ format = ast_format_compatibility_bitfield2format(best_bitfield);
+ if (format && ast_format_cap_append(cap, format, prefs->framing[idx])) {
+ return -1;
+ }
+
+ /* Remove just added codec. */
+ bitfield &= ~best_bitfield;
+ }
+ }
+ }
+
+ /* Add the hard coded "best" codecs. */
+ while (bitfield) {
+ best_bitfield = iax2_format_compatibility_best(bitfield);
+ if (!best_bitfield) {
+ /* No more codecs considered best. */
break;
}
- ast_format_cap_append(cap, ast_format_compatibility_bitfield2format(pref->order[idx]), pref->framing[idx]);
+
+ format = ast_format_compatibility_bitfield2format(best_bitfield);
+ /* The best_bitfield should always be convertible to a format. */
+ ast_assert(format != NULL);
+
+ if (ast_format_cap_append(cap, format, 0)) {
+ return -1;
+ }
+
+ /* Remove just added "best" codec to find the next "best". */
+ bitfield &= ~best_bitfield;
+ }
+
+ /* Add any remaining codecs. */
+ if (bitfield) {
+ int bit;
+
+ for (bit = 0; bit < 64; ++bit) {
+ uint64_t mask = (1ULL << bit);
+
+ if (mask & bitfield) {
+ format = ast_format_compatibility_bitfield2format(mask);
+ if (format && ast_format_cap_append(cap, format, 0)) {
+ return -1;
+ }
+ }
+ }
}
+
+ return 0;
}
int iax2_codec_pref_string(struct iax2_codec_pref *pref, char *buf, size_t size)
{
int x;
- struct ast_format_cap *cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+ struct ast_format_cap *cap;
size_t total_len;
char *cur;
- if (!cap) {
- return -1;
- }
-
/* This function is useless if you have less than a 6 character buffer.
* '(...)' is six characters. */
if (size < 6) {
return -1;
}
- /* Convert the preferences into a format cap so that we can read the formst names */
- for (x = 0; x < IAX2_CODEC_PREF_SIZE; x++) {
- uint64_t bitfield = iax2_codec_pref_order_value_to_format_bitfield(pref->order[x]);
- if (!bitfield) {
- break;
- }
-
- iax2_format_compatibility_bitfield2cap(bitfield, cap);
+ /* Convert the preferences into a format cap so that we can read the format names */
+ cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+ if (!cap || iax2_codec_pref_to_cap(pref, cap)) {
+ strcpy(buf, "(...)"); /* Safe */
+ ao2_cleanup(cap);
+ return -1;
}
/* We know that at a minimum, 3 characters are used - (, ), and \0 */
total_len = size - 3;
- memset(buf, 0, size);
-
/* This character has already been accounted for total_len purposes */
buf[0] = '(';
cur = buf + 1;
@@ -173,12 +247,20 @@ int iax2_codec_pref_string(struct iax2_codec_pref *pref, char *buf, size_t size)
static void codec_pref_remove_index(struct iax2_codec_pref *pref, int codec_pref_index)
{
- int x;
+ int idx;
- for (x = codec_pref_index; x < IAX2_CODEC_PREF_SIZE; x++) {
- pref->order[x] = pref->order[x + 1];
- pref->framing[x] = pref->framing[x + 1];
- if (!pref->order[x]) {
+ idx = codec_pref_index;
+ if (idx == ARRAY_LEN(pref->order) - 1) {
+ /* Remove from last array entry. */
+ pref->order[idx] = 0;
+ pref->framing[idx] = 0;
+ return;
+ }
+
+ for (; idx < ARRAY_LEN(pref->order); ++idx) {
+ pref->order[idx] = pref->order[idx + 1];
+ pref->framing[idx] = pref->framing[idx + 1];
+ if (!pref->order[idx]) {
return;
}
}
@@ -193,7 +275,7 @@ static void codec_pref_remove(struct iax2_codec_pref *pref, int format_index)
return;
}
- for (x = 0; x < IAX2_CODEC_PREF_SIZE; x++) {
+ for (x = 0; x < ARRAY_LEN(pref->order); ++x) {
if (!pref->order[x]) {
break;
}
@@ -207,86 +289,181 @@ static void codec_pref_remove(struct iax2_codec_pref *pref, int format_index)
void iax2_codec_pref_remove_missing(struct iax2_codec_pref *pref, uint64_t bitfield)
{
- int x;
+ int idx;
if (!pref->order[0]) {
return;
}
- for (x = 0; x < IAX2_CODEC_PREF_SIZE; x++) {
- uint64_t format_as_bitfield = iax2_codec_pref_order_value_to_format_bitfield(pref->order[x]);
- if (!pref->order[x]) {
- break;
+ /*
+ * Work from the end of the list so we always deal with
+ * unmodified entries in case we have to remove a pref.
+ */
+ for (idx = ARRAY_LEN(pref->order); idx--;) {
+ uint64_t pref_bitfield;
+
+ pref_bitfield = iax2_codec_pref_order_value_to_format_bitfield(pref->order[idx]);
+ if (!pref_bitfield) {
+ continue;
}
/* If this format isn't in the bitfield, remove it from the prefs. */
- if (!(format_as_bitfield & bitfield)) {
- codec_pref_remove_index(pref, x);
+ if (!(pref_bitfield & bitfield)) {
+ codec_pref_remove_index(pref, idx);
}
}
}
-uint64_t iax2_codec_pref_order_value_to_format_bitfield(uint64_t order_value)
+/*!
+ * \brief Formats supported by IAX2.
+ *
+ * \note All AST_FORMAT_xxx compatibility bit defines must be
+ * represented here.
+ *
+ * \note The order is important because the array index+1 values
+ * go out over the wire.
+ */
+static const uint64_t iax2_supported_formats[] = {
+ AST_FORMAT_G723,
+ AST_FORMAT_GSM,
+ AST_FORMAT_ULAW,
+ AST_FORMAT_ALAW,
+ AST_FORMAT_G726,
+ AST_FORMAT_ADPCM,
+ AST_FORMAT_SLIN,
+ AST_FORMAT_LPC10,
+ AST_FORMAT_G729,
+ AST_FORMAT_SPEEX,
+ AST_FORMAT_SPEEX16,
+ AST_FORMAT_ILBC,
+ AST_FORMAT_G726_AAL2,
+ AST_FORMAT_G722,
+ AST_FORMAT_SLIN16,
+ AST_FORMAT_JPEG,
+ AST_FORMAT_PNG,
+ AST_FORMAT_H261,
+ AST_FORMAT_H263,
+ AST_FORMAT_H263P,
+ AST_FORMAT_H264,
+ AST_FORMAT_MP4,
+ AST_FORMAT_T140_RED,
+ AST_FORMAT_T140,
+ AST_FORMAT_SIREN7,
+ AST_FORMAT_SIREN14,
+ AST_FORMAT_TESTLAW,
+ AST_FORMAT_G719,
+ 0, /* Place holder */
+ 0, /* Place holder */
+ 0, /* Place holder */
+ 0, /* Place holder */
+ 0, /* Place holder */
+ 0, /* Place holder */
+ 0, /* Place holder */
+ 0, /* Place holder */
+ AST_FORMAT_OPUS,
+ AST_FORMAT_VP8,
+ /* ONLY ADD TO THE END OF THIS LIST */
+ /* XXX Use up the place holder slots first. */
+};
+
+uint64_t iax2_codec_pref_order_value_to_format_bitfield(int order_value)
{
- if (!order_value) {
+ if (order_value < 1 || ARRAY_LEN(iax2_supported_formats) < order_value) {
return 0;
}
- return 1 << (order_value - 1);
+ return iax2_supported_formats[order_value - 1];
}
-uint64_t iax2_codec_pref_format_bitfield_to_order_value(uint64_t bitfield)
+int iax2_codec_pref_format_bitfield_to_order_value(uint64_t bitfield)
{
- int format_index = 1;
-
- if (!bitfield) {
- return 0;
- }
+ int idx;
- while (bitfield > 1) {
- bitfield = bitfield >> 1;
- format_index++;
+ if (bitfield) {
+ for (idx = 0; idx < ARRAY_LEN(iax2_supported_formats); ++idx) {
+ if (iax2_supported_formats[idx] == bitfield) {
+ return idx + 1;
+ }
+ }
}
-
- return format_index;
+ return 0;
}
-/*! \brief Append codec to list */
-int iax2_codec_pref_append(struct iax2_codec_pref *pref, struct ast_format *format, unsigned int framing)
+/*!
+ * \internal
+ * \brief Append the bitfield format to the codec preference list.
+ * \since 13.0.0
+ *
+ * \param pref Codec preference list to append the given bitfield.
+ * \param bitfield Format bitfield to append.
+ * \param framing Framing size of the codec.
+ *
+ * \return Nothing
+ */
+static void iax2_codec_pref_append_bitfield(struct iax2_codec_pref *pref, uint64_t bitfield, unsigned int framing)
{
- uint64_t bitfield = ast_format_compatibility_format2bitfield(format);
- int format_index = iax2_codec_pref_format_bitfield_to_order_value(bitfield);
+ int format_index;
int x;
+ format_index = iax2_codec_pref_format_bitfield_to_order_value(bitfield);
+ if (!format_index) {
+ return;
+ }
+
codec_pref_remove(pref, format_index);
- for (x = 0; x < IAX2_CODEC_PREF_SIZE; x++) {
+ for (x = 0; x < ARRAY_LEN(pref->order); ++x) {
if (!pref->order[x]) {
pref->order[x] = format_index;
pref->framing[x] = framing;
break;
}
}
+}
+
+void iax2_codec_pref_append(struct iax2_codec_pref *pref, struct ast_format *format, unsigned int framing)
+{
+ uint64_t bitfield;
+
+ bitfield = ast_format_compatibility_format2bitfield(format);
+ if (!bitfield) {
+ return;
+ }
- return x;
+ iax2_codec_pref_append_bitfield(pref, bitfield, framing);
}
-/*! \brief Prepend codec to list */
void iax2_codec_pref_prepend(struct iax2_codec_pref *pref, struct ast_format *format, unsigned int framing,
int only_if_existing)
{
- uint64_t bitfield = ast_format_compatibility_format2bitfield(format);
+ uint64_t bitfield;
+ int format_index;
int x;
+ bitfield = ast_format_compatibility_format2bitfield(format);
+ if (!bitfield) {
+ return;
+ }
+ format_index = iax2_codec_pref_format_bitfield_to_order_value(bitfield);
+ if (!format_index) {
+ return;
+ }
+
/* Now find any existing occurrence, or the end */
- for (x = 0; x < IAX2_CODEC_PREF_SIZE; x++) {
- if (!pref->order[x] || pref->order[x] == bitfield)
+ for (x = 0; x < ARRAY_LEN(pref->order); ++x) {
+ if (!pref->order[x] || pref->order[x] == format_index)
break;
}
- /* If we failed to find any occurrence, set to the end */
- if (x == IAX2_CODEC_PREF_SIZE) {
- --x;
+ /*
+ * The array can never be full without format_index
+ * also being in the array.
+ */
+ ast_assert(x < ARRAY_LEN(pref->order));
+
+ /* If we failed to find any occurrence, set to the end for safety. */
+ if (ARRAY_LEN(pref->order) <= x) {
+ x = ARRAY_LEN(pref->order) - 1;
}
if (only_if_existing && !pref->order[x]) {
@@ -295,39 +472,63 @@ void iax2_codec_pref_prepend(struct iax2_codec_pref *pref, struct ast_format *fo
/* Move down to make space to insert - either all the way to the end,
or as far as the existing location (which will be overwritten) */
- for (; x > 0; x--) {
+ for (; x > 0; --x) {
pref->order[x] = pref->order[x - 1];
pref->framing[x] = pref->framing[x - 1];
}
/* And insert the new entry */
- pref->order[0] = bitfield;
+ pref->order[0] = format_index;
pref->framing[0] = framing;
}
-unsigned int iax2_codec_pref_getsize(struct iax2_codec_pref *pref, int idx)
+uint64_t iax2_codec_pref_from_bitfield(struct iax2_codec_pref *pref, uint64_t bitfield)
{
- if ((idx >= 0) && (idx < sizeof(pref->order)) && pref->order[idx]) {
- return pref->framing[idx];
- } else {
- return 0;
- }
-}
+ int bit;
+ uint64_t working_bitfield;
+ uint64_t best_bitfield;
+ struct ast_format *format;
-int iax2_codec_pref_setsize(struct iax2_codec_pref *pref, struct ast_format *format, int framems)
-{
- int idx;
+ /* Init the preference list. */
+ memset(pref, 0, sizeof(*pref));
- for (idx = 0; idx < sizeof(pref->order); idx++) {
- if (!pref->order[idx]) {
+ working_bitfield = bitfield;
+
+ /* Add the "best" codecs first. */
+ while (working_bitfield) {
+ best_bitfield = iax2_format_compatibility_best(working_bitfield);
+ if (!best_bitfield) {
+ /* No more codecs considered best. */
break;
- } else if (ast_format_cmp(ast_format_compatibility_bitfield2format(pref->order[idx]),
- format) != AST_FORMAT_CMP_EQUAL) {
- continue;
}
- pref->framing[idx] = framems;
- return 0;
+
+ /* Remove current "best" codec to find the next "best". */
+ working_bitfield &= ~best_bitfield;
+
+ format = ast_format_compatibility_bitfield2format(best_bitfield);
+ /* The best_bitfield should always be convertible to a format. */
+ ast_assert(format != NULL);
+
+ iax2_codec_pref_append_bitfield(pref, best_bitfield, 0);
+ }
+
+ /* Add any remaining codecs. */
+ if (working_bitfield) {
+ for (bit = 0; bit < 64; ++bit) {
+ uint64_t mask = (1ULL << bit);
+
+ if (mask & working_bitfield) {
+ format = ast_format_compatibility_bitfield2format(mask);
+ if (!format) {
+ /* The bit is not associated with any format. */
+ bitfield &= ~mask;
+ continue;
+ }
+
+ iax2_codec_pref_append_bitfield(pref, mask, 0);
+ }
+ }
}
- return -1;
+ return bitfield;
}
diff --git a/channels/iax2/format_compatibility.c b/channels/iax2/format_compatibility.c
index 8085c2c26..be7852c8f 100644
--- a/channels/iax2/format_compatibility.c
+++ b/channels/iax2/format_compatibility.c
@@ -38,6 +38,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/format_compatibility.h"
#include "asterisk/format_cache.h"
#include "asterisk/format_cap.h"
+#include "asterisk/utils.h"
#include "include/format_compatibility.h"
@@ -59,13 +60,75 @@ uint64_t iax2_format_compatibility_cap2bitfield(const struct ast_format_cap *cap
int iax2_format_compatibility_bitfield2cap(uint64_t bitfield, struct ast_format_cap *cap)
{
- int x;
+ int bit;
+
+ for (bit = 0; bit < 64; ++bit) {
+ uint64_t mask = (1ULL << bit);
+
+ if (mask & bitfield) {
+ struct ast_format *format;
+
+ format = ast_format_compatibility_bitfield2format(mask);
+ if (format && ast_format_cap_append(cap, format, 0)) {
+ return -1;
+ }
+ }
+ }
- for (x = 0; x < 64; x++) {
- uint64_t tmp = (1ULL << x);
+ return 0;
+}
+
+uint64_t iax2_format_compatibility_best(uint64_t formats)
+{
+ /*
+ * This just our opinion, expressed in code. We are
+ * asked to choose the best codec to use, given no
+ * information.
+ */
+ static const uint64_t best[] = {
+ /*! Okay, ulaw is used by all telephony equipment, so start with it */
+ AST_FORMAT_ULAW,
+ /*! Unless of course, you're a silly European, so then prefer ALAW */
+ AST_FORMAT_ALAW,
+ AST_FORMAT_G719,
+ AST_FORMAT_SIREN14,
+ AST_FORMAT_SIREN7,
+ AST_FORMAT_TESTLAW,
+ /*! G.722 is better then all below, but not as common as the above... so give ulaw and alaw priority */
+ AST_FORMAT_G722,
+ /*! Okay, well, signed linear is easy to translate into other stuff */
+ AST_FORMAT_SLIN16,
+ AST_FORMAT_SLIN,
+ /*! G.726 is standard ADPCM, in RFC3551 packing order */
+ AST_FORMAT_G726,
+ /*! G.726 is standard ADPCM, in AAL2 packing order */
+ AST_FORMAT_G726_AAL2,
+ /*! ADPCM has great sound quality and is still pretty easy to translate */
+ AST_FORMAT_ADPCM,
+ /*! Okay, we're down to vocoders now, so pick GSM because it's small and easier to
+ translate and sounds pretty good */
+ AST_FORMAT_GSM,
+ /*! iLBC is not too bad */
+ AST_FORMAT_ILBC,
+ /*! Speex is free, but computationally more expensive than GSM */
+ AST_FORMAT_SPEEX16,
+ AST_FORMAT_SPEEX,
+ /*! Opus */
+ AST_FORMAT_OPUS,
+ /*! Ick, LPC10 sounds terrible, but at least we have code for it, if you're tacky enough
+ to use it */
+ AST_FORMAT_LPC10,
+ /*! G.729a is faster than 723 and slightly less expensive */
+ AST_FORMAT_G729,
+ /*! Down to G.723.1 which is proprietary but at least designed for voice */
+ AST_FORMAT_G723,
+ };
+ int idx;
- if ((tmp & bitfield) && ast_format_cap_append(cap, ast_format_compatibility_bitfield2format(tmp), 0)) {
- return -1;
+ /* Find the first preferred codec in the format given */
+ for (idx = 0; idx < ARRAY_LEN(best); ++idx) {
+ if (formats & best[idx]) {
+ return best[idx];
}
}
diff --git a/channels/iax2/include/codec_pref.h b/channels/iax2/include/codec_pref.h
index bfb889164..2af3692d8 100644
--- a/channels/iax2/include/codec_pref.h
+++ b/channels/iax2/include/codec_pref.h
@@ -32,8 +32,8 @@ struct ast_format_cap;
#define IAX2_CODEC_PREF_SIZE 64
struct iax2_codec_pref {
- /*! This array is ordered by preference and contains the codec bitfield. */
- uint64_t order[IAX2_CODEC_PREF_SIZE];
+ /*! Array is ordered by preference. Contains the iax2_supported_formats[] index + 1. */
+ char order[IAX2_CODEC_PREF_SIZE];
/*! Framing size of the codec */
unsigned int framing[IAX2_CODEC_PREF_SIZE];
};
@@ -45,7 +45,7 @@ struct iax2_codec_pref {
*
* \return the bitfield value of the order_value format
*/
-uint64_t iax2_codec_pref_order_value_to_format_bitfield(uint64_t order_value);
+uint64_t iax2_codec_pref_order_value_to_format_bitfield(int order_value);
/*!
* \brief Convert a format bitfield into an iax2_codec_pref order value
@@ -59,7 +59,7 @@ uint64_t iax2_codec_pref_order_value_to_format_bitfield(uint64_t order_value);
* It will work with multiformat bitfields, but it can only return the
* index of the most significant one if that is the case.
*/
-uint64_t iax2_codec_pref_format_bitfield_to_order_value(uint64_t bitfield);
+int iax2_codec_pref_format_bitfield_to_order_value(uint64_t bitfield);
/*!
* \brief Codec located at a particular place in the preference index.
@@ -70,8 +70,32 @@ uint64_t iax2_codec_pref_format_bitfield_to_order_value(uint64_t bitfield);
*/
struct ast_format *iax2_codec_pref_index(struct iax2_codec_pref *pref, int index, struct ast_format **result);
-/*! \brief Convert a preference structure to a capabilities structure */
-void iax2_codec_pref_to_cap(struct iax2_codec_pref *pref, struct ast_format_cap *cap);
+/*!
+ * \brief Convert a preference structure to a capabilities structure.
+ *
+ * \param pref Formats in preference order to build the capabilities.
+ * \param cap Capabilities structure to place formats into
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ *
+ * \note If failure occurs the capabilities structure may contain a partial set of formats
+ */
+int iax2_codec_pref_to_cap(struct iax2_codec_pref *pref, struct ast_format_cap *cap);
+
+/*!
+ * \brief Convert a bitfield to a format capabilities structure in the "best" order.
+ *
+ * \param bitfield The bitfield for the media formats
+ * \param prefs Format preference order to use as a guide. (May be NULL)
+ * \param cap Capabilities structure to place formats into
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ *
+ * \note If failure occurs the capabilities structure may contain a partial set of formats
+ */
+int iax2_codec_pref_best_bitfield2cap(uint64_t bitfield, struct iax2_codec_pref *prefs, struct ast_format_cap *cap);
/*! \brief Removes format from the pref list that aren't in the bitfield */
void iax2_codec_pref_remove_missing(struct iax2_codec_pref *pref, uint64_t bitfield);
@@ -94,21 +118,13 @@ int iax2_codec_pref_string(struct iax2_codec_pref *pref, char *buf, size_t size)
/*! \brief Append a audio codec to a preference list, removing it first if it was already there
*/
-int iax2_codec_pref_append(struct iax2_codec_pref *pref, struct ast_format *format, unsigned int framing);
+void iax2_codec_pref_append(struct iax2_codec_pref *pref, struct ast_format *format, unsigned int framing);
/*! \brief Prepend an audio codec to a preference list, removing it first if it was already there
*/
void iax2_codec_pref_prepend(struct iax2_codec_pref *pref, struct ast_format *format, unsigned int framing,
int only_if_existing);
-/*! \brief Get packet size for codec
-*/
-unsigned int iax2_codec_pref_getsize(struct iax2_codec_pref *pref, int index);
-
-/*! \brief Set packet size for codec
-*/
-int iax2_codec_pref_setsize(struct iax2_codec_pref *pref, struct ast_format *format, int framems);
-
/*! \brief Shift an audio codec preference list up or down 65 bytes so that it becomes an ASCII string
* \note Due to a misunderstanding in how codec preferences are stored, this
* list starts at 'B', not 'A'. For backwards compatibility reasons, this
@@ -120,4 +136,15 @@ int iax2_codec_pref_setsize(struct iax2_codec_pref *pref, struct ast_format *for
*/
void iax2_codec_pref_convert(struct iax2_codec_pref *pref, char *buf, size_t size, int right);
+/*!
+ * \brief Create codec preference list from the given bitfield formats.
+ * \since 13.0.0
+ *
+ * \param pref Codec preference list to setup from the given bitfield.
+ * \param bitfield Format bitfield to guide preference list creation.
+ *
+ * \return Updated bitfield with any bits not mapped to a format cleared.
+ */
+uint64_t iax2_codec_pref_from_bitfield(struct iax2_codec_pref *pref, uint64_t bitfield);
+
#endif /* _IAX2_CODEC_PREF_H_ */
diff --git a/channels/iax2/include/format_compatibility.h b/channels/iax2/include/format_compatibility.h
index aa29cfa2c..e3839fcda 100644
--- a/channels/iax2/include/format_compatibility.h
+++ b/channels/iax2/include/format_compatibility.h
@@ -45,11 +45,21 @@ uint64_t iax2_format_compatibility_cap2bitfield(const struct ast_format_cap *cap
* \param bitfield The bitfield for the media formats
* \param cap Capabilities structure to place formats into
*
- * \retval non-NULL success
- * \retval NULL failure
+ * \retval 0 on success.
+ * \retval -1 on error.
*
* \note If failure occurs the capabilities structure may contain a partial set of formats
*/
int iax2_format_compatibility_bitfield2cap(uint64_t bitfield, struct ast_format_cap *cap);
+/*!
+ * \brief Pick the best format from the given bitfield formats.
+ *
+ * \param formats The bitfield for the media formats
+ *
+ * \retval non-zero Best format out of the given formats.
+ * \retval zero No formats present or no formats considered best.
+ */
+uint64_t iax2_format_compatibility_best(uint64_t formats);
+
#endif /* _IAX2_FORMAT_COMPATIBILITY_H */
diff --git a/include/asterisk/format_compatibility.h b/include/asterisk/format_compatibility.h
index f14b7166c..0420ec69d 100644
--- a/include/asterisk/format_compatibility.h
+++ b/include/asterisk/format_compatibility.h
@@ -120,7 +120,7 @@ uint64_t ast_format_compatibility_codec2bitfield(const struct ast_codec *codec);
* \param bitfield The bitfield for the media format
*
* \retval non-NULL success
- * \retval NULL failure
+ * \retval NULL failure (The format bitfield value is not supported)
*
* \note The reference count of the returned format is NOT incremented
*/
diff --git a/main/format_compatibility.c b/main/format_compatibility.c
index df82bacd5..cf66af282 100644
--- a/main/format_compatibility.c
+++ b/main/format_compatibility.c
@@ -269,6 +269,6 @@ struct ast_format *ast_format_compatibility_bitfield2format(uint64_t bitfield)
case AST_FORMAT_T140:
return ast_format_t140_red;
}
- return 0;
+ return NULL;
}