Commit 5d74fed7 authored by Leigh Stoller's avatar Leigh Stoller

In an attempt to solve the webcam problem ... I have used my

burgeoning Java Applet skills to to write a little applet to decode
the motion jpeg stream that the webcams spit out. Not exactly
efficient, but at least it works everyplace I tried, even with
Internet Explorer.

For the web page, the default view is still static, with an option
near the top to use the applet version of the page. The nice thing is
that when you leave the page (go someplace else) the data stream
actually stops (cause the applet is stopped).
parent 298f15b5
......@@ -2239,6 +2239,7 @@ outfiles="$outfiles Makeconf GNUmakefile \
rc.d/2.elvind.sh rc.d/3.plab.sh rc.d/2.dhcpd.sh \
tools/GNUmakefile \
tools/pcapper/GNUmakefile tools/teachswitch/GNUmakefile \
tools/webcamapplet/GNUmakefile \
$eventfiles \
$winfiles \
apache/GNUmakefile apache/httpd.conf \
......
......@@ -734,6 +734,7 @@ outfiles="$outfiles Makeconf GNUmakefile \
rc.d/2.elvind.sh rc.d/3.plab.sh rc.d/2.dhcpd.sh \
tools/GNUmakefile \
tools/pcapper/GNUmakefile tools/teachswitch/GNUmakefile \
tools/webcamapplet/GNUmakefile \
$eventfiles \
$winfiles \
apache/GNUmakefile apache/httpd.conf \
......
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2002, 2004 University of Utah and the Flux Group.
# Copyright (c) 2000-2002, 2004-2005 University of Utah and the Flux Group.
# All rights reserved.
#
......@@ -11,7 +11,7 @@ SUBDIR = tools
include $(OBJDIR)/Makeconf
SUBDIRS = pcapper teachswitch
SUBDIRS = pcapper teachswitch webcamapplet
all: all-subdirs
......
#
# EMULAB-COPYRIGHT
# Copyright (c) 2005 University of Utah and the Flux Group.
# All rights reserved.
#
SRCDIR = @srcdir@
TESTBED_SRCDIR = @top_srcdir@
OBJDIR = ../..
SUBDIR = tools/webcamapplet
include $(OBJDIR)/Makeconf
ifeq ($(JAVAC),)
JARS =
else
JARS = $(TESTBED_SRCDIR)/www/WebCamApplet.jar
endif
all:
jar: $(JARS)
include $(TESTBED_SRCDIR)/GNUmakerules
WebCamApplet.class: $(SRCDIR)/WebCamApplet.java
$(JAVAC) -deprecation -d . $^
$(TESTBED_SRCDIR)/www/WebCamApplet.jar: WebCamApplet.class
echo '$(filter-out WebCamApplet.class, $(shell echo *.class))'
$(JAR) cvf $@ $^ $(patsubst %,'%',$(filter-out WebCamApplet.class, $(shell echo *.class)))
install:
clean:
/bin/rm -f *.class *.jar
$(INSTALL_WWWDIR)/%: %
@echo "Installing $<"
-mkdir -p $(patsubst %/,%,$(dir $@))
$(INSTALL_DATA) $(subst $$,\$$,$<) $(subst $$,\$$,$@)
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2005 University of Utah and the Flux Group.
* All rights reserved.
*/
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.event.*;
import java.net.URL;
import java.util.*;
import java.text.*;
import java.io.*;
import java.net.URL;
import java.net.URLEncoder;
import java.net.URLConnection;
import com.sun.image.codec.jpeg.*;
/*
* A really stupid applet to display a motion jpeg.
*/
public class WebCamApplet extends JApplet {
private WebCam webcam;
private BufferedImage curimage = null;
/*
* The connection to boss that will provide the data stream.
*/
private InputStream is;
public void init() {
try
{
URL urlServer = this.getCodeBase(), webcamurl;
URLConnection uc;
// form the URL that we use to get image data.
webcamurl = new URL(urlServer, this.getParameter("URL"));
// And connect to it.
uc = webcamurl.openConnection();
uc.setDoInput(true);
uc.setUseCaches(false);
this.is = uc.getInputStream();
}
catch(Throwable th)
{
th.printStackTrace();
}
/*
* Add the single component to the pane.
*/
getContentPane().add(webcam = new WebCam());
}
public void start() {
webcam.start();
}
public void stop() {
webcam.stop();
}
private class WebCam extends JPanel implements Runnable {
private Thread thread;
private BufferedImage bimg;
private Graphics2D G2 = null;
public WebCam() {
}
/*
* I stole this from some demo programs. Not really sure what
* it does exactly, but has something to do with double buffering.
*/
public Graphics2D createGraphics2D(int w, int h) {
//System.out.println("createGraphics2D");
if (G2 == null) {
bimg = (BufferedImage) createImage(w, h);
G2 = bimg.createGraphics();
G2.setBackground(getBackground());
G2.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
}
return G2;
}
public void drawImage(int w, int h, Graphics2D g2) {
int fw, fh;
fw = curimage.getWidth(this);
fh = curimage.getHeight(this);
/*
* Shove the new image in.
*/
g2.drawImage(curimage, 0, 0, fw, fh, this);
}
/*
* This is the callback to paint the map.
*/
public void paint(Graphics g) {
Dimension dim = getSize();
int h = dim.height;
int w = dim.width;
if (curimage == null)
return;
if (false) {
Graphics2D g2 = createGraphics2D(w, h);
drawImage(w, h, g2);
g.drawImage(bimg, 0, 0, this);
}
else {
g.drawImage(curimage, 0, 0, this);
}
}
public void start() {
G2 = null;
thread = new Thread(this);
thread.start();
}
public synchronized void stop() {
thread = null;
}
public void run() {
Thread me = Thread.currentThread();
BufferedInputStream input = new BufferedInputStream(is);
String lenhdr = "content-length: ";
byte imageBytes[] = new byte[100000];
ByteArrayInputStream imageinput;
String str;
int size;
while (thread == me) {
try
{
// First line is the boundry marker
if ((str = readLine(input).trim()) == null)
break;
if (!str.equals("--myboundary")) {
System.out.println("Sync error at boundry");
break;
}
// Next line is the Content-type. Skip it.
if ((str = readLine(input)) == null)
break;
// Next line is the Content-length. We need this.
if ((str = readLine(input)) == null)
break;
str = str.toLowerCase();
if (! str.startsWith(lenhdr)) {
System.out.println("Sync error at content length");
break;
}
str = str.substring(lenhdr.length(), str.length());
size = Integer.parseInt(str.trim());
if (size < 0) {
System.out.println("Bad content length: " + size);
break;
}
//System.out.println("content length: " + size);
// Next line is a blank line. Skip it.
if ((str = readLine(input)) == null)
break;
// Now we have the data. Buffer that up.
int count = 0;
while (count < size) {
int cc = input.read(imageBytes, count, size - count);
if (cc == -1)
break;
count += cc;
}
if (count != size) {
System.out.println("Not enough image data");
break;
}
// For the jpeg decoder.
imageinput = new ByteArrayInputStream(imageBytes);
JPEGImageDecoder decoder =
JPEGCodec.createJPEGDecoder(imageinput);
curimage = decoder.decodeAsBufferedImage();
//System.out.println("Got the image");
// Next line is a blank line. Skip it.
if ((str = readLine(input)) == null)
break;
repaint();
me.yield();
}
catch(IOException e)
{
e.printStackTrace();
break;
}
}
thread = null;
}
/*
* Catch termination.
*/
public void destroy() {
try
{
if (is != null)
is.close();
}
catch(IOException e)
{
e.printStackTrace();
}
}
/*
* A utility function since a BufferedInputStream has no readline().
*/
private byte[] buf = new byte[1024];
private String readLine(BufferedInputStream input) {
int index = 0;
int ch;
try
{
while ((ch = input.read()) != -1) {
buf[index++] = (byte) ch;
if (ch == '\n')
break;
}
}
catch(IOException e)
{
e.printStackTrace();
}
if (index == 0)
return "";
return new String(buf, 0, index);
}
}
public static void main(String argv[]) {
final WebCamApplet webcamapplet = new WebCamApplet();
try
{
webcamapplet.init();
webcamapplet.is = System.in;
}
catch(Throwable th)
{
th.printStackTrace();
}
Frame f = new Frame("WebCamApplet");
f.add(webcamapplet);
f.pack();
f.setSize(new Dimension(640,480));
f.show();
webcamapplet.start();
}
}
......@@ -60,22 +60,41 @@ if (isset($refreshrate)) {
}
else {
echo "<center>
<a href=webcam.php3?refreshrate=1>Auto-refresh</a> images at one
second interval.
<a href=webcam.php3?refreshrate=2>Auto-refresh</a> images at two
second interval, or<br>
<a href=webcam.php3?applet=1>Live Image</a> using a java applet.
</center>\n";
}
echo "<table cellpadding='0' cellspacing='0' border='0' class='stealth'>\n";
if (isset($applet)) {
$auth = $HTTP_COOKIE_VARS[$TBAUTHCOOKIE];
while ($row = mysql_fetch_array($query_result)) {
$id = $row["id"];
$url = "webcamimg.php3?webcamid=${id}&nocookieuid=${uid}".
"&nocookieauth=${auth}&applet=1";
while ($row = mysql_fetch_array($query_result)) {
$id = $row["id"];
echo "<applet archive=WebCamApplet.jar
code=WebCamApplet.class height=480 width=640>
<param name=URL value=$url>
</applet><br<br>\n";
}
}
else {
echo "<table cellpadding='0' cellspacing='0' border='0'
class='stealth'>\n";
while ($row = mysql_fetch_array($query_result)) {
$id = $row["id"];
echo "<tr><td align=center>Web Cam $id</td></tr>
<tr><td align=center class='stealth'>
<img src='webcamimg.php3?webcamid=$id' align=center></td></tr>
<tr><tr>\n";
echo "<tr><td align=center>Web Cam $id</td></tr>
<tr><td align=center class='stealth'>
<img src='webcamimg.php3?webcamid=$id'
align=center></td></tr>
<tr><tr>\n";
}
echo "</table>\n";
}
echo "</table>\n";
#
# Standard Testbed Footer
......
......@@ -50,7 +50,7 @@ if (!$query_result || !mysql_num_rows($query_result)) {
MyError("No such webcam ID: '$webcamid'");
}
$row = mysql_fetch_array($query_result);
$URL = $row["stillimage_URL"];
$URL = (isset($applet) ? $row["URL"] : $row["stillimage_URL"]);
#
# Check sitevar to make sure mere users are allowed to peek at us.
......@@ -81,7 +81,9 @@ if (!$socket) {
# of the interface we are using (fopen). No biggie, but we have to
# spit them out ourselves so the client knows what to do.
#
#header("Content-type: multipart/x-mixed-replace;boundary=--myboundary");
if (isset($applet)) {
header("Content-type: multipart/x-mixed-replace;boundary=--myboundary");
}
#TBERROR(print_r($http_response_header, TRUE) . "\n\n", 0);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment