Intelligent Video Conferencing

Java & C Source Code for Video Conferencing Subsystem


*** IntScrollbar.java
// IntScrollbar

import java.awt.*;
import java.util.*;

public class IntScrollbar extends Scrollbar implements Observer
{
	private ObservableInt intValue;

	// IntScrollbar -- constructor for a java.awt.Scrollbar
	//	which observes the intValue
	public IntScrollbar(ObservableInt newValue)
	{
		super();
		intValue = newValue;
		intValue.addObserver(this);
		setValue(intValue.getValue());
	}

	// IntScrollbar -- constructor for a java.awt.Scrollbar
	//	which supports different orientations of the
	//	scrollbar, as does java.awt.Scrollbar
	public IntScrollbar(ObservableInt newValue, int orientation)
	{
		super(orientation);
		intValue = newValue;
		intValue.addObserver(this);
		setValue(intValue.getValue());
	}


	// Int Scollbar -- another constructor, allowing for a more
	//	flexible java.lang.Scrollbar
	public IntScrollbar(ObservableInt newValue, int orientation,
		int value, int pageSize, int lowValue, int highValue)
	{
		super(orientation, value, pageSize, lowValue, highValue);
		intValue = newValue;
		intValue.addObserver(this);
		setValue(intValue.getValue());
	}

	// handler for the IntScrollbar object; passes event to
	//	super class if event is supported by that class.
	//	if not, update value of intValue to current value
	//	of scrollbar
	public boolean handleEvent(Event evt)
	{
		if (super.handleEvent(evt))
		{
			return true;
		}
		intValue.setValue(getValue());
		return true;
	}

	// update method -- updates scrollbar's value when
	//	the observed intValue changes
	public void update(Observable obs, Object arg)
	{
		setValue(intValue.getValue());
	}
}


*** IntTextField.java
// IntTextField

import java.awt.*;
import java.util.*;

public class IntTextField extends TextField implements Observer
{
	private ObservableInt intValue;

	// constructor for a text field which follows an
	//	integer
	public IntTextField(ObservableInt theInt)
	{
		super(""+theInt.getValue(), 3);
		intValue = theInt;
		intValue.addObserver(this);
	}

	// action handler updates value of the observed when
	//	this IntTextField is changed.  The empty
	//	try/catch block handles any errors --
	//	except we just ignore the errors since we
	//	are not anticipating any.  What's wrong with
	//	that? We are perfect programmers and
	//	our users neve make mistakes! ;-)
	public boolean action(Event evt, Object whatAction)
	{
		Integer intStr;
		try
		{
			intStr = new Integer(getText());
			intValue.setValue(intStr.intValue());
		}
		catch (Exception oops)
		{ }
		return true;
	}

	// update the value of the IntTextField when observed
	//	integer value changes
	public void update(Observable obs, Object arg)
	{
		setText(""+intValue.getValue());
	}
}


*** ObservableInt.java
// ObservableInt

import java.util.*;

public class ObservableInt extends Observable
{
	// variable to store the data of interest
	int value;

	// constructor; if no value is provided, the
	//	variable is initialized to 0
	public ObservableInt()
	{
		value = 0;
	}

	// constructor which takes an initial value
	public ObservableInt(int newValue)
	{
		value = newValue;
	}

	// a syncronized method to update the value of the
	//	observed variable.  By being synchronized,
	//	only one observer can be executing this handler
	//	at any given time.  Therefore, we don't have to
	//	worry about the possibilty of two things changing
	//	the variable at the same time, resulting in
	//	corrupted data.  One of the beauties of Java...
	//	add the one keyword and a lot of work is saved
	//	the programmer.  (We only tell others that the
	//	value changed if it really changed.)
	public synchronized void setValue(int newValue)
	{
		if (newValue != value)
		{
			value = newValue;
			setChanged();
			notifyObservers();
		}
	}

	// Another synchronized method, this one to return
	//	the value of the observed variable.  It is
	//	syncronized because only one synchronized
	//	method of a given object can be executed at
	//	any one time.  Therefore, we also do not need
	//	to worry about somebody writing to the variable
	//	while somebody else is reading.
	public synchronized int getValue()
	{
		return value;
	}
}


*** ObservableString.java
// ObservableString -- almost identical to the ObservableInt,
//	except we're dealing with a String instead of an
//	Integer.  I don't really want to retype the same
//	boring comments, so just look at the comments in the
//	ObservableInt.java file and leave me alone!

import java.util.*;

public class ObservableString extends Observable
{
	String value;

