summaryrefslogtreecommitdiff
path: root/channels
diff options
context:
space:
mode:
authorMatt Jordan <mjordan@digium.com>2016-08-07 09:58:59 -0500
committerMatt Jordan <mjordan@digium.com>2016-08-10 11:20:11 -0500
commit5f815f9dbaf78680fd7b21af27ed7fde900264fa (patch)
treee796124ce4a38d7422d97d3f045cb3f44d515561 /channels
parent5a5b949333ca9d5f2befc2c07fb5f376dbd05ec0 (diff)
channels/chan_pjsip: Add PJSIP_SEND_SESSION_REFRESH
This patch adds a new PJSIP specific dialplan function, PJSIP_SEND_SESSION_REFRESH. When invoked on a PJSIP channel, the media session will be refreshed via either an UPDATE or re-INVITE request. When used in conjunction with the PJSIP_MEDIA_OFFER dialplan function, the formats in use on a PJSIP channel can be re-negotiated and changed dynamically after call setup. ASTERISK-26277 #close Change-Id: Ib98fe09ba889aafe26d58d32f0fd1323f8fd9b1b
Diffstat (limited to 'channels')
-rw-r--r--channels/chan_pjsip.c12
-rw-r--r--channels/pjsip/dialplan_functions.c116
-rw-r--r--channels/pjsip/include/dialplan_functions.h12
3 files changed, 139 insertions, 1 deletions
diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index 98b4cf81c..82f716f08 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -2384,6 +2384,11 @@ static struct ast_custom_function media_offer_function = {
.write = pjsip_acf_media_offer_write
};
+static struct ast_custom_function session_refresh_function = {
+ .name = "PJSIP_SEND_SESSION_REFRESH",
+ .write = pjsip_acf_session_refresh_write,
+};
+
/*!
* \brief Load the module
*
@@ -2423,6 +2428,11 @@ static int load_module(void)
goto end;
}
+ if (ast_custom_function_register(&session_refresh_function)) {
+ ast_log(LOG_WARNING, "Unable to register PJSIP_SEND_SESSION_REFRESH dialplan function\n");
+ goto end;
+ }
+
if (ast_sip_session_register_supplement(&chan_pjsip_supplement)) {
ast_log(LOG_ERROR, "Unable to register PJSIP supplement\n");
goto end;
@@ -2479,6 +2489,7 @@ end:
pjsip_uids_onhold = NULL;
ast_custom_function_unregister(&media_offer_function);
ast_custom_function_unregister(&chan_pjsip_dial_contacts_function);
+ ast_custom_function_unregister(&session_refresh_function);
ast_channel_unregister(&chan_pjsip_tech);
ast_rtp_glue_unregister(&chan_pjsip_rtp_glue);
@@ -2500,6 +2511,7 @@ static int unload_module(void)
ast_custom_function_unregister(&media_offer_function);
ast_custom_function_unregister(&chan_pjsip_dial_contacts_function);
+ ast_custom_function_unregister(&session_refresh_function);
ast_channel_unregister(&chan_pjsip_tech);
ao2_ref(chan_pjsip_tech.capabilities, -1);
diff --git a/channels/pjsip/dialplan_functions.c b/channels/pjsip/dialplan_functions.c
index b5665039a..7962a7566 100644
--- a/channels/pjsip/dialplan_functions.c
+++ b/channels/pjsip/dialplan_functions.c
@@ -59,8 +59,55 @@
</parameter>
</syntax>
<description>
- <para>Returns the codecs offered based upon the media choice</para>
+ <para>When read, returns the codecs offered based upon the media choice.</para>
+ <para>When written, sets the codecs to offer when an outbound dial attempt is made,
+ or when a session refresh is sent using <replaceable>PJSIP_SEND_SESSION_REFRESH</replaceable>.
+ </para>
</description>
+ <see-also>
+ <ref type="function">PJSIP_SEND_SESSION_REFRESH</ref>
+ </see-also>
+</function>
+<function name="PJSIP_SEND_SESSION_REFRESH" language="en_US">
+ <synopsis>
+ W/O: Initiate a session refresh via an UPDATE or re-INVITE on an established media session
+ </synopsis>
+ <syntax>
+ <parameter name="update_type" required="false">
+ <para>The type of update to send. Default is <literal>invite</literal>.</para>
+ <enumlist>
+ <enum name="invite">
+ <para>Send the session refresh as a re-INVITE.</para>
+ </enum>
+ <enum name="update">
+ <para>Send the session refresh as an UPDATE.</para>
+ </enum>
+ </enumlist>
+ </parameter>
+ </syntax>
+ <description>
+ <para>This function will cause the PJSIP stack to immediately refresh
+ the media session for the channel. This will be done using either a
+ re-INVITE (default) or an UPDATE request.
+ </para>
+ <para>This is most useful when combined with the <replaceable>PJSIP_MEDIA_OFFER</replaceable>
+ dialplan function, as it allows the formats in use on a channel to be
+ re-negotiated after call setup.</para>
+ <warning>
+ <para>The formats the endpoint supports are <emphasis>not</emphasis>
+ checked or enforced by this function. Using this function to offer
+ formats not supported by the endpoint <emphasis>may</emphasis> result
+ in a loss of media.</para>
+ </warning>
+ <example title="Re-negotiate format to g722">
+ ; Within some existing extension on an answered channel
+ same => n,Set(PJSIP_MEDIA_OFFER(audio)=!all,g722)
+ same => n,Set(PJSIP_SEND_SESSION_REFRESH()=invite)
+ </example>
+ </description>
+ <see-also>
+ <ref type="function">PJSIP_MEDIA_OFFER</ref>
+ </see-also>
</function>
<info name="PJSIPCHANNEL" language="en_US" tech="PJSIP">
<enumlist>
@@ -961,3 +1008,70 @@ int pjsip_acf_media_offer_write(struct ast_channel *chan, const char *cmd, char
return ast_sip_push_task_synchronous(channel->session->serializer, media_offer_write_av, &mdata);
}
+
+struct refresh_data {
+ struct ast_sip_session *session;
+ enum ast_sip_session_refresh_method method;
+};
+
+static int sip_session_response_cb(struct ast_sip_session *session, pjsip_rx_data *rdata)
+{
+ struct ast_format *fmt;
+
+ if (!session->channel) {
+ /* Egads! */
+ return 0;
+ }
+
+ fmt = ast_format_cap_get_best_by_type(ast_channel_nativeformats(session->channel), AST_MEDIA_TYPE_AUDIO);
+ if (!fmt) {
+ /* No format? That's weird. */
+ return 0;
+ }
+ ast_channel_set_writeformat(session->channel, fmt);
+ ast_channel_set_rawwriteformat(session->channel, fmt);
+ ast_channel_set_readformat(session->channel, fmt);
+ ast_channel_set_rawreadformat(session->channel, fmt);
+ ao2_ref(fmt, -1);
+
+ return 0;
+}
+
+static int refresh_write_cb(void *obj)
+{
+ struct refresh_data *data = obj;
+
+ ast_sip_session_refresh(data->session, NULL, NULL,
+ sip_session_response_cb, data->method, 1);
+
+ return 0;
+}
+
+int pjsip_acf_session_refresh_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+ struct ast_sip_channel_pvt *channel;
+ struct refresh_data rdata = {
+ .method = AST_SIP_SESSION_REFRESH_METHOD_INVITE,
+ };
+
+ if (!chan) {
+ ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
+ return -1;
+ }
+
+ if (strcmp(ast_channel_tech(chan)->type, "PJSIP")) {
+ ast_log(LOG_WARNING, "Cannot call %s on a non-PJSIP channel\n", cmd);
+ return -1;
+ }
+
+ channel = ast_channel_tech_pvt(chan);
+ rdata.session = channel->session;
+
+ if (!strcmp(value, "invite")) {
+ rdata.method = AST_SIP_SESSION_REFRESH_METHOD_INVITE;
+ } else if (!strcmp(value, "update")) {
+ rdata.method = AST_SIP_SESSION_REFRESH_METHOD_UPDATE;
+ }
+
+ return ast_sip_push_task_synchronous(channel->session->serializer, refresh_write_cb, &rdata);
+}
diff --git a/channels/pjsip/include/dialplan_functions.h b/channels/pjsip/include/dialplan_functions.h
index cbc06f076..8b80bfa74 100644
--- a/channels/pjsip/include/dialplan_functions.h
+++ b/channels/pjsip/include/dialplan_functions.h
@@ -61,6 +61,18 @@ int pjsip_acf_media_offer_write(struct ast_channel *chan, const char *cmd, char
int pjsip_acf_media_offer_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
/*!
+ * \brief PJSIP_SEND_SESSION_REFRESH function write callback
+ * \param chan The channel the function is called on
+ * \param cmd the Name of the function
+ * \param data Arguments passed to the function
+ * \param value Value to be set by the function
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int pjsip_acf_session_refresh_write(struct ast_channel *chan, const char *cmd, char *data, const char *value);
+
+/*!
* \brief PJSIP_DIAL_CONTACTS function read callback
* \param chan The channel the function is called on
* \param cmd The name of the function