summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES8
-rw-r--r--bridges/bridge_native_rtp.c645
-rw-r--r--channels/chan_pjsip.c3
-rw-r--r--channels/chan_sip.c6
-rw-r--r--configs/samples/pjsip.conf.sample11
-rwxr-xr-xconfigure47
-rw-r--r--include/asterisk/rtp_engine.h9
-rw-r--r--main/channel.c6
-rw-r--r--res/res_pjsip.c11
-rw-r--r--res/res_pjsip/pjsip_distributor.c242
-rw-r--r--third-party/configure.m45
-rw-r--r--third-party/pjproject/Makefile.rules5
-rw-r--r--third-party/pjproject/configure.m424
-rw-r--r--third-party/pjproject/patches/0070-Set-PJSIP_INV_SUPPORT_UPDATE-correctly-in-pjsip_inv_.patch29
14 files changed, 826 insertions, 225 deletions
diff --git a/CHANGES b/CHANGES
index 98b4aa110..c3a2d0ab4 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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
diff --git a/configure b/configure
index 55b02250a..c5663576b 100755
--- a/configure
+++ b/configure
@@ -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
+