1 package iotcode.AmcrestCamera;
4 import iotcode.annotation.*;
5 import iotcode.interfaces.*;
6 import iotruntime.IoTHTTP;
7 import iotruntime.slave.IoTSet;
8 import iotruntime.slave.IoTDeviceAddress;
10 // Standard Java Packages
11 import java.io.IOException;
12 import java.io.DataInputStream;
13 import java.io.InputStream;
14 import java.io.BufferedInputStream;
15 import java.io.ByteArrayInputStream;
16 import java.io.IOException;
17 import java.io.ByteArrayOutputStream;
19 import java.awt.image.BufferedImage;
20 import java.awt.image.ColorModel;
21 import java.awt.image.WritableRaster;
22 import java.awt.image.BufferedImage;
23 import javax.imageio.ImageIO;
24 import java.util.Base64;
25 import java.util.Arrays;
26 import java.util.Date;
27 import java.util.List;
28 import java.util.ArrayList;
30 import java.util.HashSet;
31 import java.util.concurrent.locks.Lock;
32 import java.util.concurrent.locks.ReadWriteLock;
33 import java.util.concurrent.locks.ReentrantReadWriteLock;
34 import java.util.concurrent.atomic.AtomicBoolean;
35 import java.util.Iterator;
36 import javax.imageio.ImageIO;
37 import java.util.concurrent.CopyOnWriteArrayList;
38 import java.util.concurrent.Semaphore;
41 import java.rmi.Remote;
42 import java.rmi.RemoteException;
44 // Checker annotations
45 //import iotchecker.qual.*;
47 public class AmcrestCamera implements Camera {
49 /*******************************************************************************************************************************************
53 *******************************************************************************************************************************************/
54 private String credentialUsername = "";
55 private String credentialPassword = "";
56 private DataInputStream dataInStream = null;
57 private boolean isStreamConnected = false;
58 private byte[] latestImage = null;
59 private ReadWriteLock imageReadWriteLock = new ReentrantReadWriteLock();
60 private Lock imageReadLock = imageReadWriteLock.readLock();
61 private Lock imageWriteLock = imageReadWriteLock.writeLock();
62 private AtomicBoolean newFrameAvailable = new AtomicBoolean(false);
63 private ReadWriteLock timestampReadWriteLock = new ReentrantReadWriteLock();
64 private Lock timestampReadLock = timestampReadWriteLock.readLock();
65 private Lock timestampWriteLock = timestampReadWriteLock.writeLock();
66 private Date latestImageTimestamp = null;
67 private List <CameraSmartCallback> callbackList =
68 new CopyOnWriteArrayList <CameraSmartCallback> ();
69 private AtomicBoolean doEnd = new AtomicBoolean(false);
70 private IoTDeviceAddress deviceAddress = null;
71 private AtomicBoolean didInit = new AtomicBoolean();
72 private AtomicBoolean didStart = new AtomicBoolean();
73 static Semaphore settingsSettings = new Semaphore(1);
75 /*******************************************************************************************************************************************
79 *******************************************************************************************************************************************/
80 private Thread callbackThread = null;
81 private Thread workerThread = null;
84 /*******************************************************************************************************************************************
86 ** IoT Sets and Relations
88 *******************************************************************************************************************************************/
90 // IoTSet of Device Addresses.
91 // Will be filled with only 1 address.
92 @config private IoTSet<IoTDeviceAddress> cam_addresses;
94 public AmcrestCamera(String _credentialUsername, String _credentialPassword, IoTSet<IoTDeviceAddress> _camAddresses) throws RemoteException {
95 credentialUsername = _credentialUsername;
96 credentialPassword = _credentialPassword;
97 cam_addresses = _camAddresses;
100 public AmcrestCamera(String _credentialUsername, String _credentialPassword) throws RemoteException {
101 credentialUsername = _credentialUsername;
102 credentialPassword = _credentialPassword;
105 /*******************************************************************************************************************************************
107 ** Camera Interface Methods
109 *******************************************************************************************************************************************/
111 public byte[] getLatestFrame() {
113 byte[] newImage = null;
115 imageReadLock.lock();
117 if (latestImage != null) {
118 newImage = Arrays.copyOf(latestImage, latestImage.length);
120 } catch (Exception x) {
123 imageReadLock.unlock();
128 public long getTimestamp() {
129 timestampReadLock.lock();
130 Date ret = (Date)latestImageTimestamp.clone();
131 timestampReadLock.unlock();
132 long retLong = ret.getTime();
136 public void registerCallback(CameraSmartCallback _callbackTo) {
137 callbackList.add(_callbackTo);
140 public boolean setFPS(int _fps) {
142 settingsSettings.acquire();
144 String camUrlString = "/cgi-bin/configManager.cgi?action=setConfig&Encode[0].MainFormat[0].Video.FPS=" + Integer.toString(_fps);
148 String credsPreBase64 = credentialUsername + ":" + credentialPassword;
149 String credsBase64 = Base64.getEncoder().encodeToString(credsPreBase64.getBytes("utf-8"));
150 String httpAuthCredentials = "Basic " + credsBase64;
152 IoTHTTP httpConnection = new IoTHTTP(deviceAddress);
153 httpConnection.setURL(camUrlString);
154 httpConnection.openConnection();
155 httpConnection.setDoInput(true);
156 httpConnection.setRequestProperty("Authorization", httpAuthCredentials);
157 httpConnection.connect();
159 InputStream is = httpConnection.getInputStream();
160 BufferedInputStream bis = new BufferedInputStream(is);
161 DataInputStream din = new DataInputStream(bis);
163 // wait for a response
166 } catch (InterruptedException ie) {
169 byte[] byteBuf = new byte[100];
171 int r = din.read(byteBuf, 0, byteBuf.length);
172 String retString = new String(byteBuf);
174 if (!retString.substring(0, 2).equals("OK")) {
175 httpConnection.disconnect();
179 } catch (Exception e) {
180 httpConnection.disconnect();
182 // e.printStackTrace();
185 httpConnection.disconnect();
186 } catch (IOException e) {
188 } catch (Exception e) {
191 } catch (Exception e) {
194 settingsSettings.release();
199 public int getMaxFPS() {
200 // Hard coded since this is hardware dependant
204 public int getMinFPS() {
205 // Hard coded since this is hardware dependant
209 public List<Resolution> getSupportedResolutions() {
211 // Hard coded since this is hardware dependant
212 List<Resolution> ret = new ArrayList<Resolution>();
213 ret.add(Resolution.RES_1080P);
214 ret.add(Resolution.RES_720P);
215 ret.add(Resolution.RES_VGA);
219 public boolean setResolution(Resolution _res) {
222 settingsSettings.acquire();
225 String camUrlString = "/cgi-bin/configManager.cgi?action=setConfig";
227 if (_res == Resolution.RES_1080P) {
228 camUrlString += "&Encode[0].MainFormat[0].Video.Height=1080&Encode[0].MainFormat[0].Video.Width=1920";
230 } else if (_res == Resolution.RES_720P) {
231 camUrlString += "&Encode[0].MainFormat[0].Video.Height=720&Encode[0].MainFormat[0].Video.Width=1280";
233 } else if (_res == Resolution.RES_VGA) {
234 camUrlString += "&Encode[0].MainFormat[0].Video.Height=480&Encode[0].MainFormat[0].Video.Width=640";
239 String credsPreBase64 = credentialUsername + ":" + credentialPassword;
240 String credsBase64 = Base64.getEncoder().encodeToString(credsPreBase64.getBytes("utf-8"));
241 String httpAuthCredentials = "Basic " + credsBase64;
243 IoTHTTP httpConnection = new IoTHTTP(deviceAddress);
244 httpConnection.setURL(camUrlString);
245 httpConnection.openConnection();
246 httpConnection.setDoInput(true);
247 httpConnection.setRequestProperty("Authorization", httpAuthCredentials);
248 httpConnection.connect();
250 InputStream is = httpConnection.getInputStream();
251 BufferedInputStream bis = new BufferedInputStream(is);
252 DataInputStream din = new DataInputStream(bis);
254 // wait for a response
257 } catch (InterruptedException ie) {
260 byte[] byteBuf = new byte[100];
262 int r = din.read(byteBuf, 0, byteBuf.length);
263 String retString = new String(byteBuf);
265 if (!retString.substring(0, 2).equals("OK")) {
266 httpConnection.disconnect();
270 } catch (Exception e) {
271 httpConnection.disconnect();
273 // e.printStackTrace();
276 httpConnection.disconnect();
277 } catch (IOException e) {
279 } catch (Exception e) {
282 } catch (Exception e) {
286 settingsSettings.release();
291 public void start() {
293 if (didStart.compareAndSet(false, true) == false) {
294 return; // already started
299 if (!streamConnect()) {
303 callbackThread = new Thread(new Runnable() {
308 callbackThread.start();
310 workerThread = new Thread(new Runnable() {
315 workerThread.start();
319 if (didStart.compareAndSet(true, false) == false) {
320 return; // already stopped
326 callbackThread.join();
328 } catch (Exception e) {
336 if (didInit.compareAndSet(false, true) == false) {
337 return; // already init
340 // get the device address and save it for later use when creating HTTP connections
341 Iterator itr = cam_addresses.iterator();
342 deviceAddress = (IoTDeviceAddress)itr.next();
344 System.out.println("Address: " + deviceAddress.getCompleteAddress());
347 /*******************************************************************************************************************************************
351 *******************************************************************************************************************************************/
352 private byte[] readFromStream(int num) {
353 byte[] byteBuf = new byte[num];
355 dataInStream.readFully(byteBuf, 0, byteBuf.length);
356 } catch (Exception e) {
363 private void findBoundry() {
364 String boundary = "**************";
366 byte b = readFromStream(1)[0];
367 boundary = boundary.substring(1);
370 if (boundary.equals("--myboundary\r\n")) {
376 private String getLine() {
379 byte b = readFromStream(1)[0];
384 } else if (c != '\r') {
392 private BufferedImage parseImage() {
396 String contentTypeString = getLine();
397 String contentLengthString = getLine();
399 // remove the new line characters \r\n
402 int imageDataLength = Integer.parseInt(contentLengthString.substring(16));
404 byte[] imageDataBuf = readFromStream(imageDataLength);
406 // remove the new line characters \r\n
411 InputStream imageInStream = new ByteArrayInputStream(imageDataBuf);
412 return ImageIO.read(imageInStream);
414 } catch (Exception e) {
416 System.out.println("Has Exception");
423 private boolean streamConnect() {
427 String credsPreBase64 = credentialUsername + ":" + credentialPassword;
428 String credsBase64 = Base64.getEncoder().encodeToString(credsPreBase64.getBytes("utf-8"));
429 String httpAuthCredentials = "Basic " + credsBase64;
431 IoTHTTP httpConnection = new IoTHTTP(deviceAddress);
432 httpConnection.setURL("/cgi-bin/mjpg/video.cgi?channel=0&subtype=1");
433 //httpConnection.setURL("/cgi-bin/mjpg/video.cgi?channel=0&subtype=1", credentialUsername, credentialPassword);
434 httpConnection.openConnection();
435 httpConnection.setDoInput(true);
436 httpConnection.setRequestProperty("Authorization", httpAuthCredentials);
437 httpConnection.connect();
439 InputStream is = httpConnection.getInputStream();
440 BufferedInputStream bis = new BufferedInputStream(is);
441 dataInStream = new DataInputStream(bis);
443 isStreamConnected = true;
445 } catch (IOException e) {
446 isStreamConnected = false;
448 } catch (Exception e) {
449 isStreamConnected = false;
452 return isStreamConnected;
455 private void streamDisconnect() {
457 if (isStreamConnected) {
458 dataInStream.close();
459 isStreamConnected = false;
461 } catch (Exception e) {
465 private void doCallbacks() {
467 while (!doEnd.get()) {
468 if (newFrameAvailable.compareAndSet(true, false)) {
470 for (CameraSmartCallback c : callbackList) {
472 c.newCameraFrameAvailable(this.getLatestFrame(), this.getTimestamp());
473 //c.newCameraFrameAvailable(this);
477 // Sleep for 15 millisec to give time for new frame to arrive
480 } catch (InterruptedException ie) {
487 private void doWork() {
489 // parse the images that are loaded into the buffer
490 while (!doEnd.get()) {
492 BufferedImage img = parseImage();
496 timestampWriteLock.lock();
497 latestImageTimestamp = new Date();
498 timestampWriteLock.unlock();
500 imageWriteLock.lock();
503 ByteArrayOutputStream baos = new ByteArrayOutputStream();
504 ImageIO.write(img, "jpg", baos);
506 latestImage = baos.toByteArray();
509 } catch (Exception e) {
512 imageWriteLock.unlock();
514 newFrameAvailable.set(true);
518 if (dataInStream.available() > 120000) {
519 dataInStream.skip(120000);
521 } catch (Exception e) {
527 /* TODO: Uncomment this part to do camera test
528 public static void main(String[] args) throws Exception {
530 System.out.println("Running AmcrestCamera!");
532 IoTDeviceAddress iotDevAdd = new IoTDeviceAddress(args[0], 12345, 80, false, false);
533 Set<IoTDeviceAddress> set = new HashSet<IoTDeviceAddress>();
535 IoTSet<IoTDeviceAddress> iotset = new IoTSet<IoTDeviceAddress>(set);
537 AmcrestCamera cam = new AmcrestCamera(args[1], args[2], iotset);
540 cam.setResolution(Resolution.RES_VGA);