/*
* 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;
import java.net.*;
import java.util.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.colibri.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
import net.java.sip.communicator.impl.protocol.jabber.jinglesdp.*;
import net.java.sip.communicator.service.protocol.*;
import org.jitsi.service.neomedia.*;
import org.jivesoftware.smack.packet.*;
/**
* A {@link TransportManagerJabberImpl} implementation that would only gather a
* single candidate pair (i.e. RTP and RTCP).
*
* @author Emil Ivov
* @author Lyubomir Marinov
* @author Hristo Terezov
*/
public class RawUdpTransportManager
extends TransportManagerJabberImpl
{
/**
* The list of ContentPacketExtensions which represents the local
* counterpart of the negotiation between the local and the remote peers.
*/
private List local;
/**
* The collection of ContentPacketExtensions which represents the
* remote counterpart of the negotiation between the local and the remote
* peers.
*/
private final List> remotes
= new LinkedList>();
/**
* Creates a new instance of this transport manager, binding it to the
* specified peer.
*
* @param callPeer the {@link CallPeer} whose traffic we will be taking
* care of.
*/
public RawUdpTransportManager(CallPeerJabberImpl callPeer)
{
super(callPeer);
}
/**
* {@inheritDoc}
*/
protected PacketExtension createTransport(String media)
throws OperationFailedException
{
MediaType mediaType = MediaType.parseString(media);
return createTransport(mediaType, getStreamConnector(mediaType));
}
/**
* Creates a raw UDP transport element according to a specific
* StreamConnector.
*
* @param mediaType the MediaType of the MediaStream which
* uses the specified connector or channel
* @param connector the StreamConnector to be described within the
* transport element
* @return a {@link RawUdpTransportPacketExtension} containing the RTP and
* RTCP candidates of the specified connector
*/
private RawUdpTransportPacketExtension createTransport(
MediaType mediaType,
StreamConnector connector)
{
RawUdpTransportPacketExtension ourTransport
= new RawUdpTransportPacketExtension();
int generation = getCurrentGeneration();
// create and add candidates that correspond to the stream connector
// RTP
CandidatePacketExtension rtpCand = new CandidatePacketExtension();
rtpCand.setComponent(CandidatePacketExtension.RTP_COMPONENT_ID);
rtpCand.setGeneration(generation);
rtpCand.setID(getNextID());
rtpCand.setType(CandidateType.host);
DatagramSocket dataSocket = connector.getDataSocket();
rtpCand.setIP(dataSocket.getLocalAddress().getHostAddress());
rtpCand.setPort(dataSocket.getLocalPort());
ourTransport.addCandidate(rtpCand);
// RTCP
CandidatePacketExtension rtcpCand = new CandidatePacketExtension();
rtcpCand.setComponent(CandidatePacketExtension.RTCP_COMPONENT_ID);
rtcpCand.setGeneration(generation);
rtcpCand.setID(getNextID());
rtcpCand.setType(CandidateType.host);
DatagramSocket controlSocket = connector.getControlSocket();
rtcpCand.setIP(controlSocket.getLocalAddress().getHostAddress());
rtcpCand.setPort(controlSocket.getLocalPort());
ourTransport.addCandidate(rtcpCand);
return ourTransport;
}
/**
* {@inheritDoc}
*/
protected PacketExtension createTransportPacketExtension()
{
return new RawUdpTransportPacketExtension();
}
/**
* Implements {@link TransportManagerJabberImpl#getStreamTarget(MediaType)}.
* Gets the MediaStreamTarget to be used as the target of
* the MediaStream with a specific MediaType.
*
* @param mediaType the MediaType of the MediaStream which
* is to have its target set to the returned
* MediaStreamTarget
* @return the MediaStreamTarget to be used as the target
* of the MediaStream with the specified MediaType
* @see TransportManagerJabberImpl#getStreamTarget(MediaType)
*/
@Override
public MediaStreamTarget getStreamTarget(MediaType mediaType)
{
ColibriConferenceIQ.Channel channel
= getColibriChannel(mediaType, true /* local */);
MediaStreamTarget streamTarget = null;
if (channel == null)
{
String media = mediaType.toString();
for (Iterable remote : remotes)
{
for (ContentPacketExtension content : remote)
{
RtpDescriptionPacketExtension rtpDescription
= content.getFirstChildOfType(
RtpDescriptionPacketExtension.class);
if (media.equals(rtpDescription.getMedia()))
{
streamTarget
= JingleUtils.extractDefaultTarget(content);
break;
}
}
}
}
else
{
IceUdpTransportPacketExtension transport = channel.getTransport();
if (transport != null)
streamTarget = JingleUtils.extractDefaultTarget(transport);
if (streamTarget == null)
{
/*
* For the purposes of compatibility with legacy Jitsi
* Videobridge, support the channel attributes host, rtpPort and
* rtcpPort.
*/
@SuppressWarnings("deprecation")
String host = channel.getHost();
if (host != null)
{
@SuppressWarnings("deprecation")
int rtpPort = channel.getRTPPort();
@SuppressWarnings("deprecation")
int rtcpPort = channel.getRTCPPort();
streamTarget
= new MediaStreamTarget(
new InetSocketAddress(host, rtpPort),
new InetSocketAddress(host, rtcpPort));
}
}
}
return streamTarget;
}
/**
* Implements {@link TransportManagerJabberImpl#getXmlNamespace()}. Gets the
* XML namespace of the Jingle transport implemented by this
* TransportManagerJabberImpl.
*
* @return the XML namespace of the Jingle transport implemented by this
* TransportManagerJabberImpl
* @see TransportManagerJabberImpl#getXmlNamespace()
*/
@Override
public String getXmlNamespace()
{
return ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_RAW_UDP_0;
}
/**
* Removes a content with a specific name from the transport-related part of
* the session represented by this TransportManagerJabberImpl which
* may have been reported through previous calls to the
* startCandidateHarvest and
* startConnectivityEstablishment methods.
*
* @param name the name of the content to be removed from the
* transport-related part of the session represented by this
* TransportManagerJabberImpl
* @see TransportManagerJabberImpl#removeContent(String)
*/
@Override
public void removeContent(String name)
{
if (local != null)
removeContent(local, name);
removeRemoteContent(name);
}
/**
* Removes a content with a specific name from the remote counterpart of the
* negotiation between the local and the remote peers.
*
* @param name the name of the content to be removed from the remote
* counterpart of the negotiation between the local and the remote peers
*/
private void removeRemoteContent(String name)
{
for (Iterator> remoteIter
= remotes.iterator();
remoteIter.hasNext();)
{
Iterable remote = remoteIter.next();
/*
* Once the remote content is removed, make sure that we are not
* retaining sets which do not have any contents.
*/
if ((removeContent(remote, name) != null)
&& !remote.iterator().hasNext())
{
remoteIter.remove();
}
}
}
/**
* {@inheritDoc}
*/
protected PacketExtension startCandidateHarvest(
ContentPacketExtension theirContent,
ContentPacketExtension ourContent,
TransportInfoSender transportInfoSender,
String media)
throws OperationFailedException
{
return createTransportForStartCandidateHarvest(media);
}
/**
* Starts transport candidate harvest. This method should complete rapidly
* and, in case of lengthy procedures like STUN/TURN/UPnP candidate harvests
* are necessary, they should be executed in a separate thread. Candidate
* harvest would then need to be concluded in the
* {@link #wrapupCandidateHarvest()} method which would be called once we
* absolutely need the candidates.
*
* @param theirOffer a media description offer that we've received from the
* remote party and that we should use in case we need to know what
* transports our peer is using.
* @param ourAnswer the content descriptions that we should be adding our
* transport lists to (although not necessarily in this very instance).
* @param transportInfoSender the TransportInfoSender to be used by
* this TransportManagerJabberImpl to send transport-info
* JingleIQs from the local peer to the remote peer if this
* TransportManagerJabberImpl wishes to utilize
* transport-info. Local candidate addresses sent by this
* TransportManagerJabberImpl in transport-info are
* expected to not be included in the result of
* {@link #wrapupCandidateHarvest()}.
*
* @throws OperationFailedException if we fail to allocate a port number.
* @see TransportManagerJabberImpl#startCandidateHarvest(List, List,
* TransportInfoSender)
*/
@Override
public void startCandidateHarvest(
List theirOffer,
List ourAnswer,
TransportInfoSender transportInfoSender)
throws OperationFailedException
{
this.local = ourAnswer;
super.startCandidateHarvest(theirOffer, ourAnswer, transportInfoSender);
}
/**
* Overrides the super implementation in order to remember the remote
* counterpart of the negotiation between the local and the remote peer for
* subsequent calls to {@link #getStreamTarget(MediaType)}.
*
* @param remote the collection of ContentPacketExtensions which
* represents the remote counterpart of the negotiation between the local
* and the remote peer
* @return true because RawUdpTransportManager does not
* perform connectivity checks
* @see TransportManagerJabberImpl#startConnectivityEstablishment(Iterable)
*/
@Override
public boolean startConnectivityEstablishment(
Iterable remote)
{
if ((remote != null) && !remotes.contains(remote))
{
/*
* The state of the session in Jingle is maintained by each peer and
* is modified by content-add and content-remove. The remotes field
* of this RawUdpTransportManager represents the state of the
* session with respect to the remote peer. When the remote peer
* tells us about a specific set of contents, make sure that it is
* the only record we will have with respect to the specified set of
* contents.
*/
for (ContentPacketExtension content : remote)
removeRemoteContent(content.getName());
remotes.add(remote);
}
return super.startConnectivityEstablishment(remote);
}
/**
* Simply returns the list of local candidates that we gathered during the
* harvest. This is a raw UDP transport manager so there's no real wrapping
* up to do.
*
* @return the list of local candidates that we gathered during the harvest
* @see TransportManagerJabberImpl#wrapupCandidateHarvest()
*/
@Override
public List wrapupCandidateHarvest()
{
return local;
}
/**
* Returns the extended type of the candidate selected if this transport
* manager is using ICE.
*
* @param streamName The stream name (AUDIO, VIDEO);
*
* @return The extended type of the candidate selected if this transport
* manager is using ICE. Otherwise, returns null.
*/
@Override
public String getICECandidateExtendedType(String streamName)
{
return null;
}
/**
* Returns the current state of ICE processing.
*
* @return the current state of ICE processing.
*/
@Override
public String getICEState()
{
return null;
}
/**
* Returns the ICE local host address.
*
* @param streamName The stream name (AUDIO, VIDEO);
*
* @return the ICE local host address if this transport
* manager is using ICE. Otherwise, returns null.
*/
@Override
public InetSocketAddress getICELocalHostAddress(String streamName)
{
return null;
}
/**
* Returns the ICE remote host address.
*
* @param streamName The stream name (AUDIO, VIDEO);
*
* @return the ICE remote host address if this transport
* manager is using ICE. Otherwise, returns null.
*/
@Override
public InetSocketAddress getICERemoteHostAddress(String streamName)
{
return null;
}
/**
* Returns the ICE local reflexive address (server or peer reflexive).
*
* @param streamName The stream name (AUDIO, VIDEO);
*
* @return the ICE local reflexive address. May be null if this transport
* manager is not using ICE or if there is no reflexive address for the
* local candidate used.
*/
@Override
public InetSocketAddress getICELocalReflexiveAddress(String streamName)
{
return null;
}
/**
* Returns the ICE remote reflexive address (server or peer reflexive).
*
* @param streamName The stream name (AUDIO, VIDEO);
*
* @return the ICE remote reflexive address. May be null if this transport
* manager is not using ICE or if there is no reflexive address for the
* remote candidate used.
*/
@Override
public InetSocketAddress getICERemoteReflexiveAddress(String streamName)
{
return null;
}
/**
* Returns the ICE local relayed address (server or peer relayed).
*
* @param streamName The stream name (AUDIO, VIDEO);
*
* @return the ICE local relayed address. May be null if this transport
* manager is not using ICE or if there is no relayed address for the
* local candidate used.
*/
@Override
public InetSocketAddress getICELocalRelayedAddress(String streamName)
{
return null;
}
/**
* Returns the ICE remote relayed address (server or peer relayed).
*
* @param streamName The stream name (AUDIO, VIDEO);
*
* @return the ICE remote relayed address. May be null if this transport
* manager is not using ICE or if there is no relayed address for the
* remote candidate used.
*/
@Override
public InetSocketAddress getICERemoteRelayedAddress(String streamName)
{
return null;
}
/**
* Returns the total harvesting time (in ms) for all harvesters.
*
* @return The total harvesting time (in ms) for all the harvesters. 0 if
* the ICE agent is null, or if the agent has nevers harvested.
*/
@Override
public long getTotalHarvestingTime()
{
return 0;
}
/**
* Returns the harvesting time (in ms) for the harvester given in parameter.
*
* @param harvesterName The class name if the harvester.
*
* @return The harvesting time (in ms) for the harvester given in parameter.
* 0 if this harvester does not exists, if the ICE agent is null, or if the
* agent has never harvested with this harvester.
*/
@Override
public long getHarvestingTime(String harvesterName)
{
return 0;
}
/**
* Returns the number of harvesting for this agent.
*
* @return The number of harvesting for this agent.
*/
@Override
public int getNbHarvesting()
{
return 0;
}
/**
* Returns the number of harvesting time for the harvester given in
* parameter.
*
* @param harvesterName The class name if the harvester.
*
* @return The number of harvesting time for the harvester given in
* parameter.
*/
@Override
public int getNbHarvesting(String harvesterName)
{
return 0;
}
}