diff --git a/lib/fmj.jar b/lib/fmj.jar index 2c9c69913dc13e1413f788cdfd4d035f96d9ce22..63ccd7b1e1574e9bca3db4a3ae58e4873c1689e0 100644 Binary files a/lib/fmj.jar and b/lib/fmj.jar differ diff --git a/src/org/jitsi/impl/neomedia/MediaStreamImpl.java b/src/org/jitsi/impl/neomedia/MediaStreamImpl.java index 5b021583759b65688b3bca6e5a5a0c7e90fb3533..6a97530907c2e327243f739fba6b8a79e7170448 100644 --- a/src/org/jitsi/impl/neomedia/MediaStreamImpl.java +++ b/src/org/jitsi/impl/neomedia/MediaStreamImpl.java @@ -502,10 +502,6 @@ public void close() csrcEngine = null; } - if (rtpConnector != null) - rtpConnector.removeTargets(); - rtpConnectorTarget = null; - if (rtpManager != null) { if (logger.isInfoEnabled()) @@ -544,6 +540,15 @@ public void close() } } + /* + * XXX Call AbstractRTPConnector#removeTargets() after + * StreamRTPManager#dispose(). Otherwise, the latter will try to send an + * RTCP BYE and there will be no targets to send it to. + */ + if (rtpConnector != null) + rtpConnector.removeTargets(); + rtpConnectorTarget = null; + if (deviceSession != null) deviceSession.close(); } diff --git a/src/org/jitsi/impl/neomedia/RTCPConnectorInputStream.java b/src/org/jitsi/impl/neomedia/RTCPConnectorInputStream.java index 9eb4c917a04e1421627211ee4b5a2ed2bb100a95..e8f10495a30aabb2e26aab6973b3fc7580e14a21 100644 --- a/src/org/jitsi/impl/neomedia/RTCPConnectorInputStream.java +++ b/src/org/jitsi/impl/neomedia/RTCPConnectorInputStream.java @@ -23,7 +23,7 @@ public class RTCPConnectorInputStream /** * List of feedback listeners; */ - private final List<RTCPFeedbackListener> rtcpFeedbackListeners + private final List<RTCPFeedbackListener> listeners = new ArrayList<RTCPFeedbackListener>(); /** @@ -45,8 +45,57 @@ public RTCPConnectorInputStream(DatagramSocket socket) */ public void addRTCPFeedbackListener(RTCPFeedbackListener listener) { - if(!rtcpFeedbackListeners.contains(listener)) - rtcpFeedbackListeners.add(listener); + if (listener == null) + throw new NullPointerException("listener"); + if(!listeners.contains(listener)) + listeners.add(listener); + } + + /** + * Notifies a specific list of <tt>RTCPFeedbackListener</tt>s about a + * specific RTCP feedback message if such a message can be parsed out of a + * specific <tt>byte</tt> buffer. + * + * @param source the object to be reported as the source of the + * <tt>RTCPFeedbackEvent</tt> to be fired + * @param buffer the <tt>byte</tt> buffer which may specific an RTCP + * feedback message + * @param offset the offset in <tt>buffer</tt> at which the reading of bytes + * is to begin + * @param length the number of bytes in <tt>buffer</tt> to be read for the + * purposes of parsing an RTCP feedback message and firing an + * <tt>RTPCFeedbackEvent</tt> + * @param listeners the list of <tt>RTCPFeedbackListener</tt>s to be + * notified about the specified RTCP feedback message if such a message can + * be parsed out of the specified <tt>buffer</tt> + */ + public static void fireRTCPFeedbackReceived( + Object source, + byte[] buffer, int offset, int length, + List<RTCPFeedbackListener> listeners) + { + /* + * RTCP feedback message size is minimum 12 bytes: + * Version/Padding/Feedback message type: 1 byte + * Payload type: 1 byte + * Length: 2 bytes + * SSRC of packet sender: 4 bytes + * SSRC of media source: 4 bytes + */ + if ((length >= 12) && !listeners.isEmpty()) + { + int pt = buffer[offset + 1] & 0xFF; + + if ((pt == RTCPFeedbackEvent.PT_PS) + || (pt == RTCPFeedbackEvent.PT_TL)) + { + int fmt = buffer[offset] & 0x1F; + RTCPFeedbackEvent evt = new RTCPFeedbackEvent(source, fmt, pt); + + for (RTCPFeedbackListener l : listeners) + l.rtcpFeedbackReceived(evt); + } + } } /** @@ -56,65 +105,31 @@ public void addRTCPFeedbackListener(RTCPFeedbackListener listener) */ public void removeRTCPFeedbackListener(RTCPFeedbackListener listener) { - rtcpFeedbackListeners.remove(listener); + listeners.remove(listener); } /** * Copies the content of the most recently received packet into * <tt>inBuffer</tt>. * - * @param inBuffer the <tt>byte[]</tt> that we'd like to copy the content - * of the packet to. + * @param buffer the <tt>byte[]</tt> that we'd like to copy the content of + * the packet to. * @param offset the position where we are supposed to start writing in - * <tt>inBuffer</tt>. + * <tt>buffer</tt>. * @param length the number of <tt>byte</tt>s available for writing in - * <tt>inBuffer</tt>. + * <tt>buffer</tt>. * * @return the number of bytes read * * @throws IOException if <tt>length</tt> is less than the size of the * packet. */ - public int read(byte[] inBuffer, int offset, int length) + public int read(byte[] buffer, int offset, int length) throws IOException { - if (ioError) - return -1; - - int pktLength = pkt.getLength(); - - if (length < pktLength) - throw - new IOException("Input buffer not big enough for " + pktLength); - - /* check if RTCP feedback message */ - - /* Feedback message size is minimum 12 bytes: - * Version/Padding/Feedback message type: 1 byte - * Payload type: 1 byte - * Length: 2 bytes - * SSRC of packet sender: 4 bytes - * SSRC of media source: 4 bytes - */ - if(pktLength >= 12) - { - byte data[] = pkt.getBuffer(); - int fmt = 0; - int pt = 0; - - /* get FMT field (last 5 bits of first byte) */ - fmt = (data[0] & 0x1F); - pt |= (data[1] & 0xFF); - - RTCPFeedbackEvent evt = new RTCPFeedbackEvent(this, fmt, pt); - - /* notify feedback listeners */ - for(RTCPFeedbackListener l : rtcpFeedbackListeners) - l.feedbackReceived(evt); - } + int pktLength = super.read(buffer, offset, length); - System.arraycopy( - pkt.getBuffer(), pkt.getOffset(), inBuffer, offset, pktLength); + fireRTCPFeedbackReceived(this, buffer, offset, pktLength, listeners); return pktLength; } diff --git a/src/org/jitsi/impl/neomedia/RTPConnectorInputStream.java b/src/org/jitsi/impl/neomedia/RTPConnectorInputStream.java index e03c383d27f01114d3c9875f5b26bd7148dff6ab..fc1b29694d866804ec457e42314da4a80342e3ec 100755 --- a/src/org/jitsi/impl/neomedia/RTPConnectorInputStream.java +++ b/src/org/jitsi/impl/neomedia/RTPConnectorInputStream.java @@ -248,11 +248,15 @@ public int read(byte[] buffer, int offset, int length) int pktLength = pkt.getLength(); if (length < pktLength) - throw - new IOException("Input buffer not big enough for " + pktLength); + { + throw new IOException( + "Input buffer not big enough for " + pktLength); + } System.arraycopy( - pkt.getBuffer(), pkt.getOffset(), buffer, offset, pktLength); + pkt.getBuffer(), pkt.getOffset(), + buffer, offset, + pktLength); return pktLength; } diff --git a/src/org/jitsi/impl/neomedia/RTPConnectorOutputStream.java b/src/org/jitsi/impl/neomedia/RTPConnectorOutputStream.java index 54f7a299b91afb077981487ccdcc03a26ee64519..0a73313ad2c2bedb7a54456160acce3227d5739e 100755 --- a/src/org/jitsi/impl/neomedia/RTPConnectorOutputStream.java +++ b/src/org/jitsi/impl/neomedia/RTPConnectorOutputStream.java @@ -16,6 +16,7 @@ import org.jitsi.service.libjitsi.*; import org.jitsi.service.packetlogging.*; +import org.jitsi.util.*; /** * @author Bing SU (nova.su@gmail.com) @@ -24,6 +25,12 @@ public abstract class RTPConnectorOutputStream implements OutputDataStream { + /** + * The <tt>Logger</tt> used by the <tt>RTPConnectorOutputStream</tt> class + * and its instances for logging output. + */ + private static final Logger logger + = Logger.getLogger(RTPConnectorOutputStream.class); /** * The maximum number of packets to be sent to be kept in the queue of @@ -307,6 +314,15 @@ public void setMaxPacketsPerMillis(int maxPackets, long perMillis) */ public int write(byte[] buffer, int offset, int length) { + /* + * While calling write without targets can be carried out without a + * problem, such a situation may be a symptom of a problem. For example, + * it was discovered during testing that RTCP was seemingly-endlessly + * sent after hanging up a call. + */ + if (logger.isDebugEnabled() && targets.isEmpty()) + logger.debug("Write called without targets!", new Throwable()); + RawPacket packet = createRawPacket(buffer, offset, length); /* @@ -333,8 +349,8 @@ public int write(byte[] buffer, int offset, int length) public void setPriority(int priority) { // currently no priority is set -// if(maxPacketsPerMillisPolicy != null && -// maxPacketsPerMillisPolicy.sendThread != null) +// if ((maxPacketsPerMillisPolicy != null) +// && (maxPacketsPerMillisPolicy.sendThread != null)) // maxPacketsPerMillisPolicy.sendThread.setPriority(priority); } diff --git a/src/org/jitsi/impl/neomedia/codec/video/h264/JNIEncoder.java b/src/org/jitsi/impl/neomedia/codec/video/h264/JNIEncoder.java index ab1f160f7c9ac4c3d40a969fd155ad66eb468ce8..4062191fb88861795b40d5300d7724b6e91af9f2 100644 --- a/src/org/jitsi/impl/neomedia/codec/video/h264/JNIEncoder.java +++ b/src/org/jitsi/impl/neomedia/codec/video/h264/JNIEncoder.java @@ -238,16 +238,19 @@ public synchronized void close() } /** - * Event fired when RTCP feedback message is received. + * Notifies this <tt>RTCPFeedbackListener</tt> that an RTCP feedback message + * has been received * - * @param event <tt>RTCPFeedbackEvent</tt> + * @param event an <tt>RTCPFeedbackEvent</tt> which specifies the details of + * the notification event such as the feedback message type and the payload + * type */ - public void feedbackReceived(RTCPFeedbackEvent event) + public void rtcpFeedbackReceived(RTCPFeedbackEvent event) { /* - * If RTCP message is a Picture Loss Indication (PLI) or a - * Full Intra-frame Request (FIR) the encoder will force the next frame - * to be a keyframe. + * If RTCP message is a Picture Loss Indication (PLI) or a Full + * Intra-frame Request (FIR) the encoder will force the next frame to be + * a keyframe. */ if (event.getPayloadType() == RTCPFeedbackEvent.PT_PS) { diff --git a/src/org/jitsi/impl/neomedia/jmfext/media/renderer/video/JAWTRendererVideoComponent.java b/src/org/jitsi/impl/neomedia/jmfext/media/renderer/video/JAWTRendererVideoComponent.java index e8b1b0899aa53e4557bea13e37a91a133a529106..26f05e62dbe3bb58fec8da240fd91c1ddfc4ddac 100644 --- a/src/org/jitsi/impl/neomedia/jmfext/media/renderer/video/JAWTRendererVideoComponent.java +++ b/src/org/jitsi/impl/neomedia/jmfext/media/renderer/video/JAWTRendererVideoComponent.java @@ -21,7 +21,8 @@ public class JAWTRendererVideoComponent extends Canvas { /** - * Serial version UID. + * The serial version UID of the <tt>JAWTRendererVideoComponent</tt> class + * defined to silence a serialization compile-time warning. */ private static final long serialVersionUID = 0L; diff --git a/src/org/jitsi/impl/neomedia/transform/ControlTransformInputStream.java b/src/org/jitsi/impl/neomedia/transform/ControlTransformInputStream.java index db4a4ffc4ca85dfc8685a6dcef069d55710cd615..7b651e01b36d53913b2cb851b627aea4a780dc7f 100644 --- a/src/org/jitsi/impl/neomedia/transform/ControlTransformInputStream.java +++ b/src/org/jitsi/impl/neomedia/transform/ControlTransformInputStream.java @@ -10,6 +10,7 @@ import java.net.*; import java.util.*; +import org.jitsi.impl.neomedia.*; import org.jitsi.service.neomedia.event.*; /** @@ -67,60 +68,27 @@ public void removeRTCPFeedbackListener(RTCPFeedbackListener listener) * Copies the content of the most recently received packet into * <tt>inBuffer</tt>. * - * @param inBuffer the <tt>byte[]</tt> that we'd like to copy the content - * of the packet to. + * @param buffer the <tt>byte[]</tt> that we'd like to copy the content of + * the packet to. * @param offset the position where we are supposed to start writing in - * <tt>inBuffer</tt>. + * <tt>buffer</tt>. * @param length the number of <tt>byte</tt>s available for writing in - * <tt>inBuffer</tt>. + * <tt>buffer</tt>. * * @return the number of bytes read * * @throws IOException if <tt>length</tt> is less than the size of the * packet. */ - public int read(byte[] inBuffer, int offset, int length) + public int read(byte[] buffer, int offset, int length) throws IOException { - if (ioError) - return -1; - - int pktLength = pkt.getLength(); - - if (length < pktLength) - throw new IOException( - "Input buffer not big enough for " + pktLength); - - /* check if RTCP feedback message */ - - /* Feedback message size is minimum 12 bytes: - * Version/Padding/Feedback message type: 1 byte - * Payload type: 1 byte - * Length: 2 bytes - * SSRC of packet sender: 4 bytes - * SSRC of media source: 4 bytes - */ - if ((pktLength >= 12) && !listeners.isEmpty()) - { - byte data[] = pkt.getBuffer(); - int fmt = 0; - int pt = 0; - - /* get FMT field (last 5 bits of first byte) */ - fmt = (data[0] & 0x1F); - pt |= (data[1] & 0xFF); - - RTCPFeedbackEvent evt = new RTCPFeedbackEvent(this, fmt, pt); - - /* notify feedback listeners */ - for(RTCPFeedbackListener l : listeners) - l.feedbackReceived(evt); - } + int pktLength = super.read(buffer, offset, length); - System.arraycopy( - pkt.getBuffer(), pkt.getOffset(), - inBuffer, offset, - pktLength); + RTCPConnectorInputStream.fireRTCPFeedbackReceived( + this, + buffer, offset, pktLength, + listeners); return pktLength; } diff --git a/src/org/jitsi/service/neomedia/event/RTCPFeedbackEvent.java b/src/org/jitsi/service/neomedia/event/RTCPFeedbackEvent.java index 346f2e3a33edd568d610c1fa8d57b4c8d1640762..e0d1e15a6d00274a3a83ca8762d15306d826fcdc 100644 --- a/src/org/jitsi/service/neomedia/event/RTCPFeedbackEvent.java +++ b/src/org/jitsi/service/neomedia/event/RTCPFeedbackEvent.java @@ -45,12 +45,12 @@ public class RTCPFeedbackEvent /** * Feedback message type. */ - private int feedbackMessageType = 0; + private final int feedbackMessageType; /** * Payload type. */ - private int payloadType = 0; + private final int payloadType; /** * Constructor. diff --git a/src/org/jitsi/service/neomedia/event/RTCPFeedbackListener.java b/src/org/jitsi/service/neomedia/event/RTCPFeedbackListener.java index 7f1577b56546b8400efd383ecc88fb6b319b6280..7b7269768f10387f6067f34cddc559408d25858f 100644 --- a/src/org/jitsi/service/neomedia/event/RTCPFeedbackListener.java +++ b/src/org/jitsi/service/neomedia/event/RTCPFeedbackListener.java @@ -7,19 +7,21 @@ package org.jitsi.service.neomedia.event; /** - * <tt>RTCPFeedbackListener</tt> is used by codec to be notified - * by RTCP feedback event such as PLI (Picture Loss Indication) or - * FIR (Full Intra-frame Request). + * Represents a listener of RTCP feedback messages such as PLI (Picture Loss + * Indication) or FIR (Full Intra-frame Request). * * @author Sebastien Vincent */ public interface RTCPFeedbackListener { /** - * Event fired when RTCP feedback message is received. + * Notifies this <tt>RTCPFeedbackListener</tt> that an RTCP feedback message + * has been received * - * @param event <tt>RTCPFeedbackEvent</tt> + * @param event an <tt>RTCPFeedbackEvent</tt> which specifies the details of + * the notification event such as the feedback message type and the payload + * type */ - public void feedbackReceived(RTCPFeedbackEvent event); + public void rtcpFeedbackReceived(RTCPFeedbackEvent event); }