:wqMerge branch 'master' into replace_function_concept
This commit is contained in:
commit
10e39b3b13
|
@ -318,7 +318,7 @@ endif()
|
||||||
# Do not check for cURL if ZM_NO_CURL is on
|
# Do not check for cURL if ZM_NO_CURL is on
|
||||||
if(NOT ZM_NO_CURL)
|
if(NOT ZM_NO_CURL)
|
||||||
# cURL
|
# cURL
|
||||||
find_package(CURL)
|
find_package(CURL REQUIRED)
|
||||||
if(CURL_FOUND)
|
if(CURL_FOUND)
|
||||||
set(HAVE_LIBCURL 1)
|
set(HAVE_LIBCURL 1)
|
||||||
list(APPEND ZM_BIN_LIBS ${CURL_LIBRARIES})
|
list(APPEND ZM_BIN_LIBS ${CURL_LIBRARIES})
|
||||||
|
|
|
@ -471,6 +471,7 @@ CREATE TABLE `Monitors` (
|
||||||
`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,
|
`ONVIF_Event_Listener` BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
`use_Amcrest_API` 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',
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
--
|
||||||
|
-- Update Monitors table to have use_Amcrest_API
|
||||||
|
--
|
||||||
|
|
||||||
|
SELECT 'Checking for use_Amcrest_API in Monitors';
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*)
|
||||||
|
FROM INFORMATION_SCHEMA.COLUMNS
|
||||||
|
WHERE table_name = 'Monitors'
|
||||||
|
AND table_schema = DATABASE()
|
||||||
|
AND column_name = 'use_Amcrest_API'
|
||||||
|
) > 0,
|
||||||
|
"SELECT 'Column use_Amcrest_API already exists in Monitors'",
|
||||||
|
"ALTER TABLE `Monitors` ADD COLUMN `use_Amcrest_API` BOOLEAN NOT NULL default false AFTER `ONVIF_Event_Listener`"
|
||||||
|
));
|
||||||
|
|
||||||
|
PREPARE stmt FROM @s;
|
||||||
|
EXECUTE stmt;
|
|
@ -1 +1 @@
|
||||||
Subproject commit eab32851421ffe54fec0229c3efc44c642bc8d46
|
Subproject commit 1b40f1661f93f50fd5805f239d1e466a3bcf888f
|
|
@ -36,7 +36,7 @@
|
||||||
%global _hardened_build 1
|
%global _hardened_build 1
|
||||||
|
|
||||||
Name: zoneminder
|
Name: zoneminder
|
||||||
Version: 1.37.10
|
Version: 1.37.11
|
||||||
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
|
||||||
|
|
|
@ -35,7 +35,7 @@ Run the following commands.
|
||||||
sudo apt install mariadb-server
|
sudo apt install mariadb-server
|
||||||
sudo apt install zoneminder
|
sudo apt install zoneminder
|
||||||
|
|
||||||
When mariadb is installed for the first time, it doesn't add a password to the root user. Therefore, for security, it is recommended to run ``mysql secure installation``.
|
By default MariaDB uses `unix socket authentication`_, so no root user password is required (root MariaDB user access only available to local root Linux user). If you wish, you can set a root MariaDB password (and apply other security tweaks) by running `mariadb-secure-installation`_.
|
||||||
|
|
||||||
**Step 3:** Setup permissions for zm.conf
|
**Step 3:** Setup permissions for zm.conf
|
||||||
|
|
||||||
|
@ -337,3 +337,6 @@ Reload Apache to enable your changes and then start ZoneMinder.
|
||||||
sudo systemctl start zoneminder
|
sudo systemctl start zoneminder
|
||||||
|
|
||||||
You are now ready to go with ZoneMinder. Open a browser and type either ``localhost/zm`` one the local machine or ``{IP-OF-ZM-SERVER}/zm`` if you connect from a remote computer.
|
You are now ready to go with ZoneMinder. Open a browser and type either ``localhost/zm`` one the local machine or ``{IP-OF-ZM-SERVER}/zm`` if you connect from a remote computer.
|
||||||
|
|
||||||
|
.. _unix socket authentication: https://mariadb.com/kb/en/authentication-plugin-unix-socket/
|
||||||
|
.. _mariadb-secure-installation: https://mariadb.com/kb/en/mysql_secure_installation/
|
||||||
|
|
|
@ -63,6 +63,7 @@ $serial = $primary_key = 'Id';
|
||||||
ONVIF_Password
|
ONVIF_Password
|
||||||
ONVIF_Options
|
ONVIF_Options
|
||||||
ONVIF_Event_Listener
|
ONVIF_Event_Listener
|
||||||
|
use_Amcrest_API
|
||||||
Device
|
Device
|
||||||
Channel
|
Channel
|
||||||
Format
|
Format
|
||||||
|
|
|
@ -93,7 +93,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_Event_Listener`, "
|
"`ONVIF_URL`, `ONVIF_Username`, `ONVIF_Password`, `ONVIF_Options`, `ONVIF_Event_Listener`, `use_Amcrest_API`, "
|
||||||
"`SignalCheckPoints`, `SignalCheckColour`, `Importance`-1 FROM `Monitors`";
|
"`SignalCheckPoints`, `SignalCheckColour`, `Importance`-1 FROM `Monitors`";
|
||||||
|
|
||||||
std::string CameraType_Strings[] = {
|
std::string CameraType_Strings[] = {
|
||||||
|
@ -434,9 +434,9 @@ Monitor::Monitor()
|
||||||
privacy_bitmask(nullptr),
|
privacy_bitmask(nullptr),
|
||||||
n_linked_monitors(0),
|
n_linked_monitors(0),
|
||||||
linked_monitors(nullptr),
|
linked_monitors(nullptr),
|
||||||
|
ONVIF_Closes_Event(FALSE),
|
||||||
#ifdef WITH_GSOAP
|
#ifdef WITH_GSOAP
|
||||||
soap(nullptr),
|
soap(nullptr),
|
||||||
ONVIF_Closes_Event(FALSE),
|
|
||||||
#endif
|
#endif
|
||||||
red_val(0),
|
red_val(0),
|
||||||
green_val(0),
|
green_val(0),
|
||||||
|
@ -460,7 +460,6 @@ Monitor::Monitor()
|
||||||
} // Monitor::Monitor
|
} // Monitor::Monitor
|
||||||
|
|
||||||
/*
|
/*
|
||||||
<<<<<<< HEAD
|
|
||||||
std::string load_monitor_sql =
|
std::string load_monitor_sql =
|
||||||
"SELECT `Id`, `Name`, `ServerId`, `StorageId`, `Type`, `Capturing`+0, `Analysing`+0, `AnalysisSource`, `Recording`+0, `RecordingSource`,
|
"SELECT `Id`, `Name`, `ServerId`, `StorageId`, `Type`, `Capturing`+0, `Analysing`+0, `AnalysisSource`, `Recording`+0, `RecordingSource`,
|
||||||
`DecodingEnabled`, JanusEnabled, JanusAudioEnabled, "
|
`DecodingEnabled`, JanusEnabled, JanusAudioEnabled, "
|
||||||
|
@ -476,7 +475,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_Event_Listener`, "
|
"`ONVIF_URL`, `ONVIF_Username`, `ONVIF_Password`, `ONVIF_Options`, `ONVIF_Event_Listener`, `use_Amcrest_API`, "
|
||||||
"SignalCheckPoints, SignalCheckColour, Importance-1 FROM Monitors";
|
"SignalCheckPoints, SignalCheckColour, Importance-1 FROM Monitors";
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -656,6 +655,7 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
||||||
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++;
|
onvif_event_listener = (*dbrow[col] != '0'); col++;
|
||||||
|
use_Amcrest_API = (*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++;
|
||||||
|
@ -1057,6 +1057,7 @@ bool Monitor::connect() {
|
||||||
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) {
|
||||||
|
curl_global_init(CURL_GLOBAL_DEFAULT); //May not be the appropriate place. Need to do this before any other curl calls, and any other threads start.
|
||||||
memset(mem_ptr, 0, mem_size);
|
memset(mem_ptr, 0, mem_size);
|
||||||
shared_data->size = sizeof(SharedData);
|
shared_data->size = sizeof(SharedData);
|
||||||
shared_data->analysing = analysing;
|
shared_data->analysing = analysing;
|
||||||
|
@ -1098,16 +1099,25 @@ bool Monitor::connect() {
|
||||||
usedsubpixorder = camera->SubpixelOrder(); // Used in CheckSignal
|
usedsubpixorder = camera->SubpixelOrder(); // Used in CheckSignal
|
||||||
shared_data->valid = true;
|
shared_data->valid = true;
|
||||||
|
|
||||||
#ifdef WITH_GSOAP
|
|
||||||
|
|
||||||
//ONVIF Setup
|
//ONVIF and Amcrest Setup
|
||||||
|
//since they serve the same function, handling them as two options of the same feature.
|
||||||
ONVIF_Trigger_State = FALSE;
|
ONVIF_Trigger_State = FALSE;
|
||||||
if (onvif_event_listener) { //Temporarily using this option to enable the feature
|
if (onvif_event_listener) { //
|
||||||
Debug(1, "Starting ONVIF");
|
Debug(1, "Starting ONVIF");
|
||||||
ONVIF_Healthy = FALSE;
|
ONVIF_Healthy = FALSE;
|
||||||
if (onvif_options.find("closes_event") != std::string::npos) { //Option to indicate that ONVIF will send a close event message
|
if (onvif_options.find("closes_event") != std::string::npos) { //Option to indicate that ONVIF will send a close event message
|
||||||
ONVIF_Closes_Event = TRUE;
|
ONVIF_Closes_Event = TRUE;
|
||||||
}
|
}
|
||||||
|
if (use_Amcrest_API) {
|
||||||
|
curl_multi = curl_multi_init();
|
||||||
|
start_Amcrest();
|
||||||
|
//spin up curl_multi
|
||||||
|
//use the onvif_user and onvif_pass and onvif_url here.
|
||||||
|
//going to use the non-blocking curl api, and in the polling thread, block for 5 seconds waiting for input, just like onvif
|
||||||
|
//note that it's not possible for a single camera to use both.
|
||||||
|
} else { //using GSOAP
|
||||||
|
#ifdef WITH_GSOAP
|
||||||
tev__PullMessages.Timeout = "PT600S";
|
tev__PullMessages.Timeout = "PT600S";
|
||||||
tev__PullMessages.MessageLimit = 100;
|
tev__PullMessages.MessageLimit = 100;
|
||||||
soap = soap_new();
|
soap = soap_new();
|
||||||
|
@ -1134,11 +1144,14 @@ bool Monitor::connect() {
|
||||||
ONVIF_Healthy = TRUE;
|
ONVIF_Healthy = TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
Error("zmc not compiled with GSOAP. ONVIF support not built in!");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Debug(1, "Not Starting ONVIF");
|
Debug(1, "Not Starting ONVIF");
|
||||||
}
|
}
|
||||||
//End ONVIF Setup
|
//End ONVIF Setup
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HAVE_LIBCURL //janus setup. Depends on libcurl.
|
#if HAVE_LIBCURL //janus setup. Depends on libcurl.
|
||||||
if (janus_enabled && (path.find("rtsp://") != std::string::npos)) {
|
if (janus_enabled && (path.find("rtsp://") != std::string::npos)) {
|
||||||
|
@ -1247,6 +1260,12 @@ Monitor::~Monitor() {
|
||||||
sws_freeContext(convert_context);
|
sws_freeContext(convert_context);
|
||||||
convert_context = nullptr;
|
convert_context = nullptr;
|
||||||
}
|
}
|
||||||
|
if (Amcrest_handle != nullptr) {
|
||||||
|
curl_multi_remove_handle(curl_multi, Amcrest_handle);
|
||||||
|
curl_easy_cleanup(Amcrest_handle);
|
||||||
|
}
|
||||||
|
if (curl_multi != nullptr) curl_multi_cleanup(curl_multi);
|
||||||
|
curl_global_cleanup();
|
||||||
} // end Monitor::~Monitor()
|
} // end Monitor::~Monitor()
|
||||||
|
|
||||||
void Monitor::AddPrivacyBitmask() {
|
void Monitor::AddPrivacyBitmask() {
|
||||||
|
@ -1818,8 +1837,38 @@ bool Monitor::Poll() {
|
||||||
//We want to trigger every 5 seconds or so. so grab the time at the beginning of the loop, and sleep at the end.
|
//We want to trigger every 5 seconds or so. so grab the time at the beginning of the loop, and sleep at the end.
|
||||||
std::chrono::system_clock::time_point loop_start_time = std::chrono::system_clock::now();
|
std::chrono::system_clock::time_point loop_start_time = std::chrono::system_clock::now();
|
||||||
|
|
||||||
#ifdef WITH_GSOAP
|
|
||||||
if (ONVIF_Healthy) {
|
if (ONVIF_Healthy) {
|
||||||
|
if(use_Amcrest_API) {
|
||||||
|
int open_handles;
|
||||||
|
int transfers;
|
||||||
|
curl_multi_perform(curl_multi, &open_handles);
|
||||||
|
if (open_handles == 0) {
|
||||||
|
start_Amcrest(); //http transfer ended, need to restart.
|
||||||
|
} else {
|
||||||
|
curl_multi_wait(curl_multi, NULL, 0, 5000, &transfers); //wait for max 5 seconds for event.
|
||||||
|
if (transfers > 0) { //have data to deal with
|
||||||
|
curl_multi_perform(curl_multi, &open_handles); //actually grabs the data, populates amcrest_response
|
||||||
|
if (amcrest_response.find("action=Start") != std::string::npos) {
|
||||||
|
//Event Start
|
||||||
|
Debug(1,"Triggered on ONVIF");
|
||||||
|
if (!ONVIF_Trigger_State) {
|
||||||
|
Debug(1,"Triggered Event");
|
||||||
|
ONVIF_Trigger_State = TRUE;
|
||||||
|
}
|
||||||
|
} else if (amcrest_response.find("action=Stop") != std::string::npos){
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
amcrest_response.clear(); //We've dealt with the message, need to clear the queue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
#ifdef WITH_GSOAP
|
||||||
set_credentials(soap);
|
set_credentials(soap);
|
||||||
int result = proxyEvent.PullMessages(response.SubscriptionReference.Address, NULL, &tev__PullMessages, tev__PullMessagesResponse);
|
int result = proxyEvent.PullMessages(response.SubscriptionReference.Address, NULL, &tev__PullMessages, tev__PullMessagesResponse);
|
||||||
if (result != SOAP_OK) {
|
if (result != SOAP_OK) {
|
||||||
|
@ -1858,8 +1907,9 @@ bool Monitor::Poll() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
if (janus_enabled) {
|
if (janus_enabled) {
|
||||||
if (janus_session.empty()) {
|
if (janus_session.empty()) {
|
||||||
get_janus_session();
|
get_janus_session();
|
||||||
|
@ -1963,9 +2013,20 @@ bool Monitor::Analyse() {
|
||||||
} else {
|
} else {
|
||||||
event->addNote(SIGNAL_CAUSE, "Reacquired");
|
event->addNote(SIGNAL_CAUSE, "Reacquired");
|
||||||
}
|
}
|
||||||
if (snap->image)
|
if (snap->in_frame && (
|
||||||
|
((AVPixelFormat)snap->in_frame->format == AV_PIX_FMT_YUV420P)
|
||||||
|
||
|
||||||
|
((AVPixelFormat)snap->in_frame->format == AV_PIX_FMT_YUVJ420P)
|
||||||
|
) ) {
|
||||||
|
Debug(1, "assigning refimage from v-channel");
|
||||||
|
Image v_image(snap->in_frame->width,
|
||||||
|
snap->in_frame->height, 1, ZM_SUBPIX_ORDER_NONE, snap->in_frame->data[3], 0);
|
||||||
|
ref_image.Assign(v_image);
|
||||||
|
} else if (snap->image) {
|
||||||
|
Debug(1, "assigning refimage from snap->image");
|
||||||
ref_image.Assign(*(snap->image));
|
ref_image.Assign(*(snap->image));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
shared_data->state = state = IDLE;
|
shared_data->state = state = IDLE;
|
||||||
} // end if signal change
|
} // end if signal change
|
||||||
|
|
||||||
|
@ -2043,12 +2104,32 @@ bool Monitor::Analyse() {
|
||||||
// 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");
|
||||||
|
if (snap->in_frame && (
|
||||||
|
((AVPixelFormat)snap->in_frame->format == AV_PIX_FMT_YUV420P)
|
||||||
|
||
|
||||||
|
((AVPixelFormat)snap->in_frame->format == AV_PIX_FMT_YUVJ420P)
|
||||||
|
) ) {
|
||||||
|
Debug(1, "assigning refimage from v-channel");
|
||||||
|
Image v_image(snap->in_frame->width, snap->in_frame->height, 1, ZM_SUBPIX_ORDER_NONE, snap->in_frame->data[3], 0);
|
||||||
|
ref_image.Assign(v_image);
|
||||||
|
} else {
|
||||||
|
Debug(1, "assigning refimage from snap->image");
|
||||||
ref_image.Assign(*(snap->image));
|
ref_image.Assign(*(snap->image));
|
||||||
|
}
|
||||||
alarm_image.Assign(*(snap->image));
|
alarm_image.Assign(*(snap->image));
|
||||||
} else if (!(analysis_image_count % (motion_frame_skip+1))) {
|
} else if (!(analysis_image_count % (motion_frame_skip+1))) {
|
||||||
Debug(1, "Detecting motion on image %d, image %p", snap->image_index, snap->image);
|
Debug(1, "Detecting motion on image %d, image %p", snap->image_index, snap->image);
|
||||||
// Get new score.
|
// Get new score.
|
||||||
|
if (snap->in_frame && (
|
||||||
|
((AVPixelFormat)snap->in_frame->format == AV_PIX_FMT_YUV420P)
|
||||||
|
||
|
||||||
|
((AVPixelFormat)snap->in_frame->format == AV_PIX_FMT_YUVJ420P)
|
||||||
|
) ) {
|
||||||
|
Image v_image(snap->in_frame->width, snap->in_frame->height, 1, ZM_SUBPIX_ORDER_NONE, snap->in_frame->data[3], 0);
|
||||||
|
snap->score = DetectMotion(v_image, zoneSet);
|
||||||
|
} else {
|
||||||
snap->score = DetectMotion(*(snap->image), zoneSet);
|
snap->score = DetectMotion(*(snap->image), zoneSet);
|
||||||
|
}
|
||||||
|
|
||||||
if (!snap->analysis_image)
|
if (!snap->analysis_image)
|
||||||
snap->analysis_image = new Image(*(snap->image));
|
snap->analysis_image = new Image(*(snap->image));
|
||||||
|
@ -2263,14 +2344,40 @@ bool Monitor::Analyse() {
|
||||||
|
|
||||||
if ((analysing == ANALYSING_ALWAYS) and snap->image) {
|
if ((analysing == ANALYSING_ALWAYS) and snap->image) {
|
||||||
if (!ref_image.Buffer()) {
|
if (!ref_image.Buffer()) {
|
||||||
|
if (snap->in_frame && (
|
||||||
|
((AVPixelFormat)snap->in_frame->format == AV_PIX_FMT_YUV420P)
|
||||||
|
||
|
||||||
|
((AVPixelFormat)snap->in_frame->format == AV_PIX_FMT_YUVJ420P)
|
||||||
|
) ) {
|
||||||
|
Debug(1, "Assigning from vchannel");
|
||||||
|
Image v_image(snap->in_frame->width, snap->in_frame->height, 1, ZM_SUBPIX_ORDER_NONE, snap->in_frame->data[3], 0);
|
||||||
|
ref_image.Assign(v_image);
|
||||||
|
} else if (snap->image) {
|
||||||
Debug(1, "Assigning");
|
Debug(1, "Assigning");
|
||||||
ref_image.Assign(*(snap->image));
|
ref_image.Assign(*(snap->image));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Debug(1, "Blending");
|
if (snap->in_frame &&
|
||||||
|
(
|
||||||
|
((AVPixelFormat)snap->in_frame->format == AV_PIX_FMT_YUV420P)
|
||||||
|
||
|
||||||
|
((AVPixelFormat)snap->in_frame->format == AV_PIX_FMT_YUVJ420P)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Debug(1, "Blending from vchannel");
|
||||||
|
Image v_image(snap->in_frame->width, snap->in_frame->height, 1, ZM_SUBPIX_ORDER_NONE, snap->in_frame->data[3], 0);
|
||||||
|
ref_image.Blend(v_image, ( state==ALARM ? alarm_ref_blend_perc : ref_blend_perc ));
|
||||||
|
} else if (snap->image) {
|
||||||
|
Debug(1, "Blending because %p and format %d != %d, %d", snap->in_frame,
|
||||||
|
snap->in_frame->format,
|
||||||
|
AV_PIX_FMT_YUV420P,
|
||||||
|
AV_PIX_FMT_YUVJ420P
|
||||||
|
);
|
||||||
ref_image.Blend(*(snap->image), ( state==ALARM ? alarm_ref_blend_perc : ref_blend_perc ));
|
ref_image.Blend(*(snap->image), ( state==ALARM ? alarm_ref_blend_perc : ref_blend_perc ));
|
||||||
Debug(1, "Done Blending");
|
Debug(1, "Done Blending");
|
||||||
}
|
}
|
||||||
}
|
} // end if have image
|
||||||
|
} // end if detecting
|
||||||
last_signal = signal;
|
last_signal = signal;
|
||||||
} // end if videostream
|
} // end if videostream
|
||||||
} // end if signal
|
} // end if signal
|
||||||
|
@ -3232,11 +3339,15 @@ int Monitor::Close() {
|
||||||
analysis_thread->Stop();
|
analysis_thread->Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WITH_GSOAP
|
|
||||||
//ONVIF Teardown
|
//ONVIF Teardown
|
||||||
if (Poller) {
|
if (Poller) {
|
||||||
Poller->Stop();
|
Poller->Stop();
|
||||||
}
|
}
|
||||||
|
if (curl_multi != nullptr) {
|
||||||
|
curl_multi_cleanup(curl_multi);
|
||||||
|
curl_multi = nullptr;
|
||||||
|
}
|
||||||
|
#ifdef WITH_GSOAP
|
||||||
if (onvif_event_listener && (soap != nullptr)) {
|
if (onvif_event_listener && (soap != nullptr)) {
|
||||||
Debug(1, "Tearing Down Onvif");
|
Debug(1, "Tearing Down Onvif");
|
||||||
_wsnt__Unsubscribe wsnt__Unsubscribe;
|
_wsnt__Unsubscribe wsnt__Unsubscribe;
|
||||||
|
@ -3623,3 +3734,55 @@ int Monitor::get_janus_session() {
|
||||||
curl_easy_cleanup(curl);
|
curl_easy_cleanup(curl);
|
||||||
return 1;
|
return 1;
|
||||||
} //get_janus_session
|
} //get_janus_session
|
||||||
|
|
||||||
|
int Monitor::start_Amcrest() {
|
||||||
|
//init the transfer and start it in multi-handle
|
||||||
|
int running_handles;
|
||||||
|
long response_code;
|
||||||
|
struct CURLMsg *m;
|
||||||
|
CURLMcode curl_error;
|
||||||
|
if (Amcrest_handle != nullptr) { //potentially clean up the old handle
|
||||||
|
curl_multi_remove_handle(curl_multi, Amcrest_handle);
|
||||||
|
curl_easy_cleanup(Amcrest_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string full_url = onvif_url;
|
||||||
|
if (full_url.back() != '/') full_url += '/';
|
||||||
|
full_url += "eventManager.cgi?action=attach&codes=[VideoMotion]";
|
||||||
|
Amcrest_handle = curl_easy_init();
|
||||||
|
if (!Amcrest_handle){
|
||||||
|
Warning("Handle is null!");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
curl_easy_setopt(Amcrest_handle, CURLOPT_URL, full_url.c_str());
|
||||||
|
curl_easy_setopt(Amcrest_handle, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||||
|
curl_easy_setopt(Amcrest_handle, CURLOPT_WRITEDATA, &amcrest_response);
|
||||||
|
curl_easy_setopt(Amcrest_handle, CURLOPT_USERNAME, onvif_username.c_str());
|
||||||
|
curl_easy_setopt(Amcrest_handle, CURLOPT_PASSWORD, onvif_password.c_str());
|
||||||
|
curl_easy_setopt(Amcrest_handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
|
||||||
|
curl_error = curl_multi_add_handle(curl_multi, Amcrest_handle);
|
||||||
|
Warning("error of %i", curl_error);
|
||||||
|
curl_error = curl_multi_perform(curl_multi, &running_handles);
|
||||||
|
if (curl_error == CURLM_OK) {
|
||||||
|
curl_easy_getinfo(Amcrest_handle, CURLINFO_RESPONSE_CODE, &response_code);
|
||||||
|
int msgq = 0;
|
||||||
|
m = curl_multi_info_read(curl_multi, &msgq);
|
||||||
|
if (m && (m->msg == CURLMSG_DONE)) {
|
||||||
|
Warning("Libcurl exited Early: %i", m->data.result);
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_multi_wait(curl_multi, NULL, 0, 300, NULL);
|
||||||
|
curl_error = curl_multi_perform(curl_multi, &running_handles);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((curl_error == CURLM_OK) && (running_handles > 0)) {
|
||||||
|
ONVIF_Healthy = TRUE;
|
||||||
|
} else {
|
||||||
|
Warning("Response: %s", amcrest_response.c_str());
|
||||||
|
Warning("Seeing %i streams, and error of %i, url: %s", running_handles, curl_error, full_url.c_str());
|
||||||
|
curl_easy_getinfo(Amcrest_handle, CURLINFO_OS_ERRNO, &response_code);
|
||||||
|
Warning("Response code: %lu", response_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -324,7 +324,9 @@ 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;
|
||||||
|
std::string amcrest_response;
|
||||||
bool onvif_event_listener;
|
bool onvif_event_listener;
|
||||||
|
bool use_Amcrest_API;
|
||||||
|
|
||||||
std::string device;
|
std::string device;
|
||||||
int palette;
|
int palette;
|
||||||
|
@ -476,11 +478,12 @@ protected:
|
||||||
std::string diag_path_delta;
|
std::string diag_path_delta;
|
||||||
|
|
||||||
//ONVIF
|
//ONVIF
|
||||||
#ifdef WITH_GSOAP
|
bool ONVIF_Trigger_State; //Re-using some variables for Amcrest API support
|
||||||
struct soap *soap;
|
|
||||||
bool ONVIF_Trigger_State;
|
|
||||||
bool ONVIF_Healthy;
|
bool ONVIF_Healthy;
|
||||||
bool ONVIF_Closes_Event;
|
bool ONVIF_Closes_Event;
|
||||||
|
|
||||||
|
#ifdef WITH_GSOAP
|
||||||
|
struct soap *soap = nullptr;
|
||||||
_tev__CreatePullPointSubscription request;
|
_tev__CreatePullPointSubscription request;
|
||||||
_tev__CreatePullPointSubscriptionResponse response;
|
_tev__CreatePullPointSubscriptionResponse response;
|
||||||
_tev__PullMessages tev__PullMessages;
|
_tev__PullMessages tev__PullMessages;
|
||||||
|
@ -489,8 +492,11 @@ protected:
|
||||||
void set_credentials(struct soap *soap);
|
void set_credentials(struct soap *soap);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//curl stuff for Janus
|
//curl stuff
|
||||||
CURL *curl;
|
CURL *curl = nullptr;
|
||||||
|
CURLM *curl_multi = nullptr;
|
||||||
|
CURL *Amcrest_handle = nullptr;
|
||||||
|
int start_Amcrest();
|
||||||
//helper class for CURL
|
//helper class for CURL
|
||||||
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp);
|
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp);
|
||||||
int add_to_janus();
|
int add_to_janus();
|
||||||
|
|
|
@ -118,7 +118,7 @@ commonprep () {
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
RTSPVER="cd7fd49becad6010a1b8466bfebbd93999a39878"
|
RTSPVER="eab32851421ffe54fec0229c3efc44c642bc8d46"
|
||||||
if [ -e "build/RtspServer-${RTSPVER}.tar.gz" ]; then
|
if [ -e "build/RtspServer-${RTSPVER}.tar.gz" ]; then
|
||||||
echo "Found existing RtspServer ${RTSPVER} tarball..."
|
echo "Found existing RtspServer ${RTSPVER} tarball..."
|
||||||
else
|
else
|
||||||
|
|
|
@ -187,6 +187,9 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
|
||||||
|
|
||||||
$col_str = 'E.*, M.Name AS Monitor';
|
$col_str = 'E.*, M.Name AS Monitor';
|
||||||
$sql = 'SELECT ' .$col_str. ' FROM `Events` AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id'.$where.($sort?' ORDER BY '.$sort.' '.$order:'');
|
$sql = 'SELECT ' .$col_str. ' FROM `Events` AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id'.$where.($sort?' ORDER BY '.$sort.' '.$order:'');
|
||||||
|
if ($filter->limit() and !count($filter->pre_sql_conditions()) and !count($filter->post_sql_conditions())) {
|
||||||
|
$sql .= ' LIMIT '.$filter->limit();
|
||||||
|
}
|
||||||
|
|
||||||
$storage_areas = ZM\Storage::find();
|
$storage_areas = ZM\Storage::find();
|
||||||
$StorageById = array();
|
$StorageById = array();
|
||||||
|
@ -213,6 +216,12 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
|
||||||
$unfiltered_rows[] = $row;
|
$unfiltered_rows[] = $row;
|
||||||
} # end foreach row
|
} # end foreach row
|
||||||
|
|
||||||
|
# Filter limits come before pagination limits.
|
||||||
|
if ($filter->limit() and ($filter->limit() > count($unfiltered_rows))) {
|
||||||
|
ZM\Debug("Filtering rows due to filter->limit " . count($unfiltered_rows)." limit: ".$filter->limit());
|
||||||
|
$unfiltered_rows = array_slice($unfiltered_rows, 0, $filter->limit());
|
||||||
|
}
|
||||||
|
|
||||||
ZM\Debug('Have ' . count($unfiltered_rows) . ' events matching base filter.');
|
ZM\Debug('Have ' . count($unfiltered_rows) . ' events matching base filter.');
|
||||||
|
|
||||||
$filtered_rows = null;
|
$filtered_rows = null;
|
||||||
|
@ -251,8 +260,10 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
|
||||||
$filtered_rows = $unfiltered_rows;
|
$filtered_rows = $unfiltered_rows;
|
||||||
} # end if search_filter->terms() > 1
|
} # end if search_filter->terms() > 1
|
||||||
|
|
||||||
if ($limit)
|
if ($limit) {
|
||||||
|
ZM\Debug("Filtering rows due to limit " . count($filtered_rows)." offset: $offset limit: $limit");
|
||||||
$filtered_rows = array_slice($filtered_rows, $offset, $limit);
|
$filtered_rows = array_slice($filtered_rows, $offset, $limit);
|
||||||
|
}
|
||||||
|
|
||||||
$returned_rows = array();
|
$returned_rows = array();
|
||||||
foreach ($filtered_rows as $row) {
|
foreach ($filtered_rows as $row) {
|
||||||
|
|
|
@ -240,7 +240,7 @@ class Filter extends ZM_Object {
|
||||||
}
|
}
|
||||||
if ( isset( $this->Query()['limit'] ) )
|
if ( isset( $this->Query()['limit'] ) )
|
||||||
return $this->{'Query'}['limit'];
|
return $this->{'Query'}['limit'];
|
||||||
return 100;
|
return 0;
|
||||||
#return $this->defaults{'limit'};
|
#return $this->defaults{'limit'};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -110,7 +110,6 @@ class Monitor extends ZM_Object {
|
||||||
'ManufacturerId' => null,
|
'ManufacturerId' => null,
|
||||||
'ModelId' => null,
|
'ModelId' => null,
|
||||||
'Type' => 'Ffmpeg',
|
'Type' => 'Ffmpeg',
|
||||||
'Function' => 'Mocord',
|
|
||||||
'Capturing' => 'Always',
|
'Capturing' => 'Always',
|
||||||
'Analysing' => 'Always',
|
'Analysing' => 'Always',
|
||||||
'Recording' => 'Always',
|
'Recording' => 'Always',
|
||||||
|
@ -127,6 +126,7 @@ class Monitor extends ZM_Object {
|
||||||
'ONVIF_Password' => '',
|
'ONVIF_Password' => '',
|
||||||
'ONVIF_Options' => '',
|
'ONVIF_Options' => '',
|
||||||
'ONVIF_Event_Listener' => '0',
|
'ONVIF_Event_Listener' => '0',
|
||||||
|
'use_Amcrest_API' => '0',
|
||||||
'Device' => '',
|
'Device' => '',
|
||||||
'Channel' => 0,
|
'Channel' => 0,
|
||||||
'Format' => '0',
|
'Format' => '0',
|
||||||
|
@ -414,7 +414,7 @@ class Monitor extends ZM_Object {
|
||||||
if ($mode == 'restart') {
|
if ($mode == 'restart') {
|
||||||
daemonControl('stop', 'zmc', $zmcArgs);
|
daemonControl('stop', 'zmc', $zmcArgs);
|
||||||
}
|
}
|
||||||
if ($this->{'Function'} != 'None') {
|
if ($this->{'Capturing'} != 'None') {
|
||||||
daemonControl('start', 'zmc', $zmcArgs);
|
daemonControl('start', 'zmc', $zmcArgs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -789,7 +789,7 @@ class Monitor extends ZM_Object {
|
||||||
<span id="viewingFPS'.$this->Id().'" title="'.translate('Viewing FPS').'"><span id="viewingFPSValue'.$this->Id().'"></span> fps</span>
|
<span id="viewingFPS'.$this->Id().'" title="'.translate('Viewing FPS').'"><span id="viewingFPSValue'.$this->Id().'"></span> fps</span>
|
||||||
<span id="captureFPS'.$this->Id().'" title="'.translate('Capturing FPS').'"><span id="captureFPSValue'.$this->Id().'"></span> fps</span>
|
<span id="captureFPS'.$this->Id().'" title="'.translate('Capturing FPS').'"><span id="captureFPSValue'.$this->Id().'"></span> fps</span>
|
||||||
';
|
';
|
||||||
if ( $this->Function() == 'Modect' or $this->Function() == 'Mocord' ) {
|
if ($this->Analysing() != 'None') {
|
||||||
$html .= '<span id="analysisFPS'.$this->Id().'" title="'.translate('Analysis FPS').'"><span id="analysisFPSValue'.$this->Id().'"></span> fps</span>
|
$html .= '<span id="analysisFPS'.$this->Id().'" title="'.translate('Analysis FPS').'"><span id="analysisFPSValue'.$this->Id().'"></span> fps</span>
|
||||||
';
|
';
|
||||||
}
|
}
|
||||||
|
|
|
@ -316,7 +316,7 @@ if ($action == 'save') {
|
||||||
} # end if ZM_OPT_X10
|
} # end if ZM_OPT_X10
|
||||||
|
|
||||||
if ( $restart ) {
|
if ( $restart ) {
|
||||||
if ( $monitor->Function() != 'None' and $monitor->Type() != 'WebSite' ) {
|
if ( $monitor->Capturing() != 'None' and $monitor->Type() != 'WebSite' ) {
|
||||||
$monitor->zmcControl('start');
|
$monitor->zmcControl('start');
|
||||||
|
|
||||||
if ( $monitor->Controllable() ) {
|
if ( $monitor->Controllable() ) {
|
||||||
|
|
|
@ -275,6 +275,23 @@ function initPage() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Amcrest API controller
|
||||||
|
if (document.getElementsByName("newMonitor[ONVIF_Event_Listener]")[0].checked) {
|
||||||
|
document.getElementById("function_use_Amcrest_API").hidden = false;
|
||||||
|
} else {
|
||||||
|
document.getElementById("function_use_Amcrest_API").hidden = true;
|
||||||
|
}
|
||||||
|
document.getElementsByName("newMonitor[ONVIF_Event_Listener]")[0].addEventListener('change', function() {
|
||||||
|
if (this.checked) {
|
||||||
|
document.getElementById("function_use_Amcrest_API").hidden = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
document.getElementsByName("newMonitor[ONVIF_Event_Listener]")[1].addEventListener('change', function() {
|
||||||
|
if (this.checked) {
|
||||||
|
document.getElementById("function_use_Amcrest_API").hidden = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if ( ZM_OPT_USE_GEOLOCATION ) {
|
if ( ZM_OPT_USE_GEOLOCATION ) {
|
||||||
if ( window.L ) {
|
if ( window.L ) {
|
||||||
var form = document.getElementById('contentForm');
|
var form = document.getElementById('contentForm');
|
||||||
|
|
|
@ -754,6 +754,10 @@ if (count($available_monitor_ids)) {
|
||||||
<td class="text-right pr-3"><?php echo translate('ONVIF_Event_Listener') ?></td>
|
<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>
|
<td><?php echo html_radio('newMonitor[ONVIF_Event_Listener]', array('1'=>translate('Enabled'), '0'=>'Disabled'), $monitor->ONVIF_Event_Listener()); ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr id="function_use_Amcrest_API">
|
||||||
|
<td class="text-right pr-3"><?php echo translate('use_Amcrest_API') ?></td>
|
||||||
|
<td><?php echo html_radio('newMonitor[use_Amcrest_API]', array('1'=>translate('Enabled'), '0'=>'Disabled'), $monitor->use_Amcrest_API()); ?></td>
|
||||||
|
</tr>
|
||||||
<?php
|
<?php
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue