Voyeurism

For the Where-When assignment, I built an applet that shows video feeds from 3 different networked cameras. The first of these cameras is located in a street in Lisbon, Portugal, the second a pub in Manchester, UK, and the third an internet cafe in Moscow, Russia.

I used the networked camera code as a starting point. After finding that there wasn’t much going on in front of the ITP cameras that I could access, I decided to look for public cameras elsewhere. I came across a website called Opentopia.com that streams feeds from public IP cameras around the world. After some trial and error, I realized that the code we were given only works with Axis brand IP cams. Luckily, Opentopia lists the manufacturers for most of the cameras on the site. I picked three Axis cameras in locations that I liked and used their IP addresses for my applet.

One thing I found is that this applet is absolutely mesmerizing when I leave it running for a while. It is hard not to people-watch. I became invested in the people at the Manchester pub especially, since the camera provided such an intimate view of its patrons.

Here is a video I took of the applet running:

This is the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
//Kim Ash
//IPCam2 - Voyeurism
//CompCams Spring 2012
import processing.core.PApplet;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.PixelGrabber;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.URL;
import processing.core.PApplet;
import processing.core.PImage;
import javax.imageio.ImageIO;
 
public class CaptureAxis extends PApplet {
	//.28 Phys Comp (406)
	//.29 inactive - can go in ex-res when needed
	//.188 ("Is Our Machines Learning")
	//.189 On your desk...
 
	//Capture video; 
	//this is what you use for ordinary video cameras
	//requires pointing at the video.jar library from processing
 
	CaptureAxisCamera video;	 //This acts the same the regular but connects to axis net cam
	CaptureAxisCamera video2;
	CaptureAxisCamera video3;
 
	static public void main(String _args[]) {
		PApplet.main(new String[] { "CaptureAxis" });
	}
 
	public void setup() {
 
		size(1290,550);
		video3 = new CaptureAxisCamera(this, "212.42.54.137:8008",width,height,true);	//Moscow internet cafe
		video2 = new CaptureAxisCamera(this, "88.96.224.174:2220",width,height,true);	//Manchester pub
		video = new CaptureAxisCamera(this, "fotogermanoviseu.dyndns.info",width,height,true);	//Lisbon street
		//video = new Capture(this, 640,480);
		//this is what you use for ordinary video cameras
	}
 
	public void draw() {
		if (video.available()) {
			video.read();
			image(video, 0, 0);
		}
 
		if (video2.available()) {
			video2.read();
			image(video2, 320, 0);
		}
 
		if (video3.available()) {
			video3.read();
			image(video3, 651, 0);
		}
 
		fill(255, 0, 0);
		textSize(20);
		text("Lisbon, Portugal", 10, 270);
		text("Manchester, UK", 330, 320);
		text("Moscow, Russia", 660, 510);
 
	}
/**
 * 
 * @author David E. Mireles, Ph.D. (adapted for processing by Dan O'Sullivan)
 */
public class CaptureAxisCamera extends PImage implements Runnable {
	public boolean useMJPGStream = false;
 
	public String ip = "";
	public String jpgURL = "http://128.122.151.200/axis-cgi/jpg/image.cgi?resolution=352x240";
 
	public String mjpgURL  = "http://128.122.151.189/axis-cgi/mjpg/video.cgi?resolution=352x240";
 
	DataInputStream dis;
 
    Image image;
 
    BufferedImage bimage;
 
	public Dimension imageSize = null;
 
	public boolean connected = false;
 
	private boolean initCompleted = false;
 
	HttpURLConnection huc = null;
 
	PApplet parent;
 
	boolean crop;
 
	boolean available;
 
	Method captureEventMethod;
 
	/** Creates a new instance of AxisCamera */
	public CaptureAxisCamera(PApplet _parent, String _ip,int _w, int _h, boolean _useMJPGStream) {
		ip = _ip;
		parent = _parent;
		useMJPGStream = _useMJPGStream;
		jpgURL = "http://"+ ip + "/axis-cgi/jpg/image.cgi?resolution="+ String.valueOf(_w)+ "x" +String.valueOf(_h);
 
		//jpgURL = "";
		mjpgURL  = "http://"+ ip +"/axis-cgi/mjpg/video.cgi?resolution"+ String.valueOf(_w)+ "x" +String.valueOf(_h);
 
		// initialize my PImage self
		super.init(_w, _h, RGB);
 
 
 
 
		try {
			captureEventMethod = parent.getClass().getMethod("captureEvent", new Class[] { CaptureAxisCamera.class });
		} catch (Exception e) {
			// no such method, or an error.. which is fine, just ignore
		}
 
 
 
		Thread myThread = new Thread(this);
		myThread.start();
 
		parent.registerDispose(this);
	}
 
