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;
29 import java.util.concurrent.locks.Lock;
30 import java.util.concurrent.locks.ReadWriteLock;
31 import java.util.concurrent.locks.ReentrantReadWriteLock;
32 import java.util.concurrent.atomic.AtomicBoolean;
33 import java.util.Iterator;
34 import javax.imageio.ImageIO;
35 import java.util.concurrent.CopyOnWriteArrayList;
36 import java.util.concurrent.Semaphore;
39 import java.rmi.Remote;
40 import java.rmi.RemoteException;
42 // Checker annotations
43 //import iotchecker.qual.*;
45 public class AmcrestCamera implements Camera {
47 /*******************************************************************************************************************************************
51 *******************************************************************************************************************************************/
52 private String credentialUsername = "";
53 private String credentialPassword = "";
54 private DataInputStream dataInStream = null;
55 private boolean isStreamConnected = false;
56 private byte[] latestImage = null;
57 private ReadWriteLock imageReadWriteLock = new ReentrantReadWriteLock();
58 private Lock imageReadLock = imageReadWriteLock.readLock();
59 private Lock imageWriteLock = imageReadWriteLock.writeLock();
60 private AtomicBoolean newFrameAvailable = new AtomicBoolean(false);
61 private ReadWriteLock timestampReadWriteLock = new ReentrantReadWriteLock();
62 private Lock timestampReadLock = timestampReadWriteLock.readLock();
63 private Lock timestampWriteLock = timestampReadWriteLock.writeLock();
64 private Date latestImageTimestamp = null;
65 private List <CameraSmartCallback> callbackList =
66 new CopyOnWriteArrayList <CameraSmartCallback> ();
67 private AtomicBoolean doEnd = new AtomicBoolean(false);
68 private IoTDeviceAddress deviceAddress = null;
69 private AtomicBoolean didInit = new AtomicBoolean();
70 private AtomicBoolean didStart = new AtomicBoolean();
71 static Semaphore settingsSettings = new Semaphore(1);
73 /*******************************************************************************************************************************************
77 *******************************************************************************************************************************************/
78 private Thread callbackThread = null;
79 private Thread workerThread = null;
82 /*******************************************************************************************************************************************
84 ** IoT Sets and Relations
86 *******************************************************************************************************************************************/
88 // IoTSet of Device Addresses.
89 // Will be filled with only 1 address.
90 @config private IoTSet<IoTDeviceAddress> cam_addresses;
93 public AmcrestCamera(String _credentialUsername, String _credentialPassword) throws RemoteException {
94 credentialUsername = _credentialUsername;
95 credentialPassword = _credentialPassword;
98 /*******************************************************************************************************************************************
100 ** Camera Interface Methods
102 *******************************************************************************************************************************************/
104 public byte[] getLatestFrame() {
106 byte[] newImage = null;
108 imageReadLock.lock();
110 if (latestImage != null) {
111 newImage = Arrays.copyOf(latestImage, latestImage.length);
113 } catch (Exception x) {
116 imageReadLock.unlock();
121 public long getTimestamp() {
122 timestampReadLock.lock();
123 Date ret = (Date)latestImageTimestamp.clone();
124 timestampReadLock.unlock();
125 long retLong = ret.getTime();
129 public void registerCallback(CameraSmartCallback _callbackTo) {
130 callbackList.add(_callbackTo);
133 public boolean setFPS(int _fps) {
135 settingsSettings.acquire();
137 String camUrlString = "/cgi-bin/configManager.cgi?action=setConfig&Encode[0].MainFormat[0].Video.FPS=" + Integer.toString(_fps);
141 String credsPreBase64 = credentialUsername + ":" + credentialPassword;
142 String credsBase64 = Base64.getEncoder().encodeToString(credsPreBase64.getBytes("utf-8"));
143 String httpAuthCredentials = "Basic " + credsBase64;
145 IoTHTTP httpConnection = new IoTHTTP(deviceAddress);
146 httpConnection.setURL(camUrlString);
147 httpConnection.openConnection();
148 httpConnection.setDoInput(true);
149 httpConnection.setRequestProperty("Authorization", httpAuthCredentials);
150 httpConnection.connect();
152 InputStream is = httpConnection.getInputStream();
153 BufferedInputStream bis = new BufferedInputStream(is);
154 DataInputStream din = new DataInputStream(bis);
156 // wait for a response
159 } catch (InterruptedException ie) {
162 byte[] byteBuf = new byte[100];
164 int r = din.read(byteBuf, 0, byteBuf.length);
165 String retString = new String(byteBuf);
167 if (!retString.substring(0, 2).equals("OK")) {
168 httpConnection.disconnect();
172 } catch (Exception e) {
173 httpConnection.disconnect();
175 // e.printStackTrace();
178 httpConnection.disconnect();
179 } catch (IOException e) {
181 } catch (Exception e) {
184 } catch (Exception e) {
187 settingsSettings.release();
192 public int getMaxFPS() {
193 // Hard coded since this is hardware dependant
197 public int getMinFPS() {
198 // Hard coded since this is hardware dependant
202 public List<Resolution> getSupportedResolutions() {
204 // Hard coded since this is hardware dependant
205 List<Resolution> ret = new ArrayList<Resolution>();
206 ret.add(Resolution.RES_1080P);
207 ret.add(Resolution.RES_720P);
208 ret.add(Resolution.RES_VGA);
212 public boolean setResolution(Resolution _res) {
215 settingsSettings.acquire();
218 String camUrlString = "/cgi-bin/configManager.cgi?action=setConfig";
220 if (_res == Resolution.RES_1080P) {
221 camUrlString += "&Encode[0].MainFormat[0].Video.Height=1080&Encode[0].MainFormat[0].Video.Width=1920";
223 } else if (_res == Resolution.RES_720P) {
224 camUrlString += "&Encode[0].MainFormat[0].Video.Height=720&Encode[0].MainFormat[0].Video.Width=1280";
226 } else if (_res == Resolution.RES_VGA) {
227 camUrlString += "&Encode[0].MainFormat[0].Video.Height=480&Encode[0].MainFormat[0].Video.Width=640";
233 String credsPreBase64 = credentialUsername + ":" + credentialPassword;
234 String credsBase64 = Base64.getEncoder().encodeToString(credsPreBase64.getBytes("utf-8"));
235 String httpAuthCredentials = "Basic " + credsBase64;
237 IoTHTTP httpConnection = new IoTHTTP(deviceAddress);
238 httpConnection.setURL(camUrlString);
239 httpConnection.openConnection();
240 httpConnection.setDoInput(true);
241 httpConnection.setRequestProperty("Authorization", httpAuthCredentials);
242 httpConnection.connect();
244 InputStream is = httpConnection.getInputStream();
245 BufferedInputStream bis = new BufferedInputStream(is);
246 DataInputStream din = new DataInputStream(bis);
248 // wait for a response
251 } catch (InterruptedException ie) {
254 byte[] byteBuf = new byte[100];
256 int r = din.read(byteBuf, 0, byteBuf.length);
257 String retString = new String(byteBuf);
259 if (!retString.substring(0, 2).equals("OK")) {
260 httpConnection.disconnect();
264 } catch (Exception e) {
265 httpConnection.disconnect();
267 // e.printStackTrace();
270 httpConnection.disconnect();
271 } catch (IOException e) {
273 } catch (Exception e) {
276 } catch (Exception e) {
280 settingsSettings.release();
285 public void start() {
287 if (didStart.compareAndSet(false, true) == false) {
288 return; // already started
295 if (!streamConnect()) {
299 callbackThread = new Thread(new Runnable() {
304 callbackThread.start();
306 workerThread = new Thread(new Runnable() {
311 workerThread.start();
315 if (didStart.compareAndSet(true, false) == false) {
316 return; // already stopped
322 callbackThread.join();
324 } catch (Exception e) {
332 if (didInit.compareAndSet(false, true) == false) {
333 return; // already init
336 // get the device address and save it for later use when creating HTTP connections
337 Iterator itr = cam_addresses.iterator();
338 deviceAddress = (IoTDeviceAddress)itr.next();
340 System.out.println("Address: " + deviceAddress.getCompleteAddress());
343 /*******************************************************************************************************************************************
347 *******************************************************************************************************************************************/
348 private byte[] readFromStream(int num) {
349 byte[] byteBuf = new byte[num];
351 dataInStream.readFully(byteBuf, 0, byteBuf.length);
352 } catch (Exception e) {
359 private void findBoundry() {
360 String boundary = "**************";
362 byte b = readFromStream(1)[0];
363 boundary = boundary.substring(1);
366 if (boundary.equals("--myboundary\r\n")) {
372 private String getLine() {
375 byte b = readFromStream(1)[0];
380 } else if (c != '\r') {
388 private BufferedImage parseImage() {
392 String contentTypeString = getLine();
393 String contentLengthString = getLine();
395 // remove the new line characters \r\n
398 int imageDataLength = Integer.parseInt(contentLengthString.substring(16));
400 byte[] imageDataBuf = readFromStream(imageDataLength);
402 // remove the new line characters \r\n
407 InputStream imageInStream = new ByteArrayInputStream(imageDataBuf);
408 return ImageIO.read(imageInStream);
410 } catch (Exception e) {
412 System.out.println("Has Exception");
419 private boolean streamConnect() {
423 String credsPreBase64 = credentialUsername + ":" + credentialPassword;
424 String credsBase64 = Base64.getEncoder().encodeToString(credsPreBase64.getBytes("utf-8"));
425 String httpAuthCredentials = "Basic " + credsBase64;
427 IoTHTTP httpConnection = new IoTHTTP(deviceAddress);
428 httpConnection.setURL("/cgi-bin/mjpg/video.cgi?");
429 httpConnection.openConnection();
430 httpConnection.setDoInput(true);
431 httpConnection.setRequestProperty("Authorization", httpAuthCredentials);
432 httpConnection.connect();
434 InputStream is = httpConnection.getInputStream();
435 BufferedInputStream bis = new BufferedInputStream(is);
436 dataInStream = new DataInputStream(bis);
438 isStreamConnected = true;
440 } catch (IOException e) {
441 isStreamConnected = false;
443 } catch (Exception e) {
444 isStreamConnected = false;
447 return isStreamConnected;
450 private void streamDisconnect() {
452 if (isStreamConnected) {
453 dataInStream.close();
454 isStreamConnected = false;
456 } catch (Exception e) {
460 private void doCallbacks() {
462 while (!doEnd.get()) {
463 if (newFrameAvailable.compareAndSet(true, false)) {
465 for (CameraSmartCallback c : callbackList) {
467 c.newCameraFrameAvailable(this);
471 // Sleep for 15 millisec to give time for new frame to arrive
474 } catch (InterruptedException ie) {
481 private void doWork() {
483 // parse the images that are loaded into the buffer
484 while (!doEnd.get()) {
486 BufferedImage img = parseImage();
490 timestampWriteLock.lock();
491 latestImageTimestamp = new Date();
492 timestampWriteLock.unlock();
494 imageWriteLock.lock();
497 ByteArrayOutputStream baos = new ByteArrayOutputStream();
498 ImageIO.write(img, "jpg", baos);
500 latestImage = baos.toByteArray();
503 } catch (Exception e) {
506 imageWriteLock.unlock();
508 newFrameAvailable.set(true);
512 if (dataInStream.available() > 120000) {
513 dataInStream.skip(120000);
515 } catch (Exception e) {