1616package org .jitsi .impl .neomedia .transform .dtmf ;
1717
1818import java .util .*;
19+ import java .util .concurrent .*;
1920
2021import javax .media .*;
2122
2627import org .jitsi .service .neomedia .codec .*;
2728import org .jitsi .service .neomedia .format .*;
2829import org .jitsi .utils .*;
30+ import org .jitsi .utils .logging .*;
2931
3032/**
3133 * The class is responsible for sending DTMF tones in an RTP audio stream as
@@ -39,6 +41,13 @@ public class DtmfTransformEngine
3941 extends SinglePacketTransformer
4042 implements TransformEngine
4143{
44+ /**
45+ * The <tt>Logger</tt> used by the <tt>DtmfTransformEngine</tt> class and
46+ * its instances for logging output.
47+ */
48+ private static final Logger logger
49+ = Logger .getLogger (DtmfTransformEngine .class );
50+
4251 /**
4352 * The <tt>AudioMediaStreamImpl</tt> that this transform engine was created
4453 * by and that it's going to deliver DTMF packets for.
@@ -501,72 +510,108 @@ private void checkIfCurrentToneMustBeStopped()
501510 }
502511
503512 /**
504- * A simple thread that waits for new tones to be reported from incoming
505- * RTP packets and then delivers them to the <tt>AudioMediaStream</tt>
506- * associated with this engine. The reason we need to do this in a separate
507- * thread is of course the time sensitive nature of incoming RTP packets.
513+ * A simple thread that waits for new tone events to be reported from
514+ * incoming RTP packets and then delivers them as start / end events to the
515+ * <tt>AudioMediaStream</tt> associated with this engine.
516+ * The reason we need to do this in a separate thread is of course the
517+ * time sensitive nature of incoming RTP packets.
508518 */
509519 private class DTMFDispatcher
510520 implements Runnable
511521 {
512522 /** Indicates whether this thread is supposed to be running */
513523 private boolean isRunning = false ;
514524
515- /** The tone that we last received from the reverseTransform thread*/
516- private DTMFRtpTone lastReceivedTone = null ;
517-
518- /** The tone that we last received from the reverseTransform thread*/
525+ /** The tone that we last reported to the listener */
519526 private DTMFRtpTone lastReportedTone = null ;
520527
521528 /**
522- * Have we received end of the currently started tone.
529+ * Timestamp the last reported tone start; used to identify starts
530+ * when the marker bit is lost
531+ */
532+ private long lastReportedStart = 0 ;
533+
534+ /** Timestamp the last reported tone end; used to prevent duplicates */
535+ private long lastReportedEnd = 0 ;
536+
537+ /**
538+ * The maximum number of DTMF events that are queued for processing
523539 */
524- private boolean toEnd = false ;
540+ private int QUEUE_SIZE = 100 ;
525541
526542 /**
527- * Waits for new tone to be reported via the <tt>addTonePacket()</tt>
528- * method and then delivers them to the <tt>AudioMediaStream</tt> that
529- * we are associated with.
543+ * The queue of <tt>DtmfRawPacket</tt>s pending processing
544+ */
545+ private final LinkedBlockingQueue <DtmfRawPacket > queue
546+ = new LinkedBlockingQueue <>(QUEUE_SIZE );
547+
548+ /**
549+ * Waits for new tone events to be reported via the
550+ * <tt>addTonePacket()</tt> method and delivers them as start / end
551+ * events to the <tt>AudioMediaStream</tt> that we are associated with.
530552 */
531553 public void run ()
532554 {
533555 isRunning = true ;
534556
535- DTMFRtpTone temp = null ;
536-
537557 while (isRunning )
538558 {
539- synchronized (this )
559+ DtmfRawPacket pkt ;
560+ DTMFRtpTone tone ;
561+
562+ try
563+ {
564+ pkt = queue .poll (500 , TimeUnit .MILLISECONDS );
565+ }
566+ catch (InterruptedException iex )
540567 {
541- if (lastReceivedTone == null )
542- {
543- try
544- {
545- wait ();
546- }
547- catch (InterruptedException ie ) {}
548- }
549-
550- temp = lastReceivedTone ;
551- // make lastReportedLevels to null
552- // so we will wait for the next tone on next iteration
553- lastReceivedTone = null ;
568+ continue ;
554569 }
555570
556- if (temp != null
557- && ((lastReportedTone == null && !toEnd )
558- || (lastReportedTone != null && toEnd )))
571+ // The current thread has potentially waited.
572+ if (!isRunning )
559573 {
560- //now notify our listener
561- if (mediaStream != null )
562- {
563- mediaStream .fireDTMFEvent (temp , toEnd );
564- if (toEnd )
565- lastReportedTone = null ;
566- else
567- lastReportedTone = temp ;
568- toEnd = false ;
569- }
574+ break ;
575+ }
576+
577+ if (pkt == null )
578+ {
579+ continue ;
580+ }
581+
582+ tone = getToneFromPacket (pkt );
583+
584+ /*
585+ * Detect DTMF tone start by looking for new tones
586+ * It doesn't make sense to look at the 'marked' flag as those
587+ * packets may be re-sent multiple times if they also contain
588+ * the 'end' bit.
589+ */
590+ if (lastReportedTone == null
591+ && pkt .getTimestamp () != lastReportedStart )
592+ {
593+ logger .info ("Delivering DTMF tone start: " +
594+ tone .getValue ());
595+ // now notify our listener
596+ mediaStream .fireDTMFEvent (tone , false );
597+ lastReportedStart = pkt .getTimestamp ();
598+ lastReportedTone = tone ;
599+ }
600+
601+ /*
602+ * Detect DTMF tone end via the explicit 'end' flag.
603+ * End packets are repeated for redundancy. To filter out
604+ * duplicates, we track them by their timestamp.
605+ * Start and end may be present in the same packet, typically
606+ * for durations below 120 ms.
607+ */
608+ if (pkt .isEnd () && pkt .getTimestamp () != lastReportedEnd
609+ && tone == lastReportedTone ) {
610+ logger .info ("Delivering DTMF tone end: " + tone .getValue ());
611+ // now notify our listener
612+ mediaStream .fireDTMFEvent (tone , true );
613+ lastReportedEnd = pkt .getTimestamp ();
614+ lastReportedTone = null ;
570615 }
571616 }
572617 }
@@ -579,13 +624,7 @@ public void run()
579624 */
580625 public void addTonePacket (DtmfRawPacket p )
581626 {
582- synchronized (this )
583- {
584- this .lastReceivedTone = getToneFromPacket (p );
585- this .toEnd = p .isEnd ();
586-
587- notifyAll ();
588- }
627+ queue .offer (p );
589628 }
590629
591630 /**
@@ -594,13 +633,8 @@ public void addTonePacket(DtmfRawPacket p)
594633 */
595634 public void stop ()
596635 {
597- synchronized (this )
598- {
599- this .lastReceivedTone = null ;
600- isRunning = false ;
601-
602- notifyAll ();
603- }
636+ isRunning = false ;
637+ queue .clear ();
604638 }
605639
606640 /**
0 commit comments