Merge branch 'master' into multistream
This commit is contained in:
commit
fd3a70172d
|
@ -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()
|
||||||
|
|
|
@ -467,6 +467,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 Monitors'",
|
||||||
|
"ALTER TABLE `Monitors` ADD COLUMN `ONVIF_Event_Listener` BOOLEAN NOT NULL default false AFTER `ONVIF_Options`"
|
||||||
|
));
|
||||||
|
|
||||||
|
PREPARE stmt FROM @s;
|
||||||
|
EXECUTE stmt;
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
%global _hardened_build 1
|
%global _hardened_build 1
|
||||||
|
|
||||||
Name: zoneminder
|
Name: zoneminder
|
||||||
Version: 1.37.6
|
Version: 1.37.7
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: A camera monitoring and analysis tool
|
Summary: A camera monitoring and analysis tool
|
||||||
Group: System Environment/Daemons
|
Group: System Environment/Daemons
|
||||||
|
|
|
@ -30,6 +30,7 @@ Build-Depends: debhelper (>= 11), sphinx-doc, python3-sphinx, dh-linktree, dh-ap
|
||||||
,libdata-entropy-perl
|
,libdata-entropy-perl
|
||||||
,libvncserver-dev
|
,libvncserver-dev
|
||||||
,libjwt-gnutls-dev|libjwt-dev
|
,libjwt-gnutls-dev|libjwt-dev
|
||||||
|
,libgsoap-dev
|
||||||
Standards-Version: 4.5.0
|
Standards-Version: 4.5.0
|
||||||
Homepage: https://www.zoneminder.com/
|
Homepage: https://www.zoneminder.com/
|
||||||
|
|
||||||
|
@ -73,6 +74,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
|
||||||
,libdata-entropy-perl
|
,libdata-entropy-perl
|
||||||
,libvncclient1|libvncclient0
|
,libvncclient1|libvncclient0
|
||||||
,libjwt-gnutls0|libjwt0
|
,libjwt-gnutls0|libjwt0
|
||||||
|
,libgsoap-2.8.104|libgsoap-2.8.91|libgsoap-2.8.75|libgsoap-2.8.60|libgsoap10
|
||||||
Recommends: ${misc:Recommends}
|
Recommends: ${misc:Recommends}
|
||||||
,libapache2-mod-php | php-fpm
|
,libapache2-mod-php | php-fpm
|
||||||
,default-mysql-server | mariadb-server | virtual-mysql-server
|
,default-mysql-server | mariadb-server | virtual-mysql-server
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -702,9 +702,7 @@ bool EventStream::sendFrame(Microseconds delta_us) {
|
||||||
|
|
||||||
// This needs to be abstracted. If we are saving jpgs, then load the capture file.
|
// This needs to be abstracted. If we are saving jpgs, then load the capture file.
|
||||||
// If we are only saving analysis frames, then send that.
|
// If we are only saving analysis frames, then send that.
|
||||||
if (event_data->SaveJPEGs & 1) {
|
if ((frame_type == FRAME_ANALYSIS) && (event_data->SaveJPEGs & 2)) {
|
||||||
filepath = stringtf(staticConfig.capture_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
|
|
||||||
} else if (event_data->SaveJPEGs & 2) {
|
|
||||||
filepath = stringtf(staticConfig.analyse_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
|
filepath = stringtf(staticConfig.analyse_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
|
||||||
if (stat(filepath.c_str(), &filestat) < 0) {
|
if (stat(filepath.c_str(), &filestat) < 0) {
|
||||||
Debug(1, "analyze file %s not found will try to stream from other", filepath.c_str());
|
Debug(1, "analyze file %s not found will try to stream from other", filepath.c_str());
|
||||||
|
@ -714,7 +712,9 @@ bool EventStream::sendFrame(Microseconds delta_us) {
|
||||||
filepath = "";
|
filepath = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ( !ffmpeg_input ) {
|
} else if (event_data->SaveJPEGs & 1) {
|
||||||
|
filepath = stringtf(staticConfig.capture_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
|
||||||
|
} else if (!ffmpeg_input) {
|
||||||
Fatal("JPEGS not saved. zms is not capable of streaming jpegs from mp4 yet");
|
Fatal("JPEGS not saved. zms is not capable of streaming jpegs from mp4 yet");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -991,6 +991,8 @@ void EventStream::runStream() {
|
||||||
// Have to reset start to now when replaying
|
// Have to reset start to now when replaying
|
||||||
start = now;
|
start = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((unsigned int)curr_frame_id <= event_data->frame_count) {
|
||||||
frame_data = &event_data->frames[curr_frame_id-1];
|
frame_data = &event_data->frames[curr_frame_id-1];
|
||||||
|
|
||||||
// frame_data->delta is the time since last frame as a float in seconds
|
// frame_data->delta is the time since last frame as a float in seconds
|
||||||
|
@ -1028,8 +1030,12 @@ void EventStream::runStream() {
|
||||||
Debug(3, "Done sleeping: %" PRIi64 " us",
|
Debug(3, "Done sleeping: %" PRIi64 " us",
|
||||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(delta).count()));
|
static_cast<int64>(std::chrono::duration_cast<Microseconds>(delta).count()));
|
||||||
}
|
}
|
||||||
}
|
} // end if need to sleep
|
||||||
} else {
|
} else {
|
||||||
|
Debug(1, "invalid curr_frame_id %ld !< %lu", curr_frame_id, event_data->frame_count);
|
||||||
|
} // end if not at end of event
|
||||||
|
} else {
|
||||||
|
// Paused
|
||||||
delta = std::chrono::duration_cast<Microseconds>(FPSeconds(
|
delta = std::chrono::duration_cast<Microseconds>(FPSeconds(
|
||||||
ZM_RATE_BASE / ((base_fps ? base_fps : 1) * (replay_rate ? abs(replay_rate * 2) : 2))));
|
ZM_RATE_BASE / ((base_fps ? base_fps : 1) * (replay_rate ? abs(replay_rate * 2) : 2))));
|
||||||
|
|
||||||
|
@ -1088,11 +1094,6 @@ void EventStream::runStream() {
|
||||||
} // end void EventStream::runStream()
|
} // end void EventStream::runStream()
|
||||||
|
|
||||||
bool EventStream::send_file(const std::string &filepath) {
|
bool EventStream::send_file(const std::string &filepath) {
|
||||||
static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE];
|
|
||||||
|
|
||||||
int img_buffer_size = 0;
|
|
||||||
uint8_t *img_buffer = temp_img_buffer;
|
|
||||||
|
|
||||||
FILE *fdj = nullptr;
|
FILE *fdj = nullptr;
|
||||||
fdj = fopen(filepath.c_str(), "rb");
|
fdj = fopen(filepath.c_str(), "rb");
|
||||||
if ( !fdj ) {
|
if ( !fdj ) {
|
||||||
|
@ -1122,10 +1123,16 @@ bool EventStream::send_file(const std::string &filepath) {
|
||||||
// Success
|
// Success
|
||||||
fclose(fdj); /* Close the file handle */
|
fclose(fdj); /* Close the file handle */
|
||||||
return true;
|
return true;
|
||||||
|
} else {
|
||||||
|
Debug(1, "Failed to sendfile?");
|
||||||
}
|
}
|
||||||
Warning("Unable to send raw frame %ld: %s rc %d", curr_frame_id, strerror(errno), rc);
|
Warning("Unable to send raw frame %ld: %s rc %d", curr_frame_id, strerror(errno), rc);
|
||||||
#endif
|
#endif
|
||||||
img_buffer_size = fread(img_buffer, 1, sizeof(temp_img_buffer), fdj);
|
|
||||||
|
static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE];
|
||||||
|
|
||||||
|
uint8_t *img_buffer = temp_img_buffer;
|
||||||
|
int img_buffer_size = fread(img_buffer, 1, sizeof(temp_img_buffer), fdj);
|
||||||
fclose(fdj); /* Close the file handle */
|
fclose(fdj); /* Close the file handle */
|
||||||
if ( !img_buffer_size ) {
|
if ( !img_buffer_size ) {
|
||||||
Info("Unable to read raw frame %ld: %s", curr_frame_id, strerror(errno));
|
Info("Unable to read raw frame %ld: %s", curr_frame_id, strerror(errno));
|
||||||
|
|
|
@ -293,18 +293,16 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
mFormatContext->interrupt_callback.opaque = this;
|
mFormatContext->interrupt_callback.opaque = this;
|
||||||
|
|
||||||
ret = avformat_open_input(&mFormatContext, mPath.c_str(), nullptr, &opts);
|
ret = avformat_open_input(&mFormatContext, mPath.c_str(), nullptr, &opts);
|
||||||
if ( ret != 0 )
|
if (ret != 0) {
|
||||||
{
|
|
||||||
logPrintf(Logger::ERROR + monitor->Importance(),
|
logPrintf(Logger::ERROR + monitor->Importance(),
|
||||||
"Unable to open input %s due to: %s", mPath.c_str(),
|
"Unable to open input %s due to: %s", mPath.c_str(),
|
||||||
av_make_error_string(ret).c_str());
|
av_make_error_string(ret).c_str());
|
||||||
|
|
||||||
if ( mFormatContext ) {
|
if (mFormatContext) {
|
||||||
avformat_close_input(&mFormatContext);
|
avformat_close_input(&mFormatContext);
|
||||||
mFormatContext = nullptr;
|
mFormatContext = nullptr;
|
||||||
}
|
}
|
||||||
av_dict_free(&opts);
|
av_dict_free(&opts);
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
AVDictionaryEntry *e = nullptr;
|
AVDictionaryEntry *e = nullptr;
|
||||||
|
|
|
@ -31,8 +31,7 @@ int FFmpeg_Input::Open(
|
||||||
const AVStream * audio_in_stream,
|
const AVStream * audio_in_stream,
|
||||||
const AVCodecContext * audio_in_ctx
|
const AVCodecContext * audio_in_ctx
|
||||||
) {
|
) {
|
||||||
video_stream_id = video_in_stream->index;
|
int max_stream_index = video_stream_id = video_in_stream->index;
|
||||||
int max_stream_index = video_in_stream->index;
|
|
||||||
|
|
||||||
if ( audio_in_stream ) {
|
if ( audio_in_stream ) {
|
||||||
max_stream_index = video_in_stream->index > audio_in_stream->index ? video_in_stream->index : audio_in_stream->index;
|
max_stream_index = video_in_stream->index > audio_in_stream->index ? video_in_stream->index : audio_in_stream->index;
|
||||||
|
|
|
@ -28,7 +28,6 @@ class FifoStream : public StreamBase {
|
||||||
std::string stream_path;
|
std::string stream_path;
|
||||||
int total_read;
|
int total_read;
|
||||||
int bytes_read;
|
int bytes_read;
|
||||||
unsigned int frame_count;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
typedef enum { UNKNOWN, MJPEG, RAW } StreamType;
|
typedef enum { UNKNOWN, MJPEG, RAW } StreamType;
|
||||||
|
@ -39,9 +38,9 @@ class FifoStream : public StreamBase {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FifoStream() :
|
FifoStream() :
|
||||||
|
StreamBase(),
|
||||||
total_read(0),
|
total_read(0),
|
||||||
bytes_read(0),
|
bytes_read(0),
|
||||||
frame_count(0),
|
|
||||||
stream_type(UNKNOWN)
|
stream_type(UNKNOWN)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
|
|
@ -46,9 +46,9 @@ FileCamera::FileCamera(
|
||||||
p_hue,
|
p_hue,
|
||||||
p_colour,
|
p_colour,
|
||||||
p_capture,
|
p_capture,
|
||||||
p_record_audio)
|
p_record_audio),
|
||||||
|
path(p_path)
|
||||||
{
|
{
|
||||||
path = std::string(p_path);
|
|
||||||
if (capture) {
|
if (capture) {
|
||||||
Initialise();
|
Initialise();
|
||||||
}
|
}
|
||||||
|
|
|
@ -214,25 +214,26 @@ Image::Image(int p_width, int p_linesize, int p_height, int p_colours, int p_sub
|
||||||
update_function_pointers();
|
update_function_pointers();
|
||||||
}
|
}
|
||||||
|
|
||||||
Image::Image(const AVFrame *frame) {
|
Image::Image(const AVFrame *frame) :
|
||||||
|
colours(ZM_COLOUR_RGB32),
|
||||||
|
padding(0),
|
||||||
|
subpixelorder(ZM_SUBPIX_ORDER_RGBA),
|
||||||
|
imagePixFormat(AV_PIX_FMT_RGBA),
|
||||||
|
buffer(0),
|
||||||
|
holdbuffer(0)
|
||||||
|
{
|
||||||
width = frame->width;
|
width = frame->width;
|
||||||
height = frame->height;
|
height = frame->height;
|
||||||
pixels = width*height;
|
pixels = width*height;
|
||||||
|
|
||||||
zm_dump_video_frame(frame, "Image.Assign(frame)");
|
zm_dump_video_frame(frame, "Image.Assign(frame)");
|
||||||
// FIXME
|
// FIXME
|
||||||
colours = ZM_COLOUR_RGB32;
|
|
||||||
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
|
||||||
imagePixFormat = AV_PIX_FMT_RGBA;
|
|
||||||
//(AVPixelFormat)frame->format;
|
//(AVPixelFormat)frame->format;
|
||||||
|
|
||||||
size = av_image_get_buffer_size(AV_PIX_FMT_RGBA, width, height, 32);
|
size = av_image_get_buffer_size(AV_PIX_FMT_RGBA, width, height, 32);
|
||||||
// av_image_get_linesize isn't aligned, so we have to do that.
|
// av_image_get_linesize isn't aligned, so we have to do that.
|
||||||
linesize = FFALIGN(av_image_get_linesize(AV_PIX_FMT_RGBA, width, 0), 32);
|
linesize = FFALIGN(av_image_get_linesize(AV_PIX_FMT_RGBA, width, 0), 32);
|
||||||
padding = 0;
|
|
||||||
|
|
||||||
buffer = nullptr;
|
|
||||||
holdbuffer = 0;
|
|
||||||
AllocImgBuffer(size);
|
AllocImgBuffer(size);
|
||||||
this->Assign(frame);
|
this->Assign(frame);
|
||||||
}
|
}
|
||||||
|
@ -1677,15 +1678,15 @@ void Image::Overlay( const Image &image ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* RGB32 compatible: complete */
|
/* RGB32 compatible: complete */
|
||||||
void Image::Overlay( const Image &image, unsigned int x, unsigned int y ) {
|
void Image::Overlay( const Image &image, const unsigned int lo_x, const unsigned int lo_y ) {
|
||||||
if ( !(width < image.width || height < image.height) ) {
|
if ( !(width < image.width || height < image.height) ) {
|
||||||
Panic("Attempt to overlay image too big for destination, %dx%d > %dx%d",
|
Panic("Attempt to overlay image too big for destination, %dx%d > %dx%d",
|
||||||
image.width, image.height, width, height );
|
image.width, image.height, width, height );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !(width < (x+image.width) || height < (y+image.height)) ) {
|
if ( !(width < (lo_x+image.width) || height < (lo_y+image.height)) ) {
|
||||||
Panic("Attempt to overlay image outside of destination bounds, %dx%d @ %dx%d > %dx%d",
|
Panic("Attempt to overlay image outside of destination bounds, %dx%d @ %dx%d > %dx%d",
|
||||||
image.width, image.height, x, y, width, height );
|
image.width, image.height, lo_x, lo_y, width, height );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !(colours == image.colours) ) {
|
if ( !(colours == image.colours) ) {
|
||||||
|
@ -1693,10 +1694,8 @@ void Image::Overlay( const Image &image, unsigned int x, unsigned int y ) {
|
||||||
colours, image.colours);
|
colours, image.colours);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int lo_x = x;
|
unsigned int hi_x = (lo_x+image.width)-1;
|
||||||
unsigned int lo_y = y;
|
unsigned int hi_y = (lo_y+image.height-1);
|
||||||
unsigned int hi_x = (x+image.width)-1;
|
|
||||||
unsigned int hi_y = (y+image.height-1);
|
|
||||||
if ( colours == ZM_COLOUR_GRAY8 ) {
|
if ( colours == ZM_COLOUR_GRAY8 ) {
|
||||||
const uint8_t *psrc = image.buffer;
|
const uint8_t *psrc = image.buffer;
|
||||||
for ( unsigned int y = lo_y; y <= hi_y; y++ ) {
|
for ( unsigned int y = lo_y; y <= hi_y; y++ ) {
|
||||||
|
@ -2732,7 +2731,7 @@ void Image::Flip( bool leftright ) {
|
||||||
AssignDirect(width, height, colours, subpixelorder, flip_buffer, size, ZM_BUFTYPE_ZM);
|
AssignDirect(width, height, colours, subpixelorder, flip_buffer, size, ZM_BUFTYPE_ZM);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Image::Scale(unsigned int factor) {
|
void Image::Scale(const unsigned int factor) {
|
||||||
if ( !factor ) {
|
if ( !factor ) {
|
||||||
Error("Bogus scale factor %d found", factor);
|
Error("Bogus scale factor %d found", factor);
|
||||||
return;
|
return;
|
||||||
|
@ -2756,15 +2755,13 @@ void Image::Scale(unsigned int factor) {
|
||||||
unsigned int h_count = ZM_SCALE_BASE/2;
|
unsigned int h_count = ZM_SCALE_BASE/2;
|
||||||
unsigned int last_h_index = 0;
|
unsigned int last_h_index = 0;
|
||||||
unsigned int last_w_index = 0;
|
unsigned int last_w_index = 0;
|
||||||
unsigned int h_index;
|
|
||||||
for ( unsigned int y = 0; y < height; y++ ) {
|
for ( unsigned int y = 0; y < height; y++ ) {
|
||||||
unsigned char *ps = &buffer[y*wc];
|
unsigned char *ps = &buffer[y*wc];
|
||||||
unsigned int w_count = ZM_SCALE_BASE/2;
|
unsigned int w_count = ZM_SCALE_BASE/2;
|
||||||
unsigned int w_index;
|
|
||||||
last_w_index = 0;
|
last_w_index = 0;
|
||||||
for ( unsigned int x = 0; x < width; x++ ) {
|
for ( unsigned int x = 0; x < width; x++ ) {
|
||||||
w_count += factor;
|
w_count += factor;
|
||||||
w_index = w_count/ZM_SCALE_BASE;
|
unsigned int w_index = w_count/ZM_SCALE_BASE;
|
||||||
for (unsigned int f = last_w_index; f < w_index; f++ ) {
|
for (unsigned int f = last_w_index; f < w_index; f++ ) {
|
||||||
for ( unsigned int c = 0; c < colours; c++ ) {
|
for ( unsigned int c = 0; c < colours; c++ ) {
|
||||||
*pd++ = *(ps+c);
|
*pd++ = *(ps+c);
|
||||||
|
@ -2774,7 +2771,7 @@ void Image::Scale(unsigned int factor) {
|
||||||
last_w_index = w_index;
|
last_w_index = w_index;
|
||||||
}
|
}
|
||||||
h_count += factor;
|
h_count += factor;
|
||||||
h_index = h_count/ZM_SCALE_BASE;
|
unsigned int h_index = h_count/ZM_SCALE_BASE;
|
||||||
for ( unsigned int f = last_h_index+1; f < h_index; f++ ) {
|
for ( unsigned int f = last_h_index+1; f < h_index; f++ ) {
|
||||||
memcpy(pd, pd-nwc, nwc);
|
memcpy(pd, pd-nwc, nwc);
|
||||||
pd += nwc;
|
pd += nwc;
|
||||||
|
@ -2786,17 +2783,14 @@ void Image::Scale(unsigned int factor) {
|
||||||
} else {
|
} else {
|
||||||
unsigned char *pd = scale_buffer;
|
unsigned char *pd = scale_buffer;
|
||||||
unsigned int wc = width*colours;
|
unsigned int wc = width*colours;
|
||||||
unsigned int xstart = factor/2;
|
unsigned int h_count = factor/2;
|
||||||
unsigned int ystart = factor/2;
|
|
||||||
unsigned int h_count = ystart;
|
|
||||||
unsigned int last_h_index = 0;
|
unsigned int last_h_index = 0;
|
||||||
unsigned int last_w_index = 0;
|
unsigned int last_w_index = 0;
|
||||||
unsigned int h_index;
|
|
||||||
for ( unsigned int y = 0; y < height; y++ ) {
|
for ( unsigned int y = 0; y < height; y++ ) {
|
||||||
h_count += factor;
|
h_count += factor;
|
||||||
h_index = h_count/ZM_SCALE_BASE;
|
unsigned int h_index = h_count/ZM_SCALE_BASE;
|
||||||
if ( h_index > last_h_index ) {
|
if ( h_index > last_h_index ) {
|
||||||
unsigned int w_count = xstart;
|
unsigned int w_count = factor/2;
|
||||||
unsigned int w_index;
|
unsigned int w_index;
|
||||||
last_w_index = 0;
|
last_w_index = 0;
|
||||||
|
|
||||||
|
@ -2825,6 +2819,7 @@ void Image::Scale(unsigned int factor) {
|
||||||
|
|
||||||
void Image::Deinterlace_Discard() {
|
void Image::Deinterlace_Discard() {
|
||||||
/* Simple deinterlacing. Copy the even lines into the odd lines */
|
/* Simple deinterlacing. Copy the even lines into the odd lines */
|
||||||
|
// ICON: These can be drastically improved. But who cares?
|
||||||
|
|
||||||
if ( colours == ZM_COLOUR_GRAY8 ) {
|
if ( colours == ZM_COLOUR_GRAY8 ) {
|
||||||
const uint8_t *psrc;
|
const uint8_t *psrc;
|
||||||
|
@ -3107,9 +3102,9 @@ __attribute__((noinline,__target__("sse2")))
|
||||||
#endif
|
#endif
|
||||||
void sse2_fastblend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, double blendpercent) {
|
void sse2_fastblend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, double blendpercent) {
|
||||||
#if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE))
|
#if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE))
|
||||||
static uint32_t divider = 0;
|
|
||||||
static uint32_t clearmask = 0;
|
|
||||||
static double current_blendpercent = 0.0;
|
static double current_blendpercent = 0.0;
|
||||||
|
static uint32_t clearmask = 0;
|
||||||
|
static uint32_t divider = 0;
|
||||||
|
|
||||||
if ( current_blendpercent != blendpercent ) {
|
if ( current_blendpercent != blendpercent ) {
|
||||||
/* Attempt to match the blending percent to one of the possible values */
|
/* Attempt to match the blending percent to one of the possible values */
|
||||||
|
@ -3310,10 +3305,10 @@ void neon32_armv7_fastblend(const uint8_t* col1, const uint8_t* col2, uint8_t* r
|
||||||
|
|
||||||
__attribute__((noinline)) void neon64_armv8_fastblend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, double blendpercent) {
|
__attribute__((noinline)) void neon64_armv8_fastblend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, double blendpercent) {
|
||||||
#if (defined(__aarch64__) && !defined(ZM_STRIP_NEON))
|
#if (defined(__aarch64__) && !defined(ZM_STRIP_NEON))
|
||||||
static int8_t divider = 0;
|
|
||||||
static double current_blendpercent = 0.0;
|
static double current_blendpercent = 0.0;
|
||||||
|
|
||||||
if(current_blendpercent != blendpercent) {
|
if (current_blendpercent != blendpercent) {
|
||||||
|
static int8_t divider = 0;
|
||||||
/* Attempt to match the blending percent to one of the possible values */
|
/* Attempt to match the blending percent to one of the possible values */
|
||||||
if(blendpercent < 2.34375) {
|
if(blendpercent < 2.34375) {
|
||||||
// 1.5625% blending
|
// 1.5625% blending
|
||||||
|
|
|
@ -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[] = {
|
||||||
|
@ -421,6 +429,10 @@ Monitor::Monitor()
|
||||||
privacy_bitmask(nullptr),
|
privacy_bitmask(nullptr),
|
||||||
n_linked_monitors(0),
|
n_linked_monitors(0),
|
||||||
linked_monitors(nullptr),
|
linked_monitors(nullptr),
|
||||||
|
#ifdef WITH_GSOAP
|
||||||
|
soap(nullptr),
|
||||||
|
ONVIF_Closes_Event(FALSE),
|
||||||
|
#endif
|
||||||
red_val(0),
|
red_val(0),
|
||||||
green_val(0),
|
green_val(0),
|
||||||
blue_val(0),
|
blue_val(0),
|
||||||
|
@ -457,7 +469,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";
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -486,7 +498,7 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
||||||
} else if ( ! strcmp(dbrow[col], "Libvlc") ) {
|
} else if ( ! strcmp(dbrow[col], "Libvlc") ) {
|
||||||
type = LIBVLC;
|
type = LIBVLC;
|
||||||
} else if ( ! strcmp(dbrow[col], "cURL") ) {
|
} else if ( ! strcmp(dbrow[col], "cURL") ) {
|
||||||
type = CURL;
|
type = LIBCURL;
|
||||||
} else if ( ! strcmp(dbrow[col], "VNC") ) {
|
} else if ( ! strcmp(dbrow[col], "VNC") ) {
|
||||||
type = VNC;
|
type = VNC;
|
||||||
} else {
|
} else {
|
||||||
|
@ -635,8 +647,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++;
|
|
||||||
|
|
||||||
/*"SignalCheckPoints, SignalCheckColour, Importance-1 FROM Monitors"; */
|
/*"SignalCheckPoints, SignalCheckColour, Importance-1 FROM Monitors"; */
|
||||||
signal_check_points = atoi(dbrow[col]); col++;
|
signal_check_points = atoi(dbrow[col]); col++;
|
||||||
|
@ -649,6 +660,7 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
||||||
blue_val = BLUE_VAL_BGRA(signal_check_colour);
|
blue_val = BLUE_VAL_BGRA(signal_check_colour);
|
||||||
grayscale_val = signal_check_colour & 0xff; /* Clear all bytes but lowest byte */
|
grayscale_val = signal_check_colour & 0xff; /* Clear all bytes but lowest byte */
|
||||||
|
|
||||||
|
importance = dbrow[col] ? atoi(dbrow[col]) : 0;// col++;
|
||||||
if (importance < 0) importance = 0; // Should only be >= 0
|
if (importance < 0) importance = 0; // Should only be >= 0
|
||||||
|
|
||||||
// How many frames we need to have before we start analysing
|
// How many frames we need to have before we start analysing
|
||||||
|
@ -670,8 +682,9 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
||||||
mem_size = sizeof(SharedData)
|
mem_size = sizeof(SharedData)
|
||||||
+ sizeof(TriggerData)
|
+ sizeof(TriggerData)
|
||||||
+ sizeof(VideoStoreData) //Information to pass back to the capture process
|
+ sizeof(VideoStoreData) //Information to pass back to the capture process
|
||||||
+ (image_buffer_count * sizeof(struct timeval))
|
+ (image_buffer_count*sizeof(struct timeval))
|
||||||
+ (image_buffer_count * image_size)
|
+ (image_buffer_count*image_size)
|
||||||
|
+ image_size // alarm_image
|
||||||
+ 64; /* Padding used to permit aligning the images buffer to 64 byte boundary */
|
+ 64; /* Padding used to permit aligning the images buffer to 64 byte boundary */
|
||||||
|
|
||||||
Debug(1,
|
Debug(1,
|
||||||
|
@ -860,7 +873,7 @@ void Monitor::LoadCamera() {
|
||||||
#endif // HAVE_LIBVLC
|
#endif // HAVE_LIBVLC
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CURL: {
|
case LIBCURL: {
|
||||||
#if HAVE_LIBCURL
|
#if HAVE_LIBCURL
|
||||||
camera = zm::make_unique<cURLCamera>(this,
|
camera = zm::make_unique<cURLCamera>(this,
|
||||||
path.c_str(),
|
path.c_str(),
|
||||||
|
@ -1027,6 +1040,12 @@ bool Monitor::connect() {
|
||||||
image_buffer[i] = new Image(width, height, camera->Colours(), camera->SubpixelOrder(), &(shared_images[i*camera->ImageSize()]));
|
image_buffer[i] = new Image(width, height, camera->Colours(), camera->SubpixelOrder(), &(shared_images[i*camera->ImageSize()]));
|
||||||
image_buffer[i]->HoldBuffer(true); /* Don't release the internal buffer or replace it with another */
|
image_buffer[i]->HoldBuffer(true); /* Don't release the internal buffer or replace it with another */
|
||||||
}
|
}
|
||||||
|
alarm_image.AssignDirect(width, height, camera->Colours(), camera->SubpixelOrder(),
|
||||||
|
&(shared_images[image_buffer_count*camera->ImageSize()]),
|
||||||
|
camera->ImageSize(),
|
||||||
|
ZM_BUFTYPE_DONTFREE
|
||||||
|
);
|
||||||
|
alarm_image.HoldBuffer(true); /* Don't release the internal buffer or replace it with another */
|
||||||
Debug(3, "Allocated %zu %zu image buffers", image_buffer.capacity(), image_buffer.size());
|
Debug(3, "Allocated %zu %zu image buffers", image_buffer.capacity(), image_buffer.size());
|
||||||
|
|
||||||
if (purpose == CAPTURE) {
|
if (purpose == CAPTURE) {
|
||||||
|
@ -1068,6 +1087,45 @@ bool Monitor::connect() {
|
||||||
video_store_data->size = sizeof(VideoStoreData);
|
video_store_data->size = sizeof(VideoStoreData);
|
||||||
usedsubpixorder = camera->SubpixelOrder(); // Used in CheckSignal
|
usedsubpixorder = camera->SubpixelOrder(); // Used in CheckSignal
|
||||||
shared_data->valid = true;
|
shared_data->valid = true;
|
||||||
|
|
||||||
|
//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;
|
||||||
|
if (onvif_options.find("closes_event") != std::string::npos) { //Option to indicate that ONVIF will send a close event message
|
||||||
|
ONVIF_Closes_Event = TRUE;
|
||||||
|
}
|
||||||
|
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
|
||||||
} else if (!shared_data->valid) {
|
} else if (!shared_data->valid) {
|
||||||
Error("Shared data not initialised by capture daemon for monitor %s", name.c_str());
|
Error("Shared data not initialised by capture daemon for monitor %s", name.c_str());
|
||||||
return false;
|
return false;
|
||||||
|
@ -1186,6 +1244,10 @@ void Monitor::AddPrivacyBitmask() {
|
||||||
privacy_bitmask = privacy_image->Buffer();
|
privacy_bitmask = privacy_image->Buffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Image *Monitor::GetAlarmImage() {
|
||||||
|
return &alarm_image;
|
||||||
|
}
|
||||||
|
|
||||||
int Monitor::GetImage(int32_t index, int scale) {
|
int Monitor::GetImage(int32_t index, int scale) {
|
||||||
if (index < 0 || index > image_buffer_count) {
|
if (index < 0 || index > image_buffer_count) {
|
||||||
Debug(1, "Invalid index %d passed. image_buffer_count = %d", index, image_buffer_count);
|
Debug(1, "Invalid index %d passed. image_buffer_count = %d", index, image_buffer_count);
|
||||||
|
@ -1200,26 +1262,23 @@ int Monitor::GetImage(int32_t index, int scale) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Image *image;
|
std::string filename = stringtf("Monitor%u.jpg", id);
|
||||||
// If we are going to be modifying the snapshot before writing, then we need to copy it
|
// If we are going to be modifying the snapshot before writing, then we need to copy it
|
||||||
if ((scale != ZM_SCALE_BASE) || (!config.timestamp_on_capture)) {
|
if ((scale != ZM_SCALE_BASE) || (!config.timestamp_on_capture)) {
|
||||||
alarm_image.Assign(*image_buffer[index]);
|
Image image;
|
||||||
|
image.Assign(*image_buffer[index]);
|
||||||
|
|
||||||
if (scale != ZM_SCALE_BASE) {
|
if (scale != ZM_SCALE_BASE) {
|
||||||
alarm_image.Scale(scale);
|
image.Scale(scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config.timestamp_on_capture) {
|
if (!config.timestamp_on_capture) {
|
||||||
TimestampImage(&alarm_image, SystemTimePoint(zm::chrono::duration_cast<Microseconds>(shared_timestamps[index])));
|
TimestampImage(&image, SystemTimePoint(zm::chrono::duration_cast<Microseconds>(shared_timestamps[index])));
|
||||||
}
|
}
|
||||||
image = &alarm_image;
|
return image.WriteJpeg(filename);
|
||||||
} else {
|
} else {
|
||||||
image = image_buffer[index];
|
return image_buffer[index]->WriteJpeg(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string filename = stringtf("Monitor%u.jpg", id);
|
|
||||||
image->WriteJpeg(filename);
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ZMPacket *Monitor::getSnapshot(int index) const {
|
ZMPacket *Monitor::getSnapshot(int index) const {
|
||||||
|
@ -1727,6 +1786,52 @@ 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;
|
||||||
|
if (!ONVIF_Closes_Event) { //If we get a close event, then we know to expect them.
|
||||||
|
ONVIF_Closes_Event = TRUE;
|
||||||
|
Debug(1,"Setting ClosesEvent");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#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 method should process as much as possible before returning
|
// This method should process as much as possible before returning
|
||||||
|
@ -1774,6 +1879,22 @@ 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;
|
||||||
|
//If the camera isn't going to send an event close, we need to close it here, but only after it has actually triggered an alarm.
|
||||||
|
if (!ONVIF_Closes_Event && state == ALARM)
|
||||||
|
ONVIF_Trigger_State = FALSE;
|
||||||
|
} // 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;
|
||||||
|
@ -1874,6 +1995,8 @@ bool Monitor::Analyse() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (snap->image) {
|
if (snap->image) {
|
||||||
|
alarm_image.Assign(*(snap->image));
|
||||||
|
|
||||||
// decoder may not have been able to provide an image
|
// decoder may not have been able to provide an image
|
||||||
if (!ref_image.Buffer()) {
|
if (!ref_image.Buffer()) {
|
||||||
Debug(1, "Assigning instead of Detecting");
|
Debug(1, "Assigning instead of Detecting");
|
||||||
|
@ -2057,7 +2180,7 @@ bool Monitor::Analyse() {
|
||||||
|
|
||||||
if (state == PREALARM) {
|
if (state == PREALARM) {
|
||||||
// Generate analysis images if necessary
|
// Generate analysis images if necessary
|
||||||
if ((savejpegs > 1) and snap->image) {
|
if (snap->image) {
|
||||||
for (const Zone &zone : zones) {
|
for (const Zone &zone : zones) {
|
||||||
if (zone.Alarmed() and zone.AlarmImage()) {
|
if (zone.Alarmed() and zone.AlarmImage()) {
|
||||||
if (!snap->analysis_image)
|
if (!snap->analysis_image)
|
||||||
|
@ -2065,20 +2188,25 @@ bool Monitor::Analyse() {
|
||||||
snap->analysis_image->Overlay(*(zone.AlarmImage()));
|
snap->analysis_image->Overlay(*(zone.AlarmImage()));
|
||||||
} // end if zone is alarmed
|
} // end if zone is alarmed
|
||||||
} // end foreach zone
|
} // end foreach zone
|
||||||
} // end if savejpegs
|
if (snap->analysis_image != nullptr)
|
||||||
|
alarm_image.Assign(*(snap->analysis_image));
|
||||||
|
} // end if image.
|
||||||
|
|
||||||
// incremement pre alarm image count
|
// incremement pre alarm image count
|
||||||
Event::AddPreAlarmFrame(snap->image, timestamp, score, nullptr);
|
Event::AddPreAlarmFrame(snap->image, timestamp, score, nullptr);
|
||||||
} else if (state == ALARM) {
|
} else if (state == ALARM) {
|
||||||
|
if (snap->image) {
|
||||||
for (const Zone &zone : zones) {
|
for (const Zone &zone : zones) {
|
||||||
if (zone.Alarmed()) {
|
if (zone.Alarmed() and zone.AlarmImage()) {
|
||||||
if (zone.AlarmImage() and (savejpegs > 1) and snap->image) {
|
|
||||||
if (!snap->analysis_image)
|
if (!snap->analysis_image)
|
||||||
snap->analysis_image = new Image(*(snap->image));
|
snap->analysis_image = new Image(*(snap->image));
|
||||||
snap->analysis_image->Overlay(*(zone.AlarmImage()));
|
snap->analysis_image->Overlay(*(zone.AlarmImage()));
|
||||||
}
|
|
||||||
} // end if zone is alarmed
|
} // end if zone is alarmed
|
||||||
} // end foreach zone
|
} // end foreach zone
|
||||||
|
if (snap->analysis_image != nullptr)
|
||||||
|
alarm_image.Assign(*(snap->analysis_image));
|
||||||
|
}
|
||||||
|
|
||||||
if (event) {
|
if (event) {
|
||||||
if (noteSetMap.size() > 0)
|
if (noteSetMap.size() > 0)
|
||||||
event->updateNotes(noteSetMap);
|
event->updateNotes(noteSetMap);
|
||||||
|
@ -2557,23 +2685,21 @@ bool Monitor::Decode() {
|
||||||
} else if (deinterlacing_value == 3) {
|
} else if (deinterlacing_value == 3) {
|
||||||
capture_image->Deinterlace_Blend();
|
capture_image->Deinterlace_Blend();
|
||||||
} else if (deinterlacing_value == 4) {
|
} else if (deinterlacing_value == 4) {
|
||||||
ZMLockedPacket *deinterlace_packet_lock = nullptr;
|
|
||||||
while (!zm_terminate) {
|
while (!zm_terminate) {
|
||||||
ZMLockedPacket *second_packet_lock = packetqueue.get_packet(decoder_it);
|
ZMLockedPacket *deinterlace_packet_lock = packetqueue.get_packet(decoder_it);
|
||||||
if (!second_packet_lock) {
|
if (!deinterlace_packet_lock) {
|
||||||
packetqueue.unlock(packet_lock);
|
packetqueue.unlock(packet_lock);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (second_packet_lock->packet_->codec_type == packet->codec_type) {
|
if (deinterlace_packet_lock->packet_->codec_type == packet->codec_type) {
|
||||||
deinterlace_packet_lock = second_packet_lock;
|
capture_image->Deinterlace_4Field(deinterlace_packet_lock->packet_->image, (deinterlacing>>8)&0xff);
|
||||||
|
packetqueue.unlock(deinterlace_packet_lock);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
packetqueue.unlock(second_packet_lock);
|
packetqueue.unlock(deinterlace_packet_lock);
|
||||||
packetqueue.increment_it(decoder_it);
|
packetqueue.increment_it(decoder_it);
|
||||||
}
|
}
|
||||||
if (zm_terminate) return false;
|
if (zm_terminate) return false;
|
||||||
capture_image->Deinterlace_4Field(deinterlace_packet_lock->packet_->image, (deinterlacing>>8)&0xff);
|
|
||||||
packetqueue.unlock(deinterlace_packet_lock);
|
|
||||||
} else if (deinterlacing_value == 5) {
|
} else if (deinterlacing_value == 5) {
|
||||||
capture_image->Deinterlace_Blend_CustomRatio((deinterlacing>>8)&0xff);
|
capture_image->Deinterlace_Blend_CustomRatio((deinterlacing>>8)&0xff);
|
||||||
}
|
}
|
||||||
|
@ -2668,7 +2794,7 @@ void Monitor::TimestampImage(Image *ts_image, SystemTimePoint ts_time) const {
|
||||||
Event * Monitor::openEvent(
|
Event * Monitor::openEvent(
|
||||||
const std::shared_ptr<ZMPacket> &snap,
|
const std::shared_ptr<ZMPacket> &snap,
|
||||||
const std::string &cause,
|
const std::string &cause,
|
||||||
const Event::StringSetMap noteSetMap) {
|
const Event::StringSetMap ¬eSetMap) {
|
||||||
|
|
||||||
// FIXME this iterator is not protected from invalidation
|
// FIXME this iterator is not protected from invalidation
|
||||||
packetqueue_iterator *start_it = packetqueue.get_event_start_packet_it(
|
packetqueue_iterator *start_it = packetqueue.get_event_start_packet_it(
|
||||||
|
@ -3005,6 +3131,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) {
|
||||||
|
@ -3042,6 +3178,24 @@ int Monitor::Close() {
|
||||||
if (analysis_thread) {
|
if (analysis_thread) {
|
||||||
analysis_thread->Stop();
|
analysis_thread->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
|
||||||
|
|
||||||
packetqueue.clear();
|
packetqueue.clear();
|
||||||
if (audio_fifo) {
|
if (audio_fifo) {
|
||||||
delete audio_fifo;
|
delete audio_fifo;
|
||||||
|
@ -3144,3 +3298,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"
|
||||||
|
@ -97,7 +104,7 @@ public:
|
||||||
FILE,
|
FILE,
|
||||||
FFMPEG,
|
FFMPEG,
|
||||||
LIBVLC,
|
LIBVLC,
|
||||||
CURL,
|
LIBCURL,
|
||||||
NVSOCKET,
|
NVSOCKET,
|
||||||
VNC,
|
VNC,
|
||||||
} CameraType;
|
} CameraType;
|
||||||
|
@ -280,6 +287,8 @@ protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
|
||||||
// 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;
|
||||||
|
@ -310,6 +319,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;
|
||||||
|
@ -433,6 +443,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;
|
||||||
|
@ -459,6 +470,20 @@ protected:
|
||||||
std::string diag_path_ref;
|
std::string diag_path_ref;
|
||||||
std::string diag_path_delta;
|
std::string diag_path_delta;
|
||||||
|
|
||||||
|
//ONVIF
|
||||||
|
#ifdef WITH_GSOAP
|
||||||
|
struct soap *soap;
|
||||||
|
bool ONVIF_Trigger_State;
|
||||||
|
bool ONVIF_Healthy;
|
||||||
|
bool ONVIF_Closes_Event;
|
||||||
|
_tev__CreatePullPointSubscription request;
|
||||||
|
_tev__CreatePullPointSubscriptionResponse response;
|
||||||
|
_tev__PullMessages tev__PullMessages;
|
||||||
|
_tev__PullMessagesResponse tev__PullMessagesResponse;
|
||||||
|
PullPointSubscriptionBindingProxy proxyEvent;
|
||||||
|
void set_credentials(struct soap *soap);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Used in check signal
|
// Used in check signal
|
||||||
uint8_t red_val;
|
uint8_t red_val;
|
||||||
uint8_t green_val;
|
uint8_t green_val;
|
||||||
|
@ -618,6 +643,7 @@ public:
|
||||||
const std::string &getONVIF_Password() const { return onvif_password; };
|
const std::string &getONVIF_Password() const { return onvif_password; };
|
||||||
const std::string &getONVIF_Options() const { return onvif_options; };
|
const std::string &getONVIF_Options() const { return onvif_options; };
|
||||||
|
|
||||||
|
Image *GetAlarmImage();
|
||||||
int GetImage(int32_t index=-1, int scale=100);
|
int GetImage(int32_t index=-1, int scale=100);
|
||||||
ZMPacket *getSnapshot( int index=-1 ) const;
|
ZMPacket *getSnapshot( int index=-1 ) const;
|
||||||
SystemTimePoint GetTimestamp(int index = -1) const;
|
SystemTimePoint GetTimestamp(int index = -1) const;
|
||||||
|
@ -674,12 +700,13 @@ 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(
|
||||||
const std::shared_ptr<ZMPacket> &snap,
|
const std::shared_ptr<ZMPacket> &snap,
|
||||||
const std::string &cause,
|
const std::string &cause,
|
||||||
const Event::StringSetMap noteSetMap);
|
const Event::StringSetMap ¬eSetMap);
|
||||||
void closeEvent();
|
void closeEvent();
|
||||||
|
|
||||||
void Reload();
|
void Reload();
|
||||||
|
|
|
@ -243,6 +243,14 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
||||||
Info("User initiated exit - CMD_QUIT");
|
Info("User initiated exit - CMD_QUIT");
|
||||||
zm_terminate = true;
|
zm_terminate = true;
|
||||||
break;
|
break;
|
||||||
|
case CMD_ANALYZE_ON :
|
||||||
|
frame_type = FRAME_ANALYSIS;
|
||||||
|
Debug(1, "ANALYSIS on");
|
||||||
|
break;
|
||||||
|
case CMD_ANALYZE_OFF :
|
||||||
|
frame_type = FRAME_NORMAL;
|
||||||
|
Debug(1, "ANALYSIS off");
|
||||||
|
break;
|
||||||
case CMD_QUERY :
|
case CMD_QUERY :
|
||||||
Debug(1, "Got QUERY command, sending STATUS");
|
Debug(1, "Got QUERY command, sending STATUS");
|
||||||
break;
|
break;
|
||||||
|
@ -720,9 +728,22 @@ void MonitorStream::runStream() {
|
||||||
// Perhaps we should use NOW instead.
|
// Perhaps we should use NOW instead.
|
||||||
last_frame_timestamp =
|
last_frame_timestamp =
|
||||||
SystemTimePoint(zm::chrono::duration_cast<Microseconds>(monitor->shared_timestamps[index]));
|
SystemTimePoint(zm::chrono::duration_cast<Microseconds>(monitor->shared_timestamps[index]));
|
||||||
Image *image = monitor->image_buffer[index];
|
|
||||||
|
|
||||||
if (!sendFrame(image, last_frame_timestamp)) {
|
Image *send_image = nullptr;
|
||||||
|
if ((frame_type == FRAME_ANALYSIS) &&
|
||||||
|
(monitor->GetFunction() == Monitor::MOCORD || monitor->GetFunction() == Monitor::MODECT)) {
|
||||||
|
Debug(1, "Sending analysis image");
|
||||||
|
send_image = monitor->GetAlarmImage();
|
||||||
|
if ( !send_image ) {
|
||||||
|
Debug(1, "Falling back");
|
||||||
|
send_image = monitor->image_buffer[index];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Debug(1, "Sending regular image");
|
||||||
|
send_image = monitor->image_buffer[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sendFrame(send_image, last_frame_timestamp)) {
|
||||||
Debug(2, "sendFrame failed, quiting.");
|
Debug(2, "sendFrame failed, quiting.");
|
||||||
zm_terminate = true;
|
zm_terminate = true;
|
||||||
break;
|
break;
|
||||||
|
@ -731,7 +752,7 @@ void MonitorStream::runStream() {
|
||||||
if (frame_count == 0) {
|
if (frame_count == 0) {
|
||||||
// Chrome will not display the first frame until it receives another.
|
// Chrome will not display the first frame until it receives another.
|
||||||
// Firefox is fine. So just send the first frame twice.
|
// Firefox is fine. So just send the first frame twice.
|
||||||
if (!sendFrame(image, last_frame_timestamp)) {
|
if (!sendFrame(send_image, last_frame_timestamp)) {
|
||||||
Debug(2, "sendFrame failed, quiting.");
|
Debug(2, "sendFrame failed, quiting.");
|
||||||
zm_terminate = true;
|
zm_terminate = true;
|
||||||
break;
|
break;
|
||||||
|
@ -753,7 +774,7 @@ void MonitorStream::runStream() {
|
||||||
frame_count++;
|
frame_count++;
|
||||||
frame_count++;
|
frame_count++;
|
||||||
} else {
|
} else {
|
||||||
SystemTimePoint::duration actual_delta_time = now - last_frame_sent;
|
TimePoint::duration actual_delta_time = now - last_frame_sent;
|
||||||
if (actual_delta_time > Seconds(5)) {
|
if (actual_delta_time > Seconds(5)) {
|
||||||
if (paused_image) {
|
if (paused_image) {
|
||||||
// Send keepalive
|
// Send keepalive
|
||||||
|
|
|
@ -42,7 +42,7 @@ void VideoStream::SetupFormat( ) {
|
||||||
ofc = nullptr;
|
ofc = nullptr;
|
||||||
avformat_alloc_output_context2(&ofc, nullptr, format, filename);
|
avformat_alloc_output_context2(&ofc, nullptr, format, filename);
|
||||||
|
|
||||||
if ( !ofc ) {
|
if (!ofc) {
|
||||||
Fatal("avformat_alloc_..._context failed");
|
Fatal("avformat_alloc_..._context failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,11 +50,18 @@ void VideoStream::SetupFormat( ) {
|
||||||
Debug(1, "Using output format: %s (%s)", of->name, of->long_name);
|
Debug(1, "Using output format: %s (%s)", of->name, of->long_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int height, int bitrate, double frame_rate ) {
|
int VideoStream::SetupCodec(
|
||||||
|
int colours,
|
||||||
|
int subpixelorder,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int bitrate,
|
||||||
|
double frame_rate
|
||||||
|
) {
|
||||||
/* ffmpeg format matching */
|
/* ffmpeg format matching */
|
||||||
switch ( colours ) {
|
switch (colours) {
|
||||||
case ZM_COLOUR_RGB24:
|
case ZM_COLOUR_RGB24:
|
||||||
if ( subpixelorder == ZM_SUBPIX_ORDER_BGR ) {
|
if (subpixelorder == ZM_SUBPIX_ORDER_BGR) {
|
||||||
/* BGR subpixel order */
|
/* BGR subpixel order */
|
||||||
pf = AV_PIX_FMT_BGR24;
|
pf = AV_PIX_FMT_BGR24;
|
||||||
} else {
|
} else {
|
||||||
|
@ -63,13 +70,13 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ZM_COLOUR_RGB32:
|
case ZM_COLOUR_RGB32:
|
||||||
if ( subpixelorder == ZM_SUBPIX_ORDER_ARGB ) {
|
if (subpixelorder == ZM_SUBPIX_ORDER_ARGB) {
|
||||||
/* ARGB subpixel order */
|
/* ARGB subpixel order */
|
||||||
pf = AV_PIX_FMT_ARGB;
|
pf = AV_PIX_FMT_ARGB;
|
||||||
} else if ( subpixelorder == ZM_SUBPIX_ORDER_ABGR ) {
|
} else if (subpixelorder == ZM_SUBPIX_ORDER_ABGR) {
|
||||||
/* ABGR subpixel order */
|
/* ABGR subpixel order */
|
||||||
pf = AV_PIX_FMT_ABGR;
|
pf = AV_PIX_FMT_ABGR;
|
||||||
} else if ( subpixelorder == ZM_SUBPIX_ORDER_BGRA ) {
|
} else if (subpixelorder == ZM_SUBPIX_ORDER_BGRA) {
|
||||||
/* BGRA subpixel order */
|
/* BGRA subpixel order */
|
||||||
pf = AV_PIX_FMT_BGRA;
|
pf = AV_PIX_FMT_BGRA;
|
||||||
} else {
|
} else {
|
||||||
|
@ -85,22 +92,22 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( strcmp("rtp", of->name) == 0 ) {
|
if (strcmp("rtp", of->name) == 0) {
|
||||||
// RTP must have a packet_size.
|
// RTP must have a packet_size.
|
||||||
// Not sure what this value should be really...
|
// Not sure what this value should be really...
|
||||||
ofc->packet_size = width*height;
|
ofc->packet_size = width*height;
|
||||||
Debug(1,"Setting packet_size to %d", ofc->packet_size);
|
Debug(1,"Setting packet_size to %d", ofc->packet_size);
|
||||||
|
|
||||||
if ( of->video_codec == AV_CODEC_ID_NONE ) {
|
if (of->video_codec == AV_CODEC_ID_NONE) {
|
||||||
// RTP does not have a default codec in ffmpeg <= 0.8.
|
// RTP does not have a default codec in ffmpeg <= 0.8.
|
||||||
of->video_codec = AV_CODEC_ID_MPEG4;
|
of->video_codec = AV_CODEC_ID_MPEG4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_AVCODECID codec_id = of->video_codec;
|
_AVCODECID codec_id = of->video_codec;
|
||||||
if ( codec_name ) {
|
if (codec_name) {
|
||||||
AVCodec *a = avcodec_find_encoder_by_name(codec_name);
|
AVCodec *a = avcodec_find_encoder_by_name(codec_name);
|
||||||
if ( a ) {
|
if (a) {
|
||||||
codec_id = a->id;
|
codec_id = a->id;
|
||||||
Debug(1, "Using codec \"%s\"", codec_name);
|
Debug(1, "Using codec \"%s\"", codec_name);
|
||||||
} else {
|
} else {
|
||||||
|
@ -111,31 +118,29 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei
|
||||||
/* add the video streams using the default format codecs
|
/* add the video streams using the default format codecs
|
||||||
and initialize the codecs */
|
and initialize the codecs */
|
||||||
ost = nullptr;
|
ost = nullptr;
|
||||||
if ( codec_id != AV_CODEC_ID_NONE ) {
|
if (codec_id != AV_CODEC_ID_NONE) {
|
||||||
codec = avcodec_find_encoder(codec_id);
|
codec = avcodec_find_encoder(codec_id);
|
||||||
if ( !codec ) {
|
if (!codec) {
|
||||||
Fatal("Could not find encoder for '%s'", avcodec_get_name(codec_id));
|
Error("Could not find encoder for '%s'", avcodec_get_name(codec_id));
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug(1, "Found encoder for '%s'", avcodec_get_name(codec_id));
|
Debug(1, "Found encoder for '%s'", avcodec_get_name(codec_id));
|
||||||
|
ost = avformat_new_stream(ofc, codec);
|
||||||
ost = avformat_new_stream( ofc, codec );
|
if (!ost) {
|
||||||
|
Error("Could not alloc stream");
|
||||||
if ( !ost ) {
|
return -1;
|
||||||
Fatal( "Could not alloc stream" );
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
Debug( 1, "Allocated stream (%d) !=? (%d)", ost->id , ofc->nb_streams - 1 );
|
Debug(1, "Allocated stream (%d) !=? (%d)", ost->id , ofc->nb_streams - 1);
|
||||||
ost->id = ofc->nb_streams - 1;
|
ost->id = ofc->nb_streams - 1;
|
||||||
|
|
||||||
codec_context = avcodec_alloc_context3(nullptr);
|
codec_context = avcodec_alloc_context3(nullptr);
|
||||||
//avcodec_parameters_to_context(codec_context, ost->codecpar);
|
//avcodec_parameters_to_context(codec_context, ost->codecpar);
|
||||||
|
|
||||||
codec_context->codec_id = codec->id;
|
codec_context->codec_id = codec->id;
|
||||||
codec_context->codec_type = codec->type;
|
codec_context->codec_type = codec->type;
|
||||||
|
|
||||||
codec_context->pix_fmt = strcmp("mjpeg", ofc->oformat->name) == 0 ? AV_PIX_FMT_YUVJ422P : AV_PIX_FMT_YUV420P;
|
codec_context->pix_fmt = strcmp("mjpeg", ofc->oformat->name) == 0 ? AV_PIX_FMT_YUVJ422P : AV_PIX_FMT_YUV420P;
|
||||||
if ( bitrate <= 100 ) {
|
if (bitrate <= 100) {
|
||||||
// Quality based bitrate control (VBR). Scale is 1..31 where 1 is best.
|
// Quality based bitrate control (VBR). Scale is 1..31 where 1 is best.
|
||||||
// This gets rid of artifacts in the beginning of the movie; and well, even quality.
|
// This gets rid of artifacts in the beginning of the movie; and well, even quality.
|
||||||
codec_context->flags |= AV_CODEC_FLAG_QSCALE;
|
codec_context->flags |= AV_CODEC_FLAG_QSCALE;
|
||||||
|
@ -155,22 +160,22 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei
|
||||||
codec_context->time_base.num = 1;
|
codec_context->time_base.num = 1;
|
||||||
ost->time_base.den = frame_rate;
|
ost->time_base.den = frame_rate;
|
||||||
ost->time_base.num = 1;
|
ost->time_base.num = 1;
|
||||||
|
|
||||||
|
|
||||||
Debug( 1, "Will encode in %d fps. %dx%d", codec_context->time_base.den, width, height );
|
Debug( 1, "Will encode in %d fps. %dx%d", codec_context->time_base.den, width, height );
|
||||||
|
|
||||||
/* emit one intra frame every second */
|
/* emit one intra frame every second */
|
||||||
codec_context->gop_size = frame_rate;
|
codec_context->gop_size = frame_rate;
|
||||||
|
|
||||||
// some formats want stream headers to be separate
|
// some formats want stream headers to be separate
|
||||||
if ( of->flags & AVFMT_GLOBALHEADER )
|
if (of->flags & AVFMT_GLOBALHEADER)
|
||||||
codec_context->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
|
codec_context->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
|
||||||
|
|
||||||
avcodec_parameters_from_context(ost->codecpar, codec_context);
|
avcodec_parameters_from_context(ost->codecpar, codec_context);
|
||||||
zm_dump_codecpar(ost->codecpar);
|
zm_dump_codecpar(ost->codecpar);
|
||||||
} else {
|
} else {
|
||||||
Fatal( "of->video_codec == AV_CODEC_ID_NONE" );
|
Error("of->video_codec == AV_CODEC_ID_NONE");
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoStream::SetParameters( ) {
|
void VideoStream::SetParameters( ) {
|
||||||
|
@ -461,7 +466,6 @@ double VideoStream::EncodeFrame( const uint8_t *buffer, int buffer_size, bool _a
|
||||||
}
|
}
|
||||||
|
|
||||||
double VideoStream::ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp, unsigned int timestamp ) {
|
double VideoStream::ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp, unsigned int timestamp ) {
|
||||||
|
|
||||||
if ( codec_context->pix_fmt != pf ) {
|
if ( codec_context->pix_fmt != pf ) {
|
||||||
static struct SwsContext *img_convert_ctx = nullptr;
|
static struct SwsContext *img_convert_ctx = nullptr;
|
||||||
memcpy( tmp_opicture->data[0], buffer, buffer_size );
|
memcpy( tmp_opicture->data[0], buffer, buffer_size );
|
||||||
|
@ -492,20 +496,19 @@ double VideoStream::ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size,
|
||||||
|
|
||||||
avcodec_send_frame(codec_context, opicture_ptr);
|
avcodec_send_frame(codec_context, opicture_ptr);
|
||||||
int ret = avcodec_receive_packet(codec_context, pkt);
|
int ret = avcodec_receive_packet(codec_context, pkt);
|
||||||
if ( ret < 0 ) {
|
if (ret < 0) {
|
||||||
if ( AVERROR_EOF != ret ) {
|
if (AVERROR_EOF != ret) {
|
||||||
Error("ERror encoding video (%d) (%s)", ret,
|
Error("ERror encoding video (%d) (%s)", ret, av_err2str(ret));
|
||||||
av_err2str(ret));
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
got_packet = 1;
|
got_packet = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( got_packet ) {
|
if (got_packet) {
|
||||||
// if ( c->coded_frame->key_frame )
|
// if ( c->coded_frame->key_frame )
|
||||||
// {
|
// {
|
||||||
// pkt->flags |= AV_PKT_FLAG_KEY;
|
// pkt->flags |= AV_PKT_FLAG_KEY;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if ( pkt->pts != (int64_t)AV_NOPTS_VALUE ) {
|
if ( pkt->pts != (int64_t)AV_NOPTS_VALUE ) {
|
||||||
pkt->pts = av_rescale_q( pkt->pts, codec_context->time_base, ost->time_base );
|
pkt->pts = av_rescale_q( pkt->pts, codec_context->time_base, ost->time_base );
|
||||||
|
@ -518,16 +521,15 @@ double VideoStream::ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ( opicture_ptr->pts);
|
return opicture_ptr->pts;
|
||||||
}
|
}
|
||||||
|
|
||||||
int VideoStream::SendPacket(AVPacket *packet) {
|
int VideoStream::SendPacket(AVPacket *packet) {
|
||||||
|
int ret = av_write_frame(ofc, packet);
|
||||||
int ret = av_write_frame( ofc, packet );
|
if (ret < 0) {
|
||||||
if ( ret != 0 ) {
|
Error("Error %d while writing video frame: %s", ret, av_err2str(errno));
|
||||||
Fatal( "Error %d while writing video frame: %s", ret, av_err2str( errno ) );
|
|
||||||
}
|
}
|
||||||
av_packet_unref( packet );
|
av_packet_unref(packet);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ protected:
|
||||||
static void Initialise();
|
static void Initialise();
|
||||||
|
|
||||||
void SetupFormat( );
|
void SetupFormat( );
|
||||||
void SetupCodec( int colours, int subpixelorder, int width, int height, int bitrate, double frame_rate );
|
int SetupCodec( int colours, int subpixelorder, int width, int height, int bitrate, double frame_rate );
|
||||||
void SetParameters();
|
void SetParameters();
|
||||||
void ActuallyOpenStream();
|
void ActuallyOpenStream();
|
||||||
double ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 );
|
double ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 );
|
||||||
|
|
|
@ -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
|
|
@ -21,7 +21,7 @@ class ZoneMinderDeviceSource;
|
||||||
|
|
||||||
class BaseServerMediaSubsession {
|
class BaseServerMediaSubsession {
|
||||||
public:
|
public:
|
||||||
BaseServerMediaSubsession(StreamReplicator* replicator):
|
explicit BaseServerMediaSubsession(StreamReplicator* replicator):
|
||||||
m_replicator(replicator) {};
|
m_replicator(replicator) {};
|
||||||
|
|
||||||
FramedSource* createSource(
|
FramedSource* createSource(
|
||||||
|
|
|
@ -157,7 +157,6 @@ Image *StreamBase::prepareImage(Image *image) {
|
||||||
int disp_image_width = (image->Width() * scale) / ZM_SCALE_BASE, disp_image_height = (image->Height() * scale) / ZM_SCALE_BASE;
|
int disp_image_width = (image->Width() * scale) / ZM_SCALE_BASE, disp_image_height = (image->Height() * scale) / ZM_SCALE_BASE;
|
||||||
int last_disp_image_width = (image->Width() * last_scale) / ZM_SCALE_BASE, last_disp_image_height = (image->Height() * last_scale) / ZM_SCALE_BASE;
|
int last_disp_image_width = (image->Width() * last_scale) / ZM_SCALE_BASE, last_disp_image_height = (image->Height() * last_scale) / ZM_SCALE_BASE;
|
||||||
int send_image_width = (disp_image_width * act_mag ) / mag, send_image_height = (disp_image_height * act_mag ) / mag;
|
int send_image_width = (disp_image_width * act_mag ) / mag, send_image_height = (disp_image_height * act_mag ) / mag;
|
||||||
int last_send_image_width = (last_disp_image_width * last_act_mag ) / last_mag, last_send_image_height = (last_disp_image_height * last_act_mag ) / last_mag;
|
|
||||||
|
|
||||||
Debug(3,
|
Debug(3,
|
||||||
"Scaling by %d, zooming by %d = magnifying by %d(%d)\n"
|
"Scaling by %d, zooming by %d = magnifying by %d(%d)\n"
|
||||||
|
@ -169,8 +168,7 @@ Image *StreamBase::prepareImage(Image *image) {
|
||||||
"Last actual image width = %d, height = %d\n"
|
"Last actual image width = %d, height = %d\n"
|
||||||
"Display image width = %d, height = %d\n"
|
"Display image width = %d, height = %d\n"
|
||||||
"Last display image width = %d, height = %d\n"
|
"Last display image width = %d, height = %d\n"
|
||||||
"Send image width = %d, height = %d\n"
|
"Send image width = %d, height = %d\n",
|
||||||
"Last send image width = %d, height = %d\n",
|
|
||||||
scale, zoom, mag, act_mag,
|
scale, zoom, mag, act_mag,
|
||||||
last_scale, last_zoom, last_mag, last_act_mag,
|
last_scale, last_zoom, last_mag, last_act_mag,
|
||||||
base_image_width, base_image_height,
|
base_image_width, base_image_height,
|
||||||
|
@ -180,8 +178,7 @@ Image *StreamBase::prepareImage(Image *image) {
|
||||||
last_act_image_width, last_act_image_height,
|
last_act_image_width, last_act_image_height,
|
||||||
disp_image_width, disp_image_height,
|
disp_image_width, disp_image_height,
|
||||||
last_disp_image_width, last_disp_image_height,
|
last_disp_image_width, last_disp_image_height,
|
||||||
send_image_width, send_image_height,
|
send_image_width, send_image_height
|
||||||
last_send_image_width, last_send_image_height
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( ( mag != ZM_SCALE_BASE ) && (act_mag != ZM_SCALE_BASE) ) {
|
if ( ( mag != ZM_SCALE_BASE ) && (act_mag != ZM_SCALE_BASE) ) {
|
||||||
|
|
|
@ -40,6 +40,7 @@ public:
|
||||||
STREAM_SINGLE,
|
STREAM_SINGLE,
|
||||||
STREAM_MPEG
|
STREAM_MPEG
|
||||||
} StreamType;
|
} StreamType;
|
||||||
|
typedef enum { FRAME_NORMAL, FRAME_ANALYSIS } FrameType;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static constexpr Seconds MAX_STREAM_DELAY = Seconds(5);
|
static constexpr Seconds MAX_STREAM_DELAY = Seconds(5);
|
||||||
|
@ -89,6 +90,8 @@ protected:
|
||||||
CMD_GET_IMAGE,
|
CMD_GET_IMAGE,
|
||||||
CMD_QUIT,
|
CMD_QUIT,
|
||||||
CMD_MAXFPS,
|
CMD_MAXFPS,
|
||||||
|
CMD_ANALYZE_ON,
|
||||||
|
CMD_ANALYZE_OFF,
|
||||||
CMD_QUERY=99
|
CMD_QUERY=99
|
||||||
} MsgCommand;
|
} MsgCommand;
|
||||||
|
|
||||||
|
@ -97,6 +100,7 @@ protected:
|
||||||
std::shared_ptr<Monitor> monitor;
|
std::shared_ptr<Monitor> monitor;
|
||||||
|
|
||||||
StreamType type;
|
StreamType type;
|
||||||
|
FrameType frame_type;
|
||||||
const char *format;
|
const char *format;
|
||||||
int replay_rate;
|
int replay_rate;
|
||||||
int scale;
|
int scale;
|
||||||
|
@ -153,6 +157,7 @@ public:
|
||||||
monitor_id(0),
|
monitor_id(0),
|
||||||
monitor(nullptr),
|
monitor(nullptr),
|
||||||
type(DEFAULT_TYPE),
|
type(DEFAULT_TYPE),
|
||||||
|
frame_type(FRAME_NORMAL),
|
||||||
format(""),
|
format(""),
|
||||||
replay_rate(DEFAULT_RATE),
|
replay_rate(DEFAULT_RATE),
|
||||||
scale(DEFAULT_SCALE),
|
scale(DEFAULT_SCALE),
|
||||||
|
@ -198,7 +203,9 @@ public:
|
||||||
type = STREAM_RAW;
|
type = STREAM_RAW;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
void setStreamFrameType(FrameType p_type) {
|
||||||
|
frame_type = p_type;
|
||||||
}
|
}
|
||||||
void setStreamFormat(const char *p_format) {
|
void setStreamFormat(const char *p_format) {
|
||||||
format = p_format;
|
format = p_format;
|
||||||
|
|
|
@ -22,7 +22,14 @@
|
||||||
#include "zm_image.h"
|
#include "zm_image.h"
|
||||||
#include "zm_logger.h"
|
#include "zm_logger.h"
|
||||||
|
|
||||||
SWScale::SWScale() : gotdefaults(false), swscale_ctx(nullptr), input_avframe(nullptr), output_avframe(nullptr) {
|
SWScale::SWScale() :
|
||||||
|
gotdefaults(false),
|
||||||
|
swscale_ctx(nullptr),
|
||||||
|
input_avframe(nullptr),
|
||||||
|
output_avframe(nullptr),
|
||||||
|
default_width(0),
|
||||||
|
default_height(0)
|
||||||
|
{
|
||||||
Debug(4, "SWScale object created");
|
Debug(4, "SWScale object created");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,7 @@ Duration duration_cast(timeval const &tv) {
|
||||||
//
|
//
|
||||||
class TimeSegmentAdder {
|
class TimeSegmentAdder {
|
||||||
public:
|
public:
|
||||||
TimeSegmentAdder(Microseconds &in_target) :
|
explicit TimeSegmentAdder(Microseconds &in_target) :
|
||||||
target_(in_target),
|
target_(in_target),
|
||||||
start_time_(std::chrono::steady_clock::now()),
|
start_time_(std::chrono::steady_clock::now()),
|
||||||
finished_(false) {
|
finished_(false) {
|
||||||
|
|
|
@ -120,9 +120,6 @@ std::string Join(const StringVector &values, const std::string &delim) {
|
||||||
std::string stringtf(const char* format, ...) {
|
std::string stringtf(const char* format, ...) {
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, format);
|
va_start(args, format);
|
||||||
va_list args2;
|
|
||||||
va_copy(args2, args);
|
|
||||||
|
|
||||||
int size = vsnprintf(nullptr, 0, format, args) + 1; // Extra space for '\0'
|
int size = vsnprintf(nullptr, 0, format, args) + 1; // Extra space for '\0'
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
|
@ -131,6 +128,8 @@ std::string stringtf(const char* format, ...) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<char[]> buf(new char[size]);
|
std::unique_ptr<char[]> buf(new char[size]);
|
||||||
|
va_list args2;
|
||||||
|
va_copy(args2, args);
|
||||||
vsnprintf(buf.get(), size, format, args2);
|
vsnprintf(buf.get(), size, format, args2);
|
||||||
va_end(args2);
|
va_end(args2);
|
||||||
|
|
||||||
|
@ -259,6 +258,8 @@ void HwCapsDetect() {
|
||||||
unsigned long auxval = 0;
|
unsigned long auxval = 0;
|
||||||
elf_aux_info(AT_HWCAP, &auxval, sizeof(auxval));
|
elf_aux_info(AT_HWCAP, &auxval, sizeof(auxval));
|
||||||
if (auxval & HWCAP_NEON) {
|
if (auxval & HWCAP_NEON) {
|
||||||
|
#else
|
||||||
|
{
|
||||||
#error Unsupported OS.
|
#error Unsupported OS.
|
||||||
#endif
|
#endif
|
||||||
Debug(1,"Detected ARM (AArch32) processor with Neon");
|
Debug(1,"Detected ARM (AArch32) processor with Neon");
|
||||||
|
|
|
@ -401,6 +401,10 @@ bool VideoStore::open() {
|
||||||
} else {
|
} else {
|
||||||
audio_in_ctx = avcodec_alloc_context3(audio_out_codec);
|
audio_in_ctx = avcodec_alloc_context3(audio_out_codec);
|
||||||
ret = avcodec_parameters_to_context(audio_in_ctx, audio_in_stream->codecpar);
|
ret = avcodec_parameters_to_context(audio_in_ctx, audio_in_stream->codecpar);
|
||||||
|
if (ret < 0)
|
||||||
|
Error("Failure from avcodec_parameters_to_context %s",
|
||||||
|
av_make_error_string(ret).c_str());
|
||||||
|
|
||||||
audio_in_ctx->time_base = audio_in_stream->time_base;
|
audio_in_ctx->time_base = audio_in_stream->time_base;
|
||||||
|
|
||||||
audio_out_ctx = avcodec_alloc_context3(audio_out_codec);
|
audio_out_ctx = avcodec_alloc_context3(audio_out_codec);
|
||||||
|
@ -729,7 +733,6 @@ bool VideoStore::setup_resampler() {
|
||||||
audio_out_ctx->sample_fmt = audio_in_ctx->sample_fmt;
|
audio_out_ctx->sample_fmt = audio_in_ctx->sample_fmt;
|
||||||
audio_out_ctx->channels = audio_in_ctx->channels;
|
audio_out_ctx->channels = audio_in_ctx->channels;
|
||||||
audio_out_ctx->channel_layout = audio_in_ctx->channel_layout;
|
audio_out_ctx->channel_layout = audio_in_ctx->channel_layout;
|
||||||
audio_out_ctx->sample_fmt = audio_in_ctx->sample_fmt;
|
|
||||||
if (!audio_out_ctx->channel_layout) {
|
if (!audio_out_ctx->channel_layout) {
|
||||||
Debug(3, "Correcting channel layout from (%" PRIi64 ") to (%" PRIi64 ")",
|
Debug(3, "Correcting channel layout from (%" PRIi64 ") to (%" PRIi64 ")",
|
||||||
audio_out_ctx->channel_layout,
|
audio_out_ctx->channel_layout,
|
||||||
|
@ -852,7 +855,7 @@ bool VideoStore::setup_resampler() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ((ret = swr_init(resample_ctx)) < 0) {
|
if ((ret = swr_init(resample_ctx)) < 0) {
|
||||||
Error("Could not open resampler");
|
Error("Could not open resampler %d", ret);
|
||||||
av_frame_free(&in_frame);
|
av_frame_free(&in_frame);
|
||||||
av_frame_free(&out_frame);
|
av_frame_free(&out_frame);
|
||||||
swr_free(&resample_ctx);
|
swr_free(&resample_ctx);
|
||||||
|
|
|
@ -219,7 +219,7 @@ bool Zone::CheckAlarms(const Image *delta_image) {
|
||||||
int alarm_mid_x = -1;
|
int alarm_mid_x = -1;
|
||||||
int alarm_mid_y = -1;
|
int alarm_mid_y = -1;
|
||||||
|
|
||||||
unsigned int lo_x = polygon.Extent().Lo().x_;
|
//unsigned int lo_x = polygon.Extent().Lo().x_;
|
||||||
unsigned int lo_y = polygon.Extent().Lo().y_;
|
unsigned int lo_y = polygon.Extent().Lo().y_;
|
||||||
unsigned int hi_x = polygon.Extent().Hi().x_;
|
unsigned int hi_x = polygon.Extent().Hi().x_;
|
||||||
unsigned int hi_y = polygon.Extent().Hi().y_;
|
unsigned int hi_y = polygon.Extent().Hi().y_;
|
||||||
|
@ -699,6 +699,7 @@ bool Zone::CheckAlarms(const Image *delta_image) {
|
||||||
|
|
||||||
if ((type < PRECLUSIVE) && (check_method >= BLOBS) && (monitor->GetOptSaveJPEGs() > 1)) {
|
if ((type < PRECLUSIVE) && (check_method >= BLOBS) && (monitor->GetOptSaveJPEGs() > 1)) {
|
||||||
|
|
||||||
|
unsigned int lo_x = polygon.Extent().Lo().x_;
|
||||||
// First mask out anything we don't want
|
// First mask out anything we don't want
|
||||||
for (unsigned int y = lo_y; y <= hi_y; y++) {
|
for (unsigned int y = lo_y; y <= hi_y; y++) {
|
||||||
pdiff = diff_buff + ((diff_width * y) + lo_x);
|
pdiff = diff_buff + ((diff_width * y) + lo_x);
|
||||||
|
|
12
src/zms.cpp
12
src/zms.cpp
|
@ -63,6 +63,7 @@ int main(int argc, const char *argv[], char **envp) {
|
||||||
double maxfps = 10.0;
|
double maxfps = 10.0;
|
||||||
unsigned int bitrate = 100000;
|
unsigned int bitrate = 100000;
|
||||||
unsigned int ttl = 0;
|
unsigned int ttl = 0;
|
||||||
|
bool analysis_frames = false;
|
||||||
EventStream::StreamMode replay = EventStream::MODE_NONE;
|
EventStream::StreamMode replay = EventStream::MODE_NONE;
|
||||||
std::string username;
|
std::string username;
|
||||||
std::string password;
|
std::string password;
|
||||||
|
@ -115,7 +116,14 @@ int main(int argc, const char *argv[], char **envp) {
|
||||||
char const *value = strtok(nullptr, "=");
|
char const *value = strtok(nullptr, "=");
|
||||||
if ( !value )
|
if ( !value )
|
||||||
value = "";
|
value = "";
|
||||||
if ( !strcmp(name, "source") ) {
|
if ( !strcmp(name, "analysis") ) {
|
||||||
|
if ( !strcmp(value, "true") ) {
|
||||||
|
analysis_frames = true;
|
||||||
|
} else {
|
||||||
|
analysis_frames = (atoi(value) == 1);
|
||||||
|
}
|
||||||
|
Debug(1, "Viewing analysis frames");
|
||||||
|
} else if ( !strcmp(name, "source") ) {
|
||||||
if ( !strcmp(value, "event") ) {
|
if ( !strcmp(value, "event") ) {
|
||||||
source = ZMS_EVENT;
|
source = ZMS_EVENT;
|
||||||
} else if ( !strcmp(value, "fifo") ) {
|
} else if ( !strcmp(value, "fifo") ) {
|
||||||
|
@ -271,6 +279,7 @@ int main(int argc, const char *argv[], char **envp) {
|
||||||
zmDbClose();
|
zmDbClose();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
stream.setStreamFrameType(analysis_frames ? StreamBase::FRAME_ANALYSIS: StreamBase::FRAME_NORMAL);
|
||||||
|
|
||||||
if ( mode == ZMS_JPEG ) {
|
if ( mode == ZMS_JPEG ) {
|
||||||
stream.setStreamType(MonitorStream::STREAM_JPEG);
|
stream.setStreamType(MonitorStream::STREAM_JPEG);
|
||||||
|
@ -307,6 +316,7 @@ int main(int argc, const char *argv[], char **envp) {
|
||||||
Debug(3, "Setting stream start to frame (%d)", frame_id);
|
Debug(3, "Setting stream start to frame (%d)", frame_id);
|
||||||
stream.setStreamStart(event_id, frame_id);
|
stream.setStreamStart(event_id, frame_id);
|
||||||
}
|
}
|
||||||
|
stream.setStreamFrameType(analysis_frames ? StreamBase::FRAME_ANALYSIS: StreamBase::FRAME_NORMAL);
|
||||||
if ( mode == ZMS_JPEG ) {
|
if ( mode == ZMS_JPEG ) {
|
||||||
stream.setStreamType(EventStream::STREAM_JPEG);
|
stream.setStreamType(EventStream::STREAM_JPEG);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -9,6 +9,7 @@ $raw = isset($_REQUEST['raw']);
|
||||||
$data = array();
|
$data = array();
|
||||||
|
|
||||||
if ($raw) {
|
if ($raw) {
|
||||||
|
$data['raw'] = array();
|
||||||
$sql = 'SELECT S.*,E.*,Z.Name AS ZoneName,Z.Units,Z.Area,M.Name AS MonitorName
|
$sql = 'SELECT S.*,E.*,Z.Name AS ZoneName,Z.Units,Z.Area,M.Name AS MonitorName
|
||||||
FROM Stats AS S LEFT JOIN Events AS E ON S.EventId = E.Id LEFT JOIN Zones AS Z ON S.ZoneId = Z.Id LEFT JOIN Monitors AS M ON E.MonitorId = M.Id
|
FROM Stats AS S LEFT JOIN Events AS E ON S.EventId = E.Id LEFT JOIN Zones AS Z ON S.ZoneId = Z.Id LEFT JOIN Monitors AS M ON E.MonitorId = M.Id
|
||||||
WHERE S.EventId = ? AND S.FrameId = ? ORDER BY S.ZoneId';
|
WHERE S.EventId = ? AND S.FrameId = ? ORDER BY S.ZoneId';
|
||||||
|
|
|
@ -124,6 +124,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',
|
||||||
|
|
|
@ -11,19 +11,24 @@ class ZM_Object {
|
||||||
$class = get_class($this);
|
$class = get_class($this);
|
||||||
|
|
||||||
$row = NULL;
|
$row = NULL;
|
||||||
if ( $IdOrRow ) {
|
if ($IdOrRow) {
|
||||||
|
|
||||||
if ( is_integer($IdOrRow) or ctype_digit($IdOrRow) ) {
|
if (is_integer($IdOrRow) or ctype_digit($IdOrRow)) {
|
||||||
$table = $class::$table;
|
$table = $class::$table;
|
||||||
$row = dbFetchOne("SELECT * FROM `$table` WHERE `Id`=?", NULL, array($IdOrRow));
|
$row = dbFetchOne("SELECT * FROM `$table` WHERE `Id`=?", NULL, array($IdOrRow));
|
||||||
if ( !$row ) {
|
if (!$row) {
|
||||||
Error("Unable to load $class record for Id=$IdOrRow");
|
Error("Unable to load $class record for Id=$IdOrRow");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} else if ( is_array($IdOrRow) ) {
|
} else if (is_array($IdOrRow)) {
|
||||||
$row = $IdOrRow;
|
$row = $IdOrRow;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $row ) {
|
if ( $row ) {
|
||||||
|
if (!isset($row['Id'])) {
|
||||||
|
Error("No Id in " . print_r($row, true));
|
||||||
|
return;
|
||||||
|
}
|
||||||
foreach ($row as $k => $v) {
|
foreach ($row as $k => $v) {
|
||||||
$this->{$k} = $v;
|
$this->{$k} = $v;
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,6 +120,8 @@ define('CMD_SEEK', 14 );
|
||||||
define('CMD_VARPLAY', 15);
|
define('CMD_VARPLAY', 15);
|
||||||
define('CMD_QUIT', 17);
|
define('CMD_QUIT', 17);
|
||||||
define('CMD_MAXFPS', 18);
|
define('CMD_MAXFPS', 18);
|
||||||
|
define('CMD_ANALYZE_ON', 19);
|
||||||
|
define('CMD_ANALYZE_OFF', 20);
|
||||||
define('CMD_QUERY', 99);
|
define('CMD_QUERY', 99);
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
function MonitorStream(monitorData) {
|
function MonitorStream(monitorData) {
|
||||||
this.id = monitorData.id;
|
this.id = monitorData.id;
|
||||||
this.connKey = monitorData.connKey;
|
this.connKey = monitorData.connKey;
|
||||||
|
this.auth_relay = auth_relay;
|
||||||
|
this.auth_hash = auth_hash;
|
||||||
this.url = monitorData.url;
|
this.url = monitorData.url;
|
||||||
this.url_to_zms = monitorData.url_to_zms;
|
this.url_to_zms = monitorData.url_to_zms;
|
||||||
this.width = monitorData.width;
|
this.width = monitorData.width;
|
||||||
|
@ -206,36 +208,38 @@ function MonitorStream(monitorData) {
|
||||||
var newAlarm = ( isAlarmed && !wasAlarmed );
|
var newAlarm = ( isAlarmed && !wasAlarmed );
|
||||||
var oldAlarm = ( !isAlarmed && wasAlarmed );
|
var oldAlarm = ( !isAlarmed && wasAlarmed );
|
||||||
|
|
||||||
if ( newAlarm ) {
|
if (newAlarm) {
|
||||||
if ( false && SOUND_ON_ALARM ) {
|
if (false && SOUND_ON_ALARM) {
|
||||||
// Enable the alarm sound
|
// Enable the alarm sound
|
||||||
$j('#alarmSound').removeClass('hidden');
|
$j('#alarmSound').removeClass('hidden');
|
||||||
}
|
}
|
||||||
if ( (typeof POPUP_ON_ALARM !== 'undefined') && POPUP_ON_ALARM ) {
|
if ((typeof POPUP_ON_ALARM !== 'undefined') && POPUP_ON_ALARM) {
|
||||||
windowToFront();
|
windowToFront();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( false && SOUND_ON_ALARM ) {
|
if (false && SOUND_ON_ALARM) {
|
||||||
if ( oldAlarm ) {
|
if ( oldAlarm ) {
|
||||||
// Disable alarm sound
|
// Disable alarm sound
|
||||||
$j('#alarmSound').addClass('hidden');
|
$j('#alarmSound').addClass('hidden');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( this.status.auth ) {
|
if (this.status.auth) {
|
||||||
if ( this.status.auth != auth_hash ) {
|
if (this.status.auth != auth_hash) {
|
||||||
// Try to reload the image stream.
|
// Try to reload the image stream.
|
||||||
if ( stream ) {
|
if (stream) {
|
||||||
stream.src = stream.src.replace(/auth=\w+/i, 'auth='+this.status.auth);
|
const oldsrc = stream.src;
|
||||||
|
stream.src = '';
|
||||||
|
stream.src = oldsrc.replace(/auth=\w+/i, 'auth='+this.status.auth);
|
||||||
}
|
}
|
||||||
console.log("Changed auth from " + auth_hash + " to " + this.status.auth);
|
console.log("Changed auth from " + this.auth_hash + " to " + this.status.auth);
|
||||||
auth_hash = this.status.auth;
|
this.auth_hash = this.status.auth;
|
||||||
}
|
}
|
||||||
} // end if have a new auth hash
|
} // end if have a new auth hash
|
||||||
} // end if has state
|
} // end if has state
|
||||||
} else {
|
} else {
|
||||||
console.error(respObj.message);
|
console.error(respObj.message);
|
||||||
// Try to reload the image stream.
|
// Try to reload the image stream.
|
||||||
if ( stream ) {
|
if (stream) {
|
||||||
if ( stream.src ) {
|
if ( stream.src ) {
|
||||||
console.log('Reloading stream: ' + stream.src);
|
console.log('Reloading stream: ' + stream.src);
|
||||||
src = stream.src.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) ));
|
src = stream.src.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) ));
|
||||||
|
@ -276,4 +280,10 @@ function MonitorStream(monitorData) {
|
||||||
.fail(this.onFailure.bind(this));
|
.fail(this.onFailure.bind(this));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
this.analyse_frames = true;
|
||||||
|
this.show_analyse_frames = function(toggle) {
|
||||||
|
this.analyse_frames = toggle;
|
||||||
|
this.streamCmdParms.command = this.analyse_frames?CMD_ANALYZE_ON:CMD_ANALYZE_OFF;
|
||||||
|
this.streamCmdReq(this.streamCmdParms);
|
||||||
|
};
|
||||||
} // end function MonitorStream
|
} // end function MonitorStream
|
||||||
|
|
|
@ -116,7 +116,10 @@ xhtmlHeaders(__FILE__, translate('Frame').' - '.$Event->Id().' - '.$Frame->Frame
|
||||||
<p id="image">
|
<p id="image">
|
||||||
<?php
|
<?php
|
||||||
if ( $imageData['hasAnalImage'] ) {
|
if ( $imageData['hasAnalImage'] ) {
|
||||||
echo sprintf('<a href="?view=frame&eid=%d&fid=%d&scale=%d&show=%s">', $Event->Id(), $Frame->FrameId(), $scale, ( $show=='anal'?'capt':'anal' ) );
|
echo sprintf('<a href="?view=frame&eid=%d&fid=%d&scale=%d&show=%s" title="Click to display frame %s analysis">',
|
||||||
|
$Event->Id(), $Frame->FrameId(), $scale, ( $show=='anal'?'capt':'anal' ),
|
||||||
|
( $show=='anal'?'without':'with' ),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
<img id="frameImg"
|
<img id="frameImg"
|
||||||
|
|
|
@ -88,9 +88,15 @@ function initialAlarmCues(eventId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function setAlarmCues(data) {
|
function setAlarmCues(data) {
|
||||||
|
if (!data) {
|
||||||
|
Error('No data in setAlarmCues for event ' + eventData.Id);
|
||||||
|
} else if (!data.frames) {
|
||||||
|
Error('No data.frames in setAlarmCues for event ' + eventData.Id);
|
||||||
|
} else {
|
||||||
cueFrames = data.frames;
|
cueFrames = data.frames;
|
||||||
alarmSpans = renderAlarmCues(vid ? $j("#videoobj") : $j("#evtStream"));//use videojs width or zms width
|
alarmSpans = renderAlarmCues(vid ? $j("#videoobj") : $j("#evtStream"));//use videojs width or zms width
|
||||||
$j(".alarmCue").html(alarmSpans);
|
$j(".alarmCue").html(alarmSpans);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderAlarmCues(containerEl) {
|
function renderAlarmCues(containerEl) {
|
||||||
|
|
|
@ -40,8 +40,8 @@ function changeScale() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFrameStatsCookie() {
|
function getFrameStatsCookie() {
|
||||||
var cookie = 'zmFrameStats';
|
const cookie = 'zmFrameStats';
|
||||||
var stats = getCookie(cookie);
|
let stats = getCookie(cookie);
|
||||||
|
|
||||||
if (!stats) {
|
if (!stats) {
|
||||||
stats = 'on';
|
stats = 'on';
|
||||||
|
@ -53,7 +53,7 @@ function getFrameStatsCookie() {
|
||||||
function getStat(params) {
|
function getStat(params) {
|
||||||
$j.getJSON(thisUrl + '?view=request&request=stats&raw=true', params)
|
$j.getJSON(thisUrl + '?view=request&request=stats&raw=true', params)
|
||||||
.done(function(data) {
|
.done(function(data) {
|
||||||
var stats = data.raw;
|
const stats = data.raw;
|
||||||
|
|
||||||
|
|
||||||
$j('#frameStatsTable').empty().append('<tbody>');
|
$j('#frameStatsTable').empty().append('<tbody>');
|
||||||
|
|
|
@ -4,7 +4,9 @@ var saveBtn = $j('#saveBtn');
|
||||||
var cancelBtn = $j('#cancelBtn');
|
var cancelBtn = $j('#cancelBtn');
|
||||||
var backBtn = $j('#backBtn');
|
var backBtn = $j('#backBtn');
|
||||||
var refreshBtn = $j('#refreshBtn');
|
var refreshBtn = $j('#refreshBtn');
|
||||||
|
var analyseBtn = $j('#analyseBtn');
|
||||||
var monitors = [];
|
var monitors = [];
|
||||||
|
var analyse_frames = true;
|
||||||
|
|
||||||
function validateForm( form ) {
|
function validateForm( form ) {
|
||||||
var errors = [];
|
var errors = [];
|
||||||
|
@ -566,6 +568,7 @@ function watchdogCheck(type) {
|
||||||
function watchdogOk(type) {
|
function watchdogOk(type) {
|
||||||
watchdogInactive[type] = false;
|
watchdogInactive[type] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function presetSelectorBlur() {
|
function presetSelectorBlur() {
|
||||||
this.selectedIndex = 0;
|
this.selectedIndex = 0;
|
||||||
}
|
}
|
||||||
|
@ -656,12 +659,34 @@ function initPage() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( el = analyseBtn[0] ) {
|
||||||
|
el.onclick = function() {
|
||||||
|
console.log(analyse_frames);
|
||||||
|
analyse_frames = !analyse_frames;
|
||||||
|
if (analyse_frames) {
|
||||||
|
analyseBtn.addClass('btn-primary');
|
||||||
|
analyseBtn.removeClass('btn-secondary');
|
||||||
|
analyseBtn.attr('title', translate['Showing Analysis']);
|
||||||
|
} else {
|
||||||
|
analyseBtn.removeClass('btn-primaryary');
|
||||||
|
analyseBtn.addClass('btn-secondary');
|
||||||
|
analyseBtn.attr('title', translate['Not Showing Analysis']);
|
||||||
|
}
|
||||||
|
for ( var i = 0, length = monitors.length; i < length; i++ ) {
|
||||||
|
monitors[i].show_analyse_frames(analyse_frames);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
console.log('Analyse button not found');
|
||||||
|
}
|
||||||
|
|
||||||
for ( var i = 0, length = monitorData.length; i < length; i++ ) {
|
for ( var i = 0, length = monitorData.length; i < length; i++ ) {
|
||||||
monitors[i] = new MonitorStream(monitorData[i]);
|
monitors[i] = new MonitorStream(monitorData[i]);
|
||||||
|
|
||||||
// Start the fps and status updates. give a random delay so that we don't assault the server
|
// Start the fps and status updates. give a random delay so that we don't assault the server
|
||||||
var delay = Math.round( (Math.random()+0.5)*statusRefreshTimeout );
|
var delay = Math.round( (Math.random()+0.5)*statusRefreshTimeout );
|
||||||
monitors[i].setScale('auto');
|
monitors[i].setScale('auto');
|
||||||
|
monitors[i].show_analyse_frames(analyse_frames);
|
||||||
monitors[i].start(delay);
|
monitors[i].start(delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,8 @@ var deleteString = "<?php echo translate('Delete') ?>";
|
||||||
var CMD_PAUSE = <?php echo CMD_PAUSE ?>;
|
var CMD_PAUSE = <?php echo CMD_PAUSE ?>;
|
||||||
var CMD_PLAY = <?php echo CMD_PLAY ?>;
|
var CMD_PLAY = <?php echo CMD_PLAY ?>;
|
||||||
var CMD_STOP = <?php echo CMD_STOP ?>;
|
var CMD_STOP = <?php echo CMD_STOP ?>;
|
||||||
|
var CMD_ANALYZE_ON = <?php echo CMD_ANALYZE_ON ?>;
|
||||||
|
var CMD_ANALYZE_OFF = <?php echo CMD_ANALYZE_OFF ?>;
|
||||||
var CMD_QUERY = <?php echo CMD_QUERY ?>;
|
var CMD_QUERY = <?php echo CMD_QUERY ?>;
|
||||||
|
|
||||||
var SCALE_BASE = <?php echo SCALE_BASE ?>;
|
var SCALE_BASE = <?php echo SCALE_BASE ?>;
|
||||||
|
@ -120,3 +122,7 @@ var canStreamNative = <?php echo canStreamNative()?'true':'false' ?>;
|
||||||
var refreshApplet = <?php echo (canStreamApplet() && $streamMode == "jpeg")?'true':'false' ?>;
|
var refreshApplet = <?php echo (canStreamApplet() && $streamMode == "jpeg")?'true':'false' ?>;
|
||||||
var appletRefreshTime = <?php echo ZM_RELOAD_CAMBOZOLA ?>;
|
var appletRefreshTime = <?php echo ZM_RELOAD_CAMBOZOLA ?>;
|
||||||
|
|
||||||
|
var translate = {
|
||||||
|
"Showing Analysis": '<?php echo translate('Showing Analysis'); ?>',
|
||||||
|
"Not Showing Analysis": '<?php echo translate('Not Showing Analysis'); ?>'
|
||||||
|
};
|
||||||
|
|
|
@ -723,6 +723,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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,10 +169,13 @@ if ( count($other_zones) ) {
|
||||||
<?php echo translate('State') ?>: <span id="stateValue<?php echo $monitor->Id() ?>"></span> - <span id="fpsValue<?php echo $monitor->Id() ?>"></span> fps
|
<?php echo translate('State') ?>: <span id="stateValue<?php echo $monitor->Id() ?>"></span> - <span id="fpsValue<?php echo $monitor->Id() ?>"></span> fps
|
||||||
</div>
|
</div>
|
||||||
<div id="StreamControlButtons">
|
<div id="StreamControlButtons">
|
||||||
<button type="button" id="pauseBtn" title="<?php echo translate('Pause') ?>">
|
<button type="button" id="analyseBtn" class="btn btn-primary" title="<?php echo translate('Showing Analysis') ?>">
|
||||||
|
<i class="material-icons md-18">assessment</i>
|
||||||
|
</button>
|
||||||
|
<button type="button" id="pauseBtn" class="btn btn-primary" title="<?php echo translate('Pause') ?>">
|
||||||
<i class="material-icons md-18">pause</i>
|
<i class="material-icons md-18">pause</i>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" id="playBtn" title="<?php echo translate('Play') ?>">
|
<button type="button" id="playBtn" class="btn btn-primary" title="<?php echo translate('Play') ?>">
|
||||||
<i class="material-icons md-18">play_arrow</i>
|
<i class="material-icons md-18">play_arrow</i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue