PageRenderTime 60ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/VoipClient.java

https://github.com/atmouse/VoiceOverIP
Java | 404 lines | 269 code | 62 blank | 73 comment | 8 complexity | 50f7e860a4142d1318d8886050531881 MD5 | raw file
  1. import java.awt.GridBagConstraints;
  2. import java.awt.GridBagLayout;
  3. import java.awt.Insets;
  4. import java.awt.event.ActionEvent;
  5. import java.awt.event.ActionListener;
  6. import java.io.BufferedReader;
  7. import java.io.ByteArrayInputStream;
  8. import java.io.InputStream;
  9. import java.io.InputStreamReader;
  10. import java.io.OutputStream;
  11. import java.net.DatagramPacket;
  12. import java.net.DatagramSocket;
  13. import java.net.InetAddress;
  14. import java.net.Socket;
  15. import java.util.ArrayList;
  16. import javax.sound.sampled.AudioFormat;
  17. import javax.sound.sampled.AudioInputStream;
  18. import javax.sound.sampled.AudioSystem;
  19. import javax.sound.sampled.DataLine;
  20. import javax.sound.sampled.SourceDataLine;
  21. import javax.sound.sampled.TargetDataLine;
  22. import javax.swing.JFrame;
  23. import javax.swing.JList;
  24. import javax.swing.JPanel;
  25. import javax.swing.JScrollPane;
  26. import javax.swing.JTextArea;
  27. import javax.swing.JTextField;
  28. public class VoipClient implements ActionListener{
  29. //gui
  30. private static JFrame frame;
  31. private static JTextField textField;
  32. private static JTextArea textArea;
  33. private static JScrollPane scrollPane;
  34. private static JList userlist;
  35. private InetAddress ip; //the ip address the client whishes to call
  36. private InetAddress server_ip; //the ip of the server
  37. private int receive_port; //the port to which the client wishes to send
  38. private int send_port; //the clients own port, to listen on for traffic
  39. private int server_port; //the port with which to connect to the server
  40. private DatagramSocket receive_socket; //the socket to receive on
  41. private DatagramSocket send_socket; //the socket to send on
  42. private Socket socket; //TCP socket to server
  43. private static OutputStream out = null; //XXX made these static
  44. private static BufferedReader in = null; //XXX made these static
  45. private static Thread listenLoop; //XXX my addition
  46. private static boolean connected = false;
  47. //private static ArrayList<item> users; //XXX my addition
  48. //boolean stopCapture = false;
  49. AudioFormat audioFormat;
  50. TargetDataLine targetDataLine;
  51. AudioInputStream audioInputStream;
  52. SourceDataLine sourceDataLine;
  53. public VoipClient(String ip, int receive_port, int send_port){
  54. //build the gui
  55. frame = new JFrame("VOIP");
  56. JPanel pane = new JPanel(new GridBagLayout());
  57. GridBagConstraints c1 = new GridBagConstraints();
  58. GridBagConstraints c2 = new GridBagConstraints();
  59. GridBagConstraints c3 = new GridBagConstraints();
  60. textArea = new JTextArea("Commands: \n \\call <ip> \n \\dc \n \\exit \n");
  61. textArea.setEditable(false);
  62. textArea.setLineWrap(true);
  63. scrollPane = new JScrollPane(textArea);
  64. scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
  65. textField = new JTextField();
  66. textField.addActionListener(this);
  67. textField.setText("\\call 127.0.0.1");
  68. userlist = new JList();
  69. c1.fill = GridBagConstraints.BOTH;
  70. c1.insets = new Insets(10, 10, 5, 5); //top, left, bottom, right
  71. c1.gridwidth = 1;
  72. c2.gridheight = 1;
  73. c1.ipadx = 350;
  74. c1.ipady = 400;
  75. c1.weightx = 1;
  76. c1.weighty = 1;
  77. c1.gridx = 0;
  78. c1.gridy = 0;
  79. pane.add(scrollPane, c1);
  80. c2.fill = GridBagConstraints.BOTH;
  81. c2.insets = new Insets(5, 10, 10, 10);
  82. c2.gridwidth = 1;
  83. c2.gridheight = 1;
  84. c2.ipadx = 350;
  85. c2.ipady = 100;
  86. c2.weightx = 1;
  87. c2.weighty = 1;
  88. c2.gridx = 0;
  89. c2.gridy = 1;
  90. pane.add(textField, c2);
  91. c3.fill = GridBagConstraints.BOTH;
  92. c3.insets = new Insets(10, 5, 10, 10);
  93. c3.gridwidth = 1;
  94. c3.gridheight = 1;
  95. c3.ipadx = 200;
  96. c3.ipady = 400;
  97. c3.weightx = 0.2;
  98. c3.weighty = 0.2;
  99. c3.gridx = 1;
  100. c3.gridy = 0;
  101. pane.add(userlist, c3);
  102. frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  103. frame.getContentPane().add(pane);
  104. frame.pack();
  105. /*
  106. * requestFocusInWindow must be called after component realization,
  107. * frame.pack() and before the frame is displayed.
  108. */
  109. textField.requestFocusInWindow();
  110. textField.setSelectionStart(6);
  111. textField.setSelectionEnd(15);
  112. frame.setSize(500, 500);
  113. frame.setVisible(true);
  114. this.receive_port = receive_port;
  115. this.send_port = send_port;
  116. try {
  117. this.ip = InetAddress.getByName(ip);
  118. } catch (Exception e) {
  119. System.out.println(e.getMessage());
  120. }
  121. try {
  122. this.receive_socket = new DatagramSocket(this.receive_port);
  123. this.send_socket = new DatagramSocket(this.send_port);
  124. } catch (Exception e) {
  125. System.out.println(e.getMessage());
  126. }
  127. /*
  128. Thread CaptureAudio = new Thread(new CaptureAudio(this.send_socket, this.ip, this.receive_port));
  129. CaptureAudio.start();
  130. Thread PlayAudio = new Thread(new PlayAudio(this.receive_socket));
  131. PlayAudio.start();
  132. */
  133. }
  134. //This method creates and returns an
  135. // AudioFormat object for a given set
  136. // of format parameters. If these
  137. // parameters don't work well for
  138. // you, try some of the other
  139. // allowable parameter values, which
  140. // are shown in comments following
  141. // the declarations.
  142. private AudioFormat getAudioFormat(){
  143. float sampleRate = 8000.0F;
  144. //8000,11025,16000,22050,44100
  145. int sampleSizeInBits = 16;
  146. //8,16
  147. int channels = 1;
  148. //1,2
  149. boolean signed = true;
  150. //true,false
  151. boolean bigEndian = false;
  152. //true,false
  153. return new AudioFormat(sampleRate, sampleSizeInBits, channels, signed, bigEndian);
  154. }//end getAudioFormat
  155. //This method captures audio input
  156. // from a microphone and saves it in
  157. // a ByteArrayOutputStream object.
  158. class CaptureAudio implements Runnable {
  159. private byte[] tempBuffer;
  160. private DatagramSocket socket;
  161. private InetAddress ip;
  162. private int port;
  163. public CaptureAudio(DatagramSocket socket, InetAddress ip, int port) {
  164. this.socket = socket;
  165. this.ip = ip;
  166. this.port = port;
  167. this.tempBuffer = new byte[10000];
  168. }
  169. public void run() {
  170. try {
  171. //Get everything set up for capture
  172. audioFormat = getAudioFormat();
  173. DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class, audioFormat);
  174. targetDataLine = (TargetDataLine)
  175. AudioSystem.getLine(dataLineInfo);
  176. targetDataLine.open(audioFormat);
  177. targetDataLine.start();
  178. //Create a thread to capture the
  179. // microphone data and start it
  180. // running. It will run until
  181. // the Stop button is clicked.
  182. //Thread captureThread = new Thread(new CaptureThread(send_socket, ip, dest_port));
  183. //captureThread.start();
  184. //capture the audio now
  185. //byteArrayOutputStream = new ByteArrayOutputStream();
  186. //stopCapture = false;
  187. try{//Loop until stopCapture is set
  188. // by another thread that
  189. // services the Stop button.
  190. while(/*!stopCapture*/true){
  191. //Read data from the internal
  192. // buffer of the data line.
  193. int cnt = targetDataLine.read(tempBuffer, 0, tempBuffer.length);
  194. if(cnt > 0){
  195. //Save data in output stream
  196. // object.
  197. //byteArrayOutputStream.write(tempBuffer, 0, cnt); //XXX save to memory, put socket here?
  198. DatagramPacket outPacket = new DatagramPacket(tempBuffer, tempBuffer.length, this.ip, this.port);
  199. this.socket.send(outPacket);
  200. }//end if
  201. }//end while
  202. //byteArrayOutputStream.close();
  203. }catch (Exception e) {
  204. System.out.println(e);
  205. System.exit(0);
  206. }//end catch
  207. } catch (Exception e) {
  208. System.out.println(e);
  209. System.exit(0);
  210. }//end catch
  211. }
  212. }//end captureAudio method
  213. class PlayAudio implements Runnable {
  214. private DatagramSocket socket;
  215. private byte[] tempBuffer;
  216. public PlayAudio(DatagramSocket socket) {
  217. this.socket = socket;
  218. this.tempBuffer = new byte[10000];
  219. }
  220. public void run() {
  221. try{
  222. //Get everything set up for
  223. // playback.
  224. //Get the previously-saved data
  225. // into a byte array object.
  226. //byte audioData[] = byteArrayOutputStream.toByteArray(); //XXX saved stuff, put socket here
  227. DatagramPacket inPacket;
  228. while (true) {
  229. inPacket = new DatagramPacket(tempBuffer, tempBuffer.length);
  230. this.socket.receive(inPacket);
  231. byte[] audioData = inPacket.getData();
  232. //Get an input stream on the
  233. // byte array containing the data
  234. InputStream byteArrayInputStream = new ByteArrayInputStream(audioData);
  235. AudioFormat audioFormat = getAudioFormat();
  236. audioInputStream = new AudioInputStream(byteArrayInputStream, audioFormat, audioData.length/audioFormat.getFrameSize());
  237. DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, audioFormat);
  238. sourceDataLine = (SourceDataLine)
  239. AudioSystem.getLine(dataLineInfo);
  240. sourceDataLine.open(audioFormat);
  241. sourceDataLine.start();
  242. //Create a thread to play back
  243. // the data and start it
  244. // running. It will run until
  245. // all the data has been played
  246. // back.
  247. //Thread playThread = new Thread(new PlayThread());
  248. //playThread.start();
  249. try {
  250. int cnt;
  251. //Keep looping until the input
  252. // read method returns -1 for
  253. // empty stream.
  254. while((cnt = audioInputStream.read(tempBuffer, 0, tempBuffer.length)) != -1){
  255. if(cnt > 0){
  256. //Write data to the internal
  257. // buffer of the data line
  258. // where it will be delivered
  259. // to the speaker.
  260. sourceDataLine.write(tempBuffer, 0, cnt);
  261. }//end if
  262. }//end while
  263. //Block and wait for internal
  264. // buffer of the data line to
  265. // empty.
  266. sourceDataLine.drain();
  267. sourceDataLine.close();
  268. }catch (Exception e) {
  269. System.out.println(e);
  270. System.exit(0);
  271. }//end catch
  272. } //while
  273. } catch (Exception e) {
  274. System.out.println(e);
  275. System.exit(0);
  276. }//end catch
  277. }
  278. }//end playAudio
  279. private void client_connect(String ip) {
  280. Thread CaptureAudio = new Thread(new CaptureAudio(this.send_socket, this.ip, this.receive_port));
  281. CaptureAudio.start();
  282. Thread PlayAudio = new Thread(new PlayAudio(this.receive_socket));
  283. PlayAudio.start();
  284. }
  285. private void server_connect(String ip, int port) {
  286. System.out.println("IP of server: " + ip);
  287. try {
  288. this.server_ip = InetAddress.getByName(ip);
  289. this.server_port = port;
  290. this.socket = new Socket(this.server_ip, this.server_port);
  291. this.out = socket.getOutputStream();
  292. this.in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
  293. textArea.append("Connected to server at: " + ip + "\n");
  294. connected = true;
  295. } catch (Exception e) {
  296. System.out.println(e.getMessage());
  297. }
  298. }
  299. private void server_send(String message) {
  300. try {
  301. out.write(message.getBytes());
  302. } catch (Exception e){
  303. System.out.println("Error sending message: " + e.getMessage());
  304. }
  305. }
  306. public void actionPerformed(ActionEvent a) {
  307. if (a.getSource() == textField) {
  308. String text = textField.getText();
  309. try {
  310. server_send(text); //XXX TODO do not prepend stuff
  311. } catch(Exception e) {
  312. textArea.append("Error processing text.\n");
  313. }
  314. textField.setText("");
  315. }
  316. }
  317. private static class ConnectionListener implements Runnable { //XXX my addition
  318. public void run() { //listens to incoming messages from the server
  319. try {
  320. while(true) {
  321. String data = in.readLine();
  322. if (data != null) {
  323. textArea.append(data+"\n");
  324. }
  325. }
  326. } catch (Exception e) {
  327. System.err.println(e.getMessage());
  328. }
  329. }
  330. }
  331. public static void main(String args[]){
  332. String ip = "127.0.0.1"; //destination ip
  333. String receive_port = "3001";
  334. String send_port = "3002";
  335. VoipClient c = new VoipClient(ip, Integer.parseInt(receive_port), Integer.parseInt(send_port));
  336. c.server_connect(args[0], Integer.parseInt(args[1])); //TODO use command line arg
  337. listenLoop = new Thread(new ConnectionListener()); //XXX my addition
  338. listenLoop.start(); //XXX my addition
  339. while(connected);
  340. }
  341. }