diff --git a/doc/rtcp-termination.md b/doc/rtcp-termination.md
index c1f2fd8baf..3d4ebd25e2 100644
--- a/doc/rtcp-termination.md
+++ b/doc/rtcp-termination.md
@@ -65,7 +65,8 @@ We implemented that in the following way :
- libjitsi and/or other parts of the video bridge can override the
default RTCP report generation behavior by parametrizing the RTP
session manager (in FMJ) with an implementation of the
- `RTCPReportBuilder` interface.
+ `RTCPReportBuilder` interface. Keep in mind that in the JVB each content
+ has its own RTP translator.
- Created an interface called `RTCPPacketTransformer` whose purpose is
to inspect and/or modify and/or eliminate incoming RTCP packets. It
diff --git a/lib/fmj.jar b/lib/fmj.jar
index 3de9f38279..8c510757f9 100644
Binary files a/lib/fmj.jar and b/lib/fmj.jar differ
diff --git a/lib/libjitsi-1.0-SNAPSHOT.jar b/lib/libjitsi-1.0-SNAPSHOT.jar
index f6bfe149a0..dfd8b4c477 100644
Binary files a/lib/libjitsi-1.0-SNAPSHOT.jar and b/lib/libjitsi-1.0-SNAPSHOT.jar differ
diff --git a/src/main/java/org/jitsi/videobridge/Content.java b/src/main/java/org/jitsi/videobridge/Content.java
index a4b938a2a9..636f0f41ee 100644
--- a/src/main/java/org/jitsi/videobridge/Content.java
+++ b/src/main/java/org/jitsi/videobridge/Content.java
@@ -15,7 +15,6 @@
*/
package org.jitsi.videobridge;
-import java.beans.*;
import java.io.*;
import java.lang.ref.*;
import java.util.*;
@@ -23,13 +22,11 @@
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
import org.jitsi.impl.neomedia.rtp.translator.*;
-import org.jitsi.service.configuration.*;
import org.jitsi.service.neomedia.*;
import org.jitsi.service.neomedia.device.*;
import org.jitsi.service.neomedia.recording.*;
import org.jitsi.util.*;
import org.jitsi.videobridge.eventadmin.*;
-import org.jitsi.videobridge.rtcp.*;
import org.osgi.framework.*;
/**
@@ -63,8 +60,6 @@ public class Content
*/
private boolean expired = false;
- private String fallbackSrategyFQN;
-
/**
* The local synchronization source identifier (SSRC) associated with this
* Content, which is to to be pre-announced by the
@@ -117,8 +112,6 @@ public class Content
private RTCPFeedbackMessageSender rtcpFeedbackMessageSender;
- private String rtcpTerminationStrategyFQN;
-
/**
* The Object which synchronizes the access to the RTP-level relays
* (i.e. {@link #mixer} and {@link #rtpTranslator}) provided by this
@@ -151,23 +144,6 @@ public Content(Conference conference, String name)
this.conference = conference;
this.name = name;
- // If endpoints have changed, maybe change the RTCP termination
- // strategy.
- // TODO(gp) this should not always be enabled, it should be configurable
- this.conference.addPropertyChangeListener(
- new PropertyChangeListener()
- {
- @Override
- public void propertyChange(PropertyChangeEvent ev)
- {
- if (Conference.ENDPOINTS_PROPERTY_NAME.equals(
- ev.getPropertyName()))
- {
- updateRTCPTerminationStrategy();
- }
- }
- });
-
mediaType = MediaType.parseString(this.name);
EventAdmin eventAdmin
@@ -487,7 +463,7 @@ void feedKnownSsrcsToSynchronizer()
* ssrc in its list of received SSRCs, or null in case no
* such Channel exists.
*/
- Channel findChannel(long ssrc)
+ public Channel findChannel(long ssrc)
{
for (Channel channel : getChannels())
{
@@ -790,13 +766,24 @@ public RTPTranslator getRTPTranslator()
RTPTranslatorImpl rtpTranslatorImpl
= (RTPTranslatorImpl) rtpTranslator;
+ /**
+ * XXX(gp) some thoughts on the use of initialLocalSSRC:
+ *
+ * 1. By using the initialLocalSSRC as the SSRC of the
+ * translator aren't we breaking the mixing
+ * functionality? because FMJ is going to use its "own"
+ * SSRC to for mixed stream, which remains unannounced.
+ *
+ * 2. By using an initialLocalSSRC we're losing the FMJ
+ * collision detection mechanism.
+ *
+ * The places that are involved in this have been tagged
+ * with TAG(cat4-local-ssrc-hurricane).
+ */
initialLocalSSRC = Videobridge.RANDOM.nextInt();
rtpTranslatorImpl.setLocalSSRC(initialLocalSSRC);
- if (MediaType.VIDEO.equals(getMediaType()))
- setRTCPTerminationStrategyFromConfiguration();
-
rtcpFeedbackMessageSender
= rtpTranslatorImpl.getRtcpFeedbackMessageSender();
}
@@ -891,63 +878,6 @@ public boolean setRecording(boolean recording, String path)
return this.recording;
}
- public void setRTCPTerminationFallbackStrategyFQN(
- String rtcpTerminationFallbackStrategyFQN)
- {
- this.fallbackSrategyFQN = rtcpTerminationFallbackStrategyFQN;
- }
-
- public void setRTCPTerminationStrategyFQN(String strategyFQN)
- {
- // NOTE(gp) we want to *always* update the RTCP termination strategy,
- // even if rtcpTerminationStrategyFQN.equals(strategyFQN).
- //
- // The reason for this is that "this.rtpTranslator" (the translator of
- // this content) can be "null" when the updateRTCPTerminationStrategy()
- // method is called, and, as a result, "this.rtpTranslator" might have
- // not been configured with the correct RTCP termination strategy.
- //
- // This is especially true when adaptive last N and adaptive simulcast
- // are used. Those two features need the basic bridge RTCP termination
- // strategy but, when they set it, "this.translator" is "null".
- //
- // Calling the updateRTCPTerminationStrategy() method even if
- // rtcpTerminationStrategyFQN.equals(strategyFQN) is fine because the
- // method checks for class equality.
-
- this.rtcpTerminationStrategyFQN = strategyFQN;
- this.updateRTCPTerminationStrategy();
- }
-
- /**
- * Sets the RTCP termination strategy of the rtpTranslator to the
- * one specified in the configuration.
- *
- */
- public void setRTCPTerminationStrategyFromConfiguration()
- {
- if (!MediaType.VIDEO.equals(mediaType))
- {
- return;
- }
-
- ConfigurationService cfg = getConference().getVideobridge()
- .getConfigurationService();
-
- if (cfg != null)
- {
- String fallbackFQN = cfg.getString(
- Videobridge.RTCP_TERMINATION_FALLBACK_STRATEGY_PNAME, "");
-
- setRTCPTerminationFallbackStrategyFQN(fallbackFQN);
-
- String strategyFQN = cfg.getString(
- Videobridge.RTCP_TERMINATION_STRATEGY_PNAME, "");
-
- setRTCPTerminationStrategyFQN(strategyFQN);
- }
- }
-
/**
* Tries to start a specific Recorder.
* @param recorder the Recorder to start.
@@ -993,68 +923,6 @@ public void touch()
}
}
- /**
- * Sets the RTCP termination strategy of the rtpTranslator to the
- * one specified in the rtcpTerminationStrategyFQN parameter.
- *
- */
- private void updateRTCPTerminationStrategy()
- {
- Conference conf = this.conference;
-
- if (conf == null)
- return;
-
- RTPTranslator rtpTranslator = this.rtpTranslator;
-
- if (rtpTranslator == null)
- return;
-
- String strategyFQN;
-
- // If the conference has less than 3 participants, it switches the RTCP
- // termination strategy to the RTCP termination strategy defined by
- // {@link fallbackSrategyFQN}. It restores the configured RTCP
- // termination strategy otherwise.
- if (conf.getEndpointCount() < 3)
- {
- strategyFQN = this.fallbackSrategyFQN;
- if (StringUtils.isNullOrEmpty(strategyFQN))
- strategyFQN = this.rtcpTerminationStrategyFQN;
- }
- else
- {
- strategyFQN = this.rtcpTerminationStrategyFQN;
- }
-
- if (StringUtils.isNullOrEmpty(strategyFQN))
- return;
-
- try
- {
- Class> clazz = Class.forName(strategyFQN);
- RTCPTerminationStrategy oldStrategy
- = rtpTranslator.getRTCPTerminationStrategy();
-
- if (clazz.isInstance(oldStrategy))
- return;
-
- RTCPTerminationStrategy strategy
- = (RTCPTerminationStrategy) clazz.newInstance();
-
- if (strategy instanceof AbstractBridgeRTCPTerminationStrategy)
- ((AbstractBridgeRTCPTerminationStrategy) strategy).setConference(conf);
-
- rtpTranslator.setRTCPTerminationStrategy(strategy);
- }
- catch (Exception e)
- {
- logger.error(
- "Failed to configure the RTCP termination strategy",
- e);
- }
- }
-
private static class RTPTranslatorWriteFilter
implements RTPTranslator.WriteFilter
{
diff --git a/src/main/java/org/jitsi/videobridge/VideoChannel.java b/src/main/java/org/jitsi/videobridge/VideoChannel.java
index 049c9f9a14..2d3e3fc678 100644
--- a/src/main/java/org/jitsi/videobridge/VideoChannel.java
+++ b/src/main/java/org/jitsi/videobridge/VideoChannel.java
@@ -30,10 +30,13 @@
import org.jitsi.impl.neomedia.*;
import org.jitsi.impl.neomedia.rtcp.*;
+import org.jitsi.impl.neomedia.rtcp.termination.strategies.*;
import org.jitsi.impl.neomedia.rtp.remotebitrateestimator.*;
import org.jitsi.impl.neomedia.transform.*;
+import org.jitsi.service.configuration.*;
+import org.jitsi.service.neomedia.*;
import org.jitsi.service.neomedia.codec.*;
-import org.jitsi.util.Logger;
+import org.jitsi.util.*;
import org.jitsi.videobridge.ratecontrol.*;
import org.jitsi.videobridge.rtcp.*;
import org.jitsi.videobridge.simulcast.*;
@@ -57,6 +60,13 @@ public class VideoChannel
*/
private static final int INCOMING_BITRATE_INTERVAL_MS = 5000;
+ /**
+ * The name of the property which specifies the FQN name of the RTCP
+ * strategy to use by default.
+ */
+ public static final String RTCP_TERMINATION_STRATEGY_PNAME
+ = "org.jitsi.videobridge.rtcp.strategy";
+
/**
* The Logger used by the VideoChannel class and its
* instances to print debug information.
@@ -183,6 +193,66 @@ public VideoChannel(Content content,
setTransformEngine(new RtpChannelTransformEngine(this));
}
+ /**
+ * {@inheritDoc}
+ *
+ * Creates media stream.
+ */
+ @Override
+ public void initialize()
+ throws IOException
+ {
+ super.initialize();
+
+ ConfigurationService configurationService
+ = getContent().getConference().getVideobridge()
+ .getConfigurationService();
+
+ if (configurationService == null)
+ {
+ return;
+ }
+
+ // Initialize the RTCP termination strategy from the configuration.
+ String strategyFQN = configurationService.getString(
+ RTCP_TERMINATION_STRATEGY_PNAME, "");
+
+ RTCPTerminationStrategy strategy;
+ if (StringUtils.isNullOrEmpty(strategyFQN))
+ {
+ return;
+ }
+
+ try
+ {
+ Class> clazz = Class.forName(strategyFQN);
+ strategy = (RTCPTerminationStrategy) clazz.newInstance();
+ }
+ catch (Exception e)
+ {
+ logger.error(
+ "Failed to configure the video channel RTCP termination strategy",
+ e);
+
+ return;
+ }
+
+ // Initialize the RTCP termination strategy.
+ if (strategy instanceof MediaStreamRTCPTerminationStrategy)
+ {
+ ((MediaStreamRTCPTerminationStrategy) strategy)
+ .initialize(getStream());
+ }
+ else if (strategy
+ instanceof VideoChannelRTCPTerminationStrategy)
+ {
+ ((VideoChannelRTCPTerminationStrategy) strategy)
+ .initialize(this);
+ }
+
+ getStream().setRTCPTerminationStrategy(strategy);
+ }
+
/**
* {@inheritDoc}
*/
@@ -878,14 +948,6 @@ private void sendLastNEndpointsChangeEventOnDataChannel(
public void setAdaptiveLastN(boolean adaptiveLastN)
{
this.adaptiveLastN = adaptiveLastN;
-
- if (adaptiveLastN)
- {
- // Ensure that we are using BasicBridgeRTCPTerminationStrategy,
- // which is currently needed to notify us of incoming REMBs.
- getContent().setRTCPTerminationStrategyFQN(
- BasicBridgeRTCPTerminationStrategy.class.getName());
- }
}
/**
@@ -895,14 +957,6 @@ public void setAdaptiveLastN(boolean adaptiveLastN)
public void setAdaptiveSimulcast(boolean adaptiveSimulcast)
{
this.adaptiveSimulcast = adaptiveSimulcast;
-
- if (adaptiveSimulcast)
- {
- // Ensure that we are using BasicBridgeRTCPTerminationStrategy,
- // which is currently needed to notify us of incoming REMBs.
- getContent().setRTCPTerminationStrategyFQN(
- BasicBridgeRTCPTerminationStrategy.class.getName());
- }
}
/**
@@ -1265,10 +1319,18 @@ public void handleNACK(NACKPacket nackPacket)
logger.debug("Retransmitting packet from cache. SSRC "
+ ssrc + " seq " + seq);
}
- getStream().injectPacket(
- createPacketForRetransmission(pkt),
- true,
- true);
+ try
+ {
+ getStream().injectPacket(
+ createPacketForRetransmission(pkt),
+ true,
+ true);
+ }
+ catch (TransmissionFailedException e)
+ {
+ logger.warn("Failed to inject packet in MediaStream: "
+ + e);
+ }
iter.remove();
}
}
@@ -1344,7 +1406,15 @@ public void handleNACK(NACKPacket nackPacket)
+ " on channel " + c.getID());
}
- c.getStream().injectPacket(pkt, false, true);
+ try
+ {
+ c.getStream().injectPacket(pkt, false, true);
+ }
+ catch (TransmissionFailedException e)
+ {
+ logger.warn("Failed to inject packet in MediaStream: "
+ + e);
+ }
}
}
}
diff --git a/src/main/java/org/jitsi/videobridge/Videobridge.java b/src/main/java/org/jitsi/videobridge/Videobridge.java
index e4f00232ab..2e89aa97c5 100644
--- a/src/main/java/org/jitsi/videobridge/Videobridge.java
+++ b/src/main/java/org/jitsi/videobridge/Videobridge.java
@@ -130,20 +130,6 @@ public class Videobridge
public static final String REST_API_PNAME
= "org.jitsi.videobridge." + REST_API;
- /**
- * The name of the property which specifies the FQN name of the RTCP
- * strategy to use when there are less than 3 participants.
- */
- static final String RTCP_TERMINATION_FALLBACK_STRATEGY_PNAME
- = "org.jitsi.videobridge.rtcp.fallbackStrategy";
-
- /**
- * The name of the property which specifies the FQN name of the RTCP
- * strategy to use by default.
- */
- static final String RTCP_TERMINATION_STRATEGY_PNAME
- = "org.jitsi.videobridge.rtcp.strategy";
-
/**
* The property that specifies allowed entities for turning on graceful
* shutdown mode. For XMPP API this is "from" JID. In case of REST
@@ -698,26 +684,7 @@ else if (authorizedSourcePattern != null &&
}
}
- // Get the RTCP termination strategy.
- ColibriConferenceIQ.RTCPTerminationStrategy strategyIQ
- = conferenceIQ.getRTCPTerminationStrategy();
- String strategyFQN;
-
- if (strategyIQ == null)
- {
- strategyFQN = null;
- }
- else
- {
- strategyFQN = strategyIQ.getName();
- if (strategyFQN != null)
- {
- strategyFQN = strategyFQN.trim();
- if (strategyFQN.length() == 0)
- strategyFQN = null;
- }
- }
-
+ // TODO(gp) Remove ColibriConferenceIQ.RTCPTerminationStrategy
for (ColibriConferenceIQ.Content contentIQ
: conferenceIQ.getContents())
{
@@ -735,10 +702,6 @@ else if (authorizedSourcePattern != null &&
}
else
{
- // Set the RTCP termination strategy.
- if (strategyFQN != null)
- content.setRTCPTerminationStrategyFQN(strategyFQN);
-
ColibriConferenceIQ.Content responseContentIQ
= new ColibriConferenceIQ.Content(content.getName());
diff --git a/src/main/java/org/jitsi/videobridge/rtcp/AbstractBridgeRTCPTerminationStrategy.java b/src/main/java/org/jitsi/videobridge/rtcp/AbstractBridgeRTCPTerminationStrategy.java
deleted file mode 100644
index 0b7f28477b..0000000000
--- a/src/main/java/org/jitsi/videobridge/rtcp/AbstractBridgeRTCPTerminationStrategy.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright @ 2015 Atlassian Pty Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jitsi.videobridge.rtcp;
-
-import org.jitsi.impl.neomedia.rtcp.termination.strategies.*;
-import org.jitsi.videobridge.*;
-
-/**
- * @author George Politis
- */
-public abstract class AbstractBridgeRTCPTerminationStrategy
- extends AbstractRTCPTerminationStrategy
-{
- private Conference conference;
-
- /**
- * Sets the Conference associated to this
- * BridgeRTCPTerminationStrategy
- *
- * @param conference The Conference associated to this
- * BridgeRTCPTerminationStrategy
- */
- public void setConference(Conference conference)
- {
- this.conference = conference;
- }
-
- /**
- * Gets the Conference associated to this
- * BridgeRTCPTerminationStrategy
- *
- * @return The Conference associated to this
- * BridgeRTCPTerminationStrategy
- */
- public Conference getConference()
- {
- return conference;
- }
-}
diff --git a/src/main/java/org/jitsi/videobridge/rtcp/BasicBridgeRTCPTerminationStrategy.java b/src/main/java/org/jitsi/videobridge/rtcp/BasicBridgeRTCPTerminationStrategy.java
deleted file mode 100644
index 441eb64c7f..0000000000
--- a/src/main/java/org/jitsi/videobridge/rtcp/BasicBridgeRTCPTerminationStrategy.java
+++ /dev/null
@@ -1,303 +0,0 @@
-/*
- * Copyright @ 2015 Atlassian Pty Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jitsi.videobridge.rtcp;
-
-import net.sf.fmj.media.rtp.*;
-
-import org.jitsi.impl.neomedia.rtcp.*;
-import org.jitsi.impl.neomedia.rtcp.termination.strategies.*;
-import org.jitsi.impl.neomedia.rtp.translator.*;
-import org.jitsi.service.neomedia.*;
-import org.jitsi.service.neomedia.rtp.*;
-import org.jitsi.util.*;
-import org.jitsi.videobridge.*;
-
-import java.util.*;
-
-/**
- * This class extends the BasicRTCPTerminationStrategy to make it work
- * with features found exclusively in the video bridge like, for example, lastN
- * and simulcast.
- *
- * @author George Politis
- */
-public class BasicBridgeRTCPTerminationStrategy
- extends AbstractBridgeRTCPTerminationStrategy
-{
- private static final Logger logger
- = Logger.getLogger(BasicBridgeRTCPTerminationStrategy.class);
-
- /**
- * Constructor.
- */
- public BasicBridgeRTCPTerminationStrategy()
- {
- setTransformerChain(new Transformer[]{
- new REMBNotifier(this),
- new ReceiverFeedbackFilter(),
- new SenderFeedbackExploder(this)
- });
- }
-
- /**
- * Sends RRs using data from FMJ and and REMBs using data from our remote
- * bitrate estimator.
- *
- * @return null
- */
- public RTCPPacket[] makeReports()
- {
- RTPTranslator rtpTranslator = this.getRTPTranslator();
-
- if (!(rtpTranslator instanceof RTPTranslatorImpl))
- return null;
-
- Conference conference = this.getConference();
- if (conference == null)
- return null;
-
- long time = System.currentTimeMillis();
- RTPTranslatorImpl rtpTranslatorImpl = (RTPTranslatorImpl) rtpTranslator;
- // Use the SSRC of the bridge (that is announced through signaling) so
- // that the endpoints won't drop the packet.
- int localSSRC = (int) rtpTranslatorImpl.getLocalSSRC(null);
- RTCPTransmitter rtcpTransmitter
- = this.getRTCPReportBuilder().getRTCPTransmitter();
-
- for (Endpoint endpoint : conference.getEndpoints())
- {
- for (RtpChannel channel : endpoint.getChannels(MediaType.VIDEO))
- {
- // Make the RTCP reports.
- RTCPPacket[] packets
- = makeReportsForChannel(
- (VideoChannel) channel,
- time,
- localSSRC);
-
- // Transmit the RTCP reports.
- if ((packets != null) && (packets.length != 0))
- {
- RTCPCompoundPacket compoundPacket
- = new RTCPCompoundPacket(packets);
- Payload payload = new RTCPPacketPayload(compoundPacket);
-
- rtpTranslatorImpl.writeControlPayload(
- payload,
- channel.getStream());
-
- /*
- * NOTE(gp, lyubomir): RTCPTransmitter cannot transmit
- * specific reports to specific destinations so we've
- * implemented the transmission ourselves. We're updating
- * the (global) transmission statistics maintained by
- * RTCPTranmitter by calling its onRTCPCompoundPacketSent
- * method.
- */
- rtcpTransmitter.onRTCPCompoundPacketSent(compoundPacket);
- }
- }
- }
-
- return null;
- }
-
-
- /**
- *
- * @param ssrc
- * @return
- */
- private RTCPSDES createRTCPSDES(int ssrc)
- {
- RTCPTransmitter rtcpTransmitter
- = this.getRTCPReportBuilder().getRTCPTransmitter();
-
- SSRCInfo ssrcInfo = rtcpTransmitter.cache.cache.get(ssrc);
- RTCPSDES rtcpSDES = null;
-
- if (ssrcInfo != null)
- {
- String cname = ssrcInfo.getCNAME();
-
- if (cname != null)
- {
- rtcpSDES = new RTCPSDES();
-
- rtcpSDES.ssrc = ssrc;
- rtcpSDES.items
- = new RTCPSDESItem[]
- {
- new RTCPSDESItem(RTCPSDESItem.CNAME, cname)
- };
- }
- }
- return rtcpSDES;
- }
-
- /**
- *
- * @param videoChannel
- * @param time
- * @return
- */
- private RTCPReportBlock[] createRTCPReportBlocksForChannel(
- VideoChannel videoChannel,
- long time)
- {
- RTCPTransmitter rtcpTransmitter
- = this.getRTCPReportBuilder().getRTCPTransmitter();
-
- int[] ssrcs = videoChannel.getReceiveSSRCs();
- List receiverReports
- = new ArrayList(ssrcs.length);
-
- for (int ssrc : ssrcs)
- {
- // TODO(gp) we need a mutex here for accessing the RTCP transmitter
- // cache.
- SSRCInfo info = rtcpTransmitter.cache.cache.get(ssrc);
-
- if (info != null)
- {
- RTCPReportBlock receiverReport
- = info.makeReceiverReport(time);
-
- receiverReports.add(receiverReport);
- }
- else
- {
- // Don't send RTCP feedback information for this sub-stream.
- // TODO(gp) Any endpoints receiving this stream must switch to
- // a lower quality stream.
- if (logger.isInfoEnabled())
- {
- logger.info(
- "FMJ has no information for SSRC "
- + (ssrc & 0xffffffffl) + " (" + ssrc + ")");
- }
- }
- }
-
- return
- receiverReports.toArray(
- new RTCPReportBlock[receiverReports.size()]);
- }
-
- /**
- *
- * @param videoChannel
- * @param localSSRC
- * @return
- */
- private RTCPREMBPacket createRTCPREMBPacketForChannel(
- VideoChannel videoChannel,
- int localSSRC)
- {
- if (videoChannel == null)
- throw new IllegalArgumentException("videoChannel");
-
- // Destination
- RemoteBitrateEstimator remoteBitrateEstimator
- = ((VideoMediaStream) videoChannel.getStream())
- .getRemoteBitrateEstimator();
- Collection ssrcs = remoteBitrateEstimator.getSsrcs();
-
- // TODO(gp) intersect with SSRCs from signaled simulcast layers
- // NOTE(gp) The Google Congestion Control algorithm (sender side)
- // doesn't seem to care about the SSRCs in the dest field.
- long[] dest = new long[ssrcs.size()];
- int i = 0;
-
- for (Integer ssrc : ssrcs)
- dest[i++] = ssrc & 0xFFFFFFFFL;
-
- // Exp & mantissa
- long bitrate = remoteBitrateEstimator.getLatestEstimate();
-
- if (bitrate == -1)
- return null;
-
- if (logger.isDebugEnabled())
- logger.debug("Estimated bitrate: " + bitrate);
-
- // Create and return the packet.
- return
- new RTCPREMBPacket(
- localSSRC & 0xFFFFFFFFL,
- /* mediaSSRC */ 0L,
- bitrate,
- dest);
- }
-
- private RTCPPacket[] makeReportsForChannel(
- VideoChannel videoChannel,
- long time,
- int localSSRC)
- {
- List packets = new ArrayList(3);
-
- // RTCP RR
- RTCPReportBlock[] receiverReports
- = createRTCPReportBlocksForChannel(videoChannel, time);
- RTCPPacket rr = new RTCPRRPacket(localSSRC, receiverReports);
-
- packets.add(rr);
-
- // RTCP REMB
- RTCPREMBPacket remb
- = createRTCPREMBPacketForChannel(videoChannel, localSSRC);
-
- if (remb != null)
- {
- packets.add(remb);
- if (logger.isDebugEnabled())
- logger.debug(remb);
- }
-
- // RTCP SDES
- List sdesChunks
- = new ArrayList(1 + receiverReports.length);
- RTCPSDES sdesChunk = createRTCPSDES(localSSRC);
-
- if (sdesChunk != null)
- sdesChunks.add(sdesChunk);
-
- long[] dest = new long[receiverReports.length];
-
- for (int i = 0; i < dest.length; i++)
- dest[i] = receiverReports[i].getSSRC();
-
- for (long ssrc : dest)
- {
- sdesChunk = createRTCPSDES((int) ssrc);
- if (sdesChunk != null)
- sdesChunks.add(sdesChunk);
- }
-
- // TODO(gp) why does this happen : sdesChunks.size() == 0
- if (sdesChunks.size() != 0)
- {
- RTCPSDESPacket sdes
- = new RTCPSDESPacket(
- sdesChunks.toArray(new RTCPSDES[sdesChunks.size()]));
-
- packets.add(sdes);
- }
-
- return packets.toArray(new RTCPPacket[packets.size()]);
- }
-}
diff --git a/src/main/java/org/jitsi/videobridge/rtcp/FallbackingRTCPTerminationStrategy.java b/src/main/java/org/jitsi/videobridge/rtcp/FallbackingRTCPTerminationStrategy.java
new file mode 100644
index 0000000000..968f7c2fdf
--- /dev/null
+++ b/src/main/java/org/jitsi/videobridge/rtcp/FallbackingRTCPTerminationStrategy.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright @ 2015 Atlassian Pty Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jitsi.videobridge.rtcp;
+
+import org.jitsi.impl.neomedia.*;
+import org.jitsi.impl.neomedia.rtcp.termination.strategies.*;
+import org.jitsi.impl.neomedia.transform.*;
+import org.jitsi.service.configuration.*;
+import org.jitsi.service.neomedia.*;
+import org.jitsi.util.*;
+import org.jitsi.videobridge.*;
+
+/**
+ * This RTCP termination strategy delegates to an "active" RTCP
+ * termination strategy. The "active" RTCP termination strategy is determined
+ * by the number of Endpoints in a Conference. The default
+ * fallback RTCP termination strategy is the
+ * SilentBridgeRTCPTerminationStrategy. The default main RTCP
+ * termination strategy is the BasicRTCPTerminationStrategy.
+ *
+ * @author George Politis
+ */
+public class FallbackingRTCPTerminationStrategy
+ extends VideoChannelRTCPTerminationStrategy
+{
+ /**
+ * The Logger used by the
+ * FallbackingRTCPTerminationStrategy class and its instances to
+ * print debug information.
+ */
+ private static final Logger logger
+ = Logger.getLogger(FallbackingRTCPTerminationStrategy.class);
+
+ /**
+ * The name of the property which specifies the FQN name of the RTCP
+ * strategy to use when there are less than 3 participants.
+ */
+ public static final String FALLBACK_STRATEGY_PNAME
+ = FallbackingRTCPTerminationStrategy.class.getName()
+ + ".fallbackStrategy";
+
+ /**
+ * The name of the property which specifies the FQN name of the RTCP
+ * strategy to use when there are less than 3 participants.
+ */
+ public static final String MAIN_STRATEGY_PNAME
+ = FallbackingRTCPTerminationStrategy.class.getName() + ".mainStrategy";
+
+ /**
+ * Determines the number of participants needed for the RTCP termination
+ * strategy to switch to the main one.
+ */
+ private static final int PARTICIPANTS_THRESHOLD = 3;
+
+ /**
+ * The fallback RTCPTerminationStrategy that is to be used when < 3
+ * participants are in the conference.
+ */
+ private RTCPTerminationStrategy fallbackRTCPTerminationStrategy;
+
+ /**
+ * The main RTCPTerminationStrategy that is to be used when >= 3
+ * participants are in the conference.
+ */
+ private RTCPTerminationStrategy mainRTCPTerminationStrategy;
+
+ //#region Private methods
+
+ /**
+ * Sets the {@link this.mainRTCPTerminationStrategy} and
+ * {@link this.fallbackRTCPTerminationStrategy} from the configuration.
+ */
+ public void initialize(VideoChannel vc)
+ {
+ super.initialize(vc);
+
+ ConfigurationService configurationService
+ = vc.getContent().getConference().getVideobridge()
+ .getConfigurationService();
+
+ if (configurationService == null)
+ {
+ return;
+ }
+
+ // Initialize {@link this.fallbackRTCPTerminationStrategy} from the
+ // configuration.
+ String fallbackFQN = configurationService.getString(
+ FALLBACK_STRATEGY_PNAME, "");
+
+ if (!StringUtils.isNullOrEmpty(fallbackFQN))
+ {
+ try
+ {
+ Class> clazz = Class.forName(fallbackFQN);
+ RTCPTerminationStrategy fallbackRTCPTerminationSrategy
+ = (RTCPTerminationStrategy) clazz.newInstance();
+
+ this.fallbackRTCPTerminationStrategy
+ = fallbackRTCPTerminationSrategy;
+ }
+ catch (Exception e)
+ {
+ logger.error(
+ "Failed to configure the fallback RTCP termination " +
+ "strategy", e);
+ }
+ }
+ else
+ {
+ this.fallbackRTCPTerminationStrategy
+ = new SilentBridgeRTCPTerminationStrategy();
+ }
+
+ // Initialize the RTCP termination strategy.
+ if (fallbackRTCPTerminationStrategy != null)
+ {
+ if (fallbackRTCPTerminationStrategy
+ instanceof MediaStreamRTCPTerminationStrategy)
+ {
+ ((MediaStreamRTCPTerminationStrategy)
+ fallbackRTCPTerminationStrategy)
+ .initialize(vc.getStream());
+ }
+ else if (fallbackRTCPTerminationStrategy
+ instanceof VideoChannelRTCPTerminationStrategy)
+ {
+ ((VideoChannelRTCPTerminationStrategy)
+ fallbackRTCPTerminationStrategy)
+ .initialize(vc);
+ }
+
+ }
+
+ // Initialize {@link this.mainRTCPTerminationStrategy} from the
+ // configuration.
+ String mainRTCPTerminationStrategyFQN = configurationService.getString(
+ MAIN_STRATEGY_PNAME, "");
+
+ if (!StringUtils.isNullOrEmpty(mainRTCPTerminationStrategyFQN))
+ {
+ try
+ {
+ Class> clazz = Class.forName(mainRTCPTerminationStrategyFQN);
+ RTCPTerminationStrategy mainRTCPTerminationStrategy
+ = (RTCPTerminationStrategy) clazz.newInstance();
+
+ this.mainRTCPTerminationStrategy = mainRTCPTerminationStrategy;
+ }
+ catch (Exception e)
+ {
+ logger.error(
+ "Failed to configure the main RTCP termination strategy",
+ e);
+ }
+ }
+ else
+ {
+ this.mainRTCPTerminationStrategy
+ = new BasicRTCPTerminationStrategy();
+ }
+
+ // Initialize the RTCP termination strategy.
+ if (this.mainRTCPTerminationStrategy != null)
+ {
+ if (mainRTCPTerminationStrategy
+ instanceof MediaStreamRTCPTerminationStrategy)
+ {
+ ((MediaStreamRTCPTerminationStrategy)
+ mainRTCPTerminationStrategy)
+ .initialize(vc.getStream());
+ }
+ else if (mainRTCPTerminationStrategy
+ instanceof VideoChannelRTCPTerminationStrategy)
+ {
+ ((VideoChannelRTCPTerminationStrategy)
+ mainRTCPTerminationStrategy)
+ .initialize(vc);
+ }
+
+ }
+ }
+
+ /**
+ * Returns a boolean indicating whether to use the fallback or the
+ * main RTCPTerminationStrategy.
+ *
+ * @return
+ */
+ private boolean fallback()
+ {
+ VideoChannel vc = getVideoChannel();
+
+ return (vc != null && fallbackRTCPTerminationStrategy != null
+ && vc.getContent()
+ .getConference().getEndpointCount() < PARTICIPANTS_THRESHOLD);
+ }
+
+ //#endregion
+
+ //#region TransformEngineWrapper implementation
+
+ public RTCPTerminationStrategy getActiveRTCPTerminationStrategy()
+ {
+ return fallback() ? fallbackRTCPTerminationStrategy
+ : mainRTCPTerminationStrategy;
+ }
+
+ //#endregion
+
+ //#region Settable implementation
+
+ public PacketTransformer getRTPTransformer()
+ {
+ return getActiveRTCPTerminationStrategy().getRTPTransformer();
+ }
+
+ public PacketTransformer getRTCPTransformer()
+ {
+ return getActiveRTCPTerminationStrategy().getRTCPTransformer();
+ }
+
+ public RawPacket report()
+ {
+ return getActiveRTCPTerminationStrategy().report();
+ }
+
+ //#endregion
+}
diff --git a/src/main/java/org/jitsi/videobridge/rtcp/HighestQualityBridgeRTCPTerminationStrategy.java b/src/main/java/org/jitsi/videobridge/rtcp/HighestQualityBridgeRTCPTerminationStrategy.java
deleted file mode 100644
index e3d99313f1..0000000000
--- a/src/main/java/org/jitsi/videobridge/rtcp/HighestQualityBridgeRTCPTerminationStrategy.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright @ 2015 Atlassian Pty Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jitsi.videobridge.rtcp;
-
-import net.sf.fmj.media.rtp.*;
-import org.jitsi.impl.neomedia.rtcp.termination.strategies.*;
-import org.jitsi.impl.neomedia.rtp.translator.*;
-import org.jitsi.service.neomedia.*;
-import org.jitsi.util.*;
-
-/**
- * @author George Politis
- */
-@Deprecated
-public class HighestQualityBridgeRTCPTerminationStrategy
- extends AbstractBridgeRTCPTerminationStrategy
-{
- private static final Logger logger =
- Logger.getLogger(HighestQualityBridgeRTCPTerminationStrategy.class);
-
- /**
- * The cache processor that will be making the RTCP reports coming from
- * the bridge.
- */
- private final FeedbackCacheProcessor feedbackCacheProcessor;
-
- /**
- * A cache of media receiver feedback. It contains both receiver report
- * blocks and REMB packets.
- */
- private final FeedbackCache feedbackCache;
-
- public HighestQualityBridgeRTCPTerminationStrategy()
- {
- logger.warn("This RTCP termination strategy is deprecated and should" +
- "not be used!");
-
- this.feedbackCache = new FeedbackCache();
- this.feedbackCacheProcessor
- = new FeedbackCacheProcessor(feedbackCache);
-
- // TODO(gp) make percentile configurable.
- this.feedbackCacheProcessor.setPercentile(70);
-
- setTransformerChain(new Transformer[]{
- new REMBNotifier(this),
- new FeedbackCacheUpdater(feedbackCache),
- new ReceiverFeedbackFilter(),
- new SenderFeedbackExploder(this)
- });
- }
-
- @Override
- public RTCPPacket[] makeReports()
- {
- // Uses the cache processor to make the RTCP reports.
-
- RTPTranslator t = this.getRTPTranslator();
- if (t == null || !(t instanceof RTPTranslatorImpl))
- return new RTCPPacket[0];
-
- long localSSRC = ((RTPTranslatorImpl)t).getLocalSSRC(null);
-
- RTCPPacket[] packets = feedbackCacheProcessor.makeReports(
- (int) localSSRC);
-
- return packets;
- }
-}
diff --git a/src/main/java/org/jitsi/videobridge/rtcp/MaxThroughputBridgeRTCPTerminationStrategy.java b/src/main/java/org/jitsi/videobridge/rtcp/MaxThroughputBridgeRTCPTerminationStrategy.java
deleted file mode 100644
index 4f28c90477..0000000000
--- a/src/main/java/org/jitsi/videobridge/rtcp/MaxThroughputBridgeRTCPTerminationStrategy.java
+++ /dev/null
@@ -1,349 +0,0 @@
-/*
- * Copyright @ 2015 Atlassian Pty Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jitsi.videobridge.rtcp;
-
-import java.util.*;
-
-import net.sf.fmj.media.rtp.*;
-
-import org.jitsi.impl.neomedia.rtcp.*;
-import org.jitsi.impl.neomedia.rtp.translator.*;
-import org.jitsi.service.neomedia.*;
-import org.jitsi.service.neomedia.rtp.*;
-import org.jitsi.util.*;
-import org.jitsi.videobridge.*;
-import org.jitsi.videobridge.simulcast.*;
-
-/**
- *
- * @author George Politis
- * @author Lyubomir Marinov
- */
-@Deprecated
-public class MaxThroughputBridgeRTCPTerminationStrategy
- extends AbstractBridgeRTCPTerminationStrategy
-{
- private static final Logger logger
- = Logger.getLogger(MaxThroughputBridgeRTCPTerminationStrategy.class);
-
- private RTCPSDES createRTCPSDES(RTCPTransmitter rtcpTransmitter, int ssrc)
- {
- SSRCInfo ssrcInfo = rtcpTransmitter.cache.cache.get(ssrc);
- RTCPSDES rtcpSDES = null;
-
- if (ssrcInfo != null)
- {
- String cname = ssrcInfo.getCNAME();
-
- if (cname != null)
- {
- rtcpSDES = new RTCPSDES();
-
- rtcpSDES.ssrc = ssrc;
- rtcpSDES.items
- = new RTCPSDESItem[]
- {
- new RTCPSDESItem(RTCPSDESItem.CNAME, cname)
- };
- }
- }
- return rtcpSDES;
- }
-
- private RTCPReportBlock[] makeReceiverReports(
- VideoChannel videoChannel,
- RTCPTransmitter rtcpTransmitter,
- long time)
- {
- SortedSet layers
- = videoChannel.getSimulcastManager().getSimulcastLayers();
- List receiverReports
- = new ArrayList(layers.size());
-
- for (SimulcastLayer layer : layers)
- {
- int ssrc = (int) layer.getPrimarySSRC();
- SSRCInfo info = rtcpTransmitter.cache.cache.get(ssrc);
-
- if (info != null)
- {
- RTCPReportBlock receiverReport
- = info.makeReceiverReport(time);
-
- receiverReports.add(receiverReport);
- }
- else
- {
- // Don't send RTCP feedback information for this sub-stream.
- // TODO(gp) Any endpoints receiving this stream must switch to
- // a lower quality stream.
- if (logger.isInfoEnabled())
- {
- logger.info(
- "FMJ has no information for SSRC "
- + (ssrc & 0xffffffffl) + " (" + ssrc + ")");
- }
- }
- }
-
- return
- receiverReports.toArray(
- new RTCPReportBlock[receiverReports.size()]);
- }
-
- private RTCPREMBPacket makeREMBPacket(
- VideoChannel videoChannel,
- int localSSRC)
- {
- if (videoChannel == null)
- throw new IllegalArgumentException("videoChannel");
-
- // Media SSRC (always 0)
- final long mediaSSRC = 0l;
-
- // Destination
- RemoteBitrateEstimator remoteBitrateEstimator
- = ((VideoMediaStream) videoChannel.getStream())
- .getRemoteBitrateEstimator();
- Collection ssrcs = remoteBitrateEstimator.getSsrcs();
-
- // TODO(gp) intersect with SSRCs from signaled simulcast layers
- long[] dest = new long[ssrcs.size()];
- int i = 0;
-
- for (Integer ssrc : ssrcs)
- dest[i++] = ssrc & 0xffffffffl;
-
- // Exp & mantissa
- long bitrate = remoteBitrateEstimator.getLatestEstimate();
- if (bitrate == -1)
- {
- return null;
- }
-
- if (logger.isDebugEnabled())
- logger.debug("Estimated bitrate: " + bitrate);
-
- // Create and return the packet.
- return
- new RTCPREMBPacket(
- localSSRC & 0xFFFFFFFFL,
- mediaSSRC,
- bitrate,
- dest);
- }
-
- @Override
- public RTCPPacket[] makeReports()
- {
- RTPTranslator rtpTranslator = this.getRTPTranslator();
-
- if (!(rtpTranslator instanceof RTPTranslatorImpl))
- return null;
-
- long time = System.currentTimeMillis();
-
- RTPTranslatorImpl rtpTranslatorImpl = (RTPTranslatorImpl) rtpTranslator;
-
- // Use the SSRC of the bridge (that is announced through signaling) so
- // that the endpoints won't drop the packet.
- int localSSRC = (int) rtpTranslatorImpl.getLocalSSRC(null);
-
- for (Endpoint endpoint : getConference().getEndpoints())
- {
- for (RtpChannel channel : endpoint.getChannels(MediaType.VIDEO))
- {
- // Make the RTCP reports.
- RTCPPacket[] packets
- = makeReports(
- (VideoChannel) channel,
- getRTCPTransmitter(),
- time,
- localSSRC);
-
- // Transmit the RTCP reports.
- if ((packets != null) && (packets.length != 0))
- {
- RTCPCompoundPacket compoundPacket
- = new RTCPCompoundPacket(packets);
- Payload payload = new RTCPPacketPayload(compoundPacket);
-
- rtpTranslatorImpl.writeControlPayload(
- payload,
- channel.getStream());
-
- /*
- * NOTE(gp, lyubomir): RTCPTransmitter cannot transmit
- * specific reports to specific destinations so we've
- * implemented the transmission ourselves. We're updating
- * the (global) transmission statistics maintained by
- * RTCPTranmitter by calling its onRTCPCompoundPacketSent
- * method.
- */
- getRTCPTransmitter().onRTCPCompoundPacketSent(compoundPacket);
- }
- }
- }
-
- return null;
- }
-
- private RTCPPacket[] makeReports(
- VideoChannel videoChannel,
- RTCPTransmitter rtcpTransmitter,
- long time,
- int localSSRC)
- {
- // RTCP RR
- RTCPReportBlock[] receiverReports
- = makeReceiverReports(videoChannel, rtcpTransmitter, time);
- RTCPPacket rr = new RTCPRRPacket(localSSRC, receiverReports);
-
- // RTCP REMB
- RTCPREMBPacket remb = makeREMBPacket(videoChannel, localSSRC);
-
- if (remb != null)
- {
- if (logger.isDebugEnabled())
- logger.debug(remb);
- }
-
- // RTCP SDES
- List sdesChunks
- = new ArrayList(1 + receiverReports.length);
- RTCPSDES sdesChunk = createRTCPSDES(rtcpTransmitter, localSSRC);
-
- if (sdesChunk != null)
- sdesChunks.add(sdesChunk);
-
- long[] dest = new long[receiverReports.length];
-
- for (int i = 0; i < dest.length; i++)
- dest[i] = receiverReports[i].getSSRC();
-
- for (long ssrc : dest)
- {
- sdesChunk = createRTCPSDES(rtcpTransmitter, (int) ssrc);
- if (sdesChunk != null)
- sdesChunks.add(sdesChunk);
- }
-
- RTCPSDESPacket sdes
- = new RTCPSDESPacket(
- sdesChunks.toArray(new RTCPSDES[sdesChunks.size()]));
-
- return (remb != null)
- ? new RTCPPacket[] { rr, remb, sdes }
- : new RTCPPacket[] { rr, sdes };
- }
-
- public MaxThroughputBridgeRTCPTerminationStrategy()
- {
- logger.warn("This RTCP termination strategy is deprecated and should" +
- "not be used!");
- setTransformerChain(new Transformer[]{
- transformer
- });
- }
-
- Transformer transformer = new Transformer()
- {
- @Override
- public RTCPCompoundPacket reverseTransform(RTCPCompoundPacket inPacket)
- {
- if (inPacket == null)
- return inPacket;
-
- RTCPPacket[] inPackets = inPacket.packets;
-
- if ((inPackets == null) || (inPackets.length == 0))
- return inPacket;
-
- List outPackets
- = new ArrayList(inPackets.length);
-
- for (RTCPPacket p : inPackets)
- {
- switch (p.type)
- {
- case RTCPPacket.RR:
- // Mute RRs from the peers. We send our own.
- break;
-
- case RTCPPacket.SR:
- // Remove feedback information from the SR and forward.
- RTCPSRPacket sr = (RTCPSRPacket) p;
-
- sr.reports = new RTCPReportBlock[0];
- outPackets.add(sr);
- break;
-
- case RTCPFBPacket.PSFB:
- RTCPFBPacket psfb = (RTCPFBPacket) p;
-
- switch (psfb.fmt)
- {
- case RTCPREMBPacket.FMT:
- // Mute REMBs.
- break;
- default:
- // Pass through everything else, like PLIs and NACKs
- outPackets.add(psfb);
- break;
- }
- break;
-
- default:
- // Pass through everything else, like PLIs and NACKs
- outPackets.add(p);
- break;
- }
- }
-
- RTCPCompoundPacket outPacket;
-
- if (outPackets.isEmpty())
- {
- outPacket = null;
- }
- else
- {
- outPacket
- = new RTCPCompoundPacket(
- outPackets.toArray(new RTCPPacket[outPackets.size()]));
- }
- return outPacket;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void close()
- {
- // nothing to be done here
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public RTCPCompoundPacket transform(RTCPCompoundPacket inPacket)
- {
- return inPacket;
- }
- };
-}
diff --git a/src/main/java/org/jitsi/videobridge/rtcp/NACKNotifier.java b/src/main/java/org/jitsi/videobridge/rtcp/NACKNotifier.java
index 0d1c1fd7b6..25c528f9d9 100644
--- a/src/main/java/org/jitsi/videobridge/rtcp/NACKNotifier.java
+++ b/src/main/java/org/jitsi/videobridge/rtcp/NACKNotifier.java
@@ -16,8 +16,11 @@
package org.jitsi.videobridge.rtcp;
import net.sf.fmj.media.rtp.*;
+import net.sf.fmj.media.rtp.util.*;
+import org.jitsi.impl.neomedia.*;
import org.jitsi.impl.neomedia.rtcp.*;
-import org.jitsi.service.neomedia.*;
+import org.jitsi.impl.neomedia.transform.*;
+import org.jitsi.util.function.*;
import java.util.*;
@@ -28,7 +31,8 @@
* @author Boris Grozev
*/
public class NACKNotifier
- implements Transformer
+ extends SinglePacketTransformer
+ implements TransformEngine
{
/**
* The handler to use for intercepted NACK packets.
@@ -40,6 +44,18 @@ public class NACKNotifier
*/
private boolean enabled = true;
+ /**
+ * The Function that generates RTCPCompoundPackets from
+ * RawPackets.
+ */
+ private RTCPPacketParserEx parser = new RTCPPacketParserEx();
+
+ /**
+ * The Function that generates RawPackets from
+ * RTCPCompoundPackets.
+ */
+ private RTCPGenerator generator = new RTCPGenerator();
+
/**
* Initializes a new NACKNotifier which is to use a specific
* NACKHandler instalce.
@@ -50,21 +66,45 @@ public NACKNotifier(NACKHandler handler)
this.handler = handler;
}
+ @Override
+ public RawPacket transform(RawPacket pkt)
+ {
+ return pkt;
+ }
+
/**
* Looks for RTCP NACK packets contained in the compound packet
* inPacket, passes them on the the handler and removes them from
* the resulting compount packet.
- * @param inPacket the input RTCP compound packet.
+ * @param pkt the input RTCP compound packet.
* @return a packet which consists of the packets from inPacket,
* with NACK packets removed.
*/
@Override
- public RTCPCompoundPacket reverseTransform(RTCPCompoundPacket inPacket)
+ public RawPacket reverseTransform(RawPacket pkt)
{
- if (!enabled || inPacket == null || inPacket.packets == null
- || inPacket.packets.length == 0)
+ if (!enabled)
{
- return inPacket;
+ return pkt;
+ }
+
+ RTCPCompoundPacket inPacket;
+ try
+ {
+ inPacket = (RTCPCompoundPacket) parser.parse(
+ pkt.getBuffer(),
+ pkt.getOffset(),
+ pkt.getLength());
+ }
+ catch (BadFormatException e)
+ {
+ return null;
+ }
+
+ if (inPacket == null || inPacket.packets == null
+ || inPacket.packets.length == 0)
+ {
+ return pkt;
}
List outPackets = new LinkedList();
@@ -91,33 +131,30 @@ public RTCPCompoundPacket reverseTransform(RTCPCompoundPacket inPacket)
if (removed)
{
if (outPackets.size() > 0)
- return new RTCPCompoundPacket(outPackets.toArray(
+ {
+ RTCPCompoundPacket outPacket
+ = new RTCPCompoundPacket(outPackets.toArray(
new RTCPPacket[outPackets.size()]));
+
+ return generator.apply(outPacket);
+ }
else
return null;
}
else
{
- return inPacket;
+ return pkt;
}
}
- /**
- * {@inheritDoc}
- */
- @Override
- public void close()
+ public PacketTransformer getRTPTransformer()
{
- // nothing to be done here
+ return null;
}
- /**
- * {@inheritDoc}
- */
- @Override
- public RTCPCompoundPacket transform(RTCPCompoundPacket inPacket)
+ public PacketTransformer getRTCPTransformer()
{
- return inPacket;
+ return this;
}
}
diff --git a/src/main/java/org/jitsi/videobridge/rtcp/REMBNotifier.java b/src/main/java/org/jitsi/videobridge/rtcp/REMBNotifier.java
index 62d7f0315e..10e1394804 100644
--- a/src/main/java/org/jitsi/videobridge/rtcp/REMBNotifier.java
+++ b/src/main/java/org/jitsi/videobridge/rtcp/REMBNotifier.java
@@ -16,67 +16,106 @@
package org.jitsi.videobridge.rtcp;
import net.sf.fmj.media.rtp.*;
+import net.sf.fmj.media.rtp.util.*;
+import org.jitsi.impl.neomedia.*;
import org.jitsi.impl.neomedia.rtcp.*;
-import org.jitsi.service.neomedia.*;
+import org.jitsi.impl.neomedia.transform.*;
import org.jitsi.videobridge.*;
+import java.lang.ref.*;
+
/**
+ * Intercepts REMBs and passes them down to the VideoChannel logic.
+ *
* @author George Politis
*/
-class REMBNotifier implements Transformer
+public class REMBNotifier
+ implements TransformEngine
{
- private AbstractBridgeRTCPTerminationStrategy strategy;
+ /**
+ *
+ */
+ private final WeakReference weakVideoChannel;
+
+ /**
+ *
+ */
+ private final RTCPPacketParserEx parserEx = new RTCPPacketParserEx();
+
+ /**
+ * Ctor.
+ *
+ * @param videoChannel
+ */
+ public REMBNotifier(VideoChannel videoChannel)
+ {
+ this.weakVideoChannel = new WeakReference(videoChannel);
+ }
- public REMBNotifier(AbstractBridgeRTCPTerminationStrategy strategy)
+ public PacketTransformer getRTPTransformer()
{
- this.strategy = strategy;
+ return null;
}
- @Override
- public RTCPCompoundPacket reverseTransform(RTCPCompoundPacket inPacket)
+ public PacketTransformer getRTCPTransformer()
{
- // Intercept REMBs and forward them to the VideoChannel logic
- for (RTCPPacket p : inPacket.packets)
+ return new SinglePacketTransformer()
{
- if (p != null && p.type == RTCPFBPacket.PSFB)
+ @Override
+ public RawPacket transform(RawPacket pkt)
+ {
+ return pkt;
+ }
+
+ @Override
+ public RawPacket reverseTransform(RawPacket pkt)
{
- RTCPFBPacket psfb = (RTCPFBPacket) p;
- if (psfb.fmt == RTCPREMBPacket.FMT)
+ if (pkt == null)
+ {
+ return pkt;
+ }
+
+ RTCPCompoundPacket inPacket;
+ try
+ {
+ inPacket = (RTCPCompoundPacket) parserEx.parse(
+ pkt.getBuffer(), pkt.getOffset(), pkt.getLength());
+ }
+ catch (BadFormatException ex)
{
- RTCPREMBPacket remb = (RTCPREMBPacket) psfb;
- Conference conference = strategy.getConference();
- if (conference != null)
+ return pkt;
+ }
+
+ if (inPacket == null)
+ {
+ return pkt;
+ }
+
+ // Intercept REMBs and forward them to the VideoChannel logic
+ for (RTCPPacket p : inPacket.packets)
+ {
+ if (p != null && p.type == RTCPFBPacket.PSFB)
{
- Channel channel
- = conference.findChannelByReceiveSSRC(remb.senderSSRC,
- MediaType.VIDEO);
- if (channel != null && channel instanceof VideoChannel)
+ RTCPFBPacket psfb = (RTCPFBPacket) p;
+ if (psfb.fmt == RTCPREMBPacket.FMT)
{
- ((VideoChannel) channel).receivedREMB(remb.getBitrate());
+ RTCPREMBPacket remb = (RTCPREMBPacket) psfb;
+
+ WeakReference wc = weakVideoChannel;
+ VideoChannel videoChannel = wc == null
+ ? null
+ : wc.get();
+
+ if (videoChannel != null)
+ {
+ videoChannel.receivedREMB(remb.getBitrate());
+ }
}
}
}
- }
- }
-
- return inPacket;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void close()
- {
- // nothing to be done here
- }
- /**
- * {@inheritDoc}
- */
- @Override
- public RTCPCompoundPacket transform(RTCPCompoundPacket inPacket)
- {
- return inPacket;
+ return pkt;
+ }
+ };
}
}
diff --git a/src/main/java/org/jitsi/videobridge/rtcp/RTCPPacketPayload.java b/src/main/java/org/jitsi/videobridge/rtcp/RTCPPacketPayload.java
deleted file mode 100644
index b83266a9af..0000000000
--- a/src/main/java/org/jitsi/videobridge/rtcp/RTCPPacketPayload.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright @ 2015 Atlassian Pty Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jitsi.videobridge.rtcp;
-
-import net.sf.fmj.media.rtp.*;
-import org.jitsi.impl.neomedia.rtp.translator.*;
-
-import javax.media.rtp.*;
-
-/**
-* @author George Politis
-*/
-class RTCPPacketPayload
- implements Payload
-{
- private final RTCPCompoundPacket packet;
-
- public RTCPPacketPayload(RTCPCompoundPacket p)
- {
- this.packet = p;
- }
-
- @Override
- public void writeTo(OutputDataStream stream)
- {
- if (packet != null)
- {
- int len = packet.calcLength();
- packet.assemble(len, false);
- byte[] buf = packet.data;
-
- stream.write(buf, 0, len);
- }
- }
-}
diff --git a/src/main/java/org/jitsi/videobridge/rtcp/SenderFeedbackExploder.java b/src/main/java/org/jitsi/videobridge/rtcp/SenderFeedbackExploder.java
deleted file mode 100644
index 1be0cd5bdd..0000000000
--- a/src/main/java/org/jitsi/videobridge/rtcp/SenderFeedbackExploder.java
+++ /dev/null
@@ -1,319 +0,0 @@
-/*
- * Copyright @ 2015 Atlassian Pty Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jitsi.videobridge.rtcp;
-
-import net.sf.fmj.media.rtp.*;
-import org.jitsi.impl.neomedia.rtp.translator.*;
-import org.jitsi.service.neomedia.*;
-import org.jitsi.service.neomedia.recording.*;
-import org.jitsi.videobridge.*;
-
-import java.util.*;
-
-/**
- * @author George Politis
- */
-class SenderFeedbackExploder implements Transformer
-{
- private final AbstractBridgeRTCPTerminationStrategy strategy;
-
- /**
- * Ctor.
- *
- * @param strategy
- */
- public SenderFeedbackExploder(AbstractBridgeRTCPTerminationStrategy strategy)
- {
- this.strategy = strategy;
- }
-
- @Override
- public RTCPCompoundPacket reverseTransform(RTCPCompoundPacket inPacket)
- {
- // Call the super method that:
- //
- // 1. Removes receiver report blocks from RRs and SRs and kills REMBs.
- // 2. Updates the receiver feedback cache.
-
- // RTCPCompoundPacket outPacket = super.reverseTransform(inPacket);
- RTCPCompoundPacket outPacket = inPacket;
-
- if (outPacket.packets != null
- && outPacket.packets.length != 0
- && outPacket.packets[0].type == RTCPPacket.SR)
- {
- // 3. This is a sender report, pass it on to the bridge sender
- // reporting for "explosion".
- if (explodeSenderReport(outPacket))
- {
- return null;
- } else
- {
- // "Explosion" failed, send as is.
- return outPacket;
- }
- } else
- {
- // Not an SR, don't touch.
- return outPacket;
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void close()
- {
- // nothing to be done here
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public RTCPCompoundPacket transform(RTCPCompoundPacket inPacket)
- {
- return inPacket;
- }
-
- private final Map>
- lastSenderInformationMap
- = new HashMap>();
-
- private void explodeSenderReport(boolean destIsReceiving,
- RTCPCompoundPacket outPacket,
- RTCPSRPacket senderReport,
- RTPTranslatorImpl rtpTranslatorImpl,
- Integer senderSSRC,
- Map receiverSenderInformationMap,
- MediaStream stream)
- {
- // "Clone" the SR.
- RTCPSRPacket sr = new RTCPSRPacket(
- senderSSRC, new RTCPReportBlock[0]);
- sr.ntptimestampmsw = senderReport.ntptimestampmsw;
- sr.ntptimestamplsw = senderReport.ntptimestamplsw;
- sr.rtptimestamp = senderReport.rtptimestamp;
- sr.octetcount = senderReport.octetcount;
- sr.packetcount = senderReport.packetcount;
-
- Integer receiverSSRC = (int) stream.getLocalSourceID();
-
- if (destIsReceiving)
- {
- // The sender is being received by this receiver:
- // Cache the sender information.
- SenderInformation si = new SenderInformation();
- si.octetCount = senderReport.octetcount;
- si.packetCount = senderReport.packetcount;
-
- synchronized (receiverSenderInformationMap)
- {
- receiverSenderInformationMap.put(receiverSSRC, si);
- }
- }
- else
- {
- // The sender is NOT being received by this receiver:
- // We keep the packet count/octet count stable.
- SenderInformation si;
- synchronized (receiverSenderInformationMap)
- {
- if (receiverSenderInformationMap
- .containsKey(receiverSSRC))
- {
- si = receiverSenderInformationMap
- .get(receiverSSRC);
- }
- else
- {
- si = null;
- }
- }
-
- if (si != null)
- {
- sr.packetcount = si.packetCount;
- sr.octetcount = si.octetCount;
- }
- else
- {
- sr.packetcount = 0L;
- sr.octetcount = 0L;
- }
- }
-
- // Send the SR to the receiver.
- RTCPPacket[] packets
- = new RTCPPacket[outPacket.packets.length];
-
- packets[0] = sr;
-
- System.arraycopy(
- outPacket.packets, 1,
- packets, 1, outPacket.packets.length - 1);
-
- RTCPCompoundPacket compoundPacket
- = new RTCPCompoundPacket(packets);
-
- Payload payload = new RTCPPacketPayload(compoundPacket);
- rtpTranslatorImpl.writeControlPayload(payload, stream);
- }
-
- /**
- * Explode the SRs to make them compliant with features from the translator.
- *
- * @param outPacket
- * @return
- */
- public boolean explodeSenderReport(RTCPCompoundPacket outPacket)
- {
- if (outPacket.packets == null
- || outPacket.packets.length == 0
- || outPacket.packets[0].type != RTCPPacket.SR)
- {
- return false;
- }
-
- RTCPSRPacket senderReport = (RTCPSRPacket) outPacket.packets[0];
-
- Conference conf = strategy.getConference();
- if (senderReport == null || conf == null)
- return false;
-
- RTPTranslator rtpTranslator = strategy.getRTPTranslator();
- if (rtpTranslator == null
- || !(rtpTranslator instanceof RTPTranslatorImpl))
- return false;
-
- RTPTranslatorImpl rtpTranslatorImpl = (RTPTranslatorImpl)rtpTranslator;
-
- long ssrc = senderReport.ssrc & 0xFFFFFFFFL;
- if (ssrc < 1)
- return false;
-
- Integer senderSSRC = senderReport.ssrc;
- Map receiverSenderInformationMap
- = getReceiverSenderInformationMap(senderSSRC);
-
- Channel srcChannel = conf
- .findChannelByReceiveSSRC(ssrc, MediaType.VIDEO);
-
- if (srcChannel == null || !(srcChannel instanceof RtpChannel))
- return false;
-
- RtpChannel srcRtpChannel = (RtpChannel)srcChannel;
-
- // Send to every channel that receives this sender an SR.
- for (Content content : conf.getContents())
- {
- if (MediaType.VIDEO.equals(content.getMediaType()))
- {
- for (Channel destChannel : content.getChannels())
- {
- if (!(destChannel instanceof RtpChannel)
- || srcRtpChannel == destChannel)
- continue;
-
- RtpChannel destRtpChannel = (RtpChannel) destChannel;
- MediaStream stream = destRtpChannel.getStream();
- if (stream == null)
- continue;
-
- boolean destIsReceiving
- = srcRtpChannel.isInLastN(destChannel);
-
- if (destIsReceiving && srcRtpChannel instanceof VideoChannel)
- {
- VideoChannel srcVideoChannel
- = (VideoChannel) srcRtpChannel;
-
- if (!(destChannel instanceof VideoChannel))
- {
- destIsReceiving = false;
- }
- else
- {
- VideoChannel destVideoChannel
- = (VideoChannel) destChannel;
-
- destIsReceiving
- = destVideoChannel.getSimulcastManager().accept(
- ssrc,
- srcVideoChannel);
- }
- }
-
- explodeSenderReport(destIsReceiving, outPacket,
- senderReport,
- rtpTranslatorImpl,
- senderSSRC,
- receiverSenderInformationMap,
- stream);
- }
-
- if (content.isRecording())
- {
- Recorder recorder = content.getRecorder();
- MediaStream s;
-
- if (recorder != null && (s = recorder.getMediaStream()) != null)
- {
- explodeSenderReport(true, outPacket,
- senderReport,
- rtpTranslatorImpl,
- senderSSRC,
- receiverSenderInformationMap,
- s);
- }
- }
- }
- }
-
- return true;
- }
-
- private Map getReceiverSenderInformationMap(
- Integer senderSSRC)
- {
- Map receiverSenderInformationMap;
- synchronized (lastSenderInformationMap)
- {
- if (lastSenderInformationMap.containsKey(senderSSRC))
- {
- receiverSenderInformationMap
- = lastSenderInformationMap.get(senderSSRC);
- }
- else
- {
- receiverSenderInformationMap
- = new HashMap();
-
- lastSenderInformationMap.put(senderSSRC,
- receiverSenderInformationMap);
- }
- }
-
- return receiverSenderInformationMap;
- }
-
- private static class SenderInformation
- {
- long octetCount;
- long packetCount;
- }
-}
diff --git a/src/main/java/org/jitsi/videobridge/rtcp/VideoChannelRTCPTerminationStrategy.java b/src/main/java/org/jitsi/videobridge/rtcp/VideoChannelRTCPTerminationStrategy.java
new file mode 100644
index 0000000000..96f72bea14
--- /dev/null
+++ b/src/main/java/org/jitsi/videobridge/rtcp/VideoChannelRTCPTerminationStrategy.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright @ 2015 Atlassian Pty Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jitsi.videobridge.rtcp;
+
+import org.jitsi.service.neomedia.*;
+import org.jitsi.videobridge.*;
+
+import java.lang.ref.*;
+
+/**
+ * @author George Politis
+ */
+public abstract class VideoChannelRTCPTerminationStrategy
+ implements RTCPTerminationStrategy
+{
+ /**
+ * A WeakReference to the VideoChannel that owns this
+ * this VideoChannelRTCPTerminationStrategy.
+ */
+ private WeakReference weakVideoChannel;
+
+ /**
+ * Gets the VideoChannel that owns this
+ * VideoChannelRTCPTerminationStrategy.
+ *
+ * @return the VideoChannel that owns this
+ * VideoChannelRTCPTerminationStrategy.
+ */
+ public VideoChannel getVideoChannel()
+ {
+ WeakReference wvc = this.weakVideoChannel;
+ return wvc != null ? wvc.get() : null;
+ }
+
+ /**
+ * Initializes this RTCP termination strategy with a VideoChannel.
+ *
+ * @param vc the VideoChannel that owns this
+ * VideoChannelRTCPTerminationStrategy.
+ */
+ public void initialize(VideoChannel vc)
+ {
+ if (vc == null)
+ {
+ return;
+ }
+
+ this.weakVideoChannel = new WeakReference(vc);
+ }
+}
diff --git a/src/main/java/org/jitsi/videobridge/transform/RTCPTransformEngine.java b/src/main/java/org/jitsi/videobridge/transform/RTCPTransformEngine.java
deleted file mode 100644
index 87692051ff..0000000000
--- a/src/main/java/org/jitsi/videobridge/transform/RTCPTransformEngine.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright @ 2015 Atlassian Pty Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jitsi.videobridge.transform;
-
-import net.sf.fmj.media.rtp.*;
-import net.sf.fmj.media.rtp.util.*;
-import org.jitsi.impl.neomedia.*;
-import org.jitsi.impl.neomedia.rtcp.*;
-import org.jitsi.impl.neomedia.transform.*;
-import org.jitsi.service.neomedia.*;
-import org.jitsi.util.*;
-
-/**
- * A TransformEngine implementation which parses RTCP packets and
- * transforms them using a transformer for RTCPCompoundPackets.
- * This is similar to (and based on) libjitsi's
- * RTCPTerminationTransformEngine but is not connected with a
- * MediaStream.
- * @author Boris Grozev
- */
-public class RTCPTransformEngine
- extends SinglePacketTransformer
- implements TransformEngine, Transformer
-{
- /**
- * The Logger used by the RTCPTransformEngine class
- * and its instances to print debug information.
- */
- private static final Logger logger
- = Logger.getLogger(RTCPTransformEngine.class);
-
- /**
- * The chain of transformers that operate on RTCPCompoundPackets
- * (already parsed).
- */
- private Transformer[] chain;
-
- /**
- * The instance used to parse the input RawPackets/bytes into
- * RTCPCompoundPackets.
- */
- private final RTCPPacketParserEx parser = new RTCPPacketParserEx();
-
- /**
- * Initializes this transformer with the given chain of transformers.
- * @param chain
- */
- public RTCPTransformEngine(Transformer[] chain)
- {
- this.chain = chain;
- }
-
- /**
- * Implements
- * {@link org.jitsi.service.neomedia.Transformer#transform(Object)}.
- *
- * Does not touch outgoing packets.
- */
- @Override
- public RTCPCompoundPacket transform(RTCPCompoundPacket rtcpCompoundPacket)
- {
- // Not implemented.
- return rtcpCompoundPacket;
- }
-
- /**
- * Implements
- * {@link org.jitsi.service.neomedia.Transformer#reverseTransform(Object)}.
- *
- * Transforms incoming RTCP packets through the configured transformer
- * chain.
- */
- @Override
- public RTCPCompoundPacket reverseTransform(
- RTCPCompoundPacket inPacket)
- {
- if (chain != null)
- {
- for (Transformer transformer : chain)
- {
- if (transformer != null)
- inPacket = transformer.reverseTransform(inPacket);
- }
- }
-
- return inPacket;
- }
-
- /**
- * Implements
- * {@link org.jitsi.service.neomedia.Transformer#close()}.
- */
- @Override
- public void close()
- {
- }
-
- /**
- * Implements
- * {@link org.jitsi.impl.neomedia.transform.TransformEngine#getRTPTransformer()}.
- */
- @Override
- public PacketTransformer getRTPTransformer()
- {
- return null;
- }
-
- /**
- * Implements
- * {@link org.jitsi.impl.neomedia.transform.TransformEngine#getRTCPTransformer()}.
- */
- @Override
- public PacketTransformer getRTCPTransformer()
- {
- return this;
- }
-
- /**
- * Implements
- * {@link org.jitsi.impl.neomedia.transform.SinglePacketTransformer#transform(org.jitsi.impl.neomedia.RawPacket)}
- *
- * Does not touch outgoing packets.
- */
- @Override
- public RawPacket transform(RawPacket packet)
- {
- // Not implemented.
- return packet;
- }
-
- /**
- * Implements
- * {@link org.jitsi.impl.neomedia.transform.SinglePacketTransformer#reverseTransform(org.jitsi.impl.neomedia.RawPacket)}
- *
- * Parses the given packet as an RTCP packet and transforms the result
- * using the configured transformer chain. Returns the RawPacket
- * constructed from the transformed RTCPCompoundPacket.
- */
- @Override
- public RawPacket reverseTransform(RawPacket pkt)
- {
- // Parse the RTCP packet.
- RTCPCompoundPacket inRTCPPacket;
- try
- {
- inRTCPPacket = (RTCPCompoundPacket) parser.parse(
- pkt.getBuffer(),
- pkt.getOffset(),
- pkt.getLength());
- }
- catch (BadFormatException e)
- {
- // TODO(gp) decide what to do with malformed packets!
- logger.error("Could not parse RTCP packet.", e);
- return pkt;
- }
-
- // Transform the RTCP packet with the transform chain.
- RTCPCompoundPacket outRTCPPacket = reverseTransform(inRTCPPacket);
-
- if (outRTCPPacket == null
- || outRTCPPacket.packets == null
- || outRTCPPacket.packets.length == 0)
- return null;
-
- // Assemble the RTCP packet.
- int len = outRTCPPacket.calcLength();
- outRTCPPacket.assemble(len, false);
-
- pkt.setBuffer(outRTCPPacket.data);
- pkt.setLength(outRTCPPacket.data.length);
- pkt.setOffset(0);
-
- return pkt;
- }
-}
diff --git a/src/main/java/org/jitsi/videobridge/transform/RetransmissionRequester.java b/src/main/java/org/jitsi/videobridge/transform/RetransmissionRequester.java
index 524f395576..0042a45929 100644
--- a/src/main/java/org/jitsi/videobridge/transform/RetransmissionRequester.java
+++ b/src/main/java/org/jitsi/videobridge/transform/RetransmissionRequester.java
@@ -230,7 +230,15 @@ private void runInRequesterThread()
}
if (pkt != null)
+ try
+ {
mediaStream.injectPacket(pkt, false, true);
+ }
+ catch (TransmissionFailedException ex)
+ {
+ logger.warn("Failed to inject packet in MediaStream: "
+ + ex);
+ }
}
}
diff --git a/src/main/java/org/jitsi/videobridge/transform/RtpChannelTransformEngine.java b/src/main/java/org/jitsi/videobridge/transform/RtpChannelTransformEngine.java
index d096aa8087..ccc49ac8c6 100644
--- a/src/main/java/org/jitsi/videobridge/transform/RtpChannelTransformEngine.java
+++ b/src/main/java/org/jitsi/videobridge/transform/RtpChannelTransformEngine.java
@@ -17,7 +17,6 @@
import org.jitsi.impl.neomedia.transform.*;
import org.jitsi.service.configuration.*;
-import org.jitsi.service.neomedia.*;
import org.jitsi.util.*;
import org.jitsi.videobridge.*;
import org.jitsi.videobridge.rewriting.*;
@@ -77,15 +76,16 @@ public class RtpChannelTransformEngine
private CachingTransformer cache;
/**
- * The transformer which parses incoming RTCP packets.
+ * The transformer which intercepts NACK packets and passes them on to the
+ * channel logic.
*/
- private RTCPTransformEngine rtcpTransformEngine;
+ private NACKNotifier nackNotifier;
/**
- * The transformer which intercepts NACK packets and passes them on to the
+ * The transformer which intercepts REMB packets and passes them on to the
* channel logic.
*/
- private NACKNotifier nackNotifier;
+ private REMBNotifier rembNotifier;
/**
* The transformer which replaces the timestamp in an abs-send-time RTP
@@ -162,23 +162,15 @@ private TransformEngine[] createChain()
cache = new CachingTransformer();
transformerList.add(cache);
- // Note: we use a separate RTCPTransformer here, instead of using
- // the RTCPTerminationStrategy, because interpreting RTCP NACK
- // packets should happen in the context of a specific channel, and
- // the RTCPTermination strategy is a single instance for a
- // conference. The current intention/idea is to eventually move
- // the RTCP parsing code from the RTCPTerminationStrategy here, so
- // that we only parse RTCP once, and so that the REMB/RR code
- // doesn't have to find the source Channel by SSRC.
nackNotifier = new NACKNotifier((NACKHandler) channel);
- rtcpTransformEngine
- = new RTCPTransformEngine(new Transformer[] {nackNotifier});
- transformerList.add(rtcpTransformEngine);
+ transformerList.add(nackNotifier);
}
if (channel instanceof VideoChannel)
{
VideoChannel videoChannel = (VideoChannel) channel;
+ rembNotifier = new REMBNotifier(videoChannel);
+ transformerList.add(rembNotifier);
ssrcRewritingEngine = new SsrcRewritingEngine(videoChannel);
transformerList.add(ssrcRewritingEngine);
}