import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.TimeZone;
import javax.imageio.ImageIO;
/**
* one motion detection
*
* @author matecsaba
*/
public class motionData implements Runnable {
/**
* parent of me
*/
protected final motion parent;
/**
* my number
*/
protected int myNum;
/**
* name of camera
*/
protected String myName;
/**
* url to read
*/
protected String myUrl;
/**
* username to use
*/
protected String userName;
/**
* password to use
*/
protected String password;
/**
* inter image sleep
*/
protected int sleep;
/**
* images to keep
*/
protected int imgPre;
/**
* images to keep
*/
protected int imgPost;
/**
* ignore diff
*/
protected int ignore;
/**
* trigger diff
*/
protected int trigger;
/**
* alarm mode
*/
protected int alarm;
/**
* exceptions happened
*/
protected int errors;
/**
* images got
*/
protected int fetches;
/**
* events happened
*/
protected int events;
/**
* last event time
*/
protected long lastEvnt;
/**
* last event file
*/
protected String lastPath;
/**
* last event speed
*/
protected int lastBps;
/**
* images saved
*/
protected int saved;
/**
* minimum diff
*/
protected int difMin = Integer.MAX_VALUE;
/**
* maximum diff
*/
protected int difMax = Integer.MIN_VALUE;
/**
* last diff
*/
protected int difLst;
/**
* last diff
*/
protected int difAvg;
private final static Object sleeper = new Object();
private byte[][] imgDat;
private long[] imgTim;
private int[] imgDif;
private int[] imgCol;
private BufferedImage imgLst;
private int imgPos;
/**
* create instance
*
* @param lower parent
*/
protected motionData(motion lower) {
parent = lower;
}
/**
* get last image
*
* @param buf where to write
* @throws java.lang.Exception on error
*/
protected void getImage(ByteArrayOutputStream buf) throws Exception {
buf.write(imgDat[imgPos]);
}
/**
* get last video
*
* @param buf where to write
* @throws java.lang.Exception on error
*/
protected void getVideo(ByteArrayOutputStream buf) throws Exception {
buf.write(lastPath.getBytes());
buf.write("\n\n".getBytes());
buf.write("//multipart/x-mixed-replace;boundary=ThisRandomString".getBytes());
buf.write(("\n" + lastBps + "\n").getBytes());
}
/**
* get alert needed
*
* @return true if yes, false if no
*/
protected boolean needAlert() {
switch (alarm) {
case 0:
return false;
case 1:
return true;
case 2:
return parent.alarmed;
default:
return false;
}
}
/**
* get web line
*
* @param tim current time
* @return string
*/
protected String getMeas(long tim) {
String a;
if (lastPath == null) {
a = "never";
} else {
a = "" + motionUtil.timePast(tim, lastEvnt) + "";
}
return "
" + myNum + " | " + myName + " | " + needAlert() + " | " + events + " | " + a + " | " + errors + " | " + fetches + " | " + saved + " | pic/vid | " + difMin + " | " + difLst + " | " + difMax + " | " + difAvg + " |
";
}
/**
* clear statistics
*/
protected void doClear() {
difMin = Integer.MAX_VALUE;
difMax = Integer.MIN_VALUE;
difLst = -1;
difAvg = -1;
}
public void run() {
imgDat = new byte[imgPre][];
imgTim = new long[imgPre];
imgDif = new int[imgPre];
imgCol = new int[imgPre];
for (;;) {
try {
doRound();
} catch (Exception e) {
errors++;
}
}
}
private void sleep() throws Exception {
synchronized (sleeper) {
sleeper.wait(sleep);
}
}
private BufferedImage fetchImage() throws Exception {
URL testUrl = new URI(myUrl).toURL();
URLConnection testConn = testUrl.openConnection();
testConn.setConnectTimeout(5000);
testConn.setReadTimeout(5000);
String userpass = testUrl.getUserInfo();
if (userpass != null) {
String auth = "Basic " + new String(Base64.getEncoder().encode(userpass.getBytes()));
testConn.setRequestProperty("Authorization", auth);
}
InputStream testStream = testConn.getInputStream();
List buf1 = new ArrayList();
for (;;) {
byte[] buf0 = new byte[4096];
int len = testStream.read(buf0);
if (len < 0) {
break;
}
for (int i = 0; i < len; i++) {
buf1.add(buf0[i]);
}
}
byte[] buf2 = new byte[buf1.size()];
for (int i = 0; i < buf2.length; i++) {
buf2[i] = buf1.get(i);
}
BufferedImage result = ImageIO.read(new ByteArrayInputStream(buf2));
fetches++;
imgPos = (imgPos + 1) % imgDat.length;
imgDat[imgPos] = buf2;
imgTim[imgPos] = motionUtil.getTime();
imgLst = result;
return result;
}
private int saveImage(int seq, OutputStream output) throws Exception {
if (imgDat[seq] == null) {
return -1;
}
byte[] crlf = new byte[2];
crlf[0] = 13;
crlf[1] = 10;
output.write("--ThisRandomString".getBytes());
output.write(crlf);
output.write("Content-Type: image/jpeg".getBytes());
output.write(crlf);
output.write(("X-TimeStamp: " + imgTim[seq]).getBytes());
output.write(crlf);
output.write(("X-Differences: pixels=" + imgDif[seq] + " colors=" + imgCol[seq]).getBytes());
output.write(crlf);
output.write(("Content-Length: " + imgDat[seq].length).getBytes());
output.write(crlf);
output.write(crlf);
output.write(imgDat[seq]);
output.write(crlf);
saved++;
return imgDat[seq].length;
}
private static int getDiff(int p1, int p2, int rot) {
p1 = (p1 >>> rot) & 0xff;
p2 = (p2 >>> rot) & 0xff;
p1 = p1 - p2;
if (p1 < 0) {
return -p1;
} else {
return p1;
}
}
private int getDiff(BufferedImage i1, BufferedImage i2) {
int res = 0;
int tot = 0;
int cnt = 0;
for (int y = i1.getHeight() - 1; y >= 0; y--) {
for (int x = i1.getWidth() - 1; x >= 0; x--) {
int p1 = i1.getRGB(x, y);
int p2 = i2.getRGB(x, y);
int dif = getDiff(p1, p2, 0);
dif += getDiff(p1, p2, 8);
dif += getDiff(p1, p2, 16);
tot += dif;
cnt++;
if (dif < ignore) {
continue;
}
res++;
}
}
if (res < difMin) {
difMin = res;
}
if (res > difMax) {
difMax = res;
}
difAvg = tot / cnt;
difLst = res;
imgDif[imgPos] = difLst;
imgCol[imgPos] = difAvg;
return res;
}
private void doRound() throws Exception {
sleep();
BufferedImage old = imgLst;
BufferedImage cur = fetchImage();
if (old == null) {
return;
}
int dif = getDiff(cur, old);
if (dif < trigger) {
return;
}
events++;
lastEvnt = motionUtil.getTime();
if (needAlert()) {
motionSend snd = new motionSend(this);
new Thread(snd).start();
}
Calendar cal = new GregorianCalendar(TimeZone.getTimeZone(parent.tzdata));
cal.setTime(new Date());
String date = cal.get(Calendar.YEAR) + motionUtil.padBeg("" + (cal.get(Calendar.MONTH) + 1), 2, "0") + motionUtil.padBeg("" + cal.get(Calendar.DAY_OF_MONTH), 2, "0");
String time = motionUtil.padBeg("" + cal.get(Calendar.HOUR_OF_DAY), 2, "0") + motionUtil.padBeg("" + cal.get(Calendar.MINUTE), 2, "0") + motionUtil.padBeg("" + cal.get(Calendar.SECOND), 2, "0");
String path = parent.target + date;
new File(path).mkdir();
path = path + "/" + myName + "-" + date + "-" + time + "-" + dif + ".mjpeg";
OutputStream output = new FileOutputStream(new File(path));
lastPath = path;
for (int i = 0; i < imgDat.length; i++) {
saveImage((imgPos + 1 + i) % imgDat.length, output);
}
int ago = 0;
int sav = 0;
for (;;) {
ago++;
if (ago > imgPost) {
break;
}
sleep();
dif = 0;
try {
old = imgLst;
cur = fetchImage();
dif = getDiff(cur, old);
sav += saveImage(imgPos, output);
} catch (Exception e) {
}
if (dif < trigger) {
continue;
}
ago = 0;
}
output.flush();
output.close();
lastBps = sav / (int) ((motionUtil.getTime() - lastEvnt) / 1000);
}
}
class motionSend implements Runnable {
private motionData parent;
public motionSend(motionData lower) {
parent = lower;
}
public void run() {
try {
parent.parent.sendAlert(parent.myName);
} catch (Exception e) {
parent.errors++;
}
}
}