[Asterisk-code-review] chan_pjsip: Add support for passing hold and unhold requests through. (...asterisk[13])

Torrey Searle asteriskteam at digium.com
Wed Sep 18 07:38:47 CDT 2019


Hello Joshua Colp,

I'd like you to do a code review. Please visit

    https://gerrit.asterisk.org/c/asterisk/+/12891

to review the following change.


Change subject: chan_pjsip: Add support for passing hold and unhold requests through.
......................................................................

chan_pjsip: Add support for passing hold and unhold requests through.

This change adds an option, moh_passthrough, that when enabled will pass
hold and unhold requests through using a SIP re-invite. When placing on
hold a re-invite with sendonly will be sent and when taking off hold a
re-invite with sendrecv will be sent. This allows remote servers to handle
the musiconhold instead of the local Asterisk instance being responsible.

Review: https://reviewboard.asterisk.org/r/4103/

Change-Id: Ib6294e906e577e1a4245cb1f058d3976ff484c52
---
M channels/chan_pjsip.c
M channels/pjsip/dialplan_functions.c
M configs/samples/pjsip.conf.sample
A contrib/ast-db-manage/config/versions/339e1dfa644d_add_moh_passthrough_option_to_pjsip.py
M include/asterisk/res_pjsip.h
M include/asterisk/res_pjsip_session.h
M res/res_pjsip.c
M res/res_pjsip/pjsip_configuration.c
M res/res_pjsip_sdp_rtp.c
9 files changed, 107 insertions(+), 14 deletions(-)



  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/91/12891/1

diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index b0d5fda..2a243b6 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -1349,6 +1349,39 @@
 	return 0;
 }
 
+/*! \brief Callback which changes the value of locally held on the media stream */
+static int local_hold_set_state(void *obj, void *arg, int flags)
+{
+	struct ast_sip_session_media *session_media = obj;
+	unsigned int *held = arg;
+
+	session_media->locally_held = *held;
+
+	return 0;
+}
+
+/*! \brief Update local hold state and send a re-INVITE with the new SDP */
+static int remote_send_hold_refresh(struct ast_sip_session *session, unsigned int held)
+{
+	ao2_callback(session->media, OBJ_NODATA, local_hold_set_state, &held);
+	ast_sip_session_refresh(session, NULL, NULL, NULL, AST_SIP_SESSION_REFRESH_METHOD_INVITE, 1);
+	ao2_ref(session, -1);
+
+	return 0;
+}
+
+/*! \brief Update local hold state to be held */
+static int remote_send_hold(void *data)
+{
+	return remote_send_hold_refresh(data, 1);
+}
+
+/*! \brief Update local hold state to be unheld */
+static int remote_send_unhold(void *data)
+{
+	return remote_send_hold_refresh(data, 0);
+}
+
 /*! \brief Function called by core to ask the channel to indicate some sort of condition */
 static int chan_pjsip_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
 {
@@ -1493,7 +1526,15 @@
 		device_buf = alloca(device_buf_size);
 		ast_channel_get_device_name(ast, device_buf, device_buf_size);
 		ast_devstate_changed_literal(AST_DEVICE_ONHOLD, 1, device_buf);
-		ast_moh_start(ast, data, NULL);
+		if (!channel->session->endpoint->moh_passthrough) {
+			ast_moh_start(ast, data, NULL);
+		} else {
+			if (ast_sip_push_task(channel->session->serializer, remote_send_hold, ao2_bump(channel->session))) {
+				ast_log(LOG_WARNING, "Could not queue task to remotely put session '%s' on hold with endpoint '%s'\n",
+					ast_sorcery_object_get_id(channel->session), ast_sorcery_object_get_id(channel->session->endpoint));
+				ao2_ref(channel->session, -1);
+			}
+		}
 		break;
 	case AST_CONTROL_UNHOLD:
 		chan_pjsip_remove_hold(ast_channel_uniqueid(ast));
@@ -1501,7 +1542,15 @@
 		device_buf = alloca(device_buf_size);
 		ast_channel_get_device_name(ast, device_buf, device_buf_size);
 		ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, 1, device_buf);
-		ast_moh_stop(ast);
+		if (!channel->session->endpoint->moh_passthrough) {
+			ast_moh_stop(ast);
+		} else {
+			if (ast_sip_push_task(channel->session->serializer, remote_send_unhold, ao2_bump(channel->session))) {
+				ast_log(LOG_WARNING, "Could not queue task to remotely take session '%s' off hold with endpoint '%s'\n",
+					ast_sorcery_object_get_id(channel->session), ast_sorcery_object_get_id(channel->session->endpoint));
+				ao2_ref(channel->session, -1);
+			}
+		}
 		break;
 	case AST_CONTROL_SRCUPDATE:
 		break;
