From 423d01cf162224ac9316ea0beaaada9cd4c162bb Mon Sep 17 00:00:00 2001 From: Torrey Searle Date: Mon, 26 Jun 2017 14:52:52 +0200 Subject: chan_pjsip: add a new function PJSIP_DTMF_MODE This function is a replica of SIPDtmfMode, allowing the DTMF mode of a PJSIP call to be modified on a per-call basis ASTERISK-27085 #close Change-Id: I20eef5da3e5d1d3e58b304416bc79683f87e7612 --- channels/chan_pjsip.c | 17 +++- channels/pjsip/dialplan_functions.c | 152 ++++++++++++++++++++++++++++ channels/pjsip/include/dialplan_functions.h | 25 +++++ 3 files changed, 192 insertions(+), 2 deletions(-) (limited to 'channels') diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c index c542e149d..db0a69712 100644 --- a/channels/chan_pjsip.c +++ b/channels/chan_pjsip.c @@ -1701,7 +1701,7 @@ static int chan_pjsip_digit_begin(struct ast_channel *chan, char digit) struct ast_sip_session_media *media = pvt->media[SIP_MEDIA_AUDIO]; int res = 0; - switch (channel->session->endpoint->dtmf) { + switch (channel->session->dtmf) { case AST_SIP_DTMF_RFC_4733: if (!media || !media->rtp) { return -1; @@ -1821,7 +1821,7 @@ static int chan_pjsip_digit_end(struct ast_channel *ast, char digit, unsigned in struct ast_sip_session_media *media = pvt->media[SIP_MEDIA_AUDIO]; int res = 0; - switch (channel->session->endpoint->dtmf) { + switch (channel->session->dtmf) { case AST_SIP_DTMF_AUTO_INFO: { if (!media || !media->rtp) { @@ -2633,6 +2633,12 @@ static struct ast_custom_function media_offer_function = { .write = pjsip_acf_media_offer_write }; +static struct ast_custom_function dtmf_mode_function = { + .name = "PJSIP_DTMF_MODE", + .read = pjsip_acf_dtmf_mode_read, + .write = pjsip_acf_dtmf_mode_write +}; + static struct ast_custom_function session_refresh_function = { .name = "PJSIP_SEND_SESSION_REFRESH", .write = pjsip_acf_session_refresh_write, @@ -2677,6 +2683,11 @@ static int load_module(void) goto end; } + if (ast_custom_function_register(&dtmf_mode_function)) { + ast_log(LOG_WARNING, "Unable to register PJSIP_DTMF_MODE dialplan function\n"); + 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; @@ -2736,6 +2747,7 @@ static int load_module(void) end: ao2_cleanup(pjsip_uids_onhold); pjsip_uids_onhold = NULL; + ast_custom_function_unregister(&dtmf_mode_function); ast_custom_function_unregister(&media_offer_function); ast_custom_function_unregister(&chan_pjsip_dial_contacts_function); ast_custom_function_unregister(&session_refresh_function); @@ -2758,6 +2770,7 @@ static int unload_module(void) ast_sip_session_unregister_supplement(&chan_pjsip_ack_supplement); ast_sip_session_unregister_supplement(&call_pickup_supplement); + ast_custom_function_unregister(&dtmf_mode_function); ast_custom_function_unregister(&media_offer_function); ast_custom_function_unregister(&chan_pjsip_dial_contacts_function); ast_custom_function_unregister(&session_refresh_function); diff --git a/channels/pjsip/dialplan_functions.c b/channels/pjsip/dialplan_functions.c index 22d773820..a382c4e0a 100644 --- a/channels/pjsip/dialplan_functions.c +++ b/channels/pjsip/dialplan_functions.c @@ -68,6 +68,18 @@ PJSIP_SEND_SESSION_REFRESH + + + Get or change the DTMF mode for a SIP call. + + + + + When read, returns the current DTMF mode + When written, sets the current DTMF mode + This function uses the same DTMF mode naming as the dtmf_mode configuration option + + W/O: Initiate a session refresh via an UPDATE or re-INVITE on an established media session @@ -440,6 +452,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/app.h" #include "asterisk/channel.h" #include "asterisk/format.h" +#include "asterisk/dsp.h" #include "asterisk/pbx.h" #include "asterisk/res_pjsip.h" #include "asterisk/res_pjsip_session.h" @@ -1039,6 +1052,34 @@ 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); } +int pjsip_acf_dtmf_mode_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) +{ + struct ast_sip_channel_pvt *channel; + + if (!chan) { + ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd); + return -1; + } + + ast_channel_lock(chan); + if (strcmp(ast_channel_tech(chan)->type, "PJSIP")) { + ast_log(LOG_WARNING, "Cannot call %s on a non-PJSIP channel\n", cmd); + ast_channel_unlock(chan); + return -1; + } + + channel = ast_channel_tech_pvt(chan); + + if (ast_sip_dtmf_to_str(channel->session->dtmf, buf, len) < 0) { + ast_log(LOG_WARNING, "Unknown DTMF mode %d on PJSIP channel %s\n", channel->session->dtmf, ast_channel_name(chan)); + ast_channel_unlock(chan); + return -1; + } + + ast_channel_unlock(chan); + return 0; +} + struct refresh_data { struct ast_sip_session *session; enum ast_sip_session_refresh_method method; @@ -1067,6 +1108,117 @@ static int sip_session_response_cb(struct ast_sip_session *session, pjsip_rx_dat return 0; } +static int dtmf_mode_refresh_cb(void *obj) +{ + struct refresh_data *data = obj; + + if (data->session->inv_session->state == PJSIP_INV_STATE_CONFIRMED) { + ast_debug(3, "Changing DTMF mode on channel %s after OFFER/ANSER completion. Sending session refresh\n", ast_channel_name(data->session->channel)); + + ast_sip_session_refresh(data->session, NULL, NULL, + sip_session_response_cb, data->method, 1); + } + + return 0; +} + +int pjsip_acf_dtmf_mode_write(struct ast_channel *chan, const char *cmd, char *data, const char *value) +{ + struct ast_sip_channel_pvt *channel; + struct chan_pjsip_pvt *pjsip_pvt; + int dsp_features = 0; + int dtmf = -1; + 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; + } + + ast_channel_lock(chan); + if (strcmp(ast_channel_tech(chan)->type, "PJSIP")) { + ast_log(LOG_WARNING, "Cannot call %s on a non-PJSIP channel\n", cmd); + ast_channel_unlock(chan); + return -1; + } + + channel = ast_channel_tech_pvt(chan); + rdata.session = channel->session; + + dtmf = ast_sip_str_to_dtmf(value); + + if (dtmf == -1) { + ast_log(LOG_WARNING, "Cannot set DTMF mode to '%s' on channel '%s' as value is invalid.\n", value, + ast_channel_name(chan)); + ast_channel_unlock(chan); + return -1; + } + + if (channel->session->dtmf == dtmf) { + /* DTMF mode unchanged, nothing to do! */ + ast_channel_unlock(chan); + return 0; + } + + channel->session->dtmf = dtmf; + + pjsip_pvt = channel->pvt; + if (pjsip_pvt->media[SIP_MEDIA_AUDIO] && (pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp) { + if (channel->session->dtmf == AST_SIP_DTMF_RFC_4733) { + ast_rtp_instance_set_prop((pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp, AST_RTP_PROPERTY_DTMF, 1); + ast_rtp_instance_dtmf_mode_set((pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp, AST_RTP_DTMF_MODE_RFC2833); + } else if (channel->session->dtmf == AST_SIP_DTMF_INFO) { + ast_rtp_instance_set_prop((pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp, AST_RTP_PROPERTY_DTMF, 0); + ast_rtp_instance_dtmf_mode_set((pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp, AST_RTP_DTMF_MODE_NONE); + } else if (channel->session->dtmf == AST_SIP_DTMF_INBAND) { + ast_rtp_instance_set_prop((pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp, AST_RTP_PROPERTY_DTMF, 0); + ast_rtp_instance_dtmf_mode_set((pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp, AST_RTP_DTMF_MODE_INBAND); + } else if (channel->session->dtmf == AST_SIP_DTMF_NONE) { + ast_rtp_instance_set_prop((pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp, AST_RTP_PROPERTY_DTMF, 0); + ast_rtp_instance_dtmf_mode_set((pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp, AST_RTP_DTMF_MODE_NONE); + } else if (channel->session->dtmf == AST_SIP_DTMF_AUTO) { + if (ast_rtp_instance_dtmf_mode_get((pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp) != AST_RTP_DTMF_MODE_RFC2833) { + /* no RFC4733 negotiated, enable inband */ + ast_rtp_instance_dtmf_mode_set((pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp, AST_RTP_DTMF_MODE_INBAND); + } + } else if (channel->session->dtmf == AST_SIP_DTMF_AUTO_INFO) { + ast_rtp_instance_set_prop((pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp, AST_RTP_PROPERTY_DTMF, 0); + if (ast_rtp_instance_dtmf_mode_get((pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp) == AST_RTP_DTMF_MODE_INBAND) { + /* if inband, switch to INFO */ + ast_rtp_instance_dtmf_mode_set((pjsip_pvt->media[SIP_MEDIA_AUDIO])->rtp, AST_RTP_DTMF_MODE_NONE); + } + } + } + + if (channel->session->dsp) { + dsp_features = ast_dsp_get_features(channel->session->dsp); + } + if (channel->session->dtmf == AST_SIP_DTMF_INBAND || + channel->session->dtmf == AST_SIP_DTMF_AUTO) { + dsp_features |= DSP_FEATURE_DIGIT_DETECT; + } else { + dsp_features &= ~DSP_FEATURE_DIGIT_DETECT; + } + if (dsp_features) { + if (!channel->session->dsp) { + if (!(channel->session->dsp = ast_dsp_new())) { + ast_channel_unlock(chan); + return 0; + } + } + ast_dsp_set_features(channel->session->dsp, dsp_features); + } else if (channel->session->dsp) { + ast_dsp_free(channel->session->dsp); + channel->session->dsp = NULL; + } + + ast_channel_unlock(chan); + + return ast_sip_push_task_synchronous(channel->session->serializer, dtmf_mode_refresh_cb, &rdata); +} + static int refresh_write_cb(void *obj) { struct refresh_data *data = obj; diff --git a/channels/pjsip/include/dialplan_functions.h b/channels/pjsip/include/dialplan_functions.h index 8b80bfa74..731e91d13 100644 --- a/channels/pjsip/include/dialplan_functions.h +++ b/channels/pjsip/include/dialplan_functions.h @@ -47,6 +47,31 @@ int pjsip_acf_channel_read(struct ast_channel *chan, const char *cmd, char *data */ int pjsip_acf_media_offer_write(struct ast_channel *chan, const char *cmd, char *data, const char *value); +/*! + * \brief PJSIP_DTMF_MODE function read 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 buf Out buffer that should be populated with the data + * \param len Size of the buffer + * + * \retval 0 on success + * \retval -1 on failure + */ +int pjsip_acf_dtmf_mode_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len); + +/*! + * \brief PJSIP_DTMF_MODE 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_dtmf_mode_write(struct ast_channel *chan, const char *cmd, char *data, const char *value); + /*! * \brief PJSIP_MEDIA_OFFER function read callback * \param chan The channel the function is called on -- cgit v1.2.3