<p>George Joseph has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/13303">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">channel.c: Resolve issue with receiving SIP INFO packets for DTMF<br><br>The problem is essentially the same as in ASTERISK-28245. Besides<br>the direct media scenario we have an additionally scenario where a<br>special client is involved. This device mutes audio by default in<br>transmit direction (no rtp frames) and activates audio only by a<br>foot switch. In this situation dtmf input (pin for conferences,<br>transfer features codes , etc) using SIP INFO mode is not<br>understood properly especially when SIP INFO are sent quick after<br>one another.<br><br>This patch ensures that SIP INFO frames are properly queued and<br>processed in the above scenario. The patch also corrects situations<br>where successive dtmf events are received quicker than the<br>signalled event duration (plus minimum gap/pause) allows, i.e. DTMF<br>events has to be buffered in the ast channel read queue and<br>emulation has to be processed asynchronously at slower speed.<br><br>Reported by: Thomas Arimont<br>patches:<br>  trigger_dtmf_emulation.patch submitted by Thomas Arimont (license 5525)<br><br>Change-Id: I309bf61dd065c9978c8e48f5b9a936ab47de64c2<br>---<br>M main/channel.c<br>1 file changed, 97 insertions(+), 1 deletion(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/03/13303/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/main/channel.c b/main/channel.c</span><br><span>index ff25c98..aa2a0cd 100644</span><br><span>--- a/main/channel.c</span><br><span>+++ b/main/channel.c</span><br><span>@@ -2971,6 +2971,33 @@</span><br><span>       return (ast_channel_get_up_time_ms(chan) / 1000);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Determine whether or not we have to trigger dtmf emulating using 50 fps timer events</span><br><span style="color: hsl(120, 100%, 40%);">+ * especially when no voice frames are received during dtmf processing (direct media or muted</span><br><span style="color: hsl(120, 100%, 40%);">+ * sender case using SIP INFO)</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline int should_trigger_dtmf_emulating(struct ast_channel *chan)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_DEFER_DTMF | AST_FLAG_EMULATE_DTMF)) {</span><br><span style="color: hsl(120, 100%, 40%);">+            /* We're in the middle of emulating a digit, or DTMF has been</span><br><span style="color: hsl(120, 100%, 40%);">+              * explicitly deferred. Trigger dtmf with periodic 50 pfs timer events, then. */</span><br><span style="color: hsl(120, 100%, 40%);">+              return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!ast_tvzero(*ast_channel_dtmf_tv(chan)) &&</span><br><span style="color: hsl(120, 100%, 40%);">+                        ast_tvdiff_ms(ast_tvnow(), *ast_channel_dtmf_tv(chan)) < 2*AST_MIN_DTMF_GAP) {</span><br><span style="color: hsl(120, 100%, 40%);">+             /*</span><br><span style="color: hsl(120, 100%, 40%);">+             * We're not in the middle of a digit, but it hasn't been long enough</span><br><span style="color: hsl(120, 100%, 40%);">+          * since the last digit, so we'll have to trigger DTMF furtheron.</span><br><span style="color: hsl(120, 100%, 40%);">+          * Using 2 times AST_MIN_DTMF_GAP to trigger readq reading for possible</span><br><span style="color: hsl(120, 100%, 40%);">+                * buffered next dtmf event</span><br><span style="color: hsl(120, 100%, 40%);">+            */</span><br><span style="color: hsl(120, 100%, 40%);">+           return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static void deactivate_generator_nolock(struct ast_channel *chan)</span><br><span> {</span><br><span>     if (ast_channel_generatordata(chan)) {</span><br><span>@@ -2991,6 +3018,10 @@</span><br><span> {</span><br><span>         ast_channel_lock(chan);</span><br><span>      deactivate_generator_nolock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (should_trigger_dtmf_emulating(chan)) {</span><br><span style="color: hsl(120, 100%, 40%);">+            /* if in the middle of dtmf emulation keep 50 tick per sec timer on rolling */</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_timer_set_rate(ast_channel_timer(chan), 50);</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span>    ast_channel_unlock(chan);</span><br><span> }</span><br><span> </span><br><span>@@ -3867,6 +3898,7 @@</span><br><span> </span><br><span>       if (ast_channel_timingfd(chan) > -1 && ast_channel_fdno(chan) == AST_TIMING_FD) {</span><br><span>                 enum ast_timer_event res;</span><br><span style="color: hsl(120, 100%, 40%);">+             int trigger_dtmf_emulating = should_trigger_dtmf_emulating(chan);</span><br><span> </span><br><span>                ast_clear_flag(ast_channel_flags(chan), AST_FLAG_EXCEPTION);</span><br><span> </span><br><span>@@ -3894,10 +3926,23 @@</span><br><span>                           if (got_ref) {</span><br><span>                                       ao2_ref(data, -1);</span><br><span>                           }</span><br><span style="color: hsl(120, 100%, 40%);">+                             if (trigger_dtmf_emulating) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                 /* generate null frame to trigger dtmf emulating */</span><br><span style="color: hsl(120, 100%, 40%);">+                                   f = &ast_null_frame;</span><br><span style="color: hsl(120, 100%, 40%);">+                                      break;</span><br><span style="color: hsl(120, 100%, 40%);">+                                }</span><br><span style="color: hsl(120, 100%, 40%);">+                     } else if (trigger_dtmf_emulating) {</span><br><span style="color: hsl(120, 100%, 40%);">+                          ast_channel_unlock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+                             /* generate null frame to trigger dtmf emualating */</span><br><span style="color: hsl(120, 100%, 40%);">+                          f = &ast_null_frame;</span><br><span style="color: hsl(120, 100%, 40%);">+                              break;</span><br><span>                       } else {</span><br><span>                             ast_timer_set_rate(ast_channel_timer(chan), 0);</span><br><span>                              ast_channel_fdno_set(chan, -1);</span><br><span>                              ast_channel_unlock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+                             /* generate very last null frame to trigger dtmf emulating */</span><br><span style="color: hsl(120, 100%, 40%);">+                         f = &ast_null_frame;</span><br><span style="color: hsl(120, 100%, 40%);">+                              break;</span><br><span>                       }</span><br><span> </span><br><span>                        /* cannot 'goto done' because the channel is already unlocked */</span><br><span>@@ -3935,6 +3980,7 @@</span><br><span> </span><br><span>         /* Check for pending read queue */</span><br><span>   if (!AST_LIST_EMPTY(ast_channel_readq(chan))) {</span><br><span style="color: hsl(120, 100%, 40%);">+               int skipped_dtmf_frame = 0;</span><br><span>          int skip_dtmf = should_skip_dtmf(chan);</span><br><span> </span><br><span>          AST_LIST_TRAVERSE_SAFE_BEGIN(ast_channel_readq(chan), f, frame_list) {</span><br><span>@@ -3943,6 +3989,7 @@</span><br><span>                        * some later time. */</span><br><span> </span><br><span>                   if ( (f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END) && skip_dtmf) {</span><br><span style="color: hsl(120, 100%, 40%);">+                               skipped_dtmf_frame = 1;</span><br><span>                              continue;</span><br><span>                    }</span><br><span> </span><br><span>@@ -3954,7 +4001,19 @@</span><br><span>               if (!f) {</span><br><span>                    /* There were no acceptable frames on the readq. */</span><br><span>                  f = &ast_null_frame;</span><br><span style="color: hsl(0, 100%, 40%);">-                        ast_channel_alert_write(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+                        if (!skipped_dtmf_frame) {</span><br><span style="color: hsl(120, 100%, 40%);">+                            /*</span><br><span style="color: hsl(120, 100%, 40%);">+                             * Do not trigger alert pipe if only buffered dtmf begin or end frames</span><br><span style="color: hsl(120, 100%, 40%);">+                                 * are left in the readq.</span><br><span style="color: hsl(120, 100%, 40%);">+                              */</span><br><span style="color: hsl(120, 100%, 40%);">+                           ast_channel_alert_write(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+                        } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                              /*</span><br><span style="color: hsl(120, 100%, 40%);">+                             * Safely disable continous timer events if only buffered dtmf begin or end</span><br><span style="color: hsl(120, 100%, 40%);">+                            * frames are left in the readq.</span><br><span style="color: hsl(120, 100%, 40%);">+                               */</span><br><span style="color: hsl(120, 100%, 40%);">+                           ast_timer_disable_continuous(ast_channel_timer(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+                        }</span><br><span>            }</span><br><span> </span><br><span>                /* Interpret hangup and end-of-Q frames to return NULL */</span><br><span>@@ -4091,6 +4150,16 @@</span><br><span>                                   } else</span><br><span>                                               ast_channel_emulate_dtmf_duration_set(chan, AST_DEFAULT_EMULATE_DTMF_DURATION);</span><br><span>                                      ast_log(LOG_DTMF, "DTMF begin emulation of '%c' with duration %u queued on %s\n", f->subclass.integer, ast_channel_emulate_dtmf_duration(chan), ast_channel_name(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                                       /*</span><br><span style="color: hsl(120, 100%, 40%);">+                                     * Start generating 50 fps timer events (null frames) for dtmf emulating</span><br><span style="color: hsl(120, 100%, 40%);">+                                       * independently from any existing incoming voice frames.</span><br><span style="color: hsl(120, 100%, 40%);">+                                      * If channel generator is already activated in regular mode use these</span><br><span style="color: hsl(120, 100%, 40%);">+                                         * timer events to generate null frames.</span><br><span style="color: hsl(120, 100%, 40%);">+                                       */</span><br><span style="color: hsl(120, 100%, 40%);">+                                   if (!ast_channel_generator(chan)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                           ast_timer_set_rate(ast_channel_timer(chan), 50);</span><br><span style="color: hsl(120, 100%, 40%);">+                                      }</span><br><span>                            }</span><br><span>                            if (ast_channel_audiohooks(chan)) {</span><br><span>                                  struct ast_frame *old_frame = f;</span><br><span>@@ -4132,12 +4201,30 @@</span><br><span>                                   ast_channel_emulate_dtmf_duration_set(chan, option_dtmfminduration - f->len);</span><br><span>                                     ast_frfree(f);</span><br><span>                                       f = &ast_null_frame;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                                    /* Start generating 50 fps timer events (null frames) for dtmf emulating</span><br><span style="color: hsl(120, 100%, 40%);">+                                       * independently from any existing incoming voice frames.</span><br><span style="color: hsl(120, 100%, 40%);">+                                      * If channel generator is already activated in regular mode use these</span><br><span style="color: hsl(120, 100%, 40%);">+                                         * timer events to generate null frames.</span><br><span style="color: hsl(120, 100%, 40%);">+                                       */</span><br><span style="color: hsl(120, 100%, 40%);">+                                   if (!ast_channel_generator(chan)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                           ast_timer_set_rate(ast_channel_timer(chan), 50);</span><br><span style="color: hsl(120, 100%, 40%);">+                                      }</span><br><span>                            } else {</span><br><span>                                     ast_log(LOG_DTMF, "DTMF end passthrough '%c' on %s\n", f->subclass.integer, ast_channel_name(chan));</span><br><span>                                    if (f->len < option_dtmfminduration) {</span><br><span>                                                 f->len = option_dtmfminduration;</span><br><span>                                  }</span><br><span>                                    ast_channel_dtmf_tv_set(chan, &now);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                                    /* Start generating 50 fps timer events (null frames) for dtmf emulating</span><br><span style="color: hsl(120, 100%, 40%);">+                                       * independently from any existing incoming voice frames.</span><br><span style="color: hsl(120, 100%, 40%);">+                                      * If channel generator is already activated in regular mode use these</span><br><span style="color: hsl(120, 100%, 40%);">+                                         * timer events to generate null frames.</span><br><span style="color: hsl(120, 100%, 40%);">+                                       */</span><br><span style="color: hsl(120, 100%, 40%);">+                                   if (!ast_channel_generator(chan)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                           ast_timer_set_rate(ast_channel_timer(chan), 50);</span><br><span style="color: hsl(120, 100%, 40%);">+                                      }</span><br><span>                            }</span><br><span>                            if (ast_channel_audiohooks(chan)) {</span><br><span>                                  struct ast_frame *old_frame = f;</span><br><span>@@ -4191,6 +4278,15 @@</span><br><span>                                                    ast_frfree(old_frame);</span><br><span>                                               }</span><br><span>                                    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                                   /* Start generating 50 fps timer events (null frames) for dtmf emulating</span><br><span style="color: hsl(120, 100%, 40%);">+                                       * independently from any existing incoming voice frames.</span><br><span style="color: hsl(120, 100%, 40%);">+                                      * If channel generator is already activated in regular mode use these</span><br><span style="color: hsl(120, 100%, 40%);">+                                         * timer events to generate null frames.</span><br><span style="color: hsl(120, 100%, 40%);">+                                       */</span><br><span style="color: hsl(120, 100%, 40%);">+                                   if (!ast_channel_generator(chan)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                           ast_timer_set_rate(ast_channel_timer(chan), 50);</span><br><span style="color: hsl(120, 100%, 40%);">+                                      }</span><br><span>                            }</span><br><span>                    }</span><br><span>                    break;</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/13303">change 13303</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/13303"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 13 </div>
<div style="display:none"> Gerrit-Change-Id: I309bf61dd065c9978c8e48f5b9a936ab47de64c2 </div>
<div style="display:none"> Gerrit-Change-Number: 13303 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>