diff --git a/channels/pjsip/dialplan_functions.c b/channels/pjsip/dialplan_functions.c
index 76f351f..6cdc721 100644
--- a/channels/pjsip/dialplan_functions.c
+++ b/channels/pjsip/dialplan_functions.c
@@ -592,7 +592,7 @@
 			snprintf(buf, buflen, "%d", 0);
 		}
 	} else if (!strcmp(type, "hold")) {
-		snprintf(buf, buflen, "%d", media->held ? 1 : 0);
+		snprintf(buf, buflen, "%d", media->remotely_held ? 1 : 0);
 	} else {
 		ast_log(AST_LOG_WARNING, "Unknown type field '%s' specified for 'rtp' information\n", type);
 		return -1;
diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample
index bbd0317..9e6365e 100644
--- a/configs/samples/pjsip.conf.sample
+++ b/configs/samples/pjsip.conf.sample
@@ -655,6 +655,8 @@
                       ; An MWI subscribe will replace unsoliticed NOTIFYs
                       ; (default: "no")
 ;moh_suggest=default    ; Default Music On Hold class (default: "default")
+;moh_passthrough=yes    ; Pass Music On Hold through using SIP re-invites with sendonly
+                        ; when placing on hold and sendrecv when taking off hold
 ;outbound_auth= ; Authentication object used for outbound requests (default:
                 ; "")
 ;outbound_proxy=        ; Proxy through which to send requests, a full SIP URI
diff --git a/contrib/ast-db-manage/config/versions/339e1dfa644d_add_moh_passthrough_option_to_pjsip.py b/contrib/ast-db-manage/config/versions/339e1dfa644d_add_moh_passthrough_option_to_pjsip.py
new file mode 100644
index 0000000..d3ee2e5
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/339e1dfa644d_add_moh_passthrough_option_to_pjsip.py
@@ -0,0 +1,30 @@
+"""Add moh_passthrough option to pjsip
+
+Revision ID: 339e1dfa644d
+Revises: 1443687dda65
+Create Date: 2014-10-21 14:55:34.197448
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '339e1dfa644d'
+down_revision = '1443687dda65'
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects.postgresql import ENUM
+
+YESNO_NAME = 'yesno_values'
+YESNO_VALUES = ['yes', 'no']
+
+def upgrade():
+    ############################# Enums ##############################
+
+    # yesno_values have already been created, so use postgres enum object
+    # type to get around "already created" issue - works okay with mysql
+    yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False)
+
+    op.add_column('ps_endpoints', sa.Column('moh_passthrough', yesno_values))
+
+def downgrade():
+    op.drop_column('ps_endpoints', 'moh_passthrough')
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index 5e8dd3a..54977d1 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -768,6 +768,7 @@
 	struct ast_variable *channel_vars;
 	/*! Whether to place a 'user=phone' parameter into the request URI if user is a number */
 	unsigned int usereqphone;
+<<<<<<< HEAD
 	/*! Do we send messages for connected line updates for unanswered incoming calls immediately to this endpoint? */
 	unsigned int rpid_immediate;
 	/*! Access control list */
@@ -802,6 +803,8 @@
 	unsigned int send_connected_line;
 	/*! Ignore 183 if no SDP is present */
 	unsigned int ignore_183_without_sdp;
+	/*! Whether to pass through hold and unhold using re-invites with recvonly and sendrecv */
+	unsigned int moh_passthrough;
 };
 
 /*! URI parameter for symmetric transport */
diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h
index e2d4fcd..51d75c6 100644
--- a/include/asterisk/res_pjsip_session.h
+++ b/include/asterisk/res_pjsip_session.h
@@ -81,8 +81,10 @@
 	int keepalive_sched_id;
 	/*! \brief Scheduler ID for RTP timeout */
 	int timeout_sched_id;
-	/*! \brief Stream is on hold */
-	unsigned int held:1;
+	/*! \brief Stream is on hold by remote side */
+	unsigned int remotely_held:1;
+	/*! \brief Stream is on hold by local side */
+	unsigned int locally_held:1;
 	/*! \brief Does remote support rtcp_mux */
 	unsigned int remote_rtcp_mux:1;
 	/*! \brief Does remote support ice */
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index c41b545..ec159d6 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -744,6 +744,9 @@
 				<configOption name="user_eq_phone" default="no">
 					<synopsis>Determines whether a user=phone parameter is placed into the request URI if the user is determined to be a phone number</synopsis>
 				</configOption>
+				<configOption name="moh_passthrough" default="no">
+					<synopsis>Determines whether hold and unhold will be passed through using re-INVITEs with recvonly and sendrecv to the remote side</synopsis>
+				</configOption>
 				<configOption name="sdp_owner" default="-">
 					<synopsis>String placed as the username portion of an SDP origin (o=) line.</synopsis>
 				</configOption>
@@ -2286,6 +2289,9 @@
 				<parameter name="UserEqPhone">
 					<para><xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='endpoint']/configOption[@name='user_eq_phone']/synopsis/node())"/></para>
 				</parameter>
+				<parameter name="MohPassthrough">
+					<para><xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='endpoint']/configOption[@name='moh_passthrough']/synopsis/node())"/></para>
+				</parameter>
 				<parameter name="SdpOwner">
 					<para><xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='endpoint']/configOption[@name='sdp_owner']/synopsis/node())"/></para>
 				</parameter>
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index b5525be..db53592 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -1858,6 +1858,7 @@
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "record_off_feature", "automixmon", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, info.recording.offfeature));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "allow_transfer", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, allowtransfer));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "user_eq_phone", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, usereqphone));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "moh_passthrough", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, moh_passthrough));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "sdp_owner", "-", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, media.sdpowner));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "sdp_session", "Asterisk", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, media.sdpsession));
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "tos_audio", "0", tos_handler, tos_audio_to_str, NULL, 0, 0);
diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c
index 76d47a3..b3be455 100644
--- a/res/res_pjsip_sdp_rtp.c
+++ b/res/res_pjsip_sdp_rtp.c
@@ -1184,6 +1184,7 @@
 	static const pj_str_t STR_IP4 = { "IP4", 3};
 	static const pj_str_t STR_IP6 = { "IP6", 3};
 	static const pj_str_t STR_SENDRECV = { "sendrecv", 8 };
+	static const pj_str_t STR_SENDONLY = { "sendonly", 8 };
 	pjmedia_sdp_media *media;
 	const char *hostip = NULL;
 	struct ast_sockaddr addr;
@@ -1369,7 +1370,7 @@
 
 	/* Add the sendrecv attribute - we purposely don't keep track because pjmedia-sdp will automatically change our offer for us */
 	attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
-	attr->name = STR_SENDRECV;
+	attr->name = !session_media->locally_held ? STR_SENDRECV : STR_SENDONLY;
 	media->attr[media->attr_count++] = attr;
 
 	/* If we've got rtcp-mux enabled, add it unless we received an offer without it */
@@ -1469,20 +1470,19 @@
 
 	if (ast_sockaddr_isnull(addrs) ||
 		ast_sockaddr_is_any(addrs) ||
-		pjmedia_sdp_media_find_attr2(remote_stream, "sendonly", NULL) ||
-		pjmedia_sdp_media_find_attr2(remote_stream, "inactive", NULL)) {
-		if (!session_media->held) {
+		pjmedia_sdp_media_find_attr2(remote_stream, "sendonly", NULL)) {
+		if (!session_media->remotely_held) {
 			/* The remote side has put us on hold */
 			ast_queue_hold(session->channel, session->endpoint->mohsuggest);
 			ast_rtp_instance_stop(session_media->rtp);
 			ast_queue_frame(session->channel, &ast_null_frame);
-			session_media->held = 1;
+			session_media->remotely_held = 1;
 		}
-	} else if (session_media->held) {
+	} else if (session_media->remotely_held) {
 		/* The remote side has taken us off hold */
 		ast_queue_unhold(session->channel);
 		ast_queue_frame(session->channel, &ast_null_frame);
-		session_media->held = 0;
+		session_media->remotely_held = 0;
 	} else if ((pjmedia_sdp_neg_was_answer_remote(session->inv_session->neg) == PJ_FALSE)
 		&& (session->inv_session->state == PJSIP_INV_STATE_CONFIRMED)) {
 		ast_queue_control(session->channel, AST_CONTROL_UPDATE_RTP_PEER);
@@ -1513,9 +1513,9 @@
 	 * instance itself.
 	 */
 	ast_rtp_instance_set_timeout(session_media->rtp, 0);
-	if (session->endpoint->media.rtp.timeout && !session_media->held) {
+	if (session->endpoint->media.rtp.timeout && !session_media->remotely_held) {
 		ast_rtp_instance_set_timeout(session_media->rtp, session->endpoint->media.rtp.timeout);
-	} else if (session->endpoint->media.rtp.timeout_hold && session_media->held) {
+	} else if (session->endpoint->media.rtp.timeout_hold && session_media->remotely_held) {
 		ast_rtp_instance_set_timeout(session_media->rtp, session->endpoint->media.rtp.timeout_hold);
 	}
 

-- 
To view, visit https://gerrit.asterisk.org/c/asterisk/+/12891
To unsubscribe, or for help writing mail filters, visit https://gerrit.asterisk.org/settings

Gerrit-Project: asterisk
Gerrit-Branch: 13
Gerrit-Change-Id: Ib6294e906e577e1a4245cb1f058d3976ff484c52
Gerrit-Change-Number: 12891
Gerrit-PatchSet: 1
Gerrit-Owner: Torrey Searle <tsearle at gmail.com>
Gerrit-Reviewer: Joshua Colp <jcolp at digium.com>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20190918/737aed44/attachment-0001.html>


More information about the asterisk-code-review mailing list