diff options
-rw-r--r-- | CHANGES | 8 | ||||
-rw-r--r-- | bridges/bridge_native_rtp.c | 645 | ||||
-rw-r--r-- | channels/chan_pjsip.c | 3 | ||||
-rw-r--r-- | channels/chan_sip.c | 6 | ||||
-rw-r--r-- | configs/samples/pjsip.conf.sample | 11 | ||||
-rwxr-xr-x | configure | 47 | ||||
-rw-r--r-- | include/asterisk/rtp_engine.h | 9 | ||||
-rw-r--r-- | main/channel.c | 6 | ||||
-rw-r--r-- | res/res_pjsip.c | 11 | ||||
-rw-r--r-- | res/res_pjsip/pjsip_distributor.c | 242 | ||||
-rw-r--r-- | third-party/configure.m4 | 5 | ||||
-rw-r--r-- | third-party/pjproject/Makefile.rules | 5 | ||||
-rw-r--r-- | third-party/pjproject/configure.m4 | 24 | ||||
-rw-r--r-- | third-party/pjproject/patches/0070-Set-PJSIP_INV_SUPPORT_UPDATE-correctly-in-pjsip_inv_.patch | 29 |
14 files changed, 826 insertions, 225 deletions
@@ -54,6 +54,14 @@ chan_pjsip from the SDP, unless the remote side sends a different codec and we will switch to match. +Build System +------------------ + * Added a new PJPROJECT_CONFIGURE_OPTS environment variable which can be used + to pass arbitrary options to the bundled pjproject configure. + + * Automatically set the bundled pjproject configure --host and --build + options to match those supplied for the asterisk configure. + ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 13.15.0 to Asterisk 13.16.0 ---------- ------------------------------------------------------------------------------ diff --git a/bridges/bridge_native_rtp.c b/bridges/bridge_native_rtp.c index 78e35a16b..4e55a3e88 100644 --- a/bridges/bridge_native_rtp.c +++ b/bridges/bridge_native_rtp.c @@ -46,76 +46,214 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/frame.h" #include "asterisk/rtp_engine.h" -/*! \brief Internal structure which contains information about bridged RTP channels */ -struct native_rtp_bridge_data { +/*! \brief Internal structure which contains bridged RTP channel hook data */ +struct native_rtp_framehook_data { /*! \brief Framehook used to intercept certain control frames */ int id; /*! \brief Set when this framehook has been detached */ unsigned int detached; }; -/*! \brief Internal helper function which gets all RTP information (glue and instances) relating to the given channels */ -static enum ast_rtp_glue_result native_rtp_bridge_get(struct ast_channel *c0, struct ast_channel *c1, struct ast_rtp_glue **glue0, - struct ast_rtp_glue **glue1, struct ast_rtp_instance **instance0, struct ast_rtp_instance **instance1, - struct ast_rtp_instance **vinstance0, struct ast_rtp_instance **vinstance1) +struct rtp_glue_stream { + /*! \brief RTP instance */ + struct ast_rtp_instance *instance; + /*! \brief glue result */ + enum ast_rtp_glue_result result; +}; + +struct rtp_glue_data { + /*! + * \brief glue callbacks + * + * \note The glue data is considered valid if cb is not NULL. + */ + struct ast_rtp_glue *cb; + struct rtp_glue_stream audio; + struct rtp_glue_stream video; + /*! Combined glue result of both bridge channels. */ + enum ast_rtp_glue_result result; +}; + +/*! \brief Internal structure which contains instance information about bridged RTP channels */ +struct native_rtp_bridge_channel_data { + /*! \brief Channel's hook data */ + struct native_rtp_framehook_data *hook_data; + /*! + * \brief Glue callbacks to bring remote channel streams back to Asterisk. + * \note NULL if channel streams are local. + */ + struct ast_rtp_glue *remote_cb; + /*! \brief Channel's cached RTP glue information */ + struct rtp_glue_data glue; +}; + +static void rtp_glue_data_init(struct rtp_glue_data *glue) { - enum ast_rtp_glue_result audio_glue0_res; - enum ast_rtp_glue_result video_glue0_res; - enum ast_rtp_glue_result audio_glue1_res; - enum ast_rtp_glue_result video_glue1_res; + glue->cb = NULL; + glue->audio.instance = NULL; + glue->audio.result = AST_RTP_GLUE_RESULT_FORBID; + glue->video.instance = NULL; + glue->video.result = AST_RTP_GLUE_RESULT_FORBID; + glue->result = AST_RTP_GLUE_RESULT_FORBID; +} - if (!(*glue0 = ast_rtp_instance_get_glue(ast_channel_tech(c0)->type)) || - !(*glue1 = ast_rtp_instance_get_glue(ast_channel_tech(c1)->type))) { - return AST_RTP_GLUE_RESULT_FORBID; +static void rtp_glue_data_destroy(struct rtp_glue_data *glue) +{ + if (!glue) { + return; } + ao2_cleanup(glue->audio.instance); + ao2_cleanup(glue->video.instance); +} + +static void rtp_glue_data_reset(struct rtp_glue_data *glue) +{ + rtp_glue_data_destroy(glue); + rtp_glue_data_init(glue); +} + +static void native_rtp_bridge_channel_data_free(struct native_rtp_bridge_channel_data *data) +{ + ast_debug(2, "Destroying channel tech_pvt data %p\n", data); - audio_glue0_res = (*glue0)->get_rtp_info(c0, instance0); - video_glue0_res = (*glue0)->get_vrtp_info ? (*glue0)->get_vrtp_info(c0, vinstance0) : AST_RTP_GLUE_RESULT_FORBID; + /* + * hook_data will probably already have been unreferenced by the framehook detach + * and the pointer set to null. + */ + ao2_cleanup(data->hook_data); - audio_glue1_res = (*glue1)->get_rtp_info(c1, instance1); - video_glue1_res = (*glue1)->get_vrtp_info ? (*glue1)->get_vrtp_info(c1, vinstance1) : AST_RTP_GLUE_RESULT_FORBID; + rtp_glue_data_reset(&data->glue); + ast_free(data); +} + +static struct native_rtp_bridge_channel_data *native_rtp_bridge_channel_data_alloc(void) +{ + struct native_rtp_bridge_channel_data *data; + + data = ast_calloc(1, sizeof(*data)); + if (data) { + rtp_glue_data_init(&data->glue); + } + return data; +} + +/*! + * \internal + * \brief Helper function which gets all RTP information (glue and instances) relating to the given channels + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rtp_glue_data_get(struct ast_channel *c0, struct rtp_glue_data *glue0, + struct ast_channel *c1, struct rtp_glue_data *glue1) +{ + struct ast_rtp_glue *cb0; + struct ast_rtp_glue *cb1; + enum ast_rtp_glue_result combined_result; + + cb0 = ast_rtp_instance_get_glue(ast_channel_tech(c0)->type); + cb1 = ast_rtp_instance_get_glue(ast_channel_tech(c1)->type); + if (!cb0 || !cb1) { + /* One or both channels doesn't have any RTP glue registered. */ + return -1; + } + + /* The glue callbacks bump the RTP instance refcounts for us. */ + + glue0->cb = cb0; + glue0->audio.result = cb0->get_rtp_info(c0, &glue0->audio.instance); + glue0->video.result = cb0->get_vrtp_info + ? cb0->get_vrtp_info(c0, &glue0->video.instance) : AST_RTP_GLUE_RESULT_FORBID; + + glue1->cb = cb1; + glue1->audio.result = cb1->get_rtp_info(c1, &glue1->audio.instance); + glue1->video.result = cb1->get_vrtp_info + ? cb1->get_vrtp_info(c1, &glue1->video.instance) : AST_RTP_GLUE_RESULT_FORBID; + + /* + * Now determine the combined glue result. + */ /* Apply any limitations on direct media bridging that may be present */ - if (audio_glue0_res == audio_glue1_res && audio_glue1_res == AST_RTP_GLUE_RESULT_REMOTE) { - if ((*glue0)->allow_rtp_remote && !((*glue0)->allow_rtp_remote(c0, *instance1))) { + if (glue0->audio.result == glue1->audio.result && glue1->audio.result == AST_RTP_GLUE_RESULT_REMOTE) { + if (glue0->cb->allow_rtp_remote && !glue0->cb->allow_rtp_remote(c0, glue1->audio.instance)) { /* If the allow_rtp_remote indicates that remote isn't allowed, revert to local bridge */ - audio_glue0_res = audio_glue1_res = AST_RTP_GLUE_RESULT_LOCAL; - } else if ((*glue1)->allow_rtp_remote && !((*glue1)->allow_rtp_remote(c1, *instance0))) { - audio_glue0_res = audio_glue1_res = AST_RTP_GLUE_RESULT_LOCAL; + glue0->audio.result = glue1->audio.result = AST_RTP_GLUE_RESULT_LOCAL; + } else if (glue1->cb->allow_rtp_remote && !glue1->cb->allow_rtp_remote(c1, glue0->audio.instance)) { + glue0->audio.result = glue1->audio.result = AST_RTP_GLUE_RESULT_LOCAL; } } - if (video_glue0_res == video_glue1_res && video_glue1_res == AST_RTP_GLUE_RESULT_REMOTE) { - if ((*glue0)->allow_vrtp_remote && !((*glue0)->allow_vrtp_remote(c0, *instance1))) { + if (glue0->video.result == glue1->video.result && glue1->video.result == AST_RTP_GLUE_RESULT_REMOTE) { + if (glue0->cb->allow_vrtp_remote && !glue0->cb->allow_vrtp_remote(c0, glue1->audio.instance)) { /* if the allow_vrtp_remote indicates that remote isn't allowed, revert to local bridge */ - video_glue0_res = video_glue1_res = AST_RTP_GLUE_RESULT_LOCAL; - } else if ((*glue1)->allow_vrtp_remote && !((*glue1)->allow_vrtp_remote(c1, *instance0))) { - video_glue0_res = video_glue1_res = AST_RTP_GLUE_RESULT_LOCAL; + glue0->video.result = glue1->video.result = AST_RTP_GLUE_RESULT_LOCAL; + } else if (glue1->cb->allow_vrtp_remote && !glue1->cb->allow_vrtp_remote(c1, glue0->audio.instance)) { + glue0->video.result = glue1->video.result = AST_RTP_GLUE_RESULT_LOCAL; } } /* If we are carrying video, and both sides are not going to remotely bridge... fail the native bridge */ - if (video_glue0_res != AST_RTP_GLUE_RESULT_FORBID - && (audio_glue0_res != AST_RTP_GLUE_RESULT_REMOTE - || video_glue0_res != AST_RTP_GLUE_RESULT_REMOTE)) { - audio_glue0_res = AST_RTP_GLUE_RESULT_FORBID; + if (glue0->video.result != AST_RTP_GLUE_RESULT_FORBID + && (glue0->audio.result != AST_RTP_GLUE_RESULT_REMOTE + || glue0->video.result != AST_RTP_GLUE_RESULT_REMOTE)) { + glue0->audio.result = AST_RTP_GLUE_RESULT_FORBID; } - if (video_glue1_res != AST_RTP_GLUE_RESULT_FORBID - && (audio_glue1_res != AST_RTP_GLUE_RESULT_REMOTE - || video_glue1_res != AST_RTP_GLUE_RESULT_REMOTE)) { - audio_glue1_res = AST_RTP_GLUE_RESULT_FORBID; + if (glue1->video.result != AST_RTP_GLUE_RESULT_FORBID + && (glue1->audio.result != AST_RTP_GLUE_RESULT_REMOTE + || glue1->video.result != AST_RTP_GLUE_RESULT_REMOTE)) { + glue1->audio.result = AST_RTP_GLUE_RESULT_FORBID; } /* The order of preference is: forbid, local, and remote. */ - if (audio_glue0_res == AST_RTP_GLUE_RESULT_FORBID || - audio_glue1_res == AST_RTP_GLUE_RESULT_FORBID) { + if (glue0->audio.result == AST_RTP_GLUE_RESULT_FORBID + || glue1->audio.result == AST_RTP_GLUE_RESULT_FORBID) { /* If any sort of bridge is forbidden just completely bail out and go back to generic bridging */ - return AST_RTP_GLUE_RESULT_FORBID; - } else if (audio_glue0_res == AST_RTP_GLUE_RESULT_LOCAL || - audio_glue1_res == AST_RTP_GLUE_RESULT_LOCAL) { - return AST_RTP_GLUE_RESULT_LOCAL; + combined_result = AST_RTP_GLUE_RESULT_FORBID; + } else if (glue0->audio.result == AST_RTP_GLUE_RESULT_LOCAL + || glue1->audio.result == AST_RTP_GLUE_RESULT_LOCAL) { + combined_result = AST_RTP_GLUE_RESULT_LOCAL; } else { - return AST_RTP_GLUE_RESULT_REMOTE; + combined_result = AST_RTP_GLUE_RESULT_REMOTE; + } + glue0->result = combined_result; + glue1->result = combined_result; + + return 0; +} + +/*! + * \internal + * \brief Get the current RTP native bridge combined glue result. + * \since 15.0.0 + * + * \param c0 First bridge channel + * \param c1 Second bridge channel + * + * \note Both channels must be locked when calling this function. + * + * \return Current combined glue result. + */ +static enum ast_rtp_glue_result rtp_glue_get_current_combined_result(struct ast_channel *c0, + struct ast_channel *c1) +{ + struct rtp_glue_data glue_a; + struct rtp_glue_data glue_b; + struct rtp_glue_data *glue0; + struct rtp_glue_data *glue1; + enum ast_rtp_glue_result combined_result; + + rtp_glue_data_init(&glue_a); + glue0 = &glue_a; + rtp_glue_data_init(&glue_b); + glue1 = &glue_b; + if (rtp_glue_data_get(c0, glue0, c1, glue1)) { + return AST_RTP_GLUE_RESULT_FORBID; } + + combined_result = glue0->result; + rtp_glue_data_destroy(glue0); + rtp_glue_data_destroy(glue1); + return combined_result; } /*! @@ -131,52 +269,91 @@ static void native_rtp_bridge_start(struct ast_bridge *bridge, struct ast_channe { struct ast_bridge_channel *bc0 = AST_LIST_FIRST(&bridge->channels); struct ast_bridge_channel *bc1 = AST_LIST_LAST(&bridge->channels); - enum ast_rtp_glue_result native_type = AST_RTP_GLUE_RESULT_FORBID; - struct ast_rtp_glue *glue0, *glue1; - RAII_VAR(struct ast_rtp_instance *, instance0, NULL, ao2_cleanup); - RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ao2_cleanup); - RAII_VAR(struct ast_rtp_instance *, vinstance0, NULL, ao2_cleanup); - RAII_VAR(struct ast_rtp_instance *, vinstance1, NULL, ao2_cleanup); - RAII_VAR(struct ast_rtp_instance *, tinstance0, NULL, ao2_cleanup); - RAII_VAR(struct ast_rtp_instance *, tinstance1, NULL, ao2_cleanup); - RAII_VAR(struct ast_format_cap *, cap0, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup); - RAII_VAR(struct ast_format_cap *, cap1, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup); + struct native_rtp_bridge_channel_data *data0; + struct native_rtp_bridge_channel_data *data1; + struct rtp_glue_data *glue0; + struct rtp_glue_data *glue1; + struct ast_format_cap *cap0; + struct ast_format_cap *cap1; + enum ast_rtp_glue_result native_type; if (bc0 == bc1) { return; } + data0 = bc0->tech_pvt; + data1 = bc1->tech_pvt; + if (!data0 || !data1) { + /* Not all channels are joined with the bridge tech yet */ + return; + } + glue0 = &data0->glue; + glue1 = &data1->glue; ast_channel_lock_both(bc0->chan, bc1->chan); - if (!bc0->suspended && !bc1->suspended) { - native_type = native_rtp_bridge_get(bc0->chan, bc1->chan, &glue0, &glue1, &instance0, &instance1, &vinstance0, &vinstance1); + + if (!glue0->cb || !glue1->cb) { + /* + * Somebody doesn't have glue data so the bridge isn't running + * + * Actually neither side should have glue data. + */ + ast_assert(!glue0->cb && !glue1->cb); + + if (rtp_glue_data_get(bc0->chan, glue0, bc1->chan, glue1)) { + /* + * This might happen if one of the channels got masqueraded + * at a critical time. It's a bit of a stretch even then + * since the channel is in a bridge. + */ + goto done; + } } + ast_debug(2, "Bridge '%s'. Tech starting '%s' and '%s' with target '%s'\n", + bridge->uniqueid, ast_channel_name(bc0->chan), ast_channel_name(bc1->chan), + target ? ast_channel_name(target) : "none"); + + native_type = glue0->result; + switch (native_type) { case AST_RTP_GLUE_RESULT_LOCAL: - if (ast_rtp_instance_get_engine(instance0)->local_bridge) { - ast_rtp_instance_get_engine(instance0)->local_bridge(instance0, instance1); + if (ast_rtp_instance_get_engine(glue0->audio.instance)->local_bridge) { + ast_rtp_instance_get_engine(glue0->audio.instance)->local_bridge(glue0->audio.instance, glue1->audio.instance); } - if (ast_rtp_instance_get_engine(instance1)->local_bridge) { - ast_rtp_instance_get_engine(instance1)->local_bridge(instance1, instance0); + if (ast_rtp_instance_get_engine(glue1->audio.instance)->local_bridge) { + ast_rtp_instance_get_engine(glue1->audio.instance)->local_bridge(glue1->audio.instance, glue0->audio.instance); } - ast_rtp_instance_set_bridged(instance0, instance1); - ast_rtp_instance_set_bridged(instance1, instance0); + ast_rtp_instance_set_bridged(glue0->audio.instance, glue1->audio.instance); + ast_rtp_instance_set_bridged(glue1->audio.instance, glue0->audio.instance); ast_verb(4, "Locally RTP bridged '%s' and '%s' in stack\n", ast_channel_name(bc0->chan), ast_channel_name(bc1->chan)); break; - case AST_RTP_GLUE_RESULT_REMOTE: - if (glue0->get_codec) { - glue0->get_codec(bc0->chan, cap0); + cap0 = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); + cap1 = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); + if (!cap0 || !cap1) { + ao2_cleanup(cap0); + ao2_cleanup(cap1); + break; } - if (glue1->get_codec) { - glue1->get_codec(bc1->chan, cap1); + + if (glue0->cb->get_codec) { + glue0->cb->get_codec(bc0->chan, cap0); + } + if (glue1->cb->get_codec) { + glue1->cb->get_codec(bc1->chan, cap1); } - /* If we have a target, it's the channel that received the UNHOLD or UPDATE_RTP_PEER frame and was told to resume */ + /* + * If we have a target, it's the channel that received the UNHOLD or + * UPDATE_RTP_PEER frame and was told to resume + */ if (!target) { - glue0->update_peer(bc0->chan, instance1, vinstance1, tinstance1, cap1, 0); - glue1->update_peer(bc1->chan, instance0, vinstance0, tinstance0, cap0, 0); + /* Send both channels to remote */ + data0->remote_cb = glue0->cb; + data1->remote_cb = glue1->cb; + glue0->cb->update_peer(bc0->chan, glue1->audio.instance, glue1->video.instance, NULL, cap1, 0); + glue1->cb->update_peer(bc1->chan, glue0->audio.instance, glue0->video.instance, NULL, cap0, 0); ast_verb(4, "Remotely bridged '%s' and '%s' - media will flow directly between them\n", ast_channel_name(bc0->chan), ast_channel_name(bc1->chan)); } else { @@ -186,51 +363,121 @@ static void native_rtp_bridge_start(struct ast_bridge *bridge, struct ast_channe * already set up to handle the new media path or will have its own set of updates independent * of this pass. */ + ast_debug(2, "Bridge '%s'. Sending '%s' back to remote\n", + bridge->uniqueid, ast_channel_name(target)); if (bc0->chan == target) { - glue0->update_peer(bc0->chan, instance1, vinstance1, tinstance1, cap1, 0); + data0->remote_cb = glue0->cb; + glue0->cb->update_peer(bc0->chan, glue1->audio.instance, glue1->video.instance, NULL, cap1, 0); } else { - glue1->update_peer(bc1->chan, instance0, vinstance0, tinstance0, cap0, 0); + data1->remote_cb = glue1->cb; + glue1->cb->update_peer(bc1->chan, glue0->audio.instance, glue0->video.instance, NULL, cap0, 0); } } + + ao2_cleanup(cap0); + ao2_cleanup(cap1); break; case AST_RTP_GLUE_RESULT_FORBID: break; } + if (native_type != AST_RTP_GLUE_RESULT_REMOTE) { + /* Bring any remaining channels back to us. */ + if (data0->remote_cb) { + ast_debug(2, "Bridge '%s'. Bringing back '%s' to us\n", + bridge->uniqueid, ast_channel_name(bc0->chan)); + data0->remote_cb->update_peer(bc0->chan, NULL, NULL, NULL, NULL, 0); + data0->remote_cb = NULL; + } + if (data1->remote_cb) { + ast_debug(2, "Bridge '%s'. Bringing back '%s' to us\n", + bridge->uniqueid, ast_channel_name(bc1->chan)); + data1->remote_cb->update_peer(bc1->chan, NULL, NULL, NULL, NULL, 0); + data1->remote_cb = NULL; + } + } + +done: ast_channel_unlock(bc0->chan); ast_channel_unlock(bc1->chan); } +/*! + * \internal + * \brief Stop native RTP bridging of two channels + * + * \param bridge The bridge that had native RTP bridging happening on it + * \param target If remote RTP bridging, the channel that is held. + * + * \note The first channel to leave the bridge triggers the cleanup for both channels + */ static void native_rtp_bridge_stop(struct ast_bridge *bridge, struct ast_channel *target) { struct ast_bridge_channel *bc0 = AST_LIST_FIRST(&bridge->channels); struct ast_bridge_channel *bc1 = AST_LIST_LAST(&bridge->channels); - enum ast_rtp_glue_result native_type; - struct ast_rtp_glue *glue0, *glue1 = NULL; - RAII_VAR(struct ast_rtp_instance *, instance0, NULL, ao2_cleanup); - RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ao2_cleanup); - RAII_VAR(struct ast_rtp_instance *, vinstance0, NULL, ao2_cleanup); - RAII_VAR(struct ast_rtp_instance *, vinstance1, NULL, ao2_cleanup); + struct native_rtp_bridge_channel_data *data0; + struct native_rtp_bridge_channel_data *data1; + struct rtp_glue_data *glue0; + struct rtp_glue_data *glue1; if (bc0 == bc1) { return; } + data0 = bc0->tech_pvt; + data1 = bc1->tech_pvt; + if (!data0 || !data1) { + /* Not all channels are joined with the bridge tech */ + return; + } + glue0 = &data0->glue; + glue1 = &data1->glue; + + ast_debug(2, "Bridge '%s'. Tech stopping '%s' and '%s' with target '%s'\n", + bridge->uniqueid, ast_channel_name(bc0->chan), ast_channel_name(bc1->chan), + target ? ast_channel_name(target) : "none"); + + if (!glue0->cb || !glue1->cb) { + /* + * Somebody doesn't have glue data so the bridge isn't running + * + * Actually neither side should have glue data. + */ + ast_assert(!glue0->cb && !glue1->cb); + /* At most one channel can be left at the remote endpoint here. */ + ast_assert(!data0->remote_cb || !data1->remote_cb); + + /* Bring selected channel streams back to us */ + if (data0->remote_cb && (!target || target == bc0->chan)) { + ast_channel_lock(bc0->chan); + ast_debug(2, "Bridge '%s'. Bringing back '%s' to us\n", + bridge->uniqueid, ast_channel_name(bc0->chan)); + data0->remote_cb->update_peer(bc0->chan, NULL, NULL, NULL, NULL, 0); + data0->remote_cb = NULL; + ast_channel_unlock(bc0->chan); + } + if (data1->remote_cb && (!target || target == bc1->chan)) { + ast_channel_lock(bc1->chan); + ast_debug(2, "Bridge '%s'. Bringing back '%s' to us\n", + bridge->uniqueid, ast_channel_name(bc1->chan)); + data1->remote_cb->update_peer(bc1->chan, NULL, NULL, NULL, NULL, 0); + data1->remote_cb = NULL; + ast_channel_unlock(bc1->chan); + } + return; + } ast_channel_lock_both(bc0->chan, bc1->chan); - native_type = native_rtp_bridge_get(bc0->chan, bc1->chan, &glue0, &glue1, &instance0, &instance1, &vinstance0, &vinstance1); - switch (native_type) { + switch (glue0->result) { case AST_RTP_GLUE_RESULT_LOCAL: - if (ast_rtp_instance_get_engine(instance0)->local_bridge) { - ast_rtp_instance_get_engine(instance0)->local_bridge(instance0, NULL); - } - if (instance1 && ast_rtp_instance_get_engine(instance1)->local_bridge) { - ast_rtp_instance_get_engine(instance1)->local_bridge(instance1, NULL); + if (ast_rtp_instance_get_engine(glue0->audio.instance)->local_bridge) { + ast_rtp_instance_get_engine(glue0->audio.instance)->local_bridge(glue0->audio.instance, NULL); } - ast_rtp_instance_set_bridged(instance0, NULL); - if (instance1) { - ast_rtp_instance_set_bridged(instance1, NULL); + if (ast_rtp_instance_get_engine(glue1->audio.instance)->local_bridge) { + ast_rtp_instance_get_engine(glue1->audio.instance)->local_bridge(glue1->audio.instance, NULL); } + ast_rtp_instance_set_bridged(glue0->audio.instance, NULL); + ast_rtp_instance_set_bridged(glue1->audio.instance, NULL); break; case AST_RTP_GLUE_RESULT_REMOTE: if (target) { @@ -238,10 +485,38 @@ static void native_rtp_bridge_stop(struct ast_bridge *bridge, struct ast_channel * If a target was provided, it is being put on hold and should expect to * receive media from Asterisk instead of what it was previously connected to. */ + ast_debug(2, "Bridge '%s'. Bringing back '%s' to us\n", + bridge->uniqueid, ast_channel_name(target)); if (bc0->chan == target) { - glue0->update_peer(bc0->chan, NULL, NULL, NULL, NULL, 0); + data0->remote_cb = NULL; + glue0->cb->update_peer(bc0->chan, NULL, NULL, NULL, NULL, 0); + } else { + data1->remote_cb = NULL; + glue1->cb->update_peer(bc1->chan, NULL, NULL, NULL, NULL, 0); + } + } else { + data0->remote_cb = NULL; + data1->remote_cb = NULL; + /* + * XXX We don't want to bring back the channels if we are + * switching to T.38. We have received a reinvite on one channel + * and we will be sending a reinvite on the other to start T.38. + * If we bring the streams back now we confuse the chan_pjsip + * channel driver processing the incoming T.38 reinvite with + * reinvite glare. I think this is really a bug in chan_pjsip + * that this exception case is working around. + */ + if (rtp_glue_get_current_combined_result(bc0->chan, bc1->chan) + != AST_RTP_GLUE_RESULT_FORBID) { + ast_debug(2, "Bridge '%s'. Bringing back '%s' and '%s' to us\n", + bridge->uniqueid, ast_channel_name(bc0->chan), + ast_channel_name(bc1->chan)); + glue0->cb->update_peer(bc0->chan, NULL, NULL, NULL, NULL, 0); + glue1->cb->update_peer(bc1->chan, NULL, NULL, NULL, NULL, 0); } else { - glue1->update_peer(bc1->chan, NULL, NULL, NULL, NULL, 0); + ast_debug(2, "Bridge '%s'. Skip bringing back '%s' and '%s' to us\n", + bridge->uniqueid, ast_channel_name(bc0->chan), + ast_channel_name(bc1->chan)); } } break; @@ -249,10 +524,8 @@ static void native_rtp_bridge_stop(struct ast_bridge *bridge, struct ast_channel break; } - if (!target && native_type != AST_RTP_GLUE_RESULT_FORBID) { - glue0->update_peer(bc0->chan, NULL, NULL, NULL, NULL, 0); - glue1->update_peer(bc1->chan, NULL, NULL, NULL, NULL, 0); - } + rtp_glue_data_reset(glue0); + rtp_glue_data_reset(glue1); ast_debug(2, "Discontinued RTP bridging of '%s' and '%s' - media will flow through Asterisk core\n", ast_channel_name(bc0->chan), ast_channel_name(bc1->chan)); @@ -261,11 +534,15 @@ static void native_rtp_bridge_stop(struct ast_bridge *bridge, struct ast_channel ast_channel_unlock(bc1->chan); } -/*! \brief Frame hook that is called to intercept hold/unhold */ -static struct ast_frame *native_rtp_framehook(struct ast_channel *chan, struct ast_frame *f, enum ast_framehook_event event, void *data) +/*! + * \internal + * \brief Frame hook that is called to intercept hold/unhold + */ +static struct ast_frame *native_rtp_framehook(struct ast_channel *chan, + struct ast_frame *f, enum ast_framehook_event event, void *data) { RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup); - struct native_rtp_bridge_data *native_data = data; + struct native_rtp_framehook_data *native_data = data; if (!f || (event != AST_FRAMEHOOK_EVENT_WRITE)) { return f; @@ -295,39 +572,49 @@ static struct ast_frame *native_rtp_framehook(struct ast_channel *chan, struct a } ast_bridge_unlock(bridge); ast_channel_lock(chan); - } return f; } -/*! \brief Callback function which informs upstream if we are consuming a frame of a specific type */ +/*! + * \internal + * \brief Callback function which informs upstream if we are consuming a frame of a specific type + */ static int native_rtp_framehook_consume(void *data, enum ast_frame_type type) { return (type == AST_FRAME_CONTROL ? 1 : 0); } -/*! \brief Internal helper function which checks whether the channels are compatible with our native bridging */ +/*! + * \internal + * \brief Internal helper function which checks whether a channel is compatible with our native bridging + */ static int native_rtp_bridge_capable(struct ast_channel *chan) { return !ast_channel_has_hook_requiring_audio(chan); } +/*! + * \internal + * \brief Internal helper function which checks whether both channels are compatible with our native bridging + */ static int native_rtp_bridge_compatible_check(struct ast_bridge *bridge, struct ast_bridge_channel *bc0, struct ast_bridge_channel *bc1) { enum ast_rtp_glue_result native_type; - struct ast_rtp_glue *glue0; - struct ast_rtp_glue *glue1; - RAII_VAR(struct ast_rtp_instance *, instance0, NULL, ao2_cleanup); - RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ao2_cleanup); - RAII_VAR(struct ast_rtp_instance *, vinstance0, NULL, ao2_cleanup); - RAII_VAR(struct ast_rtp_instance *, vinstance1, NULL, ao2_cleanup); - RAII_VAR(struct ast_format_cap *, cap0, NULL, ao2_cleanup); - RAII_VAR(struct ast_format_cap *, cap1, NULL, ao2_cleanup); int read_ptime0; int read_ptime1; int write_ptime0; int write_ptime1; + struct rtp_glue_data glue_a; + struct rtp_glue_data glue_b; + RAII_VAR(struct ast_format_cap *, cap0, NULL, ao2_cleanup); + RAII_VAR(struct ast_format_cap *, cap1, NULL, ao2_cleanup); + RAII_VAR(struct rtp_glue_data *, glue0, NULL, rtp_glue_data_destroy); + RAII_VAR(struct rtp_glue_data *, glue1, NULL, rtp_glue_data_destroy); + + ast_debug(1, "Bridge '%s'. Checking compatability for channels '%s' and '%s'\n", + bridge->uniqueid, ast_channel_name(bc0->chan), ast_channel_name(bc1->chan)); if (!native_rtp_bridge_capable(bc0->chan)) { ast_debug(1, "Bridge '%s' can not use native RTP bridge as channel '%s' has features which prevent it\n", @@ -341,8 +628,17 @@ static int native_rtp_bridge_compatible_check(struct ast_bridge *bridge, struct return 0; } - native_type = native_rtp_bridge_get(bc0->chan, bc1->chan, &glue0, &glue1, - &instance0, &instance1, &vinstance0, &vinstance1); + rtp_glue_data_init(&glue_a); + glue0 = &glue_a; + rtp_glue_data_init(&glue_b); + glue1 = &glue_b; + if (rtp_glue_data_get(bc0->chan, glue0, bc1->chan, glue1)) { + ast_debug(1, "Bridge '%s' can not use native RTP bridge as could not get details\n", + bridge->uniqueid); + return 0; + } + native_type = glue0->result; + if (native_type == AST_RTP_GLUE_RESULT_FORBID) { ast_debug(1, "Bridge '%s' can not use native RTP bridge as it was forbidden while getting details\n", bridge->uniqueid); @@ -350,25 +646,25 @@ static int native_rtp_bridge_compatible_check(struct ast_bridge *bridge, struct } if (ao2_container_count(bc0->features->dtmf_hooks) - && ast_rtp_instance_dtmf_mode_get(instance0)) { + && ast_rtp_instance_dtmf_mode_get(glue0->audio.instance)) { ast_debug(1, "Bridge '%s' can not use native RTP bridge as channel '%s' has DTMF hooks\n", bridge->uniqueid, ast_channel_name(bc0->chan)); return 0; } if (ao2_container_count(bc1->features->dtmf_hooks) - && ast_rtp_instance_dtmf_mode_get(instance1)) { + && ast_rtp_instance_dtmf_mode_get(glue1->audio.instance)) { ast_debug(1, "Bridge '%s' can not use native RTP bridge as channel '%s' has DTMF hooks\n", bridge->uniqueid, ast_channel_name(bc1->chan)); return 0; } if (native_type == AST_RTP_GLUE_RESULT_LOCAL - && (ast_rtp_instance_get_engine(instance0)->local_bridge - != ast_rtp_instance_get_engine(instance1)->local_bridge - || (ast_rtp_instance_get_engine(instance0)->dtmf_compatible - && !ast_rtp_instance_get_engine(instance0)->dtmf_compatible(bc0->chan, - instance0, bc1->chan, instance1)))) { + && (ast_rtp_instance_get_engine(glue0->audio.instance)->local_bridge + != ast_rtp_instance_get_engine(glue1->audio.instance)->local_bridge + || (ast_rtp_instance_get_engine(glue0->audio.instance)->dtmf_compatible + && !ast_rtp_instance_get_engine(glue0->audio.instance)->dtmf_compatible(bc0->chan, + glue0->audio.instance, bc1->chan, glue1->audio.instance)))) { ast_debug(1, "Bridge '%s' can not use local native RTP bridge as local bridge or DTMF is not compatible\n", bridge->uniqueid); return 0; @@ -381,11 +677,11 @@ static int native_rtp_bridge_compatible_check(struct ast_bridge *bridge, struct } /* Make sure that codecs match */ - if (glue0->get_codec) { - glue0->get_codec(bc0->chan, cap0); + if (glue0->cb->get_codec) { + glue0->cb->get_codec(bc0->chan, cap0); } - if (glue1->get_codec) { - glue1->get_codec(bc1->chan, cap1); + if (glue1->cb->get_codec) { + glue1->cb->get_codec(bc1->chan, cap1); } if (ast_format_cap_count(cap0) != 0 && ast_format_cap_count(cap1) != 0 @@ -415,6 +711,10 @@ static int native_rtp_bridge_compatible_check(struct ast_bridge *bridge, struct return 1; } +/*! + * \internal + * \brief Called by the bridge core "compatible' callback + */ static int native_rtp_bridge_compatible(struct ast_bridge *bridge) { struct ast_bridge_channel *bc0; @@ -439,10 +739,13 @@ static int native_rtp_bridge_compatible(struct ast_bridge *bridge) return is_compatible; } -/*! \brief Helper function which adds frame hook to bridge channel */ +/*! + * \internal + * \brief Helper function which adds frame hook to bridge channel + */ static int native_rtp_bridge_framehook_attach(struct ast_bridge_channel *bridge_channel) { - struct native_rtp_bridge_data *data = ao2_alloc(sizeof(*data), NULL); + struct native_rtp_bridge_channel_data *data = bridge_channel->tech_pvt; static struct ast_framehook_interface hook = { .version = AST_FRAMEHOOK_INTERFACE_VERSION, .event_cb = native_rtp_framehook, @@ -451,45 +754,82 @@ static int native_rtp_bridge_framehook_attach(struct ast_bridge_channel *bridge_ .disable_inheritance = 1, }; - if (!data) { + ast_assert(data->hook_data == NULL); + data->hook_data = ao2_alloc_options(sizeof(*data->hook_data), NULL, + AO2_ALLOC_OPT_LOCK_NOLOCK); + if (!data->hook_data) { return -1; } + ast_debug(2, "Bridge '%s'. Attaching hook data %p to '%s'\n", + bridge_channel->bridge->uniqueid, data, ast_channel_name(bridge_channel->chan)); + ast_channel_lock(bridge_channel->chan); - hook.data = ao2_bump(data); - data->id = ast_framehook_attach(bridge_channel->chan, &hook); + /* We're giving 1 ref to the framehook and keeping the one from the alloc for ourselves */ + hook.data = ao2_bump(data->hook_data); + data->hook_data->id = ast_framehook_attach(bridge_channel->chan, &hook); ast_channel_unlock(bridge_channel->chan); - if (data->id < 0) { - /* We need to drop both the reference we hold, and the one the framehook would hold */ - ao2_ref(data, -2); + if (data->hook_data->id < 0) { + /* + * We need to drop both the reference we hold in data, + * and the one the framehook would hold. + */ + ao2_ref(data->hook_data, -2); + data->hook_data = NULL; + return -1; } - bridge_channel->tech_pvt = data; - return 0; } -/*! \brief Helper function which removes frame hook from bridge channel */ +/*! + * \internal + * \brief Helper function which removes frame hook from bridge channel + */ static void native_rtp_bridge_framehook_detach(struct ast_bridge_channel *bridge_channel) { - RAII_VAR(struct native_rtp_bridge_data *, data, bridge_channel->tech_pvt, ao2_cleanup); + struct native_rtp_bridge_channel_data *data = bridge_channel->tech_pvt; - if (!data) { + if (!data || !data->hook_data) { return; } + ast_debug(2, "Bridge '%s'. Detaching hook data %p from '%s'\n", + bridge_channel->bridge->uniqueid, data->hook_data, ast_channel_name(bridge_channel->chan)); + ast_channel_lock(bridge_channel->chan); - ast_framehook_detach(bridge_channel->chan, data->id); - data->detached = 1; + ast_framehook_detach(bridge_channel->chan, data->hook_data->id); + data->hook_data->detached = 1; ast_channel_unlock(bridge_channel->chan); - bridge_channel->tech_pvt = NULL; + ao2_cleanup(data->hook_data); + data->hook_data = NULL; } +/*! + * \internal + * \brief Called by the bridge core 'join' callback for each channel joining he bridge + */ static int native_rtp_bridge_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel) { - native_rtp_bridge_framehook_detach(bridge_channel); + ast_debug(2, "Bridge '%s'. Channel '%s' is joining bridge tech\n", + bridge->uniqueid, ast_channel_name(bridge_channel->chan)); + + ast_assert(bridge_channel->tech_pvt == NULL); + + if (bridge_channel->suspended) { + /* The channel will rejoin when it is unsuspended */ + return 0; + } + + bridge_channel->tech_pvt = native_rtp_bridge_channel_data_alloc(); + if (!bridge_channel->tech_pvt) { + return -1; + } + if (native_rtp_bridge_framehook_attach(bridge_channel)) { + native_rtp_bridge_channel_data_free(bridge_channel->tech_pvt); + bridge_channel->tech_pvt = NULL; return -1; } @@ -497,15 +837,46 @@ static int native_rtp_bridge_join(struct ast_bridge *bridge, struct ast_bridge_c return 0; } +/*! + * \internal + * \brief Add the channel back into the bridge + */ static void native_rtp_bridge_unsuspend(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel) { + ast_debug(2, "Bridge '%s'. Channel '%s' is unsuspended back to bridge tech\n", + bridge->uniqueid, ast_channel_name(bridge_channel->chan)); native_rtp_bridge_join(bridge, bridge_channel); } +/*! + * \internal + * \brief Leave the bridge + */ static void native_rtp_bridge_leave(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel) { + ast_debug(2, "Bridge '%s'. Channel '%s' is leaving bridge tech\n", + bridge->uniqueid, ast_channel_name(bridge_channel->chan)); + + if (!bridge_channel->tech_pvt) { + return; + } + native_rtp_bridge_framehook_detach(bridge_channel); native_rtp_bridge_stop(bridge, NULL); + + native_rtp_bridge_channel_data_free(bridge_channel->tech_pvt); + bridge_channel->tech_pvt = NULL; +} + +/*! + * \internal + * \brief Suspend the channel from the bridge + */ +static void native_rtp_bridge_suspend(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel) +{ + ast_debug(2, "Bridge '%s'. Channel '%s' is suspending from bridge tech\n", + bridge->uniqueid, ast_channel_name(bridge_channel->chan)); + native_rtp_bridge_leave(bridge, bridge_channel); } static int native_rtp_bridge_write(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame) @@ -550,7 +921,7 @@ static struct ast_bridge_technology native_rtp_bridge = { .join = native_rtp_bridge_join, .unsuspend = native_rtp_bridge_unsuspend, .leave = native_rtp_bridge_leave, - .suspend = native_rtp_bridge_leave, + .suspend = native_rtp_bridge_suspend, .write = native_rtp_bridge_write, .compatible = native_rtp_bridge_compatible, }; diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c index d1691b87e..e2fd13c29 100644 --- a/channels/chan_pjsip.c +++ b/channels/chan_pjsip.c @@ -1272,8 +1272,7 @@ static int update_connected_line_information(void *data) int generate_new_sdp; method = session->endpoint->id.refresh_method; - if (session->inv_session->invite_tsx - && (session->inv_session->options & PJSIP_INV_SUPPORT_UPDATE)) { + if (session->inv_session->options & PJSIP_INV_SUPPORT_UPDATE) { method = AST_SIP_SESSION_REFRESH_METHOD_UPDATE; } diff --git a/channels/chan_sip.c b/channels/chan_sip.c index e3b3c8439..488fbf418 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -33171,15 +33171,15 @@ static int reload_config(enum channelreloadreason reason) /* If TCP is running on a different IP than UDP, then add it too */ if (!ast_sockaddr_isnull(&sip_tcp_desc.local_address) && - !ast_sockaddr_cmp(&bindaddr, &sip_tcp_desc.local_address)) { + ast_sockaddr_cmp_addr(&bindaddr, &sip_tcp_desc.local_address)) { add_sip_domain(ast_sockaddr_stringify_addr(&sip_tcp_desc.local_address), SIP_DOMAIN_AUTO, NULL); } /* If TLS is running on a different IP than UDP and TCP, then add that too */ if (!ast_sockaddr_isnull(&sip_tls_desc.local_address) && - !ast_sockaddr_cmp(&bindaddr, &sip_tls_desc.local_address) && - !ast_sockaddr_cmp(&sip_tcp_desc.local_address, + ast_sockaddr_cmp_addr(&bindaddr, &sip_tls_desc.local_address) && + ast_sockaddr_cmp_addr(&sip_tcp_desc.local_address, &sip_tls_desc.local_address)) { add_sip_domain(ast_sockaddr_stringify_addr(&sip_tls_desc.local_address), SIP_DOMAIN_AUTO, NULL); diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample index d90811968..f983a87db 100644 --- a/configs/samples/pjsip.conf.sample +++ b/configs/samples/pjsip.conf.sample @@ -608,8 +608,15 @@ ;direct_media_glare_mitigation=none ; Mitigation of direct media re INVITE ; glare (default: "none") ;direct_media_method=invite ; Direct Media method type (default: "invite") -;connected_line_method=invite ; Connected line method type (default: - ; "invite") +;connected_line_method=invite ; Connected line method type. + ; When set to "invite", check the remote's + ; Allow header and if UPDATE is allowed, send + ; UPDATE instead of INVITE to avoid SDP + ; renegotiation. If UPDATE is not Allowed, + ; send INVITE. + ; If set to "update", send UPDATE regardless + ; of what the remote Allows. + ; (default: "invite") ;direct_media=yes ; Determines whether media may flow directly between ; endpoints (default: "yes") ;disable_direct_media_on_nat=no ; Disable direct media session refreshes when @@ -1191,6 +1191,7 @@ PJPROJECT_LIB PBX_PJPROJECT PJPROJECT_DIR PJPROJECT_BUNDLED +PJPROJECT_CONFIGURE_OPTS AST_C_COMPILER_FAMILY AST_CLANG_BLOCKS AST_CLANG_BLOCKS_LIBS @@ -1327,7 +1328,6 @@ infodir docdir oldincludedir includedir -runstatedir localstatedir sharedstatedir sysconfdir @@ -1452,6 +1452,7 @@ CXX CXXFLAGS CCC CXXCPP +PJPROJECT_CONFIGURE_OPTS PKG_CONFIG PKG_CONFIG_PATH PKG_CONFIG_LIBDIR @@ -1507,7 +1508,6 @@ datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' -runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' @@ -1760,15 +1760,6 @@ do | -silent | --silent | --silen | --sile | --sil) silent=yes ;; - -runstatedir | --runstatedir | --runstatedi | --runstated \ - | --runstate | --runstat | --runsta | --runst | --runs \ - | --run | --ru | --r) - ac_prev=runstatedir ;; - -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ - | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ - | --run=* | --ru=* | --r=*) - runstatedir=$ac_optarg ;; - -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ @@ -1906,7 +1897,7 @@ fi for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir runstatedir + libdir localedir mandir do eval ac_val=\$$ac_var # Remove trailing slashes. @@ -2059,7 +2050,6 @@ Fine tuning of the installation directories: --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] - --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] @@ -2206,6 +2196,8 @@ Some influential environment variables: CXX C++ compiler command CXXFLAGS C++ compiler flags CXXCPP C++ preprocessor + PJPROJECT_CONFIGURE_OPTS + Additional configure options to pass to bundled pjproject PKG_CONFIG path to pkg-config utility PKG_CONFIG_PATH directories to add to pkg-config's search path @@ -9276,20 +9268,33 @@ $as_echo "configuring" >&6; } as_fn_error $? "cat is required to build bundled pjproject" "$LINENO" 5 fi + + this_host=$(./config.sub $(./config.guess)) + if test "$build" != "$this_host" ; then + PJPROJECT_CONFIGURE_OPTS+=" --build=$build" + fi + if test "$host" != "$this_host" ; then + PJPROJECT_CONFIGURE_OPTS+=" --host=$host" + fi + export TAR PATCH SED NM EXTERNALS_CACHE_DIR DOWNLOAD_TO_STDOUT DOWNLOAD_TIMEOUT DOWNLOAD MD5 CAT - ${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} EXTERNALS_CACHE_DIR=${EXTERNALS_CACHE_DIR} configure + export NOISY_BUILD + ${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} \ + PJPROJECT_CONFIGURE_OPTS="$PJPROJECT_CONFIGURE_OPTS" \ + EXTERNALS_CACHE_DIR="${EXTERNALS_CACHE_DIR}" \ + configure if test $? -ne 0 ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5 $as_echo "failed" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: Unable to configure ${PJPROJECT_DIR}" >&5 $as_echo "$as_me: Unable to configure ${PJPROJECT_DIR}" >&6;} - as_fn_error $? "Run \"${GNU_MAKE} -C ${PJPROJECT_DIR} NOISY_BUILD=yes configure\" to see error details." "$LINENO" 5 + as_fn_error $? "Re-run the ./configure command with 'NOISY_BUILD=yes' appended to see error details." "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for bundled pjproject" >&5 $as_echo_n "checking for bundled pjproject... " >&6; } - PJPROJECT_INCLUDE=$(${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} EXTERNALS_CACHE_DIR=${EXTERNALS_CACHE_DIR} echo_cflags) + PJPROJECT_INCLUDE=$(${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} PJPROJECT_CONFIGURE_OPTS="$PJPROJECT_CONFIGURE_OPTS" EXTERNALS_CACHE_DIR="${EXTERNALS_CACHE_DIR}" echo_cflags) PJPROJECT_CFLAGS="$PJPROJECT_INCLUDE" PBX_PJPROJECT=1 @@ -14668,7 +14673,7 @@ else We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -14714,7 +14719,7 @@ else We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -14738,7 +14743,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -14783,7 +14788,7 @@ else We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -14807,7 +14812,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h index e8f3d78b4..f9bdc508c 100644 --- a/include/asterisk/rtp_engine.h +++ b/include/asterisk/rtp_engine.h @@ -624,12 +624,13 @@ struct ast_rtp_glue { /*! * \brief Used to prevent two channels from remotely bridging audio rtp if the channel tech has a * reason for prohibiting it based on qualities that need to be compared from both channels. - * \note This function may be NULL for a given channel driver. This should be accounted for and if that is the case, function this is not used. + * \note This function may be NULL for a given channel driver. This should be accounted for and if that is the case, this function is not used. */ int (*allow_rtp_remote)(struct ast_channel *chan1, struct ast_rtp_instance *instance); /*! * \brief Callback for retrieving the RTP instance carrying video * \note This function increases the reference count on the returned RTP instance. + * \note This function may be NULL for a given channel driver. This should be accounted for and if that is the case, this function is not used. */ enum ast_rtp_glue_result (*get_vrtp_info)(struct ast_channel *chan, struct ast_rtp_instance **instance); /*! @@ -642,11 +643,15 @@ struct ast_rtp_glue { /*! * \brief Callback for retrieving the RTP instance carrying text * \note This function increases the reference count on the returned RTP instance. + * \note This function may be NULL for a given channel driver. This should be accounted for and if that is the case, this function is not used. */ enum ast_rtp_glue_result (*get_trtp_info)(struct ast_channel *chan, struct ast_rtp_instance **instance); /*! Callback for updating the destination that the remote side should send RTP to */ int (*update_peer)(struct ast_channel *chan, struct ast_rtp_instance *instance, struct ast_rtp_instance *vinstance, struct ast_rtp_instance *tinstance, const struct ast_format_cap *cap, int nat_active); - /*! Callback for retrieving codecs that the channel can do. Result returned in result_cap. */ + /*! + * \brief Callback for retrieving codecs that the channel can do. Result returned in result_cap. + * \note This function may be NULL for a given channel driver. This should be accounted for and if that is the case, this function is not used. + */ void (*get_codec)(struct ast_channel *chan, struct ast_format_cap *result_cap); /*! Linked list information */ AST_RWLIST_ENTRY(ast_rtp_glue) entry; diff --git a/main/channel.c b/main/channel.c index c5060b705..c6c035f39 100644 --- a/main/channel.c +++ b/main/channel.c @@ -3557,8 +3557,12 @@ int ast_waitfordigit_full(struct ast_channel *c, int timeout_ms, int audiofd, in } else if (rchan) { int res; struct ast_frame *f = ast_read(c); - if (!f) + + if (!f) { + ast_channel_clear_flag(c, AST_FLAG_END_DTMF_ONLY); + return -1; + } switch (f->frametype) { case AST_FRAME_DTMF_BEGIN: diff --git a/res/res_pjsip.c b/res/res_pjsip.c index 057ae3360..02d24e918 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -193,11 +193,18 @@ <description> <para>Method used when updating connected line information.</para> <enumlist> - <enum name="invite" /> + <enum name="invite"> + <para>When set to <literal>invite</literal>, check the remote's Allow header and + if UPDATE is allowed, send UPDATE instead of INVITE to avoid SDP + renegotiation. If UPDATE is not Allowed, send INVITE.</para> + </enum> <enum name="reinvite"> <para>Alias for the <literal>invite</literal> value.</para> </enum> - <enum name="update" /> + <enum name="update"> + <para>If set to <literal>update</literal>, send UPDATE regardless of what the remote + Allows. </para> + </enum> </enumlist> </description> </configOption> diff --git a/res/res_pjsip/pjsip_distributor.c b/res/res_pjsip/pjsip_distributor.c index dadde2577..3f245eea0 100644 --- a/res/res_pjsip/pjsip_distributor.c +++ b/res/res_pjsip/pjsip_distributor.c @@ -140,62 +140,189 @@ static struct ast_taskprocessor *find_request_serializer(pjsip_rx_data *rdata) /*! Dialog-specific information the distributor uses */ struct distributor_dialog_data { + /*! dialog_associations ao2 container key */ + pjsip_dialog *dlg; /*! Serializer to distribute tasks to for this dialog */ struct ast_taskprocessor *serializer; /*! Endpoint associated with this dialog */ struct ast_sip_endpoint *endpoint; }; +#define DIALOG_ASSOCIATIONS_BUCKETS 251 + +static struct ao2_container *dialog_associations; + /*! * \internal + * \brief Compute a hash value on an arbitrary buffer. + * \since 13.17.0 * - * \note Call this with the dialog locked + * \param[in] pos The buffer to add to the hash + * \param[in] len The buffer length to add to the hash + * \param[in] hash The hash value to add to + * + * \details + * This version of the function is for when you need to compute a + * hash of more than one buffer. + * + * This famous hash algorithm was written by Dan Bernstein and is + * commonly used. + * + * \sa http://www.cse.yorku.ca/~oz/hash.html */ -static struct distributor_dialog_data *distributor_dialog_data_alloc(pjsip_dialog *dlg) +static int buf_hash_add(const char *pos, size_t len, int hash) { - struct distributor_dialog_data *dist; + while (len--) { + hash = hash * 33 ^ *pos++; + } + + return hash; +} + +/*! + * \internal + * \brief Compute a hash value on an arbitrary buffer. + * \since 13.17.0 + * + * \param[in] pos The buffer to add to the hash + * \param[in] len The buffer length to add to the hash + * + * \details + * This version of the function is for when you need to compute a + * hash of more than one buffer. + * + * This famous hash algorithm was written by Dan Bernstein and is + * commonly used. + * + * \sa http://www.cse.yorku.ca/~oz/hash.html + */ +static int buf_hash(const char *pos, size_t len) +{ + return buf_hash_add(pos, len, 5381); +} - dist = PJ_POOL_ZALLOC_T(dlg->pool, struct distributor_dialog_data); - pjsip_dlg_set_mod_data(dlg, distributor_mod.id, dist); +static int dialog_associations_hash(const void *obj, int flags) +{ + const struct distributor_dialog_data *object; + union { + const pjsip_dialog *dlg; + const char buf[sizeof(pjsip_dialog *)]; + } key; - return dist; + switch (flags & OBJ_SEARCH_MASK) { + case OBJ_SEARCH_KEY: + key.dlg = obj; + break; + case OBJ_SEARCH_OBJECT: + object = obj; + key.dlg = object->dlg; + break; + default: + /* Hash can only work on something with a full key. */ + ast_assert(0); + return 0; + } + return ast_str_hash_restrict(buf_hash(key.buf, sizeof(key.buf))); +} + +static int dialog_associations_cmp(void *obj, void *arg, int flags) +{ + const struct distributor_dialog_data *object_left = obj; + const struct distributor_dialog_data *object_right = arg; + const pjsip_dialog *right_key = arg; + int cmp = 0; + + switch (flags & OBJ_SEARCH_MASK) { + case OBJ_SEARCH_OBJECT: + right_key = object_right->dlg; + /* Fall through */ + case OBJ_SEARCH_KEY: + if (object_left->dlg == right_key) { + cmp = CMP_MATCH; + } + break; + case OBJ_SEARCH_PARTIAL_KEY: + /* There is no such thing for this container. */ + ast_assert(0); + break; + default: + cmp = 0; + break; + } + return cmp; } void ast_sip_dialog_set_serializer(pjsip_dialog *dlg, struct ast_taskprocessor *serializer) { struct distributor_dialog_data *dist; - SCOPED_LOCK(lock, dlg, pjsip_dlg_inc_lock, pjsip_dlg_dec_lock); - dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id); + ao2_wrlock(dialog_associations); + dist = ao2_find(dialog_associations, dlg, OBJ_SEARCH_KEY | OBJ_NOLOCK); if (!dist) { - dist = distributor_dialog_data_alloc(dlg); + if (serializer) { + dist = ao2_alloc(sizeof(*dist), NULL); + if (dist) { + dist->dlg = dlg; + dist->serializer = serializer; + ao2_link_flags(dialog_associations, dist, OBJ_NOLOCK); + ao2_ref(dist, -1); + } + } + } else { + ao2_lock(dist); + dist->serializer = serializer; + if (!dist->serializer && !dist->endpoint) { + ao2_unlink_flags(dialog_associations, dist, OBJ_NOLOCK); + } + ao2_unlock(dist); + ao2_ref(dist, -1); } - dist->serializer = serializer; + ao2_unlock(dialog_associations); } void ast_sip_dialog_set_endpoint(pjsip_dialog *dlg, struct ast_sip_endpoint *endpoint) { struct distributor_dialog_data *dist; - SCOPED_LOCK(lock, dlg, pjsip_dlg_inc_lock, pjsip_dlg_dec_lock); - dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id); + ao2_wrlock(dialog_associations); + dist = ao2_find(dialog_associations, dlg, OBJ_SEARCH_KEY | OBJ_NOLOCK); if (!dist) { - dist = distributor_dialog_data_alloc(dlg); + if (endpoint) { + dist = ao2_alloc(sizeof(*dist), NULL); + if (dist) { + dist->dlg = dlg; + dist->endpoint = endpoint; + ao2_link_flags(dialog_associations, dist, OBJ_NOLOCK); + ao2_ref(dist, -1); + } + } + } else { + ao2_lock(dist); + dist->endpoint = endpoint; + if (!dist->serializer && !dist->endpoint) { + ao2_unlink_flags(dialog_associations, dist, OBJ_NOLOCK); + } + ao2_unlock(dist); + ao2_ref(dist, -1); } - dist->endpoint = endpoint; + ao2_unlock(dialog_associations); } struct ast_sip_endpoint *ast_sip_dialog_get_endpoint(pjsip_dialog *dlg) { struct distributor_dialog_data *dist; - SCOPED_LOCK(lock, dlg, pjsip_dlg_inc_lock, pjsip_dlg_dec_lock); + struct ast_sip_endpoint *endpoint; - dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id); - if (!dist || !dist->endpoint) { - return NULL; + dist = ao2_find(dialog_associations, dlg, OBJ_SEARCH_KEY); + if (dist) { + ao2_lock(dist); + endpoint = ao2_bump(dist->endpoint); + ao2_unlock(dist); + ao2_ref(dist, -1); + } else { + endpoint = NULL; } - ao2_ref(dist->endpoint, +1); - return dist->endpoint; + return endpoint; } static pjsip_dialog *find_dialog(pjsip_rx_data *rdata) @@ -227,7 +354,7 @@ static pjsip_dialog *find_dialog(pjsip_rx_data *rdata) pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_cancel_method) || rdata->msg_info.to->tag.slen != 0) { dlg = pjsip_ua_find_dialog(&rdata->msg_info.cid->id, local_tag, - remote_tag, PJ_TRUE); + remote_tag, PJ_FALSE); if (dlg) { return dlg; } @@ -265,11 +392,6 @@ static pjsip_dialog *find_dialog(pjsip_rx_data *rdata) pj_mutex_unlock(tsx->mutex); #endif - if (!dlg) { - return NULL; - } - - pjsip_dlg_inc_lock(dlg); return dlg; } @@ -292,16 +414,7 @@ static pjsip_dialog *find_dialog(pjsip_rx_data *rdata) */ static int pjstr_hash_add(pj_str_t *str, int hash) { - size_t len; - const char *pos; - - len = pj_strlen(str); - pos = pj_strbuf(str); - while (len--) { - hash = hash * 33 ^ *pos++; - } - - return hash; + return buf_hash_add(pj_strbuf(str), pj_strlen(str), hash); } /*! @@ -340,7 +453,7 @@ struct ast_taskprocessor *ast_sip_get_distributor_serializer(pjsip_rx_data *rdat /* Compute the hash from the SIP message call-id and remote-tag */ hash = pjstr_hash(&rdata->msg_info.cid->id); hash = pjstr_hash_add(remote_tag, hash); - hash = abs(hash); + hash = ast_str_hash_restrict(hash); serializer = ao2_bump(distributor_pool[hash % ARRAY_LEN(distributor_pool)]); if (serializer) { @@ -375,17 +488,18 @@ static pj_bool_t distributor(pjsip_rx_data *rdata) dlg = find_dialog(rdata); if (dlg) { - ast_debug(3, "Searching for serializer on dialog %s for %s\n", + ast_debug(3, "Searching for serializer associated with dialog %s for %s\n", dlg->obj_name, pjsip_rx_data_get_info(rdata)); - dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id); + dist = ao2_find(dialog_associations, dlg, OBJ_SEARCH_KEY); if (dist) { + ao2_lock(dist); serializer = ao2_bump(dist->serializer); + ao2_unlock(dist); if (serializer) { - ast_debug(3, "Found serializer %s on dialog %s\n", + ast_debug(3, "Found serializer %s associated with dialog %s\n", ast_taskprocessor_name(serializer), dlg->obj_name); } } - pjsip_dlg_dec_lock(dlg); } if (serializer) { @@ -407,6 +521,7 @@ static pj_bool_t distributor(pjsip_rx_data *rdata) /* We have a BYE or CANCEL request without a serializer. */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, PJSIP_SC_CALL_TSX_DOES_NOT_EXIST, NULL, NULL, NULL); + ao2_cleanup(dist); return PJ_TRUE; } else { if (ast_taskprocessor_alert_get()) { @@ -421,6 +536,7 @@ static pj_bool_t distributor(pjsip_rx_data *rdata) */ ast_debug(3, "Taskprocessor overload alert: Ignoring '%s'.\n", pjsip_rx_data_get_info(rdata)); + ao2_cleanup(dist); return PJ_TRUE; } @@ -428,10 +544,17 @@ static pj_bool_t distributor(pjsip_rx_data *rdata) serializer = ast_sip_get_distributor_serializer(rdata); } - pjsip_rx_data_clone(rdata, 0, &clone); + if (pjsip_rx_data_clone(rdata, 0, &clone) != PJ_SUCCESS) { + ast_taskprocessor_unreference(serializer); + ao2_cleanup(dist); + return PJ_TRUE; + } if (dist) { + ao2_lock(dist); clone->endpt_info.mod_data[endpoint_mod.id] = ao2_bump(dist->endpoint); + ao2_unlock(dist); + ao2_cleanup(dist); } if (ast_sip_push_task(serializer, distribute, clone)) { @@ -827,7 +950,7 @@ static int suspects_compare(void *obj, void *arg, int flags) /* Fall through */ case OBJ_SEARCH_KEY: if (strcmp(object_left->src_name, right_key) == 0) { - cmp = CMP_MATCH | CMP_STOP; + cmp = CMP_MATCH; } break; case OBJ_SEARCH_PARTIAL_KEY: @@ -842,15 +965,25 @@ static int suspects_compare(void *obj, void *arg, int flags) return cmp; } -static int suspects_hash(const void *obj, int flags) { - const struct unidentified_request *object_left = obj; +static int suspects_hash(const void *obj, int flags) +{ + const struct unidentified_request *object; + const char *key; - if (flags & OBJ_SEARCH_OBJECT) { - return ast_str_hash(object_left->src_name); - } else if (flags & OBJ_SEARCH_KEY) { - return ast_str_hash(obj); + switch (flags & OBJ_SEARCH_MASK) { + case OBJ_SEARCH_KEY: + key = obj; + break; + case OBJ_SEARCH_OBJECT: + object = obj; + key = object->src_name; + break; + default: + /* Hash can only work on something with a full key. */ + ast_assert(0); + return 0; } - return -1; + return ast_str_hash(key); } static struct ao2_container *cli_unid_get_container(const char *regex) @@ -1068,6 +1201,14 @@ int ast_sip_initialize_distributor(void) return -1; } + dialog_associations = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_RWLOCK, 0, + DIALOG_ASSOCIATIONS_BUCKETS, dialog_associations_hash, NULL, + dialog_associations_cmp); + if (!dialog_associations) { + ast_sip_destroy_distributor(); + return -1; + } + if (distributor_pool_setup()) { ast_sip_destroy_distributor(); return -1; @@ -1146,5 +1287,6 @@ void ast_sip_destroy_distributor(void) distributor_pool_shutdown(); + ao2_cleanup(dialog_associations); ao2_cleanup(unidentified_requests); } diff --git a/third-party/configure.m4 b/third-party/configure.m4 index 635446638..55b72daf9 100644 --- a/third-party/configure.m4 +++ b/third-party/configure.m4 @@ -1,4 +1,7 @@ - +# +# If this file is changed, be sure to run ASTTOPDIR/bootstrap.sh +# before committing. +# AC_DEFUN([THIRD_PARTY_CONFIGURE], [ diff --git a/third-party/pjproject/Makefile.rules b/third-party/pjproject/Makefile.rules index 3f99c8a8f..acd766218 100644 --- a/third-party/pjproject/Makefile.rules +++ b/third-party/pjproject/Makefile.rules @@ -1,8 +1,11 @@ PJPROJECT_URL ?= https://raw.githubusercontent.com/asterisk/third-party/master/pjproject/$(PJPROJECT_VERSION) +# PJPROJECT_CONFIGURE_OPTS could come from the command line or could be +# set/modified by configure.m4 if the build or host tuples aren't the same +# as the current build environment (cross-compile). # Even though we're not installing pjproject, we're setting prefix to /opt/pjproject to be safe -PJPROJECT_CONFIG_OPTS = --prefix=/opt/pjproject \ +PJPROJECT_CONFIG_OPTS = $(PJPROJECT_CONFIGURE_OPTS) --prefix=/opt/pjproject \ --disable-speex-codec \ --disable-speex-aec \ --disable-speex-aec \ diff --git a/third-party/pjproject/configure.m4 b/third-party/pjproject/configure.m4 index a5e9fca60..709a706a1 100644 --- a/third-party/pjproject/configure.m4 +++ b/third-party/pjproject/configure.m4 @@ -1,3 +1,8 @@ +# +# If this file is changed, be sure to run ASTTOPDIR/bootstrap.sh +# before committing. +# + AC_DEFUN([_PJPROJECT_CONFIGURE], [ if test "${ac_mandatory_list#*PJPROJECT*}" != "$ac_mandatory_list" ; then @@ -35,17 +40,30 @@ AC_DEFUN([_PJPROJECT_CONFIGURE], AC_MSG_ERROR(cat is required to build bundled pjproject) fi + AC_ARG_VAR([PJPROJECT_CONFIGURE_OPTS],[Additional configure options to pass to bundled pjproject]) + this_host=$(./config.sub $(./config.guess)) + if test "$build" != "$this_host" ; then + PJPROJECT_CONFIGURE_OPTS+=" --build=$build" + fi + if test "$host" != "$this_host" ; then + PJPROJECT_CONFIGURE_OPTS+=" --host=$host" + fi + export TAR PATCH SED NM EXTERNALS_CACHE_DIR DOWNLOAD_TO_STDOUT DOWNLOAD_TIMEOUT DOWNLOAD MD5 CAT - ${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} EXTERNALS_CACHE_DIR=${EXTERNALS_CACHE_DIR} configure + export NOISY_BUILD + ${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} \ + PJPROJECT_CONFIGURE_OPTS="$PJPROJECT_CONFIGURE_OPTS" \ + EXTERNALS_CACHE_DIR="${EXTERNALS_CACHE_DIR}" \ + configure if test $? -ne 0 ; then AC_MSG_RESULT(failed) AC_MSG_NOTICE(Unable to configure ${PJPROJECT_DIR}) - AC_MSG_ERROR(Run "${GNU_MAKE} -C ${PJPROJECT_DIR} NOISY_BUILD=yes configure" to see error details.) + AC_MSG_ERROR(Re-run the ./configure command with 'NOISY_BUILD=yes' appended to see error details.) fi AC_MSG_CHECKING(for bundled pjproject) - PJPROJECT_INCLUDE=$(${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} EXTERNALS_CACHE_DIR=${EXTERNALS_CACHE_DIR} echo_cflags) + PJPROJECT_INCLUDE=$(${GNU_MAKE} --quiet --no-print-directory -C ${PJPROJECT_DIR} PJPROJECT_CONFIGURE_OPTS="$PJPROJECT_CONFIGURE_OPTS" EXTERNALS_CACHE_DIR="${EXTERNALS_CACHE_DIR}" echo_cflags) PJPROJECT_CFLAGS="$PJPROJECT_INCLUDE" PBX_PJPROJECT=1 diff --git a/third-party/pjproject/patches/0070-Set-PJSIP_INV_SUPPORT_UPDATE-correctly-in-pjsip_inv_.patch b/third-party/pjproject/patches/0070-Set-PJSIP_INV_SUPPORT_UPDATE-correctly-in-pjsip_inv_.patch new file mode 100644 index 000000000..9238e3ec9 --- /dev/null +++ b/third-party/pjproject/patches/0070-Set-PJSIP_INV_SUPPORT_UPDATE-correctly-in-pjsip_inv_.patch @@ -0,0 +1,29 @@ +From 1193681959816effa121c4470748d5faa3a59272 Mon Sep 17 00:00:00 2001 +From: George Joseph <gjoseph@digium.com> +Date: Thu, 29 Jun 2017 13:42:10 -0600 +Subject: [PATCH] Set PJSIP_INV_SUPPORT_UPDATE correctly in + pjsip_inv_verify_request3 + +pjsip_inv_verify_request3 was setting rem_options when UPDATE was +detected in the Allow header. That's just an internal variable and +doesn't go anywhere. It's '*options' that needs to be set. +--- + pjsip/src/pjsip-ua/sip_inv.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/pjsip/src/pjsip-ua/sip_inv.c b/pjsip/src/pjsip-ua/sip_inv.c +index fbc8ebe..6db7e6b 100644 +--- a/pjsip/src/pjsip-ua/sip_inv.c ++++ b/pjsip/src/pjsip-ua/sip_inv.c +@@ -1237,7 +1237,7 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request3(pjsip_rx_data *rdata, + + if (i != allow->count) { + /* UPDATE is present in Allow */ +- rem_option |= PJSIP_INV_SUPPORT_UPDATE; ++ *options |= PJSIP_INV_SUPPORT_UPDATE; + } + + } +-- +2.9.4 + |