Merge pull request #3404 from jp-bennett/master
Adds ONVIF motion Detection
This commit is contained in:
commit
5840602b6e
|
@ -518,6 +518,15 @@ endif()
|
||||||
#list(APPEND ZM_BIN_LIBS "${Boost_LIBRARIES}")
|
#list(APPEND ZM_BIN_LIBS "${Boost_LIBRARIES}")
|
||||||
#endif()
|
#endif()
|
||||||
|
|
||||||
|
|
||||||
|
find_package(GSOAP 2.0.0)
|
||||||
|
if (GSOAP_FOUND)
|
||||||
|
set(optlibsfound "${optlibsfound} gsoap")
|
||||||
|
add_compile_definitions(WITH_GSOAP)
|
||||||
|
else()
|
||||||
|
set(optlibsnotfound "${optlibsnotfound} gsoap")
|
||||||
|
endif()
|
||||||
|
|
||||||
if(NOT ZM_NO_RTSPSERVER)
|
if(NOT ZM_NO_RTSPSERVER)
|
||||||
set(HAVE_RTSP_SERVER 1)
|
set(HAVE_RTSP_SERVER 1)
|
||||||
else()
|
else()
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
#
|
||||||
|
# This module detects if gsoap is installed and determines where the
|
||||||
|
# include files and libraries are.
|
||||||
|
#
|
||||||
|
# This code sets the following variables:
|
||||||
|
#
|
||||||
|
# GSOAP_IMPORT_DIR = full path to the gsoap import directory
|
||||||
|
# GSOAP_LIBRARIES = full path to the gsoap libraries
|
||||||
|
# GSOAP_SSL_LIBRARIES = full path to the gsoap ssl libraries
|
||||||
|
# GSOAP_INCLUDE_DIR = include dir to be used when using the gsoap library
|
||||||
|
# GSOAP_PLUGIN_DIR = gsoap plugins directory
|
||||||
|
# GSOAP_WSDL2H = wsdl2h binary
|
||||||
|
# GSOAP_SOAPCPP2 = soapcpp2 binary
|
||||||
|
# GSOAP_FOUND = set to true if gsoap was found successfully
|
||||||
|
#
|
||||||
|
# GSOAP_ROOT
|
||||||
|
# setting this enables search for gsoap libraries / headers in this location
|
||||||
|
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# GSOAP Import Directories
|
||||||
|
# -----------------------------------------------------
|
||||||
|
find_path(GSOAP_IMPORT_DIR
|
||||||
|
NAMES wsa.h
|
||||||
|
PATHS ${GSOAP_ROOT}/import ${GSOAP_ROOT}/share/gsoap/import
|
||||||
|
)
|
||||||
|
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# GSOAP Libraries
|
||||||
|
# -----------------------------------------------------
|
||||||
|
find_library(GSOAP_CXX_LIBRARIES
|
||||||
|
NAMES gsoap++
|
||||||
|
HINTS ${GSOAP_ROOT}/lib ${GSOAP_ROOT}/lib64
|
||||||
|
${GSOAP_ROOT}/lib32
|
||||||
|
DOC "The main gsoap library"
|
||||||
|
)
|
||||||
|
find_library(GSOAP_SSL_CXX_LIBRARIES
|
||||||
|
NAMES gsoapssl++
|
||||||
|
HINTS ${GSOAP_ROOT}/lib ${GSOAP_ROOT}/lib64
|
||||||
|
${GSOAP_ROOT}/lib32
|
||||||
|
DOC "The ssl gsoap library"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# GSOAP Include Directories
|
||||||
|
# -----------------------------------------------------
|
||||||
|
find_path(GSOAP_INCLUDE_DIR
|
||||||
|
NAMES stdsoap2.h
|
||||||
|
HINTS ${GSOAP_ROOT} ${GSOAP_ROOT}/include ${GSOAP_ROOT}/include/*
|
||||||
|
DOC "The gsoap include directory"
|
||||||
|
)
|
||||||
|
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# GSOAP plugin Directories
|
||||||
|
# -----------------------------------------------------
|
||||||
|
find_path(GSOAP_PLUGIN_DIR
|
||||||
|
NAMES wsseapi.c
|
||||||
|
HINTS ${GSOAP_ROOT} /usr/share/gsoap/plugin
|
||||||
|
DOC "The gsoap plugin directory"
|
||||||
|
)
|
||||||
|
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# GSOAP Binaries
|
||||||
|
# ----------------------------------------------------
|
||||||
|
if(NOT GSOAP_TOOL_DIR)
|
||||||
|
set(GSOAP_TOOL_DIR GSOAP_ROOT)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_program(GSOAP_WSDL2H
|
||||||
|
NAMES wsdl2h
|
||||||
|
HINTS ${GSOAP_TOOL_DIR}/bin
|
||||||
|
DOC "The gsoap bin directory"
|
||||||
|
)
|
||||||
|
find_program(GSOAP_SOAPCPP2
|
||||||
|
NAMES soapcpp2
|
||||||
|
HINTS ${GSOAP_TOOL_DIR}/bin
|
||||||
|
DOC "The gsoap bin directory"
|
||||||
|
)
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# GSOAP version
|
||||||
|
# try to determine the flagfor the 2.7.6 compatiblity, break with 2.7.13 and re-break with 2.7.16
|
||||||
|
# ----------------------------------------------------
|
||||||
|
if(GSOAP_SOAPCPP2)
|
||||||
|
execute_process(COMMAND ${GSOAP_SOAPCPP2} "-V" OUTPUT_VARIABLE GSOAP_STRING_VERSION ERROR_VARIABLE GSOAP_STRING_VERSION )
|
||||||
|
string(REGEX MATCH "[0-9]*\\.[0-9]*\\.[0-9]*" GSOAP_VERSION ${GSOAP_STRING_VERSION})
|
||||||
|
endif()
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# GSOAP_276_COMPAT_FLAGS and GSOAPVERSION
|
||||||
|
# try to determine the flagfor the 2.7.6 compatiblity, break with 2.7.13 and re-break with 2.7.16
|
||||||
|
# ----------------------------------------------------
|
||||||
|
if( "${GSOAP_VERSION}" VERSION_LESS "2.7.6")
|
||||||
|
set(GSOAP_276_COMPAT_FLAGS "")
|
||||||
|
elseif ( "${GSOAP_VERSION}" VERSION_LESS "2.7.14")
|
||||||
|
set(GSOAP_276_COMPAT_FLAGS "-z")
|
||||||
|
else ( "${GSOAP_VERSION}" VERSION_LESS "2.7.14")
|
||||||
|
set(GSOAP_276_COMPAT_FLAGS "-z1 -z2")
|
||||||
|
endif ( "${GSOAP_VERSION}" VERSION_LESS "2.7.6")
|
||||||
|
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# handle the QUIETLY and REQUIRED arguments and set GSOAP_FOUND to TRUE if
|
||||||
|
# all listed variables are TRUE
|
||||||
|
# -----------------------------------------------------
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(GSOAP DEFAULT_MSG GSOAP_CXX_LIBRARIES
|
||||||
|
GSOAP_INCLUDE_DIR GSOAP_WSDL2H GSOAP_SOAPCPP2)
|
||||||
|
mark_as_advanced(GSOAP_INCLUDE_DIR GSOAP_LIBRARIES GSOAP_WSDL2H GSOAP_SOAPCPP2)
|
||||||
|
|
||||||
|
if(GSOAP_FOUND)
|
||||||
|
if(GSOAP_FIND_REQUIRED AND GSOAP_FIND_VERSION AND ${GSOAP_VERSION} VERSION_LESS ${GSOAP_FIND_VERSION})
|
||||||
|
message(SEND_ERROR "Found GSOAP version ${GSOAP_VERSION} less then required ${GSOAP_FIND_VERSION}.")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
|
@ -463,6 +463,7 @@ CREATE TABLE `Monitors` (
|
||||||
`ONVIF_Username` VARCHAR(64) NOT NULL DEFAULT '',
|
`ONVIF_Username` VARCHAR(64) NOT NULL DEFAULT '',
|
||||||
`ONVIF_Password` VARCHAR(64) NOT NULL DEFAULT '',
|
`ONVIF_Password` VARCHAR(64) NOT NULL DEFAULT '',
|
||||||
`ONVIF_Options` VARCHAR(64) NOT NULL DEFAULT '',
|
`ONVIF_Options` VARCHAR(64) NOT NULL DEFAULT '',
|
||||||
|
`ONVIF_Event_Listener` BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
`Device` tinytext NOT NULL default '',
|
`Device` tinytext NOT NULL default '',
|
||||||
`Channel` tinyint(3) unsigned NOT NULL default '0',
|
`Channel` tinyint(3) unsigned NOT NULL default '0',
|
||||||
`Format` int(10) unsigned NOT NULL default '0',
|
`Format` int(10) unsigned NOT NULL default '0',
|
||||||
|
|
|
@ -1,2 +1,21 @@
|
||||||
/* Change Cause from varchar(32) to TEXT. We now include alarmed zone name */
|
/* Change Cause from varchar(32) to TEXT. We now include alarmed zone name */
|
||||||
ALTER TABLE `Events` MODIFY `Cause` TEXT;
|
ALTER TABLE `Events` MODIFY `Cause` TEXT;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Update Monitors table to have a ONVIF_Event_Listener Column
|
||||||
|
--
|
||||||
|
|
||||||
|
SELECT 'Checking for ONVIF_Event_Listener in Monitors';
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*)
|
||||||
|
FROM INFORMATION_SCHEMA.COLUMNS
|
||||||
|
WHERE table_name = 'Monitors'
|
||||||
|
AND table_schema = DATABASE()
|
||||||
|
AND column_name = 'ONVIF_Event_Listener'
|
||||||
|
) > 0,
|
||||||
|
"SELECT 'Column ONVIF_Event_Listener already exists in Monitorss'",
|
||||||
|
"ALTER TABLE `Monitors` ADD COLUMN `ONVIF_Event_Listener` BOOLEAN NOT NULL default false AFTER `ONVIF_Options`"
|
||||||
|
));
|
||||||
|
|
||||||
|
PREPARE stmt FROM @s;
|
||||||
|
EXECUTE stmt;
|
||||||
|
|
|
@ -56,6 +56,13 @@ $serial = $primary_key = 'Id';
|
||||||
Enabled
|
Enabled
|
||||||
LinkedMonitors
|
LinkedMonitors
|
||||||
Triggers
|
Triggers
|
||||||
|
EventStartCommand
|
||||||
|
EventEndCommand
|
||||||
|
ONVIF_URL
|
||||||
|
ONVIF_Username
|
||||||
|
ONVIF_Password
|
||||||
|
ONVIF_Options
|
||||||
|
ONVIF_Event_Listener
|
||||||
Device
|
Device
|
||||||
Channel
|
Channel
|
||||||
Format
|
Format
|
||||||
|
|
|
@ -6,6 +6,7 @@ configure_file(zm_config_data.h.in "${CMAKE_BINARY_DIR}/zm_config_data.h" @ONLY)
|
||||||
# Group together all the source files that are used by all the binaries (zmc, zmu, zms etc)
|
# Group together all the source files that are used by all the binaries (zmc, zmu, zms etc)
|
||||||
set(ZM_BIN_SRC_FILES
|
set(ZM_BIN_SRC_FILES
|
||||||
zm_analysis_thread.cpp
|
zm_analysis_thread.cpp
|
||||||
|
zm_poll_thread.cpp
|
||||||
zm_buffer.cpp
|
zm_buffer.cpp
|
||||||
zm_camera.cpp
|
zm_camera.cpp
|
||||||
zm_comms.cpp
|
zm_comms.cpp
|
||||||
|
@ -67,6 +68,57 @@ set(ZM_BIN_SRC_FILES
|
||||||
zm_zone.cpp
|
zm_zone.cpp
|
||||||
zm_storage.cpp)
|
zm_storage.cpp)
|
||||||
|
|
||||||
|
if(GSOAP_FOUND)
|
||||||
|
set(ZM_BIN_SRC_FILES
|
||||||
|
${ZM_BIN_SRC_FILES}
|
||||||
|
${CMAKE_BINARY_DIR}/generated/soapPullPointSubscriptionBindingProxy.cpp
|
||||||
|
${CMAKE_BINARY_DIR}/generated/soapC.cpp
|
||||||
|
${GSOAP_PLUGIN_DIR}/smdevp.c
|
||||||
|
${GSOAP_PLUGIN_DIR}/mecevp.c
|
||||||
|
${GSOAP_PLUGIN_DIR}/wsaapi.c
|
||||||
|
${GSOAP_PLUGIN_DIR}/wsseapi.c
|
||||||
|
${GSOAP_PLUGIN_DIR}/../custom/struct_timeval.c
|
||||||
|
)
|
||||||
|
|
||||||
|
SET(GCC_COMPILE_FLAGS "-DWITH_OPENSSL -DWITH_DOM")
|
||||||
|
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COMPILE_FLAGS}")
|
||||||
|
|
||||||
|
#Create the directory that will host files generated by GSOAP
|
||||||
|
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/generated)
|
||||||
|
|
||||||
|
#some files are generated by gsoap
|
||||||
|
set_source_files_properties( ${CMAKE_BINARY_DIR}/generated/soapClientLib.c PROPERTIES GENERATED TRUE )
|
||||||
|
set_source_files_properties( ${CMAKE_BINARY_DIR}/generated/soapC.c PROPERTIES GENERATED TRUE )
|
||||||
|
set_source_files_properties( ${CMAKE_BINARY_DIR}/generated/soapPullPointSubscriptionBindingProxy.cpp PROPERTIES GENERATED TRUE )
|
||||||
|
set_source_files_properties( ${GSOAP_PLUGIN_DIR}/smdevp.c PROPERTIES LANGUAGE CXX)
|
||||||
|
set_source_files_properties( ${GSOAP_PLUGIN_DIR}/mecevp.c PROPERTIES LANGUAGE CXX)
|
||||||
|
set_source_files_properties( ${GSOAP_PLUGIN_DIR}/wsaapi.c PROPERTIES LANGUAGE CXX)
|
||||||
|
set_source_files_properties( ${GSOAP_PLUGIN_DIR}/wsseapi.c PROPERTIES LANGUAGE CXX)
|
||||||
|
set_source_files_properties( ${GSOAP_PLUGIN_DIR}/../custom/struct_timeval.c PROPERTIES LANGUAGE CXX)
|
||||||
|
|
||||||
|
#Create a cmake target that generate gsoap files
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${CMAKE_BINARY_DIR}/generated/soapC.cpp
|
||||||
|
OUTPUT ${CMAKE_BINARY_DIR}/generated/soapPullPointSubscriptionBindingProxy.cpp
|
||||||
|
COMMAND ${GSOAP_WSDL2H} -d -P -O2 -o ${CMAKE_BINARY_DIR}/generated/bindings.h http://www.onvif.org/onvif/ver10/events/wsdl/event.wsdl
|
||||||
|
COMMAND echo '\#import \"wsse.h\"' >> ${CMAKE_BINARY_DIR}/generated/bindings.h
|
||||||
|
COMMAND echo '\#import \"struct_timeval.h\"' >> ${CMAKE_BINARY_DIR}/generated/bindings.h
|
||||||
|
COMMAND ${GSOAP_SOAPCPP2} -n -2 -C -I ${GSOAP_PLUGIN_DIR}/.. -I ${GSOAP_PLUGIN_DIR}/../import/ -I ${GSOAP_PLUGIN_DIR}/../custom/ -d ${CMAKE_BINARY_DIR}/generated -j -x ${CMAKE_BINARY_DIR}/generated/bindings.h
|
||||||
|
COMMENT "CREATING STUBS AND GLUE CODE"
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_target(GSOAP_GENERATION_TARGET
|
||||||
|
DEPENDS ${CMAKE_BINARY_DIR}/generated/soapC.cpp
|
||||||
|
DEPENDS ${CMAKE_BINARY_DIR}/generated/soapPullPointSubscriptionBindingProxy.cpp
|
||||||
|
DEPENDS ${GSOAP_PLUGIN_DIR}/smdevp.c
|
||||||
|
DEPENDS ${GSOAP_PLUGIN_DIR}/mecevp.c
|
||||||
|
DEPENDS ${GSOAP_PLUGIN_DIR}/wsaapi.c
|
||||||
|
DEPENDS ${GSOAP_PLUGIN_DIR}/wsseapi.c
|
||||||
|
DEPENDS ${GSOAP_PLUGIN_DIR}/../custom/struct_timeval.c
|
||||||
|
)
|
||||||
|
|
||||||
|
endif()
|
||||||
|
|
||||||
# A fix for cmake recompiling the source files for every target.
|
# A fix for cmake recompiling the source files for every target.
|
||||||
add_library(zm STATIC ${ZM_BIN_SRC_FILES})
|
add_library(zm STATIC ${ZM_BIN_SRC_FILES})
|
||||||
|
|
||||||
|
@ -75,6 +127,15 @@ target_include_directories(zm
|
||||||
${CMAKE_BINARY_DIR}
|
${CMAKE_BINARY_DIR}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR})
|
${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
|
if(GSOAP_FOUND)
|
||||||
|
target_include_directories(zm
|
||||||
|
PUBLIC
|
||||||
|
${CMAKE_BINARY_DIR}/generated
|
||||||
|
${GSOAP_PLUGIN_DIR}/..
|
||||||
|
${GSOAP_INCLUDE_DIR})
|
||||||
|
|
||||||
|
endif()
|
||||||
|
|
||||||
target_link_libraries(zm
|
target_link_libraries(zm
|
||||||
PUBLIC
|
PUBLIC
|
||||||
FFMPEG::avcodec
|
FFMPEG::avcodec
|
||||||
|
@ -89,6 +150,15 @@ target_link_libraries(zm
|
||||||
PRIVATE
|
PRIVATE
|
||||||
zm-core-interface)
|
zm-core-interface)
|
||||||
|
|
||||||
|
if(GSOAP_FOUND)
|
||||||
|
target_link_libraries(zm
|
||||||
|
PUBLIC
|
||||||
|
${GSOAP_CXX_LIBRARIES}
|
||||||
|
${GSOAP_SSL_CXX_LIBRARIES}
|
||||||
|
${OPENSSL_SSL_LIBRARY}
|
||||||
|
${OPENSSL_CRYPTO_LIBRARY})
|
||||||
|
endif()
|
||||||
|
|
||||||
if(${ZM_JWT_BACKEND} STREQUAL "jwt_cpp")
|
if(${ZM_JWT_BACKEND} STREQUAL "jwt_cpp")
|
||||||
target_link_libraries(zm
|
target_link_libraries(zm
|
||||||
PUBLIC
|
PUBLIC
|
||||||
|
@ -110,6 +180,11 @@ add_executable(zms zms.cpp)
|
||||||
add_executable(zmu zmu.cpp)
|
add_executable(zmu zmu.cpp)
|
||||||
add_executable(zmbenchmark zmbenchmark.cpp)
|
add_executable(zmbenchmark zmbenchmark.cpp)
|
||||||
|
|
||||||
|
if(GSOAP_FOUND)
|
||||||
|
#Make sure that the client is compiled only after gsoap has been processed
|
||||||
|
add_dependencies(zmc GSOAP_GENERATION_TARGET)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_link_libraries(zmc
|
target_link_libraries(zmc
|
||||||
PRIVATE
|
PRIVATE
|
||||||
zm-core-interface
|
zm-core-interface
|
||||||
|
|
|
@ -66,6 +66,14 @@
|
||||||
#define MAP_LOCKED 0
|
#define MAP_LOCKED 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef WITH_GSOAP
|
||||||
|
//Workaround for the gsoap library on RHEL
|
||||||
|
struct Namespace namespaces[] =
|
||||||
|
{
|
||||||
|
{NULL, NULL} // end of table
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
// This is the official SQL (and ordering of the fields) to load a Monitor.
|
// This is the official SQL (and ordering of the fields) to load a Monitor.
|
||||||
// It will be used whereever a Monitor dbrow is needed. WHERE conditions can be appended
|
// It will be used whereever a Monitor dbrow is needed. WHERE conditions can be appended
|
||||||
std::string load_monitor_sql =
|
std::string load_monitor_sql =
|
||||||
|
@ -83,7 +91,7 @@ std::string load_monitor_sql =
|
||||||
"`SectionLength`, `MinSectionLength`, `FrameSkip`, `MotionFrameSkip`, "
|
"`SectionLength`, `MinSectionLength`, `FrameSkip`, `MotionFrameSkip`, "
|
||||||
"`FPSReportInterval`, `RefBlendPerc`, `AlarmRefBlendPerc`, `TrackMotion`, `Exif`,"
|
"`FPSReportInterval`, `RefBlendPerc`, `AlarmRefBlendPerc`, `TrackMotion`, `Exif`,"
|
||||||
"`RTSPServer`, `RTSPStreamName`,"
|
"`RTSPServer`, `RTSPStreamName`,"
|
||||||
"`ONVIF_URL`, `ONVIF_Username`, `ONVIF_Password`, `ONVIF_Options`,"
|
"`ONVIF_URL`, `ONVIF_Username`, `ONVIF_Password`, `ONVIF_Options`, `ONVIF_Event_Listener`, "
|
||||||
"`SignalCheckPoints`, `SignalCheckColour`, `Importance`-1 FROM `Monitors`";
|
"`SignalCheckPoints`, `SignalCheckColour`, `Importance`-1 FROM `Monitors`";
|
||||||
|
|
||||||
std::string CameraType_Strings[] = {
|
std::string CameraType_Strings[] = {
|
||||||
|
@ -446,7 +454,7 @@ Monitor::Monitor()
|
||||||
"SectionLength, MinSectionLength, FrameSkip, MotionFrameSkip, "
|
"SectionLength, MinSectionLength, FrameSkip, MotionFrameSkip, "
|
||||||
"FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif,"
|
"FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif,"
|
||||||
"`RTSPServer`,`RTSPStreamName`,
|
"`RTSPServer`,`RTSPStreamName`,
|
||||||
"`ONVIF_URL`, `ONVIF_Username`, `ONVIF_Password`, `ONVIF_Options`,"
|
"`ONVIF_URL`, `ONVIF_Username`, `ONVIF_Password`, `ONVIF_Options`, `ONVIF_Event_Listener`, "
|
||||||
"SignalCheckPoints, SignalCheckColour, Importance-1 FROM Monitors";
|
"SignalCheckPoints, SignalCheckColour, Importance-1 FROM Monitors";
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -621,6 +629,7 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
||||||
onvif_username = std::string(dbrow[col] ? dbrow[col] : ""); col++;
|
onvif_username = std::string(dbrow[col] ? dbrow[col] : ""); col++;
|
||||||
onvif_password = std::string(dbrow[col] ? dbrow[col] : ""); col++;
|
onvif_password = std::string(dbrow[col] ? dbrow[col] : ""); col++;
|
||||||
onvif_options = std::string(dbrow[col] ? dbrow[col] : ""); col++;
|
onvif_options = std::string(dbrow[col] ? dbrow[col] : ""); col++;
|
||||||
|
onvif_event_listener = (*dbrow[col] != '0'); col++;
|
||||||
|
|
||||||
importance = dbrow[col] ? atoi(dbrow[col]) : 0;// col++;
|
importance = dbrow[col] ? atoi(dbrow[col]) : 0;// col++;
|
||||||
|
|
||||||
|
@ -1059,6 +1068,42 @@ bool Monitor::connect() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//ONVIF Setup
|
||||||
|
#ifdef WITH_GSOAP
|
||||||
|
ONVIF_Trigger_State = FALSE;
|
||||||
|
if (onvif_event_listener) { //Temporarily using this option to enable the feature
|
||||||
|
Debug(1, "Starting ONVIF");
|
||||||
|
ONVIF_Healthy = FALSE;
|
||||||
|
tev__PullMessages.Timeout = "PT600S";
|
||||||
|
tev__PullMessages.MessageLimit = 100;
|
||||||
|
soap = soap_new();
|
||||||
|
soap->connect_timeout = 5;
|
||||||
|
soap->recv_timeout = 5;
|
||||||
|
soap->send_timeout = 5;
|
||||||
|
soap_register_plugin(soap, soap_wsse);
|
||||||
|
proxyEvent = PullPointSubscriptionBindingProxy(soap);
|
||||||
|
std::string full_url = onvif_url + "/Events";
|
||||||
|
proxyEvent.soap_endpoint = full_url.c_str();
|
||||||
|
set_credentials(soap);
|
||||||
|
Debug(1, "ONVIF Endpoint: %s", proxyEvent.soap_endpoint);
|
||||||
|
if (proxyEvent.CreatePullPointSubscription(&request, response) != SOAP_OK) {
|
||||||
|
Warning("Couldn't create subscription!");
|
||||||
|
} else {
|
||||||
|
//Empty the stored messages
|
||||||
|
set_credentials(soap);
|
||||||
|
if (proxyEvent.PullMessages(response.SubscriptionReference.Address, NULL, &tev__PullMessages, tev__PullMessagesResponse) != SOAP_OK) {
|
||||||
|
Warning("Couldn't do initial event pull! %s", response.SubscriptionReference.Address);
|
||||||
|
} else {
|
||||||
|
Debug(1, "Good Initial ONVIF Pull");
|
||||||
|
ONVIF_Healthy = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Debug(1, "Not Starting ONVIF");
|
||||||
|
}
|
||||||
|
//End ONVIF Setup
|
||||||
|
#endif
|
||||||
|
|
||||||
// We set these here because otherwise the first fps calc is meaningless
|
// We set these here because otherwise the first fps calc is meaningless
|
||||||
last_fps_time = std::chrono::system_clock::now();
|
last_fps_time = std::chrono::system_clock::now();
|
||||||
last_analysis_fps_time = std::chrono::system_clock::now();
|
last_analysis_fps_time = std::chrono::system_clock::now();
|
||||||
|
@ -1713,6 +1758,50 @@ void Monitor::UpdateFPS() {
|
||||||
} // end if report fps
|
} // end if report fps
|
||||||
} // void Monitor::UpdateFPS()
|
} // void Monitor::UpdateFPS()
|
||||||
|
|
||||||
|
//Thread where ONVIF polling, and other similar status polling can happen.
|
||||||
|
//Since these can be blocking, run here to avoid intefering with other processing
|
||||||
|
bool Monitor::Poll() {
|
||||||
|
|
||||||
|
#ifdef WITH_GSOAP
|
||||||
|
if (ONVIF_Healthy) {
|
||||||
|
set_credentials(soap);
|
||||||
|
int result = proxyEvent.PullMessages(response.SubscriptionReference.Address, NULL, &tev__PullMessages, tev__PullMessagesResponse);
|
||||||
|
if (result != SOAP_OK) {
|
||||||
|
if (result != -1) //Ignore the timeout error
|
||||||
|
Warning("Failed to get ONVIF messages! %i", result);
|
||||||
|
} else {
|
||||||
|
Debug(1, "Got Good Response! %i", result);
|
||||||
|
for (auto msg : tev__PullMessagesResponse.wsnt__NotificationMessage) {
|
||||||
|
if (msg->Topic->__any.text != NULL &&
|
||||||
|
std::strstr(msg->Topic->__any.text, "MotionAlarm") &&
|
||||||
|
msg->Message.__any.elts != NULL &&
|
||||||
|
msg->Message.__any.elts->next != NULL &&
|
||||||
|
msg->Message.__any.elts->next->elts != NULL &&
|
||||||
|
msg->Message.__any.elts->next->elts->atts != NULL &&
|
||||||
|
msg->Message.__any.elts->next->elts->atts->next != NULL &&
|
||||||
|
msg->Message.__any.elts->next->elts->atts->next->text != NULL) {
|
||||||
|
Debug(1,"Got Motion Alarm!");
|
||||||
|
if (strcmp(msg->Message.__any.elts->next->elts->atts->next->text, "true") == 0) {
|
||||||
|
//Event Start
|
||||||
|
Debug(1,"Triggered on ONVIF");
|
||||||
|
if (!ONVIF_Trigger_State) {
|
||||||
|
Debug(1,"Triggered Event");
|
||||||
|
ONVIF_Trigger_State = TRUE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Debug(1, "Triggered off ONVIF");
|
||||||
|
ONVIF_Trigger_State = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return TRUE;
|
||||||
|
} //end Poll
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Would be nice if this JUST did analysis
|
// Would be nice if this JUST did analysis
|
||||||
// This idea is that we should be analysing as close to the capture frame as possible.
|
// This idea is that we should be analysing as close to the capture frame as possible.
|
||||||
// This function should process as much as possible before returning
|
// This function should process as much as possible before returning
|
||||||
|
@ -1760,6 +1849,19 @@ bool Monitor::Analyse() {
|
||||||
std::string cause;
|
std::string cause;
|
||||||
Event::StringSetMap noteSetMap;
|
Event::StringSetMap noteSetMap;
|
||||||
|
|
||||||
|
#ifdef WITH_GSOAP
|
||||||
|
if (ONVIF_Trigger_State) {
|
||||||
|
score += 9;
|
||||||
|
Debug(1, "Triggered on ONVIF");
|
||||||
|
if (!event) {
|
||||||
|
cause += "ONVIF";
|
||||||
|
}
|
||||||
|
Event::StringSet noteSet;
|
||||||
|
noteSet.insert("ONVIF2");
|
||||||
|
noteSetMap[MOTION_CAUSE] = noteSet;
|
||||||
|
} // end ONVIF_Trigger
|
||||||
|
#endif
|
||||||
|
|
||||||
// Specifically told to be on. Setting the score here will trigger the alarm.
|
// Specifically told to be on. Setting the score here will trigger the alarm.
|
||||||
if (trigger_data->trigger_state == TriggerState::TRIGGER_ON) {
|
if (trigger_data->trigger_state == TriggerState::TRIGGER_ON) {
|
||||||
score += trigger_data->trigger_score;
|
score += trigger_data->trigger_score;
|
||||||
|
@ -2999,6 +3101,16 @@ int Monitor::PrimeCapture() {
|
||||||
}
|
}
|
||||||
} // end if rtsp_server
|
} // end if rtsp_server
|
||||||
|
|
||||||
|
#ifdef WITH_GSOAP //For now, just don't run the thread if no ONVIF support. This may change if we add other long polling options.
|
||||||
|
//ONVIF Thread
|
||||||
|
if (onvif_event_listener) {
|
||||||
|
if (!Poller) {
|
||||||
|
Poller = zm::make_unique<PollThread>(this);
|
||||||
|
} else {
|
||||||
|
Poller->Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (decoding_enabled) {
|
if (decoding_enabled) {
|
||||||
if (!decoder_it) decoder_it = packetqueue.get_video_it(false);
|
if (!decoder_it) decoder_it = packetqueue.get_video_it(false);
|
||||||
if (!decoder) {
|
if (!decoder) {
|
||||||
|
@ -3033,6 +3145,24 @@ int Monitor::Close() {
|
||||||
if (decoder) {
|
if (decoder) {
|
||||||
decoder->Stop();
|
decoder->Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef WITH_GSOAP
|
||||||
|
//ONVIF Teardown
|
||||||
|
if (Poller) {
|
||||||
|
Poller->Stop();
|
||||||
|
}
|
||||||
|
if (onvif_event_listener && soap != nullptr) {
|
||||||
|
Debug(1, "Tearing Down Onvif");
|
||||||
|
_wsnt__Unsubscribe wsnt__Unsubscribe;
|
||||||
|
_wsnt__UnsubscribeResponse wsnt__UnsubscribeResponse;
|
||||||
|
proxyEvent.Unsubscribe(response.SubscriptionReference.Address, NULL, &wsnt__Unsubscribe, wsnt__UnsubscribeResponse);
|
||||||
|
soap_destroy(soap);
|
||||||
|
soap_end(soap);
|
||||||
|
soap_free(soap);
|
||||||
|
soap = nullptr;
|
||||||
|
}//End ONVIF
|
||||||
|
#endif
|
||||||
|
|
||||||
if (analysis_thread) {
|
if (analysis_thread) {
|
||||||
analysis_thread->Stop();
|
analysis_thread->Stop();
|
||||||
}
|
}
|
||||||
|
@ -3138,3 +3268,36 @@ StringVector Monitor::GroupNames() {
|
||||||
}
|
}
|
||||||
return groupnames;
|
return groupnames;
|
||||||
} // end Monitor::GroupNames()
|
} // end Monitor::GroupNames()
|
||||||
|
|
||||||
|
#ifdef WITH_GSOAP
|
||||||
|
//ONVIF Set Credentials
|
||||||
|
void Monitor::set_credentials(struct soap *soap)
|
||||||
|
{
|
||||||
|
soap_wsse_delete_Security(soap);
|
||||||
|
soap_wsse_add_Timestamp(soap, NULL, 10);
|
||||||
|
soap_wsse_add_UsernameTokenDigest(soap, "Auth", onvif_username.c_str(), onvif_password.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
//GSOAP boilerplate
|
||||||
|
int SOAP_ENV__Fault(struct soap *soap, char *faultcode, char *faultstring, char *faultactor, struct SOAP_ENV__Detail *detail, struct SOAP_ENV__Code *SOAP_ENV__Code, struct SOAP_ENV__Reason *SOAP_ENV__Reason, char *SOAP_ENV__Node, char *SOAP_ENV__Role, struct SOAP_ENV__Detail *SOAP_ENV__Detail)
|
||||||
|
{
|
||||||
|
// populate the fault struct from the operation arguments to print it
|
||||||
|
soap_fault(soap);
|
||||||
|
// SOAP 1.1
|
||||||
|
soap->fault->faultcode = faultcode;
|
||||||
|
soap->fault->faultstring = faultstring;
|
||||||
|
soap->fault->faultactor = faultactor;
|
||||||
|
soap->fault->detail = detail;
|
||||||
|
// SOAP 1.2
|
||||||
|
soap->fault->SOAP_ENV__Code = SOAP_ENV__Code;
|
||||||
|
soap->fault->SOAP_ENV__Reason = SOAP_ENV__Reason;
|
||||||
|
soap->fault->SOAP_ENV__Node = SOAP_ENV__Node;
|
||||||
|
soap->fault->SOAP_ENV__Role = SOAP_ENV__Role;
|
||||||
|
soap->fault->SOAP_ENV__Detail = SOAP_ENV__Detail;
|
||||||
|
// set error
|
||||||
|
soap->error = SOAP_FAULT;
|
||||||
|
// handle or display the fault here with soap_stream_fault(soap, std::cerr);
|
||||||
|
// return HTTP 202 Accepted
|
||||||
|
return soap_send_empty_response(soap, SOAP_OK);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "zm_define.h"
|
#include "zm_define.h"
|
||||||
#include "zm_camera.h"
|
#include "zm_camera.h"
|
||||||
#include "zm_analysis_thread.h"
|
#include "zm_analysis_thread.h"
|
||||||
|
#include "zm_poll_thread.h"
|
||||||
#include "zm_decoder_thread.h"
|
#include "zm_decoder_thread.h"
|
||||||
#include "zm_event.h"
|
#include "zm_event.h"
|
||||||
#include "zm_fifo.h"
|
#include "zm_fifo.h"
|
||||||
|
@ -34,6 +35,12 @@
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#ifdef WITH_GSOAP
|
||||||
|
#include "soapPullPointSubscriptionBindingProxy.h"
|
||||||
|
#include "plugin/wsseapi.h"
|
||||||
|
#include <openssl/err.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
class Group;
|
class Group;
|
||||||
|
|
||||||
#define SIGNAL_CAUSE "Signal"
|
#define SIGNAL_CAUSE "Signal"
|
||||||
|
@ -248,6 +255,20 @@ protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
//ONVIF
|
||||||
|
#ifdef WITH_GSOAP
|
||||||
|
struct soap *soap;
|
||||||
|
bool ONVIF_Trigger_State;
|
||||||
|
bool ONVIF_Healthy;
|
||||||
|
_tev__CreatePullPointSubscription request;
|
||||||
|
_tev__CreatePullPointSubscriptionResponse response;
|
||||||
|
_tev__PullMessages tev__PullMessages;
|
||||||
|
_tev__PullMessagesResponse tev__PullMessagesResponse;
|
||||||
|
PullPointSubscriptionBindingProxy proxyEvent;
|
||||||
|
void set_credentials(struct soap *soap);
|
||||||
|
#endif
|
||||||
|
|
||||||
// These are read from the DB and thereafter remain unchanged
|
// These are read from the DB and thereafter remain unchanged
|
||||||
unsigned int id;
|
unsigned int id;
|
||||||
std::string name;
|
std::string name;
|
||||||
|
@ -272,6 +293,7 @@ protected:
|
||||||
std::string onvif_username;
|
std::string onvif_username;
|
||||||
std::string onvif_password;
|
std::string onvif_password;
|
||||||
std::string onvif_options;
|
std::string onvif_options;
|
||||||
|
bool onvif_event_listener;
|
||||||
|
|
||||||
std::string device;
|
std::string device;
|
||||||
int palette;
|
int palette;
|
||||||
|
@ -394,6 +416,7 @@ protected:
|
||||||
|
|
||||||
VideoStore *videoStore;
|
VideoStore *videoStore;
|
||||||
PacketQueue packetqueue;
|
PacketQueue packetqueue;
|
||||||
|
std::unique_ptr<PollThread> Poller;
|
||||||
packetqueue_iterator *analysis_it;
|
packetqueue_iterator *analysis_it;
|
||||||
std::unique_ptr<AnalysisThread> analysis_thread;
|
std::unique_ptr<AnalysisThread> analysis_thread;
|
||||||
packetqueue_iterator *decoder_it;
|
packetqueue_iterator *decoder_it;
|
||||||
|
@ -600,6 +623,7 @@ public:
|
||||||
bool CheckSignal( const Image *image );
|
bool CheckSignal( const Image *image );
|
||||||
bool Analyse();
|
bool Analyse();
|
||||||
bool Decode();
|
bool Decode();
|
||||||
|
bool Poll();
|
||||||
void DumpImage( Image *dump_image ) const;
|
void DumpImage( Image *dump_image ) const;
|
||||||
void TimestampImage(Image *ts_image, SystemTimePoint ts_time) const;
|
void TimestampImage(Image *ts_image, SystemTimePoint ts_time) const;
|
||||||
Event *openEvent(
|
Event *openEvent(
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
#include "zm_poll_thread.h"
|
||||||
|
|
||||||
|
#include "zm_monitor.h"
|
||||||
|
#include "zm_signal.h"
|
||||||
|
#include "zm_time.h"
|
||||||
|
|
||||||
|
PollThread::PollThread(Monitor *monitor) :
|
||||||
|
monitor_(monitor), terminate_(false) {
|
||||||
|
thread_ = std::thread(&PollThread::Run, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
PollThread::~PollThread() {
|
||||||
|
Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PollThread::Start() {
|
||||||
|
if (thread_.joinable()) thread_.join();
|
||||||
|
terminate_ = false;
|
||||||
|
Debug(3, "Starting polling thread");
|
||||||
|
thread_ = std::thread(&PollThread::Run, this);
|
||||||
|
}
|
||||||
|
void PollThread::Stop() {
|
||||||
|
terminate_ = true;
|
||||||
|
if (thread_.joinable()) {
|
||||||
|
thread_.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void PollThread::Run() {
|
||||||
|
while (!(terminate_ or zm_terminate)) {
|
||||||
|
monitor_->Poll();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef ZM_POLL_THREAD_H
|
||||||
|
#define ZM_POLL_THREAD_H
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
class Monitor;
|
||||||
|
|
||||||
|
class PollThread {
|
||||||
|
public:
|
||||||
|
explicit PollThread(Monitor *monitor);
|
||||||
|
~PollThread();
|
||||||
|
PollThread(PollThread &rhs) = delete;
|
||||||
|
PollThread(PollThread &&rhs) = delete;
|
||||||
|
|
||||||
|
void Start();
|
||||||
|
void Stop();
|
||||||
|
bool Stopped() const { return terminate_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Run();
|
||||||
|
|
||||||
|
Monitor *monitor_;
|
||||||
|
std::atomic<bool> terminate_;
|
||||||
|
std::thread thread_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -63,6 +63,7 @@ class Monitor extends ZM_Object {
|
||||||
'ONVIF_Username' => '',
|
'ONVIF_Username' => '',
|
||||||
'ONVIF_Password' => '',
|
'ONVIF_Password' => '',
|
||||||
'ONVIF_Options' => '',
|
'ONVIF_Options' => '',
|
||||||
|
'ONVIF_Event_Listener' => '0',
|
||||||
'Device' => '',
|
'Device' => '',
|
||||||
'Channel' => 0,
|
'Channel' => 0,
|
||||||
'Format' => '0',
|
'Format' => '0',
|
||||||
|
|
|
@ -706,6 +706,10 @@ if (count($available_monitor_ids)) {
|
||||||
<td class="text-right pr-3"><?php echo translate('ONVIF_Options') ?></td>
|
<td class="text-right pr-3"><?php echo translate('ONVIF_Options') ?></td>
|
||||||
<td><input type="text" name="newMonitor[ONVIF_Options]" value="<?php echo validHtmlStr($monitor->ONVIF_Options()) ?>"/></td>
|
<td><input type="text" name="newMonitor[ONVIF_Options]" value="<?php echo validHtmlStr($monitor->ONVIF_Options()) ?>"/></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="text-right pr-3"><?php echo translate('ONVIF_Event_Listener') ?></td>
|
||||||
|
<td><?php echo html_radio('newMonitor[ONVIF_Event_Listener]', array('1'=>translate('Enabled'), '0'=>'Disabled'), $monitor->ONVIF_Event_Listener()); ?></td>
|
||||||
|
</tr>
|
||||||
<?php
|
<?php
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue