4 import iotcode.interfaces.*;
5 import iotcode.annotation.*;
6 import iotruntime.IoTUDP;
7 import iotruntime.IoTTCP;
8 import iotruntime.slave.IoTSet;
9 import iotruntime.slave.IoTDeviceAddress;
12 import java.rmi.Remote;
13 import java.rmi.RemoteException;
15 // Checker annotations
16 //import iotchecker.qual.*;
18 // Standard Java Packages
19 import java.util.concurrent.atomic.AtomicBoolean;
21 import java.util.HashMap;
22 import java.util.HashSet;
24 import java.util.List;
25 import java.util.ArrayList;
26 import java.io.InputStreamReader;
27 import java.io.BufferedReader;
28 import java.io.PrintWriter;
29 import java.io.ByteArrayInputStream;
30 import java.util.LinkedList;
31 import java.util.concurrent.Semaphore;
32 import java.util.concurrent.CopyOnWriteArrayList;
35 public class IHome implements Speaker {
38 /*******************************************************************************************************************************************
40 *******************************************************************************************************************************************/
42 public static final float VOLUME_MUTED_VALUE_DB = (float) (-144.0);
43 public static final float VOLUME_MIN_VALUE_DB = (float) (-30.0);
44 public static final float VOLUME_MAX_VALUE_DB = (float) (-0.0);
45 public static final float DEFAULT_VOLUME = (float) (30.0);
47 public static final long SEQUENCE_NUMBER_INITIAL_VALUE = 18086;
48 public static final long SEQUENCE_NUMBER_WRAP_AROUND = 32768L;
49 public static final long RTP_TIMESTAMP_INITIAL_VALUE = 3132223670L;
50 public static final long RTP_TIMESTAMP_INCREMENT_VALUE = 352L;
51 public static final long SOURCE_ID = 1326796157;
52 public static final long SEQUENCE_ID = 0x86b27741;
54 private IoTDeviceAddress tcpAddress = null;
55 private IoTDeviceAddress myAddress = null;
56 private IoTDeviceAddress controlAddress = null;
57 private IoTDeviceAddress timingAddress = null;
58 private IoTDeviceAddress serverAddress = null;
60 private IoTTCP iHomeTCPConnection = null;
62 private AtomicBoolean driverIsShuttingDown = new AtomicBoolean();
63 private boolean didClose = false;
65 private AtomicBoolean didEnd = new AtomicBoolean();
66 private AtomicBoolean playbackStarted = new AtomicBoolean();
67 private AtomicBoolean playbackFileIsDone = new AtomicBoolean();
68 private AtomicBoolean isDoneEnding = new AtomicBoolean();
69 private AtomicBoolean playbackState = new AtomicBoolean();
71 private AtomicBoolean didInit = new AtomicBoolean();
72 private AtomicBoolean playbackAboutToStart = new AtomicBoolean();
73 private AtomicBoolean settingVolume = new AtomicBoolean();
74 private AtomicBoolean playbackAboutToStop = new AtomicBoolean();
78 private long sequenceNumber = SEQUENCE_NUMBER_INITIAL_VALUE;
79 private long rtpTimestamp = RTP_TIMESTAMP_INITIAL_VALUE;
81 private long currentPlaybackTime = 0;
82 static Semaphore currentPlaybackTimeMutex = new Semaphore(1);
84 private long desiredPlaybackTime = 0;
85 static Semaphore desiredPlaybackTimeMutex = new Semaphore(1);
88 private String connectionURL = "";
89 private float currentVolume = DEFAULT_VOLUME;
90 private LinkedList audioLinkedList = new LinkedList();
92 private List < SpeakerSmartCallback > callbackList = new CopyOnWriteArrayList< SpeakerSmartCallback > ();
94 /*******************************************************************************************************************************************
96 *******************************************************************************************************************************************/
97 private Thread timingThread = null;
98 private Thread audioThread = null;
99 private Thread controlThread = null;
100 private Thread monitorThread = null;
103 @config private IoTSet<IoTDeviceAddress> speakerAddresses;
107 playbackAboutToStart.set(false);
108 settingVolume.set(false);
111 /*******************************************************************************************************************************************
113 ** Speaker Interface Methods
115 *******************************************************************************************************************************************/
119 if (didInit.compareAndSet(false, true) == false) {
120 return; // already init
124 isDoneEnding.set(true);
125 playbackFileIsDone.set(false);
126 Map<String, Integer> addrCount = new HashMap<String, Integer>();
129 // get correct addresses
130 for (IoTDeviceAddress devAdrr : speakerAddresses.values()) {
131 if (addrCount.containsKey(devAdrr.getAddress())) {
132 addrCount.put(devAdrr.getAddress(), addrCount.get(devAdrr.getAddress()) + 1);
134 addrCount.put(devAdrr.getAddress(), 1);
138 for (IoTDeviceAddress devAdrr : speakerAddresses.values()) {
139 if (addrCount.get(devAdrr.getAddress()) <= 1) {
142 if (devAdrr.getIsDstPortWildcard()) {
143 if (controlAddress == null) {
144 controlAddress = devAdrr;
145 } else if (timingAddress == null) {
146 timingAddress = devAdrr;
148 serverAddress = devAdrr;
151 tcpAddress = devAdrr;
156 System.out.println("tcpAddress: " + tcpAddress.getAddress() + ":" + tcpAddress.getSourcePortNumber() +
157 ":" + tcpAddress.getDestinationPortNumber());
158 System.out.println("myAddress: " + myAddress.getAddress() + ":" + myAddress.getSourcePortNumber() +
159 ":" + myAddress.getDestinationPortNumber());
160 System.out.println("controlAddress: " + controlAddress.getAddress() + ":" + controlAddress.getSourcePortNumber() +
161 ":" + controlAddress.getDestinationPortNumber());
162 System.out.println("timingAddress: " + timingAddress.getAddress() + ":" + timingAddress.getSourcePortNumber() +
163 ":" + timingAddress.getDestinationPortNumber());
164 System.out.println("serverAddress: " + serverAddress.getAddress() + ":" + serverAddress.getSourcePortNumber() +
165 ":" + serverAddress.getDestinationPortNumber());
167 // Launch the worker function in a separate thread.
168 monitorThread = new Thread(new Runnable() {
170 monitorThreadWorker();
173 monitorThread.start();
178 public boolean startPlayback() {
181 if (playbackAboutToStart.compareAndSet(false, true) == false) {
185 if (playbackStarted.get()) {
189 if (isDoneEnding.get() == false) {
193 // Reset all Parameters
195 playbackFileIsDone.set(false);
196 playbackState.set(true);
199 currentPlaybackTimeMutex.acquire();
200 currentPlaybackTime = 0;
201 } catch (Exception e) {
204 currentPlaybackTimeMutex.release();
209 desiredPlaybackTimeMutex.acquire();
210 desiredPlaybackTime = 0;
211 } catch (Exception e) {
214 desiredPlaybackTimeMutex.release();
216 sequenceNumber = SEQUENCE_NUMBER_INITIAL_VALUE;
217 rtpTimestamp = RTP_TIMESTAMP_INITIAL_VALUE;
220 // start TCP connection
221 iHomeTCPConnection = new IoTTCP(tcpAddress);
222 iHomeTCPConnection.setReuseAddress(true);
224 // Get in and out communication
225 PrintWriter tcpOut = new PrintWriter(iHomeTCPConnection.getOutputStream(), true);
226 BufferedReader tcpIn = new BufferedReader(new InputStreamReader(iHomeTCPConnection.getInputStream()));
229 String session = String.valueOf(SOURCE_ID);
230 connectionURL = "rtsp://" + myAddress.getAddress() + "/" + session;
232 // Construct The commands
233 String optionsCommand = "OPTIONS * RTSP/1.0\r\n" +
235 "User-Agent: iTunes/11.0.4 (Windows; N)\r\n" +
236 "Client-Instance: c0cb804fd20e80f6\r\n" +
237 "Apple-Challenge: i8j36XRYVmSZs9nZ7Kf0Cg\r\n\r\n";
239 String announceCommandBody = "v=0\r\n" +
240 "o=iTunes " + session + " 0 IN IP4 " + myAddress.getAddress() + "\r\n" +
242 "c=IN IP4 " + tcpAddress.getAddress() + "\r\n" +
244 "m=audio 0 RTP/AVP 96\r\n" +
245 "a=rtpmap:96 AppleLossless\r\n" +
246 "a=fmtp:96 352 0 16 40 10 14 2 255 0 0 44100\r\n";
248 String announceCommand = "ANNOUNCE " + connectionURL + " RTSP/1.0\r\n" +
250 "Content-Type: application/sdp\r\n" +
251 "Content-Length: " + announceCommandBody.length() + "\r\n" +
252 "User-Agent: iTunes/11.0.4 (Windows; N)\r\n\r\n" +
256 // get the ports that we are going to tell the iHome to use
257 int ourControlPort = controlAddress.getSourcePortNumber();
258 int ourTimingPort = timingAddress.getSourcePortNumber();
260 String setupCommand = "SETUP " + connectionURL + " RTSP/1.0\r\n" +
262 "Transport: RTP/AVP/UDP;unicast;interleaved=0-1;mode=record;control_port=" + Integer.toString(ourControlPort) + ";timing_port=" + Integer.toString(ourTimingPort) + "\r\n" +
263 "User-Agent: iTunes/11.0.4 (Windows; N)\r\n\r\n";
265 String recordCommand = "RECORD " + connectionURL + " RTSP/1.0\r\nCSeq: 3\r\nSession: 1\r\nRange: npt=0-\r\nRTP-Info: seq=" + sequenceNumber + ";rtptime=" + rtpTimestamp + "\r\nUser-Agent: iTunes/11.0.4 (Windows; N)\r\n\r\n";
269 tcpOut.print(optionsCommand);
271 while (!tcpIn.ready()) {
273 while (tcpIn.ready()) {
274 String answer = tcpIn.readLine();
275 System.out.println(answer);
279 tcpOut.print(announceCommand);
282 while (!tcpIn.ready()) {
284 while (tcpIn.ready()) {
285 String answer = tcpIn.readLine();
286 System.out.println(answer);
290 tcpOut.print(setupCommand);
292 while (!tcpIn.ready()) {
295 // ports that the speaker told us to communicate over
297 int controlPort = -1;
300 while (tcpIn.ready()) {
301 String answer = tcpIn.readLine();
302 System.out.println(answer);
304 if (answer.contains("Transport")) {
306 String[] splitString = answer.split(";");
308 for (String str : splitString) {
309 String[] keyValue = str.split("=");
311 if (keyValue.length == 2) {
312 if (keyValue[0].equals("server_port")) {
313 serverPort = Integer.parseInt(keyValue[1]);
315 } else if (keyValue[0].equals("control_port")) {
316 controlPort = Integer.parseInt(keyValue[1]);
318 } else if (keyValue[0].equals("timing_port")) {
319 timingPort = Integer.parseInt(keyValue[1]);
328 serverAddress.setDstPort(serverPort);
329 controlAddress.setDstPort(controlPort);
330 timingAddress.setDstPort(timingPort);
332 // Launch the worker function in a separate thread.
333 // Must launch timing thread before record message since record message
335 timingThread = new Thread(new Runnable() {
337 timingWorkerFunction();
340 timingThread.start();
343 // give the timing thread some time to set itself up
346 tcpOut.print(recordCommand);
348 while (!tcpIn.ready()) {
350 while (tcpIn.ready()) {
351 String answer = tcpIn.readLine();
352 System.out.println(answer);
358 // Launch the worker function in a separate thread.
359 controlThread = new Thread(new Runnable() {
361 controlWorkerFunction();
364 controlThread.start();
367 playbackFileIsDone.set(false);
369 // wait for audio Data
372 // Launch the worker function in a separate thread.
373 audioThread = new Thread(new Runnable() {
375 audioWorkerFunction();
383 // playback has officially Started
384 playbackStarted.set(true);
387 playbackAboutToStart.set(true);
389 // Set the volume to the current volume
390 setVolume(currentVolume);
392 } catch (Exception e) {
400 public boolean stopPlayback() {
402 if (playbackAboutToStop.compareAndSet(false, true) == false) {
406 isDoneEnding.set(false);
407 playbackState.set(false);
408 if (playbackStarted.get() == false) {
412 playbackStarted.set(false);
418 controlThread.join();
419 } catch (Exception e) {
423 isDoneEnding.set(true);
426 String teardownCommand = "TEARDOWN " + connectionURL + " RTSP/1.0\r\n" +
429 "User-Agent: iTunes/11.0.4 (Windows; N)\r\n\r\n";
434 // Get in and out communication
435 PrintWriter tcpOut = new PrintWriter(iHomeTCPConnection.getOutputStream(), true);
436 BufferedReader tcpIn = new BufferedReader(new InputStreamReader(iHomeTCPConnection.getInputStream()));
438 tcpOut.print(teardownCommand);
440 while (!tcpIn.ready()) {
442 while (tcpIn.ready()) {
443 String answer = tcpIn.readLine();
444 System.out.println(answer);
447 // close the connection
448 iHomeTCPConnection.close();
450 } catch (Exception e) {
454 playbackAboutToStop.set(false);
460 public boolean getPlaybackState() {
461 return playbackState.get();
464 public boolean setVolume(float _percent) {
466 if (settingVolume.compareAndSet(false, true) == false) {
470 // keep in range of percentage
473 } else if (_percent > 100) {
477 // cant set the volume if there is no playback
478 if (playbackStarted.get() == false) {
482 // convert the volume from a percentage to a db
486 dbVolume = ((float)(_percent / 100.0) * (float)(VOLUME_MAX_VALUE_DB - VOLUME_MIN_VALUE_DB)) + (float)VOLUME_MIN_VALUE_DB;
488 // cap the volume to a level that the speaker supports
489 if (dbVolume > VOLUME_MAX_VALUE_DB) {
490 dbVolume = VOLUME_MAX_VALUE_DB;
494 // construct the command
495 String body = "volume: " + String.format("%f", dbVolume) + "\r\n";
496 String volumeCommand = "SET_PARAMETER " + connectionURL + " RTSP/1.0\r\nCSeq: 4\r\nSession: 1\r\nContent-Type: text/parameters\r\nContent-Length: " + body.length() + "\r\nUser-Agent: iTunes/11.0.4 (Windows; N)\r\n\r\n" + body;
501 // Get in and out communication
502 PrintWriter tcpOut = new PrintWriter(iHomeTCPConnection.getOutputStream(), true);
503 BufferedReader tcpIn = new BufferedReader(new InputStreamReader(iHomeTCPConnection.getInputStream()));
506 tcpOut.print(volumeCommand);
509 // Wait for data to come back
510 while (!tcpIn.ready()) {
513 // read the data from the iHome
514 while (tcpIn.ready()) {
515 String answer = tcpIn.readLine();
516 System.out.println(answer);
519 // update the current volume parameter
520 currentVolume = _percent;
521 } catch (Exception e) {
525 settingVolume.set(false);
530 public float getVolume() {
532 while (settingVolume.get()) {
533 // block until volume set is done
535 return currentVolume;
538 public void loadData(short[] _samples, int _offs, int _len) {
540 short[] sample = new short[_len];
542 for (int i = 0; i < _len; i++, j++) {
543 sample[i] = _samples[j];
545 synchronized (audioLinkedList) {
546 audioLinkedList.addLast(sample);
550 public void clearData() {
551 synchronized (audioLinkedList) {
552 audioLinkedList.clear();
556 public int getPosition() {
559 currentPlaybackTimeMutex.acquire();
560 pTime = currentPlaybackTime;
561 } catch (Exception e) {
564 currentPlaybackTimeMutex.release();
566 int mSecPos = (int)((pTime * 1000) / 44100);
570 public void setPosition(int _mSec) {
571 int sampleNumber = (_mSec * 44100) / 1000;
574 desiredPlaybackTimeMutex.acquire();
575 desiredPlaybackTime = sampleNumber;
576 } catch (Exception e) {
579 desiredPlaybackTimeMutex.release();
583 public void registerCallback(SpeakerSmartCallback _cb) {
584 callbackList.add(_cb);
588 /*******************************************************************************************************************************************
592 *******************************************************************************************************************************************/
595 private void timingWorkerFunction() {
597 IoTUDP timingUDP = new IoTUDP(timingAddress);
599 byte[] receiveData = new byte[1024];
600 byte[] sendData = new byte[32];
602 while (didEnd.get() == false) {
604 receiveData = timingUDP.recieveData(receiveData.length);
606 long nanotime = nanoTime();
607 int seconds = (int)((nanotime / 1000000000) & 0xffffffff);
608 long fractions = ((( nanotime % 1000000000) * (0xffffffffL)) / 1000000000);
610 sendData[0] = (byte)0x80; // Header bit field
611 sendData[1] = (byte)0xd3; // mark bit and message payload number
613 sendData[2] = (byte) 0x00;
614 sendData[3] = (byte) 0x07;
616 sendData[4] = (byte) 0x00;
617 sendData[5] = (byte) 0x00;
618 sendData[6] = (byte) 0x00;
619 sendData[7] = (byte) 0x00;
622 sendData[8] = receiveData[24];
623 sendData[9] = receiveData[25];
624 sendData[10] = receiveData[26];
625 sendData[11] = receiveData[27];
626 sendData[12] = receiveData[28];
627 sendData[13] = receiveData[29];
628 sendData[14] = receiveData[30];
629 sendData[15] = receiveData[31];
631 // arrival time-stamp
632 sendData[16] = (byte)((seconds >> 24) & 0xff);
633 sendData[17] = (byte)((seconds >> 16) & 0xff);
634 sendData[18] = (byte)((seconds >> 8) & 0xff);
635 sendData[19] = (byte)((seconds >> 0) & 0xff);
636 sendData[20] = (byte)((fractions >> 24) & 0xff);
637 sendData[21] = (byte)((fractions >> 16) & 0xff);
638 sendData[22] = (byte)((fractions >> 8) & 0xff);
639 sendData[23] = (byte)((fractions >> 0) & 0xff);
642 nanotime = nanoTime();
643 seconds = (int)( nanotime / 1000000000);
644 fractions = ((( nanotime % 1000000000) * (0xffffffffL)) / 1000000000);
646 // transmit time-stamp
647 sendData[24] = (byte)((seconds >> 24) & 0xff);
648 sendData[25] = (byte)((seconds >> 16) & 0xff);
649 sendData[26] = (byte)((seconds >> 8) & 0xff);
650 sendData[27] = (byte)((seconds >> 0) & 0xff);
651 sendData[28] = (byte)((fractions >> 24) & 0xff);
652 sendData[29] = (byte)((fractions >> 16) & 0xff);
653 sendData[30] = (byte)((fractions >> 8) & 0xff);
654 sendData[31] = (byte)((fractions >> 0) & 0xff);
657 timingUDP.sendData(sendData);
659 } catch (Exception e) {
664 private void controlWorkerFunction() {
668 IoTUDP controlUDP = new IoTUDP(controlAddress);
669 controlUDP.setSoTimeout(1);
670 byte[] sendData = new byte[20];
671 boolean first = true;
674 while (didEnd.get() == false) {
677 byte[] receiveData = new byte[24];
678 receiveData = controlUDP.recieveData(receiveData.length);
680 // System.out.println("Control Packet Arrived");
681 // String packetData = bytesToHex(receiveData);
682 // System.out.println(packetData);
684 } catch (Exception e) {
685 // e.printStackTrace();
689 long rtpTimestampCopy = rtpTimestamp;
690 long nanotime = nanoTime();
691 int seconds = (int)( nanotime / 1000000000);
692 long fractions = (( nanotime % 1000000000) * (0xffffffffL)) / 1000000000;
696 sendData[0] = (byte)0x90; // Header bit field
699 sendData[0] = (byte)0x80; // Header bit field
703 sendData[1] = (byte)0xd4; // mark bit and message payload number
704 sendData[2] = (byte)0x00;
705 sendData[3] = (byte)0x07;
707 // time-stamp of packet
708 sendData[4] = (byte)((rtpTimestampCopy >> 24) & 0xFF);
709 sendData[5] = (byte)((rtpTimestampCopy >> 16) & 0xFF);
710 sendData[6] = (byte)((rtpTimestampCopy >> 8) & 0xFF);
711 sendData[7] = (byte)((rtpTimestampCopy >> 0) & 0xFF);
714 sendData[8] = (byte)((seconds >> 24) & 0xff);
715 sendData[9] = (byte)((seconds >> 16) & 0xff);
716 sendData[10] = (byte)((seconds >> 8) & 0xff);
717 sendData[11] = (byte)((seconds >> 0) & 0xff);
719 sendData[12] = (byte)((fractions >> 24) & 0xff);
720 sendData[13] = (byte)((fractions >> 16) & 0xff);
721 sendData[14] = (byte)((fractions >> 8) & 0xff);
722 sendData[15] = (byte)((fractions >> 0) & 0xff);
724 rtpTimestampCopy += 88200;
725 sendData[16] = (byte)((rtpTimestampCopy >> 24) & 0xFF);
726 sendData[17] = (byte)((rtpTimestampCopy >> 16) & 0xFF);
727 sendData[18] = (byte)((rtpTimestampCopy >> 8) & 0xFF);
728 sendData[19] = (byte)((rtpTimestampCopy >> 0) & 0xFF);
731 controlUDP.sendData(sendData);
733 // System.out.println("---------------------------------------------");
734 // System.out.println("Sending Control Sync");
735 // System.out.println("---------------------------------------------");
739 } catch (Exception e) {
744 private void audioWorkerFunction() {
747 IoTUDP serverUDP = new IoTUDP(serverAddress);
749 // current frame being played
750 long frameCounter = 0;
752 // used for bit packing for audio stream
753 short[] array = null;
756 int noAudioCount = 0;
758 while (didEnd.get() == false) {
760 byte[] sendData = new byte[352 * 4 + 19];
762 sendData[0] = (byte)0x80;
764 if (frameCounter == 0) {
765 sendData[1] = (byte)0xe0;
768 sendData[1] = (byte)0x60;
771 sendData[2] = (byte)((sequenceNumber >> 8) & 0xFF);
772 sendData[3] = (byte)((sequenceNumber >> 0) & 0xFF);
774 long rtpTmp = rtpTimestamp;
776 sendData[4] = (byte)((rtpTmp >> 24) & 0xFF);
777 sendData[5] = (byte)((rtpTmp >> 16) & 0xFF);
778 sendData[6] = (byte)((rtpTmp >> 8) & 0xFF);
779 sendData[7] = (byte)((rtpTmp >> 0) & 0xFF);
781 sendData[8] = (byte)((SEQUENCE_ID >> 24) & 0xFF);
782 sendData[9] = (byte)((SEQUENCE_ID >> 16) & 0xFF);
783 sendData[10] = (byte)((SEQUENCE_ID >> 8) & 0xFF);
784 sendData[11] = (byte)((SEQUENCE_ID >> 0) & 0xFF);
786 sendData[12] = (byte) 0x20;
787 sendData[13] = (byte) 0x00;
788 sendData[14] = (byte) 0x12;
789 sendData[15] = (byte) 0x00;
790 sendData[16] = (byte) 0x00;
791 sendData[17] = (byte) 0x02;
792 sendData[18] = (byte) 0xc0;
794 for (int i = 19; i < sendData.length; i += 4) {
795 if (array != null && (offset + 1) >= array.length) {
802 synchronized (audioLinkedList) {
803 array = (short[])audioLinkedList.poll();
813 desiredPlaybackTimeMutex.acquire();
814 time1 = desiredPlaybackTime;
815 } catch (Exception e) {
819 desiredPlaybackTimeMutex.release();
823 currentPlaybackTimeMutex.acquire();
824 time2 = currentPlaybackTime;
825 } catch (Exception e) {
828 currentPlaybackTimeMutex.release();
831 while ((time2 < time1)) {
835 currentPlaybackTimeMutex.acquire();
836 currentPlaybackTime++;
837 } catch (Exception e) {
840 currentPlaybackTimeMutex.release();
843 if ((offset + 1) >= array.length) {
845 synchronized (audioLinkedList) {
846 array = (short[])audioLinkedList.poll();
864 currentPlaybackTimeMutex.acquire();
865 currentPlaybackTime++;
866 } catch (Exception e) {
869 currentPlaybackTimeMutex.release();
873 desiredPlaybackTimeMutex.acquire();
874 desiredPlaybackTime++;
875 } catch (Exception e) {
879 desiredPlaybackTimeMutex.release();
885 if (noAudioCount > 10) {
887 if (playbackFileIsDone.get() == false) {
888 playbackFileIsDone.set(true);
893 sendData[i - 1] |= (byte)((l >> 15) & 1);
894 sendData[i] = (byte)((l >> 7) & 0xff);
895 sendData[i + 1] = (byte)(((l << 1) & 0xfe) | ((r >> 15) & 1));
896 sendData[i + 2] = (byte)((r >> 7) & 0xff);
897 sendData[i + 3] = (byte)((r << 1) & 0xfe);
902 sequenceNumber = sequenceNumber % SEQUENCE_NUMBER_WRAP_AROUND;
903 rtpTimestamp += RTP_TIMESTAMP_INCREMENT_VALUE;
906 serverUDP.sendData(sendData);
909 // need to sleep for a bit
910 if ((frameCounter % 2) == 0) {
917 } catch (Exception e) {
922 private void monitorThreadWorker() {
923 while (driverIsShuttingDown.get() == false) {
924 if (playbackFileIsDone.get()) {
926 playbackFileIsDone.set(false);
928 for (SpeakerSmartCallback c : callbackList) {
930 //c.speakerDone(this);
932 } catch (Exception e) {
940 private void endDriver() {
943 driverIsShuttingDown.set(true);
945 monitorThread.join();
946 } catch (Exception e) {
954 * close() called by the garbage collector right before trashing object
956 public void finalize() {
962 private static long nanoTime() {
963 long nanotime = System.nanoTime();