res_pjsip: Add fax_detect_timeout endpoint option.
authorRichard Mudgett <rmudgett@digium.com>
Sat, 16 Jul 2016 01:44:52 +0000 (20:44 -0500)
committerRichard Mudgett <rmudgett@digium.com>
Tue, 19 Jul 2016 15:33:45 +0000 (10:33 -0500)
The new endpoint option allows the PJSIP channel driver's fax_detect
endpoint option to timeout on a call after the specified number of
seconds into a call.  The new feature is disabled if the timeout is set
to zero.  The option is disabled by default.

ASTERISK-26214
Reported by: Richard Mudgett

Change-Id: Id5a87375fb2c4f9dc1d4b44c78ec8735ba65453d

CHANGES
channels/chan_pjsip.c
configs/samples/pjsip.conf.sample
contrib/ast-db-manage/config/versions/4a6c67fa9b7a_add_fax_detect_timeout_option.py [new file with mode: 0644]
include/asterisk/res_pjsip.h
include/asterisk/res_pjsip_session.h
main/channel.c
res/res_pjsip.c
res/res_pjsip/pjsip_configuration.c

diff --git a/CHANGES b/CHANGES
index 9caa524..62d8178 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -366,6 +366,10 @@ cdr_csv
 
 res_pjsip
 ------------------
+ * Added "fax_detect_timeout" to endpoint.
+   The option determines how many seconds into a call before fax_detect
+   is disabled for the call.  Setting the value to zero disables the timeout.
+
  * Added "subscribe_context" to endpoint.
    If specified, incoming SUBSCRIBE requests will be searched for the matching
    extension in the indicated context. If no "subscribe_context" is specified,
