/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* 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 net.java.sip.communicator.impl.protocol.jabber.extensions.colibri;
import java.util.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
import org.jitsi.util.*;
import org.jitsi.service.neomedia.*;
import org.jivesoftware.smack.packet.*;
import org.jivesoftware.smack.packet.IQ;
/**
* Implements the Jitsi Videobridge conference IQ within the
* COnferencing with LIghtweight BRIdging.
*
* @author Lyubomir Marinov
* @author Boris Grozev
* @author George Politis
*/
public class ColibriConferenceIQ
extends IQ
{
/**
* The XML element name of the Jitsi Videobridge conference IQ.
*/
public static final String ELEMENT_NAME = "conference";
/**
* The XML name of the id attribute of the Jitsi Videobridge
* conference IQ which represents the value of the id
* property of ColibriConferenceIQ.
*/
public static final String ID_ATTR_NAME = "id";
/**
* The XML name of the name attribute of the Jitsi Videobridge
* conference IQ which represents the value of the name
* property of ColibriConferenceIQ if available.
*/
public static final String NAME_ATTR_NAME = "name";
/**
* The XML COnferencing with LIghtweight BRIdging namespace of the Jitsi
* Videobridge conference IQ.
*/
public static final String NAMESPACE
= "http://jitsi.org/protocol/colibri";
/**
* The logger instance used by this class.
*/
private final static Logger logger
= Logger.getLogger(ColibriConferenceIQ.class);
/**
* An array of ints which represents the lack of any (RTP) SSRCs
* seen/received on a Channel. Explicitly defined to reduce
* unnecessary allocations.
*/
public static final int[] NO_SSRCS = new int[0];
/**
* The list of {@link ChannelBundle}s included into this conference
* IQ.
*/
private final List channelBundles
= new LinkedList();
/**
* The list of {@link Content}s included into this conference IQ.
*/
private final List contents = new LinkedList();
/**
* The list of Endpoints included into this conference IQ.
*/
private final List endpoints = new LinkedList();
/**
* The ID of the conference represented by this IQ.
*/
private String id;
/**
* Media recording.
*/
private Recording recording;
private RTCPTerminationStrategy rtcpTerminationStrategy;
/**
* Indicates if the information about graceful shutdown status is being
* carried by this IQ.
*/
private boolean gracefulShutdown;
/**
* World readable name for the conference.
*/
private String name;
/**
* Returns an error response for given IQ that is returned by
* the videobridge after it has entered graceful shutdown mode and new
* conferences can no longer be created.
*
* @param request the IQ for which error response will be created.
* @return an IQ of 'error' type and 'service-unavailable' condition plus
* the body of request IQ.
*/
public static IQ createGracefulShutdownErrorResponse(final IQ request)
{
final XMPPError error
= new XMPPError(XMPPError.Condition.service_unavailable);
error.addExtension(new GracefulShutdown());
final IQ result = IQ.createErrorResponse(request, error);
result.setType(Type.ERROR);
result.setPacketID(request.getPacketID());
result.setFrom(request.getTo());
result.setTo(request.getFrom());
result.setError(error);
return result;
}
/** Initializes a new ColibriConferenceIQ instance. */
public ColibriConferenceIQ()
{
}
/**
* Adds a specific {@link Content} instance to the list of Content
* instances included into this conference IQ.
* @param channelBundle the ChannelBundle to add.
*/
public boolean addChannelBundle(ChannelBundle channelBundle)
{
if (channelBundle == null)
throw new NullPointerException("channelBundle");
return
channelBundles.contains(channelBundles)
? false
: channelBundles.add(channelBundle);
}
/**
* Adds a specific {@link Content} instance to the list of Content
* instances included into this conference IQ.
*
* @param content the Content instance to be added to this list of
* Content instances included into this conference IQ
* @return true if the list of Content instances included
* into this conference IQ has been modified as a result of the
* method call; otherwise, false
* @throws NullPointerException if the specified content is
* null
*/
public boolean addContent(Content content)
{
if (content == null)
throw new NullPointerException("content");
return contents.contains(content) ? false : contents.add(content);
}
/**
* Initializes a new {@link Content} instance with a specific name and adds
* it to the list of Content instances included into this
* conference IQ.
*
* @param contentName the name which which the new Content instance
* is to be initialized
* @return true if the list of Content instances included
* into this conference IQ has been modified as a result of the
* method call; otherwise, false
*/
public boolean addContent(String contentName)
{
return addContent(new Content(contentName));
}
/**
* Add an Endpoint to this ColibriConferenceIQ.
* @param endpoint the Endpoint to add.
*/
public void addEndpoint(Endpoint endpoint)
{
endpoints.add(endpoint);
}
/**
* Returns a list of the ChannelBundles included into this
* conference IQ.
*
* @return an unmodifiable List of the ChannelBundles
* included into this conference IQ.
*/
public List getChannelBundles()
{
return Collections.unmodifiableList(channelBundles);
}
/**
* Finds {@link ChannelBundle} identified by given bundleId.
* @param bundleId ChannelBundle identifier.
* @return {@link ChannelBundle} identified by given bundleId or
* null if not found.
*/
public ChannelBundle getChannelBundle(String bundleId)
{
if (bundleId == null)
{
return null;
}
for (ChannelBundle bundle : channelBundles)
{
if (bundleId.equals(bundle.getId()))
{
return bundle;
}
}
return null;
}
/**
* Returns an XML String representation of this IQ.
*
* @return an XML String representation of this IQ
*/
@Override
public String getChildElementXML()
{
StringBuilder xml = new StringBuilder();
xml.append('<').append(ELEMENT_NAME);
xml.append(" xmlns='").append(NAMESPACE).append('\'');
String id = getID();
if (id != null)
xml.append(' ').append(ID_ATTR_NAME).append("='").append(id)
.append('\'');
if (name != null)
xml.append(' ').append(NAME_ATTR_NAME).append("='").append(name)
.append('\'');
List contents = getContents();
List channelBundles = getChannelBundles();
boolean hasChildren
= (recording != null)
|| (rtcpTerminationStrategy != null)
|| (gracefulShutdown)
|| (contents.size() > 0)
|| (channelBundles.size() > 0);
if (!hasChildren)
{
xml.append(" />");
}
else
{
xml.append('>');
for (Content content : contents)
content.toXML(xml);
for (ChannelBundle channelBundle : channelBundles)
channelBundle.toXML(xml);
if (recording != null)
recording.toXML(xml);
if (rtcpTerminationStrategy != null)
rtcpTerminationStrategy.toXML(xml);
if (gracefulShutdown)
xml.append(new GracefulShutdown().toXML());
xml.append("").append(ELEMENT_NAME).append('>');
}
return xml.toString();
}
/**
* Returns a Content from the list of Contents of this
* conference IQ which has a specific name. If no such
* Content exists, returns null.
*
* @param contentName the name of the Content to be returned
* @return a Content from the list of Contents of this
* conference IQ which has the specified contentName if
* such a Content exists; otherwise, null
*/
public Content getContent(String contentName)
{
for (Content content : getContents())
{
if (contentName.equals(content.getName()))
return content;
}
return null;
}
/**
* Returns a list of the Contents included into this
* conference IQ.
*
* @return an unmodifiable List of the Contents included
* into this conference IQ
*/
public List getContents()
{
return Collections.unmodifiableList(contents);
}
/**
* Returns the list of Endpoints included in this
* ColibriConferenceIQ.
* @return the list of Endpoints included in this
* ColibriConferenceIQ.
*/
public List getEndpoints()
{
return Collections.unmodifiableList(endpoints);
}
/**
* Gets the ID of the conference represented by this IQ.
*
* @return the ID of the conference represented by this IQ
*/
public String getID()
{
return id;
}
/**
* Returns a Content from the list of Contents of this
* conference IQ which has a specific name. If no such
* Content exists at the time of the invocation of the method,
* initializes a new Content instance with the specified
* contentName and includes it into this conference IQ.
*
* @param contentName the name of the Content to be returned
* @return a Content from the list of Contents of this
* conference IQ which has the specified contentName
*/
public Content getOrCreateContent(String contentName)
{
Content content = getContent(contentName);
if (content == null)
{
content = new Content(contentName);
addContent(content);
}
return content;
}
/**
* Gets the value of the recording field.
* @return the value of the recording field.
*/
public Recording getRecording()
{
return recording;
}
public RTCPTerminationStrategy getRTCPTerminationStrategy()
{
return rtcpTerminationStrategy;
}
/**
* Removes a specific {@link Content} instance from the list of
* Content instances included into this conference IQ.
*
* @param content the Content instance to be removed from the list
* of Content instances included into this conference IQ
* @return true if the list of Content instances included
* into this conference IQ has been modified as a result of the
* method call; otherwise, false
*/
public boolean removeContent(Content content)
{
return contents.remove(content);
}
/**
* Sets the ID of the conference represented by this IQ.
*
* @param id the ID of the conference represented by this IQ
*/
public void setID(String id)
{
this.id = id;
}
/**
* Sets the recording field.
* @param recording the value to set.
*/
public void setRecording(Recording recording)
{
this.recording = recording;
}
public void setRTCPTerminationStrategy(
RTCPTerminationStrategy rtcpTerminationStrategy)
{
this.rtcpTerminationStrategy = rtcpTerminationStrategy;
}
/**
* Sets whether this IQ should contain the information about graceful
* shutdown in progress status.
*
* @param isGracefulShutdown true if graceful shutdown status
* should be indicated in this IQ.
*/
public void setGracefulShutdown(boolean isGracefulShutdown)
{
this.gracefulShutdown = isGracefulShutdown;
}
/**
* Returns true if graceful shutdown status info is indicated in
* this ColibriConferenceIQ instance.
*/
public boolean isGracefulShutdown()
{
return gracefulShutdown;
}
/**
* The world readable name of the conference.
* @return name of the conference.
*/
public String getName()
{
return name;
}
/**
* Sets name.
* @param name the name to set.
*/
public void setName(String name)
{
this.name = name;
}
/**
* Represents a channel included into a content of a Jitsi
* Videobridge conference IQ.
*/
public static class Channel
extends ChannelCommon
{
/**
* The name of the XML attribute of a channel which represents
* its direction.
*/
public static final String DIRECTION_ATTR_NAME = "direction";
/**
* The XML element name of a channel of a content of a
* Jitsi Videobridge conference IQ.
*/
public static final String ELEMENT_NAME = "channel";
/**
* The XML name of the host attribute of a channel of
* a content of a conference IQ which represents the
* value of the host property of
* ColibriConferenceIQ.Channel.
*
* @deprecated The attribute is supported for the purposes of
* compatibility with legacy versions of Jitsi and Jitsi Videobridge.
*/
@Deprecated
public static final String HOST_ATTR_NAME = "host";
/**
* The XML name of the last-n attribute of a video
* channel which specifies the maximum number of video RTP
* streams to be sent from Jitsi Videobridge to the endpoint associated
* with the video channel. The value of the last-n
* attribute is a positive number.
*/
public static final String LAST_N_ATTR_NAME = "last-n";
/**
* The XML name of the adaptive-last-n attribute of a video
* channel.
*/
public static final String ADAPTIVE_LAST_N_ATTR_NAME
= "adaptive-last-n";
/**
* The XML name of the adaptive-simulcast attribute of a video
* channel.
*/
public static final String ADAPTIVE_SIMULCAST_ATTR_NAME
= "adaptive-simulcast";
/**
* The XML name of the simulcast-mode attribute of a video
* channel.
*/
public static final String SIMULCAST_MODE_ATTR_NAME
= "simulcast-mode";
/**
* The XML name of the receive-simulcast-layer attribute of a
* video Channel which specifies the target quality of the
* simulcast substreams to be sent from Jitsi Videobridge to the
* endpoint associated with the video Channel. The value of the
* receive-simulcast-layer attribute is an unsigned integer.
* Typically used for debugging purposes.
*/
public static final String RECEIVING_SIMULCAST_LAYER
= "receive-simulcast-layer";
/**
* The XML name of the rtcpport attribute of a channel
* of a content of a conference IQ which represents
* the value of the rtcpPort property of
* ColibriConferenceIQ.Channel.
*
* @deprecated The attribute is supported for the purposes of
* compatibility with legacy versions of Jitsi and Jitsi Videobridge.
*/
@Deprecated
public static final String RTCP_PORT_ATTR_NAME = "rtcpport";
public static final String RTP_LEVEL_RELAY_TYPE_ATTR_NAME
= "rtp-level-relay-type";
/**
* The XML name of the rtpport attribute of a channel
* of a content of a conference IQ which represents
* the value of the rtpPort property of
* ColibriConferenceIQ.Channel.
*
* @deprecated The attribute is supported for the purposes of
* compatibility with legacy versions of Jitsi and Jitsi Videobridge.
*/
@Deprecated
public static final String RTP_PORT_ATTR_NAME = "rtpport";
/**
* The name of the XML element which is a child of the <channel>
* element and which identifies/specifies an (RTP) SSRC which has been
* seen/received on the respective Channel.
*/
public static final String SSRC_ELEMENT_NAME = "ssrc";
/**
* The direction of the channel represented by this instance.
*/
private MediaDirection direction;
/**
* The host of the channel represented by this instance.
*
* @deprecated The field is supported for the purposes of compatibility
* with legacy versions of Jitsi and Jitsi Videobridge.
*/
@Deprecated
private String host;
/**
* The maximum number of video RTP streams to be sent from Jitsi
* Videobridge to the endpoint associated with this video
* Channel.
*/
private Integer lastN;
/**
* The 'adaptive-last-n' flag.
*/
private Boolean adaptiveLastN;
/**
* The 'adaptive-simulcast' flag.
*/
private Boolean adaptiveSimulcast;
/**
* The 'simulcast-mode' flag.
*/
private SimulcastMode simulcastMode;
/**
* The payload-type elements defined by XEP-0167: Jingle RTP
* Sessions associated with this channel.
*/
private final List payloadTypes
= new ArrayList();
/**
* The rtp-hdrext elements defined by XEP-0294: Jingle RTP
* Header Extensions Negotiation associated with this channel.
*/
private final Map rtpHeaderExtensions
= new HashMap();
/**
* The target quality of the simulcast substreams to be sent from Jitsi
* Videobridge to the endpoint associated with this video
* Channel.
*/
private Integer receivingSimulcastLayer;
/**
* The RTCP port of the channel represented by this instance.
*
* @deprecated The field is supported for the purposes of compatibility
* with legacy versions of Jitsi and Jitsi Videobridge.
*/
@Deprecated
private int rtcpPort;
/**
* The type of RTP-level relay (in the terms specified by RFC 3550
* "RTP: A Transport Protocol for Real-Time Applications" in
* section 2.3 "Mixers and Translators") used for this
* Channel.
*/
private RTPLevelRelayType rtpLevelRelayType;
/**
* The RTP port of the channel represented by this instance.
*
* @deprecated The field is supported for the purposes of compatibility
* with legacy versions of Jitsi and Jitsi Videobridge.
*/
@Deprecated
private int rtpPort;
/**
* The SourceGroupPacketExtensions of this channel.
*/
private List sourceGroups;
/**
* The SourcePacketExtensions of this channel.
*/
private final List sources
= new LinkedList();
/**
* The list of (RTP) SSRCs which have been seen/received on this
* Channel by now. These may exclude SSRCs which are no longer
* active. Set by the Jitsi Videobridge server, not its clients.
*/
private int[] ssrcs = NO_SSRCS;
/** Initializes a new Channel instance. */
public Channel()
{
super(Channel.ELEMENT_NAME);
}
/**
* Adds a payload-type element defined by XEP-0167: Jingle RTP
* Sessions to this channel.
*
* @param payloadType the payload-type element to be added to
* this channel
* @return true if the list of payload-type elements
* associated with this channel has been modified as part of
* the method call; otherwise, false
* @throws NullPointerException if the specified payloadType is
* null
*/
public boolean addPayloadType(PayloadTypePacketExtension payloadType)
{
if (payloadType == null)
throw new NullPointerException("payloadType");
// Make sure that the COLIBRI namespace is used.
payloadType.setNamespace(null);
for (ParameterPacketExtension p : payloadType.getParameters())
p.setNamespace(null);
return
payloadTypes.contains(payloadType)
? false
: payloadTypes.add(payloadType);
}
/**
* Adds an rtp-hdrext element defined by XEP-0294: Jingle RTP
* Header Extensions Negotiation to this Channel.
*
* @param ext the payload-type element to be added to
* this channel
* @return true if the list of rtp-hdrext elements
* associated with this channel has been modified as part of
* the method call; otherwise, false
* @throws NullPointerException if the specified ext is
* null
*/
public void addRtpHeaderExtension(RTPHdrExtPacketExtension ext)
{
if (ext == null)
throw new NullPointerException("payloadType");
// Create a new instance, because we are going to modify the NS
RTPHdrExtPacketExtension newExt = new RTPHdrExtPacketExtension(ext);
// Make sure that the parent namespace (COLIBRI) is used.
newExt.setNamespace(null);
int id = -1;
try
{
id = Integer.valueOf(newExt.getID());
}
catch (NumberFormatException nfe)
{}
// Only accept valid extension IDs (4-bits, 0xF reserved)
if (id < 0 || id > 14)
{
logger.warn("Failed to add an RTP header extension element "
+ "with an invalid ID: " + newExt.getID());
return;
}
rtpHeaderExtensions.put(id, newExt);
}
/**
* Adds a SourcePacketExtension to the list of sources of this
* channel.
*
* @param source the SourcePacketExtension to add to the list
* of sources of this channel
* @return true if the list of sources of this channel changed
* as a result of the execution of the method; otherwise, false
*/
public synchronized boolean addSource(SourcePacketExtension source)
{
if (source == null)
throw new NullPointerException("source");
return sources.contains(source) ? false : sources.add(source);
}
/**
* Adds a SourceGroupPacketExtension to the list of source
* groups of this channel.
*
* @param sourceGroup the SourcePacketExtension to add to the
* list of sources of this channel
*
* @return true if the list of sources of this channel changed
* as a result of the execution of the method; otherwise, false
*/
public synchronized boolean addSourceGroup(
SourceGroupPacketExtension sourceGroup)
{
if (sourceGroup == null)
throw new NullPointerException("sourceGroup");
if (sourceGroups == null)
sourceGroups = new LinkedList();
return
sourceGroups.contains(sourceGroup)
? false
: sourceGroups.add(sourceGroup);
}
/**
* Adds a specific (RTP) SSRC to the list of SSRCs seen/received on this
* Channel. Invoked by the Jitsi Videobridge server, not its
* clients.
*
* @param ssrc the (RTP) SSRC to be added to the list of SSRCs
* seen/received on this Channel
* @return true if the list of SSRCs seen/received on this
* Channel has been modified as part of the method call;
* otherwise, false
*/
public synchronized boolean addSSRC(int ssrc)
{
// contains
for (int i = 0; i < ssrcs.length; i++)
{
if (ssrcs[i] == ssrc)
return false;
}
// add
int[] newSSRCs = new int[ssrcs.length + 1];
System.arraycopy(ssrcs, 0, newSSRCs, 0, ssrcs.length);
newSSRCs[ssrcs.length] = ssrc;
ssrcs = newSSRCs;
return true;
}
/**
* Gets the direction of this Channel.
*
* @return the direction of this Channel.
*/
public MediaDirection getDirection()
{
return (direction == null) ? MediaDirection.SENDRECV : direction;
}
/**
* Gets the IP address (as a String value) of the host on which
* the channel represented by this instance has been allocated.
*
* @return a String value which represents the IP address of
* the host on which the channel represented by this instance
* has been allocated
*
* @deprecated The method is supported for the purposes of compatibility
* with legacy versions of Jitsi and Jitsi Videobridge.
*/
@Deprecated
public String getHost()
{
return host;
}
/**
* Gets the maximum number of video RTP streams to be sent from Jitsi
* Videobridge to the endpoint associated with this video
* Channel.
*
* @return the maximum number of video RTP streams to be sent from Jitsi
* Videobridge to the endpoint associated with this video
* Channel
*/
public Integer getLastN()
{
return lastN;
}
/**
* Gets the value of the 'adaptive-last-n' flag.
* @return the value of the 'adaptive-last-n' flag.
*/
public Boolean getAdaptiveLastN()
{
return adaptiveLastN;
}
/**
* Gets the value of the 'adaptive-simulcast' flag.
* @return the value of the 'adaptive-simulcast' flag.
*/
public Boolean getAdaptiveSimulcast()
{
return adaptiveSimulcast;
}
/**
* Gets the value of the 'simulcast-mode' flag.
* @return the value of the 'simulcast-mode' flag.
*/
public SimulcastMode getSimulcastMode()
{
return simulcastMode;
}
/**
* Gets a list of payload-type elements defined by XEP-0167:
* Jingle RTP Sessions added to this channel.
*
* @return an unmodifiable List of payload-type
* elements defined by XEP-0167: Jingle RTP Sessions added to this
* channel
*/
public List getPayloadTypes()
{
return Collections.unmodifiableList(payloadTypes);
}
/**
* Gets a list of rtp-hdrext elements defined by XEP-0294:
* Jingle RTP Header Extensions Negotiation added to this
* channel.
*
* @return an unmodifiable List of rtp-hdrext
* elements defined by XEP-0294: Jingle RTP Header Extensions
* Negotiation added to this channel
*/
public Collection getRtpHeaderExtensions()
{
return Collections
.unmodifiableCollection(rtpHeaderExtensions.values());
}
/**
* Gets the target quality of the simulcast substreams to be sent from
* Jitsi Videobridge to the endpoint associated with this video
* Channel.
*
* @return the target quality of the simulcast substreams to be sent
* from Jitsi Videobridge to the endpoint associated with this video
* Channel.
*/
public Integer getReceivingSimulcastLayer()
{
return receivingSimulcastLayer;
}
/**
* Gets the port which has been allocated to this channel for
* the purposes of transmitting RTCP packets.
*
* @return the port which has been allocated to this channel
* for the purposes of transmitting RTCP packets
*
* @deprecated The method is supported for the purposes of compatibility
* with legacy versions of Jitsi and Jitsi Videobridge.
*/
@Deprecated
public int getRTCPPort()
{
return rtcpPort;
}
/**
* Gets the type of RTP-level relay (in the terms specified by RFC 3550
* "RTP: A Transport Protocol for Real-Time Applications" in
* section 2.3 "Mixers and Translators") used for this
* Channel.
*
* @return the type of RTP-level relay used for this Channel
*/
public RTPLevelRelayType getRTPLevelRelayType()
{
return rtpLevelRelayType;
}
/**
* Gets the port which has been allocated to this channel for
* the purposes of transmitting RTP packets.
*
* @return the port which has been allocated to this channel
* for the purposes of transmitting RTP packets
*
* @deprecated The method is supported for the purposes of compatibility
* with legacy versions of Jitsi and Jitsi Videobridge.
*/
@Deprecated
public int getRTPPort()
{
return rtpPort;
}
/**
* Gets the list of SourceGroupPacketExtensionss which
* represent the source groups of this channel.
*
* @return a List of SourceGroupPacketExtensions which
* represent the source groups of this channel
*/
public synchronized List getSourceGroups()
{
return
(sourceGroups == null)
? null
: new ArrayList(sourceGroups);
}
/**
* Gets the list of SourcePacketExtensionss which represent the
* sources of this channel.
*
* @return a List of SourcePacketExtensions which
* represent the sources of this channel
*/
public synchronized List getSources()
{
return new ArrayList(sources);
}
/**
* Gets (a copy of) the list of (RTP) SSRCs seen/received on this
* Channel.
*
* @return an array of ints which represents (a copy of) the
* list of (RTP) SSRCs seen/received on this Channel
*/
public synchronized int[] getSSRCs()
{
return (ssrcs.length == 0) ? NO_SSRCS : ssrcs.clone();
}
@Override
protected boolean hasContent()
{
List payloadTypes = getPayloadTypes();
if (!payloadTypes.isEmpty())
return true;
List sourceGroups = getSourceGroups();
if (sourceGroups != null && !getSourceGroups().isEmpty())
return true;
List sources = getSources();
if (!sources.isEmpty())
return true;
int[] ssrcs = getSSRCs();
return (ssrcs.length != 0);
}
@Override
protected void printAttributes(StringBuilder xml)
{
// direction
MediaDirection direction = getDirection();
if ((direction != null) && (direction != MediaDirection.SENDRECV))
{
xml.append(' ').append(DIRECTION_ATTR_NAME).append("='")
.append(direction.toString()).append('\'');
}
// host
String host = getHost();
if (host != null)
{
xml.append(' ').append(HOST_ATTR_NAME).append("='").append(host)
.append('\'');
}
// lastN
Integer lastN = getLastN();
if (lastN != null)
{
xml.append(' ').append(LAST_N_ATTR_NAME).append("='")
.append(lastN).append('\'');
}
// simulcastMode
SimulcastMode simulcastMode = getSimulcastMode();
if (simulcastMode != null)
{
xml.append(' ').append(SIMULCAST_MODE_ATTR_NAME).append("='")
.append(simulcastMode).append('\'');
}
// rtcpPort
int rtcpPort = getRTCPPort();
if (rtcpPort > 0)
{
xml.append(' ').append(RTCP_PORT_ATTR_NAME).append("='")
.append(rtcpPort).append('\'');
}
// rtpLevelRelayType
RTPLevelRelayType rtpLevelRelayType = getRTPLevelRelayType();
if (rtpLevelRelayType != null)
{
xml.append(' ').append(RTP_LEVEL_RELAY_TYPE_ATTR_NAME)
.append("='").append(rtpLevelRelayType).append('\'');
}
// rtpPort
int rtpPort = getRTPPort();
if (rtpPort > 0)
{
xml.append(' ').append(RTP_PORT_ATTR_NAME).append("='")
.append(rtpPort).append('\'');
}
}
@Override
protected void printContent(StringBuilder xml)
{
List payloadTypes = getPayloadTypes();
Collection rtpHdrExtPacketExtensions
= getRtpHeaderExtensions();
List sources = getSources();
List sourceGroups = getSourceGroups();
int[] ssrcs = getSSRCs();
for (PayloadTypePacketExtension payloadType : payloadTypes)
xml.append(payloadType.toXML());
for (RTPHdrExtPacketExtension ext : rtpHdrExtPacketExtensions)
xml.append(ext.toXML());
for (SourcePacketExtension source : sources)
xml.append(source.toXML());
if (sourceGroups != null && sourceGroups.size() != 0)
for (SourceGroupPacketExtension sourceGroup : sourceGroups)
xml.append(sourceGroup.toXML());
for (int i = 0; i < ssrcs.length; i++)
{
xml.append('<').append(SSRC_ELEMENT_NAME).append('>')
.append(Long.toString(ssrcs[i] & 0xFFFFFFFFL))
.append("").append(SSRC_ELEMENT_NAME)
.append('>');
}
}
/**
* Removes a payload-type element defined by XEP-0167: Jingle
* RTP Sessions from this channel.
*
* @param payloadType the payload-type element to be removed
* from this channel
* @return true if the list of payload-type elements
* associated with this channel has been modified as part of
* the method call; otherwise, false
*/
public boolean removePayloadType(PayloadTypePacketExtension payloadType)
{
return payloadTypes.remove(payloadType);
}
/**
* Removes a rtp-hdrext element defined by XEP-0294: Jingle
* RTP Header Extensions Negotiation from this channel.
*
* @param ext the rtp-hdrext element to be removed
* from this channel
* @return true if the list of rtp-hdrext elements
* associated with this channel has been modified as part of
* the method call; otherwise, false
*/
public void removeRtpHeaderExtension(RTPHdrExtPacketExtension ext)
{
int id = -1;
try
{
id = Integer.valueOf(ext.getID());
}
catch (NumberFormatException nfe)
{
logger.warn("Invalid ID: " + ext.getID());
return;
}
rtpHeaderExtensions.remove(id);
}
/**
* Removes a SourcePacketExtension from the list of sources of
* this channel.
*
* @param source the SourcePacketExtension to remove from the
* list of sources of this channel
* @return true if the list of sources of this channel changed
* as a result of the execution of the method; otherwise, false
*/
public synchronized boolean removeSource(SourcePacketExtension source)
{
return sources.remove(source);
}
/**
* Removes a specific (RTP) SSRC from the list of SSRCs seen/received on
* this Channel. Invoked by the Jitsi Videobridge server, not
* its clients.
*
* @param ssrc the (RTP) SSRC to be removed from the list of SSRCs
* seen/received on this Channel
* @return true if the list of SSRCs seen/received on this
* Channel has been modified as part of the method call;
* otherwise, false
*/
public synchronized boolean removeSSRC(int ssrc)
{
if (ssrcs.length == 1)
{
if (ssrcs[0] == ssrc)
{
ssrcs = NO_SSRCS;
return true;
}
else
return false;
}
else
{
for (int i = 0; i < ssrcs.length; i++)
{
if (ssrcs[i] == ssrc)
{
int[] newSSRCs = new int[ssrcs.length - 1];
if (i != 0)
System.arraycopy(ssrcs, 0, newSSRCs, 0, i);
if (i != newSSRCs.length)
{
System.arraycopy(
ssrcs, i + 1,
newSSRCs, i,
newSSRCs.length - i);
}
ssrcs = newSSRCs;
return true;
}
}
return false;
}
}
/**
* Sets the direction of this Channel
*
* @param direction the MediaDirection to set the
* direction of this Channel to.
*/
public void setDirection(MediaDirection direction)
{
this.direction = direction;
}
/**
* Sets the IP address (as a String value) of the host on which
* the channel represented by this instance has been allocated.
*
* @param host a String value which represents the IP address
* of the host on which the channel represented by this
* instance has been allocated
*
* @deprecated The method is supported for the purposes of compatibility
* with legacy versions of Jitsi and Jitsi Videobridge.
*/
@Deprecated
public void setHost(String host)
{
this.host = host;
}
/**
* Sets the maximum number of video RTP streams to be sent from Jitsi
* Videobridge to the endpoint associated with this video
* Channel.
*
* @param lastN the maximum number of video RTP streams to be sent from
* Jitsi Videobridge to the endpoint associated with this video
* Channel
*/
public void setLastN(Integer lastN)
{
this.lastN = lastN;
}
/**
* Sets the value of the 'adaptive-last-n' flag.
* @param adaptiveLastN the value to set.
*/
public void setAdaptiveLastN(Boolean adaptiveLastN)
{
this.adaptiveLastN = adaptiveLastN;
}
/**
* Sets the value of the 'adaptive-simulcast' flag.
* @param adaptiveSimulcast the value to set.
*/
public void setAdaptiveSimulcast(Boolean adaptiveSimulcast)
{
this.adaptiveSimulcast = adaptiveSimulcast;
}
/**
* Sets the value of the 'simulcast-mode' flag.
* @param simulcastMode the value to set.
*/
public void setSimulcastMode(SimulcastMode simulcastMode)
{
this.simulcastMode = simulcastMode;
}
/**
* Sets the target quality of the simulcast substreams to be sent from
* Jitsi Videobridge to the endpoint associated with this video
* Channel.
*
* @param simulcastLayer the target quality of the simulcast substreams
* to be sent from Jitsi Videobridge to the endpoint associated with
* this video Channel.
*/
public void setReceivingSimulcastLayer(Integer simulcastLayer)
{
this.receivingSimulcastLayer = simulcastLayer;
}
/**
* Sets the port which has been allocated to this channel for
* the purposes of transmitting RTCP packets.
*
* @param rtcpPort the port which has been allocated to this
* channel for the purposes of transmitting RTCP packets
*
* @deprecated The method is supported for the purposes of compatibility
* with legacy versions of Jitsi and Jitsi Videobridge.
*/
@Deprecated
public void setRTCPPort(int rtcpPort)
{
this.rtcpPort = rtcpPort;
}
/**
* Sets the type of RTP-level relay (in the terms specified by RFC 3550
* "RTP: A Transport Protocol for Real-Time Applications" in
* section 2.3 "Mixers and Translators") used for this
* Channel.
*
* @param rtpLevelRelayType the type of RTP-level relay used for
* this Channel
*/
public void setRTPLevelRelayType(RTPLevelRelayType rtpLevelRelayType)
{
this.rtpLevelRelayType = rtpLevelRelayType;
}
/**
* Sets the type of RTP-level relay (in the terms specified by RFC 3550
* "RTP: A Transport Protocol for Real-Time Applications" in
* section 2.3 "Mixers and Translators") used for this
* Channel.
*
* @param s the type of RTP-level relay used for this Channel
*/
public void setRTPLevelRelayType(String s)
{
setRTPLevelRelayType(RTPLevelRelayType.parseRTPLevelRelayType(s));
}
/**
* Sets the port which has been allocated to this channel for
* the purposes of transmitting RTP packets.
*
* @param rtpPort the port which has been allocated to this
* channel for the purposes of transmitting RTP packets
*
* @deprecated The method is supported for the purposes of compatibility
* with legacy versions of Jitsi and Jitsi Videobridge.
*/
@Deprecated
public void setRTPPort(int rtpPort)
{
this.rtpPort = rtpPort;
}
/**
* Sets the list of (RTP) SSRCs seen/received on this Channel.
*
* @param ssrcs the list of (RTP) SSRCs to be set as seen/received on
* this Channel
*/
public void setSSRCs(int[] ssrcs)
{
/*
* TODO Make sure that the SSRCs set on this instance do not contain
* duplicates.
*/
this.ssrcs
= ((ssrcs == null) || (ssrcs.length == 0))
? NO_SSRCS
: ssrcs.clone();
}
}
/**
* Represents a "channel-bundle" element.
*/
public static class ChannelBundle
{
/**
* The name of the "channel-bundle" element.
*/
public static final String ELEMENT_NAME = "channel-bundle";
/**
* The name of the "id" attribute.
*/
public static final String ID_ATTR_NAME = "id";
/**
* The ID of this ChannelBundle.
*/
private String id;
/**
* The transport element of this ChannelBundle.
*/
private IceUdpTransportPacketExtension transport;
/**
* Initializes a new ChannelBundle with the given ID.
* @param id the ID.
*/
public ChannelBundle(String id)
{
this.id = id;
}
/**
* Returns the ID of this ChannelBundle.
* @return the ID of this ChannelBundle.
*/
public String getId()
{
return id;
}
/**
* Returns the transport element of this ChannelBundle.
* @return the transport element of this ChannelBundle.
*/
public IceUdpTransportPacketExtension getTransport()
{
return transport;
}
/**
* Sets the ID of this ChannelBundle.
* @param id the ID to set.
*/
public void setId(String id)
{
this.id = id;
}
/**
* Sets the transport element of this ChannelBundle.
* @param transport the transport to set.
*/
public void setTransport(IceUdpTransportPacketExtension transport)
{
this.transport = transport;
}
/**
* Appends an XML representation of this ChannelBundle to
* xml.
* @param xml the StringBuilder to append to.
*/
public void toXML(StringBuilder xml)
{
xml.append('<').append(ELEMENT_NAME).append(' ')
.append(ID_ATTR_NAME).append("='").append(id).append('\'');
if (transport != null)
{
xml.append('>');
xml.append(transport.toXML());
xml.append("").append(ELEMENT_NAME).append('>');
}
else
{
xml.append(" />");
}
}
}
/**
* Class contains common code for both Channel and
* SctpConnection IQ classes.
*
* @author Pawel Domas
*/
public static abstract class ChannelCommon
{
/**
* The name of the "channel-bundle-id" attribute.
*/
public static final String CHANNEL_BUNDLE_ID_ATTR_NAME
= "channel-bundle-id";
/**
* The XML name of the endpoint attribute which specifies the
* optional identifier of the endpoint of the conference participant
* associated with a channel. The value of the
* endpoint attribute is an opaque String from the
* point of view of Jitsi Videobridge.
*/
public static final String ENDPOINT_ATTR_NAME = "endpoint";
/**
* The XML name of the expire attribute of a channel
* of a content of a conference IQ which represents
* the value of the expire property of
* ColibriConferenceIQ.Channel.
*/
public static final String EXPIRE_ATTR_NAME = "expire";
/**
* The value of the expire property of
* ColibriConferenceIQ.Channel which indicates that no actual
* value has been specified for the property in question.
*/
public static final int EXPIRE_NOT_SPECIFIED = -1;
/**
* The XML name of the id attribute of a channel of a
* content of a conference IQ which represents the
* value of the id property of
* ColibriConferenceIQ.Channel.
*/
public static final String ID_ATTR_NAME = "id";
/**
* The XML name of the initiator attribute of a
* channel of a content of a conference IQ
* which represents the value of the initiator property of
* ColibriConferenceIQ.Channel.
*/
public static final String INITIATOR_ATTR_NAME = "initiator";
/**
* The channel-bundle-id attribute of this CommonChannel.
*/
private String channelBundleId = null;
/**
* XML element name.
*/
private String elementName;
/**
* The identifier of the endpoint of the conference participant
* associated with this Channel.
*/
private String endpoint;
/**
* The number of seconds of inactivity after which the channel
* represented by this instance expires.
*/
private int expire = EXPIRE_NOT_SPECIFIED;
/**
* The ID of the channel represented by this instance.
*/
private String id;
/**
* The indicator which determines whether the conference focus is the
* initiator/offerer (as opposed to the responder/answerer) of the media
* negotiation associated with this instance.
*/
private Boolean initiator;
private IceUdpTransportPacketExtension transport;
/**
* Initializes this class with given XML elementName.
* @param elementName XML element name to be used for producing XML
* representation of derived IQ class.
*/
protected ChannelCommon(String elementName)
{
this.elementName = elementName;
}
/**
* Get the channel-bundle-id attribute of this CommonChannel.
* @return the channel-bundle-id attribute of this
* CommonChannel.
*/
public String getChannelBundleId()
{
return channelBundleId;
}
/**
* Gets the identifier of the endpoint of the conference participant
* associated with this Channel.
*
* @return the identifier of the endpoint of the conference participant
* associated with this Channel
*/
public String getEndpoint()
{
return endpoint;
}
/**
* Gets the number of seconds of inactivity after which the
* channel represented by this instance expires.
*
* @return the number of seconds of inactivity after which the
* channel represented by this instance expires
*/
public int getExpire()
{
return expire;
}
/**
* Gets the ID of the channel represented by this instance.
*
* @return the ID of the channel represented by this instance
*/
public String getID()
{
return id;
}
public IceUdpTransportPacketExtension getTransport()
{
return transport;
}
/**
* Indicates whether there are some contents that should be printed as
* child elements of this IQ. If true is returned
* {@link #printContent(StringBuilder)} method will be called when
* XML representation of this IQ is being constructed.
* @return true if there are content to be printed as child
* elements of this IQ or false otherwise.
*/
protected abstract boolean hasContent();
/**
* Gets the indicator which determines whether the conference focus is
* the initiator/offerer (as opposed to the responder/answerer) of the
* media negotiation associated with this instance.
*
* @return {@link Boolean#TRUE} if the conference focus is the
* initiator/offerer of the media negotiation associated with this
* instance, {@link Boolean#FALSE} if the conference focus is the
* responder/answerer or null if the initiator state
* is unspecified
*/
public Boolean isInitiator()
{
return initiator;
}
/**
* Derived class implements this method in order to print additional
* attributes to main XML element.
* @param xml StringBuilder to which the XML
* String representation of this Channel
* is to be appended
*/
protected abstract void printAttributes(StringBuilder xml);
/**
* Implement in order to print content child elements of this IQ using
* given StringBuilder. Called during construction of XML
* representation if {@link #hasContent()} returns true.
*
* @param xml the StringBuilder to which the XML
* String representation of this Channel
* is to be appended.
*/
protected abstract void printContent(StringBuilder xml);
/**
* Sets the channel-bundle-id attribute of this CommonChannel.
* @param channelBundleId the value to set.
*/
public void setChannelBundleId(String channelBundleId)
{
this.channelBundleId = channelBundleId;
}
/**
* Sets the identifier of the endpoint of the conference participant
* associated with this Channel.
*
* @param endpoint the identifier of the endpoint of the conference
* participant associated with this Channel
*/
public void setEndpoint(String endpoint)
{
this.endpoint = endpoint;
}
/**
* Sets the number of seconds of inactivity after which the
* channel represented by this instance expires.
*
* @param expire the number of seconds of activity after which the
* channel represented by this instance expires
* @throws IllegalArgumentException if the value of the specified
* expire is other than {@link #EXPIRE_NOT_SPECIFIED} and
* negative
*/
public void setExpire(int expire)
{
if ((expire != EXPIRE_NOT_SPECIFIED) && (expire < 0))
throw new IllegalArgumentException("expire");
this.expire = expire;
}
/*
* Sets the ID of the channel represented by this instance.
*
* @param id the ID of the channel represented by this instance
*/
public void setID(String id)
{
this.id = id;
}
/**
* Sets the indicator which determines whether the conference focus is
* the initiator/offerer (as opposed to the responder/answerer) of the
* media negotiation associated with this instance.
*
* @param initiator {@link Boolean#TRUE} if the conference focus is the
* initiator/offerer of the media negotiation associated with this
* instance, {@link Boolean#FALSE} if the conference focus is the
* responder/answerer or null if the initiator state
* is to be unspecified
*/
public void setInitiator(Boolean initiator)
{
this.initiator = initiator;
}
public void setTransport(IceUdpTransportPacketExtension transport)
{
this.transport = transport;
}
/**
* Appends the XML String representation of this
* Channel to a specific StringBuilder.
*
* @param xml the StringBuilder to which the XML
* String representation of this Channel is to be
* appended
*/
public void toXML(StringBuilder xml)
{
xml.append('<').append(elementName);
// endpoint
String endpoint = getEndpoint();
if (endpoint != null)
{
xml.append(' ').append(ENDPOINT_ATTR_NAME).append("='")
.append(endpoint).append('\'');
}
// expire
int expire = getExpire();
if (expire >= 0)
{
xml.append(' ').append(EXPIRE_ATTR_NAME).append("='")
.append(expire).append('\'');
}
// id
String id = getID();
if (id != null)
{
xml.append(' ').append(ID_ATTR_NAME).append("='")
.append(id).append('\'');
}
// initiator
Boolean initiator = isInitiator();
if (initiator != null)
{
xml.append(' ').append(INITIATOR_ATTR_NAME).append("='")
.append(initiator).append('\'');
}
String channelBundleId = getChannelBundleId();
if (channelBundleId != null)
{
xml.append(' ').append(CHANNEL_BUNDLE_ID_ATTR_NAME)
.append("='").append(channelBundleId).append('\'');
}
// Print derived class attributes
printAttributes(xml);
IceUdpTransportPacketExtension transport = getTransport();
boolean hasTransport = (transport != null);
if (hasTransport || hasContent())
{
xml.append('>');
if(hasContent())
printContent(xml);
if (hasTransport)
xml.append(transport.toXML());
xml.append("").append(elementName).append('>');
}
else
{
xml.append(" />");
}
}
}
/**
* Represents a content included into a Jitsi Videobridge
* conference IQ.
*/
public static class Content
{
/**
* The XML element name of a content of a Jitsi Videobridge
* conference IQ.
*/
public static final String ELEMENT_NAME = "content";
/**
* The XML name of the name attribute of a content of
* a conference IQ which represents the name property
* of ColibriConferenceIQ.Content.
*/
public static final String NAME_ATTR_NAME = "name";
/**
* The list of {@link Channel}s included into this content of a
* conference IQ.
*/
private final List channels = new LinkedList();
/**
* The name of the content represented by this instance.
*/
private String name;
/**
* The list of {@link SctpConnection}s included into this
* content of a conference IQ.
*/
private final List sctpConnections
= new LinkedList();
/**
* Initializes a new Content instance without a name and
* channels.
*/
public Content()
{
}
/**
* Initializes a new Content instance with a specific name and
* without channels.
*
* @param name the name to initialize the new instance with
*/
public Content(String name)
{
setName(name);
}
/**
* Adds a specific Channel to the list of Channels
* included into this Content.
*
* @param channel the Channel to be included into this
* Content
* @return true if the list of Channels included into
* this Content was modified as a result of the execution of
* the method; otherwise, false
* @throws NullPointerException if the specified channel is
* null
*/
public boolean addChannel(Channel channel)
{
if (channel == null)
throw new NullPointerException("channel");
return channels.contains(channel) ? false : channels.add(channel);
}
/**
* Adds a specific SctpConnection to the list of
* SctpConnections included into this Content.
*
* @param conn the SctpConnection to be included into this
* Content
* @return true if the list of SctpConnections
* included into this Content was modified as a result of
* the execution of the method; otherwise, false
* @throws NullPointerException if the specified conn is
* null
*/
public boolean addSctpConnection(SctpConnection conn)
{
if(conn == null)
throw new NullPointerException("Sctp connection");
return !sctpConnections.contains(conn) && sctpConnections.add(conn);
}
/**
* Gets the Channel at a specific index/position within the
* list of Channels included in this Content.
*
* @param channelIndex the index/position within the list of
* Channels included in this Content of the
* Channel to be returned
* @return the Channel at the specified channelIndex
* within the list of Channels included in this
* Content
*/
public Channel getChannel(int channelIndex)
{
return getChannels().get(channelIndex);
}
/**
* Gets a Channel which is included into this Content
* and which has a specific ID.
*
* @param channelID the ID of the Channel included into this
* Content to be returned
* @return the Channel which is included into this
* Content and which has the specified channelID if
* such a Channel exists; otherwise, null
*/
public Channel getChannel(String channelID)
{
for (Channel channel : getChannels())
{
if (channelID.equals(channel.getID()))
return channel;
}
return null;
}
/**
* Finds an SCTP connection identified by given connectionID.
* @param connectionID the ID of the SCTP connection to find.
* @return SctpConnection instance identified by given ID
* or null if no such connection is contained in
* this IQ.
*/
public SctpConnection getSctpConnection(String connectionID)
{
for (SctpConnection conn : getSctpConnections())
if (connectionID.equals(conn.getID()))
return conn;
return null;
}
/**
* Gets the number of Channels included into/associated with
* this Content.
*
* @return the number of Channels included into/associated with
* this Content
*/
public int getChannelCount()
{
return getChannels().size();
}
/**
* Gets a list of the Channel included into/associated with
* this Content.
*
* @return an unmodifiable List of the Channels
* included into/associated with this Content
*/
public List getChannels()
{
return Collections.unmodifiableList(channels);
}
/**
* Gets the name of the content represented by this instance.
*
* @return the name of the content represented by this instance
*/
public String getName()
{
return name;
}
/**
* Gets a list of the SctpConnections included into/associated
* with this Content.
*
* @return an unmodifiable List of the SctpConnections
* included into/associated with this Content
*/
public List getSctpConnections()
{
return Collections.unmodifiableList(sctpConnections);
}
/**
* Removes a specific Channel from the list of
* Channels included into this Content.
*
* @param channel the Channel to be excluded from this
* Content
* @return true if the list of Channels included into
* this Content was modified as a result of the execution of
* the method; otherwise, false
*/
public boolean removeChannel(Channel channel)
{
return channels.remove(channel);
}
/**
* Sets the name of the content represented by this instance.
*
* @param name the name of the content represented by this
* instance
* @throws NullPointerException if the specified name is
* null
*/
public void setName(String name)
{
if (name == null)
throw new NullPointerException("name");
this.name = name;
}
/**
* Appends the XML String representation of this
* Content to a specific StringBuilder.
*
* @param xml the StringBuilder to which the XML
* String representation of this Content is to be
* appended
*/
public void toXML(StringBuilder xml)
{
xml.append('<').append(ELEMENT_NAME);
xml.append(' ').append(NAME_ATTR_NAME).append("='")
.append(getName()).append('\'');
List channels = getChannels();
List connections = getSctpConnections();
if (channels.size() == 0 && connections.size() == 0)
{
xml.append(" />");
}
else
{
xml.append('>');
for (Channel channel : channels)
channel.toXML(xml);
for(SctpConnection conn : connections)
conn.toXML(xml);
xml.append("").append(ELEMENT_NAME).append('>');
}
}
/**
* Removes given SCTP connection from this IQ.
* @param connection the SCTP connection instance to be removed.
* @return true if given connection was contained in
* this IQ and has been removed successfully.
*/
public boolean removeSctpConnection(SctpConnection connection)
{
return sctpConnections.remove(connection);
}
}
/**
* Represents an 'endpoint' element.
*/
public static class Endpoint
{
/**
* The name of the 'displayname' attribute.
*/
public static final String DISPLAYNAME_ATTR_NAME = "displayname";
/**
* The name of the 'endpoint' element.
*/
public static final String ELEMENT_NAME = "endpoint";
/**
* The name of the 'id' attribute.
*/
public static final String ID_ATTR_NAME = "id";
/**
* The 'display name' of this Endpoint.
*/
private String displayName;
/**
* The 'id' of this Endpoint.
*/
private String id;
/**
* Initializes a new Endpoint with the given ID and display
* name.
* @param id the ID.
* @param displayName the display name.
*/
public Endpoint(String id, String displayName)
{
this.id = id;
this.displayName = displayName;
}
/**
* Returns the display name of this Endpoint.
* @return the display name of this Endpoint.
*/
public String getDisplayName()
{
return displayName;
}
/**
* Returns the ID of this Endpoint.
* @return the ID of this Endpoint.
*/
public String getId()
{
return id;
}
/**
* Sets the display name of this Endpoint.
* @param displayName the display name to set.
*/
public void setDisplayName(String displayName)
{
this.displayName = displayName;
}
/**
* Sets the ID of this Endpoint.
* @param id the ID to set.
*/
public void setId(String id)
{
this.id = id;
}
}
/**
* Represents a recording element.
*/
public static class Recording
{
/**
* The XML name of the recording element.
*/
public static final String ELEMENT_NAME = "recording";
/**
* The XML name of the path attribute.
*/
public static final String DIRECTORY_ATTR_NAME = "directory";
/**
* The XML name of the state attribute.
*/
public static final String STATE_ATTR_NAME = "state";
/**
* The XML name of the token attribute.
*/
public static final String TOKEN_ATTR_NAME = "token";
/**
* The target directory.
*/
private String directory;
/**
* State of the recording..
*/
private State state;
/**
* Access token.
*/
private String token;
/**
* Construct new recording element.
* @param state the state as string
*/
public Recording(String state)
{
this.state = State.parseString(state);
}
/**
* Construct new recording element.
* @param state
*/
public Recording(State state)
{
this.state = state;
}
/**
* Construct new recording element.
* @param state the state as string
* @param token the token to authenticate
*/
public Recording(String state, String token)
{
this(State.parseString(state), token);
}
/**
* Construct new recording element.
* @param state the state
* @param token the token to authenticate
*/
public Recording(State state, String token)
{
this(state);
this.token = token;
}
public String getDirectory()
{
return directory;
}
public State getState()
{
return state;
}
public String getToken()
{
return token;
}
public void setToken(String token)
{
this.token = token;
}
public void setDirectory(String directory)
{
this.directory = directory;
}
public void toXML(StringBuilder xml)
{
xml.append('<').append(ELEMENT_NAME);
xml.append(' ').append(STATE_ATTR_NAME).append("='")
.append(state).append('\'');
if (token != null)
{
xml.append(' ').append(TOKEN_ATTR_NAME).append("='")
.append(token).append('\'');
}
if (directory != null)
{
xml.append(' ').append(DIRECTORY_ATTR_NAME).append("='")
.append(directory).append('\'');
}
xml.append("/>");
}
/**
* The recording state.
*/
public enum State
{
/**
* Recording is started.
*/
ON("on"),
/**
* Recording is stopped.
*/
OFF("off"),
/**
* Recording is pending. Record has been requested but no conference
* has been established and it will be started once this is done.
*/
PENDING("pending");
/**
* The name.
*/
private String name;
/**
* Constructs new state.
* @param name
*/
private State(String name)
{
this.name = name;
}
/**
* Returns state name.
* @return returns state name.
*/
public String toString()
{
return name;
}
/**
* Parses state.
* @param s state name.
* @return the state found.
*/
public static State parseString(String s)
{
if (ON.toString().equalsIgnoreCase(s))
return ON;
else if (PENDING.toString().equalsIgnoreCase(s))
return PENDING;
return OFF;
}
}
}
/**
* Packet extension indicating graceful shutdown in progress status.
*/
public static class GracefulShutdown
extends AbstractPacketExtension
{
public static final String ELEMENT_NAME = "graceful-shutdown";
public static final String NAMESPACE = ColibriConferenceIQ.NAMESPACE;
public GracefulShutdown()
{
super(ColibriConferenceIQ.NAMESPACE, ELEMENT_NAME);
}
}
public static class RTCPTerminationStrategy
{
public static final String ELEMENT_NAME = "rtcp-termination-strategy";
public static final String NAME_ATTR_NAME = "name";
private String name;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public void toXML(StringBuilder xml)
{
xml.append('<').append(ELEMENT_NAME);
xml.append(' ').append(NAME_ATTR_NAME).append("='")
.append(name).append('\'');
xml.append("/>");
}
}
/**
* Represents a SCTP connection included into a content
* of a Jitsi Videobridge conference IQ.
*
* @author Pawel Domas
*/
public static class SctpConnection
extends ChannelCommon
{
/**
* The XML element name of a content of a Jitsi Videobridge
* conference IQ.
*/
public static final String ELEMENT_NAME = "sctpconnection";
/**
* The XML name of the port attribute of a
* SctpConnection of a conference IQ which represents
* the SCTP port property of
* ColibriConferenceIQ.SctpConnection.
*/
public static final String PORT_ATTR_NAME = "port";
/**
* SCTP port attribute. 5000 by default.
*/
private int port = 5000;
/**
* Initializes a new SctpConnection instance without an
* endpoint name and with default port value set.
*/
public SctpConnection()
{
super(SctpConnection.ELEMENT_NAME);
}
/**
* Gets the SCTP port of the SctpConnection described by this
* instance.
*
* @return the SCTP port of the SctpConnection represented by
* this instance.
*/
public int getPort()
{
return port;
}
/**
* {@inheritDoc}
*
* No content other than transport for SctpConnection.
*/
@Override
protected boolean hasContent()
{
return false;
}
/**
* {@inheritDoc}
*/
@Override
protected void printAttributes(StringBuilder xml)
{
xml.append(' ').append(PORT_ATTR_NAME).append("='")
.append(getPort()).append('\'');
}
@Override
protected void printContent(StringBuilder xml)
{
// No other content than the transport shared from ChannelCommon
}
/**
* Sets the SCTP port of the SctpConnection represented by this
* instance.
*
* @param port the SCTP port of the SctpConnection
* represented by this instance
*/
public void setPort(int port)
{
this.port = port;
}
}
}