Source code: org/mitre/cvw/AVController.java
1 /*
2 * Copyright (c) 1996-2000. The MITRE Corporation (http://www.mitre.org/).
3 * All rights reserved.
4 * CVW comes with ABSOLUTELY NO WARRANTY. See license for details.
5 */
6
7 package org.mitre.cvw;
8
9 import java.io.FileNotFoundException;
10
11 /**
12 * CVW Audio and Video Tools controller.
13 *
14 * @author Stephen Jones, The MITRE Corporation
15 * @version 1
16 */
17
18 /* This is a first cut at removing the audio and video variables/functions from
19 CVWCoordinator. Only one AVController should exist, and it should be the gateway for
20 anything done with the audio/video tools (vic and vat right now). Will try to make this
21 generic enough to plug in other tools if possible. Still have a lot of references to
22 variables in CVWCoordinator!
23 */
24
25 public class AVController extends Object {
26 // instance variable
27 private static AVController _instance = null;
28 private static int count = 0;
29
30 //variables from CVWCoordinator
31 String mcastAddress = null;
32 String audioProgram = null;
33 Process audioHandle = null;
34 String audioPort = "3456";
35 String videoProgram = null;
36 Process videoHandle = null;
37 String videoPort = "3454";
38 CVWRoom currentRoom;
39 CVWUser currentUser;
40 CVWPreferences cvwPrefs;
41 CVWCoordinator mainFrame;
42 NPDocServer npds;
43 String avFileName = null;
44
45 /**
46 * Get the current AVController instance or create one if necessary.
47 *
48 * @return object instance
49 */
50 public static AVController getInstance() {
51 if (_instance == null) {
52 _instance = new AVController();
53 }
54 return _instance;
55 }
56
57 /* 9/30/96 dage - play the error audio clip
58 * 10/21/96 dage - use code base not doc base
59 * 6/25/98 srj - now an application. use new AudioFilePlayer
60 * 12/31/98 dage - moved from CVWCoordinator
61 */
62 /**
63 * Play an audio sound using AudioFilePlayer
64 * @param fileName relative to the executable directory, so create afp with full path
65 */
66 public static void playAudio(String fileName) {
67 String sep = System.getProperty("file.separator");
68 // fileName is relative to the executable directory, so create afp with full path
69 String dirName = NPDocServer.getSystemDir();
70 fileName = dirName + sep + fileName;
71 fileName = fileName.replace('/', sep.charAt(0));
72
73 try {
74 AudioFilePlayer afp = new AudioFilePlayer(fileName);
75 if(afp != null)
76 afp.play();
77 else
78 System.err.println("afp could not play");
79 }
80 catch (FileNotFoundException fnfe) {
81 System.err.println(fnfe.getMessage() + ": " + fileName);
82 }
83 catch (Exception e) {
84 System.err.println(e.getMessage() + ": " + fileName);
85 }
86 }
87
88 /**
89 * Constructor
90 */
91 protected AVController() {
92 // initialize the classes we need.
93 mainFrame = CVWCoordinator.getInstance();
94 npds = NPDocServer.getInstance();
95 }
96
97 /**
98 * Sets the preferences
99 *
100 * @param prefs current user preferences
101 */
102 public void setPrefs(CVWPreferences prefs) {
103 cvwPrefs = prefs;
104 }
105
106 /* methods from CVWCoordinator */
107
108 /**************************************************************
109 ******************* audio routines *************************
110 **************************************************************/
111 /**
112 * Checks to see if the audio tool (vat) is running.
113 *
114 * @return <code>true</code> if already started, else <code>false</code>
115 */
116 public boolean audioActive() {
117 return (audioHandle != null);
118 }
119
120 /**
121 * Will stop the audio tool and reset the toolbar button and menu to reflect that audio is not active
122 */
123 public void stopAudio(){
124 mainFrame.audioVisualOn(false);
125 if(audioHandle != null) {
126 int result = npds.terminateProgram(audioHandle);
127 if(result==0) { // error from terminateProgram
128 if(CVWCoordinator.DEBUG) System.err.println("GAB - audio has already been shut down by user. " + result);
129 }
130 audioHandle = null;
131 }
132 }
133
134 /* 1/28/98 dage - audio uses mcastAddress and uses port 3456
135 * 11/6/98 dage - use currentRoom and currentUser .name incase the user/room name
136 * changes
137 */
138 /**
139 * Starts the audio tool (vat) with a multicast address
140 *
141 * @return
142 */
143 public Process startAudio() {
144 return startAudio(true, mainFrame.currentRoom.name, mainFrame.currentUser.name, mcastAddress, audioPort);
145 }
146
147 /**
148 * Starts the audio tool (vat) point-to-point
149 *
150 * @param phoneConv The phone conversion that is implemented with the audio tool
151 * @return The process id of the audio tool
152 */
153 public Process startAudio(PhoneConversation phoneConv) {
154 return startAudio(false, "Private Phone Conversation",
155 mainFrame.currentUser.name, phoneConv.getPhoneLoc(), phoneConv.getPort());
156 }
157
158 /* 4/10/98 dage - combined the checkAV and checkAudio methods as part of
159 * the plugin re-organization
160 * 6/30/98 dage -pt to pt uses port 3458, room uses port 3456
161 * 8/27/98 dage - the port is sent from server via mcp
162 */
163 /**
164 * Starts the audio tool (vat)
165 *
166 * @param room <code>true</code>if starting tool for a room (multicast).
167 * <code>false</code>if starting tool for unicast (point-to-point)
168 * @param title The title to place in the window.
169 * @param user The user name to use in the tool.
170 * @param ipAddress The IP address the tool will connect to.
171 * @param port The port number the tool will connect to.
172 * @return The process id of the audio tool.
173 */
174 public Process startAudio(boolean room, String title, String user, String ipAddress, String port) {
175 if(!checkAudio()) {
176 if (room) mainFrame.audioVisualOn(false);
177 return null;
178 }
179
180 String ttlStr;
181 if (room) {
182 mainFrame.audioVisualOn(true);
183 ttlStr = cvwPrefs.getAudioTTLMulticast();
184 } else
185 ttlStr = cvwPrefs.getAudioTTLUnicast();
186
187 String rtp = "-n";
188 if (cvwPrefs.useAudioRTP())
189 rtp = "-r";
190
191 String speaker = "-s";
192 if (!cvwPrefs.useAudioSpeakers())
193 speaker = "-j"; //headset
194
195 String tclFlag = "-u";
196 String vatWrap = getTCLFile("vat.tcl");
197 if (vatWrap == null) {
198 tclFlag = "";
199 vatWrap = "";
200 }
201
202 String program[] = {audioProgram,
203 rtp,
204 "-C", "\"" + title + "\"",
205 "-N", user,
206 "-f", cvwPrefs.getAudioEncodingString(),
207 "-t", ttlStr,
208 "-g", cvwPrefs.getAudioGeometry(),
209 "-X", "speakerGain="+cvwPrefs.getAudioSpeakerVol(),
210 "-X", "mikeGain="+cvwPrefs.getAudioMicVol(),
211 speaker,
212 tclFlag, vatWrap,
213 ipAddress+"/"+port};
214
215 if (CVWCoordinator.DEBUG) {
216 String tmp = audioProgram;
217 for (int i = 1; i < program.length;i++)
218 tmp += " " + program[i];
219 System.err.println(program.length+"run aud" + tmp);
220 }
221
222 Process aHandle;
223 mainFrame.setSystemMsg("Launching audio...", true);
224 aHandle = npds.executeProgram(program);
225 if (room)
226 audioHandle = aHandle;
227 if(CVWCoordinator.DEBUG) System.err.println("GAB - Got to execute program " + audioHandle);
228 return aHandle;
229 }
230
231 /* 7/17/97 dage - check for program each time but only setup var the
232 * first time
233 * 4/7/98 dage - move checkAV call to this method.
234 * 4/10/98 dage - if checkAV returns true then SysUtils.getInstance() is
235 * guarenteed to return non null.
236 */
237 /**
238 * Checks the existence of an audio tool.
239 *
240 * @return <code>true</code> if the tool is found, else <code>false</code>
241 */
242 public boolean checkAudio() {
243 if (!checkAV()) {
244 return false;
245 }
246
247 if (audioProgram == null) {
248 audioProgram = NPDocServer.getProgramDir("vat") + "vat.exe";
249 }
250 boolean exists = npds.fileExists(audioProgram);
251 if (!exists) {
252 mainFrame.displayError("VAT could not be found. Please make sure vat.exe is installed in the modules directory.", "Audio Program not Found");
253 }
254 return exists;
255 }
256
257 /**************************************************************
258 ******************* video routines *************************
259 **************************************************************/
260 /**
261 * Stops the video tool and reset the toolbar button and menu to reflect that audio is not active
262 */
263 public void stopVideo(){
264 mainFrame.videoVisualOn(false);
265 if(videoHandle != null) {
266 int result = npds.terminateProgram(videoHandle);
267 if(result==0) { //error from terminateProgram
268 }
269 videoHandle = null;
270 }
271 }
272
273 /**
274 * Check to see if video tool (vic) is active
275 *
276 * @return <code>true</code> if the tool is running, else <code>false</code>
277 */
278 public boolean videoActive() {
279 return (videoHandle != null);
280 }
281
282 /* 1/28/98 dage - video mcast address is audio mcast address + 1 and uses
283 * port 3454
284 * 4/10/98 dage - combined the checkAV and checkVideo methods as part of
285 * the plugin re-organization
286 */
287 /**
288 * Starts video tool after some initial checks.
289 */
290 public void startVideo(){
291
292 if(!checkVideo()) {
293 mainFrame.videoVisualOn(false);
294 return;
295 }
296
297 String videoAddress = getVideoMCastAddress();
298 if (videoAddress == null) {
299 mainFrame.videoVisualOn(false);
300 return;
301 }
302
303 String tclFlag = "-u";
304 String vicWrap = getTCLFile("vic.tcl");
305 if (vicWrap == null) {
306 tclFlag = "";
307 vicWrap = "";
308 }
309
310
311 String program[] = {videoProgram,
312 "-C", "\"" + mainFrame.roomName + "\"",
313 "-N", mainFrame.currentUser.name,
314 "-B", String.valueOf(cvwPrefs.getVideoMaxBandwidth()),
315 "-X", "bandwidth="+cvwPrefs.getVideoBandwidth(),
316 "-X", "framerate="+cvwPrefs.getVideoFrameRate(),
317 "-X", "geometry="+cvwPrefs.getVideoGeometry(),
318 "-f", cvwPrefs.getVideoEncodingString(),
319 "-t", cvwPrefs.getVideoTTLMulticast(),
320 "-X", "privacyEncoderDefault=Default",
321 tclFlag, vicWrap,
322 videoAddress + "/" + videoPort};
323
324 if (CVWCoordinator.DEBUG) {
325 String tmp = videoProgram;
326 for (int i = 1; i < program.length;i++)
327 tmp += " " + program[i];
328 System.err.println(program.length+"run vid" + tmp);
329 }
330
331 //start up video
332 CVWCoordinator.getInstance().setSystemMsg("Launching video...", true);
333 mainFrame.videoVisualOn(true); //videoMenuItem.setState(true);
334 videoHandle = npds.executeProgram(program);
335 if(CVWCoordinator.DEBUG) System.err.println("GAB - Got to execute program " + videoHandle);
336 }
337
338 /* 7/17/97 dage - check for program each time but only setup var the
339 * first time
340 * 4/7/98 dage - move checkAV call to this method.
341 * 4/10/98 dage - if checkAV returns true then SysUtils.getInstance() is
342 * guarenteed to return non null.
343 */
344 /**
345 * Checks for the existence of a video tool (vic)
346 *
347 * @return <code>true</code> if the program is found, else <code>false</code>
348 */
349 public boolean checkVideo() {
350 if (!checkAV()) {
351 return false;
352 }
353
354 if (videoProgram == null) {
355 videoProgram = NPDocServer.getProgramDir("vic") + "vic.exe";
356 }
357
358 boolean exists = npds.fileExists(videoProgram);
359
360 if (!exists) {
361 mainFrame.displayError("VIC could not be found. Please make sure vic.exe is installed in the modules directory.");
362 }
363 return exists;
364 }
365
366 /* 1/28/98 - dage parse mcast addressa and add 1 to the last octel
367 * video address is audio + 1
368 */
369 /**
370 * Determines the video multicast address.
371 * The address is calculated by adding 1 to the last octet of the audio multicast address
372 *
373 * @return The video multicast address
374 */
375 public String getVideoMCastAddress() {
376 try {
377 int ind = mcastAddress.lastIndexOf(".");
378 if (ind == -1) return null;
379 ind++; //index after .
380 String lastStr = mcastAddress.substring(ind);
381
382 int end = Integer.parseInt(lastStr);
383 end++;
384 String newAddr = mcastAddress.substring(0,ind);
385 newAddr = newAddr + end;
386 return newAddr;
387 } catch (Exception e) {
388 System.err.println(e.toString());
389 return null;
390 }
391 }
392
393 /* 8/27/98 dage - set max video bandwidth from server
394 */
395 /**
396 * Sets the video bandwidth if set in preferences
397 *
398 * @param max The value to set the bandwidth to.
399 */
400 public void setVideoMaxBandwidth(String max) {
401 if (cvwPrefs != null)
402 cvwPrefs.setVideoMaxBandwidth(max);
403 }
404
405 /*************************************************************
406 ******************* end av routines ************************
407 *************************************************************/
408
409 /**************************************************************
410 ******************* audio/video routines ********************
411 **************************************************************/
412
413 public void logout() {
414 stopAudio();
415 stopVideo();
416 String path = getServerAVFileName();
417 npds.removeFile(path);
418 avFileName = null;
419 }
420
421
422 /* 7/17/97 dage - general checks to plugin/mcast for both audio/video
423 * 4/10/98 dage - moved the check for plugin into sysUtilsAvailable method
424 */
425 /**
426 * Preliminary checks to see if multicast tools can be run.
427 *
428 * @return <code>true</code> if checks are ok, else <code>false</code>
429 */
430 public boolean checkAV(){
431
432 if (!mainFrame.sysUtilsAvailable())
433 return false;
434
435 if(mcastAddress == null) {
436 mainFrame.displayError("Login is not yet complete. Try again when it is.");
437 return false;
438 }
439
440 return true;
441 }
442
443 /* 2/16/98 dage - move setting of mcast address here and starting stoping AV apps.
444 * set cursor to wait
445 */
446 /**
447 * Sets the multicast address parameters
448 *
449 * @param mcast The multicast address.
450 * @param aPort The audio port.
451 * @param vPort The video port.
452 * @param rONum Room number. (not used)
453 */
454 public void setAV(String mcast, String aPort, String vPort, String rONum) {
455 audioPort = aPort;
456 videoPort = vPort;
457 setAV(mcast);
458 exportAV(rONum);
459 }
460
461 /**
462 * Writes the server, current user, room, and av info to a file external to CVW.
463 * <br> MCP receive cvw-av-mcast-address
464 */
465 public void exportAV(String room) {
466 String text = "mcastAddress="+mcastAddress+"\n";
467 text += "audioPort="+ audioPort + "\n";
468 text += "videoPort="+videoPort+"\n";
469 String rName = room;
470 CVWObject rObj = CVWCache.getInstance().get(room);
471 if (rObj != null)
472 rName = rObj.name;
473 text += "roomName="+rName+"\n";
474 text += "userName="+mainFrame.getCurrentUser().name+"\n";
475
476 CVWServerComm serverComm = CVWServerComm.getInstance();
477 text += "CVWServerHostName="+serverComm.getServerHost()+"\n";
478 text += "CVWServerHostPort="+serverComm.getServerPort()+"\n";
479 text += "CVWServerName="+serverComm.getServerName()+"\n";
480
481 text += "DocServer="+mainFrame.DSserver+"\n";
482 text += "DocPort="+mainFrame.DSport+"\n";
483 String fileName = getServerAVFileName();
484 npds.saveTextFile(fileName, text );
485 }
486
487 /**
488 * Returns a file name based on CVW server name and located in the
489 * current user directory.
490 *
491 * @returns serverName.res
492 */
493 public String getServerAVFileName() {
494 if (avFileName == null) {
495 CVWServerComm serverComm = CVWServerComm.getInstance();
496 String srvName = serverComm.getServerName();
497 avFileName = npds.getUserDir() + npds.slash + srvName + ".res";
498 }
499 return avFileName;
500 }
501
502 /**
503 * Sets the multicast address parameters
504 *
505 * @param mcast The multicast address.
506 */
507 public void setAV(String mcast) {
508 mainFrame.processing = false;
509 mainFrame.waitCursor(false);
510 cvwPrefs = mainFrame.getPrefs();
511
512 if (mcastAddress == null) {
513 mcastAddress = mcast;
514 if (cvwPrefs.getAudioShow()) {
515 mainFrame.audioVisualOn(true);
516 startAudio();
517 }
518 if (cvwPrefs.getVideoShow()) {
519 mainFrame.videoVisualOn(true);
520 startVideo();
521 }
522 return;
523 }
524
525 mcastAddress = mcast;
526 boolean audio, video;
527 if(audio = audioActive()) stopAudio();
528 if(video = videoActive()) stopVideo();
529 if(audio) startAudio();
530 if(video) startVideo();
531 }
532
533 /**
534 * Finds the tcl parameters file (vic.tcl or vat.tcl)
535 *
536 * @param tcl The program to find the file for (vic or vat)
537 * @return The path to the *.tcl file
538 */
539 public String getTCLFile(String tcl) {
540 String sysDir = NPDocServer.getProgramDir(tcl.substring(0,tcl.indexOf(".")));
541 String file = sysDir + tcl;
542
543 if (!npds.fileExists(file))
544 return null;
545 //need to search for \ and replace with /
546 file = file.replace('\\', '/');
547 if (file.indexOf(" ") != -1) // should look like "\"c:/dir/dir 2/dir3\""
548 file = "\"\\\"" + file + "\\\"\"";
549
550 return file;
551 }
552
553
554 /* 7/17/97 dage - had to reverse the start and stop calls
555 */
556 /**
557 * Function called for the audio menu and button
558 */
559 public void audioCommand() {
560 if(audioActive()){
561 stopAudio();
562 } else {
563 startAudio();
564 }
565 }
566
567 /* 7/17/97 dage - had to reverse the start and stop calls
568 */
569 /**
570 * Function called for the video menu and button
571 */
572 public void videoCommand() {
573 if(videoActive()){
574 stopVideo();
575 } else {
576 startVideo();
577 }
578 }
579
580 }