	public ObservableString()
	{
		value = null;
	}

	public ObservableString(String newValue)
	{
		value = newValue;
	}

	public synchronized void setValue(String newValue)
	{
		if (newValue != value)
		{
			value = newValue;
			setChanged();
			notifyObservers();
		}
	}

	public synchronized String getValue()
	{
		return value;
	}
}


*** StringTextField.java
// StringTextField -- essentially the same as IntTextField,
//	so I direct you there for documentation and commentary
//	as I'm not about to duplicate them here.

import java.awt.*;
import java.util.*;

public class StringTextField extends TextField implements Observer
{
	private ObservableString StringValue;

	public StringTextField(ObservableString theString)
	{
		super(""+theString.getValue());
		StringValue = theString;
		StringValue.addObserver(this);
	}

	public StringTextField(ObservableString theString, int size)
	{
		super(""+theString.getValue(),size);
		StringValue = theString;
		StringValue.addObserver(this);
	}

	public boolean action(Event evt, Object whatAction)
	{
		String StringStr;
		try
		{
			StringStr = new String(getText());
			StringValue.setValue(StringStr);
		}
		catch (Exception oops)
		{ }
		return true;
	}

	public void update(Observable obs, Object arg)
	{
		setText(""+StringValue.getValue());
	}
}


*** qcSys.c
// qcSys -- the all important C function, to allow access to the QuickCam
//	this source code, along with some other autogenerated and
//	system standard headers, etc. are compiled into a shared library
//	which is loaded by the Java system to provide access to the
//	function in this file

// we need some standard Java (StubPreamble) and C (stdio) header files
#include <StubPreamble.h>
#include <stdio.h>

// and the header file for the native function, generated by the javah
//	program, which is part of the JDK
#include "SrDesign.h"

// the function, with header as javah (and Java) understands it
//	Java structures are accessed via the unhand() call which
// 	provides us with a pointer to the Java structure.
// 	A Java byte array is equivalent to a C char array
//	The function operates by opening a pipe from the command
//	requested by the main (run) loop and fget'ting that data
//	into a character array
void SrDesign_qcSys(struct HSrDesign *this,
	struct Hjava_lang_String *command,
	HArrayOfByte *byteImage)
{
	char *QCimage = unhand(byteImage)->body;
	int length = obj_length(byteImage);
	FILE *fp;

	fp = popen(makeCString(command),"r");

	fgets(QCimage, length, fp);
	return;
}


*** SrDesign.java
// SrDesign -- the main body of code for the video conferencing
//	portion of the Intelligent Video Conferencing project
// Copyright 1996-1997, Apu <apu@spfld.com> and Joanna Welenc
// Created for Stevens Institute of Technology EE/CS 415/416
//	Dr. Stanley Smith, Advisor


import java.applet.*;
import java.awt.*;
import java.awt.image.*;
import java.net.*;

public class SrDesign extends Applet implements Runnable
{
	// global variables
	Thread animThread;
	ObservableInt brightness, contrast, whitebal;
	ObservableString remoteAddr;
	Frame ControlPanel;
	Image qcLocal, qcRemote;
	byte[] byteImgLocal = new byte[24475];
	byte[] byteImgRemote = new byte[24475];
	static boolean inApplet=true;

	// application startup
	public static void main(String args[])
	{
		inApplet = false;
		Frame applFrame = new Frame("SrDesign");
		Applet SDappl = new SrDesign();
		SDappl.init();
		SDappl.start();
		applFrame.setLayout(new FlowLayout());
		applFrame.add(SDappl);
		applFrame.resize(640,240);
		applFrame.show();
	}

	// initialization
	public void init()
	{
		// default values
		brightness = new ObservableInt(135);
		contrast = new ObservableInt(215);
		whitebal = new ObservableInt(150);
		remoteAddr = new ObservableString("localhost");

		// create and add Control Panel frame
		ControlPanel = ControlPanel("SrDesign - Controls");
	}

	public void start()
	{
		// start animation Thread
		if (animThread == null)
		{
			animThread = new Thread(this);
			animThread.start();
		}
	}

	public void stop()
	{
		// stop animation Thread -- called by system on stopping
		animThread.stop();
		animThread = null;
	}

	// what do we repaint
	public void paint(Graphics g)
	{
		g.drawImage(qcLocal, 0, 0, this);
		g.drawImage(qcRemote, 320, 0, this);
	}

