diff options
Diffstat (limited to 'channels')
-rw-r--r-- | channels/chan_pjsip.c | 17 | ||||
-rw-r--r-- | channels/pjsip/dialplan_functions.c | 153 | ||||
-rw-r--r-- | channels/pjsip/include/dialplan_functions.h | 25 |
3 files changed, 193 insertions, 2 deletions
diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c index 51b5dab5c..ebda6c7ee 100644 --- a/channels/chan_pjsip.c +++ b/channels/chan_pjsip.c @@ -1947,7 +1947,7 @@ static int chan_pjsip_digit_begin(struct ast_channel *chan, char digit) media = channel->session->active_media_state->default_session[AST_MEDIA_TYPE_AUDIO]; - switch (channel->session->endpoint->dtmf) { + switch (channel->session->dtmf) { case AST_SIP_DTMF_RFC_4733: if (!media || !media->rtp) { return -1; @@ -2068,7 +2068,7 @@ static int chan_pjsip_digit_end(struct ast_channel *ast, char digit, unsigned in media = channel->session->active_media_state->default_session[AST_MEDIA_TYPE_AUDIO]; - switch (channel->session->endpoint->dtmf) { + switch (channel->session->dtmf) { case AST_SIP_DTMF_AUTO_INFO: { if (!media || !media->rtp) { @@ -2893,6 +2893,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, @@ -2937,6 +2943,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; @@ -2996,6 +3007,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); @@ -3018,6 +3030,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 59ca9d791..c89d9ca26 100644 --- a/channels/pjsip/dialplan_functions.c +++ b/channels/pjsip/dialplan_functions.c @@ -68,6 +68,18 @@ <ref type="function">PJSIP_SEND_SESSION_REFRESH</ref> </see-also> </function> +<function name="PJSIP_DTMF_MODE" language="en_US"> + <synopsis> + Get or change the DTMF mode for a SIP call. + </synopsis> + <syntax> + </syntax> + <description> + <para>When read, returns the current DTMF mode</para> + <para>When written, sets the current DTMF mode</para> + <para>This function uses the same DTMF mode naming as the dtmf_mode configuration option</para> + </description> +</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 @@ -439,6 +451,7 @@ #include "asterisk/channel.h" #include "asterisk/stream.h" #include "asterisk/format.h" +#include "asterisk/dsp.h" #include "asterisk/pbx.h" #include "asterisk/res_pjsip.h" #include "asterisk/res_pjsip_session.h" @@ -1167,6 +1180,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; @@ -1195,6 +1236,118 @@ 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, NULL); + } + + 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 ast_sip_session_media *media; + 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; + + media = channel->session->active_media_state->default_session[AST_MEDIA_TYPE_AUDIO]; + + if (media && media->rtp) { + if (channel->session->dtmf == AST_SIP_DTMF_RFC_4733) { + ast_rtp_instance_set_prop(media->rtp, AST_RTP_PROPERTY_DTMF, 1); + ast_rtp_instance_dtmf_mode_set(media->rtp, AST_RTP_DTMF_MODE_RFC2833); + } else if (channel->session->dtmf == AST_SIP_DTMF_INFO) { + ast_rtp_instance_set_prop(media->rtp, AST_RTP_PROPERTY_DTMF, 0); + ast_rtp_instance_dtmf_mode_set(media->rtp, AST_RTP_DTMF_MODE_NONE); + } else if (channel->session->dtmf == AST_SIP_DTMF_INBAND) { + ast_rtp_instance_set_prop(media->rtp, AST_RTP_PROPERTY_DTMF, 0); + ast_rtp_instance_dtmf_mode_set(media->rtp, AST_RTP_DTMF_MODE_INBAND); + } else if (channel->session->dtmf == AST_SIP_DTMF_NONE) { + ast_rtp_instance_set_prop(media->rtp, AST_RTP_PROPERTY_DTMF, 0); + ast_rtp_instance_dtmf_mode_set(media->rtp, AST_RTP_DTMF_MODE_NONE); + } else if (channel->session->dtmf == AST_SIP_DTMF_AUTO) { + if (ast_rtp_instance_dtmf_mode_get(media->rtp) != AST_RTP_DTMF_MODE_RFC2833) { + /* no RFC4733 negotiated, enable inband */ + ast_rtp_instance_dtmf_mode_set(media->rtp, AST_RTP_DTMF_MODE_INBAND); + } + } else if (channel->session->dtmf == AST_SIP_DTMF_AUTO_INFO) { + ast_rtp_instance_set_prop(media->rtp, AST_RTP_PROPERTY_DTMF, 0); + if (ast_rtp_instance_dtmf_mode_get(media->rtp) == AST_RTP_DTMF_MODE_INBAND) { + /* if inband, switch to INFO */ + ast_rtp_instance_dtmf_mode_set(media->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 @@ -48,6 +48,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 * \param cmd The name of the function |