All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.lastbamboo.common.ice.sdp.IceCandidateSdpEncoder Maven / Gradle / Ivy

The newest version!
package org.lastbamboo.common.ice.sdp;

import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.Vector;

import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.ObjectUtils.Null;
import org.lastbamboo.common.ice.candidate.IceCandidate;
import org.lastbamboo.common.ice.candidate.IceCandidateVisitor;
import org.lastbamboo.common.ice.candidate.IceTcpActiveCandidate;
import org.lastbamboo.common.ice.candidate.IceTcpHostPassiveCandidate;
import org.lastbamboo.common.ice.candidate.IceTcpPeerReflexiveCandidate;
import org.lastbamboo.common.ice.candidate.IceTcpRelayPassiveCandidate;
import org.lastbamboo.common.ice.candidate.IceTcpServerReflexiveSoCandidate;
import org.lastbamboo.common.ice.candidate.IceUdpHostCandidate;
import org.lastbamboo.common.ice.candidate.IceUdpPeerReflexiveCandidate;
import org.lastbamboo.common.ice.candidate.IceUdpRelayCandidate;
import org.lastbamboo.common.ice.candidate.IceUdpServerReflexiveCandidate;
import org.lastbamboo.common.sdp.api.Attribute;
import org.lastbamboo.common.sdp.api.Connection;
import org.lastbamboo.common.sdp.api.MediaDescription;
import org.lastbamboo.common.sdp.api.Origin;
import org.lastbamboo.common.sdp.api.SdpException;
import org.lastbamboo.common.sdp.api.SdpFactory;
import org.lastbamboo.common.sdp.api.SessionDescription;
import org.lastbamboo.common.sdp.api.SessionName;
import org.lastbamboo.common.sdp.api.TimeDescription;
import org.littleshoot.util.NetworkUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Class for encoding ICE candidates into SDP.  For a discussion of the 
 * priorities associated with each candidate type and for a general discussion 
 * of ICE, this was written based on draft-ietf-mmusic-ice-16 at:

* * http://www.tools.ietf.org/html/draft-ietf-mmusic-ice-16 * *

