// Path: TCC/src/Dependencias/Message.java
// public class Message implements Serializable {
// 
//     private long chId; //-1 means from client to server, otherwise chId generated by the server
//     private long timestamp, //-1 means from client to server, otherwise timeStamp of the moment when the server receives the message
//             ttl = 2000; //2 seconds TTL
//     private Object data; //can carry any type of object. in this program, i used a sound packet, but it could be a string, a chunk of video, ...
// 
//     public Message(long chId, long timestamp, Object data) {
//         this.chId = chId;
//         this.timestamp = timestamp;
//         this.data = data;
//     }
// 
//     public void setTimestamp(long timestamp) {
//         this.timestamp = timestamp;
//     }
// 
//     public long getChId() {
//         return chId;
//     }
// 
//     public Object getData() {
//         return data;
//     }
// 
//     public long getTimestamp() {
//         return timestamp;
//     }
// 
//     public long getTtl() {
//         return ttl;
//     }
// 
//     public void setTtl(long ttl) {
//         this.ttl = ttl;
//     }
// 
//     public void setChId(long chId) {
//         this.chId = chId;
//     }
// 
// }
// 
// Path: TCC/src/Dependencias/Utils.java
// public class Utils {
// 
//     public static void sleep(int ms) {
//         try {
//             Thread.sleep(ms);
//         } catch (InterruptedException ex) {
//         }
//     }
// 
//     public static String getExternalIP() {
//         try {
//             URL myIp = new URL("http://checkip.dyndns.org/");
//             BufferedReader in = new BufferedReader(new InputStreamReader(myIp.openStream()));
//             String s = in.readLine();
//             return s.substring(s.lastIndexOf(":") + 2, s.lastIndexOf("</body>"));
//         } catch (Exception ex) {
//             return "error " + ex;
//         }
//     }
// 
//     public static String getInternalIP() {
//         try {
//             return InetAddress.getLocalHost().getHostAddress();
//         } catch (UnknownHostException ex) {
//             return "error";
//         }
//     }
// }


import Dependencias.Message;
import Dependencias.Utils;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
        s = new Socket(serverIp, serverPort);
    }

    @Override
    public void run() {
        try {
            ObjectInputStream fromServer = new ObjectInputStream(s.getInputStream());  //create object streams with the server
            ObjectOutputStream toServer = new ObjectOutputStream(s.getOutputStream());
            try {
                Utils.sleep(100); //wait for the GUI microphone test to release the microphone
                st = new MicThread(toServer);  //creates a MicThread that sends microphone data to the server
                st.start(); //starts the MicThread
            } catch (Exception e) { //error acquiring microphone. causes: no microphone or microphone busy
                System.out.println("mic unavailable " + e);
            }
            for (;;) { //this infinite cycle checks for new data from the server, then sends it to the correct AudioChannel. if needed, a new AudioChannel is created

                if (s.getInputStream().available() > 0) { //we got something from the server (workaround: used available method from InputStream instead of the one from ObjetInputStream because of a bug in the JRE)
                    Message in = (Message) (fromServer.readObject()); //read message
                    //decide which audio channel should get this message
                    AudioChannel sendTo = null;
                    for (AudioChannel ch : chs) {
                        if (ch.getChId() == in.getChId()) {
                            sendTo = ch;
                        }
                    }
                    if (sendTo != null) {
                        sendTo.addToQueue(in);
                    } else { //new AudioChannel is needed
                        AudioChannel ch = new AudioChannel(in.getChId());
// Path: TCC/src/Dependencias/Message.java
// public class Message implements Serializable {
// 
//     private long chId; //-1 means from client to server, otherwise chId generated by the server
//     private long timestamp, //-1 means from client to server, otherwise timeStamp of the moment when the server receives the message
//             ttl = 2000; //2 seconds TTL
//     private Object data; //can carry any type of object. in this program, i used a sound packet, but it could be a string, a chunk of video, ...
// 
//     public Message(long chId, long timestamp, Object data) {
//         this.chId = chId;
//         this.timestamp = timestamp;
//         this.data = data;
//     }
// 
//     public void setTimestamp(long timestamp) {
//         this.timestamp = timestamp;
//     }
// 
//     public long getChId() {
//         return chId;
//     }
// 
//     public Object getData() {
//         return data;
//     }
// 
//     public long getTimestamp() {
//         return timestamp;
//     }
// 
//     public long getTtl() {
//         return ttl;
//     }
// 
//     public void setTtl(long ttl) {
//         this.ttl = ttl;
//     }
// 
//     public void setChId(long chId) {
//         this.chId = chId;
//     }
// 
// }
// 
// Path: TCC/src/Dependencias/Utils.java
// public class Utils {
// 
//     public static void sleep(int ms) {
//         try {
//             Thread.sleep(ms);
//         } catch (InterruptedException ex) {
//         }
//     }
// 
//     public static String getExternalIP() {
//         try {
//             URL myIp = new URL("http://checkip.dyndns.org/");
//             BufferedReader in = new BufferedReader(new InputStreamReader(myIp.openStream()));
//             String s = in.readLine();
//             return s.substring(s.lastIndexOf(":") + 2, s.lastIndexOf("</body>"));
//         } catch (Exception ex) {
//             return "error " + ex;
//         }
//     }
// 
//     public static String getInternalIP() {
//         try {
//             return InetAddress.getLocalHost().getHostAddress();
//         } catch (UnknownHostException ex) {
//             return "error";
//         }
//     }
// }


// Path: TCC/src/ClienteVoIP/Client.java
import Dependencias.Message;
import Dependencias.Utils;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;

        s = new Socket(serverIp, serverPort);
    }

    @Override
    public void run() {
        try {
            ObjectInputStream fromServer = new ObjectInputStream(s.getInputStream());  //create object streams with the server
            ObjectOutputStream toServer = new ObjectOutputStream(s.getOutputStream());
            try {
                Utils.sleep(100); //wait for the GUI microphone test to release the microphone
                st = new MicThread(toServer);  //creates a MicThread that sends microphone data to the server
                st.start(); //starts the MicThread
            } catch (Exception e) { //error acquiring microphone. causes: no microphone or microphone busy
                System.out.println("mic unavailable " + e);
            }
            for (;;) { //this infinite cycle checks for new data from the server, then sends it to the correct AudioChannel. if needed, a new AudioChannel is created

                if (s.getInputStream().available() > 0) { //we got something from the server (workaround: used available method from InputStream instead of the one from ObjetInputStream because of a bug in the JRE)
                    Message in = (Message) (fromServer.readObject()); //read message
                    //decide which audio channel should get this message
                    AudioChannel sendTo = null;
                    for (AudioChannel ch : chs) {
                        if (ch.getChId() == in.getChId()) {
                            sendTo = ch;
                        }
                    }
                    if (sendTo != null) {
                        sendTo.addToQueue(in);
                    } else { //new AudioChannel is needed
                        AudioChannel ch = new AudioChannel(in.getChId());
                        ch.addToQueue(in);