	// the main loop of the program
	public void run()
	{
		// the command sent to the C library is stored
		//	in a local string
		String command;

		Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
		while (true)
		{
			try
			{
				// sleep for a while to let other stuff
				//	do its thing
				animThread.sleep(100);

				// Construct command string with current
				//	values of the the observables
				//	and get image
				command = "qcam " +
					" -b " + brightness.getValue() +
					" -c " + contrast.getValue() +
					" -w " + whitebal.getValue() +
					" | ppmtogif";

				qcSys(command, byteImgLocal);
				qcLocal = createImage(new
					MemoryImageSource(
					320, 240,
					ColorModel.getRGBdefault(),
					byteImgLocal, 0, 0));

				// send image to remote party
				InetAddress internet_addr =
					InetAddress.getByName(remoteAddr.getValue());
				int port = 2000;
				DatagramPacket snd_packet = new
					DatagramPacket(
					byteImgLocal, 24475,
					internet_addr, port);
				DatagramSocket snd_socket = new
					DatagramSocket();
				snd_socket.send(snd_packet);
				snd_socket.close();

				// and receive a frame from them
				DatagramPacket rcv_packet = new
					DatagramPacket(byteImgRemote,
					byteImgRemote.length);
				DatagramSocket rcv_socket = new
					DatagramSocket(port);
				rcv_socket.receive(rcv_packet);
				rcv_socket.close();

				// and now repaint both on screen
				repaint();
			}
			catch (Exception sleepProblem)
			{ }
		}
	}

	// the qcSys function is implemented in native (non-Java) code
	//	we just need the prototype for others to use it
	public native void qcSys(String command, byte[] byteImgLocal);

	// (we need to load in the non-native code to be able to access it)
	static
	{
		System.loadLibrary("qcSys");
	}

	// the ControlPanel is implemented as a separate frame so that
	//	we can collapse (minimize) it out of the way when we
	//	don't need it
	private Frame ControlPanel(String label)
	{
		Frame frame = new Frame(label);

		Panel panel;
		IntScrollbar scrollbar;

		GridBagLayout gridbag = new GridBagLayout();
		GridBagConstraints constraints = new GridBagConstraints();
		constraints.fill = GridBagConstraints.HORIZONTAL;
		constraints.anchor = GridBagConstraints.WEST;
		constraints.gridwidth = GridBagConstraints.REMAINDER;
		frame.setLayout(gridbag);

		panel = IntControl("Brightness",brightness);
		gridbag.setConstraints(panel, constraints);
		frame.add(panel);

		scrollbar = new IntScrollbar(brightness,
				Scrollbar.HORIZONTAL,
				0, 10, 0, 255);
		gridbag.setConstraints(scrollbar, constraints);
		frame.add(scrollbar);

		panel = IntControl("Contrast",contrast);
		gridbag.setConstraints(panel, constraints);
		frame.add(panel);

		scrollbar = new IntScrollbar(contrast,
				Scrollbar.HORIZONTAL,
				0, 10, 0, 255);
		gridbag.setConstraints(scrollbar, constraints);
		frame.add(scrollbar);

		panel = IntControl("White Balance",whitebal);
		gridbag.setConstraints(panel, constraints);
		frame.add(panel);

		scrollbar = new IntScrollbar(whitebal,
				Scrollbar.HORIZONTAL,
				0, 10, 0, 255);
		gridbag.setConstraints(scrollbar, constraints);
		frame.add(scrollbar);

		panel = StringControl("Remote Party",remoteAddr);
		gridbag.setConstraints(panel, constraints);
		frame.add(panel);

		frame.resize(225,275);
		frame.show();
		return frame;
	}

	// parts for the ControlPanel implementing varies other classes
	//	remember, Java is an object-oriented language; the other
	//	classes are reusable, and hence not included as part of
	//	this file
	private Panel IntControl(String label, ObservableInt variable)
	{
		Panel panel = new Panel();

		panel.add(new Label(label));
		panel.add(new IntTextField(variable));

		return panel;
	}

	private Panel StringControl(String label, ObservableString variable)
	{
		Panel panel = new Panel();
		Label lbl;
		StringTextField field;		

		GridBagLayout gridbag = new GridBagLayout();
		GridBagConstraints constraints = new GridBagConstraints();
		constraints.fill = GridBagConstraints.HORIZONTAL;
		constraints.gridwidth = GridBagConstraints.REMAINDER;
		panel.setLayout(gridbag);

		lbl = new Label(label);
		gridbag.setConstraints(lbl, constraints);
		panel.add(lbl);

		field = new StringTextField(variable);
		gridbag.setConstraints(field, constraints);
		panel.add(field);

		return panel;
	}
}