index 5ad1174..5a341f8 100644 (file)
@@ -612,10 +612,12 @@ static struct ast_frame *chan_pjsip_cng_tone_detected(struct ast_sip_session *se
 {
        const char *target_context;
        int exists;
+       int dsp_features;
 
-       /* If we only needed this DSP for fax detection purposes we can just drop it now */
-       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);
+       dsp_features = ast_dsp_get_features(session->dsp);
+       dsp_features &= ~DSP_FEATURE_FAX_DETECT;
+       if (dsp_features) {
+               ast_dsp_set_features(session->dsp, dsp_features);
        } else {
                ast_dsp_free(session->dsp);
                session->dsp = NULL;
@@ -660,6 +662,7 @@ static struct ast_frame *chan_pjsip_cng_tone_detected(struct ast_sip_session *se
 static struct ast_frame *chan_pjsip_read(struct ast_channel *ast)
 {
        struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(ast);
+       struct ast_sip_session *session;
        struct chan_pjsip_pvt *pvt = channel->pvt;
        struct ast_frame *f;
        struct ast_sip_session_media *media = NULL;
@@ -697,22 +700,42 @@ static struct ast_frame *chan_pjsip_read(struct ast_channel *ast)
                return f;
        }
 
-       if (ast_format_cap_iscompatible_format(channel->session->endpoint->media.codecs, f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
+       session = channel->session;
+
+       if (ast_format_cap_iscompatible_format(session->endpoint->media.codecs, f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
                ast_debug(1, "Oooh, got a frame with format of %s on channel '%s' when endpoint '%s' is not configured for it\n",
                        ast_format_get_name(f->subclass.format), ast_channel_name(ast),
-                       ast_sorcery_object_get_id(channel->session->endpoint));
+                       ast_sorcery_object_get_id(session->endpoint));
 
                ast_frfree(f);
                return &ast_null_frame;
        }
 
-       if (channel->session->dsp) {
-               f = ast_dsp_process(ast, channel->session->dsp, f);
+       if (session->dsp) {
+               int dsp_features;
 
+               dsp_features = ast_dsp_get_features(session->dsp);
+               if ((dsp_features & DSP_FEATURE_FAX_DETECT)
+                       && session->endpoint->faxdetect_timeout
+                       && session->endpoint->faxdetect_timeout <= ast_channel_get_up_time(ast)) {
+                       dsp_features &= ~DSP_FEATURE_FAX_DETECT;
+                       if (dsp_features) {
+                               ast_dsp_set_features(session->dsp, dsp_features);
+                       } else {
+                               ast_dsp_free(session->dsp);
+                               session->dsp = NULL;
+                       }
+                       ast_debug(3, "Channel driver fax CNG detection timeout on %s\n",
+                               ast_channel_name(ast));
+               }
+       }
+       if (session->dsp) {
+               f = ast_dsp_process(ast, session->dsp, f);
                if (f && (f->frametype == AST_FRAME_DTMF)) {
                        if (f->subclass.integer == 'f') {
-                               ast_debug(3, "Fax CNG detected on %s\n", ast_channel_name(ast));
-                               f = chan_pjsip_cng_tone_detected(channel->session, f);
+                               ast_debug(3, "Channel driver fax CNG detected on %s\n",
+                                       ast_channel_name(ast));
+                               f = chan_pjsip_cng_tone_detected(session, f);
                        } else {
                                ast_debug(3, "* Detected inband DTMF '%c' on '%s'\n", f->subclass.integer,
                                        ast_channel_name(ast));
index 9069a66..4c42e8a 100644 (file)
 ;t38_udptl_maxdatagram=0        ; T 38 UDPTL maximum datagram size (default:
                                 ; "0")
 ;fax_detect=no  ; Whether CNG tone detection is enabled (default: "no")
+;fax_detect_timeout=30  ; How many seconds into a call before fax_detect is
+                        ; disabled for the call.
+                        ; Zero disables the timeout.
+                        ; (default: "0")
 ;t38_udptl_nat=no       ; Whether NAT support is enabled on UDPTL sessions
                         ; (default: "no")
 ;t38_udptl_ipv6=no      ; Whether IPv6 is used for UDPTL Sessions (default:
diff --git a/contrib/ast-db-manage/config/versions/4a6c67fa9b7a_add_fax_detect_timeout_option.py b/contrib/ast-db-manage/config/versions/4a6c67fa9b7a_add_fax_detect_timeout_option.py
new file mode 100644 (file)
index 0000000..91774c4
--- /dev/null
@@ -0,0 +1,23 @@
+"""add fax_detect_timeout option
+
+Revision ID: 4a6c67fa9b7a
+Revises: 9deac0ae4717
+Create Date: 2016-07-18 18:20:44.249491
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '4a6c67fa9b7a'
+down_revision = '9deac0ae4717'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+    op.add_column('ps_endpoints', sa.Column('fax_detect_timeout', sa.Integer))
+
+
+def downgrade():
+    op.drop_column('ps_endpoints', 'fax_detect_timeout')
+
index 6f59283..9dd70db 100644 (file)
@@ -753,6 +753,8 @@ struct ast_sip_endpoint {
        struct ast_acl_list *acl;
        /* Restrict what IPs are allowed in the Contact header (for registration) */
        struct ast_acl_list *contact_acl;
+       /*! The number of seconds into call to disable fax detection.  (0 = disabled) */
+       unsigned int faxdetect_timeout;
 };
 
 /*!
index 5ca2c99..26dd451 100644 (file)
@@ -139,7 +139,7 @@ struct ast_sip_session {
        struct ast_party_id id;
        /*! Requested capabilities */
        struct ast_format_cap *req_caps;
-       /*! Optional DSP, used only for inband DTMF detection if configured */
+       /*! Optional DSP, used only for inband DTMF/Fax-CNG detection if configured */
        struct ast_dsp *dsp;
        /*! Whether the termination of the session should be deferred */
        unsigned int defer_terminate:1;
index f654e4d..911c269 100644 (file)
@@ -2692,10 +2692,28 @@ void ast_hangup(struct ast_channel *chan)
        ast_channel_unref(chan);
 }
 
+/*!
+ * \internal
+ * \brief Set channel answered time if not already set.
+ * \since 13.11.0
+ *
+ * \param chan Channel to set answered time.
+ *
+ * \return Nothing
+ */
+static void set_channel_answer_time(struct ast_channel *chan)
+{
+       if (ast_tvzero(ast_channel_answertime(chan))) {
+               struct timeval answertime;
+
+               answertime = ast_tvnow();
+               ast_channel_answertime_set(chan, &answertime);
+       }
+}
+
 int ast_raw_answer(struct ast_channel *chan)
 {
        int res = 0;
-       struct timeval answertime;
 
        ast_channel_lock(chan);
 
@@ -2711,8 +2729,11 @@ int ast_raw_answer(struct ast_channel *chan)
                return -1;
        }
 
-       answertime = ast_tvnow();
-       ast_channel_answertime_set(chan, &answertime);
+       /*
+        * Mark when incoming channel answered so we can know how
+        * long the channel has been up.
+        */
+       set_channel_answer_time(chan);
 
        ast_channel_unlock(chan);
 
@@ -3911,6 +3932,12 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
                                        ast_frfree(f);
                                        f = &ast_null_frame;
                                } else {
+                                       /*
+                                        * Mark when outgoing channel answered so we can know how
+                                        * long the channel has been up.
+                                        */
+                                       set_channel_answer_time(chan);
+
                                        ast_setstate(chan, AST_STATE_UP);
                                }
                        } else if (f->subclass.integer == AST_CONTROL_READ_ACTION) {
index af2f937..4034faf 100644 (file)
                                                detected.
                                        </para></description>
                                </configOption>
+                               <configOption name="fax_detect_timeout">
+                                       <synopsis>How long into a call before fax_detect is disabled for the call</synopsis>
+                                       <description><para>
+                                               The option determines how many seconds into a call before the
+                                               fax_detect option is disabled for the call.  Setting the value
+                                               to zero disables the timeout.
+                                       </para></description>
+                               </configOption>
                                <configOption name="t38_udptl_nat" default="no">
                                        <synopsis>Whether NAT support is enabled on UDPTL sessions</synopsis>
                                        <description><para>
index d399f0b..16405eb 100644 (file)
@@ -1849,6 +1849,7 @@ int ast_res_pjsip_initialize_configuration(void)
        ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "t38_udptl_ec", "none", t38udptl_ec_handler, t38udptl_ec_to_str, NULL, 0, 0);
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38_udptl_maxdatagram", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.t38.maxdatagram));
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "fax_detect", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, faxdetect));
+       ast_sorcery_object_field_register(sip_sorcery, "endpoint", "fax_detect_timeout", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, faxdetect_timeout));
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38_udptl_nat", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.t38.nat));
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38_udptl_ipv6", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.t38.ipv6));
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "tone_zone", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, zone));