package io.evercam.network.onvif; import io.evercam.network.EvercamDiscover; import io.evercam.network.discovery.DiscoveredCamera; import io.evercam.network.discovery.IpTranslator; import java.io.StringReader; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Locale; import java.util.UUID; import org.simpleframework.xml.core.Persister; import org.simpleframework.xml.stream.InputNode; import org.simpleframework.xml.stream.NodeBuilder; public abstract class OnvifDiscovery { private static final int SOCKET_TIMEOUT = 4000; private static final String PROBE_MESSAGE = "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:a=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\"><s:Header><a:Action s:mustUnderstand=\"1\">http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</a:Action><a:MessageID>uuid:21859bf9-6193-4c8a-ad50-d082e6d296ab</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><a:To s:mustUnderstand=\"1\">urn:schemas-xmlsoap-org:ws:2005:04:discovery</a:To></s:Header><s:Body><Probe xmlns=\"http://schemas.xmlsoap.org/ws/2005/04/discovery\"><d:Types xmlns:d=\"http://schemas.xmlsoap.org/ws/2005/04/discovery\" xmlns:dp0=\"http://www.onvif.org/ver10/network/wsdl\">dp0:NetworkVideoTransmitter</d:Types></Probe></s:Body></s:Envelope>"; private static final String PROBE_IP = "239.255.255.250"; private static final int PROBE_PORT = 3702; private static final String SCOPE_NAME = "onvif://www.onvif.org/name/"; private static final String SCOPE_HARDWARE = "onvif://www.onvif.org/hardware/"; public OnvifDiscovery() { } public ArrayList<DiscoveredCamera> probe() { ArrayList<DiscoveredCamera> cameraList = new ArrayList<DiscoveredCamera>(); try { DatagramSocket datagramSocket = new DatagramSocket(); datagramSocket.setSoTimeout(SOCKET_TIMEOUT); InetAddress multicastAddress = InetAddress.getByName(PROBE_IP); if (multicastAddress == null) { // System.out.println("InetAddress.getByName() for multicast returns null"); return cameraList; } // Send the UDP probe message String soapMessage = getProbeSoapMessage(); // System.out.println(soapMessage); byte[] soapMessageByteArray = soapMessage.getBytes(); DatagramPacket datagramPacketSend = new DatagramPacket( soapMessageByteArray, soapMessageByteArray.length, multicastAddress, PROBE_PORT); datagramSocket.send(datagramPacketSend); ArrayList<String> uuidArrayList = new ArrayList<String>(); while (true) { // System.out.println("Receiving..."); byte[] responseMessageByteArray = new byte[4000]; DatagramPacket datagramPacketRecieve = new DatagramPacket( responseMessageByteArray, responseMessageByteArray.length); datagramSocket.receive(datagramPacketRecieve); String responseMessage = new String( datagramPacketRecieve.getData()); EvercamDiscover.printLogMessage("\nResponse Message:\n" + responseMessage); StringReader stringReader = new StringReader(responseMessage); InputNode localInputNode = NodeBuilder.read(stringReader); EnvelopeProbeMatches localEnvelopeProbeMatches = new Persister() .read(EnvelopeProbeMatches.class, localInputNode); if (localEnvelopeProbeMatches.BodyProbeMatches.ProbeMatches.listProbeMatches .size() <= 0) { continue; } ProbeMatch localProbeMatch = localEnvelopeProbeMatches.BodyProbeMatches.ProbeMatches.listProbeMatches .get(0); // EvercamDiscover.printLogMessage("Probe matches with UUID:\n" // + // localProbeMatch.EndpointReference.Address + " URL: " + // localProbeMatch.XAddrs); if (uuidArrayList .contains(localProbeMatch.EndpointReference.Address)) { EvercamDiscover.printLogMessage("ONVIFDiscovery: Address " + localProbeMatch.EndpointReference.Address + " already added"); continue; } uuidArrayList.add(localProbeMatch.EndpointReference.Address); DiscoveredCamera discoveredCamera = getCameraFromProbeMatch(localProbeMatch); if (discoveredCamera.hasValidIpv4Address()) { onActiveOnvifDevice(discoveredCamera); cameraList.add(discoveredCamera); } } } catch (Exception e) { // ONVIF timeout. Don't print anything. } return cameraList; } private static String getProbeSoapMessage() { return PROBE_MESSAGE.replaceFirst( "<a:MessageID>uuid:.+?</a:MessageID>", "<a:MessageID>uuid:" + UUID.randomUUID().toString() + "</a:MessageID>"); } private static DiscoveredCamera getCameraFromProbeMatch( ProbeMatch probeMatch) { DiscoveredCamera discoveredCamera = null; try { String[] urlArray = probeMatch.XAddrs.split("\\s"); String[] scopeArray = probeMatch.Scopes.split("\\s"); String scopeModel = ""; String scopeVendor = ""; for (String scope : scopeArray) { final String URL_SPACE = "%20"; if (scope.contains(SCOPE_NAME)) { scopeVendor = scope.replace(SCOPE_NAME, "").replace( URL_SPACE, " "); } if (scope.contains(SCOPE_HARDWARE)) { scopeModel = scope.replace(SCOPE_HARDWARE, "").replace( URL_SPACE, " "); } } // Make the ONVIF scopes match vendor + model pattern if (scopeVendor.contains(scopeModel)) { scopeVendor = scopeVendor.replace(scopeModel, "").replace(" ", ""); } try { String ipAddressString = ""; int httpPort = 0; for (String urlString : urlArray) { URL localURL = new URL(urlString); String urlHost = localURL.getHost(); // Make sure it's a valid local IPv4 address if (IpTranslator.isLocalIpv4(urlHost)) { ipAddressString = urlHost; httpPort = localURL.getPort(); if (httpPort == -1) { httpPort = 80; } break; // Only break when it gets a valid address } else { EvercamDiscover .printLogMessage("Discarded a ONVIF IP: " + urlHost); } } discoveredCamera = new DiscoveredCamera(ipAddressString); discoveredCamera.setHttp(httpPort); if (!scopeVendor.isEmpty()) { discoveredCamera.setVendor(scopeVendor .toLowerCase(Locale.UK)); } if (!scopeModel.isEmpty()) { discoveredCamera .setModel(scopeModel.toLowerCase(Locale.UK)); } } catch (MalformedURLException localMalformedURLException) { EvercamDiscover.printLogMessage("Cannot parse xAddr: " + probeMatch.XAddrs); } } catch (Exception e) { EvercamDiscover.printLogMessage("Parse ONVIF search result error: " + e.getMessage()); } EvercamDiscover.printLogMessage("ONVIF camera: " + discoveredCamera.toString()); return discoveredCamera; } public abstract void onActiveOnvifDevice(DiscoveredCamera discoveredCamera); }