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