/* * Copyright (C) 2016 Jason von Nieda <jason@vonnieda.org> * * This file is part of OpenPnP. * * OpenPnP is free software: you can redistribute it and/or modify it under the terms of the GNU * General Public License as published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * OpenPnP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * Public License for more details. * * You should have received a copy of the GNU General Public License along with OpenPnP. If not, see * <http://www.gnu.org/licenses/>. * * For more information about OpenPnP visit http://openpnp.org */ package org.openpnp.machine.reference.camera; import java.awt.Graphics2D; import java.awt.Image; import java.awt.image.BufferedImage; import java.io.IOException; import java.net.ConnectException; import java.net.MalformedURLException; import java.net.URL; import java.util.List; import javax.imageio.ImageIO; import javax.swing.Action; import javax.xml.soap.SOAPException; import org.onvif.ver10.device.wsdl.GetDeviceInformationResponse; import org.onvif.ver10.schema.JpegOptions; import org.onvif.ver10.schema.Profile; import org.onvif.ver10.schema.VideoEncoderConfiguration; import org.onvif.ver10.schema.VideoEncoderConfigurationOptions; import org.onvif.ver10.schema.VideoEncoding; import org.onvif.ver10.schema.VideoRateControl; import org.onvif.ver10.schema.VideoResolution; import org.openpnp.CameraListener; import org.openpnp.gui.support.Wizard; import org.openpnp.machine.reference.ReferenceCamera; import org.openpnp.machine.reference.camera.wizards.OnvifIPCameraConfigurationWizard; import org.openpnp.spi.PropertySheetHolder; import org.simpleframework.xml.Attribute; import de.onvif.soap.OnvifDevice; import de.onvif.soap.devices.InitialDevices; import de.onvif.soap.devices.MediaDevices; /** * A Camera implementation for ONVIF compatible IP cameras. */ public class OnvifIPCamera extends ReferenceCamera implements Runnable { @Attribute(required = false) private String preferredResolution; @Attribute(required = false) private int resizeWidth; @Attribute(required = false) private int resizeHeight; @Attribute(required = false) private int fps = 10; @Attribute(required = false) private String hostIP; @Attribute(required = false) private String username; @Attribute(required = false) private String password; private Thread thread; private boolean dirty = false; private OnvifDevice nvt; private URL snapshotURI; public OnvifIPCamera() {} @Override public BufferedImage internalCapture() { if (thread == null) { initCamera(); } try { if (snapshotURI == null) { return null; } BufferedImage img = ImageIO.read(snapshotURI); return transformImage(resizeImage(img)); } catch (Exception e) { e.printStackTrace(); return null; } } private BufferedImage resizeImage(BufferedImage src) { int imgW = src.getWidth(); int imgH = src.getHeight(); if (resizeWidth != 0) { imgW = resizeWidth; } if (resizeHeight != 0) { imgH = resizeHeight; } if ((imgW != src.getWidth()) || (imgH != src.getHeight())) { Image tmp = src.getScaledInstance(imgW, imgH, Image.SCALE_SMOOTH); BufferedImage dst = new BufferedImage(imgW, imgH, BufferedImage.TYPE_INT_RGB); Graphics2D g2d = dst.createGraphics(); g2d.drawImage(tmp, 0, 0, null); g2d.dispose(); return dst; } return src; } @Override public synchronized void startContinuousCapture(CameraListener listener, int maximumFps) { if (thread == null) { initCamera(); } super.startContinuousCapture(listener, maximumFps); } public void run() { while (!Thread.interrupted()) { try { BufferedImage image = internalCapture(); if (image != null) { broadcastCapture(image); } } catch (Exception e) { e.printStackTrace(); } try { Thread.sleep(1000 / fps); } catch (InterruptedException e) { break; } } } private Profile findJPEGProfile(InitialDevices devices) throws Exception { List<Profile> profiles = devices.getProfiles(); for (Profile profile : profiles) { VideoEncoderConfiguration videoEncoderConfiguration = profile.getVideoEncoderConfiguration(); VideoEncoding videoEncoding = videoEncoderConfiguration.getEncoding(); if (videoEncoding == VideoEncoding.JPEG) { return profile; } } throw new Exception("No JPEG profiles available for camera at " + hostIP); } private void initCamera() { if (thread != null) { thread.interrupt(); try { thread.join(); } catch (Exception e) { e.printStackTrace(); } thread = null; } try { setDirty(false); width = null; height = null; nvt = null; snapshotURI = null; if ((hostIP != null) && (!hostIP.isEmpty())) { try { if ((username != null) && (!username.isEmpty())) { nvt = new OnvifDevice(hostIP, username, password); } else { nvt = new OnvifDevice(hostIP); } InitialDevices devices = nvt.getDevices(); GetDeviceInformationResponse deviceInformation = devices.getDeviceInformation(); System.out.println("Camera " + hostIP); System.out.println(" Manufacturer : " + deviceInformation.getManufacturer()); System.out.println(" Model : " + deviceInformation.getModel()); System.out.println(" Serial Number : " + deviceInformation.getSerialNumber()); System.out.println(" Hardware ID : " + deviceInformation.getHardwareId()); System.out.println(" Firmware Version: " + deviceInformation.getFirmwareVersion()); MediaDevices media = nvt.getMedia(); Profile profile = findJPEGProfile(devices); String profileToken = profile.getToken(); VideoEncoderConfigurationOptions videoEncoderConfigurationOptions = media .getVideoEncoderConfigurationOptions(profileToken); JpegOptions jpegOptions = videoEncoderConfigurationOptions.getJPEG(); List<VideoResolution> jpegResolutions = jpegOptions.getResolutionsAvailable(); int maxRes = -1; int selectedResolutionIndex = -1; // Step 1: Select the highest resolution available for (int i = 0; i < jpegResolutions.size(); i++) { VideoResolution jpegResolution = jpegResolutions.get(i); int res = jpegResolution.getWidth() * jpegResolution.getHeight(); if (res > maxRes) { maxRes = res; selectedResolutionIndex = i; } } // Step 2: If there's a preferredResolution specified, select that (if it // exists) instead if ((preferredResolution != null) && (!preferredResolution.isEmpty())) { for (int i = 0; i < jpegResolutions.size(); i++) { VideoResolution jpegResolution = jpegResolutions.get(i); String strRes = jpegResolution.getWidth() + "x" + jpegResolution.getHeight(); if (strRes.equalsIgnoreCase(preferredResolution)) { selectedResolutionIndex = i; break; } } } if (selectedResolutionIndex >= 0) { VideoEncoderConfiguration videoEncoderConfiguration = profile.getVideoEncoderConfiguration(); VideoResolution jpegResolution = jpegResolutions.get(selectedResolutionIndex); videoEncoderConfiguration.setResolution(jpegResolution); System.out.println( " -> Selected " + jpegResolution.getWidth() + "x" + jpegResolution.getHeight()); videoEncoderConfiguration .setQuality(videoEncoderConfigurationOptions.getQualityRange().getMax()); VideoRateControl videoRateControl = videoEncoderConfiguration.getRateControl(); videoRateControl.setFrameRateLimit(jpegOptions.getFrameRateRange().getMax()); videoRateControl.setEncodingInterval(jpegOptions.getEncodingIntervalRange().getMin()); videoRateControl.setBitrateLimit( videoEncoderConfigurationOptions.getExtension().getJPEG().getBitrateRange().getMax()); videoEncoderConfiguration.setRateControl(videoRateControl); profile.setVideoEncoderConfiguration(videoEncoderConfiguration); media.setVideoEncoderConfiguration(videoEncoderConfiguration); } snapshotURI = new URL(media.getSnapshotUri(profileToken)); System.out.println("Snapshot URI: " + snapshotURI.toString()); } catch (ConnectException e) { System.err.println("Could not connect to IP camera at " + hostIP + ": " + e.toString()); e.printStackTrace(); } catch (SOAPException e) { System.err.println("Error communicating with IP camera at " + hostIP + ": " + e.toString()); e.printStackTrace(); } catch (MalformedURLException e) { System.err.println("Malformed URL for IP camera at " + hostIP + ": " + e.toString()); e.printStackTrace(); } catch (Exception e) { System.err.println("Unknown error initializing IP camera at " + hostIP + ": " + e.toString()); e.printStackTrace(); } } } catch (Exception e) { e.printStackTrace(); return; } thread = new Thread(this); thread.start(); } @Override public void close() throws IOException { super.close(); if (thread != null) { thread.interrupt(); try { thread.join(); } catch (Exception e) { } } } public List<VideoResolution> getSupportedResolutions() { if (thread == null) { initCamera(); } if (nvt == null) { return null; } try { InitialDevices devices = nvt.getDevices(); MediaDevices media = nvt.getMedia(); Profile profile = findJPEGProfile(devices); String profileToken = profile.getToken(); VideoEncoderConfigurationOptions videoEncoderConfigurationOptions = media .getVideoEncoderConfigurationOptions(profileToken); JpegOptions jpegOptions = videoEncoderConfigurationOptions.getJPEG(); List<VideoResolution> jpegResolutions = jpegOptions.getResolutionsAvailable(); return jpegResolutions; } catch (ConnectException e) { System.err.println("Could not connect to IP camera at " + hostIP + ": " + e.toString()); e.printStackTrace(); return null; } catch (SOAPException e) { System.err.println("Error communicating with IP camera at " + hostIP + ": " + e.toString()); e.printStackTrace(); return null; } catch (Exception e) { System.err.println("Unknown error communicating with IP camera at " + hostIP + ": " + e.toString()); e.printStackTrace(); return null; } } public String getHostIP() { return hostIP; } public synchronized void setHostIP(String hostIP) { this.hostIP = hostIP; setDirty(true); initCamera(); } public String getUsername() { return username; } public synchronized void setUsername(String username) { this.username = username; setDirty(true); } public String getPassword() { return password; } public synchronized void setPassword(String password) { this.password = password; setDirty(true); } public String getPreferredResolution() { return preferredResolution; } public void setPreferredResolution(String preferredResolution) { this.preferredResolution = preferredResolution; setDirty(true); } public int getResizeWidth() { return resizeWidth; } public void setResizeWidth(int resizeWidth) { this.resizeWidth = resizeWidth; } public int getResizeHeight() { return resizeHeight; } public void setResizeHeight(int resizeHeight) { this.resizeHeight = resizeHeight; } public int getFps() { return fps; } public void setFps(int fps) { this.fps = fps; } public boolean isDirty() { return dirty; } public void setDirty(boolean dirty) { this.dirty = dirty; } @Override public Wizard getConfigurationWizard() { return new OnvifIPCameraConfigurationWizard(this); } @Override public String getPropertySheetHolderTitle() { return getClass().getSimpleName() + " " + getName(); } @Override public PropertySheetHolder[] getChildPropertySheetHolders() { // TODO Auto-generated method stub return null; } }