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