<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META http-equiv=Content-Type content="text/html; charset=koi8-r">
<META content="MSHTML 6.00.3790.2817" name=GENERATOR>
<STYLE></STYLE>
</HEAD>
<BODY bgColor=#ffffff>
<DIV><FONT face=Arial size=2>Hi folks,</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT>&nbsp;</DIV>
<DIV><FONT face=Arial size=2>Currently I'm playing with broken DTMFs in 
chan_h323 and got next minds to discuss (as I remember I&nbsp;shown my minds 
about VL-DTMF somewhere already, before 1.4 was released). This is my 
story.</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT>&nbsp;</DIV>
<DIV><FONT face=Arial size=2></FONT>&nbsp;</DIV>
<DIV><FONT face=Arial size=2>Old days we had AST_FRAME_DTMF message only, which 
denotes transmission of single DTMF digit regardless of its 
duration.</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT>&nbsp;</DIV>
<DIV><FONT face=Arial size=2>Immediately before 1.4 beta was released a code for 
support variable-length DTMF (VL-DTMF) which intended to transmit&nbsp;DTMF 
digits and care about its duration&nbsp;as&nbsp;accurate as possible. To support 
this feature, two new types of frame was added - AST_FRAME_DTMF_BEGIN and 
AST_FRAME_DTMF_END with meaningful names. But&nbsp;some channels and old 
versions of Asterisk don't have support for such events, and some channels have 
options to provide optional DTMF tone duration in single event (example of such 
channels includes chan_sip and chan_h323). To resolve compatibility problems, 
old AST_FRAME_DTMF was associated to AST_FRAME_DTMF_END (and frame code assigned 
to AST_FRAME_DTMF_END was equal to "old days" AST_FRAME_DTMF). To pass duration 
information, channel driver should&nbsp;translate this value&nbsp;to number of 
8KHz samples occuped by tone and assign to .samples field. When&nbsp;channel 
which&nbsp;transmits DTMF&nbsp;provides support for VL-DTMF, Asterisk should 
expand single AST_FRAME_DTMF_END&lt;duration&gt; frame into two frames - 
AST_FRAME_DTMF_BEGIN and AST_FRAME_DTMF_END which distanced in time by 
&lt;duration&gt; milliseconds.</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT>&nbsp;</DIV>
<DIV><FONT face=Arial size=2>In real life first impelentation of VL-DTMF was a 
bit far from description shown above. Internally (within Asterisk core) only 
AST_FRAME_DTMF_BEGIN and AST_FRAME_DTMF_END frames&nbsp;are in use. If a channel 
which receives DTMF from endpoint and needs to pass it through Asterisk do not 
have &lt;duration&gt; information or just have single frame, AST_FRAME_DTMF, 
Asterisk core provides "emulation" to generate fake AST_FRAME_DTMF_BEGIN frame 
and delays AST_FRAME_DTMF (which is equal to AST_FRAME_DTMF_END) for 
&lt;duration&gt; (pointed by channel driver or assumed by Asterisk from default 
value) milliseconds. Channel driver which don't support for transmission VL-DTMF 
to client, uses only AST_FRAME_DTMF (=AST_FRAME_DTMF_END) event. As result, we 
have:</FONT></DIV>
<DIV><FONT face=Arial size=2>1) to provide time lag in distribution of single 
AST_FRAME_DTMF event between&nbsp;boxes (because each box will delay it for 
&lt;duration&gt; milliseconds);</FONT></DIV>
<DIV><FONT face=Arial size=2>2) API isn't allow to pass &lt;duration&gt; 
information on transmission of DTMF_END event;</FONT></DIV>
<DIV><FONT face=Arial size=2>3) applications still use AST_FRAME_DTMF 
(=AST_FRAME_DTMF_END) so they reacts to keypress&nbsp;after key 
unpressed.</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT>&nbsp;</DIV>
<DIV><FONT face=Arial size=2>Which sort of problems follow from above? A 
few:</FONT></DIV>
<DIV><FONT face=Arial size=2>1) we always make a delay when peer channel isn't 
support dual-event scheme;</FONT></DIV>
<DIV><FONT face=Arial size=2>2) peer channel should calculate DTMF duration 
itself (for SIP INFO/H.323 signal events) by remembering times of DTMF_BEGIN and 
DTMF_END events;</FONT></DIV>
<DIV><FONT face=Arial size=2>3) applications&nbsp;get&nbsp;AST_FRAME_DTMF_BEGIN 
transparently (because they react to AST_FRAME_DTMF only), which could cause 
some side effects (for example,&nbsp;Echo() application).</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT>&nbsp;</DIV>
<DIV><FONT face=Arial size=2>In the near time ago there was update for VL-DTMF 
code&nbsp;to support end-to-end passing of single-event DTMFs with support of 
passing &lt;duration&gt; information together with 
AST_FRAME_DTMF/AST_FRAME_DTMF_END event. So, positions 1) and 2) of problem list 
shown above should be solved, BUT we still have position 3) opened.</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT>&nbsp;</DIV>
<DIV><FONT face=Arial size=2>To resolve last issue, application should react on 
first DTMF event (BEGIN) only and silently ignore second event (END). This can 
be easily done by re-assigning AST_FRAME_DTMF from AST_FRAME_DTMF_END to 
AST_FRAME_DTMF_BEGIN.&nbsp;How it will impact existing&nbsp;applications? Lets 
examine...</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT>&nbsp;</DIV>
<DIV><FONT face=Arial size=2>When both channels support two-event 
scheme:</FONT></DIV>
<DIV><FONT face=Arial size=2>BEGIN&lt;fake duration&gt; -&gt; bridge -&gt; 
BEGIN&lt;fake duration&gt;</FONT></DIV>
<DIV><FONT face=Arial size=2>END&lt;real duration&gt; -&gt; bridge -&gt; 
END&lt;real duration&gt;</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT>&nbsp;</DIV>
<DIV><FONT face=Arial size=2>When transmitting channel support single-event 
scheme with duration:</FONT></DIV>
<DIV><FONT face=Arial size=2>BEGIN&lt;fake duration&gt; -&gt; bridge -&gt; 
/dev/null</FONT></DIV>
<DIV><FONT face=Arial size=2>END&lt;real duration&gt; -&gt; bridge -&gt; 
AST_FRAME_DTMF&lt;real duration&gt;</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT>&nbsp;</DIV>
<DIV><FONT face=Arial size=2>When transmitting channel support single-event 
scheme but don't support duration bypassing (applications&nbsp;are sort of such 
objects):</FONT></DIV>
<DIV><FONT face=Arial size=2>BEGIN&lt;fake duration&gt; -&gt; bridge -&gt; 
AST_FRAME_DTMF&lt;fake duration&gt;</FONT></DIV>
<DIV><FONT face=Arial size=2>END&lt;real duration&gt; -&gt; bridge -&gt; 
/dev/null</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT>&nbsp;</DIV>
<DIV><FONT face=Arial size=2>When receiving channel support single-event scheme 
with duration but&nbsp;transmitting channel support two-event scheme (bridge 
generates END event itself):</FONT></DIV>
<DIV><FONT face=Arial size=2>BEGIN&lt;duration&gt; -&gt; bridge -&gt; 
BEGIN&lt;duration&gt;</FONT></DIV>
<DIV><FONT face=Arial 
size=2>...audio...&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
-&gt; bridge (generates END&lt;duration&gt;)-&gt; 
END&lt;duration&gt;</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT>&nbsp;</DIV>
<DIV><FONT face=Arial size=2>When receiving channel support single-event scheme 
without duration but&nbsp;transmitting channel support two-event scheme (bridge 
generates END event itself too&nbsp;and&nbsp;replaces missing duration&nbsp;as 
pre-defined value, for example, 100 ms):</FONT></DIV>
<DIV><FONT face=Arial size=2>BEGIN&lt;0&gt; -&gt; bridge (&lt;0&gt; -&gt; 
&lt;std duration&gt;) -&gt; BEGIN&lt;std duration&gt;</FONT></DIV>
<DIV><FONT face=Arial size=2>...audio...&nbsp;&nbsp;&nbsp;-&gt; 
bridge&nbsp;(generates END&lt;std duration&gt;) -&gt; END&lt;std 
duration&gt;</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT>&nbsp;</DIV>
<DIV><FONT face=Arial size=2>When both channels support single-event scheme with 
duration:</FONT></DIV>
<DIV><FONT face=Arial size=2>BEGIN&lt;duration&gt; -&gt; bridge -&gt; 
BEGIN&lt;duration&gt;</FONT></DIV>
<DIV><FONT face=Arial size=2>...audio... -&gt; bridge (generates 
END&lt;duration&gt;) -&gt; /dev/null</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT>&nbsp;</DIV>
<DIV><FONT face=Arial size=2>When receiving channel support single-event scheme 
with duration while transmitting support single-event too but&nbsp;without 
duration:</FONT></DIV>
<DIV><FONT face=Arial size=2>BEGIN&lt;duration&gt; -&gt; bridge -&gt; 
BEGIN&lt;duration&gt; (transmitting channel just ignores &lt;duration&gt; 
value)</FONT></DIV>
<DIV><FONT face=Arial size=2>...audio... -&gt; bridge (generates 
END&lt;duration&gt;) -&gt; /dev/null</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT>&nbsp;</DIV>
<DIV><FONT face=Arial size=2>When transmitting channel support single-event 
scheme without duration while transmitting side support full single-event 
scheme:</FONT></DIV>
<DIV><FONT face=Arial size=2>BEGIN&lt;0&gt; -&gt; bridge (&lt;0&gt; -&gt; 
&lt;std duration&gt;) -&gt; BEGIN&lt;std duration&gt;</FONT></DIV>
<DIV><FONT face=Arial size=2>...audio... -&gt; bridge (generates END&lt;std 
duration&gt;) -&gt; /dev/null</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT>&nbsp;</DIV>
<DIV><FONT face=Arial size=2>When both channels supports single-event scheme 
without duration:</FONT></DIV>
<DIV><FONT face=Arial size=2>BEGIN&lt;0&gt; -&gt; bridge (&lt;0&gt; -&gt; 
&lt;std duration&gt;) -&gt; BEGIN&lt;std duration&gt; (&lt;std duration&gt; 
value ignored)</FONT></DIV>
<DIV><FONT face=Arial size=2>...audio... -&gt; bridge (generates END&lt;std 
duration&gt;) -&gt; /dev/null</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT>&nbsp;</DIV>
<DIV><FONT face=Arial size=2>When bridge placed into "DTMF transmission" state 
(before it generates END event itself) and receives another BEGIN event, it 
immediately generates END event for currently generating DTMF and&nbsp;handles 
new BEGIN event as usual.</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT>&nbsp;</DIV>
<DIV><FONT face=Arial size=2>&lt;fake duration&gt; should be very huge value 
(some hours, for example) to be sure that bridge will stay in "DTMF 
transmission" state until END event (or new BEGIN event) will be 
received.</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT>&nbsp;</DIV>
<DIV><FONT face=Arial size=2></FONT>&nbsp;</DIV>
<DIV><FONT face=Arial size=2>Because we send DTMF event(s) as soon as we get it, 
there&nbsp;should no timing problems&nbsp;with IAX or other channels. One real 
problem is to support existing unclear implementation of VLDTMF in shipped 1.4 
builds.</FONT></DIV>
<DIV><FONT face=Arial size=2></FONT>&nbsp;</DIV>
<DIV><FONT face=Arial size=2></FONT>&nbsp;</DIV>
<DIV><FONT face=Arial size=2>Functionality of the bridge could be described by 
next schematic code:</FONT></DIV>
<DIV><FONT face=Arial size=2>switch(frame.type) {</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp; case AST_FRAME_DTMF_BEGIN:</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp; {</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp; // Remember current time for 
forced END generation</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp; time_t now = 
time(NULL);</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp; // We should use default 
duration if it is not specified by receiver</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp; if(frame.len == 0)</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;frame.len = 
AST_DEFAULT_DTMF_DURATION;</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp; if(chan.dtmf.end_sched &gt;= 0) 
{&nbsp;&nbsp;&nbsp; // This means we placed to "DTMF transmission" state because 
we always set up timer for this state</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;size_t 
duration;</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
ast_sched_destroy(&amp;chan.sched, chan.dtmf.end_sched);</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Just to be sure 
we clean-up everything after us</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; chan.dtmf.end_sched 
= -1;</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Calculate real 
DTMF duration</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; duration =&nbsp;now 
- chan.dtmf.end_start_time;</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Pass END event to 
transmitting channel</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
peerchan-&gt;tech-&gt;send_digit_end(peerchan, chan.dtmf.digit, 
duration);</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp; }</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp; // Send BEGIN event to 
transmitting channel</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp; 
peerchan-&gt;tech-&gt;send_digit_begin(peerchan, frame.subtype, 
frame.len);</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp; // If we supposed to send END 
event, remember all required information and perform transition to "DTMF 
transmission" state</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp; if 
(peerchan-&gt;tech-&gt;send_digit_end) {</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Add 
scheduler for generating END event</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; chan.dtmf.end_sched 
= ast_sched_add(&amp;chan.sched, frame.len, dtmf_end_generate, 
chan);</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Remember 
parameters to generate END event correctly</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; chan.dtmf.digit = 
frame.subtype;</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; chan.dtmf.duration = 
frame.len;</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
chan.dtmf.end_start_time = now;</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp; }</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp; break;</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp; case AST_FRAME_DTMF_END:</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp; if (chan.dtmf.end_sched) 
{</FONT></DIV>
<DIV><FONT face=Arial size=2>
<DIV><FONT face=Arial size=2>&nbsp; &nbsp;&nbsp;&nbsp; // Exit from "DTMF 
transmission" state,</FONT> abort timer</FONT></DIV></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
ast_sched_destroy(&amp;chan.sched, chan.dtmf.end_sched);</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; chan.dtmf.end_sched 
= -1;</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;// Peer supports 
two-event DTMF transmission, then send END event</FONT></DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp; &nbsp; if 
(peerchan-&gt;tech-&gt;send_digit_end)</FONT></DIV>
<DIV><FONT face=Arial size=2>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; 
peerchan-&gt;tech-&gt;send_digit_end(peerchan, chan.dtmf.digit, 
chan.dtmf.duration);</FONT></DIV>
<DIV>&nbsp;&nbsp;&nbsp; }</DIV>
<DIV>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp; // Ignore END event if we sent 
it already&nbsp;by timer</FONT></DIV>&nbsp;&nbsp;&nbsp; break;</DIV>
<DIV>...</DIV>
<DIV>}</DIV>
<DIV>&nbsp;</DIV>
<DIV>int dtmf_end_generate(void *arg)</DIV>
<DIV>{</DIV>
<DIV>&nbsp; struct ast_channel *chan = (struct ast_channel *)arg;</DIV>
<DIV>&nbsp; struct ast_channel *peerchan = ast_bridgedpeer(chan);</DIV>
<DIV>&nbsp;</DIV>
<DIV>&nbsp; // Timer is fired, exit from "DTMF transmission" state</DIV>
<DIV>&nbsp; chan-&gt;dtmf.end_sched = -1;</DIV>
<DIV>&nbsp;</DIV>
<DIV>&nbsp; // Send END event if peer supports for two-event DTMF 
transmission</DIV>
<DIV>
<DIV><FONT face=Arial size=2>&nbsp; if 
(peerchan-&gt;tech-&gt;send_digit_end)</FONT></DIV>
<DIV><FONT face=Arial size=2>
<DIV><FONT face=Arial size=2>&nbsp;&nbsp;&nbsp; 
peerchan-&gt;tech-&gt;send_digit_end(peerchan, chan-&gt;dtmf.digit, 
chan-&gt;dtmf.duration);</FONT></DIV>
<DIV>&nbsp;</DIV>
<DIV>&nbsp; // Do not re-schedule event generation</DIV>
<DIV>&nbsp; return 0;</DIV></FONT></DIV></DIV>
<DIV>}</DIV></FONT></DIV>
<DIV><FONT face=Arial size=2></FONT>&nbsp;</DIV>
<DIV><FONT face=Arial size=2></FONT>&nbsp;</DIV>
<DIV><FONT face=Arial size=2>WBR,</FONT></DIV>
<DIV><FONT face=Arial size=2>Paul.</FONT></DIV></BODY></HTML>