Merge branch 'master' into multistream
This commit is contained in:
commit
3835087c9d
|
@ -3,7 +3,7 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
"env": {
|
"env": {
|
||||||
"browser": true,
|
"browser": true,
|
||||||
"es6": true,
|
"es2017": true,
|
||||||
},
|
},
|
||||||
"extends": ["google"],
|
"extends": ["google"],
|
||||||
"overrides": [{
|
"overrides": [{
|
||||||
|
|
|
@ -321,7 +321,7 @@ if(NOT ZM_NO_CURL)
|
||||||
find_package(CURL)
|
find_package(CURL)
|
||||||
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})
|
||||||
include_directories(${CURL_INCLUDE_DIRS})
|
include_directories(${CURL_INCLUDE_DIRS})
|
||||||
set(CMAKE_REQUIRED_INCLUDES ${CURL_INCLUDE_DIRS})
|
set(CMAKE_REQUIRED_INCLUDES ${CURL_INCLUDE_DIRS})
|
||||||
check_include_file("curl/curl.h" HAVE_CURL_CURL_H)
|
check_include_file("curl/curl.h" HAVE_CURL_CURL_H)
|
||||||
|
|
|
@ -4,7 +4,6 @@ ZoneMinder
|
||||||
[![Build Status](https://travis-ci.org/ZoneMinder/zoneminder.png)](https://travis-ci.org/ZoneMinder/zoneminder)
|
[![Build Status](https://travis-ci.org/ZoneMinder/zoneminder.png)](https://travis-ci.org/ZoneMinder/zoneminder)
|
||||||
[![Bounty Source](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received)
|
[![Bounty Source](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received)
|
||||||
[![Join Slack](https://github.com/ozonesecurity/ozonebase/blob/master/img/slacksm.png?raw=true)](https://join.slack.com/t/zoneminder-chat/shared_invite/enQtNTU0NDkxMDM5NDQwLTdhZmQ5Y2M2NWQyN2JkYTBiN2ZkMzIzZGQ0MDliMTRmM2FjZWRlYzUwYTQ2MjMwMTVjMzQ1NjYxOTdmMjE2MTE)
|
[![Join Slack](https://github.com/ozonesecurity/ozonebase/blob/master/img/slacksm.png?raw=true)](https://join.slack.com/t/zoneminder-chat/shared_invite/enQtNTU0NDkxMDM5NDQwLTdhZmQ5Y2M2NWQyN2JkYTBiN2ZkMzIzZGQ0MDliMTRmM2FjZWRlYzUwYTQ2MjMwMTVjMzQ1NjYxOTdmMjE2MTE)
|
||||||
[![IRC Network](https://img.shields.io/badge/irc-%23zoneminder-blue.svg "IRC Freenode")](https://webchat.freenode.net/?channels=zoneminder)
|
|
||||||
|
|
||||||
All documentation for ZoneMinder is now online at https://zoneminder.readthedocs.org
|
All documentation for ZoneMinder is now online at https://zoneminder.readthedocs.org
|
||||||
|
|
||||||
|
|
|
@ -459,6 +459,7 @@ CREATE TABLE `Monitors` (
|
||||||
`Recording` enum('None', 'OnMotion', 'Always') NOT NULL default 'Always',
|
`Recording` enum('None', 'OnMotion', 'Always') NOT NULL default 'Always',
|
||||||
`Enabled` tinyint(3) unsigned NOT NULL default '1',
|
`Enabled` tinyint(3) unsigned NOT NULL default '1',
|
||||||
`DecodingEnabled` tinyint(3) unsigned NOT NULL default '1',
|
`DecodingEnabled` tinyint(3) unsigned NOT NULL default '1',
|
||||||
|
`JanusEnabled` BOOLEAN NOT NULL default false,
|
||||||
`LinkedMonitors` varchar(255),
|
`LinkedMonitors` varchar(255),
|
||||||
`Triggers` set('X10') NOT NULL default '',
|
`Triggers` set('X10') NOT NULL default '',
|
||||||
`EventStartCommand` VARCHAR(255) NOT NULL DEFAULT '',
|
`EventStartCommand` VARCHAR(255) NOT NULL DEFAULT '',
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
--
|
||||||
|
-- Update Monitors table to have JanusEnabled
|
||||||
|
--
|
||||||
|
|
||||||
|
SELECT 'Checking for JanusEnabled in Monitors';
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*)
|
||||||
|
FROM INFORMATION_SCHEMA.COLUMNS
|
||||||
|
WHERE table_name = 'Monitors'
|
||||||
|
AND table_schema = DATABASE()
|
||||||
|
AND column_name = 'JanusEnabled'
|
||||||
|
) > 0,
|
||||||
|
"SELECT 'Column JanusEnabled already exists in Monitors'",
|
||||||
|
"ALTER TABLE `Monitors` ADD COLUMN `JanusEnabled` BOOLEAN NOT NULL default false AFTER `DecodingEnabled`"
|
||||||
|
));
|
||||||
|
|
||||||
|
PREPARE stmt FROM @s;
|
||||||
|
EXECUTE stmt;
|
|
@ -0,0 +1,50 @@
|
||||||
|
general: {
|
||||||
|
configs_folder = "/usr/local/etc/janus" # Configuration files folder
|
||||||
|
plugins_folder = "/usr/local/lib/janus/plugins" # Plugins folder
|
||||||
|
transports_folder = "/usr/local/lib/janus/transports" # Transports folder
|
||||||
|
events_folder = "/usr/local/lib/janus/events" # Event handlers folder
|
||||||
|
loggers_folder = "/usr/local/lib/janus/loggers" # External loggers folder
|
||||||
|
debug_level = 4 # Debug/logging level, valid values are 0-7
|
||||||
|
admin_secret = "janusoverlord" # String that all Janus requests must contain
|
||||||
|
protected_folders = [
|
||||||
|
"/bin",
|
||||||
|
"/boot",
|
||||||
|
"/dev",
|
||||||
|
"/etc",
|
||||||
|
"/initrd",
|
||||||
|
"/lib",
|
||||||
|
"/lib32",
|
||||||
|
"/lib64",
|
||||||
|
"/proc",
|
||||||
|
"/sbin",
|
||||||
|
"/sys",
|
||||||
|
"/usr",
|
||||||
|
"/var",
|
||||||
|
"/opt/janus/bin",
|
||||||
|
"/opt/janus/etc",
|
||||||
|
"/opt/janus/include",
|
||||||
|
"/opt/janus/lib",
|
||||||
|
"/opt/janus/lib32",
|
||||||
|
"/opt/janus/lib64",
|
||||||
|
"/opt/janus/sbin"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
media: {
|
||||||
|
#ipv6 = true
|
||||||
|
#ipv6_linklocal = true
|
||||||
|
rtp_port_range = "20000-40000"
|
||||||
|
}
|
||||||
|
nat: {
|
||||||
|
nice_debug = false
|
||||||
|
ignore_mdns = true
|
||||||
|
keep_private_host = true
|
||||||
|
ice_ignore_list = "vmnet"
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins: {
|
||||||
|
disable = "libjanus_audiobridge.so,libjanus_echotest.so,libjanus_recordplay.so,libjanus_sip.so,libjanus_textroom.so,libjanus_videocall.so,libjanus_videoroom.so,libjanus_voicemail.so,
|
||||||
|
libjanus_nosip.so"
|
||||||
|
}
|
||||||
|
transports: {
|
||||||
|
disable = "libjanus_rabbitmq.so, libjanus_pfunix.so,libjanus_websockets.so"
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
general: {
|
||||||
|
admin_key = "supersecret"
|
||||||
|
rtp_port_range = "20000-40000"
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
general: {
|
||||||
|
json = "indented" # Whether the JSON messages should be indented (default),
|
||||||
|
base_path = "/janus" # Base path to bind to in the web server (plain HTTP only)
|
||||||
|
http = true # Whether to enable the plain HTTP interface
|
||||||
|
port = 8088 # Web server HTTP port
|
||||||
|
https = false # Whether to enable HTTPS (default=false)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Janus can also expose an admin/monitor endpoint, to allow you to check
|
||||||
|
# which sessions are up, which handles they're managing, their current
|
||||||
|
# status and so on. This provides a useful aid when debugging potential
|
||||||
|
# issues in Janus. The configuration is pretty much the same as the one
|
||||||
|
# already presented above for the webserver stuff, as the API is very
|
||||||
|
# similar: choose the base bath for the admin/monitor endpoint (/admin
|
||||||
|
# by default), ports, etc. Besides, you can specify
|
||||||
|
# a secret that must be provided in all requests as a crude form of
|
||||||
|
# authorization mechanism, and partial or full source IPs if you want to
|
||||||
|
# limit access basing on IP addresses. For security reasons, this
|
||||||
|
# endpoint is disabled by default, enable it by setting admin_http=true.
|
||||||
|
admin: {
|
||||||
|
admin_base_path = "/admin" # Base path to bind to in the admin/monitor web server (plain HTTP only)
|
||||||
|
admin_http = false # Whether to enable the plain HTTP interface
|
||||||
|
admin_port = 7088 # Admin/monitor web server HTTP port
|
||||||
|
admin_https = false # Whether to enable HTTPS (default=false)
|
||||||
|
}
|
|
@ -132,6 +132,7 @@ class Event {
|
||||||
|
|
||||||
SystemTimePoint StartTime() const { return start_time; }
|
SystemTimePoint StartTime() const { return start_time; }
|
||||||
SystemTimePoint EndTime() const { return end_time; }
|
SystemTimePoint EndTime() const { return end_time; }
|
||||||
|
TimePoint::duration Duration() const { return end_time - start_time; };
|
||||||
|
|
||||||
void AddPacket(const std::shared_ptr<ZMPacket> &p);
|
void AddPacket(const std::shared_ptr<ZMPacket> &p);
|
||||||
void AddPacket_(const std::shared_ptr<ZMPacket> &p);
|
void AddPacket_(const std::shared_ptr<ZMPacket> &p);
|
||||||
|
@ -178,18 +179,6 @@ class Event {
|
||||||
return pre_alarm_count;
|
return pre_alarm_count;
|
||||||
}
|
}
|
||||||
static void EmptyPreAlarmFrames() {
|
static void EmptyPreAlarmFrames() {
|
||||||
#if 0
|
|
||||||
while ( pre_alarm_count > 0 ) {
|
|
||||||
int i = pre_alarm_count - 1;
|
|
||||||
delete pre_alarm_data[i].image;
|
|
||||||
pre_alarm_data[i].image = nullptr;
|
|
||||||
if ( pre_alarm_data[i].alarm_frame ) {
|
|
||||||
delete pre_alarm_data[i].alarm_frame;
|
|
||||||
pre_alarm_data[i].alarm_frame = nullptr;
|
|
||||||
}
|
|
||||||
pre_alarm_count--;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
pre_alarm_count = 0;
|
pre_alarm_count = 0;
|
||||||
}
|
}
|
||||||
static void AddPreAlarmFrame(
|
static void AddPreAlarmFrame(
|
||||||
|
@ -198,28 +187,10 @@ class Event {
|
||||||
int score=0,
|
int score=0,
|
||||||
Image *alarm_frame=nullptr
|
Image *alarm_frame=nullptr
|
||||||
) {
|
) {
|
||||||
#if 0
|
|
||||||
pre_alarm_data[pre_alarm_count].image = new Image(*image);
|
|
||||||
pre_alarm_data[pre_alarm_count].timestamp = timestamp;
|
|
||||||
pre_alarm_data[pre_alarm_count].score = score;
|
|
||||||
if ( alarm_frame ) {
|
|
||||||
pre_alarm_data[pre_alarm_count].alarm_frame = new Image(*alarm_frame);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
pre_alarm_count++;
|
pre_alarm_count++;
|
||||||
}
|
}
|
||||||
void SavePreAlarmFrames() {
|
void SavePreAlarmFrames() {
|
||||||
#if 0
|
|
||||||
for ( int i = 0; i < pre_alarm_count; i++ ) {
|
|
||||||
AddFrame(
|
|
||||||
pre_alarm_data[i].image,
|
|
||||||
pre_alarm_data[i].timestamp,
|
|
||||||
pre_alarm_data[i].score,
|
|
||||||
pre_alarm_data[i].alarm_frame);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
EmptyPreAlarmFrames();
|
EmptyPreAlarmFrames();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_EVENT_H
|
#endif // ZM_EVENT_H
|
||||||
|
|
|
@ -1126,7 +1126,8 @@ bool EventStream::send_file(const std::string &filepath) {
|
||||||
} else {
|
} else {
|
||||||
Debug(1, "Failed to sendfile?");
|
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 != %d",
|
||||||
|
curr_frame_id, strerror(errno), rc, (int)filestat.st_size);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE];
|
static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE];
|
||||||
|
|
|
@ -77,7 +77,9 @@ struct Namespace namespaces[] =
|
||||||
// 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 =
|
||||||
|
<<<<<<< HEAD
|
||||||
"SELECT `Id`, `Name`, `ServerId`, `StorageId`, `Type`, `Capturing`+0, `Analysing`+0, `AnalysisSource`, `Recording`+0, `RecordingSource`, `DecodingEnabled`, "
|
"SELECT `Id`, `Name`, `ServerId`, `StorageId`, `Type`, `Capturing`+0, `Analysing`+0, `AnalysisSource`, `Recording`+0, `RecordingSource`, `DecodingEnabled`, "
|
||||||
|
"`JanusEnabled`,"
|
||||||
"`LinkedMonitors`, `EventStartCommand`, `EventEndCommand`, `AnalysisFPSLimit`, `AnalysisUpdateDelay`, `MaxFPS`, `AlarmMaxFPS`,"
|
"`LinkedMonitors`, `EventStartCommand`, `EventEndCommand`, `AnalysisFPSLimit`, `AnalysisUpdateDelay`, `MaxFPS`, `AlarmMaxFPS`,"
|
||||||
"`Device`, `Channel`, `Format`, `V4LMultiBuffer`, `V4LCapturesPerFrame`, " // V4L Settings
|
"`Device`, `Channel`, `Format`, `V4LMultiBuffer`, `V4LCapturesPerFrame`, " // V4L Settings
|
||||||
"`Protocol`, `Method`, `Options`, `User`, `Pass`, `Host`, `Port`, `Path`, `SecondPath`, `Width`, `Height`, `Colours`, `Palette`, `Orientation`+0, `Deinterlacing`, "
|
"`Protocol`, `Method`, `Options`, `User`, `Pass`, `Host`, `Port`, `Path`, `SecondPath`, `Width`, `Height`, `Colours`, `Palette`, `Orientation`+0, `Deinterlacing`, "
|
||||||
|
@ -318,6 +320,7 @@ Monitor::Monitor()
|
||||||
analysing(ANALYSING_ALWAYS),
|
analysing(ANALYSING_ALWAYS),
|
||||||
recording(RECORDING_ALWAYS),
|
recording(RECORDING_ALWAYS),
|
||||||
decoding_enabled(false),
|
decoding_enabled(false),
|
||||||
|
janus_enabled(false),
|
||||||
//protocol
|
//protocol
|
||||||
//method
|
//method
|
||||||
//options
|
//options
|
||||||
|
@ -459,7 +462,7 @@ Monitor::Monitor()
|
||||||
/*
|
/*
|
||||||
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`, "
|
`DecodingEnabled`, JanusEnabled, "
|
||||||
"LinkedMonitors, `EventStartCommand`, `EventEndCommand`, "
|
"LinkedMonitors, `EventStartCommand`, `EventEndCommand`, "
|
||||||
"AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS,"
|
"AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS,"
|
||||||
"Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, " // V4L Settings
|
"Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, " // V4L Settings
|
||||||
|
@ -517,6 +520,7 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
||||||
|
|
||||||
decoding_enabled = dbrow[col] ? atoi(dbrow[col]) : false; col++;
|
decoding_enabled = dbrow[col] ? atoi(dbrow[col]) : false; col++;
|
||||||
// See below after save_jpegs for a recalculation of decoding_enabled
|
// See below after save_jpegs for a recalculation of decoding_enabled
|
||||||
|
janus_enabled = dbrow[col] ? atoi(dbrow[col]) : false; col++;
|
||||||
|
|
||||||
ReloadLinkedMonitors(dbrow[col]); col++;
|
ReloadLinkedMonitors(dbrow[col]); col++;
|
||||||
event_start_command = dbrow[col] ? dbrow[col] : ""; col++;
|
event_start_command = dbrow[col] ? dbrow[col] : ""; col++;
|
||||||
|
@ -1092,10 +1096,11 @@ bool Monitor::connect() {
|
||||||
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
|
#ifdef WITH_GSOAP
|
||||||
|
|
||||||
|
//ONVIF Setup
|
||||||
ONVIF_Trigger_State = FALSE;
|
ONVIF_Trigger_State = FALSE;
|
||||||
if (onvif_event_listener) {
|
if (onvif_event_listener) { //Temporarily using this option to enable the feature
|
||||||
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
|
||||||
|
@ -1111,6 +1116,7 @@ bool Monitor::connect() {
|
||||||
proxyEvent = PullPointSubscriptionBindingProxy(soap);
|
proxyEvent = PullPointSubscriptionBindingProxy(soap);
|
||||||
std::string full_url = onvif_url + "/Events";
|
std::string full_url = onvif_url + "/Events";
|
||||||
proxyEvent.soap_endpoint = full_url.c_str();
|
proxyEvent.soap_endpoint = full_url.c_str();
|
||||||
|
|
||||||
set_credentials(soap);
|
set_credentials(soap);
|
||||||
Debug(1, "ONVIF Endpoint: %s", proxyEvent.soap_endpoint);
|
Debug(1, "ONVIF Endpoint: %s", proxyEvent.soap_endpoint);
|
||||||
if (proxyEvent.CreatePullPointSubscription(&request, response) != SOAP_OK) {
|
if (proxyEvent.CreatePullPointSubscription(&request, response) != SOAP_OK) {
|
||||||
|
@ -1130,6 +1136,15 @@ bool Monitor::connect() {
|
||||||
}
|
}
|
||||||
//End ONVIF Setup
|
//End ONVIF Setup
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if HAVE_LIBCURL //janus setup. Depends on libcurl.
|
||||||
|
if (janus_enabled && (path.find("rtsp://") != std::string::npos)) {
|
||||||
|
if (add_to_janus() != 0) {
|
||||||
|
Warning("Failed to add monitor stream to Janus!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#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;
|
||||||
|
@ -1856,6 +1871,12 @@ bool Monitor::Analyse() {
|
||||||
packetqueue.increment_it(analysis_it);
|
packetqueue.increment_it(analysis_it);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// Ready means that we have captured the warmup # of frames
|
||||||
|
if (!Ready()) {
|
||||||
|
Debug(3, "Not ready?");
|
||||||
|
delete packet_lock;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// signal is set by capture
|
// signal is set by capture
|
||||||
bool signal = shared_data->signal;
|
bool signal = shared_data->signal;
|
||||||
|
@ -1864,60 +1885,43 @@ bool Monitor::Analyse() {
|
||||||
Debug(3, "Motion detection is enabled signal(%d) signal_change(%d) trigger state(%s) image index %d",
|
Debug(3, "Motion detection is enabled signal(%d) signal_change(%d) trigger state(%s) image index %d",
|
||||||
signal, signal_change, TriggerState_Strings[trigger_data->trigger_state].c_str(), snap->image_index);
|
signal, signal_change, TriggerState_Strings[trigger_data->trigger_state].c_str(), snap->image_index);
|
||||||
|
|
||||||
|
{ // scope for event lock
|
||||||
// Need to guard around event creation/deletion from Reload()
|
// Need to guard around event creation/deletion from Reload()
|
||||||
std::lock_guard<std::mutex> lck(event_mutex);
|
std::lock_guard<std::mutex> lck(event_mutex);
|
||||||
|
|
||||||
// if we have been told to be OFF, then we are off and don't do any processing.
|
// if we have been told to be OFF, then we are off and don't do any processing.
|
||||||
if (trigger_data->trigger_state != TriggerState::TRIGGER_OFF) {
|
if (trigger_data->trigger_state != TriggerState::TRIGGER_OFF) {
|
||||||
Debug(4, "Trigger not OFF state is (%d)", int(trigger_data->trigger_state));
|
Debug(4, "Trigger not OFF state is (%d)", int(trigger_data->trigger_state));
|
||||||
// Ready means that we have captured the warmup # of frames
|
|
||||||
if (!Ready()) {
|
|
||||||
Debug(3, "Not ready?");
|
|
||||||
delete packet_lock;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int score = 0;
|
int score = 0;
|
||||||
std::string cause;
|
std::string cause;
|
||||||
Event::StringSetMap noteSetMap;
|
Event::StringSetMap noteSetMap;
|
||||||
|
|
||||||
#ifdef WITH_GSOAP
|
#ifdef WITH_GSOAP
|
||||||
|
if (onvif_event_listener && ONVIF_Healthy) {
|
||||||
if (ONVIF_Trigger_State) {
|
if (ONVIF_Trigger_State) {
|
||||||
score += 9;
|
score += 9;
|
||||||
Debug(1, "Triggered on ONVIF");
|
Debug(1, "Triggered on ONVIF");
|
||||||
if (!event) {
|
|
||||||
Event::StringSet noteSet;
|
Event::StringSet noteSet;
|
||||||
noteSet.insert("ONVIF2");
|
noteSet.insert("ONVIF2");
|
||||||
noteSetMap[MOTION_CAUSE] = noteSet;
|
noteSetMap[MOTION_CAUSE] = noteSet;
|
||||||
|
|
||||||
event = openEvent(snap, "ONVIF", noteSetMap);
|
|
||||||
cause += "ONVIF";
|
cause += "ONVIF";
|
||||||
} else {
|
|
||||||
event->addNote(MOTION_CAUSE, "ONVIF2");
|
|
||||||
// Add to cause
|
|
||||||
}
|
|
||||||
// Regardless of previous state, we go to ALARM
|
|
||||||
shared_data->state = state = ALARM;
|
|
||||||
//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 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)
|
if (!ONVIF_Closes_Event && state == ALARM)
|
||||||
ONVIF_Trigger_State = FALSE;
|
ONVIF_Trigger_State = FALSE;
|
||||||
} // end ONVIF_Trigger
|
} // end ONVIF_Trigger
|
||||||
|
} // end if (onvif_event_listener && ONVIF_Healthy)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Specifically told to be on. Setting the score here will trigger the alarm.
|
// Specifically told to be on. Setting the score here is not enough to trigger the alarm. Must jump directly to 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;
|
||||||
Debug(1, "Triggered on score += %d => %d", trigger_data->trigger_score, score);
|
Debug(1, "Triggered on score += %d => %d", trigger_data->trigger_score, score);
|
||||||
if (!event) {
|
if (!cause.empty()) cause += ", ";
|
||||||
|
cause += trigger_data->trigger_cause;
|
||||||
Event::StringSet noteSet;
|
Event::StringSet noteSet;
|
||||||
noteSet.insert(trigger_data->trigger_text);
|
noteSet.insert(trigger_data->trigger_text);
|
||||||
noteSetMap[trigger_data->trigger_cause] = noteSet;
|
noteSetMap[trigger_data->trigger_cause] = noteSet;
|
||||||
event = openEvent(snap, trigger_data->trigger_cause, noteSetMap);
|
|
||||||
Info("%s: %03d - Opening new event %" PRIu64 ", alarm start", name.c_str(), analysis_image_count, event->Id());
|
|
||||||
} else {
|
|
||||||
event->addNote(trigger_data->trigger_cause, trigger_data->trigger_text);
|
|
||||||
// Need to know if we should end the previous and start a new one, or just add the data
|
|
||||||
}
|
|
||||||
shared_data->state = state = ALARM;
|
shared_data->state = state = ALARM;
|
||||||
} // end if trigger_on
|
} // end if trigger_on
|
||||||
|
|
||||||
|
@ -1984,14 +1988,21 @@ bool Monitor::Analyse() {
|
||||||
|
|
||||||
/* try to stay behind the decoder. */
|
/* try to stay behind the decoder. */
|
||||||
if (decoding_enabled) {
|
if (decoding_enabled) {
|
||||||
if (!snap->decoded and !zm_terminate and !analysis_thread->Stopped()) {
|
while (!snap->decoded and !zm_terminate and !analysis_thread->Stopped()) {
|
||||||
// Need to wait for the decoder thread.
|
// Need to wait for the decoder thread.
|
||||||
|
// decoder thread might be waiting on the lock for this packet.
|
||||||
|
// So we need to relinquish the lock and wait. Waiting automatically relinquishes the lock
|
||||||
|
// So...
|
||||||
Debug(1, "Waiting for decode");
|
Debug(1, "Waiting for decode");
|
||||||
packetqueue.unlock(packet_lock); // This will delete packet_lock and notify_all
|
packet_lock->wait();
|
||||||
packetqueue.wait();
|
//packetqueue.unlock(packet_lock); // This will delete packet_lock and notify_all
|
||||||
// Everything may have changed, just return and start again. This needs to be more RAII
|
//packetqueue.wait();
|
||||||
return true;
|
////packet_lock->lock();
|
||||||
} // end while ! decoded
|
} // end while ! decoded
|
||||||
|
if (zm_terminate or analysis_thread->Stopped()) {
|
||||||
|
delete packet_lock;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} // end if decoding enabled
|
} // end if decoding enabled
|
||||||
|
|
||||||
if (shared_data->analysing) {
|
if (shared_data->analysing) {
|
||||||
|
@ -2004,15 +2015,15 @@ bool Monitor::Analyse() {
|
||||||
motion_frame_skip, capture_fps, analysis_fps_limit);
|
motion_frame_skip, capture_fps, analysis_fps_limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (snap->image) {
|
Event::StringSet zoneSet;
|
||||||
|
|
||||||
|
if (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");
|
||||||
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))) {
|
||||||
Event::StringSet zoneSet;
|
|
||||||
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.
|
||||||
snap->score = DetectMotion(*(snap->image), zoneSet);
|
snap->score = DetectMotion(*(snap->image), zoneSet);
|
||||||
|
@ -2042,6 +2053,7 @@ bool Monitor::Analyse() {
|
||||||
if (cause.length()) cause += ", ";
|
if (cause.length()) cause += ", ";
|
||||||
cause += MOTION_CAUSE + std::string(":") + snap->alarm_cause;
|
cause += MOTION_CAUSE + std::string(":") + snap->alarm_cause;
|
||||||
noteSetMap[MOTION_CAUSE] = zoneSet;
|
noteSetMap[MOTION_CAUSE] = zoneSet;
|
||||||
|
score += snap->score;
|
||||||
} // end if motion_score
|
} // end if motion_score
|
||||||
} else {
|
} else {
|
||||||
Debug(1, "Skipped motion detection last motion score was %d", last_motion_score);
|
Debug(1, "Skipped motion detection last motion score was %d", last_motion_score);
|
||||||
|
@ -2050,18 +2062,10 @@ bool Monitor::Analyse() {
|
||||||
} else {
|
} else {
|
||||||
Debug(1, "no image so skipping motion detection");
|
Debug(1, "no image so skipping motion detection");
|
||||||
} // end if has image
|
} // end if has image
|
||||||
//score += last_motion_score;
|
|
||||||
} else {
|
} else {
|
||||||
Debug(1, "Not analysing %d", shared_data->analysing);
|
Debug(1, "Not analysing %d", shared_data->analysing);
|
||||||
} // end if active and doing motion detection
|
} // end if active and doing motion detection
|
||||||
|
|
||||||
if (shared_data->recording) {
|
|
||||||
// If doing record, check to see if we need to close the event or not.
|
|
||||||
if (event && (section_length >= Seconds(min_section_length)) && (snap->timestamp - event->StartTime() > section_length)) {
|
|
||||||
if (
|
|
||||||
(recording == RECORDING_ONMOTION && event_close_mode != CLOSE_TIME)
|
|
||||||
|| (recording == RECORDING_ALWAYS && event_close_mode == CLOSE_TIME)
|
|
||||||
|| std::chrono::duration_cast<Seconds>(snap->timestamp.time_since_epoch()) % section_length == Seconds(0)) {
|
|
||||||
Info("%s: %03d - Closing event %" PRIu64 ", section end forced %" PRIi64 " - %" PRIi64 " = %" PRIi64 " >= %" PRIi64 ,
|
Info("%s: %03d - Closing event %" PRIu64 ", section end forced %" PRIi64 " - %" PRIi64 " = %" PRIi64 " >= %" PRIi64 ,
|
||||||
name.c_str(),
|
name.c_str(),
|
||||||
image_count,
|
image_count,
|
||||||
|
@ -2076,48 +2080,7 @@ bool Monitor::Analyse() {
|
||||||
|
|
||||||
if (shared_data->capturing && !event) {
|
if (shared_data->capturing && !event) {
|
||||||
event = openEvent(snap, cause.empty() ? "Continuous" : cause, noteSetMap);
|
event = openEvent(snap, cause.empty() ? "Continuous" : cause, noteSetMap);
|
||||||
|
if (!event) {
|
||||||
Info("%s: %03d - Opened new event %" PRIu64 ", continuous section start",
|
|
||||||
name.c_str(), analysis_image_count, event->Id());
|
|
||||||
/* To prevent cancelling out an existing alert\prealarm\alarm state */
|
|
||||||
if (state == IDLE) {
|
|
||||||
shared_data->state = state = TAPE;
|
|
||||||
}
|
|
||||||
} // end if ! event
|
|
||||||
} // end if RECORDING
|
|
||||||
|
|
||||||
if ((snap->score > 0) and (function != MONITOR)) {
|
|
||||||
if ((state == IDLE) || (state == TAPE) || (state == PREALARM)) {
|
|
||||||
// If we should end then previous continuous event and start a new non-continuous event
|
|
||||||
if (event) {
|
|
||||||
if (event->Frames()
|
|
||||||
&& !event->AlarmFrames()
|
|
||||||
&& (event_close_mode == CLOSE_ALARM)
|
|
||||||
&& ((snap->timestamp - event->StartTime()) >= min_section_length)
|
|
||||||
&& ((!pre_event_count) || (Event::PreAlarmCount() >= alarm_frame_count - 1))) {
|
|
||||||
Info("%s: %03d - Closing event %" PRIu64 ", continuous end, alarm begins",
|
|
||||||
name.c_str(), image_count, event->Id());
|
|
||||||
closeEvent();
|
|
||||||
} else {
|
|
||||||
// This is so if we need more than 1 alarm frame before going into alarm, so it is basically if we have enough alarm frames
|
|
||||||
Debug(3,
|
|
||||||
"pre_alarm_count in event %d of %d, event frames %d, alarm frames %d event length %" PRIi64 " >=? %" PRIi64 " min close mode is ALARM? %d",
|
|
||||||
Event::PreAlarmCount(), pre_event_count,
|
|
||||||
event->Frames(),
|
|
||||||
event->AlarmFrames(),
|
|
||||||
static_cast<int64>(std::chrono::duration_cast<Seconds>(snap->timestamp - event->StartTime()).count()),
|
|
||||||
static_cast<int64>(Seconds(min_section_length).count()),
|
|
||||||
(event_close_mode == CLOSE_ALARM));
|
|
||||||
}
|
|
||||||
} // end if event
|
|
||||||
|
|
||||||
if (Event::PreAlarmCount() >= alarm_frame_count-1) {
|
|
||||||
shared_data->state = state = ALARM;
|
|
||||||
|
|
||||||
Info("%s: %03d - Gone into alarm state PreAlarmCount: %u > AlarmFrameCount:%u Cause:%s",
|
|
||||||
name.c_str(), image_count, Event::PreAlarmCount(), alarm_frame_count, cause.c_str());
|
|
||||||
|
|
||||||
if (!event and (recording != RECORDING_NONE)) {
|
|
||||||
event = openEvent(snap, cause, noteSetMap);
|
event = openEvent(snap, cause, noteSetMap);
|
||||||
Info("%s: %03d - Opening new event %" PRIu64 ", alarm start", name.c_str(), analysis_image_count, event->Id());
|
Info("%s: %03d - Opening new event %" PRIu64 ", alarm start", name.c_str(), analysis_image_count, event->Id());
|
||||||
} // end if no event, so start it
|
} // end if no event, so start it
|
||||||
|
@ -2143,45 +2106,42 @@ bool Monitor::Analyse() {
|
||||||
shared_data->state = state = ALARM;
|
shared_data->state = state = ALARM;
|
||||||
Debug(1, "Was in TAPE, going into ALARM");
|
Debug(1, "Was in TAPE, going into ALARM");
|
||||||
} else {
|
} else {
|
||||||
Debug(3, "Staying in %s", State_Strings[state].c_str());
|
Debug(1, "Staying in %s", State_Strings[state].c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state == ALARM) {
|
if (state == ALARM) {
|
||||||
last_alarm_count = analysis_image_count;
|
last_alarm_count = analysis_image_count;
|
||||||
} // This is needed so post_event_count counts after last alarmed frames while in ALARM not single alarmed frames while ALERT
|
} // This is needed so post_event_count counts after last alarmed frames while in ALARM not single alarmed frames while ALERT
|
||||||
} else if (!score and snap->score == 0) { // snap->score means -1 which means didn't do motion detection so don't do state transition
|
} else if (!score and (snap->score == 0)) { // snap->score means -1 which means didn't do motion detection so don't do state transition
|
||||||
Debug(1, "!score");
|
Debug(1, "!score %s", State_Strings[state].c_str());
|
||||||
alert_to_alarm_frame_count = alarm_frame_count; // load same value configured for alarm_frame_count
|
alert_to_alarm_frame_count = alarm_frame_count; // load same value configured for alarm_frame_count
|
||||||
|
|
||||||
if (state == ALARM) {
|
if (state == ALARM) {
|
||||||
Info("%s: %03d - Gone into alert state", name.c_str(), analysis_image_count);
|
Info("%s: %03d - Gone into alert state", name.c_str(), analysis_image_count);
|
||||||
shared_data->state = state = ALERT;
|
shared_data->state = state = ALERT;
|
||||||
} else if (state == ALERT) {
|
} else if (state == ALERT) {
|
||||||
if (((analysis_image_count - last_alarm_count) > post_event_count)
|
if (
|
||||||
|
((analysis_image_count - last_alarm_count) > post_event_count)
|
||||||
&&
|
&&
|
||||||
((snap->timestamp - event->StartTime()) >= min_section_length)) {
|
(event->Duration() >= min_section_length)) {
|
||||||
Info("%s: %03d - Left alarm state (%" PRIu64 ") - %d(%d) images",
|
Info("%s: %03d - Left alarm state (%" PRIu64 ") - %d(%d) images",
|
||||||
name.c_str(), analysis_image_count, event->Id(), event->Frames(), event->AlarmFrames());
|
name.c_str(), analysis_image_count, event->Id(), event->Frames(), event->AlarmFrames());
|
||||||
//if ( function != MOCORD || event_close_mode == CLOSE_ALARM || event->Cause() == SIGNAL_CAUSE )
|
if (
|
||||||
if (event) {
|
(recording = RECORDING_ONDEMAND)
|
||||||
if ((recording == RECORDING_ONMOTION)
|
|
||||||
||
|
||
|
||||||
(event_close_mode == CLOSE_ALARM || event_close_mode==CLOSE_IDLE)
|
(event_close_mode == CLOSE_ALARM || event_close_mode==CLOSE_IDLE)
|
||||||
) {
|
) {
|
||||||
shared_data->state = state = IDLE;
|
shared_data->state = state = IDLE;
|
||||||
Info("%s: %03d - Closing event %" PRIu64 ", alarm end%s",
|
Info("%s: %03d - Closing event %" PRIu64 ", alarm end%s",
|
||||||
name.c_str(), analysis_image_count, event->Id(), (function==MOCORD)?", section truncated":"" );
|
name.c_str(), analysis_image_count, event->Id(), (recording==RECORDING_ALWAYS)?", section truncated":"" );
|
||||||
|
|
||||||
closeEvent();
|
closeEvent();
|
||||||
} else {
|
} else {
|
||||||
shared_data->state = state = TAPE;
|
shared_data->state = state = TAPE;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
shared_data->state = state = IDLE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (state == PREALARM) {
|
} else if (state == PREALARM) {
|
||||||
// Back to IDLE
|
// Back to IDLE
|
||||||
shared_data->state = state = (event ? TAPE : IDLE);
|
shared_data->state = state = ((recording == RECORDING_ALWAYS) ? IDLE : TAPE);
|
||||||
} else {
|
} else {
|
||||||
Debug(1,
|
Debug(1,
|
||||||
"State %d %s because analysis_image_count(%d)-last_alarm_count(%d) > post_event_count(%d) and timestamp.tv_sec(%" PRIi64 ") - recording.tv_src(%" PRIi64 ") >= min_section_length(%" PRIi64 ")",
|
"State %d %s because analysis_image_count(%d)-last_alarm_count(%d) > post_event_count(%d) and timestamp.tv_sec(%" PRIi64 ") - recording.tv_src(%" PRIi64 ") >= min_section_length(%" PRIi64 ")",
|
||||||
|
@ -2208,12 +2168,12 @@ bool Monitor::Analyse() {
|
||||||
if (event) {
|
if (event) {
|
||||||
if (noteSetMap.size() > 0)
|
if (noteSetMap.size() > 0)
|
||||||
event->updateNotes(noteSetMap);
|
event->updateNotes(noteSetMap);
|
||||||
if (section_length != Seconds(min_section_length) && (snap->timestamp - event->StartTime() >= section_length)) {
|
if (section_length >= Seconds(min_section_length) && (event->Duration() >= section_length)) {
|
||||||
Warning("%s: %03d - event %" PRIu64 ", has exceeded desired section length. %" PRIi64 " - %" PRIi64 " = %" PRIi64 " >= %" PRIi64,
|
Warning("%s: %03d - event %" PRIu64 ", has exceeded desired section length. %" PRIi64 " - %" PRIi64 " = %" PRIi64 " >= %" PRIi64,
|
||||||
name.c_str(), analysis_image_count, event->Id(),
|
name.c_str(), analysis_image_count, event->Id(),
|
||||||
static_cast<int64>(std::chrono::duration_cast<Seconds>(snap->timestamp.time_since_epoch()).count()),
|
static_cast<int64>(std::chrono::duration_cast<Seconds>(snap->timestamp.time_since_epoch()).count()),
|
||||||
static_cast<int64>(std::chrono::duration_cast<Seconds>(GetVideoWriterStartTime().time_since_epoch()).count()),
|
static_cast<int64>(std::chrono::duration_cast<Seconds>(event->StartTime().time_since_epoch()).count()),
|
||||||
static_cast<int64>(std::chrono::duration_cast<Seconds>(snap->timestamp - GetVideoWriterStartTime()).count()),
|
static_cast<int64>(std::chrono::duration_cast<Seconds>(event->Duration()).count()),
|
||||||
static_cast<int64>(Seconds(section_length).count()));
|
static_cast<int64>(Seconds(section_length).count()));
|
||||||
closeEvent();
|
closeEvent();
|
||||||
event = openEvent(snap, cause, noteSetMap);
|
event = openEvent(snap, cause, noteSetMap);
|
||||||
|
@ -2229,7 +2189,40 @@ bool Monitor::Analyse() {
|
||||||
// bulk frame code moved to event.
|
// bulk frame code moved to event.
|
||||||
} // end if state machine
|
} // end if state machine
|
||||||
|
|
||||||
if ((analysing != ANALYSING_NONE) and snap->image) {
|
if (shared_data->recording) {
|
||||||
|
// If doing record, check to see if we need to close the event or not.
|
||||||
|
if (event && (section_length >= Seconds(min_section_length)) && (event->Duration() > section_length)) {
|
||||||
|
if (
|
||||||
|
(recording == RECORDING_ONMOTION && event_close_mode != CLOSE_TIME)
|
||||||
|
|| (recording == RECORDING_ALWAYS && event_close_mode == CLOSE_TIME)
|
||||||
|
|| std::chrono::duration_cast<Seconds>(snap->timestamp.time_since_epoch()) % section_length == Seconds(0)) {
|
||||||
|
|
||||||
|
Info("%s: %03d - Closing event %" PRIu64 ", section end forced %" PRIi64 " - %" PRIi64 " = %" PRIi64 " >= %" PRIi64 ,
|
||||||
|
name.c_str(),
|
||||||
|
image_count,
|
||||||
|
event->Id(),
|
||||||
|
static_cast<int64>(std::chrono::duration_cast<Seconds>(snap->timestamp.time_since_epoch()).count()),
|
||||||
|
static_cast<int64>(std::chrono::duration_cast<Seconds>(event->StartTime().time_since_epoch()).count()),
|
||||||
|
static_cast<int64>(std::chrono::duration_cast<Seconds>(snap->timestamp - event->StartTime()).count()),
|
||||||
|
static_cast<int64>(Seconds(section_length).count()));
|
||||||
|
closeEvent();
|
||||||
|
} // end if section_length
|
||||||
|
} // end if event
|
||||||
|
|
||||||
|
if (!event) {
|
||||||
|
event = openEvent(snap, cause.empty() ? "Continuous" : cause, noteSetMap);
|
||||||
|
|
||||||
|
Info("%s: %03d - Opened new event %" PRIu64 ", continuous section start",
|
||||||
|
name.c_str(), analysis_image_count, event->Id());
|
||||||
|
/* To prevent cancelling out an existing alert\prealarm\alarm state */
|
||||||
|
// This ignores current score status. This should all come after the state machine calculations
|
||||||
|
if (state == IDLE) {
|
||||||
|
shared_data->state = state = TAPE;
|
||||||
|
}
|
||||||
|
} // end if ! event
|
||||||
|
} // end if RECORDING
|
||||||
|
|
||||||
|
if ((analysiing == ANALYSING_ALWAYS) and snap->image) {
|
||||||
if (!ref_image.Buffer()) {
|
if (!ref_image.Buffer()) {
|
||||||
Debug(1, "Assigning");
|
Debug(1, "Assigning");
|
||||||
ref_image.Assign(*(snap->image));
|
ref_image.Assign(*(snap->image));
|
||||||
|
@ -2253,6 +2246,7 @@ bool Monitor::Analyse() {
|
||||||
} // end if ( trigger_data->trigger_state != TRIGGER_OFF )
|
} // end if ( trigger_data->trigger_state != TRIGGER_OFF )
|
||||||
|
|
||||||
if (event) event->AddPacket(snap);
|
if (event) event->AddPacket(snap);
|
||||||
|
} // end scope for event_lock
|
||||||
|
|
||||||
// In the case where people have pre-alarm frames, the web ui will generate the frame images
|
// In the case where people have pre-alarm frames, the web ui will generate the frame images
|
||||||
// from the mp4. So no one will notice anyways.
|
// from the mp4. So no one will notice anyways.
|
||||||
|
@ -2586,7 +2580,8 @@ bool Monitor::Decode() {
|
||||||
std::shared_ptr<ZMPacket> packet = packet_lock->packet_;
|
std::shared_ptr<ZMPacket> packet = packet_lock->packet_;
|
||||||
if (packet->codec_type != AVMEDIA_TYPE_VIDEO) {
|
if (packet->codec_type != AVMEDIA_TYPE_VIDEO) {
|
||||||
Debug(4, "Not video");
|
Debug(4, "Not video");
|
||||||
packetqueue.unlock(packet_lock);
|
//packetqueue.unlock(packet_lock);
|
||||||
|
delete packet_lock;
|
||||||
return true; // Don't need decode
|
return true; // Don't need decode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2692,24 +2687,35 @@ bool Monitor::Decode() {
|
||||||
capture_image->Deinterlace_Blend();
|
capture_image->Deinterlace_Blend();
|
||||||
} else if (deinterlacing_value == 4) {
|
} else if (deinterlacing_value == 4) {
|
||||||
while (!zm_terminate) {
|
while (!zm_terminate) {
|
||||||
|
// ICON FIXME SHould we not clone decoder_it?
|
||||||
ZMLockedPacket *deinterlace_packet_lock = packetqueue.get_packet(decoder_it);
|
ZMLockedPacket *deinterlace_packet_lock = packetqueue.get_packet(decoder_it);
|
||||||
if (!deinterlace_packet_lock) {
|
if (!deinterlace_packet_lock) {
|
||||||
packetqueue.unlock(packet_lock);
|
delete packet_lock;
|
||||||
|
//packetqueue.unlock(packet_lock);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (deinterlace_packet_lock->packet_->codec_type == packet->codec_type) {
|
if (deinterlace_packet_lock->packet_->codec_type == packet->codec_type) {
|
||||||
|
if (!deinterlace_packet_lock->packet_->image) {
|
||||||
|
Error("Can't de-interlace when we have to decode. De-Interlacing should only be useful on old low res local cams");
|
||||||
|
} else {
|
||||||
capture_image->Deinterlace_4Field(deinterlace_packet_lock->packet_->image, (deinterlacing>>8)&0xff);
|
capture_image->Deinterlace_4Field(deinterlace_packet_lock->packet_->image, (deinterlacing>>8)&0xff);
|
||||||
packetqueue.unlock(deinterlace_packet_lock);
|
}
|
||||||
|
delete deinterlace_packet_lock;
|
||||||
|
//packetqueue.unlock(deinterlace_packet_lock);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
packetqueue.unlock(deinterlace_packet_lock);
|
delete deinterlace_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) {
|
||||||
|
delete packet_lock;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} 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);
|
||||||
}
|
}
|
||||||
}
|
} // end if deinterlacing_value
|
||||||
|
|
||||||
if (orientation != ROTATE_0) {
|
if (orientation != ROTATE_0) {
|
||||||
Debug(3, "Doing rotation");
|
Debug(3, "Doing rotation");
|
||||||
|
@ -3140,7 +3146,9 @@ int Monitor::PrimeCapture() {
|
||||||
|
|
||||||
#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.
|
#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
|
//ONVIF Thread
|
||||||
|
|
||||||
if (onvif_event_listener && ONVIF_Healthy) {
|
if (onvif_event_listener && ONVIF_Healthy) {
|
||||||
|
|
||||||
if (!Poller) {
|
if (!Poller) {
|
||||||
Poller = zm::make_unique<PollThread>(this);
|
Poller = zm::make_unique<PollThread>(this);
|
||||||
} else {
|
} else {
|
||||||
|
@ -3202,6 +3210,11 @@ int Monitor::Close() {
|
||||||
soap = nullptr;
|
soap = nullptr;
|
||||||
} //End ONVIF
|
} //End ONVIF
|
||||||
#endif
|
#endif
|
||||||
|
#if HAVE_LIBCURL //Janus Teardown
|
||||||
|
if (janus_enabled && (purpose == CAPTURE)) {
|
||||||
|
remove_from_janus();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
packetqueue.clear();
|
packetqueue.clear();
|
||||||
if (audio_fifo) {
|
if (audio_fifo) {
|
||||||
|
@ -3338,3 +3351,141 @@ int SOAP_ENV__Fault(struct soap *soap, char *faultcode, char *faultstring, char
|
||||||
return soap_send_empty_response(soap, SOAP_OK);
|
return soap_send_empty_response(soap, SOAP_OK);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
size_t Monitor::WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
|
||||||
|
{
|
||||||
|
((std::string*)userp)->append((char*)contents, size * nmemb);
|
||||||
|
return size * nmemb;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Monitor::add_to_janus() {
|
||||||
|
//TODO clean this up, add error checking, etc
|
||||||
|
std::string response;
|
||||||
|
std::string endpoint = "127.0.0.1:8088/janus/";
|
||||||
|
std::string postData = "{\"janus\" : \"create\", \"transaction\" : \"randomString\"}";
|
||||||
|
std::string rtsp_username;
|
||||||
|
std::string rtsp_password;
|
||||||
|
std::string rtsp_path = "rtsp://";
|
||||||
|
std::string janus_id;
|
||||||
|
std::size_t pos;
|
||||||
|
std::size_t pos2;
|
||||||
|
CURLcode res;
|
||||||
|
|
||||||
|
curl = curl_easy_init();
|
||||||
|
if(!curl) return -1;
|
||||||
|
//parse username and password
|
||||||
|
pos = path.find(":", 7);
|
||||||
|
if (pos == std::string::npos) return -1;
|
||||||
|
rtsp_username = path.substr(7, pos-7);
|
||||||
|
|
||||||
|
pos2 = path.find("@", pos);
|
||||||
|
if (pos2 == std::string::npos) return -1;
|
||||||
|
|
||||||
|
rtsp_password = path.substr(pos+1, pos2 - pos - 1);
|
||||||
|
rtsp_path += path.substr(pos2 + 1);
|
||||||
|
|
||||||
|
//Start Janus API init. Need to get a session_id and handle_id
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, endpoint.c_str());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.c_str());
|
||||||
|
res = curl_easy_perform(curl);
|
||||||
|
if (res != CURLE_OK) return -1;
|
||||||
|
|
||||||
|
pos = response.find("\"id\": ");
|
||||||
|
if (pos == std::string::npos) return -1;
|
||||||
|
janus_id = response.substr(pos + 6, 16);
|
||||||
|
response = "";
|
||||||
|
endpoint += janus_id;
|
||||||
|
postData = "{\"janus\" : \"attach\", \"plugin\" : \"janus.plugin.streaming\", \"transaction\" : \"randomString\"}";
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL,endpoint.c_str());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.c_str());
|
||||||
|
res = curl_easy_perform(curl);
|
||||||
|
if (res != CURLE_OK) return -1;
|
||||||
|
pos = response.find("\"id\": ");
|
||||||
|
if (pos == std::string::npos) return -1;
|
||||||
|
std::string handle_id = response.substr(pos + 6, 16); //TODO: This is an assumption that the string is always 16
|
||||||
|
endpoint += "/";
|
||||||
|
endpoint += handle_id;
|
||||||
|
|
||||||
|
//Assemble our actual request
|
||||||
|
postData = "{\"janus\" : \"message\", \"transaction\" : \"randomString\", \"body\" : {";
|
||||||
|
postData += "\"request\" : \"create\", \"admin_key\" : \"supersecret\", \"type\" : \"rtsp\", ";
|
||||||
|
postData += "\"url\" : \"";
|
||||||
|
postData += rtsp_path;
|
||||||
|
postData += "\", \"rtsp_user\" : \"";
|
||||||
|
postData += rtsp_username;
|
||||||
|
postData += "\", \"rtsp_pwd\" : \"";
|
||||||
|
postData += rtsp_password;
|
||||||
|
postData += "\", \"id\" : ";
|
||||||
|
postData += std::to_string(id);
|
||||||
|
postData += ", \"video\" : true}}";
|
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL,endpoint.c_str());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.c_str());
|
||||||
|
res = curl_easy_perform(curl);
|
||||||
|
if (res != CURLE_OK) return -1;
|
||||||
|
Debug(1,"Added stream to Janus: %s", response.c_str());
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int Monitor::remove_from_janus() {
|
||||||
|
//TODO clean this up, add error checking, etc
|
||||||
|
std::string response;
|
||||||
|
std::string endpoint = "127.0.0.1:8088/janus/";
|
||||||
|
std::string postData = "{\"janus\" : \"create\", \"transaction\" : \"randomString\"}";
|
||||||
|
std::size_t pos;
|
||||||
|
CURLcode res;
|
||||||
|
|
||||||
|
curl = curl_easy_init();
|
||||||
|
if(!curl) return -1;
|
||||||
|
|
||||||
|
|
||||||
|
//Start Janus API init. Need to get a session_id and handle_id
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, endpoint.c_str());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.c_str());
|
||||||
|
res = curl_easy_perform(curl);
|
||||||
|
if (res != CURLE_OK) return -1;
|
||||||
|
|
||||||
|
pos = response.find("\"id\": ");
|
||||||
|
if (pos == std::string::npos) return -1;
|
||||||
|
std::string janus_id = response.substr(pos + 6, 16);
|
||||||
|
response = "";
|
||||||
|
endpoint += janus_id;
|
||||||
|
postData = "{\"janus\" : \"attach\", \"plugin\" : \"janus.plugin.streaming\", \"transaction\" : \"randomString\"}";
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL,endpoint.c_str());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.c_str());
|
||||||
|
res = curl_easy_perform(curl);
|
||||||
|
if (res != CURLE_OK) return -1;
|
||||||
|
|
||||||
|
pos = response.find("\"id\": ");
|
||||||
|
if (pos == std::string::npos) return -1;
|
||||||
|
std::string handle_id = response.substr(pos + 6, 16);
|
||||||
|
endpoint += "/";
|
||||||
|
endpoint += handle_id;
|
||||||
|
|
||||||
|
//Assemble our actual request
|
||||||
|
postData = "{\"janus\" : \"message\", \"transaction\" : \"randomString\", \"body\" : {";
|
||||||
|
postData += "\"request\" : \"destroy\", \"admin_key\" : \"supersecret\", \"id\" : ";
|
||||||
|
postData += std::to_string(id);
|
||||||
|
postData += "}}";
|
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL,endpoint.c_str());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.c_str());
|
||||||
|
res = curl_easy_perform(curl);
|
||||||
|
if (res != CURLE_OK) return -1;
|
||||||
|
|
||||||
|
Debug(1, "Removed stream from Janus: %s", response.c_str());
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <curl/curl.h>
|
||||||
|
|
||||||
#ifdef WITH_GSOAP
|
#ifdef WITH_GSOAP
|
||||||
#include "soapPullPointSubscriptionBindingProxy.h"
|
#include "soapPullPointSubscriptionBindingProxy.h"
|
||||||
|
@ -47,6 +48,7 @@ class Group;
|
||||||
#define MOTION_CAUSE "Motion"
|
#define MOTION_CAUSE "Motion"
|
||||||
#define LINKED_CAUSE "Linked"
|
#define LINKED_CAUSE "Linked"
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// This is the main class for monitors. Each monitor is associated
|
// This is the main class for monitors. Each monitor is associated
|
||||||
// with a camera and is effectively a collector for events.
|
// with a camera and is effectively a collector for events.
|
||||||
|
@ -305,6 +307,7 @@ protected:
|
||||||
RecordingSourceOption recording_source; // Primary, Secondary, Both
|
RecordingSourceOption recording_source; // Primary, Secondary, Both
|
||||||
|
|
||||||
bool decoding_enabled; // Whether the monitor will decode h264/h265 packets
|
bool decoding_enabled; // Whether the monitor will decode h264/h265 packets
|
||||||
|
bool janus_enabled; // Whether we set the h264/h265 stream up on janus
|
||||||
|
|
||||||
std::string protocol;
|
std::string protocol;
|
||||||
std::string method;
|
std::string method;
|
||||||
|
@ -485,6 +488,13 @@ protected:
|
||||||
void set_credentials(struct soap *soap);
|
void set_credentials(struct soap *soap);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
//curl stuff for Janus
|
||||||
|
CURL *curl;
|
||||||
|
//helper class for CURL
|
||||||
|
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp);
|
||||||
|
int add_to_janus();
|
||||||
|
int remove_from_janus();
|
||||||
|
|
||||||
// Used in check signal
|
// Used in check signal
|
||||||
uint8_t red_val;
|
uint8_t red_val;
|
||||||
uint8_t green_val;
|
uint8_t green_val;
|
||||||
|
|
|
@ -69,6 +69,16 @@ if ( !canEdit('Monitors') ) return;
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="form-group" id="FunctionJanusEnabled">
|
||||||
|
<label for="newJanusEnabled"><?php echo translate('Janus Enabled') ?></label>
|
||||||
|
<input type="checkbox" name="newJanusEnabled" id="newJanusEnabled" value="1"/>
|
||||||
|
<?php
|
||||||
|
if ( isset($OLANG['FUNCTION_JANUS_ENABLED']) ) {
|
||||||
|
echo '<div class="form-text">'.$OLANG['FUNCTION_JANUS_ENABLED']['Help'].'</div>';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
|
|
@ -116,6 +116,7 @@ class Monitor extends ZM_Object {
|
||||||
'Recording' => 'Always',
|
'Recording' => 'Always',
|
||||||
'Enabled' => array('type'=>'boolean','default'=>1),
|
'Enabled' => array('type'=>'boolean','default'=>1),
|
||||||
'DecodingEnabled' => array('type'=>'boolean','default'=>1),
|
'DecodingEnabled' => array('type'=>'boolean','default'=>1),
|
||||||
|
'JanusEnabled' => array('type'=>'boolean','default'=>0),
|
||||||
'LinkedMonitors' => array('type'=>'set', 'default'=>null),
|
'LinkedMonitors' => array('type'=>'set', 'default'=>null),
|
||||||
'Triggers' => array('type'=>'set','default'=>''),
|
'Triggers' => array('type'=>'set','default'=>''),
|
||||||
'EventStartCommand' => '',
|
'EventStartCommand' => '',
|
||||||
|
|
|
@ -2094,6 +2094,8 @@ function getStreamHTML($monitor, $options = array()) {
|
||||||
'format' => ZM_MPEG_LIVE_FORMAT
|
'format' => ZM_MPEG_LIVE_FORMAT
|
||||||
) );
|
) );
|
||||||
return getVideoStreamHTML( 'liveStream'.$monitor->Id(), $streamSrc, $options['width'], $options['height'], ZM_MPEG_LIVE_FORMAT, $monitor->Name() );
|
return getVideoStreamHTML( 'liveStream'.$monitor->Id(), $streamSrc, $options['width'], $options['height'], ZM_MPEG_LIVE_FORMAT, $monitor->Name() );
|
||||||
|
} else if ( $monitor->JanusEnabled() ) {
|
||||||
|
return '<video id="liveStream'.$monitor->Id().'" width="'.$options['width'].'"autoplay muted playsinline=""></video>';
|
||||||
} else if ( $options['mode'] == 'stream' and canStream() ) {
|
} else if ( $options['mode'] == 'stream' and canStream() ) {
|
||||||
$options['mode'] = 'jpeg';
|
$options['mode'] = 'jpeg';
|
||||||
$streamSrc = $monitor->getStreamSrc($options);
|
$streamSrc = $monitor->getStreamSrc($options);
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
|
@ -1124,6 +1124,11 @@ Always: A zmc process will run and immediately connect and stay connected.~~~~
|
||||||
optionally choose to not decode the H264/H265 packets. This will drastically reduce cpu use
|
optionally choose to not decode the H264/H265 packets. This will drastically reduce cpu use
|
||||||
but will make live view unavailable for this monitor.'
|
but will make live view unavailable for this monitor.'
|
||||||
),
|
),
|
||||||
|
'FUNCTION_JANUS_ENABLED' => array(
|
||||||
|
'Help' => '
|
||||||
|
Attempt to use Janus streaming server for h264/h265 live view. Experimental, but allows
|
||||||
|
for significantly better performance.'
|
||||||
|
),
|
||||||
'ImageBufferCount' => array(
|
'ImageBufferCount' => array(
|
||||||
'Help' => '
|
'Help' => '
|
||||||
Number of raw images available in /dev/shm. Currently should be set in the 3-5 range. Used for live viewing.'
|
Number of raw images available in /dev/shm. Currently should be set in the 3-5 range. Used for live viewing.'
|
||||||
|
|
|
@ -192,4 +192,6 @@ xhtmlHeaders(__FILE__, translate('CycleWatch'));
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<script src="<?php echo cache_bust('js/adapter.min.js') ?>"></script>
|
||||||
|
<script src="<?php echo cache_bust('js/janus.js') ?>"></script>
|
||||||
<?php xhtmlFooter() ?>
|
<?php xhtmlFooter() ?>
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
var server;
|
||||||
|
var janus = null;
|
||||||
|
var streaming2;
|
||||||
var intervalId;
|
var intervalId;
|
||||||
var pauseBtn = $j('#pauseBtn');
|
var pauseBtn = $j('#pauseBtn');
|
||||||
var playBtn = $j('#playBtn');
|
var playBtn = $j('#playBtn');
|
||||||
|
@ -46,6 +49,69 @@ function initCycle() {
|
||||||
intervalId = setInterval(nextCycleView, cycleRefreshTimeout);
|
intervalId = setInterval(nextCycleView, cycleRefreshTimeout);
|
||||||
var scale = $j('#scale').val();
|
var scale = $j('#scale').val();
|
||||||
if ( scale == '0' || scale == 'auto' ) changeScale();
|
if ( scale == '0' || scale == 'auto' ) changeScale();
|
||||||
|
|
||||||
|
if (monitorData[monIdx].janusEnabled) {
|
||||||
|
server = "http://" + window.location.hostname + ":8088/janus";
|
||||||
|
opaqueId = "streamingtest-"+Janus.randomString(12);
|
||||||
|
Janus.init({debug: "all", callback: function() {
|
||||||
|
janus = new Janus({
|
||||||
|
server: server,
|
||||||
|
success: function() {
|
||||||
|
janus.attach({
|
||||||
|
plugin: "janus.plugin.streaming",
|
||||||
|
opaqueId: opaqueId,
|
||||||
|
success: function(pluginHandle) {
|
||||||
|
streaming2 = pluginHandle;
|
||||||
|
var body = {"request": "watch", "id": monitorData[monIdx].id};
|
||||||
|
streaming2.send({"message": body});
|
||||||
|
},
|
||||||
|
error: function(error) {
|
||||||
|
Janus.error(" -- Error attaching plugin... ", error);
|
||||||
|
},
|
||||||
|
onmessage: function(msg, jsep) {
|
||||||
|
Janus.debug(" ::: Got a message :::");
|
||||||
|
Janus.debug(msg);
|
||||||
|
var result = msg["result"];
|
||||||
|
if (result !== null && result !== undefined) {
|
||||||
|
if (result["status"] !== undefined && result["status"] !== null) {
|
||||||
|
const status = result["status"];
|
||||||
|
console.log(status);
|
||||||
|
}
|
||||||
|
} else if (msg["error"] !== undefined && msg["error"] !== null) {
|
||||||
|
Janus.debug(msg["error"]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (jsep !== undefined && jsep !== null) {
|
||||||
|
Janus.debug("Handling SDP as well...");
|
||||||
|
Janus.debug(jsep);
|
||||||
|
// Offer from the plugin, let's answer
|
||||||
|
streaming2.createAnswer({
|
||||||
|
jsep: jsep,
|
||||||
|
// We want recvonly audio/video and, if negotiated, datachannels
|
||||||
|
media: {audioSend: false, videoSend: false, data: true},
|
||||||
|
success: function(jsep) {
|
||||||
|
Janus.debug("Got SDP!");
|
||||||
|
Janus.debug(jsep);
|
||||||
|
var body = {"request": "start"};
|
||||||
|
streaming2.send({"message": body, "jsep": jsep});
|
||||||
|
},
|
||||||
|
error: function(error) {
|
||||||
|
Janus.error("WebRTC error:", error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, //onmessage function
|
||||||
|
onremotestream: function(stream) {
|
||||||
|
Janus.debug(" ::: Got a remote track :::");
|
||||||
|
Janus.debug(stream);
|
||||||
|
Janus.attachMediaStream(document.getElementById("liveStream" + monitorData[monIdx].id), stream);
|
||||||
|
document.getElementById("liveStream" + monitorData[monIdx].id).play();
|
||||||
|
}
|
||||||
|
});// attach
|
||||||
|
} //Success functio
|
||||||
|
}); //new Janus
|
||||||
|
}}); //janus.init callback
|
||||||
|
} //if janus
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeSize() {
|
function changeSize() {
|
||||||
|
|
|
@ -20,7 +20,8 @@ monitorData[monitorData.length] = {
|
||||||
'url': '<?php echo $monitor->UrlToIndex() ?>',
|
'url': '<?php echo $monitor->UrlToIndex() ?>',
|
||||||
'onclick': function(){window.location.assign( '?view=watch&mid=<?php echo $monitor->Id() ?>' );},
|
'onclick': function(){window.location.assign( '?view=watch&mid=<?php echo $monitor->Id() ?>' );},
|
||||||
'type': '<?php echo $monitor->Type() ?>',
|
'type': '<?php echo $monitor->Type() ?>',
|
||||||
'refresh': '<?php echo $monitor->Refresh() ?>'
|
'refresh': '<?php echo $monitor->Refresh() ?>',
|
||||||
|
'janusEnabled': <?php echo $monitor->JanusEnabled() ?>
|
||||||
};
|
};
|
||||||
<?php
|
<?php
|
||||||
} // end foreach monitor
|
} // end foreach monitor
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
var server;
|
||||||
|
var janus = null;
|
||||||
|
var opaqueId;
|
||||||
|
var globalCount = 0;
|
||||||
|
var streamingList = [];
|
||||||
|
var janusMonitors = [];
|
||||||
/**
|
/**
|
||||||
* called when the layoutControl select element is changed, or the page
|
* called when the layoutControl select element is changed, or the page
|
||||||
* is rendered
|
* is rendered
|
||||||
|
@ -300,8 +306,30 @@ function initPage() {
|
||||||
$j("#flipMontageHeader").slideToggle("fast");
|
$j("#flipMontageHeader").slideToggle("fast");
|
||||||
$j("#hdrbutton").toggleClass('glyphicon-menu-down').toggleClass('glyphicon-menu-up');
|
$j("#hdrbutton").toggleClass('glyphicon-menu-down').toggleClass('glyphicon-menu-up');
|
||||||
}
|
}
|
||||||
|
var initJanus = false;
|
||||||
|
//var streamingMonitors = [];
|
||||||
for ( var i = 0, length = monitorData.length; i < length; i++ ) {
|
for ( var i = 0, length = monitorData.length; i < length; i++ ) {
|
||||||
|
if (monitorData[i].janusEnabled) {
|
||||||
|
initJanus = true;
|
||||||
|
janusMonitors.push(monitorData[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (initJanus) {
|
||||||
|
server = "http://" + window.location.hostname + ":8088/janus";
|
||||||
|
opaqueId = "streamingtest-"+Janus.randomString(12);
|
||||||
|
Janus.init({debug: "all", callback: function() {
|
||||||
|
janus = new Janus({
|
||||||
|
server: server,
|
||||||
|
success: function() {
|
||||||
|
for ( var i = 0, length = janusMonitors.length; i < length; i++ ) {
|
||||||
|
attachVideo(janus, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
for ( var i = 0, length = monitorData.length; i < length; i++ ) {
|
||||||
|
if (!monitorData[i].janusEnabled) {
|
||||||
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
|
||||||
|
@ -315,6 +343,7 @@ function initPage() {
|
||||||
}
|
}
|
||||||
monitors[i].setup_onclick();
|
monitors[i].setup_onclick();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
selectLayout('#zmMontageLayout');
|
selectLayout('#zmMontageLayout');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,5 +351,59 @@ function watchFullscreen() {
|
||||||
const content = document.getElementById('content');
|
const content = document.getElementById('content');
|
||||||
openFullscreen(content);
|
openFullscreen(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function attachVideo(janus, i) {
|
||||||
|
janus.attach({
|
||||||
|
plugin: "janus.plugin.streaming",
|
||||||
|
opaqueId: "streamingtest-"+Janus.randomString(12),
|
||||||
|
success: function(pluginHandle) {
|
||||||
|
janusMonitors[i].streaming = pluginHandle;
|
||||||
|
var body = {"request": "watch", "id": parseInt(janusMonitors[i].id)};
|
||||||
|
janusMonitors[i].streaming.send({"message": body});
|
||||||
|
},
|
||||||
|
error: function(error) {
|
||||||
|
Janus.error(" -- Error attaching plugin... ", error);
|
||||||
|
},
|
||||||
|
onmessage: function(msg, jsep) {
|
||||||
|
Janus.debug(" ::: Got a message :::");
|
||||||
|
Janus.debug(msg);
|
||||||
|
var result = msg["result"];
|
||||||
|
if (result !== null && result !== undefined) {
|
||||||
|
if (result["status"] !== undefined && result["status"] !== null) {
|
||||||
|
const status = result["status"];
|
||||||
|
console.log(status);
|
||||||
|
}
|
||||||
|
} else if (msg["error"] !== undefined && msg["error"] !== null) {
|
||||||
|
Janus.error(msg["error"]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (jsep !== undefined && jsep !== null) {
|
||||||
|
Janus.debug("Handling SDP as well...");
|
||||||
|
Janus.debug(jsep);
|
||||||
|
// Offer from the plugin, let's answer
|
||||||
|
janusMonitors[i].streaming.createAnswer({
|
||||||
|
jsep: jsep,
|
||||||
|
// We want recvonly audio/video and, if negotiated, datachannels
|
||||||
|
media: {audioSend: false, videoSend: false, data: true},
|
||||||
|
success: function(jsep) {
|
||||||
|
Janus.debug("Got SDP!");
|
||||||
|
Janus.debug(jsep);
|
||||||
|
var body = {"request": "start"};
|
||||||
|
janusMonitors[i].streaming.send({"message": body, "jsep": jsep});
|
||||||
|
},
|
||||||
|
error: function(error) {
|
||||||
|
Janus.error("WebRTC error:", error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, //onmessage function
|
||||||
|
onremotestream: function(ourstream) {
|
||||||
|
Janus.debug(" ::: Got a remote track :::");
|
||||||
|
Janus.debug(ourstream);
|
||||||
|
Janus.attachMediaStream(document.getElementById("liveStream" + janusMonitors[i].id), ourstream);
|
||||||
|
document.getElementById("liveStream" + janusMonitors[i].id).play();
|
||||||
|
}
|
||||||
|
});// attach
|
||||||
|
}
|
||||||
// Kick everything off
|
// Kick everything off
|
||||||
$j(document).ready(initPage);
|
$j(document).ready(initPage);
|
||||||
|
|
|
@ -24,6 +24,7 @@ monitorData[monitorData.length] = {
|
||||||
'connKey': <?php echo $monitor->connKey() ?>,
|
'connKey': <?php echo $monitor->connKey() ?>,
|
||||||
'width': <?php echo $monitor->ViewWidth() ?>,
|
'width': <?php echo $monitor->ViewWidth() ?>,
|
||||||
'height':<?php echo $monitor->ViewHeight() ?>,
|
'height':<?php echo $monitor->ViewHeight() ?>,
|
||||||
|
'janusEnabled':<?php echo $monitor->JanusEnabled() ?>,
|
||||||
'url': '<?php echo $monitor->UrlToIndex( ZM_MIN_STREAMING_PORT ? ($monitor->Id() + ZM_MIN_STREAMING_PORT) : '') ?>',
|
'url': '<?php echo $monitor->UrlToIndex( ZM_MIN_STREAMING_PORT ? ($monitor->Id() + ZM_MIN_STREAMING_PORT) : '') ?>',
|
||||||
'onclick': function(){window.location.assign( '?view=watch&mid=<?php echo $monitor->Id() ?>' );},
|
'onclick': function(){window.location.assign( '?view=watch&mid=<?php echo $monitor->Id() ?>' );},
|
||||||
'type': '<?php echo $monitor->Type() ?>',
|
'type': '<?php echo $monitor->Type() ?>',
|
||||||
|
|
|
@ -9,6 +9,10 @@ var forceAlmBtn = $j('#forceAlmBtn');
|
||||||
var table = $j('#eventList');
|
var table = $j('#eventList');
|
||||||
var filterQuery = '&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms][0][op]=%3d&filter[Query][terms][0][val]='+monitorId;
|
var filterQuery = '&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms][0][op]=%3d&filter[Query][terms][0][val]='+monitorId;
|
||||||
|
|
||||||
|
var server;
|
||||||
|
var janus = null;
|
||||||
|
var opaqueId;
|
||||||
|
var streaming2;
|
||||||
/*
|
/*
|
||||||
This is the format of the json object sent by bootstrap-table
|
This is the format of the json object sent by bootstrap-table
|
||||||
|
|
||||||
|
@ -899,6 +903,7 @@ function initPage() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (monitorType != 'WebSite') {
|
if (monitorType != 'WebSite') {
|
||||||
|
if (streamMode != 'janus') {
|
||||||
if (streamMode == 'single') {
|
if (streamMode == 'single') {
|
||||||
statusCmdTimer = setTimeout(statusCmdQuery, 200);
|
statusCmdTimer = setTimeout(statusCmdQuery, 200);
|
||||||
setInterval(watchdogCheck, statusRefreshTimeout*2, 'status');
|
setInterval(watchdogCheck, statusRefreshTimeout*2, 'status');
|
||||||
|
@ -906,8 +911,69 @@ function initPage() {
|
||||||
streamCmdTimer = setTimeout(streamCmdQuery, 200);
|
streamCmdTimer = setTimeout(streamCmdQuery, 200);
|
||||||
setInterval(watchdogCheck, statusRefreshTimeout*2, 'stream');
|
setInterval(watchdogCheck, statusRefreshTimeout*2, 'stream');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (canStreamNative || (streamMode == 'single')) {
|
if (streamMode == 'janus') {
|
||||||
|
server = "http://" + window.location.hostname + ":8088/janus";
|
||||||
|
opaqueId = "streamingtest-"+Janus.randomString(12);
|
||||||
|
Janus.init({debug: "all", callback: function() {
|
||||||
|
janus = new Janus({
|
||||||
|
server: server,
|
||||||
|
success: function() {
|
||||||
|
janus.attach({
|
||||||
|
plugin: "janus.plugin.streaming",
|
||||||
|
opaqueId: opaqueId,
|
||||||
|
success: function(pluginHandle) {
|
||||||
|
streaming2 = pluginHandle;
|
||||||
|
var body = {"request": "watch", "id": monitorId};
|
||||||
|
streaming2.send({"message": body});
|
||||||
|
},
|
||||||
|
error: function(error) {
|
||||||
|
Janus.error(" -- Error attaching plugin... ", error);
|
||||||
|
},
|
||||||
|
onmessage: function(msg, jsep) {
|
||||||
|
Janus.debug(" ::: Got a message :::");
|
||||||
|
Janus.debug(msg);
|
||||||
|
var result = msg["result"];
|
||||||
|
if (result !== null && result !== undefined) {
|
||||||
|
if (result["status"] !== undefined && result["status"] !== null) {
|
||||||
|
var status = result["status"];
|
||||||
|
console.log(status);
|
||||||
|
}
|
||||||
|
} else if (msg["error"] !== undefined && msg["error"] !== null) {
|
||||||
|
Janus.error(msg["error"]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (jsep !== undefined && jsep !== null) {
|
||||||
|
Janus.debug("Handling SDP as well...");
|
||||||
|
Janus.debug(jsep);
|
||||||
|
// Offer from the plugin, let's answer
|
||||||
|
streaming2.createAnswer({
|
||||||
|
jsep: jsep,
|
||||||
|
// We want recvonly audio/video and, if negotiated, datachannels
|
||||||
|
media: {audioSend: false, videoSend: false, data: true},
|
||||||
|
success: function(jsep) {
|
||||||
|
Janus.debug("Got SDP!");
|
||||||
|
Janus.debug(jsep);
|
||||||
|
var body = {"request": "start"};
|
||||||
|
streaming2.send({"message": body, "jsep": jsep});
|
||||||
|
},
|
||||||
|
error: function(error) {
|
||||||
|
Janus.error("WebRTC error:", error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, //onmessage function
|
||||||
|
onremotestream: function(stream) {
|
||||||
|
Janus.debug(" ::: Got a remote track :::");
|
||||||
|
Janus.debug(stream);
|
||||||
|
Janus.attachMediaStream(document.getElementById("liveStream" + monitorId), stream);
|
||||||
|
document.getElementById("liveStream" + monitorId).play();
|
||||||
|
}
|
||||||
|
}); // attach
|
||||||
|
} //Success functio
|
||||||
|
}); //new Janus
|
||||||
|
}}); //janus.init callback
|
||||||
|
} else if (canStreamNative || (streamMode == 'single')) {
|
||||||
var streamImg = $j('#imageFeed img');
|
var streamImg = $j('#imageFeed img');
|
||||||
if (!streamImg) streamImg = $j('#imageFeed object');
|
if (!streamImg) streamImg = $j('#imageFeed object');
|
||||||
if (!streamImg) {
|
if (!streamImg) {
|
||||||
|
|
|
@ -66,6 +66,7 @@ monitorData[monitorData.length] = {
|
||||||
'id': <?php echo $m->Id() ?>,
|
'id': <?php echo $m->Id() ?>,
|
||||||
'width': <?php echo $m->ViewWidth() ?>,
|
'width': <?php echo $m->ViewWidth() ?>,
|
||||||
'height':<?php echo $m->ViewHeight() ?>,
|
'height':<?php echo $m->ViewHeight() ?>,
|
||||||
|
'janusEnabled':<?php echo $m->JanusEnabled() ?>,
|
||||||
'url': '<?php echo $m->UrlToIndex() ?>',
|
'url': '<?php echo $m->UrlToIndex() ?>',
|
||||||
'onclick': function(){window.location.assign( '?view=watch&mid=<?php echo $m->Id() ?>' );},
|
'onclick': function(){window.location.assign( '?view=watch&mid=<?php echo $m->Id() ?>' );},
|
||||||
'type': '<?php echo $m->Type() ?>',
|
'type': '<?php echo $m->Type() ?>',
|
||||||
|
|
|
@ -1279,6 +1279,16 @@ echo htmlSelect('newMonitor[ReturnLocation]', $return_options, $monitor->ReturnL
|
||||||
<?php echo translate('frames')?>
|
<?php echo translate('frames')?>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr id="FunctionJanusEnabled">
|
||||||
|
<td class="text-right pr-3"><?php echo translate('Janus Live Stream') ?></td>
|
||||||
|
<td><input type="checkbox" name="newMonitor[JanusEnabled]" value="1"<?php echo $monitor->JanusEnabled() ? ' checked="checked"' : '' ?>/>
|
||||||
|
<?php
|
||||||
|
if ( isset($OLANG['FUNCTION_JANUS_ENABLED']) ) {
|
||||||
|
echo '<div class="form-text">'.$OLANG['FUNCTION_JANUS_ENABLED']['Help'].'</div>';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-right pr-3"><?php echo translate('DefaultRate') ?></td>
|
<td class="text-right pr-3"><?php echo translate('DefaultRate') ?></td>
|
||||||
<td><?php echo htmlSelect('newMonitor[DefaultRate]', $rates, $monitor->DefaultRate()); ?></td>
|
<td><?php echo htmlSelect('newMonitor[DefaultRate]', $rates, $monitor->DefaultRate()); ?></td>
|
||||||
|
|
|
@ -325,5 +325,7 @@ foreach (array_reverse($zones) as $zone) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<script src="<?php echo cache_bust('js/adapter.min.js') ?>"></script>
|
||||||
|
<script src="<?php echo cache_bust('js/janus.js') ?>"></script>
|
||||||
<script src="<?php echo cache_bust('js/MonitorStream.js') ?>"></script>
|
<script src="<?php echo cache_bust('js/MonitorStream.js') ?>"></script>
|
||||||
<?php xhtmlFooter() ?>
|
<?php xhtmlFooter() ?>
|
||||||
|
|
|
@ -135,7 +135,11 @@ if (isset($_REQUEST['height'])) {
|
||||||
}
|
}
|
||||||
|
|
||||||
$connkey = generateConnKey();
|
$connkey = generateConnKey();
|
||||||
|
if ( $monitor->JanusEnabled() ) {
|
||||||
|
$streamMode = 'janus';
|
||||||
|
} else {
|
||||||
$streamMode = getStreamMode();
|
$streamMode = getStreamMode();
|
||||||
|
}
|
||||||
|
|
||||||
noCacheHeaders();
|
noCacheHeaders();
|
||||||
xhtmlHeaders(__FILE__, $monitor->Name().' - '.translate('Feed'));
|
xhtmlHeaders(__FILE__, $monitor->Name().' - '.translate('Feed'));
|
||||||
|
@ -407,4 +411,6 @@ if ( ZM_WEB_SOUND_ON_ALARM ) {
|
||||||
?>
|
?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<script src="<?php echo cache_bust('js/adapter.min.js') ?>"></script>
|
||||||
|
<script src="<?php echo cache_bust('js/janus.js') ?>"></script>
|
||||||
<?php xhtmlFooter() ?>
|
<?php xhtmlFooter() ?>
|
||||||
|
|
Loading…
Reference in New Issue