diff options
-rw-r--r-- | CHANGES | 3 | ||||
-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 | ||||
-rw-r--r-- | include/asterisk/res_pjsip.h | 27 | ||||
-rw-r--r-- | include/asterisk/res_pjsip_session.h | 2 | ||||
-rw-r--r-- | res/res_pjsip.c | 50 | ||||
-rw-r--r-- | res/res_pjsip/pjsip_configuration.c | 40 | ||||
-rw-r--r-- | res/res_pjsip_sdp_rtp.c | 12 | ||||
-rw-r--r-- | res/res_pjsip_session.c | 2 |
10 files changed, 294 insertions, 37 deletions
@@ -37,6 +37,9 @@ res_musiconhold which sends signals to the application and its descendants directly, or "process" which sends signals only to the application itself. + * New dialplan function PJSIP_DTMF_MODE added to get or change the DTMF mode + of a channel on a per-call basis. + ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 14.5.0 to Asterisk 14.6.0 ------------ ------------------------------------------------------------------------------ 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 diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index cf366cbab..cb77fa356 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -2889,4 +2889,31 @@ int ast_sip_set_tpselector_from_ep_or_uri(const struct ast_sip_endpoint *endpoin int ast_sip_dlg_set_transport(const struct ast_sip_endpoint *endpoint, pjsip_dialog *dlg, pjsip_tpselector *selector); +/*! + * \brief Convert the DTMF mode enum value into a string + * \since 13.18.0 + * + * \param dtmf the dtmf mode + * \param buf Buffer to receive dtmf mode string + * \param buf_len Buffer length + * + * \retval 0 Success + * \retval -1 Failure + * + */ +int ast_sip_dtmf_to_str(const enum ast_sip_dtmf_mode dtmf, + char *buf, size_t buf_len); + +/*! + * \brief Convert the DTMF mode name into an enum + * \since 13.18.0 + * + * \param dtmf_mode dtmf mode as a string + * + * \retval >= 0 The enum value + * \retval -1 Failure + * + */ +int ast_sip_str_to_dtmf(const char *dtmf_mode); + #endif /* _RES_PJSIP_H */ diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h index eae11af43..caf10db11 100644 --- a/include/asterisk/res_pjsip_session.h +++ b/include/asterisk/res_pjsip_session.h @@ -203,6 +203,8 @@ struct ast_sip_session { unsigned int defer_end:1; /*! Session end (remote hangup) requested while termination deferred */ unsigned int ended_while_deferred:1; + /*! DTMF mode to use with this session, from endpoint but can change */ + enum ast_sip_dtmf_mode dtmf; }; typedef int (*ast_sip_session_request_creation_cb)(struct ast_sip_session *session, pjsip_tx_data *tdata); diff --git a/res/res_pjsip.c b/res/res_pjsip.c index 02112113c..f3648acdb 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -4483,6 +4483,56 @@ const char *ast_sip_get_host_ip_string(int af) return NULL; } +int ast_sip_dtmf_to_str(const enum ast_sip_dtmf_mode dtmf, + char *buf, size_t buf_len) +{ + switch (dtmf) { + case AST_SIP_DTMF_NONE: + ast_copy_string(buf, "none", buf_len); + break; + case AST_SIP_DTMF_RFC_4733: + ast_copy_string(buf, "rfc4733", buf_len); + break; + case AST_SIP_DTMF_INBAND: + ast_copy_string(buf, "inband", buf_len); + break; + case AST_SIP_DTMF_INFO: + ast_copy_string(buf, "info", buf_len); + break; + case AST_SIP_DTMF_AUTO: + ast_copy_string(buf, "auto", buf_len); + break; + case AST_SIP_DTMF_AUTO_INFO: + ast_copy_string(buf, "auto_info", buf_len); + break; + default: + buf[0] = '\0'; + return -1; + } + return 0; +} + +int ast_sip_str_to_dtmf(const char * dtmf_mode) +{ + int result = -1; + + if (!strcasecmp(dtmf_mode, "info")) { + result = AST_SIP_DTMF_INFO; + } else if (!strcasecmp(dtmf_mode, "rfc4733")) { + result = AST_SIP_DTMF_RFC_4733; + } else if (!strcasecmp(dtmf_mode, "inband")) { + result = AST_SIP_DTMF_INBAND; + } else if (!strcasecmp(dtmf_mode, "none")) { + result = AST_SIP_DTMF_NONE; + } else if (!strcasecmp(dtmf_mode, "auto")) { + result = AST_SIP_DTMF_AUTO; + } else if (!strcasecmp(dtmf_mode, "auto_info")) { + result = AST_SIP_DTMF_AUTO_INFO; + } + + return result; +} + /*! * \brief Set name and number information on an identity header. * diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index 9f9de36fa..4e12b04cc 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -366,47 +366,29 @@ static int contact_acl_to_str(const void *obj, const intptr_t *args, char **buf) static int dtmf_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { struct ast_sip_endpoint *endpoint = obj; + enum ast_sip_dtmf_mode dtmf = ast_sip_str_to_dtmf(var->value); - if (!strcasecmp(var->value, "rfc4733")) { - endpoint->dtmf = AST_SIP_DTMF_RFC_4733; - } else if (!strcasecmp(var->value, "inband")) { - endpoint->dtmf = AST_SIP_DTMF_INBAND; - } else if (!strcasecmp(var->value, "auto_info")) { - endpoint->dtmf = AST_SIP_DTMF_AUTO_INFO; - } else if (!strcasecmp(var->value, "info")) { - endpoint->dtmf = AST_SIP_DTMF_INFO; - } else if (!strcasecmp(var->value, "auto")) { - endpoint->dtmf = AST_SIP_DTMF_AUTO; - } else if (!strcasecmp(var->value, "none")) { - endpoint->dtmf = AST_SIP_DTMF_NONE; - } else { + if (dtmf == -1) { return -1; } + endpoint->dtmf = dtmf; return 0; } static int dtmf_to_str(const void *obj, const intptr_t *args, char **buf) { const struct ast_sip_endpoint *endpoint = obj; + char dtmf_str[20]; + int result = -1; - switch (endpoint->dtmf) { - case AST_SIP_DTMF_RFC_4733 : - *buf = "rfc4733"; break; - case AST_SIP_DTMF_INBAND : - *buf = "inband"; break; - case AST_SIP_DTMF_INFO : - *buf = "info"; break; - case AST_SIP_DTMF_AUTO : - *buf = "auto"; break; - case AST_SIP_DTMF_AUTO_INFO : - *buf = "auto_info"; - break; - default: - *buf = "none"; - } + result = ast_sip_dtmf_to_str(endpoint->dtmf, dtmf_str, sizeof(dtmf_str)); - *buf = ast_strdup(*buf); + if (result == 0) { + *buf = ast_strdup(dtmf_str); + } else { + *buf = ast_strdup("none"); + } return 0; } diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c index a2e7f8f92..4ed1f8a57 100644 --- a/res/res_pjsip_sdp_rtp.c +++ b/res/res_pjsip_sdp_rtp.c @@ -207,10 +207,10 @@ static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_me ice->stop(session_media->rtp); } - if (session->endpoint->dtmf == AST_SIP_DTMF_RFC_4733 || session->endpoint->dtmf == AST_SIP_DTMF_AUTO || session->endpoint->dtmf == AST_SIP_DTMF_AUTO_INFO) { + if (session->dtmf == AST_SIP_DTMF_RFC_4733 || session->dtmf == AST_SIP_DTMF_AUTO || session->dtmf == AST_SIP_DTMF_AUTO_INFO) { ast_rtp_instance_dtmf_mode_set(session_media->rtp, AST_RTP_DTMF_MODE_RFC2833); ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_DTMF, 1); - } else if (session->endpoint->dtmf == AST_SIP_DTMF_INBAND) { + } else if (session->dtmf == AST_SIP_DTMF_INBAND) { ast_rtp_instance_dtmf_mode_set(session_media->rtp, AST_RTP_DTMF_MODE_INBAND); } @@ -293,11 +293,11 @@ static void get_codecs(struct ast_sip_session *session, const struct pjmedia_sdp } } } - if (!tel_event && (session->endpoint->dtmf == AST_SIP_DTMF_AUTO)) { + if (!tel_event && (session->dtmf == AST_SIP_DTMF_AUTO)) { ast_rtp_instance_dtmf_mode_set(session_media->rtp, AST_RTP_DTMF_MODE_INBAND); } - if (session->endpoint->dtmf == AST_SIP_DTMF_AUTO_INFO) { + if (session->dtmf == AST_SIP_DTMF_AUTO_INFO) { if (tel_event) { ast_rtp_instance_dtmf_mode_set(session_media->rtp, AST_RTP_DTMF_MODE_RFC2833); } else { @@ -434,7 +434,7 @@ static int set_caps(struct ast_sip_session *session, ast_set_write_format(session->channel, ast_channel_writeformat(session->channel)); } - if ( ((session->endpoint->dtmf == AST_SIP_DTMF_AUTO) || (session->endpoint->dtmf == AST_SIP_DTMF_AUTO_INFO) ) + if ( ((session->dtmf == AST_SIP_DTMF_AUTO) || (session->dtmf == AST_SIP_DTMF_AUTO_INFO) ) && (ast_rtp_instance_dtmf_mode_get(session_media->rtp) == AST_RTP_DTMF_MODE_RFC2833) && (session->dsp)) { dsp_features = ast_dsp_get_features(session->dsp); @@ -1314,7 +1314,7 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as pj_str_t stmp; pjmedia_sdp_attr *attr; int index = 0; - int noncodec = (session->endpoint->dtmf == AST_SIP_DTMF_RFC_4733 || session->endpoint->dtmf == AST_SIP_DTMF_AUTO || session->endpoint->dtmf == AST_SIP_DTMF_AUTO_INFO) ? AST_RTP_DTMF : 0; + int noncodec = (session->dtmf == AST_SIP_DTMF_RFC_4733 || session->dtmf == AST_SIP_DTMF_AUTO || session->dtmf == AST_SIP_DTMF_AUTO_INFO) ? AST_RTP_DTMF : 0; int min_packet_size = 0, max_packet_size = 0; int rtp_code; RAII_VAR(struct ast_format_cap *, caps, NULL, ao2_cleanup); diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c index 0ad2c8f30..2711efeb8 100644 --- a/res/res_pjsip_session.c +++ b/res/res_pjsip_session.c @@ -2013,6 +2013,8 @@ struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint, session->contact = ao2_bump(contact); session->inv_session = inv_session; + session->dtmf = endpoint->dtmf; + if (add_supplements(session)) { /* Release the ref held by session->inv_session */ ao2_ref(session, -1); |