这是indexloc提供的服务,不要输入任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,12 @@ private void checkCameraConnection() {
}
return;// ffmpeg snapshot stream is still alive
}

// if ONVIF cam also use connection state which is updated by regular messages to camera
if (thing.getThingTypeUID().getId().equals(ONVIF_THING) && snapshotUri.isEmpty() && onvifCamera.isConnected()) {
return;
}

// Open a HTTP connection without sending any requests as we do not need a snapshot.
Bootstrap localBootstrap = mainBootstrap;
if (localBootstrap != null) {
Expand Down Expand Up @@ -659,7 +665,7 @@ public void operationComplete(@Nullable ChannelFuture future) {
break;
}
ch.writeAndFlush(request);
} else { // an error occured
} else { // an error occurred
cameraCommunicationError(
"Connection Timeout: Check your IP and PORT are correct and the camera can be reached.");
}
Expand Down Expand Up @@ -1417,9 +1423,16 @@ void pollingCameraConnection() {
return;
}
if (cameraConfig.getOnvifPort() > 0 && !onvifCamera.isConnected()) {
if (onvifCamera.isConnectError()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Camera is not reachable");
} else if (onvifCamera.isRefusedError()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Camera refused connection on ONVIF ports.");
}
logger.debug("About to connect to the IP Camera using the ONVIF PORT at IP:{}:{}", cameraConfig.getIp(),
cameraConfig.getOnvifPort());
onvifCamera.connect(thing.getThingTypeUID().getId().equals(ONVIF_THING));
return;
}
if ("ffmpeg".equals(snapshotUri)) {
snapshotIsFfmpeg();
Expand Down Expand Up @@ -1556,9 +1569,6 @@ void pollCameraRunnable() {
if (!snapshotPolling) {
checkCameraConnection();
}
if (!onvifCamera.isConnected()) {
onvifCamera.connect(true);
}
break;
case INSTAR_THING:
if (!snapshotPolling) {
Expand Down Expand Up @@ -1758,15 +1768,26 @@ public void initialize() {
}

private void tryConnecting() {
int firstDelay = 4;
int normalDelay = 12; // doesn't make sense to have faster retry than CONNECT_TIMEOUT, which is 10 seconds, if
// camera is off
if (!thing.getThingTypeUID().getId().equals(GENERIC_THING)
&& !thing.getThingTypeUID().getId().equals(DOORBIRD_THING) && cameraConfig.getOnvifPort() > 0) {
onvifCamera = new OnvifConnection(this, cameraConfig.getIp() + ":" + cameraConfig.getOnvifPort(),
cameraConfig.getUser(), cameraConfig.getPassword());
onvifCamera.setSelectedMediaProfile(cameraConfig.getOnvifMediaProfile());
// Only use ONVIF events if it is not an API camera.
onvifCamera.connect(supportsOnvifEvents());

if (supportsOnvifEvents()) {
// it takes some time to try to retrieve the ONVIF snapshot and stream URLs and update internal members
// on first connect; if connection lost, doesn't make sense to poll to often
firstDelay = 12;
normalDelay = 30;
}
}
cameraConnectionJob = threadPool.scheduleWithFixedDelay(this::pollingCameraConnection, 4, 8, TimeUnit.SECONDS);
cameraConnectionJob = threadPool.scheduleWithFixedDelay(this::pollingCameraConnection, firstDelay, normalDelay,
TimeUnit.SECONDS);
}

private boolean supportsOnvifEvents() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;

Expand Down Expand Up @@ -58,6 +59,21 @@ public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object ms
}
}

@Override
public void userEventTriggered(@Nullable ChannelHandlerContext ctx, @Nullable Object evt) throws Exception {
if (ctx == null) {
return;
}
if (evt instanceof IdleStateEvent) {
IdleStateEvent e = (IdleStateEvent) evt;
logger.trace("IdleStateEvent received {}", e.state());
onvifConnection.setIsConnected(false);
ctx.close();
} else {
logger.trace("Other ONVIF netty channel event occured {}", evt);
}
}

@Override
public void exceptionCaught(@Nullable ChannelHandlerContext ctx, @Nullable Throwable cause) {
if (ctx == null || cause == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import static org.openhab.binding.ipcamera.internal.IpCameraBindingConstants.*;

import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
Expand Down Expand Up @@ -50,6 +51,7 @@
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ConnectTimeoutException;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
Expand Down Expand Up @@ -127,6 +129,8 @@ public enum RequestType {
private String imagingXAddr = "http://" + ipAddress + "/onvif/device_service";
private String ptzXAddr = "http://" + ipAddress + "/onvif/ptz_service";
private String subscriptionXAddr = "http://" + ipAddress + "/onvif/device_service";
private boolean connectError = false;
private boolean refusedError = false;
private boolean isConnected = false;
private int mediaProfileIndex = 0;
private String snapshotUri = "";
Expand Down Expand Up @@ -310,24 +314,15 @@ public void processReply(String message) {
} else if (message.contains("RenewResponse")) {
sendOnvifRequest(RequestType.PullMessages, subscriptionXAddr);
} else if (message.contains("GetSystemDateAndTimeResponse")) {// 1st to be sent.
connecting.lock();
try {
isConnected = true;
} finally {
connecting.unlock();
}
setIsConnected(true);
sendOnvifRequest(RequestType.GetCapabilities, deviceXAddr);
parseDateAndTime(message);
logger.debug("Openhabs UTC dateTime is:{}", getUTCdateTime());
} else if (message.contains("GetCapabilitiesResponse")) {// 2nd to be sent.
parseXAddr(message);
sendOnvifRequest(RequestType.GetProfiles, mediaXAddr);
} else if (message.contains("GetProfilesResponse")) {// 3rd to be sent.
connecting.lock();
try {
isConnected = true;
} finally {
connecting.unlock();
}
setIsConnected(true);
parseProfiles(message);
sendOnvifRequest(RequestType.GetSnapshotUri, mediaXAddr);
sendOnvifRequest(RequestType.GetStreamUri, mediaXAddr);
Expand Down Expand Up @@ -562,33 +557,44 @@ public void sendOnvifRequest(RequestType requestType, String xAddr) {

@Override
public void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast("idleStateHandler", new IdleStateHandler(0, 0, 70));
socketChannel.pipeline().addLast("idleStateHandler", new IdleStateHandler(20, 20, 20));
socketChannel.pipeline().addLast("HttpClientCodec", new HttpClientCodec());
socketChannel.pipeline().addLast("OnvifCodec", new OnvifCodec(getHandle()));
}
});
bootstrap = localBootstap;
}
if (!mainEventLoopGroup.isShuttingDown()) {
localBootstap.connect(new InetSocketAddress(ipAddress, extractPortFromUrl(xAddr)))
.addListener(new ChannelFutureListener() {
bootstrap.connect(new InetSocketAddress(ipAddress, onvifPort)).addListener(new ChannelFutureListener() {

@Override
public void operationComplete(@Nullable ChannelFuture future) {
if (future == null) {
return;
}
if (future.isSuccess()) {
Channel ch = future.channel();
ch.writeAndFlush(request);
} else { // an error occured
logger.debug("Camera is not reachable when using xAddr:{}.", xAddr);
if (isConnected) {
disconnect();
}
@Override
public void operationComplete(@Nullable ChannelFuture future) {
if (future == null) {
return;
}
if (future.isSuccess()) {
connectError = false;
Channel ch = future.channel();
ch.writeAndFlush(request);
} else { // an error occured
if (future.isDone() && !future.isCancelled()) {
Throwable cause = future.cause();
logger.trace("connect failed - cause {}", cause.getMessage());
if (cause instanceof ConnectTimeoutException) {
logger.debug("Camera is not reachable on IP {}", ipAddress);
connectError = true;
} else if ((cause instanceof ConnectException)
&& cause.getMessage().contains("Connection refused")) {
logger.debug("Camera ONVIF port {} is refused.", onvifPort);
refusedError = true;
}
}
});
if (isConnected) {
disconnect();
}
}
}
});
} else {
logger.debug("ONVIF message not sent as connection is shutting down");
}
Expand Down Expand Up @@ -932,6 +938,14 @@ public void connect(boolean useEvents) {
}
}

public boolean isConnectError() {
return connectError;
}

public boolean isRefusedError() {
return refusedError;
}

public boolean isConnected() {
connecting.lock();
try {
Expand All @@ -941,6 +955,17 @@ public boolean isConnected() {
}
}

public void setIsConnected(boolean isConnected) {
connecting.lock();
try {
this.isConnected = isConnected;
this.connectError = false;
this.refusedError = false;
} finally {
connecting.unlock();
}
}

private void cleanup() {
if (!isConnected && !mainEventLoopGroup.isShuttingDown()) {
try {
Expand All @@ -959,9 +984,9 @@ private void cleanup() {
public void disconnect() {
connecting.lock();// Lock out multiple disconnect()/connect() attempts as we try to send Unsubscribe.
try {
isConnected = false;// isConnected is not thread safe, connecting.lock() used as fix.
if (bootstrap != null) {
if (usingEvents && !mainEventLoopGroup.isShuttingDown()) {
if (isConnected && usingEvents && !mainEventLoopGroup.isShuttingDown()) {
// Only makes sense to send if connected
// Some cameras may continue to send events even when they can't reach a server.
sendOnvifRequest(RequestType.Unsubscribe, subscriptionXAddr);
}
Expand All @@ -970,6 +995,8 @@ public void disconnect() {
} else {
cleanup();
}

isConnected = false;// isConnected is not thread safe, connecting.lock() used as fix.
} finally {
connecting.unlock();
}
Expand Down