
org.jitsi.service.neomedia.rtp.RTCPExtendedReport Maven / Gradle / Ivy
Show all versions of libjitsi Show documentation
/*
* 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.service.neomedia.rtp;
import java.io.*;
import java.util.*;
import net.sf.fmj.media.rtp.*;
/**
* Represents an RTP Control Protocol Extended Report (RTCP XR) packet in the
* terms of FMJ i.e. as an RTCPPacket sub-class.
*
* @author Lyubomir Marinov
*/
public class RTCPExtendedReport
extends RTCPPacket
{
/**
* Represents an abstract, base extended report block.
*
* @author Lyubomir Marinov
*/
public static abstract class ReportBlock
{
/**
* The block type/format of this report block.
*/
public final short blockType;
/**
* Initializes a new ReportBlock instance of a specific block
* type.
*
* @param blockType the block type/format of the new instance
*/
protected ReportBlock(short blockType)
{
this.blockType = blockType;
}
/**
* Serializes/writes the binary representation of this
* ReportBlock into a specific DataOutputStream.
*
* @param dataoutputstream the DataOutputStream into which the
* binary representation of this ReportBlock is to be
* serialized/written.
* @throws IOException if an input/output error occurs during the
* serialization/writing of the binary representation of this
* ReportBlock
*/
protected abstract void assemble(DataOutputStream dataoutputstream)
throws IOException;
/**
* Computes the length in bytes of this ReportBlock,
* including the header and any padding.
*
* The implementation of ReportBlock returns the length in
* bytes of the header of an extended report block i.e.
* 4. The implementation is provided as a convenience because
* RFC 3611 defines that the type-specific block contents of an extended
* report block may be zero bits long if the block type definition
* permits.
*
* @return the length in bytes of this ReportBlock,
* including the header and any padding.
*/
public int calcLength()
{
return
1 /* block type (BT) */
+ 1 /* type-specific */
+ 2 /* block length */;
}
}
/**
* Implements "VoIP Metrics Report Block" i.e. an extended report
* block which provides metrics for monitoring voice over IP (VoIP) calls.
*
* @author Lyubomir Marinov
*/
public static class VoIPMetricsReportBlock
extends ReportBlock
{
/**
* The jitter buffer size is being dynamically adjusted to deal with
* varying levels of jitter.
*/
public static final byte ADAPTIVE_JITTER_BUFFER_ADAPTIVE = 3;
/**
* Silence is being inserted in place of lost packets.
*/
public static final byte DISABLED_PACKET_LOSS_CONCEALMENT = 1;
/**
* An enhanced interpolation algorithm is being used; algorithms of this
* type are able to conceal high packet loss rates effectively.
*/
public static final byte ENHANCED_PACKET_LOSS_CONCEALMENT = 2;
/**
* The jitter buffer size is maintained at a fixed level.
*/
public static final byte NON_ADAPTIVE_JITTER_BUFFER_ADAPTIVE = 2;
public static final byte RESERVED_JITTER_BUFFER_ADAPTIVE = 1;
public static final String SDP_PARAMETER = "voip-metrics";
/**
* A simple replay or interpolation algorithm is being used to fill-in
* the missing packet; this approach is typically able to conceal
* isolated lost packets at low packet loss rates.
*/
public static final byte STANDARD_PACKET_LOSS_CONCEALMENT = 3;
public static final byte UNKNOWN_JITTER_BUFFER_ADAPTIVE = 0;
/**
* No information is available concerning the use of packet loss
* concealment (PLC); however, for some codecs this may be inferred.
*/
public static final byte UNSPECIFIED_PACKET_LOSS_CONCEALMENT = 0;
public static final short VOIP_METRICS_REPORT_BLOCK_TYPE = 7;
/**
* The fraction of RTP data packets within burst periods since the
* beginning of reception that were either lost or discarded. The value
* is expressed as a fixed point number with the binary point at the
* left edge of the field. It is calculated by dividing the total number
* of packets lost or discarded (excluding duplicate packet discards)
* within burst periods by the total number of packets expected within
* the burst periods, multiplying the result of the division by 256,
* limiting the maximum value to 255 (to avoid overflow), and taking the
* integer part. The field MUST be populated and MUST be set to zero if
* no packets have been received.
*/
private short burstDensity = 0;
private int burstDuration = 0;
/**
* The fraction of RTP data packets from the source that have been
* discarded since the beginning of reception, due to late or early
* arrival, under-run or overflow at the receiving jitter buffer. The
* value is expressed as a fixed point number with the binary point at
* the left edge of the field. It is calculated by dividing the total
* number of packets discarded (excluding duplicate packet discards) by
* the total number of packets expected, multiplying the result of the
* division by 256, limiting the maximum value to 255 (to avoid
* overflow), and taking the integer part.
*/
private short discardRate = 0;
private int endSystemDelay = 0;
private byte extRFactor = 127;
/**
* The fraction of RTP data packets within inter-burst gaps since the
* beginning of reception that were either lost or discarded. The value
* is expressed as a fixed point number with the binary point at the
* left edge of the field. It is calculated by dividing the total number
* of packets lost or discarded (excluding duplicate packet discards)
* within gap periods by the total number of packets expected within the
* gap periods, multiplying the result of the division by 256, limiting
* the maximum value to 255 (to avoid overflow), and taking the integer
* part. The field MUST be populated and MUST be set to zero if no
* packets have been received.
*/
private short gapDensity = 0;
private int gapDuration = 0;
private short gMin = 16;
private int jitterBufferAbsoluteMaximumDelay;
/**
* Whether the jitter buffer is adaptive. The value is one of the
* constants {@link #ADAPTIVE_JITTER_BUFFER_ADAPTIVE},
* {@link #NON_ADAPTIVE_JITTER_BUFFER_ADAPTIVE},
* {@link #RESERVED_JITTER_BUFFER_ADAPTIVE}, and
* {@link #UNKNOWN_JITTER_BUFFER_ADAPTIVE}.
*/
private byte jitterBufferAdaptive = UNKNOWN_JITTER_BUFFER_ADAPTIVE;
private int jitterBufferMaximumDelay;
private int jitterBufferNominalDelay;
/**
* The implementation specific adjustment rate of a jitter buffer in
* adaptive mode. Defined in terms of the approximate time taken to
* fully adjust to a step change in peak to peak jitter from 30 ms to
* 100 ms such that: adjustment time = 2 * J * frame size (ms)
* where J = adjustment rate (0-15). The parameter is intended
* only to provide a guide to the degree of "aggressiveness"
* of an adaptive jitter buffer and may be estimated. A value of
* 0 indicates that the adjustment time is unknown for this
* implementation.
*/
private byte jitterBufferRate = 0;
/**
* The fraction of RTP data packets from the source lost since the
* beginning of reception, expressed as a fixed point number with the
* binary point at the left edge of the field. This value is calculated
* by dividing the total number of packets lost (after the effects of
* applying any error protection such as FEC) by the total number of
* packets expected, multiplying the result of the division by 256,
* limiting the maximum value to 255 (to avoid overflow), and taking the
* integer part. The numbers of duplicated packets and discarded packets
* do not enter into this calculation. Since receivers cannot be
* required to maintain unlimited buffers, a receiver MAY categorize
* late-arriving packets as lost. The degree of lateness that triggers a
* loss SHOULD be significantly greater than that which triggers a
* discard.
*/
private short lossRate = 0;
private byte mosCq = 127;
private byte mosLq = 127;
private byte noiseLevel = 127;
/**
* The type of packet loss concealment (PLC). The value is one of the
* constants {@link #STANDARD_PACKET_LOSS_CONCEALMENT},
* {@link #ENHANCED_PACKET_LOSS_CONCEALMENT},
* {@link #DISABLED_PACKET_LOSS_CONCEALMENT}, and
* {@link #UNSPECIFIED_PACKET_LOSS_CONCEALMENT}.
*/
private byte packetLossConcealment
= UNSPECIFIED_PACKET_LOSS_CONCEALMENT;
private byte residualEchoReturnLoss = 127;
private byte rFactor = 127;
private int roundTripDelay = 0;
private byte signalLevel = 127;
/**
* The synchronization source identifier (SSRC) of the RTP data packet
* source being reported upon by this report block.
*/
private int sourceSSRC;
/**
* Initializes a new VoIPMetricsReportBlock instance.
*/
public VoIPMetricsReportBlock()
{
super(VOIP_METRICS_REPORT_BLOCK_TYPE);
}
/**
* Initializes a new VoIPMetricsReportBlock instance by
* deserializing/reading a binary representation from a
* DataInputStream.
*
* @param blockLength the length of the extended report block to read,
* not including the header, in bytes.
* @param datainputstream the binary representation from which the new
* instance is to be initialized. The datainputstream is asumed
* to contain type-specific block contents without extended report block
* header i.e. no block type (BT), type-specific, and block length
* fields will be read from datainputstream.
* @throws IOException if an input/output error occurs while
* deserializing/reading the new instance from datainputstream
* or the binary representation does not parse into an
* VoIPMetricsReportBlock instance
*/
public VoIPMetricsReportBlock(
int blockLength,
DataInputStream datainputstream)
throws IOException
{
this();
// block length (RFC 3611, Section 4.7)
if (blockLength != 8 * 4)
{
throw new IOException(
"Invalid RTCP XR VoIP Metrics block length.");
}
// SSRC of source
setSourceSSRC(datainputstream.readInt());
// lost rate
setLossRate((short) datainputstream.readUnsignedByte());
// discard rate
setDiscardRate((short) datainputstream.readUnsignedByte());
// burst density
setBurstDensity((short) datainputstream.readUnsignedByte());
// gap density
setGapDensity((short) datainputstream.readUnsignedByte());
// burst duration
setBurstDuration(datainputstream.readUnsignedShort());
// gap duration
setGapDuration(datainputstream.readUnsignedShort());
// round trip delay
setRoundTripDelay(datainputstream.readUnsignedShort());
// end system delay
setEndSystemDelay(datainputstream.readUnsignedShort());
// signal level
setSignalLevel(datainputstream.readByte());
// noise level
setNoiseLevel(datainputstream.readByte());
// residual echo return loss (RERL)
setResidualEchoReturnLoss(datainputstream.readByte());
// Gmin
setGMin((short) datainputstream.readUnsignedByte());
// R factor
setRFactor(datainputstream.readByte());
// ext. R factor
setExtRFactor(datainputstream.readByte());
// MOS-LQ
setMosLq(datainputstream.readByte());
// MOS-CQ
setMosCq(datainputstream.readByte());
// receiver configuration byte (RX config)
int rxConfig = datainputstream.readUnsignedByte();
setPacketLossConcealment((byte) ((rxConfig & 0xC0) >>> 6));
setJitterBufferAdaptive((byte) ((rxConfig & 0x30) >>> 4));
setJitterBufferRate((byte) (rxConfig & 0x0F));
// reserved
datainputstream.readByte();
// jitter buffer nominal delay (JB nominal)
setJitterBufferNominalDelay(datainputstream.readUnsignedShort());
// jitter buffer maximum delay (JB maximum)
setJitterBufferMaximumDelay(datainputstream.readUnsignedShort());
// jitter buffer absolute maximum delay (JB abs max)
setJitterBufferAbsoluteMaximumDelay(
datainputstream.readUnsignedShort());
}
/**
* {@inheritDoc}
*/
@Override
protected void assemble(DataOutputStream dataoutputstream)
throws IOException
{
// BT=7
dataoutputstream.writeByte(VOIP_METRICS_REPORT_BLOCK_TYPE);
// reserved
dataoutputstream.writeByte(0);
// block length = 8
dataoutputstream.writeShort(8);
// SSRC of source
dataoutputstream.writeInt(getSourceSSRC());
// loss rate
dataoutputstream.writeByte(getLossRate());
// discard rate
dataoutputstream.writeByte(getDiscardRate());
// burst density
dataoutputstream.writeByte(getBurstDensity());
// gap density
dataoutputstream.writeByte(getGapDensity());
// burst duration
dataoutputstream.writeShort(getBurstDuration());
// gap duration
dataoutputstream.writeShort(getGapDuration());
// round trip delay
dataoutputstream.writeShort(getRoundTripDelay());
// end system delay
dataoutputstream.writeShort(getEndSystemDelay());
// signal level
dataoutputstream.writeByte(getSignalLevel());
// noise level
dataoutputstream.writeByte(getNoiseLevel());
// residual echo return loss (RERL)
dataoutputstream.writeByte(getResidualEchoReturnLoss());
// Gmin
dataoutputstream.writeByte(getGMin());
// R factor
dataoutputstream.writeByte(getRFactor());
// ext. R factor
dataoutputstream.writeByte(getExtRFactor());
// MOS-LQ
dataoutputstream.writeByte(getMosLq());
// MOS-CQ
dataoutputstream.writeByte(getMosCq());
// receiver configuration byte (RX config)
dataoutputstream.writeByte(
((getPacketLossConcealment() & 0x03) << 6)
| ((getJitterBufferAdaptive() & 0x03) << 4)
| (getJitterBufferRate() & 0x0F));
// reserved
dataoutputstream.writeByte(0);
// jitter buffer nominal delay (JB nominal)
dataoutputstream.writeShort(getJitterBufferNominalDelay());
// jitter buffer maximum delay (JB maximum)
dataoutputstream.writeShort(getJitterBufferMaximumDelay());
// jitter buffer absolute maximum delay (JB abs max)
dataoutputstream.writeShort(getJitterBufferAbsoluteMaximumDelay());
}
/**
* {@inheritDoc}
*
* As defined by RFC 3611, a VoIP Metrics Report Block has a length in
* bytes equal to 36, including the extended report
* block header.
*/
@Override
public int calcLength()
{
return (8 /* block length */ + 1) * 4;
}
/**
* Gets the fraction of RTP data packets within burst periods since the
* beginning of reception that were either lost or discarded.
*
* @return the fraction of RTP data packets within burst periods since
* the beginning of reception that were either lost or discarded
*/
public short getBurstDensity()
{
return burstDensity;
}
public int getBurstDuration()
{
return burstDuration;
}
/**
* Gets the fraction of RTP data packets from the source that have been
* discarded since the beginning of reception, due to late or early
* arrival, under-run or overflow at the receiving jitter buffer.
*
* @return the fraction of RTP data packets from the source that have
* been discarded since the beginning of reception, due to late or early
* arrival, under-run or overflow at the receiving jitter buffer
* @see #discardRate
*/
public short getDiscardRate()
{
return discardRate;
}
public int getEndSystemDelay()
{
return endSystemDelay;
}
public byte getExtRFactor()
{
return extRFactor;
}
/**
* Get the fraction of RTP data packets within inter-burst gaps since
* the beginning of reception that were either lost or discarded.
*
* @return the fraction of RTP data packets within inter-burst gaps
* since the beginning of reception that were either lost or discarded
*/
public short getGapDensity()
{
return gapDensity;
}
public int getGapDuration()
{
return gapDuration;
}
public short getGMin()
{
return gMin;
}
public int getJitterBufferAbsoluteMaximumDelay()
{
return jitterBufferAbsoluteMaximumDelay;
}
/**
* Gets whether the jitter buffer is adaptive.
*
* @return {@link #ADAPTIVE_JITTER_BUFFER_ADAPTIVE},
* {@link #NON_ADAPTIVE_JITTER_BUFFER_ADAPTIVE},
* {@link #RESERVED_JITTER_BUFFER_ADAPTIVE}, or
* {@link #UNKNOWN_JITTER_BUFFER_ADAPTIVE}
*/
public byte getJitterBufferAdaptive()
{
return jitterBufferAdaptive;
}
public int getJitterBufferMaximumDelay()
{
return jitterBufferMaximumDelay;
}
public int getJitterBufferNominalDelay()
{
return jitterBufferNominalDelay;
}
/**
* Gets the implementation specific adjustment rate of a jitter buffer
* in adaptive mode.
*
* @return the implementation specific adjustment rate of a jitter
* buffer in adaptive mode
*/
public byte getJitterBufferRate()
{
return jitterBufferRate;
}
/**
* Gets the fraction of RTP data packets from the source lost since the
* beginning of reception.
*
* @return the fraction of RTP data packets from the source lost since
* the beginning of reception
* @see #lossRate
*/
public short getLossRate()
{
return lossRate;
}
public byte getMosCq()
{
return mosCq;
}
public byte getMosLq()
{
return mosLq;
}
public byte getNoiseLevel()
{
return noiseLevel;
}
/**
* Gets the type of packet loss concealment (PLC).
*
* @return {@link #STANDARD_PACKET_LOSS_CONCEALMENT},
* {@link #ENHANCED_PACKET_LOSS_CONCEALMENT},
* {@link #DISABLED_PACKET_LOSS_CONCEALMENT}, or
* {@link #UNSPECIFIED_PACKET_LOSS_CONCEALMENT}
*/
public byte getPacketLossConcealment()
{
return packetLossConcealment;
}
public byte getResidualEchoReturnLoss()
{
return residualEchoReturnLoss;
}
public byte getRFactor()
{
return rFactor;
}
public int getRoundTripDelay()
{
return roundTripDelay;
}
public byte getSignalLevel()
{
return signalLevel;
}
/**
* Gets the synchronization source identifier (SSRC) of the RTP data
* packet source being reported upon by this report block.
*
* @return the synchronization source identifier (SSRC) of the RTP data
* packet source being reported upon by this report block
*/
public int getSourceSSRC()
{
return sourceSSRC;
}
/**
* Sets the fraction of RTP data packets within burst periods since the
* beginning of reception that were either lost or discarded.
*
* @param burstDensity the fraction of RTP data packets within burst
* periods since the beginning of reception that were either lost or
* discarded
*/
public void setBurstDensity(short burstDensity)
{
this.burstDensity = burstDensity;
}
public void setBurstDuration(int burstDuration)
{
this.burstDuration = burstDuration;
}
/**
* Sets the fraction of RTP data packets from the source that have been
* discarded since the beginning of reception, due to late or early
* arrival, under-run or overflow at the receiving jitter buffer.
*
* @param discardRate the fraction of RTP data packets from the source
* that have been discarded since the beginning of reception, due to
* late or early arrival, under-run or overflow at the receiving jitter
* buffer
* @see #discardRate
*/
public void setDiscardRate(short discardRate)
{
this.discardRate = discardRate;
}
public void setEndSystemDelay(int endSystemDelay)
{
this.endSystemDelay = endSystemDelay;
}
public void setExtRFactor(byte extRFactor)
{
this.extRFactor = extRFactor;
}
/**
* Sets the fraction of RTP data packets within inter-burst gaps since
* the beginning of reception that were either lost or discarded.
*
* @param gapDensity the fraction of RTP data packets within inter-burst
* gaps since the beginning of reception that were either lost or
* discarded
*/
public void setGapDensity(short gapDensity)
{
this.gapDensity = gapDensity;
}
public void setGapDuration(int gapDuration)
{
this.gapDuration = gapDuration;
}
public void setGMin(short gMin)
{
this.gMin = gMin;
}
public void setJitterBufferAbsoluteMaximumDelay(
int jitterBufferAbsoluteMaximumDelay)
{
this.jitterBufferAbsoluteMaximumDelay
= jitterBufferAbsoluteMaximumDelay;
}
/**
* Sets whether the jitter buffer is adaptive.
*
* @param jitterBufferAdaptive {@link #ADAPTIVE_JITTER_BUFFER_ADAPTIVE},
* {@link #NON_ADAPTIVE_JITTER_BUFFER_ADAPTIVE},
* {@link #RESERVED_JITTER_BUFFER_ADAPTIVE}, or
* {@link #UNKNOWN_JITTER_BUFFER_ADAPTIVE}
* @throws IllegalArgumentException if the specified
* jitterBufferAdapter is not one of the constants
* ADAPTIVE_JITTER_BUFFER_ADAPTIVE,
* NON_ADAPTIVE_JITTER_BUFFER_ADAPTIVE,
* RESERVED_JITTER_BUFFER_ADAPTIVE, and
* UNKNOWN_JITTER_BUFFER_ADAPTIVE
*/
public void setJitterBufferAdaptive(byte jitterBufferAdaptive)
{
switch (jitterBufferAdaptive)
{
case ADAPTIVE_JITTER_BUFFER_ADAPTIVE:
case NON_ADAPTIVE_JITTER_BUFFER_ADAPTIVE:
case RESERVED_JITTER_BUFFER_ADAPTIVE:
case UNKNOWN_JITTER_BUFFER_ADAPTIVE:
this.jitterBufferAdaptive = jitterBufferAdaptive;
break;
default:
throw new IllegalArgumentException("jitterBufferAdaptive");
}
}
public void setJitterBufferMaximumDelay(int jitterBufferMaximumDelay)
{
this.jitterBufferMaximumDelay = jitterBufferMaximumDelay;
}
public void setJitterBufferNominalDelay(int jitterBufferNominalDelay)
{
this.jitterBufferNominalDelay = jitterBufferNominalDelay;
}
/**
* Sets the implementation specific adjustment rate of a jitter buffer
* in adaptive mode.
*
* @param jitterBufferRate the implementation specific adjustment rate
* of a jitter buffer in adaptive mode
*/
public void setJitterBufferRate(byte jitterBufferRate)
{
this.jitterBufferRate = jitterBufferRate;
}
/**
* Sets the fraction of RTP data packets from the source lost since the
* beginning of reception.
*
* @param lossRate the fraction of RTP data packets from the source lost
* since the beginning of reception
* @see #lossRate
*/
public void setLossRate(short lossRate)
{
this.lossRate = lossRate;
}
public void setMosCq(byte mosCq)
{
this.mosCq = mosCq;
}
public void setMosLq(byte mosLq)
{
this.mosLq = mosLq;
}
public void setNoiseLevel(byte noiseLevel)
{
this.noiseLevel = noiseLevel;
}
/**
* Sets the type of packet loss concealment (PLC).
*
* @param packetLossConcealment
* {@link #STANDARD_PACKET_LOSS_CONCEALMENT},
* {@link #ENHANCED_PACKET_LOSS_CONCEALMENT},
* {@link #DISABLED_PACKET_LOSS_CONCEALMENT}, or
* {@link #UNSPECIFIED_PACKET_LOSS_CONCEALMENT}
* @throws IllegalArgumentException if the specified
* packetLossConcealment is not one of the constants
* STANDARD_PACKET_LOSS_CONCEALMENT,
* ENHANCED_PACKET_LOSS_CONCEALMENT,
* DISABLED_PACKET_LOSS_CONCEALMENT, and
* UNSPECIFIED_PACKET_LOSS_CONCEALMENT
*/
public void setPacketLossConcealment(byte packetLossConcealment)
{
switch (packetLossConcealment)
{
case STANDARD_PACKET_LOSS_CONCEALMENT:
case ENHANCED_PACKET_LOSS_CONCEALMENT:
case DISABLED_PACKET_LOSS_CONCEALMENT:
case UNSPECIFIED_PACKET_LOSS_CONCEALMENT:
this.packetLossConcealment = packetLossConcealment;
break;
default:
throw new IllegalArgumentException("packetLossConcealment");
}
}
public void setResidualEchoReturnLoss(byte residualEchoReturnLoss)
{
this.residualEchoReturnLoss = residualEchoReturnLoss;
}
public void setRFactor(byte rFactor)
{
this.rFactor = rFactor;
}
public void setRoundTripDelay(int roundTripDelay)
{
this.roundTripDelay = roundTripDelay;
}
public void setSignalLevel(byte signalLevel)
{
this.signalLevel = signalLevel;
}
/**
* Sets the synchronization source identifier (SSRC) of the RTP data
* packet source being reported upon by this report block.
*
* @param sourceSSRC the synchronization source identifier (SSRC) of the
* RTP data packet source being reported upon by this report block
*/
public void setSourceSSRC(int sourceSSRC)
{
this.sourceSSRC = sourceSSRC;
}
/**
* {@inheritDoc}
*/
@Override
public String toString()
{
StringBuilder s = new StringBuilder("VoIP Metrics");
s.append(", SSRC of source ")
.append(getSourceSSRC() & 0xFFFFFFFFL);
s.append(", loss rate ").append(getLossRate());
s.append(", discard rate ").append(getDiscardRate());
s.append(", burst density ").append(getBurstDensity());
s.append(", gap density ").append(getGapDensity());
s.append(", burst duration ").append(getBurstDuration());
s.append(", gap duration ").append(getGapDuration());
s.append(", round trip delay ").append(getRoundTripDelay());
// TODO Auto-generated method stub
return s.toString();
}
}
public static final String SDP_ATTRIBUTE = "rtcp-xr";
/**
* The packet type (PT) constant 207 which identifies RTCP XR
* packets.
*/
public static final int XR = 207;
/**
* The list of zero or more extended report blocks carried by this
* RTCPExtendedReport.
*/
private final List reportBlocks
= new LinkedList();
/**
* The synchronization source identifier (SSRC) of the originator of this XR
* packet.
*/
private int ssrc;
/**
* The System time in milliseconds at which this
* RTCPExtendedReport has been received or sent by the local
* endpoint.
*/
private long systemTimeStamp;
/**
* Initializes a new RTCPExtendedReport instance.
*/
public RTCPExtendedReport()
{
type = XR;
}
/**
* Initializes a new RTCPExtendedReport instance by
* deserializing/reading a binary representation from a byte array.
*
* @param buf the binary representation from which the new instance is to be
* initialized
* @param off the offset in buf at which the binary representation
* starts
* @param len the number of bytes in buf starting at
* off which comprise the binary representation
* @throws IOException if an input/output error occurs while
* deserializing/reading the new instance from buf or the binary
* representation does not parse into an RTCPExtendedReport
* instance
*/
public RTCPExtendedReport(byte[] buf, int off, int len)
throws IOException
{
this(new DataInputStream(new ByteArrayInputStream(buf, off, len)));
}
/**
* Initializes a new RTCPExtendedReport instance by
* deserializing/reading a binary representation from a
* DataInputStream.
*
* @param datainputstream the binary representation from which the new
* instance is to be initialized.
* @throws IOException if an input/output error occurs while
* deserializing/reading the new instance from datainputstream or
* the binary representation does not parse into an
* RTCPExtendedReport instance.
*/
public RTCPExtendedReport(DataInputStream datainputstream)
throws IOException
{
this();
// V=2, P, reserved
int b0 = datainputstream.readUnsignedByte();
// PT=XR=207
int pt = datainputstream.readUnsignedByte();
// length
int length = datainputstream.readUnsignedShort();
if (length < 1)
throw new IOException("Invalid RTCP length.");
parse(b0, pt, length, datainputstream);
}
/**
* Initializes a new RTCPExtendedReport instance by
* deserializing/reading a binary representation of part of the packet from
* a DataInputStream, and taking the values found in the
* first 4 bytes of the binary representation as arguments.
*
* @param b0 the first byte of the binary representation.
* @param pt the value of the {@code packet type} field.
* @param length the value of the {@code length} field.
* @param datainputstream the binary representation from which the new
* instance is to be initialized, excluding the first 4 bytes.
* @throws IOException if an input/output error occurs while
* deserializing/reading the new instance from datainputstream or
* the binary representation does not parse into an
* RTCPExtendedReport instance.
*/
public RTCPExtendedReport(int b0, int pt, int length,
DataInputStream datainputstream)
throws IOException
{
this();
parse(b0, pt, length, datainputstream);
}
/**
* Initializes a new RTCPExtendedReport instance by
* deserializing/reading a binary representation of part of the packet from
* a DataInputStream, and taking the values normally found in the
* first 4 bytes of the binary representation as arguments.
*
* @param b0 the first byte of the binary representation.
* @param pt the value of the {@code packet type} field.
* @param length the length in bytes of the RTCP packet, including all of
* it's headers and excluding any padding.
* @param datainputstream the binary representation from which the new
* instance is to be initialized, excluding the first 4 bytes.
* @throws IOException if an input/output error occurs while
* deserializing/reading the new instance from datainputstream or
* the binary representation does not parse into an
* RTCPExtendedReport instance.
*/
private void parse(int b0, int pt, int length, DataInputStream datainputstream)
throws IOException
{
// The first 4 bytes have already been read.
length -= 4;
// V=2
if ((b0 & 0xc0) != 128)
throw new IOException("Invalid RTCP version (V).");
if (pt != XR)
throw new IOException("Invalid RTCP packet type (PT).");
// SSRC
setSSRC(datainputstream.readInt());
length = length - 4;
// report blocks. A block is at least 4 bytes long.
while (length >= 4)
{
// block type (BT)
int bt = datainputstream.readUnsignedByte();
// type-specific
datainputstream.readByte();
// block length in bytes, including the block header
int blockLength = datainputstream.readUnsignedShort() + 1 << 2;
if (length < blockLength)
{
throw new IOException("Invalid extended block");
}
if (bt == VoIPMetricsReportBlock.VOIP_METRICS_REPORT_BLOCK_TYPE)
{
addReportBlock(
new VoIPMetricsReportBlock(
blockLength - 4,
datainputstream));
}
else
{
// The implementation reads and ignores any extended report
// blocks other than VoIP Metrics Report Block.
// Already read 4 bytes
datainputstream.skip(blockLength - 4);
}
length = length - blockLength;
}
// If we didn't read all bytes of the packet, the stream is probably in
// an inconsistent state.
if (length != 0)
{
throw new IOException("Invalid XR packet, unread bytes");
}
}
/**
* Adds an extended report block to this extended report.
*
* @param reportBlock the extended report block to add to this extended
* report
* @return true if the list of extended report blocks carried by
* this extended report changed because of the method invocation; otherwise,
* false
* @throws NullPointerException if reportBlock is null
*/
public boolean addReportBlock(ReportBlock reportBlock)
{
if (reportBlock == null)
throw new NullPointerException("reportBlock");
else
return reportBlocks.add(reportBlock);
}
/**
* {@inheritDoc}
*/
@Override
public void assemble(DataOutputStream dataoutputstream)
throws IOException
{
// V=2, P, reserved
dataoutputstream.writeByte(128);
// PT=XR=207
dataoutputstream.writeByte(XR);
// length
dataoutputstream.writeShort(calcLength() / 4 - 1);
// SSRC
dataoutputstream.writeInt(getSSRC());
// report blocks
for (ReportBlock reportBlock : getReportBlocks())
reportBlock.assemble(dataoutputstream);
}
/**
* {@inheritDoc}
*/
@Override
public int calcLength()
{
int length
= 1 /* V=2, P, reserved */
+ 1 /* PT */
+ 2 /* length */
+ 4 /* SSRC */;
// report blocks
for (ReportBlock reportBlock : getReportBlocks())
length += reportBlock.calcLength();
return length;
}
/**
* Gets the number of the extended report blocks carried by this
* RTCPExtendedReport.
*
* @return the number of the extended report blocks carried by this
* RTCPExtendedReport
*/
public int getReportBlockCount()
{
return reportBlocks.size();
}
/**
* Gets a list of the extended report blocks carried by this
* RTCPExtendedReport.
*
* @return a list of the extended repot blocks carried by this
* RTCPExtendedReport
*/
public List getReportBlocks()
{
return Collections.unmodifiableList(reportBlocks);
}
/**
* Gets the synchronization source identifier (SSRC) of the originator of
* this XR packet.
*
* @return the synchronization source identifier (SSRC) of the originator of
* this XR packet
*/
public int getSSRC()
{
return ssrc;
}
/**
* Gets the System time in milliseconds at which this
* RTCPExtendedReport has been received or sent by the local
* endpoint.
*
* @return the System time in milliseconds at which this
* RTCPExtendedReport has been received or sent by the local
* endpoint
*/
public long getSystemTimeStamp()
{
return systemTimeStamp;
}
/**
* Removes an extended report block from this extended report.
*
* @param reportBlock the extended report block to remove from this extended
* report
* @return true if the list of extended report blocks carried by
* this extended report changed because of the method invocation; otherwise,
* false
*/
public boolean removeReportBlock(ReportBlock reportBlock)
{
if (reportBlock == null)
return false;
else
return reportBlocks.remove(reportBlock);
}
/**
* Sets the synchronization source identifier (SSRC) of the originator of
* this XR packet.
*
* @param ssrc the synchronization source identifier (SSRC) of the
* originator of this XR packet
*/
public void setSSRC(int ssrc)
{
this.ssrc = ssrc;
}
/**
* Sets the System time in milliseconds at which this
* RTCPExtendedReport has been received or sent by the local
* endpoint.
*
* @param systemTimeStamp the System time in milliseconds at which
* this RTCPExtendedReport has been received or sent by the local
* endpoint
*/
public void setSystemTimeStamp(long systemTimeStamp)
{
this.systemTimeStamp = systemTimeStamp;
}
/**
* {@inheritDoc}
*/
@Override
public String toString()
{
StringBuilder s = new StringBuilder("RTCP XR");
// SSRC
s.append(", SSRC ").append(getSSRC() & 0xFFFFFFFFL);
List reportBlocks = getReportBlocks();
boolean b = false;
// report blocks
s.append(", report blocks [");
for (ReportBlock reportBlock : reportBlocks)
{
if (b)
s.append("; ");
else
b = true;
s.append(reportBlock);
}
s.append("]");
return s.toString();
}
}