* * Here's the augmented BNF for candidates: * * candidate-attribute = "candidate" ":" foundation SP component-id SP * transport SP * priority SP * connection-address SP ;from RFC 4566 * port ;port from RFC 4566 * SP cand-type * [SP rel-addr] * [SP rel-port] * *(SP extension-att-name SP * extension-att-value) * * foundation = 1*32ice-char * component-id = 1*5DIGIT * transport = "UDP" / transport-extension * transport-extension = token ; from RFC 3261 * priority = 1*10DIGIT * cand-type = "typ" SP candidate-types * candidate-types = "host" / "srflx" / "prflx" / "relay" / token * rel-addr = "raddr" SP connection-address * rel-port = "rport" SP port * extension-att-name = byte-string ;from RFC 4566 * extension-att-value = byte-string * ice-char = ALPHA / DIGIT / "+" / "/" */ public class IceCandidateSdpEncoder implements IceCandidateVisitor { private final Logger m_log = LoggerFactory.getLogger(getClass()); private final SdpFactory m_sdpFactory; private final SessionDescription m_sessionDescription; private final Vector m_candidates; private final String m_mimeContentType; private final String m_mimeContentSubtype; private final IceCandidate[] m_defaultCandidates = new IceCandidate[5]; /** * Creates a new encoder for encoder ICE candidates into SDP. * * @param mimeContentType The MIME content type for the SDP. * @param mimeContentSubtype The MIME content subtype for the SDP. */ public IceCandidateSdpEncoder(final String mimeContentType, final String mimeContentSubtype) { m_mimeContentType = mimeContentType; m_mimeContentSubtype = mimeContentSubtype; this.m_sdpFactory = new SdpFactory(); final InetAddress address = getAddress(); final String addrType = address instanceof Inet6Address ? "IP6" : "IP4"; this.m_candidates = new Vector(); try { final Origin o = this.m_sdpFactory.createOrigin("-", 0, 0, "IN", addrType, address.getHostAddress()); // "s=-" final SessionName s = this.m_sdpFactory.createSessionName("-"); // "t=0 0" final TimeDescription t = this.m_sdpFactory.createTimeDescription(); final Vector timeDescriptions = new Vector(); timeDescriptions.add(t); this.m_sessionDescription = createSessionDescription(); this.m_sessionDescription.setVersion( this.m_sdpFactory.createVersion(0)); this.m_sessionDescription.setOrigin(o); this.m_sessionDescription.setSessionName(s); this.m_sessionDescription.setTimeDescriptions(timeDescriptions); } catch (final SdpException e) { m_log.error("Could not create SDP", e); throw new IllegalArgumentException("Could not create SDP", e); } } /** * Accesses the SDP as an array of bytes. * * @return The SDP as an array of bytes. */ public byte[] getSdp() { return this.m_sessionDescription.toBytes(); } private InetAddress getAddress() { try { return NetworkUtils.getLocalHost(); } catch (final UnknownHostException e) { // Should never happen. m_log.error("Could not resolve host", e); throw new RuntimeException("Could not resolve host", e); } } private SessionDescription createSessionDescription() { try { return this.m_sdpFactory.createSessionDescription(); } catch (final SdpException e) { m_log.error("Could not create SDP", e); throw new IllegalArgumentException("Could not create SDP", e); } } public void visitCandidates(final Collection candidates) { m_log.info("Visiting candidates: {}", candidates); for (final IceCandidate candidate : candidates) { candidate.accept(this); } try { // Use the UDP server reflexive address as the top level address. // This is slightly hacky because it relies on the UDP server // reflexive candidate being there. IceCandidate defaultCandidate = null; for (final IceCandidate candidate : this.m_defaultCandidates) { if (candidate != null) { defaultCandidate = candidate; } } if (defaultCandidate == null) { m_log.error("No default candidate from: {}", candidates); return; } final MediaDescription md = createMessageMediaDesc(defaultCandidate); md.setAttributes(m_candidates); m_log.debug("Adding media description"); final Vector mediaDescriptions = new Vector(); mediaDescriptions.add(md); this.m_sessionDescription.setMediaDescriptions(mediaDescriptions); } catch (final SdpException e) { m_log.error("Could not add the media descriptions", e); } } public Null visitTcpHostPassiveCandidate( final IceTcpHostPassiveCandidate candidate) { addAttribute(candidate); m_defaultCandidates[1] = candidate; return ObjectUtils.NULL; } public Null visitTcpPeerReflexiveCandidate( final IceTcpPeerReflexiveCandidate candidate) { addAttributeWithRelated(candidate); return ObjectUtils.NULL; } public Null visitTcpRelayPassiveCandidate( final IceTcpRelayPassiveCandidate candidate) { addAttributeWithRelated(candidate); m_defaultCandidates[3] = candidate; return ObjectUtils.NULL; } public Null visitTcpServerReflexiveSoCandidate( final IceTcpServerReflexiveSoCandidate candidate) { addAttributeWithRelated(candidate); return ObjectUtils.NULL; } public Null visitTcpActiveCandidate(final IceTcpActiveCandidate candidate) { addAttribute(candidate, 9); // This will only ever be relevant for testing. m_defaultCandidates[4] = candidate; return ObjectUtils.NULL; } public Null visitUdpHostCandidate(final IceUdpHostCandidate candidate) { addAttribute(candidate); m_defaultCandidates[2] = candidate; return ObjectUtils.NULL; } public Null visitUdpPeerReflexiveCandidate( final IceUdpPeerReflexiveCandidate candidate) { addAttributeWithRelated(candidate); return ObjectUtils.NULL; } public Null visitUdpRelayCandidate(final IceUdpRelayCandidate candidate) { addAttributeWithRelated(candidate); return ObjectUtils.NULL; } public Null visitUdpServerReflexiveCandidate( final IceUdpServerReflexiveCandidate candidate) { m_log.info("Visiting UDP server reflexive: {}", candidate); addAttributeWithRelated(candidate); m_defaultCandidates[0] = candidate; return ObjectUtils.NULL; } /** * Creates a new media description with ICE candidates for the specified * addresses and for the specified protocol. * * @param socketAddress The address for the media. * @param protocol The protocol of the media. * @return The new media description. * @throws SdpException If the data could not be generated for any * reason. */ private MediaDescription createMessageMediaDesc( final IceCandidate candidate) throws SdpException { final InetSocketAddress socketAddress = candidate.getSocketAddress(); final String protocol = candidate.getTransport().getName(); final MediaDescription md = this.m_sdpFactory.createMediaDescription( this.m_mimeContentType, socketAddress.getPort(), 1, protocol, new String[] { this.m_mimeContentSubtype }); final Connection conn = this.m_sdpFactory.createConnection("IN", Connection.IP4, socketAddress.getAddress().getHostAddress()); md.setConnection(conn); return md; } /** * Creates the base level attribute {@link StringBuilder} applicable for * all candidates. * * @param candidate The candidate to encode. * @param port The port to encode. This is all because TCP active * candidates encode their port as 9 for discard. * @return The {@link StringBuilder} for creating the rest of the * encoding. */ private StringBuilder createBaseCandidateAttribute( final IceCandidate candidate, final int port) { final String space = " "; final StringBuilder sb = new StringBuilder(); sb.append(candidate.getFoundation()); sb.append(space); sb.append(candidate.getComponentId()); sb.append(space); sb.append(candidate.getTransport().getName()); sb.append(space); sb.append(candidate.getPriority()); sb.append(space); final InetSocketAddress sa = candidate.getSocketAddress(); final InetAddress ia = sa.getAddress(); sb.append(ia.getHostAddress()); sb.append(space); sb.append(port); sb.append(space); sb.append("typ"); sb.append(space); sb.append(candidate.getType().toSdp()); return sb; } private void addAttribute(final IceCandidate candidate) { addAttribute(candidate, candidate.getSocketAddress().getPort()); } private void addAttribute(final IceCandidate candidate, final int port) { final StringBuilder sb = createBaseCandidateAttribute(candidate, port); final Attribute attribute = this.m_sdpFactory.createAttribute( "candidate", sb.toString()); m_candidates.add(attribute); } /** * Encodes a candidate attribute with the related address and related * port field filled in. * * @param candidate The candidate. */ private void addAttributeWithRelated(final IceCandidate candidate) { final int port = candidate.getSocketAddress().getPort(); final StringBuilder sb = createBaseCandidateAttribute(candidate, port); final String space = " "; sb.append(space); sb.append("raddr"); sb.append(space); sb.append(candidate.getRelatedAddress().getHostAddress()); sb.append(space); sb.append("rport"); sb.append(space); sb.append(candidate.getRelatedPort()); final Attribute attribute = this.m_sdpFactory.createAttribute( "candidate", sb.toString()); m_candidates.add(attribute); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy