channels/chan_pjsip: Add PJSIP_SEND_SESSION_REFRESH
authorMatt Jordan <mjordan@digium.com>
Sun, 7 Aug 2016 14:58:59 +0000 (09:58 -0500)
committerMatt Jordan <mjordan@digium.com>
Wed, 10 Aug 2016 16:30:01 +0000 (11:30 -0500)
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
(cherry picked from commit eec60dd77394f0519895fc6abce3a6f90f6470f1)

CHANGES
channels/chan_pjsip.c
channels/pjsip/dialplan_functions.c
channels/pjsip/include/dialplan_functions.h

diff --git a/CHANGES b/CHANGES
index b353e96..ee74625 100644 (file)
--- a/CHANGES
+++ b/CHANGES
 --- Functionality changes from Asterisk 14 to Asterisk 15 --------------------
 ------------------------------------------------------------------------------
 
+
+------------------------------------------------------------------------------
+--- Functionality changes from Asterisk 14.0.0 to Asterisk 14.1.0 ----------
+------------------------------------------------------------------------------
+
+app_voicemail
+------------------
+ * Added "tps_queue_high" and "tps_queue_low" options.
+   The options can modify the taskprocessor alert levels for this module.
+   Additional information can be found in the sample configuration file at
+   config/samples/voicemail.conf.sample.
+
+res_pjsip_mwi
+------------------
+ * Added "mwi_tps_queue_high" and "mwi_tps_queue_low" global configuration
+   options to tune taskprocessor alert levels.
+
+ * Added "mwi_disable_initial_unsolicited" global configuration option
+   to disable sending unsolicited MWI to all endpoints on startup.
+   Additional information can be found in the sample configuration file at
+   config/samples/pjsip.conf.sample.
+
+chan_pjsip
+------------------
+ * A new dialplan function, PJSIP_SEND_SESSION_REFRESH, has been added. When
+   invoked, a re-INVITE or UPDATE request will be sent immediately to the
+   endpoint underlying the channel. When used in combination with the existing
+   dialplan function PJSIP_MEDIA_OFFER, this allows the formats on a PJSIP
+   channel to be re-negotiated and updated after session set up.
+
+
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 13 to Asterisk 14 --------------------
 ------------------------------------------------------------------------------
@@ -379,26 +410,6 @@ cdr_csv
 
 
 ------------------------------------------------------------------------------
---- Functionality changes from Asterisk 13.11.0 to Asterisk 13.12.0 ----------
-------------------------------------------------------------------------------
-
-app_voicemail
-------------------
- * Added "tps_queue_high" and "tps_queue_low" options.
-   The options can modify the taskprocessor alert levels for this module.
-   Additional information can be found in the sample configuration file at
-   config/samples/voicemail.conf.sample.
-
-res_pjsip_mwi
-------------------
- * Added "mwi_tps_queue_high" and "mwi_tps_queue_low" global configuration
-   options to tune taskprocessor alert levels.
- * Added "mwi_disable_initial_unsolicited" global configuration option
-   to disable sending unsolicited MWI to all endpoints on startup.
-   Additional information can be found in the sample configuration file at
-   config/samples/pjsip.conf.sample.
-
-------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 13.10.0 to Asterisk 13.11.0 ----------
 ------------------------------------------------------------------------------
 
index 2e87393..02da0db 100644 (file)
@@ -2433,6 +2433,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
  *
@@ -2472,6 +2477,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;
@@ -2528,6 +2538,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);
 
@@ -2549,6 +2560,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);
index 1c08997..c1a3873 100644 (file)
                </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);
+}
index cbc06f0..8b80bfa 100644 (file)
@@ -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