From 6d1f4b4a9deb86fa5759a2653943b574f177c007 Mon Sep 17 00:00:00 2001
From: Lyubomir Marinov <lyubomir.marinov@jitsi.org>
Date: Mon, 8 Dec 2014 11:24:39 +0200
Subject: [PATCH] Allows the TransformEngineChain of a MediaStreamImpl to be
 programmatically amended.

---
 .../jitsi/impl/neomedia/MediaStreamImpl.java  |  34 +++++-
 .../rtp/translator/PushSourceStreamImpl.java  |   3 +-
 .../transform/TransformEngineChain.java       | 113 +++++++++++++-----
 .../transform/TransformInputStream.java       |  28 +++--
 .../transform/srtp/SRTPCryptoContext.java     |  56 +++++----
 5 files changed, 165 insertions(+), 69 deletions(-)

diff --git a/src/org/jitsi/impl/neomedia/MediaStreamImpl.java b/src/org/jitsi/impl/neomedia/MediaStreamImpl.java
index 9c93ccd7..7a3bd303 100644
--- a/src/org/jitsi/impl/neomedia/MediaStreamImpl.java
+++ b/src/org/jitsi/impl/neomedia/MediaStreamImpl.java
@@ -830,7 +830,6 @@ private TransformEngineChain createTransformEngineChain()
 
         // DTMF
         DtmfTransformEngine dtmfEngine = createDtmfTransformEngine();
-
         if (dtmfEngine != null)
             engineChain.add(dtmfEngine);
 
@@ -876,7 +875,6 @@ private TransformEngineChain createTransformEngineChain()
          * prevent RTP packets from a muted audio source from being decrypted.
          */
         SsrcTransformEngine ssrcEngine = createSsrcTransformEngine();
-
         if (ssrcEngine != null)
             engineChain.add(ssrcEngine);
 
@@ -2109,15 +2107,43 @@ protected void rtpConnectorChanged(
              * stream.
              */
             if(newValue instanceof RTPTransformUDPConnector)
-                ((RTPTransformUDPConnector)newValue)
+            {
+                ((RTPTransformUDPConnector) newValue)
                     .setEngine(createTransformEngineChain());
+            }
             else if(newValue instanceof RTPTransformTCPConnector)
-                ((RTPTransformTCPConnector)newValue)
+            {
+                ((RTPTransformTCPConnector) newValue)
                     .setEngine(createTransformEngineChain());
+            }
 
             if (rtpConnectorTarget != null)
                 doSetTarget(rtpConnectorTarget);
         }
+
+        /*
+         * TODO The following is a very ugly way to expose the RTPConnector
+         * created by this instance so it may be configured from outside the
+         * class hierarchy. That's why the property in use bellow is not defined
+         * as a well-known constant and is to be considered internal and likely
+         * to be removed in a future revision.
+         */
+        try
+        {
+            firePropertyChange(
+                    MediaStreamImpl.class.getName() + ".rtpConnector",
+                    oldValue,
+                    newValue);
+        }
+        catch (Throwable t)
+        {
+            if (t instanceof InterruptedException)
+                Thread.currentThread().interrupt();
+            else if (t instanceof ThreadDeath)
+                throw (ThreadDeath) t;
+            else
+                logger.error(t);
+        }
     }
 
     /**
diff --git a/src/org/jitsi/impl/neomedia/rtp/translator/PushSourceStreamImpl.java b/src/org/jitsi/impl/neomedia/rtp/translator/PushSourceStreamImpl.java
index 4d8013ea..dd0a7b88 100644
--- a/src/org/jitsi/impl/neomedia/rtp/translator/PushSourceStreamImpl.java
+++ b/src/org/jitsi/impl/neomedia/rtp/translator/PushSourceStreamImpl.java
@@ -208,8 +208,7 @@ public int read(byte[] buffer, int offset, int length)
             if (length < pktLength)
             {
                 throw new IOException(
-                        "Length " + length
-                            + " is insuffient. Must be at least "
+                        "Length " + length + " is insuffient. Must be at least "
                             + pktLength + ".");
             }
 
diff --git a/src/org/jitsi/impl/neomedia/transform/TransformEngineChain.java b/src/org/jitsi/impl/neomedia/transform/TransformEngineChain.java
index c3c8eb95..f4e71711 100644
--- a/src/org/jitsi/impl/neomedia/transform/TransformEngineChain.java
+++ b/src/org/jitsi/impl/neomedia/transform/TransformEngineChain.java
@@ -13,7 +13,7 @@
  * stream.
  *
  * @author Emil Ivov
- * @author Lubomir Marinov
+ * @author Lyubomir Marinov
  */
 public class TransformEngineChain
     implements TransformEngine