	/**
	 * True if a frame is ready to be read.
	 * 
	 *  // put this somewhere inside draw if (capture.available()) capture.read();
	 * 
	 * 
	 * 
	 * Alternatively, you can use captureEvent(Capture c) to notify you whenever available() is set to true. In which case, things might look like this:
	 * 
	 * 
	 * 
	 * public void captureEvent(Capture c) { c.read(); // do something exciting now that c has been updated }
	 * 
	 * 
	 */
	public boolean available() {
		return available;
	}
 
	public void read() {
		// try {
		// synchronized (capture) {
		if (image != null){
		loadPixels();
		synchronized (pixels) {
			// System.out.println("read1");
			if (crop) {
				// System.out.println("read2a");
				// f#$)(#$ing quicktime / jni is so g-d slow, calling copyToArray
				// for the invidual rows is literally 100x slower. instead, first
				// copy the entire buffer to a separate array (i didn't need that
				// memory anyway), and do an arraycopy for each row.
				/*
				 * if (data == null) { data = new int[dataWidth * dataHeight]; } raw.copyToArray(0, data, 0, dataWidth * dataHeight); int sourceOffset = cropX + cropY * dataWidth; int destOffset = 0; for (int y = 0; y < cropH; y++) { System.arraycopy(data, sourceOffset, pixels, destOffset, cropW); sourceOffset += dataWidth; destOffset += width; }
				 */
			} else { // no crop, just copy directly
				// System.out.println("read2b");
				// theData = (byte[]) imageBuffer.getData();
 
				PixelGrabber pg = new PixelGrabber(image,0,0,width,height,pixels,0,width);
 
				   try {
					      pg.grabPixels();
					    } catch (InterruptedException e) { }
				// raw.copyToArray(0, pixels, 0, width * height);
			// }
			// System.out.println("read3");
					    }
			available = false;
			// mark this image as modified so that PGraphicsJava2D and
			// PGraphicsOpenGL will properly re-blit and draw this guy
			updatePixels();
			// System.out.println("read4");
		}
		}
	}
	public void connect() {
		try {
			URL u = new URL(useMJPGStream ? mjpgURL : jpgURL);
			huc = (HttpURLConnection) u.openConnection();
			// System.out.println(huc.getContentType());
			InputStream is = huc.getInputStream();
			connected = true;
			BufferedInputStream bis = new BufferedInputStream(is);
			dis = new DataInputStream(bis);
			if (!initCompleted) initDisplay();
		} catch (IOException e) { // in case no connection exists wait and try again, instead of printing the error
			try {
				huc.disconnect();
				Thread.sleep(60);
			} catch (InterruptedException ie) {
				huc.disconnect();
				connect();
			}
			connect();
		} catch (Exception e) {
			;
		}
	}
 
	public void initDisplay() { // setup the display
		if (useMJPGStream)
			readMJPGStream();
		else {
			readJPG();
			disconnect();
		}
		initCompleted = true;
	}
 
	public void disconnect() {
		try {
			if (connected) {
				dis.close();
				connected = false;
			}
		} catch (Exception e) {
			;
		}
	}
 
 
	public void readStream() { // the basic method to continuously read the stream
		try {
			if (useMJPGStream) {
				while (true) {
					readMJPGStream();
 
				}
			} else {
				while (true) {
					connect();
					readJPG();
 
					disconnect();
 
				}
			}
 
		} catch (Exception e) {
			;
		}
	}
 
	public void readMJPGStream() { // preprocess the mjpg stream to remove the mjpg encapsulation
		readLine(3, dis); // discard the first 3 lines
		readJPG();
		readLine(2, dis); // discard the last two lines
	}
	public BufferedImage getImage(){
		available = false;
		return bimage;
	}
	public void readJPG() { // read the embedded jpeg image
		try {			
			bimage = ImageIO.read(dis);
			image = bimage;
			available = true;
			if (captureEventMethod != null) {
				try {
					captureEventMethod.invoke(parent, new Object[] { this });
				} catch (Exception e) {
					System.err.println("Disabling captureEvent()  because of an error.");
					e.printStackTrace();
					captureEventMethod = null;
				}
			}
 
		} catch (Exception e) {
			e.printStackTrace();
			disconnect();
		}
	}
	/**
	 * Called by PApplet to shut down video so that QuickTime can be used later by another applet.
	 */
	public void dispose() {
		disconnect();
 
	}
	public void readLine(int n, DataInputStream dis) { // used to strip out the header lines
		for (int i = 0; i < n; i++) {
			readLine(dis);
		}
	}
 
	public void readLine(DataInputStream dis) {
		try {
			boolean end = false;
			String lineEnd = "n"; // assumes that the end of the line is marked with this
			byte[] lineEndBytes = lineEnd.getBytes();
			byte[] byteBuf = new byte[lineEndBytes.length];
 
			while (!end) {
				dis.read(byteBuf, 0, lineEndBytes.length);
				String t = new String(byteBuf);
				// System.out.print(t); //uncomment if you want to see what the lines actually look like
				if (t.equals(lineEnd)) end = true;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
 
	}
 
	public void run() {
		connect();
		readStream();
	}
}
}