diff --git a/src/net/java/sip/communicator/impl/media/codec/video/h264/JNIDecoder.java b/src/net/java/sip/communicator/impl/media/codec/video/h264/JNIDecoder.java index aa312b54f..fd8a62824 100644 --- a/src/net/java/sip/communicator/impl/media/codec/video/h264/JNIDecoder.java +++ b/src/net/java/sip/communicator/impl/media/codec/video/h264/JNIDecoder.java @@ -19,7 +19,7 @@ /** * Decodes incoming rtp data of type h264 and returns the result frames in RGB * format. - * + * * @author Damian Minkov * @author Lubomir Marinov */ @@ -376,6 +376,9 @@ public Format[] getSupportedOutputFormats(Format in) /** * Utility to perform format matching. + * + * @param in input format + * @param outs array of format */ public static Format matches(Format in, Format outs[]) { diff --git a/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java b/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java index 203f06cb8..bab2323c7 100644 --- a/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java +++ b/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java @@ -190,6 +190,12 @@ else if (MediaDeviceSession */ private CsrcTransformEngine csrcEngine; + /** + * Map of advanced attributes. + */ + protected Map advancedAttributes = + new Hashtable(); + /** * Initializes a new MediaStreamImpl instance which will use the * specified MediaDevice for both capture and playback of media @@ -392,6 +398,18 @@ protected CsrcTransformEngine getCsrcEngine() return csrcEngine; } + /** + * Set list of advanced attributes. + * + * @param attrs advanced attributes map + */ + public void setAdvancedAttributes(Map attrs) + { + if(attrs != null) + { + advancedAttributes.putAll(attrs); + } + } /** * Releases the resources allocated by this instance in the course of its * execution and prepares it to be garbage collected. diff --git a/src/net/java/sip/communicator/impl/neomedia/MediaUtils.java b/src/net/java/sip/communicator/impl/neomedia/MediaUtils.java index 481b5cea6..6cd74a648 100644 --- a/src/net/java/sip/communicator/impl/neomedia/MediaUtils.java +++ b/src/net/java/sip/communicator/impl/neomedia/MediaUtils.java @@ -15,6 +15,7 @@ import net.java.sip.communicator.impl.neomedia.codec.*; import net.java.sip.communicator.impl.neomedia.format.*; import net.java.sip.communicator.service.neomedia.*; +import net.java.sip.communicator.service.neomedia.device.ScreenDevice; import net.java.sip.communicator.service.neomedia.format.*; /** @@ -154,11 +155,23 @@ public class MediaUtils Map h264FormatParams = new HashMap(); - Map h264AdvancedParams + Map h264AdvancedAttributes = new HashMap(); h264FormatParams.put("packetization-mode", "1"); - h264AdvancedParams.put("rtcp-fb", "nack pli"); + h264AdvancedAttributes.put("rtcp-fb", "nack pli"); + + ScreenDevice screen = NeomediaActivator.getMediaServiceImpl(). + getDefaultScreenDevice(); + + java.awt.Dimension res = null; + + if(screen != null) + { + res = screen.getSize(); + } + + h264AdvancedAttributes.put("imageattr", createImageAttr(null, res)); addMediaFormats( MediaFormat.RTP_PAYLOAD_TYPE_UNKNOWN, @@ -166,7 +179,7 @@ public class MediaUtils MediaType.VIDEO, Constants.H264_RTP, h264FormatParams, - h264AdvancedParams); + h264AdvancedAttributes); } /** @@ -219,6 +232,8 @@ private static void addMediaFormats( * associated with rtpPayloadType * @param formatParameters the set of format-specific parameters of the * MediaFormats to be associated with rtpPayloadType + * @param advancedAttributes the set of advanced attributes of the + * MediaFormats to be associated with rtpPayload * @param clockRates the optional list of clock rates of the * MediaFormats to be associated with rtpPayloadType */ @@ -228,7 +243,7 @@ private static void addMediaFormats( MediaType mediaType, String jmfEncoding, Map formatParameters, - Map advancedParameters, + Map advancedAttributes, double... clockRates) { int clockRateCount = clockRates.length; @@ -260,7 +275,7 @@ private static void addMediaFormats( format, clockRate, formatParameters, - advancedParameters); + advancedAttributes); if (mediaFormat != null) mediaFormats.add(mediaFormat); @@ -292,7 +307,7 @@ private static void addMediaFormats( MediaFormat mediaFormat = MediaFormatImpl .createInstance(format, clockRate, formatParameters, - advancedParameters); + advancedAttributes); if (mediaFormat != null) mediaFormats.add(mediaFormat); @@ -530,4 +545,74 @@ else if (jmfEncoding.equals(VideoFormat.H261_RTP)) else return MediaFormat.RTP_PAYLOAD_TYPE_UNKNOWN; } + + + /** + * Creates value of an imgattr. + * + * http://tools.ietf.org/html/draft-ietf-mmusic-image-attributes-04 + * + * @param sendSize maximum size peer can send + * @param maxRecvSize maximum size peer can display + * @return string that represent imgattr that can be encoded via SIP/SDP or + * XMPP/Jingle + */ + private static String createImageAttr(java.awt.Dimension sendSize, + java.awt.Dimension maxRecvSize) + { + StringBuffer img = new StringBuffer(); + + /* send width */ + if(sendSize != null) + { + /* single value => send [x=width,y=height] */ + img.append("send [x="); + img.append((int)sendSize.getWidth()); + img.append(",y="); + img.append((int)sendSize.getHeight()); + img.append("]"); + /* + else + { + // range + img.append(" send [x=["); + img.append((int)minSendSize.getWidth()); + img.append("-"); + img.append((int)maxSendSize.getWidth()); + img.append("],y=["); + img.append((int)minSendSize.getHeight()); + img.append("-"); + img.append((int)maxSendSize.getHeight()); + img.append("]]"); + } + */ + } + else + { + /* can send "all" sizes */ + img.append("send *"); + } + + /* receive size */ + if(maxRecvSize != null) + { + /* basically we can receive any size up to our + * screen display size + */ + + /* recv [x=[min-max],y=[min-max]] */ + img.append(" recv [x=[0-"); + img.append((int)maxRecvSize.getWidth()); + img.append("],y=[0-"); + img.append((int)maxRecvSize.getHeight()); + img.append("]]"); + } + else + { + /* accept all sizes */ + img.append(" recv *"); + } + + return img.toString(); + } } diff --git a/src/net/java/sip/communicator/impl/neomedia/RTCPConnectorInputStream.java b/src/net/java/sip/communicator/impl/neomedia/RTCPConnectorInputStream.java index 400b08253..1c85b33ff 100644 --- a/src/net/java/sip/communicator/impl/neomedia/RTCPConnectorInputStream.java +++ b/src/net/java/sip/communicator/impl/neomedia/RTCPConnectorInputStream.java @@ -38,6 +38,9 @@ public RTCPConnectorInputStream(DatagramSocket socket) /** * Add an RTCPFeedbackListener. + * + * @param listener object that will listen to incoming RTCP feedback + * messages. */ public void addRTCPFeedbackListener(RTCPFeedbackListener listener) { @@ -48,7 +51,9 @@ public void addRTCPFeedbackListener(RTCPFeedbackListener listener) } /** - * Removve an RTCPFeedbackListener. + * Remove an RTCPFeedbackListener. + * + * @param listener object to remove from listening RTCP feedback messages. */ public void removeRTCPFeedbackListener(RTCPFeedbackListener listener) { diff --git a/src/net/java/sip/communicator/impl/neomedia/RTCPFeedbackPacket.java b/src/net/java/sip/communicator/impl/neomedia/RTCPFeedbackPacket.java index c5c83cad1..1eec9f383 100644 --- a/src/net/java/sip/communicator/impl/neomedia/RTCPFeedbackPacket.java +++ b/src/net/java/sip/communicator/impl/neomedia/RTCPFeedbackPacket.java @@ -6,10 +6,13 @@ */ package net.java.sip.communicator.impl.neomedia; -import java.util.*; - import javax.media.rtp.*; +/** + * Represents an RTCP feedback packet as described in RFC4585. + * + * @author Sebastien Vincent + */ public class RTCPFeedbackPacket { /** @@ -49,18 +52,18 @@ public RTCPFeedbackPacket(int type, int payloadType, long sender, long src) } /** - * Write packet to output stream. + * Write RTCP packet to output stream of a DatagramSocket. * - * @param out OutputDataStream + * @param out OutputDataStream of a DatagramSocket */ public void writeTo(OutputDataStream out) { byte data[] = new byte[12]; byte vpfmt = (byte)((2 << 7) | (0 << 6) | (byte)fmt); - + data[0] = vpfmt; data[1] = (byte)payloadType; - + /* length (in 32-bit words minus one) */ data[2] = 0; data[3] = 2; /* common packet is 12 bytes so (12/4) - 1 */ @@ -70,13 +73,13 @@ public void writeTo(OutputDataStream out) data[5] = (byte)((senderSSRC >> 16) & 0xFF); data[6] = (byte)((senderSSRC >> 8) & 0xFF); data[7] = (byte)(senderSSRC & 0xFF); - + /* source SSRC */ data[8] = (byte)(sourceSSRC >> 24); data[9] = (byte)((sourceSSRC >> 16) & 0xFF); data[10] = (byte)((sourceSSRC >> 8) & 0xFF); data[11] = (byte)(sourceSSRC & 0xFF); - + /* effective write */ out.write(data, 0, 12); } diff --git a/src/net/java/sip/communicator/impl/neomedia/VideoMediaStreamImpl.java b/src/net/java/sip/communicator/impl/neomedia/VideoMediaStreamImpl.java index a9f96be9c..755fcac1b 100644 --- a/src/net/java/sip/communicator/impl/neomedia/VideoMediaStreamImpl.java +++ b/src/net/java/sip/communicator/impl/neomedia/VideoMediaStreamImpl.java @@ -8,6 +8,7 @@ import java.awt.*; import java.util.*; +import java.util.regex.*; import javax.media.*; import javax.media.control.*; @@ -55,7 +56,7 @@ public class VideoMediaStreamImpl private Dimension outputSize; /** - * Use or not PLI. + * Use or not RTCP feedback Picture Loss Indication messages. */ private boolean usePLI = false; @@ -523,23 +524,156 @@ public void setDevice(MediaDevice device) } /** - * Use or not RTCP feedback Picture Loss Indication. + * Set list of advanced attributes. * - * @param use use or not PLI + * @param attrs advanced attributes map */ - public void setRtcpFeedbackPLI(boolean use) + public void setAdvancedAttributes(Map attrs) { - usePLI = use; + super.setAdvancedAttributes(attrs); + + /* walk through advanced attributes and see + * if we recognized something we support + */ + if(attrs != null) + { + for(Map.Entry mapEntry + : advancedAttributes.entrySet()) + { + String key = mapEntry.getKey(); + String value = mapEntry.getValue(); + + if(key.equals("rtcp-fb") && value.equals("nack pli")) + { + usePLI = true; + } + else if(key.equals("imageattr")) + { + Dimension res[] = parseSendRecvResolution(value); + + if(res != null) + { + outputSize = res[1]; + } + } + } + } } /** - * Set negociated output size. + * Extracts and returns maximum resolution can receive from the image + * attribute. * - * @param size output size of video stream + * @param imgattr send/recv resolution string + * @return maximum resolution array (first element is send, second one is + * recv). Elements could be null if image attribute is not present or if + * resoluion is a wildcard. */ - public void setOutputSize(Dimension size) + public static java.awt.Dimension[] parseSendRecvResolution(String imgattr) { - outputSize = size; + java.awt.Dimension res[] = new java.awt.Dimension[2]; + String token = null; + Pattern pSendSingle = Pattern.compile("send \\[x=[0-9]+,y=[0-9]+\\]"); + Pattern pRecvSingle = Pattern.compile("recv \\[x=[0-9]+,y=[0-9]+\\]"); + Pattern pSendRange = Pattern.compile( + "send \\[x=\\[[0-9]+-[0-9]+\\],y=\\[[0-9]+-[0-9]+\\]\\]"); + Pattern pRecvRange = Pattern.compile( + "recv \\[x=\\[[0-9]+-[0-9]+\\],y=\\[[0-9]+-[0-9]+\\]\\]"); + Pattern pNumeric = Pattern.compile("[0-9]+"); + Matcher mSingle = null; + Matcher mRange = null; + Matcher m = null; + + /* resolution (width and height) can be on four forms + * + * - single value [x=1920,y=1200] + * - range of values [x=[800-1024],y=[600-768]] + * - fixed range of values [x=[800,1024],y=[600,768]] + * - range of values with step [x=[800:32:1024],y=[600:32:768]] + * + * For the moment we only support the first two forms. + */ + + /* send part */ + mSingle = pSendSingle.matcher(imgattr); + mRange = pSendRange.matcher(imgattr); + + if(mSingle.find()) + { + int val[] = new int[2]; + int i = 0; + token = imgattr.substring(mSingle.start(), mSingle.end()); + m = pNumeric.matcher(token); + + while(m.find() && i < 2) + { + val[i] = Integer.parseInt(token.substring(m.start(), m.end())); + } + + res[0] = new java.awt.Dimension(val[0], val[1]); + } + else if(mRange.find()) /* try with range */ + { + /* have two value for width and two for height (min-max) */ + int val[] = new int[4]; + int i = 0; + token = imgattr.substring(mRange.start(), mRange.end()); + m = pNumeric.matcher(token); + + while(m.find() && i < 4) + { + val[i] = Integer.parseInt(token.substring(m.start(), m.end())); + i++; + } + + res[0] = new java.awt.Dimension(val[1], val[3]); + } + + /* recv part */ + mSingle = pRecvSingle.matcher(imgattr); + mRange = pRecvRange.matcher(imgattr); + + if(mSingle.find()) + { + int val[] = new int[2]; + int i = 0; + token = imgattr.substring(mSingle.start(), mSingle.end()); + m = pNumeric.matcher(token); + + while(m.find() && i < 2) + { + val[i] = Integer.parseInt(token.substring(m.start(), m.end())); + } + + res[1] = new java.awt.Dimension(val[0], val[1]); + } + else if(mRange.find()) /* try with range */ + { + /* have two value for width and two for height (min-max) */ + int val[] = new int[4]; + int i = 0; + token = imgattr.substring(mRange.start(), mRange.end()); + m = pNumeric.matcher(token); + + while(m.find() && i < 4) + { + val[i] = Integer.parseInt(token.substring(m.start(), m.end())); + i++; + } + + res[1] = new java.awt.Dimension(val[1], val[3]); + } + + token = null; + mSingle = null; + mRange = null; + m = null; + pRecvRange = null; + pSendSingle = null; + pRecvSingle = null; + pSendRange = null; + + return res; } /** diff --git a/src/net/java/sip/communicator/impl/neomedia/codec/video/h264/DePacketizer.java b/src/net/java/sip/communicator/impl/neomedia/codec/video/h264/DePacketizer.java index 951c69349..093d355d8 100644 --- a/src/net/java/sip/communicator/impl/neomedia/codec/video/h264/DePacketizer.java +++ b/src/net/java/sip/communicator/impl/neomedia/codec/video/h264/DePacketizer.java @@ -479,6 +479,8 @@ public void setSSRC(long localSSRC, long remoteSSRC) /** * Use or not RTCP feedback PLI. + * + * @param use use or not RTCP PLI message */ public void setRtcpFeedbackPLI(boolean use) { diff --git a/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java b/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java index fdadc30d6..2b84899e8 100644 --- a/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java +++ b/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java @@ -21,6 +21,7 @@ import net.java.sip.communicator.impl.neomedia.codec.video.h264.*; import net.java.sip.communicator.impl.neomedia.imgstreaming.*; import net.java.sip.communicator.service.neomedia.*; +import net.java.sip.communicator.service.neomedia.format.*; import net.java.sip.communicator.service.neomedia.event.*; import net.java.sip.communicator.service.resources.*; import net.java.sip.communicator.util.*; @@ -1019,16 +1020,25 @@ protected void setProcessorFormat(Processor processor, Format format) { Format newFormat = null; VideoFormat tmp = (VideoFormat)format; + Dimension deviceSize = ((VideoMediaFormat)getDevice().getFormat()).getSize(); /* Add a size in the output format. As VideoFormat has no setter, we - * recreate the object. + * recreate the object. Check also if capture device can output + * this size. */ - if(outputSize != null) + if((deviceSize != null && outputSize != null) && + (outputSize.width > 0 && outputSize.height > 0) && + (deviceSize.width > outputSize.width || + deviceSize.height > outputSize.height)) { newFormat = new VideoFormat(tmp.getEncoding(), outputSize, tmp.getMaxDataLength(), tmp.getDataType(), tmp.getFrameRate()); } + else + { + outputSize = null; + } super.setProcessorFormat( processor, diff --git a/src/net/java/sip/communicator/impl/neomedia/format/MediaFormatImpl.java b/src/net/java/sip/communicator/impl/neomedia/format/MediaFormatImpl.java index 1b0539956..39a44287f 100644 --- a/src/net/java/sip/communicator/impl/neomedia/format/MediaFormatImpl.java +++ b/src/net/java/sip/communicator/impl/neomedia/format/MediaFormatImpl.java @@ -68,6 +68,7 @@ else if (format instanceof VideoFormat) * @param clockRate the clock rate of the new instance * @param formatParameters the set of format-specific parameters of the new * instance + * @param advancedAttrs advanced attributes of the new instance * @return a new MediaFormat instance for the specified JMF * Format and with the specified clock rate and set of * format-specific parameters @@ -76,7 +77,7 @@ public static MediaFormatImpl createInstance( Format format, double clockRate, Map formatParameters, - Map advancedParameters) + Map advancedAttrs) { if (format instanceof AudioFormat) { @@ -93,7 +94,7 @@ public static MediaFormatImpl createInstance( (AudioFormat) clockRateAudioFormat.intersects(audioFormat), formatParameters, - advancedParameters); + advancedAttrs); } if (format instanceof VideoFormat) return @@ -101,7 +102,7 @@ public static MediaFormatImpl createInstance( (VideoFormat) format, clockRate, formatParameters, - advancedParameters); + advancedAttrs); return null; } diff --git a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandler.java b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandler.java index f5e184a0a..1434bfa2a 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandler.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandler.java @@ -11,8 +11,6 @@ import java.util.*; import java.beans.*; -import java.awt.Dimension; /* disambiguates java.awt.List and java.util.List */ - import javax.sdp.*; import net.java.sip.communicator.impl.protocol.sip.sdp.*; @@ -159,16 +157,6 @@ public class CallPeerMediaHandler */ private VideoMediaStream videoStream = null; - /** - * Image size that remote peer can send. - */ - private Dimension maxSendSize = null; - - /** - * Image size that remote peer can receive - */ - private Dimension maxRecvSize = null; - /** * The last-known local SSRC of {@link #videoStream}. */ @@ -828,27 +816,12 @@ private MediaStream initStream(StreamConnector connector, throws OperationFailedException { MediaStream stream = null; - Dimension size = null; if (device.getMediaType() == MediaType.AUDIO) stream = this.audioStream; else { stream = this.videoStream; - - if(device != null && device.getFormat() != null) - { - Dimension deviceSize = ((VideoMediaFormat)device.getFormat()) - .getSize(); - - if((deviceSize != null && maxRecvSize != null) && - (maxRecvSize.width > 0 && maxRecvSize.height > 0) && - (deviceSize.width > maxRecvSize.width || - deviceSize.height > maxRecvSize.height)) - { - size = maxRecvSize; - } - } } if (stream == null) @@ -868,18 +841,7 @@ private MediaStream initStream(StreamConnector connector, //this is a reinit } - - if(device != null && device.getMediaType() == MediaType.VIDEO) - { - /* set negociated output size for video stream */ - ((VideoMediaStream)stream).setOutputSize(size); - - /* set rtcp-fb */ - if(format.getAdvancedParameters().containsKey("rtcp-fb")) - { - ((VideoMediaStream)stream).setRtcpFeedbackPLI(true); - } - } + stream.setAdvancedAttributes(format.getAdvancedParameters()); return configureAndStartStream( device, format, target, direction, stream); @@ -1480,16 +1442,6 @@ private void processAnswer(SessionDescription answer) MediaDirection direction = devDirection.getDirectionForAnswer(remoteDirection); - /* extract remote peer maximum supported resolution */ - Dimension res[] = SdpUtils.extractSendRecvResolution( - mediaDescription); - - if(res != null) - { - maxSendSize = res[0]; - maxRecvSize = res[1]; - } - // create the corresponding stream... initStream(connector, dev, supportedFormats.get(0), target, direction); diff --git a/src/net/java/sip/communicator/impl/protocol/sip/sdp/SdpUtils.java b/src/net/java/sip/communicator/impl/protocol/sip/sdp/SdpUtils.java index 0e535a233..22535ac16 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/sdp/SdpUtils.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/sdp/SdpUtils.java @@ -10,7 +10,6 @@ import java.net.*; import java.net.URI; import java.util.*; -import java.util.regex.*; import javax.sdp.*; import javax.sip.header.ContentTypeHeader; // disambiguates MediaType @@ -303,140 +302,6 @@ private static MediaDescription removeMediaDesc( return null; } - /** - * Extracts and returns maximum resolution can receive from the image - * attribute. - * - * @param mediaDesc the MediaDescription that we'd like to probe - * for maximum receive resolution. - * @return maximum resolution array (first element is send, second one is - * recv). Elements could be null if image attribute is not present or if - * resoluion is a wildcard. - */ - public static java.awt.Dimension[] extractSendRecvResolution( - MediaDescription mediaDesc) - { - java.awt.Dimension res[] = new java.awt.Dimension[2]; - String imgattr = null; - String token = null; - Pattern pSendSingle = Pattern.compile("send \\[x=[0-9]+,y=[0-9]+\\]"); - Pattern pRecvSingle = Pattern.compile("recv \\[x=[0-9]+,y=[0-9]+\\]"); - Pattern pSendRange = Pattern.compile( - "send \\[x=\\[[0-9]+-[0-9]+\\],y=\\[[0-9]+-[0-9]+\\]\\]"); - Pattern pRecvRange = Pattern.compile( - "recv \\[x=\\[[0-9]+-[0-9]+\\],y=\\[[0-9]+-[0-9]+\\]\\]"); - Pattern pNumeric = Pattern.compile("[0-9]+"); - Matcher mSingle = null; - Matcher mRange = null; - Matcher m = null; - - try - { - imgattr = mediaDesc.getAttribute("imageattr"); - } - catch (SdpParseException exc) - { - logger.debug("A funny thing just happened ...", exc); - return null; - } - - if(imgattr == null) - { - return null; - } - - /* resolution (width and height) can be on four forms - * - * - single value [x=1920,y=1200] - * - range of values [x=[800-1024],y=[600-768]] - * - fixed range of values [x=[800,1024],y=[600,768]] - * - range of values with step [x=[800:32:1024],y=[600:32:768]] - * - * For the moment we only support the first two forms. - */ - - /* send part */ - mSingle = pSendSingle.matcher(imgattr); - mRange = pSendRange.matcher(imgattr); - - if(mSingle.find()) - { - int val[] = new int[2]; - int i = 0; - token = imgattr.substring(mSingle.start(), mSingle.end()); - m = pNumeric.matcher(token); - - while(m.find() && i < 2) - { - val[i] = Integer.parseInt(token.substring(m.start(), m.end())); - } - - res[0] = new java.awt.Dimension(val[0], val[1]); - } - else if(mRange.find()) /* try with range */ - { - /* have two value for width and two for height (min-max) */ - int val[] = new int[4]; - int i = 0; - token = imgattr.substring(mRange.start(), mRange.end()); - m = pNumeric.matcher(token); - - while(m.find() && i < 4) - { - val[i] = Integer.parseInt(token.substring(m.start(), m.end())); - i++; - } - - res[0] = new java.awt.Dimension(val[1], val[3]); - } - - /* recv part */ - mSingle = pRecvSingle.matcher(imgattr); - mRange = pRecvRange.matcher(imgattr); - - if(mSingle.find()) - { - int val[] = new int[2]; - int i = 0; - token = imgattr.substring(mSingle.start(), mSingle.end()); - m = pNumeric.matcher(token); - - while(m.find() && i < 2) - { - val[i] = Integer.parseInt(token.substring(m.start(), m.end())); - } - - res[1] = new java.awt.Dimension(val[0], val[1]); - } - else if(mRange.find()) /* try with range */ - { - /* have two value for width and two for height (min-max) */ - int val[] = new int[4]; - int i = 0; - token = imgattr.substring(mRange.start(), mRange.end()); - m = pNumeric.matcher(token); - - while(m.find() && i < 4) - { - val[i] = Integer.parseInt(token.substring(m.start(), m.end())); - i++; - } - - res[1] = new java.awt.Dimension(val[1], val[3]); - } - - token = null; - mSingle = null; - mRange = null; - m = null; - pRecvRange = null; - pSendSingle = null; - pRecvSingle = null; - pSendRange = null; - - return res; - } - /** * Extracts and returns the list of MediaFormats advertised in * mediaDesc preserving their oder and registering dynamic payload @@ -465,7 +330,6 @@ public static List extractFormats( { List mediaFmts = new ArrayList(); Vector formatStrings; - List advp = new ArrayList(); try { @@ -481,20 +345,6 @@ public static List extractFormats( return mediaFmts; } - try - { - Attribute rtcpFbAsterisk = findPayloadTypeSpecificAttribute( - mediaDesc.getAttributes(false), "rtcp-fb", "*"); - - if(rtcpFbAsterisk != null) - advp.add(rtcpFbAsterisk); - } - catch(SdpException e) - { - //there was a problem parsing the "*" rtcp-fb. try to ignore. - logger.debug("Does not seem like a valid rtcp-fb:* attribute", e); - } - for(String ptStr : formatStrings) { byte pt = -1; @@ -525,17 +375,11 @@ public static List extractFormats( } Attribute fmtp = null; - Attribute rtcpFb = null; try { fmtp = findPayloadTypeSpecificAttribute( mediaDesc.getAttributes(false), "fmtp", ptStr); - rtcpFb = findPayloadTypeSpecificAttribute( - mediaDesc.getAttributes(false), "rtcp-fb", ptStr); - - if(rtcpFb != null) - advp.add(rtcpFb); } catch (SdpException exc) { @@ -544,6 +388,18 @@ public static List extractFormats( fmtp + " does not seem like a valid fmtp: attribute", exc); } + List advp = null; + + try + { + advp = findAdvancedAttributes(mediaDesc.getAttributes(false), + ptStr); + } + catch(SdpException exc) + { + logger.debug("Problem parsing advanced attributes", exc); + } + MediaFormat mediaFormat = null; try { @@ -727,7 +583,7 @@ private static RTPExtension parseRTPExtensionAttribute( * @param rtpmap an SDP Attribute mapping the payloadType * to an encoding name. * @param fmtp a list of format specific parameters - * @param advp list of advanced parameters (rtcp-fb, ...) + * @param advp list of advanced parameters * @param ptRegistry the {@link DynamicPayloadTypeRegistry} that we are to * use in case payloadType is dynamic and rtpmap is * null (in which case we can hope its in the registry). @@ -816,18 +672,14 @@ private static MediaFormat createFormat( //Format parameters Map fmtParamsMap = null; - Map advancedParamsMap = null; + Map advancedAttrMap = null; if ( fmtp != null) fmtParamsMap = parseFmtpAttribute(fmtp); - for(Attribute attr : advp) + if(advp != null) { - /* RTCP feedback support */ - if (attr.getName().equals("rtcp-fb")) - { - advancedParamsMap = parseRTCPFeedbackAttribute(attr); - } + advancedAttrMap = parseAdvancedAttributes(advp); } //now create the format. @@ -841,7 +693,7 @@ private static MediaFormat createFormat( clockRate, numChannels, fmtParamsMap, - advancedParamsMap); + advancedAttrMap); /* * We've just created a MediaFormat for the specified payloadType so we @@ -868,31 +720,33 @@ private static MediaFormat createFormat( /** * Parses the value of rtcpAttr attribute. * - * @param rtcpAttr SDP attribute containing rtcp-fb parameters. - * @return + * @param attrs SDP advanced attributes + * @return map containing key/value of attribute */ - private static Map parseRTCPFeedbackAttribute( - Attribute rtcpAttr) + private static Map parseAdvancedAttributes( + List attrs) + throws SdpException { - Map ret = new Hashtable(); - String attrVal = rtcpAttr.toString(); - StringTokenizer tokenizer = new StringTokenizer(attrVal, " ", false); - - /* valid rtcp-fb we support is on the form: - * a=rtcp-fb:100 nack pli - */ - if (tokenizer.countTokens() != 3) + if(attrs == null) return null; - /* skip rtcp-fb:pt */ - tokenizer.nextToken(); - String ackType = tokenizer.nextToken(); - String ackExt = tokenizer.nextToken(); + Map ret = new Hashtable(); - if(ackType.equals("nack") && (ackExt.equals("pli\r\n") || - ackExt.equals("pli "))) + for(Attribute attr : attrs) { - ret.put("rtcp-fb", "nack pli"); + String attrName = attr.getName(); + String attrVal = attr.getValue(); + int idx = -1; + + /* get the part after payloadtype */ + idx = attrVal.indexOf(" "); + + if(idx != -1) + { + attrVal = attrVal.substring(idx + 1, attrVal.length()); + } + + ret.put(attrName, attrVal); } if (ret.size() == 0) @@ -962,7 +816,6 @@ private static Map parseFmtpAttribute(Attribute fmtpAttr) String paramName = token.substring(0, indexOfEq ); String paramValue = token.substring(indexOfEq + 1, token.length()); - fmtParamsMap.put(paramName, paramValue); } @@ -973,6 +826,57 @@ private static Map parseFmtpAttribute(Attribute fmtpAttr) return fmtParamsMap; } + /** + * Tries to find advanced attributes (i.e. that are not fmtp or rtpmap + * pertaining to the specified payloadType in the + * mediaAttributes list and returns them if they exists. + * + * @param mediaAttributes the list of Attribute fields where we + * are to look for the attribute. + * @param payloadType the payloadType that we are trying to find. + * @return the list of advanced Attribute and whose value pertains + * to payloadType or null if no + * such attributes was found. + * + * @throws SdpException when ... well never really, it's there just for ... + * fun? + */ + private static List findAdvancedAttributes( + Vector mediaAttributes, + String payloadType) + throws SdpException + { + if( mediaAttributes == null || mediaAttributes.size() == 0) + return null; + + List ret = new ArrayList(); + + for(Attribute attr : mediaAttributes) + { + String attrName = attr.getName(); + String attrValue = attr.getValue(); + + /* skip fmtp and rtpmap attribute */ + if(attrName.equals("rtpmap") || attrName.equals("fmtp") || + attrValue == null) + continue; + + attrValue = attrValue.trim(); + + /* have to match payload type or wildcard */ + if(!attrValue.startsWith(payloadType + " ") && + !attrValue.startsWith("* ")) + continue; + + ret.add(attr); + } + + if(ret.isEmpty()) + return null; + + return ret; + } + /** * Tries to find an attribute with the specified attibuteName * pertaining to the specified payloadType in the @@ -1412,7 +1316,7 @@ public static MediaDescription createMediaDescription( mediaAttributes.add(fmtp); } - /* add extra attributes (rtcp-fb, ...) */ + /* add extra attributes */ Iterator> iter = format .getAdvancedParameters().entrySet().iterator(); @@ -1427,27 +1331,6 @@ public static MediaDescription createMediaDescription( payloadTypesArray[i] = payloadType; } - /* image attribute */ - if(mediaType == MediaType.VIDEO) - { - /* one image attribute for all payload - * - * basically peer can send any size and can - * receive image size up to its display screen size - */ - ScreenDevice screen = SipActivator.getMediaService(). - getDefaultScreenDevice(); - java.awt.Dimension res = null; - - if(screen != null) - { - res = screen.getSize(); - } - - Attribute imgattr = createImageAttribute((byte)0, null, res); - mediaAttributes.add(imgattr); - } - // rtcp: (only include it if different from the default (i.e. rtp + 1) int rtpPort = connector.getDataSocket().getLocalPort(); int rtcpPort = connector.getControlSocket().getLocalPort(); @@ -1571,84 +1454,6 @@ else if(MediaDirection.SENDRECV.equals(direction)) return sdpFactory.createAttribute(dirStr, null); } - /** - * Creates an Attribute instance for send/recv image negociation. - * - * http://tools.ietf.org/html/draft-ietf-mmusic-image-attributes-04 - * - * @param payloadType payload type - * @param format capture VideoMediaFormat - * @param maxRecvSize maximum size peer can display - * @return image Attribute - */ - private static Attribute createImageAttribute(byte payloadType, - java.awt.Dimension sendSize, java.awt.Dimension maxRecvSize) - { - StringBuffer img = new StringBuffer("imageattr:"); - - if(payloadType != 0) - { - img.append(payloadType); - } - else - { - img.append("*"); - } - - /* send width */ - if(sendSize != null) - { - /* single value => send [x=width,y=height] */ - img.append(" send [x="); - img.append((int)sendSize.getWidth()); - img.append(",y="); - img.append((int)sendSize.getHeight()); - img.append("]"); - /* - else - { - // range - img.append(" send [x=["); - img.append((int)minSendSize.getWidth()); - img.append("-"); - img.append((int)maxSendSize.getWidth()); - img.append("],y=["); - img.append((int)minSendSize.getHeight()); - img.append("-"); - img.append((int)maxSendSize.getHeight()); - img.append("]]"); - } - */ - } - else - { - /* can send "all" sizes */ - img.append(" send *"); - } - - /* receive size */ - if(maxRecvSize != null) - { - /* basically we can receive any size up to our - * screen display size - */ - - /* recv [x=[min-max],y=[min-max]] */ - img.append(" recv [x=[0-"); - img.append((int)maxRecvSize.getWidth()); - img.append("],y=[0-"); - img.append((int)maxRecvSize.getHeight()); - img.append("]]"); - } - else - { - /* accept all sizes */ - img.append(" recv *"); - } - - return sdpFactory.createAttribute(img.toString(), null); - } - /** * Returns the media type (e.g. audio or video) for the specified media * description. diff --git a/src/net/java/sip/communicator/service/neomedia/MediaStream.java b/src/net/java/sip/communicator/service/neomedia/MediaStream.java index 404633339..1bac2b54e 100644 --- a/src/net/java/sip/communicator/service/neomedia/MediaStream.java +++ b/src/net/java/sip/communicator/service/neomedia/MediaStream.java @@ -277,4 +277,11 @@ public void addDynamicRTPPayloadType( * @return the ZrtpControl for the current stream. */ public ZrtpControl getZrtpControl(); + + /** + * Set list of advanced attributes. + * + * @param attrs advanced attributes map + */ + public void setAdvancedAttributes(Map attrs); } diff --git a/src/net/java/sip/communicator/service/neomedia/VideoMediaStream.java b/src/net/java/sip/communicator/service/neomedia/VideoMediaStream.java index 6e006a037..b76ec1c79 100644 --- a/src/net/java/sip/communicator/service/neomedia/VideoMediaStream.java +++ b/src/net/java/sip/communicator/service/neomedia/VideoMediaStream.java @@ -70,18 +70,4 @@ public interface VideoMediaStream * VideoMediaStream */ public void removeVideoListener(VideoListener listener); - - /** - * Set negociated output size. - * - * @param size output size of video stream - */ - public void setOutputSize(Dimension size); - - /** - * Use or not RTCP feedback Picture Loss Indication. - * - * @param use use or not PLI - */ - public void setRtcpFeedbackPLI(boolean use); } diff --git a/src/net/java/sip/communicator/service/neomedia/format/MediaFormatFactory.java b/src/net/java/sip/communicator/service/neomedia/format/MediaFormatFactory.java index 391e22434..07b79da62 100644 --- a/src/net/java/sip/communicator/service/neomedia/format/MediaFormatFactory.java +++ b/src/net/java/sip/communicator/service/neomedia/format/MediaFormatFactory.java @@ -122,6 +122,8 @@ public MediaFormat createMediaFormat( * for * @param formatParams any codec specific parameters which have been * received via SIP/SDP or XMPP/Jingle + * @param advancedAttrs advanced attributes received via SIP/SDP or + * XMPP/Jingle * @return a MediaFormat with the specified encoding, * clockRate and set of format parameters which is either an * AudioMediaFormat or a VideoMediaFormat instance if @@ -132,7 +134,7 @@ public MediaFormat createMediaFormat( String encoding, double clockRate, Map formatParams, - Map advancedParams); + Map advancedAttrs); /** * Creates a MediaFormat for the specified encoding, @@ -150,6 +152,8 @@ public MediaFormat createMediaFormat( * encoding; otherwise, ignored * @param formatParams any codec specific parameters which have been * received via SIP/SDP or XMPP/Jingle + * @param advancedAttrs advanced attributes received via SIP/SDP or + * XMPP/Jingle * @return a MediaFormat with the specified encoding, * clockRate, channels and set of format parameters which * is either an AudioMediaFormat or a VideoMediaFormat @@ -161,7 +165,7 @@ public MediaFormat createMediaFormat( double clockRate, int channels, Map formatParams, - Map advancedParams); + Map advancedAttrs); /** * Creates a MediaFormat either for the specified @@ -188,6 +192,8 @@ public MediaFormat createMediaFormat( * encoding; otherwise, ignored * @param formatParams any codec specific parameters which have been * received via SIP/SDP or XMPP/Jingle + * @param advancedAttrs advanced attributes received via SIP/SDP or + * XMPP/Jingle * @return a MediaFormat with the specified encoding, * clockRate, channels and set of format parameters which * is either an AudioMediaFormat or a VideoMediaFormat @@ -200,5 +206,5 @@ public MediaFormat createMediaFormat( double clockRate, int channels, Map formatParams, - Map advancedParams); + Map advancedAttrs); }