@@ -21,22 +21,23 @@ public class TransformEngineChain
 
     /**
      * The sequence of <tt>TransformEngine</tt>s whose
-     * <tt>PacketTransformer</tt>s that this engine chain will be applying to
-     * RTP and RTCP packets.
+     * <tt>PacketTransformer</tt>s this engine chain will be applying to RTP and
+     * RTCP packets. Implemented as copy-on-write storage for the purposes of
+     * performance.
      */
-    private final TransformEngine[] engineChain;
+    private TransformEngine[] engineChain;
 
     /**
      * The sequence of <tt>PacketTransformer</tt>s that this engine chain will
-     * be applying to RTP packets.
+     * be applying to RTCP packets.
      */
-    private PacketTransformerChain rtpTransformChain;
+    private PacketTransformerChain rtcpTransformChain;
 
     /**
      * The sequence of <tt>PacketTransformer</tt>s that this engine chain will
-     * be applying to RTCP packets.
+     * be applying to RTP packets.
      */
-    private PacketTransformerChain rtcpTransformChain;
+    private PacketTransformerChain rtpTransformChain;
 
     /**
      * Creates a new <tt>TransformEngineChain</tt> using the
@@ -52,15 +53,56 @@ public TransformEngineChain(TransformEngine[] engineChain)
         this.engineChain = engineChain.clone();
     }
 
+    public boolean addEngine(TransformEngine engine)
+    {
+        if (engine == null)
+            throw new NullPointerException("engine");
+
+        synchronized (this)
+        {
+            TransformEngine[] oldValue = this.engineChain;
+
+            for (TransformEngine e : oldValue)
+            {
+                if (engine.equals(e))
+                    return false;
+            }
+
+            int oldLength = oldValue.length;
+            TransformEngine[] newValue = new TransformEngine[oldLength + 1];
+
+            if (oldLength != 0)
+                System.arraycopy(oldValue, 0, newValue, 0, oldLength);
+            newValue[oldLength] = engine;
+        }
+
+        return true;
+    }
+
+    /**
+     * Gets the sequence of <tt>TransformEngine</tt>s whose
+     * <tt>PacketTransformer</tt>s this engine chain applies to RTP and RTCP
+     * packets.
+     *
+     * @return the sequence of <tt>TransformEngine</tt>s whose
+     * <tt>PacketTransformer</tt>s this engine chain applies to RTP and RTCP
+     * packets
+     */
+    public TransformEngine[] getEngineChain()
+    {
+        return engineChain.clone();
+    }
+
     /**
      * Returns the meta <tt>PacketTransformer</tt> that will be applying
      * RTCP transformations from all engines registered in this
      * <tt>TransformEngineChain</tt>.
      *
-     * @return a <tt>PacketTransformerChain</tt> over all RTP transformers in
+     * @return a <tt>PacketTransformerChain</tt> over all RTCP transformers in
      * this engine chain.
      */
