From 7c08000d4feaba3e7caa3ca71d7b549e624ebd09 Mon Sep 17 00:00:00 2001 From: Lyubomir Marinov <lyubomir.marinov@jitsi.org> Date: Fri, 3 Aug 2012 15:14:19 +0000 Subject: [PATCH] - Works on removing the video of a video conference participant from display on the remaining non-focus participants. - Tries to fix a crash in PortAudio (often seen at the end of video conference calls). --- .../jitsi/impl/neomedia/MediaStreamImpl.java | 19 ++++- .../impl/neomedia/RTPTranslatorImpl.java | 84 ++++++++++++++++--- .../protocol/portaudio/PortAudioStream.java | 46 ++++++---- 3 files changed, 121 insertions(+), 28 deletions(-) diff --git a/src/org/jitsi/impl/neomedia/MediaStreamImpl.java b/src/org/jitsi/impl/neomedia/MediaStreamImpl.java index 6a975309..feef7edb 100644 --- a/src/org/jitsi/impl/neomedia/MediaStreamImpl.java +++ b/src/org/jitsi/impl/neomedia/MediaStreamImpl.java @@ -1879,7 +1879,22 @@ private void stop(MediaDirection direction) && (MediaDirection.SENDRECV.equals(startedDirection) || MediaDirection.SENDONLY.equals(startedDirection))) { - stopSendStreams(false); + /* + * XXX It is not very clear at the time of this writing whether the + * SendStreams are to be stopped or closed. On one hand, stopping a + * direction may be a temporary transition which relies on retaining + * the SSRC. On the other hand, it may be permanent. In which case, + * the respective ReveiveStream on the remote peer will timeout at + * some point in time. In the context of video conferences, when a + * member stops the streaming of their video without leaving the + * conference, they will stop their SendStreams. However, the other + * members will need respective BYE RTCP packets in order to know + * that they are to remove the associated ReceiveStreams from + * display. The initial version of the code here used to stop the + * SendStreams without closing them but, given the considerations + * above, it is being changed to close them in the case of video. + */ + stopSendStreams(this instanceof VideoMediaStream); if (deviceSession != null) deviceSession.stop(MediaDirection.SENDONLY); @@ -1951,9 +1966,11 @@ private void stopReceiveStreams() try { if (logger.isTraceEnabled()) + { logger.trace( "Stopping receive stream with hashcode " + receiveStream.hashCode()); + } DataSource receiveStreamDataSource = receiveStream.getDataSource(); diff --git a/src/org/jitsi/impl/neomedia/RTPTranslatorImpl.java b/src/org/jitsi/impl/neomedia/RTPTranslatorImpl.java index d03ef4ef..b7490c4b 100644 --- a/src/org/jitsi/impl/neomedia/RTPTranslatorImpl.java +++ b/src/org/jitsi/impl/neomedia/RTPTranslatorImpl.java @@ -339,6 +339,55 @@ public synchronized void initialize( } } + /** + * Logs information about an RTCP packet using {@link #logger} for debugging + * purposes. + * + * @param obj the object which is the source of the log request + * @param methodName the name of the method on <tt>obj</tt> which is the + * source of the log request + * @param buffer the <tt>byte</tt>s which (possibly) represent an RTCP + * packet to be logged for debugging purposes + * @param offset the position within <tt>buffer</tt> at which the valid data + * begins + * @param length the number of bytes in <tt>buffer</tt> which constitute the + * valid data + */ + private static void logRTCP( + Object obj, String methodName, + byte[] buffer, int offset, int length) + { + if (length > 8) + { + byte b0 = buffer[offset]; + int v = (b0 & 0xc0) >>> 6; + + if (v == 2) + { + byte b1 = buffer[offset + 1]; + int pt = b1 & 0xff; + + if (pt == 203 /* BYE */) + { + int sc = b0 & 0x1f; + long ssrc = (sc > 0) ? readInt(buffer, offset + 4) : -1; + + logger.trace( + obj.getClass().getName() + + '.' + + methodName + + ": RTCP BYE v=" + + v + + "; pt=" + + pt + + "; ssrc=" + + ssrc + + ';'); + } + } + } + } + private int read( PushSourceStreamDesc streamDesc, byte[] buffer, int offset, int length, @@ -372,6 +421,8 @@ private int read( format = streamRTPManagerDesc.getFormat(payloadType); } } + else if (logger.isTraceEnabled()) + logRTCP(this, "read", buffer, offset, read); OutputDataStreamImpl outputStream = data @@ -560,23 +611,32 @@ private synchronized int doWrite( if (streamRTPManagerDesc != exclusion) { - if (data && (format != null) && (length > 0)) + if (data) { - Integer payloadType - = streamRTPManagerDesc.getPayloadType(format); - - if ((payloadType == null) && (exclusion != null)) - payloadType = exclusion.getPayloadType(format); - if (payloadType != null) + if ((format != null) && (length > 0)) { - int payloadTypeByteIndex = offset + 1; + Integer payloadType + = streamRTPManagerDesc.getPayloadType(format); - buffer[payloadTypeByteIndex] - = (byte) - ((buffer[payloadTypeByteIndex] & 0x80) - | (payloadType & 0x7f)); + if ((payloadType == null) && (exclusion != null)) + payloadType = exclusion.getPayloadType(format); + if (payloadType != null) + { + int payloadTypeByteIndex = offset + 1; + + buffer[payloadTypeByteIndex] + = (byte) + ((buffer[payloadTypeByteIndex] & 0x80) + | (payloadType & 0x7f)); + } } } + else if (logger.isTraceEnabled()) + { + logRTCP( + this, "doWrite", + buffer, offset, length); + } int streamWrite = streamDesc.stream.write(buffer, offset, length); diff --git a/src/org/jitsi/impl/neomedia/jmfext/media/protocol/portaudio/PortAudioStream.java b/src/org/jitsi/impl/neomedia/jmfext/media/protocol/portaudio/PortAudioStream.java index e840d3a8..c0bb159b 100644 --- a/src/org/jitsi/impl/neomedia/jmfext/media/protocol/portaudio/PortAudioStream.java +++ b/src/org/jitsi/impl/neomedia/jmfext/media/protocol/portaudio/PortAudioStream.java @@ -242,6 +242,12 @@ synchronized void setDeviceIndex(int deviceIndex) // DataSource#disconnect if (this.deviceIndex != PortAudio.paNoDevice) { + /* + * Just to be on the safe side, make sure #read(Buffer) is not + * currently executing. + */ + waitWhileStreamIsBusy(); + if (stream != 0) { try @@ -420,21 +426,7 @@ public synchronized void start() public synchronized void stop() throws IOException { - boolean interrupted = false; - - while (streamIsBusy) - { - try - { - wait(); - } - catch (InterruptedException iex) - { - interrupted = true; - } - } - if (interrupted) - Thread.currentThread().interrupt(); + waitWhileStreamIsBusy(); try { @@ -450,4 +442,28 @@ public synchronized void stop() throw ioex; } } + + /** + * Waits on this instance while {@link #streamIsBusy} is equal to + * <tt>true</tt> i.e. until it becomes <tt>false</tt>. The method should + * only be called by a thread that is the owner of this object's monitor. + */ + private void waitWhileStreamIsBusy() + { + boolean interrupted = false; + + while ((stream != 0) && streamIsBusy) + { + try + { + wait(); + } + catch (InterruptedException iex) + { + interrupted = true; + } + } + if (interrupted) + Thread.currentThread().interrupt(); + } } -- GitLab