package javaforce.voip; import java.util.*; import javaforce.*; /** * SDP (Session Description Protocol) * * @author pquiring * * Created : Nov 30, 2013 */ public class SDP implements Cloneable { public static enum Type {audio, video, other}; public static enum Mode {sendonly, recvonly, sendrecv, inactive}; public static enum Profile {AVP, SAVP, AVPF, SAVPF, UNKNOWN}; public static enum KeyExchange {NONE, SDP, DTLS}; public class Key { public String crypto; public byte key[]; public byte salt[]; } public class Stream { public Type type; public Mode mode = Mode.sendrecv; public String ip; //optional (stream specific) [rare] public Codec codecs[] = new Codec[0]; public int port = -1; //NATing if not specified public String content; public SDP sdp; public Profile profile = Profile.AVP; public KeyExchange keyExchange = KeyExchange.NONE; public Key[] keys; //if KeyExchange == SDP public String getType() { switch (type) { case audio: return "audio"; case video: return "video"; } return "?"; } public String getMode() { switch (mode) { case sendonly: return "sendonly"; case recvonly: return "recvonly"; case sendrecv: return "sendrecv"; case inactive: return "inactive"; } return "?"; } public boolean hasCodec(Codec codec) { return SIP.hasCodec(codecs, codec); } public Codec addCodec(Codec codec) { codecs = SIP.addCodec(codecs, codec); return codec; } public void delCodec(Codec codec) { codecs = SIP.delCodec(codecs, codec); } public void setCodec(Codec codec) { codecs = new Codec[1]; codecs[0] = codec; } public Codec getCodec(Codec codec) { return SIP.getCodec(codecs, codec); } public String getIP() { if (ip != null) return ip; return SDP.this.ip; } public int getPort() { return port; } public boolean canSend() { return mode == Mode.sendrecv || mode == Mode.sendonly; } public boolean canRecv() { return mode == Mode.sendrecv || mode == Mode.recvonly; } public boolean isSecure() { return profile == Profile.SAVP || profile == Profile.SAVPF; } /** * Used to add SRTP keys to SDP (obsoleted by DTLS method) * Must set KeyExchange to SDP. * * @param crypto = AES_CM_128_HMAC_SHA1_80, etc. * @param key = 16 byte key * @param salt = 14 byte salt */ public void addKey(String crypto, byte key[], byte salt[]) { Key newkey = new Key(); newkey.crypto = crypto; newkey.key = key; newkey.salt = salt; if (keys == null) keys = new Key[0]; Key newKeys[] = new Key[keys.length + 1]; newKeys[keys.length] = newkey; keys = newKeys; } public Key getKey(String crypto) { if (keys == null) return null; for(int a=0;a<keys.length;a++) { if (keys[a].crypto.equals(crypto)) return keys[a]; } return null; } } public String ip; //global connection public String iceufrag, icepwd, fingerprint; public Stream streams[] = new Stream[0]; public Stream addStream(Type type) { JFLog.log("SDP.addStream:" + type); Stream stream = new Stream(); stream.sdp = this; stream.type = type; streams = Arrays.copyOf(streams, streams.length+1); streams[streams.length-1] = stream; return stream; } public void delStream(Stream stream) { for(int a=0;a<streams.length;a++) { if (streams[a] == stream) { streams = (Stream[])JF.copyOfExcluding(streams, a); return; } } } public void delAudio() { for(int a=0;a<streams.length;) { if (streams[a].type == Type.audio) { streams = (Stream[])JF.copyOfExcluding(streams, a); } else { a++; } } } public void delVideo() { for(int a=0;a<streams.length;) { if (streams[a].type == Type.video) { streams = (Stream[])JF.copyOfExcluding(streams, a); } else { a++; } } } public Stream getFirstAudioStream() { for(int a=0;a<streams.length;a++) { if (streams[a].type == Type.audio) return streams[a]; } return null; } public Stream getFirstVideoStream() { for(int a=0;a<streams.length;a++) { if (streams[a].type == Type.video) return streams[a]; } return null; } public boolean hasAudio() { return getFirstAudioStream() != null; } public boolean hasVideo() { return getFirstVideoStream() != null; } public String toString() { StringBuilder sb = new StringBuilder(); sb.append("SDP:" + streams.length + "["); for(int a=0;a<streams.length;a++) { if (a > 0) sb.append(","); sb.append("Stream=" + streams[a].type + "," + streams[a].mode); } sb.append("]"); return sb.toString(); } public Object clone() { try { return super.clone(); } catch (Exception e) { return null; } } }