-    public PacketTransformer getRTPTransformer()
+    @Override
+    public PacketTransformer getRTCPTransformer()
     {
         /*
          * XXX Certain TransformEngine implementations in engineChain may
@@ -76,21 +118,21 @@ public PacketTransformer getRTPTransformer()
 
         synchronized (this)
         {
-            if (rtpTransformChain == null)
+            if (rtcpTransformChain == null)
             {
-                rtpTransformChain = new PacketTransformerChain(true);
+                rtcpTransformChain = new PacketTransformerChain(false);
                 invokeOnEngineChain = true;
             }
             else
             {
                 invokeOnEngineChain = false;
             }
-            rtpTransformer = rtpTransformChain;
+            rtpTransformer = rtcpTransformChain;
         }
         if (invokeOnEngineChain)
         {
             for (TransformEngine engine : engineChain)
-                engine.getRTPTransformer();
+                engine.getRTCPTransformer();
         }
         return rtpTransformer;
     }
@@ -100,10 +142,11 @@ public PacketTransformer getRTPTransformer()
      * RTCP transformations from all engines registered in this
      * <tt>TransformEngineChain</tt>.
      *
-     * @return a <tt>PacketTransformerChain</tt> over all RTCP transformers in
+     * @return a <tt>PacketTransformerChain</tt> over all RTP transformers in
      * this engine chain.
      */
-    public PacketTransformer getRTCPTransformer()
+    @Override
+    public PacketTransformer getRTPTransformer()
     {
         /*
          * XXX Certain TransformEngine implementations in engineChain may
@@ -119,21 +162,21 @@ public PacketTransformer getRTCPTransformer()
 
         synchronized (this)
         {
-            if (rtcpTransformChain == null)
+            if (rtpTransformChain == null)
             {
-                rtcpTransformChain = new PacketTransformerChain(false);
+                rtpTransformChain = new PacketTransformerChain(true);
                 invokeOnEngineChain = true;
             }
             else
             {
                 invokeOnEngineChain = false;
             }
-            rtpTransformer = rtcpTransformChain;
+            rtpTransformer = rtpTransformChain;
         }
         if (invokeOnEngineChain)
         {
             for (TransformEngine engine : engineChain)
-                engine.getRTCPTransformer();
+                engine.getRTPTransformer();
         }
         return rtpTransformer;
     }
@@ -156,7 +199,7 @@ private class PacketTransformerChain
 
         /**
          * Creates an instance of this packet transformer and prepares it to
-         * deal with RTP or RTCP according to the <tt>isRtp</tt> arg.
+         * deal with RTP or RTCP according to the <tt>isRtp</tt> argument.
          *
          * @param isRtp <tt>true</tt> if this transformer will be dealing with
          * RTP (i.e. will transform packets via the RTP transformers in this
@@ -172,6 +215,7 @@ public PacketTransformerChain(boolean isRtp)
          *
          * Propagate the close to all transformers in chain.
          */
+        @Override
         public void close()
         {
             for (TransformEngine engine : engineChain)
@@ -191,13 +235,18 @@ public void close()
         /**
          * {@inheritDoc}
          *
-         * Transforms the given packets using each of the
-         * <tt>TransformEngine</tt>-s in the engine chain in order.
+         * Reverse-transforms the given packets using each of the
+         * <tt>TransformEngine</tt>-s in the engine chain in reverse order.
          */
-        public RawPacket[] transform(RawPacket[] pkts)
+        @Override
+        public RawPacket[] reverseTransform(RawPacket pkts[])
         {
-            for (TransformEngine engine : engineChain)
+            TransformEngine[] engineChain
+                = TransformEngineChain.this.engineChain;
+
+            for (int i = engineChain.length - 1 ; i >= 0; i--)
             {
+                TransformEngine engine = engineChain[i];
                 PacketTransformer pTransformer
                     = isRtp
                         ? engine.getRTPTransformer()
@@ -206,7 +255,7 @@ public RawPacket[] transform(RawPacket[] pkts)
                 //the packet transformer may be null if for example the engine
                 //only does RTP transformations and this is an RTCP transformer.
                 if (pTransformer != null)
-                    pkts = pTransformer.transform(pkts);
+                    pkts = pTransformer.reverseTransform(pkts);
             }
 
             return pkts;
@@ -215,14 +264,14 @@ public RawPacket[] transform(RawPacket[] pkts)
         /**
          * {@inheritDoc}
          *
-         * Reverse-transforms the given packets using each of the
-         * <tt>TransformEngine</tt>-s in the engine chain in reverse order.
+         * Transforms the given packets using each of the
+         * <tt>TransformEngine</tt>-s in the engine chain in order.
          */
-        public RawPacket[] reverseTransform(RawPacket pkts[])
+        @Override
+        public RawPacket[] transform(RawPacket[] pkts)
         {
-            for (int i = engineChain.length - 1 ; i >= 0; i--)
+            for (TransformEngine engine : engineChain)
             {
-                TransformEngine engine = engineChain[i];
                 PacketTransformer pTransformer
                     = isRtp
                         ? engine.getRTPTransformer()
@@ -231,7 +280,7 @@ public RawPacket[] reverseTransform(RawPacket pkts[])
                 //the packet transformer may be null if for example the engine
                 //only does RTP transformations and this is an RTCP transformer.
                 if (pTransformer != null)
-                    pkts = pTransformer.reverseTransform(pkts);
+                    pkts = pTransformer.transform(pkts);
             }
 
             return pkts;
diff --git a/src/org/jitsi/impl/neomedia/transform/TransformInputStream.java b/src/org/jitsi/impl/neomedia/transform/TransformInputStream.java
index d16217c7..c35ba426 100644
--- a/src/org/jitsi/impl/neomedia/transform/TransformInputStream.java
+++ b/src/org/jitsi/impl/neomedia/transform/TransformInputStream.java
@@ -14,7 +14,7 @@
  * Extends <tt>RTPConnectorInputStream</tt> with transform logic.
  *
  * @author Bing SU (nova.su@gmail.com)
- * @author Lubomir Marinov
+ * @author Lyubomir Marinov
  * @author Boris Grozev
  */
 public abstract class TransformInputStream<T>
@@ -26,6 +26,13 @@ public abstract class TransformInputStream<T>
      */
     private PacketTransformer transformer;
 
+    /**
+     * Initializes a new <tt>TransformInputStream</tt> which is to transform the
+     * packets received from a specific (network) socket.
+     *
+     * @param socket the (network) socket from which packets are to be received
+     * and transformed by the new instance
+     */
     protected TransformInputStream(T socket)
     {
         super(socket);
@@ -49,16 +56,21 @@ protected TransformInputStream(T socket)
     @Override
     protected RawPacket[] createRawPacket(DatagramPacket datagramPacket)
     {
-        PacketTransformer transformer = getTransformer();
-        RawPacket pkts[] = super.createRawPacket(datagramPacket);
+        RawPacket[] pkts = super.createRawPacket(datagramPacket);
 
-        /* Don't try to transform invalid packets (for ex. empty) */
-        for (int i=0; i<pkts.length; i++)
-            if(pkts[i] != null && pkts[i].isInvalid())
-                pkts[i] = null; //null elements are just ignored
+        // Don't try to transform invalid (e.g. empty) packets.
+        for (int i = 0; i < pkts.length; i++)
+        {
+            RawPacket pkt = pkts[i];
+
+            if (pkt != null && pkt.isInvalid())
+                pkts[i] = null; // null elements are ignored
+        }
+
+        PacketTransformer transformer = getTransformer();
 
         return
-                (transformer == null)
+            (transformer == null)
                 ? pkts
                 : transformer.reverseTransform(pkts);
     }
diff --git a/src/org/jitsi/impl/neomedia/transform/srtp/SRTPCryptoContext.java b/src/org/jitsi/impl/neomedia/transform/srtp/SRTPCryptoContext.java
index 4c999589..041481b5 100644
--- a/src/org/jitsi/impl/neomedia/transform/srtp/SRTPCryptoContext.java
+++ b/src/org/jitsi/impl/neomedia/transform/srtp/SRTPCryptoContext.java
@@ -28,6 +28,8 @@
 
 import java.util.*;
 
+import javax.media.*;
+
 import org.bouncycastle.crypto.params.*;
 import org.jitsi.bccontrib.params.*;
 import org.jitsi.impl.neomedia.*;
@@ -563,13 +565,13 @@ public boolean reverseTransformPacket(RawPacket pkt)
     {
         if (logger.isDebugEnabled())
         {
-            logger.debug("Reverse transform for SSRC " + this.ssrc
-                + " SeqNo=" + pkt.getSequenceNumber()
-                + " s_l=" + s_l
-                + " seqNumSet=" + seqNumSet
-                + " guessedROC=" + guessedROC
-                + " roc=" + roc
-            );
+            logger.debug(
+                    "Reverse transform for SSRC " + this.ssrc
+                        + " SeqNo=" + pkt.getSequenceNumber()
+                        + " s_l=" + s_l
+                        + " seqNumSet=" + seqNumSet
+                        + " guessedROC=" + guessedROC
+                        + " roc=" + roc);
         }
 
         int seqNo = pkt.getSequenceNumber();
@@ -591,25 +593,33 @@ public boolean reverseTransformPacket(RawPacket pkt)
             // Authenticate the packet.
             if (authenticatePacket(pkt))
             {
-                switch (policy.getEncType())
+                // If a RawPacket is flagged with Buffer.FLAG_DISCARD, then it
+                // should have been discarded earlier. Anyway, at least skip its
+                // decrypting. We flag a RawPacket with Buffer.FLAG_SILENCE when
+                // we want to ignore its payload. In the context of SRTP, we
+                // want to skip its decrypting.
+                if ((pkt.getFlags()
+                            & (Buffer.FLAG_DISCARD /* | Buffer.FLAG_SILENCE */))
+                        == 0)
                 {
-                // Decrypt the packet using Counter Mode encryption.
-                case SRTPPolicy.AESCM_ENCRYPTION:
-                case SRTPPolicy.TWOFISH_ENCRYPTION:
-                    processPacketAESCM(pkt);
-                    break;
-        
-                // Decrypt the packet using F8 Mode encryption.
-                case SRTPPolicy.AESF8_ENCRYPTION:
-                case SRTPPolicy.TWOFISHF8_ENCRYPTION:
-                    processPacketAESF8(pkt);
-                    break;
+                    switch (policy.getEncType())
+                    {
+                    // Decrypt the packet using Counter Mode encryption.
+                    case SRTPPolicy.AESCM_ENCRYPTION:
+                    case SRTPPolicy.TWOFISH_ENCRYPTION:
+                        processPacketAESCM(pkt);
+                        break;
+
+                    // Decrypt the packet using F8 Mode encryption.
+                    case SRTPPolicy.AESF8_ENCRYPTION:
+                    case SRTPPolicy.TWOFISHF8_ENCRYPTION:
+                        processPacketAESF8(pkt);
+                        break;
+                    }
                 }
 
-                /*
-                 * Update the rollover counter and highest sequence number if
-                 * necessary.
-                 */
+                // Update the rollover counter and highest sequence number if
+                // necessary.
                 update(seqNo, guessedIndex);
 
                 b = true;
-- 
GitLab