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
This commit is contained in:
Matt Jordan
2016-08-07 09:58:59 -05:00
parent 5a5b949333
commit 5f815f9dba
4 changed files with 148 additions and 1 deletions

View File

@@ -23,11 +23,20 @@ res_pjsip_mwi
------------------ ------------------
* Added "mwi_tps_queue_high" and "mwi_tps_queue_low" global configuration * Added "mwi_tps_queue_high" and "mwi_tps_queue_low" global configuration
options to tune taskprocessor alert levels. options to tune taskprocessor alert levels.
* Added "mwi_disable_initial_unsolicited" global configuration option * Added "mwi_disable_initial_unsolicited" global configuration option
to disable sending unsolicited MWI to all endpoints on startup. to disable sending unsolicited MWI to all endpoints on startup.
Additional information can be found in the sample configuration file at Additional information can be found in the sample configuration file at
config/samples/pjsip.conf.sample. 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.10.0 to Asterisk 13.11.0 ---------- --- Functionality changes from Asterisk 13.10.0 to Asterisk 13.11.0 ----------
------------------------------------------------------------------------------ ------------------------------------------------------------------------------

View File

@@ -2384,6 +2384,11 @@ static struct ast_custom_function media_offer_function = {
.write = pjsip_acf_media_offer_write .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 * \brief Load the module
* *
@@ -2423,6 +2428,11 @@ static int load_module(void)
goto end; 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)) { if (ast_sip_session_register_supplement(&chan_pjsip_supplement)) {
ast_log(LOG_ERROR, "Unable to register PJSIP supplement\n"); ast_log(LOG_ERROR, "Unable to register PJSIP supplement\n");
goto end; goto end;
@@ -2479,6 +2489,7 @@ end:
pjsip_uids_onhold = NULL; pjsip_uids_onhold = NULL;
ast_custom_function_unregister(&media_offer_function); ast_custom_function_unregister(&media_offer_function);
ast_custom_function_unregister(&chan_pjsip_dial_contacts_function); ast_custom_function_unregister(&chan_pjsip_dial_contacts_function);
ast_custom_function_unregister(&session_refresh_function);
ast_channel_unregister(&chan_pjsip_tech); ast_channel_unregister(&chan_pjsip_tech);
ast_rtp_glue_unregister(&chan_pjsip_rtp_glue); 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(&media_offer_function);
ast_custom_function_unregister(&chan_pjsip_dial_contacts_function); ast_custom_function_unregister(&chan_pjsip_dial_contacts_function);
ast_custom_function_unregister(&session_refresh_function);
ast_channel_unregister(&chan_pjsip_tech); ast_channel_unregister(&chan_pjsip_tech);
ao2_ref(chan_pjsip_tech.capabilities, -1); ao2_ref(chan_pjsip_tech.capabilities, -1);

View File

@@ -59,8 +59,55 @@
</parameter> </parameter>
</syntax> </syntax>
<description> <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> </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> </function>
<info name="PJSIPCHANNEL" language="en_US" tech="PJSIP"> <info name="PJSIPCHANNEL" language="en_US" tech="PJSIP">
<enumlist> <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); 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);
}

View File

@@ -60,6 +60,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); 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 * \brief PJSIP_DIAL_CONTACTS function read callback
* \param chan The channel the function is called on * \param chan The channel the function is called on