<p>Friendly Automation <strong>submitted</strong> this change.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/13343">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Joshua Colp: Looks good to me, but someone else must approve
  Benjamin Keith Ford: Looks good to me, but someone else must approve
  Kevin Harwell: Looks good to me, approved
  Friendly Automation: Approved for Submit

</div><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 additional 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 messages are sent<br>quickly.<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 have 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, 102 insertions(+), 3 deletions(-)<br><br></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 f096087..c350177 100644</span><br><span>--- a/main/channel.c</span><br><span>+++ b/main/channel.c</span><br><span>@@ -2819,6 +2819,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>@@ -2839,6 +2866,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>@@ -3508,6 +3539,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>@@ -3535,10 +3567,26 @@</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                           if (trigger_dtmf_emulating) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                 /*</span><br><span style="color: hsl(120, 100%, 40%);">+                                     * Since we're breaking out of this switch block and not</span><br><span style="color: hsl(120, 100%, 40%);">+                                   * returning, we need to re-lock the channel.</span><br><span style="color: hsl(120, 100%, 40%);">+                                  */</span><br><span style="color: hsl(120, 100%, 40%);">+                                   ast_channel_lock(chan);</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%);">+                          /* 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 style="color: hsl(0, 100%, 40%);">-                         ast_channel_fdno_set(chan, -1);</span><br><span style="color: hsl(0, 100%, 40%);">-                         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>@@ -3576,6 +3624,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>@@ -3584,6 +3633,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>@@ -3595,7 +3645,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>@@ -3801,6 +3863,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>@@ -3842,12 +3914,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>@@ -3901,6 +3991,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/+/13343">change 13343</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/+/13343"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I309bf61dd065c9978c8e48f5b9a936ab47de64c2 </div>
<div style="display:none"> Gerrit-Change-Number: 13343 </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-Reviewer: Benjamin Keith Ford <bford@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Friendly Automation </div>
<div style="display:none"> Gerrit-Reviewer: Joshua Colp <jcolp@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Kevin Harwell <kharwell@digium.com> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>