diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index 831f2c266c..630a66342a 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 903dca4cc8..85167c368e 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 8085c2c26e..be7852c8f5 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 bfb8891645..2af3692d80 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 aa29cfa2c0..e3839fcda4 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 f14b7166c1..0420ec69d9 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 df82bacd5f..cf66af282a 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; }