diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 051f0cf31dc546bccf8c34b4ebd550fdb251f158..05b8e82cb2bf73e87c5a8baf1bd17dc514439820 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -142,6 +142,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/abstract_jb.h" #include "asterisk/compiler.h" #include "asterisk/threadstorage.h" +#include "asterisk/translate.h" #ifndef FALSE #define FALSE 0 @@ -927,7 +928,7 @@ struct sip_pvt { unsigned int sipoptions; /*!< Supported SIP options on the other end */ struct ast_codec_pref prefs; /*!< codec prefs */ int capability; /*!< Special capability (codec) */ - int jointcapability; /*!< Supported capability at both ends (codecs ) */ + int jointcapability; /*!< Supported capability at both ends (codecs) */ int peercapability; /*!< Supported peer capability */ int prefcodec; /*!< Preferred codec (outbound only) */ int noncodeccapability; /*!< DTMF RFC2833 telephony-event */ @@ -6091,15 +6092,13 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p) if (p->redirip.sin_addr.s_addr) { dest.sin_port = p->redirip.sin_port; dest.sin_addr = p->redirip.sin_addr; - if (p->redircodecs) - capability = p->redircodecs; } else { dest.sin_addr = p->ourip; dest.sin_port = sin.sin_port; } /* Ok, let's start working with codec selection here */ - capability = p->jointcapability; + capability = ast_translate_available_formats(p->jointcapability, p->prefcodec); if (option_debug > 1) { char codecbuf[BUFSIZ]; @@ -6107,26 +6106,25 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p) ast_log(LOG_DEBUG, "** Our prefcodec: %s \n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), p->prefcodec)); } - if ((ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_RTP))) { + if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_RTP)) { ast_build_string(&m_audio_next, &m_audio_left, " %d", 191); ast_build_string(&a_audio_next, &a_audio_left, "a=rtpmap:%d %s/%d\r\n", 191, "t38", 8000); } /* Check if we need video in this call */ - if((capability & AST_FORMAT_VIDEO_MASK) && !ast_test_flag(&p->flags[0], SIP_NOVIDEO)) { + if ((capability & AST_FORMAT_VIDEO_MASK) && !ast_test_flag(&p->flags[0], SIP_NOVIDEO)) { if (p->vrtp) { needvideo = TRUE; if (option_debug > 1) - ast_log(LOG_DEBUG, "This call needs video offers! \n"); + ast_log(LOG_DEBUG, "This call needs video offers!\n"); } else if (option_debug > 1) - ast_log(LOG_DEBUG, "This call needs video offers, but there's no video support enabled ! \n"); + ast_log(LOG_DEBUG, "This call needs video offers, but there's no video support enabled!\n"); } /* Ok, we need video. Let's add what we need for video and set codecs. Video is handled differently than audio since we can not transcode. */ if (needvideo) { - /* Determine video destination */ if (p->vredirip.sin_addr.s_addr) { vdest.sin_addr = p->vredirip.sin_addr; @@ -6142,31 +6140,8 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p) snprintf(bandwidth, sizeof(bandwidth), "b=CT:%d\r\n", p->maxcallbitrate); if (debug) ast_verbose("Video is at %s port %d\n", ast_inet_ntoa(p->ourip), ntohs(vsin.sin_port)); - - /* For video, we can't negotiate video offers. Let's compare the incoming call with what we got. */ - if (p->prefcodec) { - int videocapability = (capability & p->prefcodec) & AST_FORMAT_VIDEO_MASK; /* Outbound call */ - - /*! \todo XXX We need to select one codec, not many, since there's no transcoding */ - - /* Now, merge this video capability into capability while removing unsupported codecs */ - if (!videocapability) { - needvideo = FALSE; - if (option_debug > 2) - ast_log(LOG_DEBUG, "** No compatible video codecs... Disabling video.\n"); - } - - /* Replace video capabilities with the new videocapability */ - capability = (capability & AST_FORMAT_AUDIO_MASK) | videocapability; - - if (option_debug > 4) { - char codecbuf[BUFSIZ]; - if (videocapability) - ast_log(LOG_DEBUG, "** Our video codec selection is: %s \n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), videocapability)); - ast_log(LOG_DEBUG, "** Capability now set to : %s \n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), capability)); - } - } } + if (debug) ast_verbose("Audio is at %s port %d\n", ast_inet_ntoa(p->ourip), ntohs(sin.sin_port)); diff --git a/include/asterisk/translate.h b/include/asterisk/translate.h index 07c0ae1c2068a2ca3754dc667f38a3693f85b987..a03f54ef23024400b32c4fb0786b83fe453c87c1 100644 --- a/include/asterisk/translate.h +++ b/include/asterisk/translate.h @@ -205,12 +205,26 @@ struct ast_frame *ast_translate(struct ast_trans_pvt *tr, struct ast_frame *f, i /*! * \brief Returns the number of steps required to convert from 'src' to 'dest'. - * \param dest Destination format - * \param src Source format + * \param dest destination format + * \param src source format * \return the number of translation steps required, or -1 if no path is available */ unsigned int ast_translate_path_steps(unsigned int dest, unsigned int src); +/*! + * \brief Mask off unavailable formats from a format bitmask + * \param dest possible destination formats + * \param src source formats + * \return the destination formats that are available in the source or translatable + * + * The result will include all formats from 'dest' that are either present + * in 'src' or translatable from a format present in 'src'. + * + * Note that only a single audio format and a single video format can be + * present in 'src', or the function will produce unexpected results. + */ +unsigned int ast_translate_available_formats(unsigned int dest, unsigned int src); + #if defined(__cplusplus) || defined(c_plusplus) } #endif diff --git a/main/translate.c b/main/translate.c index d130a38dea659c319ffcd5626595964765b37507..bdee23e8db01966ab0eb8716596efd74196902df 100644 --- a/main/translate.c +++ b/main/translate.c @@ -63,6 +63,9 @@ struct translator_path { * until step->dstfmt == desired_format. * * Array indexes are 'src' and 'dest', in that order. + * + * Note: the lock in the 'translators' list is also used to protect + * this structure. */ static struct translator_path tr_matrix[MAX_FORMAT][MAX_FORMAT]; @@ -253,18 +256,22 @@ struct ast_trans_pvt *ast_translator_build_path(int dest, int source) source = powerof(source); dest = powerof(dest); + AST_LIST_LOCK(&translators); + while (source != dest) { struct ast_trans_pvt *cur; struct ast_translator *t = tr_matrix[source][dest].step; if (!t) { ast_log(LOG_WARNING, "No translator path from %s to %s\n", ast_getformatname(source), ast_getformatname(dest)); + AST_LIST_UNLOCK(&translators); return NULL; } if (!(cur = newpvt(t))) { ast_log(LOG_WARNING, "Failed to build translator step from %d to %d\n", source, dest); if (head) ast_translator_free_path(head); + AST_LIST_UNLOCK(&translators); return NULL; } if (!head) @@ -276,6 +283,8 @@ struct ast_trans_pvt *ast_translator_build_path(int dest, int source) /* Keep going if this isn't the final destination */ source = cur->t->dstfmt; } + + AST_LIST_UNLOCK(&translators); return head; } @@ -560,6 +569,7 @@ static struct ast_cli_entry cli_translate[] = { int __ast_register_translator(struct ast_translator *t, struct ast_module *mod) { static int added_cli = 0; + struct ast_translator *u; if (!mod) { ast_log(LOG_WARNING, "Missing module pointer, you need to supply one\n"); @@ -617,7 +627,24 @@ int __ast_register_translator(struct ast_translator *t, struct ast_module *mod) ast_cli_register_multiple(cli_translate, sizeof(cli_translate) / sizeof(struct ast_cli_entry)); added_cli++; } - AST_LIST_INSERT_HEAD(&translators, t, list); + + /* find any existing translators that provide this same srcfmt/dstfmt, + and put this one in order based on cost */ + AST_LIST_TRAVERSE_SAFE_BEGIN(&translators, u, list) { + if ((u->srcfmt == t->srcfmt) && + (u->dstfmt == t->dstfmt) && + (u->cost > t->cost)) { + AST_LIST_INSERT_BEFORE_CURRENT(&translators, t, list); + t = NULL; + } + } + AST_LIST_TRAVERSE_SAFE_END; + + /* if no existing translator was found for this format combination, + add it to the beginning of the list */ + if (t) + AST_LIST_INSERT_HEAD(&translators, t, list); + rebuild_matrix(0); AST_LIST_UNLOCK(&translators); return 0; @@ -637,7 +664,7 @@ int ast_unregister_translator(struct ast_translator *t) break; } } - AST_LIST_TRAVERSE_SAFE_END + AST_LIST_TRAVERSE_SAFE_END; rebuild_matrix(0); AST_LIST_UNLOCK(&translators); return (u ? 0 : -1); @@ -693,12 +720,70 @@ int ast_translator_best_choice(int *dst, int *srcs) unsigned int ast_translate_path_steps(unsigned int dest, unsigned int src) { + unsigned int res = -1; + /* convert bitwise format numbers into array indices */ src = powerof(src); dest = powerof(dest); - if (!tr_matrix[src][dest].step) - return -1; - else - return tr_matrix[src][dest].multistep + 1; + + AST_LIST_LOCK(&translators); + + if (tr_matrix[src][dest].step) + res = tr_matrix[src][dest].multistep + 1; + + AST_LIST_UNLOCK(&translators); + + return res; } +unsigned int ast_translate_available_formats(unsigned int dest, unsigned int src) +{ + unsigned int res = dest; + unsigned int x; + unsigned int src_audio = powerof(src & AST_FORMAT_AUDIO_MASK); + unsigned int src_video = powerof(src & AST_FORMAT_VIDEO_MASK); + + /* if we don't have a source format, we just have to try all + possible destination formats */ + if (!src) + return dest; + + AST_LIST_LOCK(&translators); + + for (x = 1; x < AST_FORMAT_MAX_AUDIO; x <<= 1) { + /* if this is not a desired format, nothing to do */ + if (!dest & x) + continue; + + /* if the source is supplying this format, then + we can leave it in the result */ + if (src & x) + continue; + + /* if we don't have a translation path from the src + to this format, remove it from the result */ + if (!tr_matrix[src_audio][powerof(x)].step) + res &= ~x; + } + + /* roll over into video formats */ + for (; x < AST_FORMAT_MAX_VIDEO; x <<= 1) { + /* if this is not a desired format, nothing to do */ + if (!dest & x) + continue; + + /* if the source is supplying this format, then + we can leave it in the result */ + if (src & x) + continue; + + /* if we don't have a translation path from the src + to this format, remove it from the result */ + if (!tr_matrix[src_video][powerof(x)].step) + res &= ~x; + } + + AST_LIST_UNLOCK(&translators); + + return res; +}