diff --git a/UPGRADE.txt b/UPGRADE.txt index 19f947c4a144228dc45477ab7ed9315bb41109a3..8b848cc8aac80890bd62edc86b6f49c4a3a534d3 100644 --- a/UPGRADE.txt +++ b/UPGRADE.txt @@ -23,6 +23,11 @@ From 13.3.0 to 13.4.0: +res_pjsip: + - The dtmf_mode now supports a new option, 'auto'. This mode will attempt to + detect if the device supports RFC4733 DTMF. If so, it will choose that + DTMF type; if not, it will choose 'inband' DTMF. + res_pjsip_dlg_options: - A new module, this handles OPTIONS requests sent in-dialog. This module should have no adverse effects for those upgrading; this note merely diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c index c9d09dbeb17c491d5048db4644f950f0e5e730be..207a7d90b6880a937dac8f8fc44378b1d2d54bbf 100644 --- a/channels/chan_pjsip.c +++ b/channels/chan_pjsip.c @@ -548,7 +548,7 @@ static struct ast_frame *chan_pjsip_cng_tone_detected(struct ast_sip_session *se int exists; /* If we only needed this DSP for fax detection purposes we can just drop it now */ - if (session->endpoint->dtmf == AST_SIP_DTMF_INBAND) { + if (session->endpoint->dtmf == AST_SIP_DTMF_INBAND || session->endpoint->dtmf == AST_SIP_DTMF_AUTO) { ast_dsp_set_features(session->dsp, DSP_FEATURE_DIGIT_DETECT); } else { ast_dsp_free(session->dsp); @@ -1473,6 +1473,14 @@ static int chan_pjsip_digit_begin(struct ast_channel *chan, char digit) } ast_rtp_instance_dtmf_begin(media->rtp, digit); + break; + case AST_SIP_DTMF_AUTO: + if (!media || !media->rtp || (ast_rtp_instance_dtmf_mode_get(media->rtp) == AST_RTP_DTMF_MODE_INBAND)) { + return -1; + } + + ast_rtp_instance_dtmf_begin(media->rtp, digit); + break; case AST_SIP_DTMF_NONE: break; case AST_SIP_DTMF_INBAND: @@ -1576,6 +1584,15 @@ static int chan_pjsip_digit_end(struct ast_channel *ast, char digit, unsigned in } ast_rtp_instance_dtmf_end_with_duration(media->rtp, digit, duration); + break; + case AST_SIP_DTMF_AUTO: + if (!media || !media->rtp || (ast_rtp_instance_dtmf_mode_get(media->rtp) == AST_RTP_DTMF_MODE_INBAND)) { + return -1; + } + + ast_rtp_instance_dtmf_end_with_duration(media->rtp, digit, duration); + break; + case AST_SIP_DTMF_NONE: break; case AST_SIP_DTMF_INBAND: diff --git a/contrib/ast-db-manage/config/versions/31cd4f4891ec_add_auto_dtmf_mode.py b/contrib/ast-db-manage/config/versions/31cd4f4891ec_add_auto_dtmf_mode.py new file mode 100644 index 0000000000000000000000000000000000000000..fd1b6c7d40b67d9ae4ffe219e7133ee7ea2cfb6f --- /dev/null +++ b/contrib/ast-db-manage/config/versions/31cd4f4891ec_add_auto_dtmf_mode.py @@ -0,0 +1,63 @@ +"""Add auto DTMF mode + +Revision ID: 31cd4f4891ec +Revises: 23530d604b96 +Create Date: 2015-04-10 12:36:51.619419 + +""" + +# revision identifiers, used by Alembic. +revision = '31cd4f4891ec' +down_revision = '23530d604b96' + +from alembic import op +import sqlalchemy as sa + +OLD_ENUM = ['rfc4733', 'inband', 'info'] +NEW_ENUM = ['rfc4733', 'inband', 'info', 'auto'] + +old_type = sa.Enum(*OLD_ENUM, name='pjsip_dtmf_mode_values') +new_type = sa.Enum(*NEW_ENUM, name='pjsip_dtmf_mode_values_v2') + +tcr = sa.sql.table('ps_endpoints', sa.Column('dtmf_mode', new_type, + nullable=True)) + +def upgrade(): + context = op.get_context() + + # Upgrading to this revision WILL clear your directmedia values. + if context.bind.dialect.name != 'postgresql': + op.alter_column('ps_endpoints', 'dtmf_mode', + type_=new_type, + existing_type=old_type) + else: + enum = ENUM('rfc4733', 'inband', 'info', 'auto', + name='pjsip_dtmf_mode_values_v2') + enum.create(op.get_bind(), checkfirst=False) + + op.execute('ALTER TABLE ps_endpoints ALTER COLUMN dtmf_mode TYPE' + ' pjsip_dtmf_mode_values_v2 USING' + ' dtmf_mode::text::pjsip_dtmf_mode_values_v2') + + ENUM(name="pjsip_dtmf_mode_values").drop(op.get_bind(), checkfirst=False) + +def downgrade(): + context = op.get_context() + + op.execute(tcr.update().where(tcr.c.directmedia==u'outgoing') + .values(directmedia=None)) + + if context.bind.dialect.name != 'postgresql': + op.alter_column('ps_endpoints', 'dtmf_mode', + type_=old_type, + existing_type=new_type) + else: + enum = ENUM('rfc4733', 'inband', 'info', + name='pjsip_dtmf_mode_values') + enum.create(op.get_bind(), checkfirst=False) + + op.execute('ALTER TABLE ps_endpoints ALTER COLUMN dtmf_mode TYPE' + ' pjsip_dtmf_mode_values USING' + ' dtmf_mode::text::pjsip_dtmf_mode_values') + + ENUM(name="pjsip_dtmf_mode_values_v2").drop(op.get_bind(), checkfirst=False) diff --git a/include/asterisk/dsp.h b/include/asterisk/dsp.h index 16262c05d468dd49c6d95ea36dcd9fb6911885ad..7e84ebe9caa11bc41a0e8b43836fae11f6e408c6 100644 --- a/include/asterisk/dsp.h +++ b/include/asterisk/dsp.h @@ -138,6 +138,9 @@ void ast_dsp_digitreset(struct ast_dsp *dsp); /*! \brief Select feature set */ void ast_dsp_set_features(struct ast_dsp *dsp, int features); +/*! \brief Get features */ +int ast_dsp_get_features(struct ast_dsp *dsp); + /*! \brief Get pending DTMF/MF digits */ int ast_dsp_getdigits(struct ast_dsp *dsp, char *buf, int max); diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index fcad28878902918273d05fd51a70dc255222991e..06607cfc47c125feb97d709329ae50ed92a60d61 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -252,6 +252,8 @@ enum ast_sip_dtmf_mode { AST_SIP_DTMF_INBAND, /*! Use SIP INFO DTMF (blech) */ AST_SIP_DTMF_INFO, + /*! Use SIP 4733 if supported by the other side or INBAND if not */ + AST_SIP_DTMF_AUTO, }; /*! diff --git a/main/dsp.c b/main/dsp.c index 335fb3db0ff76ca9ff78da4c33a2d504c768362f..087416358e3609789a651d3c68589675e4eff8ef 100644 --- a/main/dsp.c +++ b/main/dsp.c @@ -1702,6 +1702,13 @@ void ast_dsp_set_features(struct ast_dsp *dsp, int features) } } + +int ast_dsp_get_features(struct ast_dsp *dsp) +{ + return (dsp->features); +} + + void ast_dsp_free(struct ast_dsp *dsp) { ast_free(dsp); diff --git a/res/res_pjsip.c b/res/res_pjsip.c index 448d57ddbcf2594050aee0e6e9b92569983ff75a..93905d6e966b374eb1980467796c36a190f0578d 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -210,6 +210,10 @@ <enum name="info"> <para>DTMF is sent as SIP INFO packets.</para> </enum> + <enum name="auto"> + <para>DTMF is sent as RFC 4733 if the other side supports it or as INBAND if not.</para> + </enum> + </enumlist> </description> </configOption> diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index 4f5fc74deb8bad6dac6eacf0ce5f197f16f18631..1f38e77299e692b2c91fe0037cd94657bdd6b374 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -109,6 +109,8 @@ static int dtmf_handler(const struct aco_option *opt, struct ast_variable *var, endpoint->dtmf = AST_SIP_DTMF_INBAND; } 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 { @@ -129,6 +131,8 @@ static int dtmf_to_str(const void *obj, const intptr_t *args, char **buf) *buf = "inband"; break; case AST_SIP_DTMF_INFO : *buf = "info"; break; + case AST_SIP_DTMF_AUTO : + *buf = "auto"; break; default: *buf = "none"; } diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c index ab801c9e7aef27f30195ce0cd093225fcd5704ad..f99afb302cb0bb15a96cda5e90628bc3f7375809 100644 --- a/res/res_pjsip_sdp_rtp.c +++ b/res/res_pjsip_sdp_rtp.c @@ -50,6 +50,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/sched.h" #include "asterisk/acl.h" #include "asterisk/sdp_srtp.h" +#include "asterisk/dsp.h" #include "asterisk/res_pjsip.h" #include "asterisk/res_pjsip_session.h" @@ -123,7 +124,7 @@ 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) { + if (session->endpoint->dtmf == AST_SIP_DTMF_RFC_4733 || session->endpoint->dtmf == AST_SIP_DTMF_AUTO) { 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) { @@ -143,13 +144,14 @@ static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_me return 0; } -static void get_codecs(struct ast_sip_session *session, const struct pjmedia_sdp_media *stream, struct ast_rtp_codecs *codecs) +static void get_codecs(struct ast_sip_session *session, const struct pjmedia_sdp_media *stream, struct ast_rtp_codecs *codecs, + struct ast_sip_session_media *session_media) { pjmedia_sdp_attr *attr; pjmedia_sdp_rtpmap *rtpmap; pjmedia_sdp_fmtp fmtp; struct ast_format *format; - int i, num = 0; + int i, num = 0, tel_event = 0; char name[256]; char media[20]; char fmt_param[256]; @@ -171,6 +173,9 @@ static void get_codecs(struct ast_sip_session *session, const struct pjmedia_sdp } ast_copy_pj_str(name, &rtpmap->enc_name, sizeof(name)); + if (strcmp(name,"telephone-event") == 0) { + tel_event++; + } ast_copy_pj_str(media, (pj_str_t*)&stream->desc.media, sizeof(media)); ast_rtp_codecs_payloads_set_rtpmap_type_rate(codecs, NULL, pj_strtoul(&stream->desc.fmt[i]), media, name, 0, rtpmap->clock_rate); @@ -200,7 +205,9 @@ static void get_codecs(struct ast_sip_session *session, const struct pjmedia_sdp } } } - + if ((tel_event==0) && (session->endpoint->dtmf == AST_SIP_DTMF_AUTO)) { + ast_rtp_instance_dtmf_mode_set(session_media->rtp, AST_RTP_DTMF_MODE_INBAND); + } /* Get the packetization, if it exists */ if ((attr = pjmedia_sdp_media_find_attr2(stream, "ptime", NULL))) { unsigned long framing = pj_strtoul(pj_strltrim(&attr->value)); @@ -221,6 +228,7 @@ static int set_caps(struct ast_sip_session *session, struct ast_sip_session_medi int fmts = 0; int direct_media_enabled = !ast_sockaddr_isnull(&session_media->direct_media_addr) && ast_format_cap_count(session->direct_media_cap); + int dsp_features = 0; if (!(caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT)) || !(peer = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT)) || @@ -238,7 +246,7 @@ static int set_caps(struct ast_sip_session *session, struct ast_sip_session_medi } /* get the capabilities on the peer */ - get_codecs(session, stream, &codecs); + get_codecs(session, stream, &codecs, session_media); ast_rtp_codecs_payload_formats(&codecs, peer, &fmts); /* get the joint capabilities between peer and endpoint */ @@ -288,6 +296,18 @@ static int set_caps(struct ast_sip_session *session, struct ast_sip_session_medi ast_channel_nativeformats_set(session->channel, caps); ast_set_read_format(session->channel, ast_channel_readformat(session->channel)); ast_set_write_format(session->channel, ast_channel_writeformat(session->channel)); + if ((session->endpoint->dtmf == AST_SIP_DTMF_AUTO) + && (ast_rtp_instance_dtmf_mode_get(session_media->rtp) == AST_RTP_DTMF_MODE_RFC2833) + && (session->dsp)) { + dsp_features = ast_dsp_get_features(session->dsp); + dsp_features &= ~DSP_FEATURE_DIGIT_DETECT; + if (dsp_features) { + ast_dsp_set_features(session->dsp, dsp_features); + } else { + ast_dsp_free(session->dsp); + session->dsp = NULL; + } + } ast_channel_unlock(session->channel); ao2_ref(fmt, -1); @@ -951,7 +971,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) ? AST_RTP_DTMF : 0; + int noncodec = (session->endpoint->dtmf == AST_SIP_DTMF_RFC_4733 || session->endpoint->dtmf == AST_SIP_DTMF_AUTO) ? 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 4854c51d03a0f8e51935e54ad93904e07e1a8290..87ce2b0c1b31b5d43b9107bf8b2614438865ce4b 100644 --- a/res/res_pjsip_session.c +++ b/res/res_pjsip_session.c @@ -1283,7 +1283,7 @@ struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint, session->inv_session = inv_session; session->req_caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); - if (endpoint->dtmf == AST_SIP_DTMF_INBAND) { + if ((endpoint->dtmf == AST_SIP_DTMF_INBAND) || (endpoint->dtmf == AST_SIP_DTMF_AUTO)) { dsp_features |= DSP_FEATURE_DIGIT_DETECT; }