package call; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.SocketException; import java.util.ArrayList; import java.util.List; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.SourceDataLine; import javax.sound.sampled.UnsupportedAudioFileException; public class CallPlayer extends AbstractCallConnection implements Runnable { private final SourceDataLine line; private final InputStream in; private final List<OutputStream> captureStreams = new ArrayList<>(); private int buffersize; private long bytesSent = 0; public CallPlayer(Contact contact, InputStream in, PcmFormat format, int buffersize) throws LineUnavailableException, UnsupportedAudioFileException, IOException, UnknownDefaultValueException { super(contact); this.in = in; this.buffersize = buffersize; AudioFormat audioFormat = format.getAudioFormat(); Util.log(contact, "Player: start."); Util.log(contact, "Player: source audio format: " + audioFormat); Speaker speaker = Speakers.getCurrentSpeaker(); Util.log(contact, "Player: speaker: " + speaker); line = (SourceDataLine) speaker.getLine(); line.open(audioFormat, buffersize); line.start(); } @Override public void run() { CallFactory.openCall(contact); byte[] buffer = new byte[buffersize]; bytesSent = 0; final long startTime = System.currentTimeMillis(); Thread statsThread = new Thread(new Runnable() { @Override public void run() { while (isCallOpen()) { long now = System.currentTimeMillis(); long diffTime = now - startTime; if (diffTime > 0) { float speed = bytesSent / diffTime * 1000; CallUi.updateCallStats(contact, speed, bytesSent, -1, -1); Util.sleep(5_000); } } } }, "CallPlayer -> (Stats)"); statsThread.start(); statsThread.setPriority(Thread.MIN_PRIORITY); Thread.currentThread().setPriority(Thread.MAX_PRIORITY); try { int cnt; // Keep looping until the input read method returns -1 for empty // stream. while ((cnt = in.read(buffer, 0, buffer.length)) != -1 && isCallOpen()) { if (cnt > 0) { // Write data to the internal buffer of the data line where // it will be delivered to the speaker. line.write(buffer, 0, cnt); for (OutputStream out : captureStreams) { out.write(buffer, 0, cnt); } bytesSent += cnt; } } // Block and wait for internal buffer of the data line to empty. line.drain(); line.close(); } catch (SocketException e) { Util.log(contact, "Player: " + e.getLocalizedMessage()); } catch (Exception e) { e.printStackTrace(); } CallFactory.closeCall(contact); } @Override public void onCallClose() { Util.log(contact, "Player: stop."); if (line.isRunning()) line.stop(); if (line.isOpen()) line.close(); CallUi.updateCallStats(contact, 0, 0, -1, -1); super.onCallClose(); } public void saveTo(Capture capture) { OutputStream out = capture.getCaptureOutputStream(); if (out != null) { captureStreams.add(new BufferedOutputStream(out, 256 * 1024)); } } @Override public String getId() { return "CallPlayer<" + contact.getId() + ">"; } }