Merge pull request #3423 from jp-bennett/master
Starts moving elements from zm_monitor.cpp into nested classes and dedicated files.
This commit is contained in:
commit
1982c21e1f
|
@ -33,6 +33,9 @@ set(ZM_BIN_SRC_FILES
|
||||||
zm_libvnc_camera.cpp
|
zm_libvnc_camera.cpp
|
||||||
zm_local_camera.cpp
|
zm_local_camera.cpp
|
||||||
zm_monitor.cpp
|
zm_monitor.cpp
|
||||||
|
zm_monitor_monitorlink.cpp
|
||||||
|
zm_monitor_janus.cpp
|
||||||
|
zm_monitor_amcrest.cpp
|
||||||
zm_monitorstream.cpp
|
zm_monitorstream.cpp
|
||||||
zm_ffmpeg.cpp
|
zm_ffmpeg.cpp
|
||||||
zm_ffmpeg_camera.cpp
|
zm_ffmpeg_camera.cpp
|
||||||
|
|
|
@ -130,175 +130,7 @@ std::string TriggerState_Strings[] = {
|
||||||
"Cancel", "On", "Off"
|
"Cancel", "On", "Off"
|
||||||
};
|
};
|
||||||
|
|
||||||
Monitor::MonitorLink::MonitorLink(unsigned int p_id, const char *p_name) :
|
Monitor::Monitor()
|
||||||
id(p_id),
|
|
||||||
shared_data(nullptr),
|
|
||||||
trigger_data(nullptr),
|
|
||||||
video_store_data(nullptr)
|
|
||||||
{
|
|
||||||
strncpy(name, p_name, sizeof(name)-1);
|
|
||||||
|
|
||||||
#if ZM_MEM_MAPPED
|
|
||||||
map_fd = -1;
|
|
||||||
mem_file = stringtf("%s/zm.mmap.%u", staticConfig.PATH_MAP.c_str(), id);
|
|
||||||
#else // ZM_MEM_MAPPED
|
|
||||||
shm_id = 0;
|
|
||||||
#endif // ZM_MEM_MAPPED
|
|
||||||
mem_size = 0;
|
|
||||||
mem_ptr = nullptr;
|
|
||||||
|
|
||||||
last_event_id = 0;
|
|
||||||
last_state = IDLE;
|
|
||||||
|
|
||||||
last_connect_time = 0;
|
|
||||||
connected = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Monitor::MonitorLink::~MonitorLink() {
|
|
||||||
disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Monitor::MonitorLink::connect() {
|
|
||||||
SystemTimePoint now = std::chrono::system_clock::now();
|
|
||||||
if (!last_connect_time || (now - std::chrono::system_clock::from_time_t(last_connect_time)) > Seconds(60)) {
|
|
||||||
last_connect_time = std::chrono::system_clock::to_time_t(now);
|
|
||||||
|
|
||||||
mem_size = sizeof(SharedData) + sizeof(TriggerData);
|
|
||||||
|
|
||||||
Debug(1, "link.mem.size=%jd", static_cast<intmax_t>(mem_size));
|
|
||||||
#if ZM_MEM_MAPPED
|
|
||||||
map_fd = open(mem_file.c_str(), O_RDWR, (mode_t)0600);
|
|
||||||
if (map_fd < 0) {
|
|
||||||
Debug(3, "Can't open linked memory map file %s: %s", mem_file.c_str(), strerror(errno));
|
|
||||||
disconnect();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
while (map_fd <= 2) {
|
|
||||||
int new_map_fd = dup(map_fd);
|
|
||||||
Warning("Got one of the stdio fds for our mmap handle. map_fd was %d, new one is %d", map_fd, new_map_fd);
|
|
||||||
close(map_fd);
|
|
||||||
map_fd = new_map_fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct stat map_stat;
|
|
||||||
if (fstat(map_fd, &map_stat) < 0) {
|
|
||||||
Error("Can't stat linked memory map file %s: %s", mem_file.c_str(), strerror(errno));
|
|
||||||
disconnect();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (map_stat.st_size == 0) {
|
|
||||||
Error("Linked memory map file %s is empty: %s", mem_file.c_str(), strerror(errno));
|
|
||||||
disconnect();
|
|
||||||
return false;
|
|
||||||
} else if (map_stat.st_size < mem_size) {
|
|
||||||
Error("Got unexpected memory map file size %ld, expected %jd", map_stat.st_size, static_cast<intmax_t>(mem_size));
|
|
||||||
disconnect();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
mem_ptr = (unsigned char *)mmap(nullptr, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0);
|
|
||||||
if (mem_ptr == MAP_FAILED) {
|
|
||||||
Error("Can't map file %s (%jd bytes) to memory: %s", mem_file.c_str(), static_cast<intmax_t>(mem_size), strerror(errno));
|
|
||||||
disconnect();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#else // ZM_MEM_MAPPED
|
|
||||||
shm_id = shmget((config.shm_key&0xffff0000)|id, mem_size, 0700);
|
|
||||||
if (shm_id < 0) {
|
|
||||||
Debug(3, "Can't shmget link memory: %s", strerror(errno));
|
|
||||||
connected = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
mem_ptr = (unsigned char *)shmat(shm_id, 0, 0);
|
|
||||||
if ((int)mem_ptr == -1) {
|
|
||||||
Debug(3, "Can't shmat link memory: %s", strerror(errno));
|
|
||||||
connected = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif // ZM_MEM_MAPPED
|
|
||||||
|
|
||||||
shared_data = (SharedData *)mem_ptr;
|
|
||||||
trigger_data = (TriggerData *)((char *)shared_data + sizeof(SharedData));
|
|
||||||
|
|
||||||
if (!shared_data->valid) {
|
|
||||||
Debug(3, "Linked memory not initialised by capture daemon");
|
|
||||||
disconnect();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
last_state = shared_data->state;
|
|
||||||
last_event_id = shared_data->last_event_id;
|
|
||||||
connected = true;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} // end bool Monitor::MonitorLink::connect()
|
|
||||||
|
|
||||||
bool Monitor::MonitorLink::disconnect() {
|
|
||||||
if (connected) {
|
|
||||||
connected = false;
|
|
||||||
|
|
||||||
#if ZM_MEM_MAPPED
|
|
||||||
if (mem_ptr > (void *)0) {
|
|
||||||
msync(mem_ptr, mem_size, MS_ASYNC);
|
|
||||||
munmap(mem_ptr, mem_size);
|
|
||||||
}
|
|
||||||
if (map_fd >= 0)
|
|
||||||
close(map_fd);
|
|
||||||
|
|
||||||
map_fd = -1;
|
|
||||||
#else // ZM_MEM_MAPPED
|
|
||||||
struct shmid_ds shm_data;
|
|
||||||
if (shmctl(shm_id, IPC_STAT, &shm_data) < 0) {
|
|
||||||
Debug(3, "Can't shmctl: %s", strerror(errno));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
shm_id = 0;
|
|
||||||
|
|
||||||
if (shm_data.shm_nattch <= 1) {
|
|
||||||
if (shmctl(shm_id, IPC_RMID, 0) < 0) {
|
|
||||||
Debug(3, "Can't shmctl: %s", strerror(errno));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shmdt(mem_ptr) < 0) {
|
|
||||||
Debug(3, "Can't shmdt: %s", strerror(errno));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif // ZM_MEM_MAPPED
|
|
||||||
mem_size = 0;
|
|
||||||
mem_ptr = nullptr;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Monitor::MonitorLink::isAlarmed() {
|
|
||||||
if (!connected) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return( shared_data->state == ALARM );
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Monitor::MonitorLink::inAlarm() {
|
|
||||||
if (!connected) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return( shared_data->state == ALARM || shared_data->state == ALERT );
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Monitor::MonitorLink::hasAlarmed() {
|
|
||||||
if (shared_data->state == ALARM) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
last_event_id = shared_data->last_event_id;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Monitor::Monitor()
|
|
||||||
: id(0),
|
: id(0),
|
||||||
name(""),
|
name(""),
|
||||||
server_id(0),
|
server_id(0),
|
||||||
|
@ -317,7 +149,7 @@ Monitor::Monitor()
|
||||||
//user
|
//user
|
||||||
//pass
|
//pass
|
||||||
//path
|
//path
|
||||||
//device
|
//device
|
||||||
palette(0),
|
palette(0),
|
||||||
channel(0),
|
channel(0),
|
||||||
format(0),
|
format(0),
|
||||||
|
@ -422,7 +254,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),
|
Event_Poller_Closes_Event(FALSE),
|
||||||
|
Janus_Manager(nullptr),
|
||||||
|
Amcrest_Manager(nullptr),
|
||||||
#ifdef WITH_GSOAP
|
#ifdef WITH_GSOAP
|
||||||
soap(nullptr),
|
soap(nullptr),
|
||||||
#endif
|
#endif
|
||||||
|
@ -1083,21 +917,16 @@ bool Monitor::connect() {
|
||||||
|
|
||||||
|
|
||||||
//ONVIF and Amcrest Setup
|
//ONVIF and Amcrest Setup
|
||||||
//since they serve the same function, handling them as two options of the same feature.
|
//For now, only support one event type per camera, so share some state.
|
||||||
ONVIF_Trigger_State = FALSE;
|
Poll_Trigger_State = FALSE;
|
||||||
if (onvif_event_listener) { //
|
if (onvif_event_listener) { //
|
||||||
Debug(1, "Starting ONVIF");
|
Debug(1, "Starting ONVIF");
|
||||||
ONVIF_Healthy = FALSE;
|
Event_Poller_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;
|
Event_Poller_Closes_Event = TRUE;
|
||||||
}
|
}
|
||||||
if (use_Amcrest_API) {
|
if (use_Amcrest_API) {
|
||||||
curl_multi = curl_multi_init();
|
Amcrest_Manager = new AmcrestAPI(this);
|
||||||
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
|
} else { //using GSOAP
|
||||||
#ifdef WITH_GSOAP
|
#ifdef WITH_GSOAP
|
||||||
tev__PullMessages.Timeout = "PT600S";
|
tev__PullMessages.Timeout = "PT600S";
|
||||||
|
@ -1123,7 +952,7 @@ bool Monitor::connect() {
|
||||||
Error("Couldn't do initial event pull! Error %i %s, %s", soap->error, soap_fault_string(soap), soap_fault_detail(soap));
|
Error("Couldn't do initial event pull! Error %i %s, %s", soap->error, soap_fault_string(soap), soap_fault_detail(soap));
|
||||||
} else {
|
} else {
|
||||||
Debug(1, "Good Initial ONVIF Pull");
|
Debug(1, "Good Initial ONVIF Pull");
|
||||||
ONVIF_Healthy = TRUE;
|
Event_Poller_Healthy = TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
@ -1135,17 +964,9 @@ bool Monitor::connect() {
|
||||||
}
|
}
|
||||||
//End ONVIF Setup
|
//End ONVIF Setup
|
||||||
|
|
||||||
#if HAVE_LIBCURL //janus setup. Depends on libcurl.
|
if (janus_enabled) {
|
||||||
if (janus_enabled && (path.find("rtsp://") != std::string::npos)) {
|
Janus_Manager = new JanusManager(this);
|
||||||
get_janus_session();
|
|
||||||
if (add_to_janus() != 0) {
|
|
||||||
Warning("Failed to add monitor stream to Janus!"); //The first attempt may fail. Will be reattempted in the Poller thread
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
if (janus_enabled)
|
|
||||||
Error("zmc not compiled with LIBCURL. Janus support not built in!");
|
|
||||||
#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());
|
||||||
|
@ -1242,12 +1063,12 @@ Monitor::~Monitor() {
|
||||||
sws_freeContext(convert_context);
|
sws_freeContext(convert_context);
|
||||||
convert_context = nullptr;
|
convert_context = nullptr;
|
||||||
}
|
}
|
||||||
if (Amcrest_handle != nullptr) {
|
if (Amcrest_Manager != nullptr) {
|
||||||
curl_multi_remove_handle(curl_multi, Amcrest_handle);
|
delete Amcrest_Manager;
|
||||||
curl_easy_cleanup(Amcrest_handle);
|
}
|
||||||
|
if (purpose == CAPTURE) {
|
||||||
|
curl_global_cleanup(); //not sure about this location.
|
||||||
}
|
}
|
||||||
if (curl_multi != nullptr) curl_multi_cleanup(curl_multi);
|
|
||||||
curl_global_cleanup();
|
|
||||||
} // end Monitor::~Monitor()
|
} // end Monitor::~Monitor()
|
||||||
|
|
||||||
void Monitor::AddPrivacyBitmask() {
|
void Monitor::AddPrivacyBitmask() {
|
||||||
|
@ -1817,38 +1638,13 @@ void Monitor::UpdateFPS() {
|
||||||
//Thread where ONVIF polling, and other similar status polling can happen.
|
//Thread where ONVIF polling, and other similar status polling can happen.
|
||||||
//Since these can be blocking, run here to avoid intefering with other processing
|
//Since these can be blocking, run here to avoid intefering with other processing
|
||||||
bool Monitor::Poll() {
|
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();
|
||||||
|
|
||||||
if (ONVIF_Healthy) {
|
if (Event_Poller_Healthy) {
|
||||||
if(use_Amcrest_API) {
|
if(use_Amcrest_API) {
|
||||||
int open_handles;
|
Amcrest_Manager->WaitForMessage();
|
||||||
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 {
|
} else {
|
||||||
|
|
||||||
#ifdef WITH_GSOAP
|
#ifdef WITH_GSOAP
|
||||||
|
@ -1857,7 +1653,7 @@ bool Monitor::Poll() {
|
||||||
if (result != SOAP_OK) {
|
if (result != SOAP_OK) {
|
||||||
if (result != SOAP_EOF) { //Ignore the timeout error
|
if (result != SOAP_EOF) { //Ignore the timeout error
|
||||||
Error("Failed to get ONVIF messages! %s", soap_fault_string(soap));
|
Error("Failed to get ONVIF messages! %s", soap_fault_string(soap));
|
||||||
ONVIF_Healthy = FALSE;
|
Event_Poller_Healthy = FALSE;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Debug(1, "Got Good Response! %i", result);
|
Debug(1, "Got Good Response! %i", result);
|
||||||
|
@ -1874,16 +1670,16 @@ bool Monitor::Poll() {
|
||||||
if (strcmp(msg->Message.__any.elts->next->elts->atts->next->text, "true") == 0) {
|
if (strcmp(msg->Message.__any.elts->next->elts->atts->next->text, "true") == 0) {
|
||||||
//Event Start
|
//Event Start
|
||||||
Debug(1,"Triggered on ONVIF");
|
Debug(1,"Triggered on ONVIF");
|
||||||
if (!ONVIF_Trigger_State) {
|
if (!Poll_Trigger_State) {
|
||||||
Debug(1,"Triggered Event");
|
Debug(1,"Triggered Event");
|
||||||
ONVIF_Trigger_State = TRUE;
|
Poll_Trigger_State = TRUE;
|
||||||
std::this_thread::sleep_for (std::chrono::seconds(1)); //thread sleep
|
std::this_thread::sleep_for (std::chrono::seconds(1)); //thread sleep
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Debug(1, "Triggered off ONVIF");
|
Debug(1, "Triggered off ONVIF");
|
||||||
ONVIF_Trigger_State = FALSE;
|
Poll_Trigger_State = FALSE;
|
||||||
if (!ONVIF_Closes_Event) { //If we get a close event, then we know to expect them.
|
if (!Event_Poller_Closes_Event) { //If we get a close event, then we know to expect them.
|
||||||
ONVIF_Closes_Event = TRUE;
|
Event_Poller_Closes_Event = TRUE;
|
||||||
Debug(1,"Setting ClosesEvent");
|
Debug(1,"Setting ClosesEvent");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1894,11 +1690,9 @@ bool Monitor::Poll() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (janus_enabled) {
|
if (janus_enabled) {
|
||||||
if (janus_session.empty()) {
|
|
||||||
get_janus_session();
|
if (Janus_Manager->check_janus() == 0) {
|
||||||
}
|
Janus_Manager->add_to_janus();
|
||||||
if (check_janus() == 0) {
|
|
||||||
add_to_janus();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::this_thread::sleep_until(loop_start_time + std::chrono::seconds(5));
|
std::this_thread::sleep_until(loop_start_time + std::chrono::seconds(5));
|
||||||
|
@ -1953,8 +1747,8 @@ bool Monitor::Analyse() {
|
||||||
Event::StringSetMap noteSetMap;
|
Event::StringSetMap noteSetMap;
|
||||||
|
|
||||||
#ifdef WITH_GSOAP
|
#ifdef WITH_GSOAP
|
||||||
if (onvif_event_listener && ONVIF_Healthy) {
|
if (onvif_event_listener && Event_Poller_Healthy) {
|
||||||
if (ONVIF_Trigger_State) {
|
if (Poll_Trigger_State) {
|
||||||
score += 9;
|
score += 9;
|
||||||
Debug(1, "Triggered on ONVIF");
|
Debug(1, "Triggered on ONVIF");
|
||||||
Event::StringSet noteSet;
|
Event::StringSet noteSet;
|
||||||
|
@ -1962,10 +1756,10 @@ bool Monitor::Analyse() {
|
||||||
noteSetMap[MOTION_CAUSE] = noteSet;
|
noteSetMap[MOTION_CAUSE] = noteSet;
|
||||||
cause += "ONVIF";
|
cause += "ONVIF";
|
||||||
//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 (!Event_Poller_Closes_Event && state == ALARM)
|
||||||
ONVIF_Trigger_State = FALSE;
|
Poll_Trigger_State = FALSE;
|
||||||
} // end ONVIF_Trigger
|
} // end ONVIF_Trigger
|
||||||
} // end if (onvif_event_listener && ONVIF_Healthy)
|
} // end if (onvif_event_listener && Event_Poller_Healthy)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Specifically told to be on. Setting the score here is not enough to trigger the alarm. Must jump directly to ALARM
|
// Specifically told to be on. Setting the score here is not enough to trigger the alarm. Must jump directly to ALARM
|
||||||
|
@ -3287,8 +3081,7 @@ int Monitor::PrimeCapture() {
|
||||||
} // end if rtsp_server
|
} // end if rtsp_server
|
||||||
|
|
||||||
//Poller Thread
|
//Poller Thread
|
||||||
if (onvif_event_listener || janus_enabled) {
|
if (onvif_event_listener || janus_enabled || use_Amcrest_API) {
|
||||||
|
|
||||||
if (!Poller) {
|
if (!Poller) {
|
||||||
Poller = zm::make_unique<PollThread>(this);
|
Poller = zm::make_unique<PollThread>(this);
|
||||||
} else {
|
} else {
|
||||||
|
@ -3338,10 +3131,6 @@ int Monitor::Close() {
|
||||||
if (Poller) {
|
if (Poller) {
|
||||||
Poller->Stop();
|
Poller->Stop();
|
||||||
}
|
}
|
||||||
if (curl_multi != nullptr) {
|
|
||||||
curl_multi_cleanup(curl_multi);
|
|
||||||
curl_multi = nullptr;
|
|
||||||
}
|
|
||||||
#ifdef WITH_GSOAP
|
#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");
|
||||||
|
@ -3354,11 +3143,11 @@ int Monitor::Close() {
|
||||||
soap = nullptr;
|
soap = nullptr;
|
||||||
} //End ONVIF
|
} //End ONVIF
|
||||||
#endif
|
#endif
|
||||||
#if HAVE_LIBCURL //Janus Teardown
|
//Janus Teardown
|
||||||
if (janus_enabled && (purpose == CAPTURE)) {
|
if (janus_enabled && (purpose == CAPTURE)) {
|
||||||
remove_from_janus();
|
delete Janus_Manager;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
packetqueue.clear();
|
packetqueue.clear();
|
||||||
if (audio_fifo) {
|
if (audio_fifo) {
|
||||||
|
@ -3495,289 +3284,3 @@ 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() {
|
|
||||||
std::string response;
|
|
||||||
std::string endpoint;
|
|
||||||
if ((config.janus_path != nullptr) && (config.janus_path[0] != '\0')) {
|
|
||||||
endpoint = config.janus_path;
|
|
||||||
} else {
|
|
||||||
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::size_t pos;
|
|
||||||
std::size_t pos2;
|
|
||||||
CURLcode res;
|
|
||||||
|
|
||||||
curl = curl_easy_init();
|
|
||||||
if (!curl) {
|
|
||||||
Error("Failed to init 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);
|
|
||||||
|
|
||||||
endpoint += "/";
|
|
||||||
endpoint += janus_session;
|
|
||||||
|
|
||||||
//Assemble our actual request
|
|
||||||
postData = "{\"janus\" : \"message\", \"transaction\" : \"randomString\", \"body\" : {";
|
|
||||||
postData += "\"request\" : \"create\", \"admin_key\" : \"";
|
|
||||||
postData += config.janus_secret;
|
|
||||||
postData += "\", \"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);
|
|
||||||
if (janus_audio_enabled) postData += ", \"audio\" : true";
|
|
||||||
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) {
|
|
||||||
Error("Failed to curl_easy_perform adding rtsp stream");
|
|
||||||
curl_easy_cleanup(curl);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if ((response.find("error") != std::string::npos) && ((response.find("No such session") != std::string::npos) || (response.find("No such handle") != std::string::npos))) {
|
|
||||||
janus_session = "";
|
|
||||||
curl_easy_cleanup(curl);
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
//scan for missing session or handle id "No such session" "no such handle"
|
|
||||||
|
|
||||||
Debug(1,"Added stream to Janus: %s", response.c_str());
|
|
||||||
curl_easy_cleanup(curl);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Monitor::check_janus() {
|
|
||||||
std::string response;
|
|
||||||
std::string endpoint;
|
|
||||||
if ((config.janus_path != nullptr) && (config.janus_path[0] != '\0')) {
|
|
||||||
endpoint = config.janus_path;
|
|
||||||
} else {
|
|
||||||
endpoint = "127.0.0.1:8088/janus/";
|
|
||||||
}
|
|
||||||
std::string postData;
|
|
||||||
//std::size_t pos;
|
|
||||||
CURLcode res;
|
|
||||||
|
|
||||||
curl = curl_easy_init();
|
|
||||||
if(!curl) return -1;
|
|
||||||
|
|
||||||
endpoint += "/";
|
|
||||||
endpoint += janus_session;
|
|
||||||
|
|
||||||
//Assemble our actual request
|
|
||||||
postData = "{\"janus\" : \"message\", \"transaction\" : \"randomString\", \"body\" : {";
|
|
||||||
postData += "\"request\" : \"info\", \"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) { //may mean an error code thrown by Janus, because of a bad session
|
|
||||||
Warning("Attempted %s got %s", endpoint.c_str(), curl_easy_strerror(res));
|
|
||||||
curl_easy_cleanup(curl);
|
|
||||||
janus_session = "";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
curl_easy_cleanup(curl);
|
|
||||||
Debug(1, "Queried for stream status: %s", response.c_str());
|
|
||||||
if ((response.find("error") != std::string::npos) && ((response.find("No such session") != std::string::npos) || (response.find("No such handle") != std::string::npos))) {
|
|
||||||
Warning("Janus Session timed out");
|
|
||||||
janus_session = "";
|
|
||||||
return -2;
|
|
||||||
} else if (response.find("No such mountpoint") != std::string::npos) {
|
|
||||||
Warning("Mountpoint Missing");
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int Monitor::remove_from_janus() {
|
|
||||||
std::string response;
|
|
||||||
std::string endpoint;
|
|
||||||
if ((config.janus_path != nullptr) && (config.janus_path[0] != '\0')) {
|
|
||||||
endpoint = config.janus_path;
|
|
||||||
} else {
|
|
||||||
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;
|
|
||||||
|
|
||||||
endpoint += "/";
|
|
||||||
endpoint += janus_session;
|
|
||||||
|
|
||||||
//Assemble our actual request
|
|
||||||
postData = "{\"janus\" : \"message\", \"transaction\" : \"randomString\", \"body\" : {";
|
|
||||||
postData += "\"request\" : \"destroy\", \"admin_key\" : \"";
|
|
||||||
postData += config.janus_secret;
|
|
||||||
postData += "\", \"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) {
|
|
||||||
Warning("Libcurl attempted %s got %s", endpoint.c_str(), curl_easy_strerror(res));
|
|
||||||
curl_easy_cleanup(curl);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug(1, "Removed stream from Janus: %s", response.c_str());
|
|
||||||
curl_easy_cleanup(curl);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Monitor::get_janus_session() {
|
|
||||||
std::string response;
|
|
||||||
std::string endpoint;
|
|
||||||
if ((config.janus_path != nullptr) && (config.janus_path[0] != '\0')) {
|
|
||||||
endpoint = config.janus_path;
|
|
||||||
} else {
|
|
||||||
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) {
|
|
||||||
Warning("Libcurl attempted %s got %s", endpoint.c_str(), curl_easy_strerror(res));
|
|
||||||
curl_easy_cleanup(curl);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos = response.find("\"id\": ");
|
|
||||||
if (pos == std::string::npos)
|
|
||||||
{
|
|
||||||
curl_easy_cleanup(curl);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
janus_session = response.substr(pos + 6, 16);
|
|
||||||
|
|
||||||
response = "";
|
|
||||||
endpoint += "/";
|
|
||||||
endpoint += janus_session;
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
Warning("Libcurl attempted %s got %s", endpoint.c_str(), curl_easy_strerror(res));
|
|
||||||
curl_easy_cleanup(curl);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos = response.find("\"id\": ");
|
|
||||||
if (pos == std::string::npos)
|
|
||||||
{
|
|
||||||
curl_easy_cleanup(curl);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
janus_session += "/";
|
|
||||||
janus_session += response.substr(pos + 6, 16);
|
|
||||||
curl_easy_cleanup(curl);
|
|
||||||
return 1;
|
|
||||||
} //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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -93,7 +93,7 @@ public:
|
||||||
} Orientation;
|
} Orientation;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
DEINTERLACE_DISABLED = 0x00000000,
|
DEINTERLACE_DISABLED = 0x00000000,
|
||||||
DEINTERLACE_FOUR_FIELD_SOFT = 0x00001E04,
|
DEINTERLACE_FOUR_FIELD_SOFT = 0x00001E04,
|
||||||
DEINTERLACE_FOUR_FIELD_MEDIUM = 0x00001404,
|
DEINTERLACE_FOUR_FIELD_MEDIUM = 0x00001404,
|
||||||
DEINTERLACE_FOUR_FIELD_HARD = 0x00000A04,
|
DEINTERLACE_FOUR_FIELD_HARD = 0x00000A04,
|
||||||
|
@ -154,12 +154,12 @@ protected:
|
||||||
uint32_t last_frame_score; /* +60 */
|
uint32_t last_frame_score; /* +60 */
|
||||||
uint32_t audio_frequency; /* +64 */
|
uint32_t audio_frequency; /* +64 */
|
||||||
uint32_t audio_channels; /* +68 */
|
uint32_t audio_channels; /* +68 */
|
||||||
/*
|
/*
|
||||||
** This keeps 32bit time_t and 64bit time_t identical and compatible as long as time is before 2038.
|
** This keeps 32bit time_t and 64bit time_t identical and compatible as long as time is before 2038.
|
||||||
** Shared memory layout should be identical for both 32bit and 64bit and is multiples of 16.
|
** Shared memory layout should be identical for both 32bit and 64bit and is multiples of 16.
|
||||||
** Because startup_time is 64bit it may be aligned to a 64bit boundary. So it's offset SHOULD be a multiple
|
** Because startup_time is 64bit it may be aligned to a 64bit boundary. So it's offset SHOULD be a multiple
|
||||||
** of 8. Add or delete epadding's to achieve this.
|
** of 8. Add or delete epadding's to achieve this.
|
||||||
*/
|
*/
|
||||||
union { /* +72 */
|
union { /* +72 */
|
||||||
time_t startup_time; /* When the zmc process started. zmwatch uses this to see how long the process has been running without getting any images */
|
time_t startup_time; /* When the zmc process started. zmwatch uses this to see how long the process has been running without getting any images */
|
||||||
uint64_t extrapad1;
|
uint64_t extrapad1;
|
||||||
|
@ -256,7 +256,49 @@ protected:
|
||||||
bool hasAlarmed();
|
bool hasAlarmed();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AmcrestAPI {
|
||||||
protected:
|
protected:
|
||||||
|
Monitor *parent;
|
||||||
|
std::string amcrest_response;
|
||||||
|
CURLM *curl_multi = nullptr;
|
||||||
|
CURL *Amcrest_handle = nullptr;
|
||||||
|
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp);
|
||||||
|
|
||||||
|
public:
|
||||||
|
AmcrestAPI( Monitor *parent_);
|
||||||
|
~AmcrestAPI();
|
||||||
|
int API_Connect();
|
||||||
|
void WaitForMessage();
|
||||||
|
bool Amcrest_Alarmed;
|
||||||
|
int start_Amcrest();
|
||||||
|
};
|
||||||
|
|
||||||
|
class JanusManager {
|
||||||
|
protected:
|
||||||
|
Monitor *parent;
|
||||||
|
CURL *curl = nullptr;
|
||||||
|
//helper class for CURL
|
||||||
|
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp);
|
||||||
|
bool Janus_Healthy;
|
||||||
|
std::string janus_session;
|
||||||
|
std::string janus_handle;
|
||||||
|
std::string janus_endpoint;
|
||||||
|
std::string stream_key;
|
||||||
|
std::string rtsp_username;
|
||||||
|
std::string rtsp_password;
|
||||||
|
std::string rtsp_path;
|
||||||
|
|
||||||
|
public:
|
||||||
|
JanusManager(Monitor *parent_);
|
||||||
|
~JanusManager();
|
||||||
|
int add_to_janus();
|
||||||
|
int check_janus();
|
||||||
|
int remove_from_janus();
|
||||||
|
int get_janus_session();
|
||||||
|
int get_janus_handle();
|
||||||
|
int get_janus_plugin();
|
||||||
|
std::string get_stream_key();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// These are read from the DB and thereafter remain unchanged
|
// These are read from the DB and thereafter remain unchanged
|
||||||
|
@ -285,7 +327,6 @@ 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;
|
bool use_Amcrest_API;
|
||||||
|
|
||||||
|
@ -438,9 +479,12 @@ protected:
|
||||||
std::string diag_path_delta;
|
std::string diag_path_delta;
|
||||||
|
|
||||||
//ONVIF
|
//ONVIF
|
||||||
bool ONVIF_Trigger_State; //Re-using some variables for Amcrest API support
|
bool Poll_Trigger_State;
|
||||||
bool ONVIF_Healthy;
|
bool Event_Poller_Healthy;
|
||||||
bool ONVIF_Closes_Event;
|
bool Event_Poller_Closes_Event;
|
||||||
|
|
||||||
|
JanusManager *Janus_Manager;
|
||||||
|
AmcrestAPI *Amcrest_Manager;
|
||||||
|
|
||||||
#ifdef WITH_GSOAP
|
#ifdef WITH_GSOAP
|
||||||
struct soap *soap = nullptr;
|
struct soap *soap = nullptr;
|
||||||
|
@ -452,17 +496,6 @@ protected:
|
||||||
void set_credentials(struct soap *soap);
|
void set_credentials(struct soap *soap);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//curl stuff
|
|
||||||
CURL *curl = nullptr;
|
|
||||||
CURLM *curl_multi = nullptr;
|
|
||||||
CURL *Amcrest_handle = nullptr;
|
|
||||||
int start_Amcrest();
|
|
||||||
//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();
|
|
||||||
int get_janus_session();
|
|
||||||
std::string janus_session;
|
|
||||||
|
|
||||||
// Used in check signal
|
// Used in check signal
|
||||||
uint8_t red_val;
|
uint8_t red_val;
|
||||||
|
@ -530,11 +563,9 @@ public:
|
||||||
return onvif_event_listener;
|
return onvif_event_listener;
|
||||||
}
|
}
|
||||||
int check_janus(); //returns 1 for healthy, 0 for success but missing stream, negative for error.
|
int check_janus(); //returns 1 for healthy, 0 for success but missing stream, negative for error.
|
||||||
#ifdef WITH_GSOAP
|
bool EventPollerHealthy() {
|
||||||
bool OnvifHealthy() {
|
return Event_Poller_Healthy;
|
||||||
return ONVIF_Healthy;
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
inline const char *EventPrefix() const { return event_prefix.c_str(); }
|
inline const char *EventPrefix() const { return event_prefix.c_str(); }
|
||||||
inline bool Ready() const {
|
inline bool Ready() const {
|
||||||
if ( image_count >= ready_count ) {
|
if ( image_count >= ready_count ) {
|
||||||
|
@ -583,7 +614,7 @@ public:
|
||||||
void SetVideoWriterStartTime(SystemTimePoint t) {
|
void SetVideoWriterStartTime(SystemTimePoint t) {
|
||||||
video_store_data->recording = zm::chrono::duration_cast<timeval>(t.time_since_epoch());
|
video_store_data->recording = zm::chrono::duration_cast<timeval>(t.time_since_epoch());
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int GetPreEventCount() const { return pre_event_count; };
|
unsigned int GetPreEventCount() const { return pre_event_count; };
|
||||||
int32_t GetImageBufferCount() const { return image_buffer_count; };
|
int32_t GetImageBufferCount() const { return image_buffer_count; };
|
||||||
State GetState() const { return (State)shared_data->state; }
|
State GetState() const { return (State)shared_data->state; }
|
||||||
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
//
|
||||||
|
// ZoneMinder Monitor::AmcrestAPI Class Implementation, $Date$, $Revision$
|
||||||
|
// Copyright (C) 2022 Jonathan Bennett
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License
|
||||||
|
// as published by the Free Software Foundation; either version 2
|
||||||
|
// of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "zm_monitor.h"
|
||||||
|
|
||||||
|
|
||||||
|
Monitor::AmcrestAPI::AmcrestAPI(Monitor *parent_) {
|
||||||
|
parent = parent_;
|
||||||
|
curl_multi = curl_multi_init();
|
||||||
|
start_Amcrest();
|
||||||
|
}
|
||||||
|
|
||||||
|
Monitor::AmcrestAPI::~AmcrestAPI() {
|
||||||
|
if (Amcrest_handle != nullptr) { //potentially clean up the old handle
|
||||||
|
curl_multi_remove_handle(curl_multi, Amcrest_handle);
|
||||||
|
curl_easy_cleanup(Amcrest_handle);
|
||||||
|
}
|
||||||
|
if (curl_multi != nullptr) curl_multi_cleanup(curl_multi);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Monitor::AmcrestAPI::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 = parent->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, parent->onvif_username.c_str());
|
||||||
|
curl_easy_setopt(Amcrest_handle, CURLOPT_PASSWORD, parent->onvif_password.c_str());
|
||||||
|
curl_easy_setopt(Amcrest_handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
|
||||||
|
curl_error = curl_multi_add_handle(curl_multi, Amcrest_handle);
|
||||||
|
if (curl_error != CURLM_OK) {
|
||||||
|
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)) {
|
||||||
|
parent->Event_Poller_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Monitor::AmcrestAPI::WaitForMessage() {
|
||||||
|
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 (!parent->Poll_Trigger_State) {
|
||||||
|
Debug(1,"Triggered Event");
|
||||||
|
parent->Poll_Trigger_State = TRUE;
|
||||||
|
}
|
||||||
|
} else if (amcrest_response.find("action=Stop") != std::string::npos){
|
||||||
|
Debug(1, "Triggered off ONVIF");
|
||||||
|
parent->Poll_Trigger_State = FALSE;
|
||||||
|
if (!parent->Event_Poller_Closes_Event) { //If we get a close event, then we know to expect them.
|
||||||
|
parent->Event_Poller_Closes_Event = TRUE;
|
||||||
|
Debug(1,"Setting ClosesEvent");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
amcrest_response.clear(); //We've dealt with the message, need to clear the queue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Monitor::AmcrestAPI::WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
|
||||||
|
{
|
||||||
|
((std::string*)userp)->append((char*)contents, size * nmemb);
|
||||||
|
return size * nmemb;
|
||||||
|
}
|
|
@ -0,0 +1,316 @@
|
||||||
|
//
|
||||||
|
// ZoneMinder Monitor::JanusManager Class Implementation, $Date$, $Revision$
|
||||||
|
// Copyright (C) 2022 Jonathan Bennett
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License
|
||||||
|
// as published by the Free Software Foundation; either version 2
|
||||||
|
// of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "zm_monitor.h"
|
||||||
|
|
||||||
|
|
||||||
|
Monitor::JanusManager::JanusManager(Monitor *parent_) { //constructor takes care of init and calls add_to
|
||||||
|
std::string response;
|
||||||
|
std::size_t pos;
|
||||||
|
parent = parent_;
|
||||||
|
if ((config.janus_path != nullptr) && (config.janus_path[0] != '\0')) {
|
||||||
|
janus_endpoint = config.janus_path; //TODO: strip trailing /
|
||||||
|
} else {
|
||||||
|
janus_endpoint = "127.0.0.1:8088/janus";
|
||||||
|
}
|
||||||
|
if (janus_endpoint.back() == '/') janus_endpoint.pop_back(); //remove the trailing slash if present
|
||||||
|
std::size_t pos2 = parent->path.find("@", pos);
|
||||||
|
if (pos2 != std::string::npos) { //If we find an @ symbol, we have a username/password. Otherwise, passwordless login.
|
||||||
|
|
||||||
|
std::size_t pos = parent->path.find(":", 7); //Search for the colon, but only after the RTSP:// text.
|
||||||
|
if (pos == std::string::npos) throw std::runtime_error("Cannot Parse URL for Janus."); //Looks like an invalid url
|
||||||
|
rtsp_username = parent->path.substr(7, pos-7);
|
||||||
|
|
||||||
|
rtsp_password = parent->path.substr(pos+1, pos2 - pos - 1);
|
||||||
|
rtsp_path = "RTSP://";
|
||||||
|
rtsp_path += parent->path.substr(pos2 + 1);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
rtsp_username = "";
|
||||||
|
rtsp_password = "";
|
||||||
|
rtsp_path = parent->path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Monitor::JanusManager::~JanusManager() {
|
||||||
|
if (janus_session.empty()) get_janus_session();
|
||||||
|
if (janus_handle.empty()) get_janus_handle();
|
||||||
|
|
||||||
|
std::string response;
|
||||||
|
std::string endpoint;
|
||||||
|
|
||||||
|
std::string postData = "{\"janus\" : \"create\", \"transaction\" : \"randomString\"}";
|
||||||
|
//std::size_t pos;
|
||||||
|
CURLcode res;
|
||||||
|
|
||||||
|
curl = curl_easy_init();
|
||||||
|
if(!curl) return;
|
||||||
|
|
||||||
|
endpoint = janus_endpoint;
|
||||||
|
endpoint += "/";
|
||||||
|
endpoint += janus_session;
|
||||||
|
endpoint += "/";
|
||||||
|
endpoint += janus_handle;
|
||||||
|
|
||||||
|
//Assemble our actual request
|
||||||
|
postData = "{\"janus\" : \"message\", \"transaction\" : \"randomString\", \"body\" : {";
|
||||||
|
postData += "\"request\" : \"destroy\", \"admin_key\" : \"";
|
||||||
|
postData += config.janus_secret;
|
||||||
|
postData += "\", \"id\" : ";
|
||||||
|
postData += std::to_string(parent->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) {
|
||||||
|
Warning("Libcurl attempted %s got %s", endpoint.c_str(), curl_easy_strerror(res));
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug(1, "Removed stream from Janus: %s", response.c_str());
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int Monitor::JanusManager::check_janus() {
|
||||||
|
if (janus_session.empty()) get_janus_session();
|
||||||
|
if (janus_handle.empty()) get_janus_handle();
|
||||||
|
|
||||||
|
std::string response;
|
||||||
|
std::string endpoint = janus_endpoint;
|
||||||
|
std::string postData;
|
||||||
|
//std::size_t pos;
|
||||||
|
CURLcode res;
|
||||||
|
|
||||||
|
curl = curl_easy_init();
|
||||||
|
if(!curl) return -1;
|
||||||
|
|
||||||
|
endpoint += "/";
|
||||||
|
endpoint += janus_session;
|
||||||
|
endpoint += "/";
|
||||||
|
endpoint += janus_handle;
|
||||||
|
|
||||||
|
//Assemble our actual request
|
||||||
|
postData = "{\"janus\" : \"message\", \"transaction\" : \"randomString\", \"body\" : {";
|
||||||
|
postData += "\"request\" : \"info\", \"id\" : ";
|
||||||
|
postData += std::to_string(parent->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) { //may mean an error code thrown by Janus, because of a bad session
|
||||||
|
Warning("Attempted %s got %s", endpoint.c_str(), curl_easy_strerror(res));
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
janus_session = "";
|
||||||
|
janus_handle = "";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
Debug(1, "Queried for stream status: %s", response.c_str());
|
||||||
|
if (response.find("\"janus\": \"error\"") != std::string::npos) {
|
||||||
|
if (response.find("No such session") != std::string::npos) {
|
||||||
|
Warning("Janus Session timed out");
|
||||||
|
janus_session = "";
|
||||||
|
return -2;
|
||||||
|
} else if (response.find("No such handle") != std::string::npos) {
|
||||||
|
Warning("Janus Handle timed out");
|
||||||
|
janus_handle = "";
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
} else if (response.find("No such mountpoint") != std::string::npos) {
|
||||||
|
Warning("Mountpoint Missing");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Monitor::JanusManager::add_to_janus() {
|
||||||
|
if (janus_session.empty()) get_janus_session();
|
||||||
|
if (janus_handle.empty()) get_janus_handle();
|
||||||
|
|
||||||
|
std::string response;
|
||||||
|
std::string endpoint = janus_endpoint;
|
||||||
|
|
||||||
|
CURLcode res;
|
||||||
|
|
||||||
|
curl = curl_easy_init();
|
||||||
|
if (!curl) {
|
||||||
|
Error("Failed to init curl");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint += "/";
|
||||||
|
endpoint += janus_session;
|
||||||
|
endpoint += "/";
|
||||||
|
endpoint += janus_handle;
|
||||||
|
|
||||||
|
//Assemble our actual request
|
||||||
|
std::string postData = "{\"janus\" : \"message\", \"transaction\" : \"randomString\", \"body\" : {";
|
||||||
|
postData += "\"request\" : \"create\", \"admin_key\" : \"";
|
||||||
|
postData += config.janus_secret;
|
||||||
|
postData += "\", \"type\" : \"rtsp\", ";
|
||||||
|
postData += "\"url\" : \"";
|
||||||
|
postData += rtsp_path;
|
||||||
|
if (rtsp_username != "") {
|
||||||
|
postData += "\", \"rtsp_user\" : \"";
|
||||||
|
postData += rtsp_username;
|
||||||
|
postData += "\", \"rtsp_pwd\" : \"";
|
||||||
|
postData += rtsp_password;
|
||||||
|
}
|
||||||
|
postData += "\", \"id\" : ";
|
||||||
|
postData += std::to_string(parent->id);
|
||||||
|
if (parent->janus_audio_enabled) postData += ", \"audio\" : true";
|
||||||
|
postData += ", \"video\" : true}}";
|
||||||
|
Warning("Sending %s to %s", postData.c_str(), endpoint.c_str());
|
||||||
|
|
||||||
|
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) {
|
||||||
|
Error("Failed to curl_easy_perform adding rtsp stream");
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (response.find("\"janus\": \"error\"") != std::string::npos) {
|
||||||
|
if (response.find("No such session") != std::string::npos) {
|
||||||
|
Warning("Janus Session timed out");
|
||||||
|
janus_session = "";
|
||||||
|
return -2;
|
||||||
|
} else if (response.find("No such handle") != std::string::npos) {
|
||||||
|
Warning("Janus Handle timed out");
|
||||||
|
janus_handle = "";
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//scan for missing session or handle id "No such session" "no such handle"
|
||||||
|
|
||||||
|
Debug(1,"Added stream to Janus: %s", response.c_str());
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t Monitor::JanusManager::WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
|
||||||
|
{
|
||||||
|
((std::string*)userp)->append((char*)contents, size * nmemb);
|
||||||
|
return size * nmemb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
void Monitor::JanusManager::generateKey()
|
||||||
|
{
|
||||||
|
const std::string CHARACTERS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||||
|
|
||||||
|
std::random_device random_device;
|
||||||
|
std::mt19937 generator(random_device());
|
||||||
|
std::uniform_int_distribution<> distribution(0, CHARACTERS.size() - 1);
|
||||||
|
|
||||||
|
std::string random_string;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < 16; ++i)
|
||||||
|
{
|
||||||
|
random_string += CHARACTERS[distribution(generator)];
|
||||||
|
}
|
||||||
|
|
||||||
|
stream_key = random_string;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
int Monitor::JanusManager::get_janus_session() {
|
||||||
|
janus_session = "";
|
||||||
|
std::string endpoint = janus_endpoint;
|
||||||
|
|
||||||
|
std::string response;
|
||||||
|
|
||||||
|
std::string postData = "{\"janus\" : \"create\", \"transaction\" : \"randomString\"}";
|
||||||
|
std::size_t pos;
|
||||||
|
CURLcode res;
|
||||||
|
curl = curl_easy_init();
|
||||||
|
if(!curl) return -1;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
Warning("Libcurl attempted %s got %s", endpoint.c_str(), curl_easy_strerror(res));
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = response.find("\"id\": ");
|
||||||
|
if (pos == std::string::npos)
|
||||||
|
{
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
janus_session = response.substr(pos + 6, 16);
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
} //get_janus_session
|
||||||
|
|
||||||
|
int Monitor::JanusManager::get_janus_handle() {
|
||||||
|
std::string response = "";
|
||||||
|
std::string endpoint = janus_endpoint;
|
||||||
|
std::size_t pos;
|
||||||
|
|
||||||
|
CURLcode res;
|
||||||
|
curl = curl_easy_init();
|
||||||
|
if(!curl) return -1;
|
||||||
|
|
||||||
|
endpoint += "/";
|
||||||
|
endpoint += janus_session;
|
||||||
|
std::string 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)
|
||||||
|
{
|
||||||
|
Warning("Libcurl attempted %s got %s", endpoint.c_str(), curl_easy_strerror(res));
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = response.find("\"id\": ");
|
||||||
|
if (pos == std::string::npos)
|
||||||
|
{
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
janus_handle = response.substr(pos + 6, 16);
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
return 1;
|
||||||
|
} //get_janus_handle
|
|
@ -0,0 +1,198 @@
|
||||||
|
//
|
||||||
|
// ZoneMinder Monitor Class Implementation, $Date$, $Revision$
|
||||||
|
// Copyright (C) 2001-2008 Philip Coombes
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License
|
||||||
|
// as published by the Free Software Foundation; either version 2
|
||||||
|
// of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "zm_monitor.h"
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#if ZM_MEM_MAPPED
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#else // ZM_MEM_MAPPED
|
||||||
|
#include <sys/ipc.h>
|
||||||
|
#include <sys/shm.h>
|
||||||
|
#endif // ZM_MEM_MAPPED
|
||||||
|
|
||||||
|
Monitor::MonitorLink::MonitorLink(unsigned int p_id, const char *p_name) :
|
||||||
|
id(p_id),
|
||||||
|
shared_data(nullptr),
|
||||||
|
trigger_data(nullptr),
|
||||||
|
video_store_data(nullptr)
|
||||||
|
{
|
||||||
|
strncpy(name, p_name, sizeof(name)-1);
|
||||||
|
|
||||||
|
#if ZM_MEM_MAPPED
|
||||||
|
map_fd = -1;
|
||||||
|
mem_file = stringtf("%s/zm.mmap.%u", staticConfig.PATH_MAP.c_str(), id);
|
||||||
|
#else // ZM_MEM_MAPPED
|
||||||
|
shm_id = 0;
|
||||||
|
#endif // ZM_MEM_MAPPED
|
||||||
|
mem_size = 0;
|
||||||
|
mem_ptr = nullptr;
|
||||||
|
|
||||||
|
last_event_id = 0;
|
||||||
|
last_state = IDLE;
|
||||||
|
|
||||||
|
last_connect_time = 0;
|
||||||
|
connected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Monitor::MonitorLink::~MonitorLink() {
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Monitor::MonitorLink::connect() {
|
||||||
|
SystemTimePoint now = std::chrono::system_clock::now();
|
||||||
|
if (!last_connect_time || (now - std::chrono::system_clock::from_time_t(last_connect_time)) > Seconds(60)) {
|
||||||
|
last_connect_time = std::chrono::system_clock::to_time_t(now);
|
||||||
|
|
||||||
|
mem_size = sizeof(SharedData) + sizeof(TriggerData);
|
||||||
|
|
||||||
|
Debug(1, "link.mem.size=%jd", static_cast<intmax_t>(mem_size));
|
||||||
|
#if ZM_MEM_MAPPED
|
||||||
|
map_fd = open(mem_file.c_str(), O_RDWR, (mode_t)0600);
|
||||||
|
if (map_fd < 0) {
|
||||||
|
Debug(3, "Can't open linked memory map file %s: %s", mem_file.c_str(), strerror(errno));
|
||||||
|
disconnect();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
while (map_fd <= 2) {
|
||||||
|
int new_map_fd = dup(map_fd);
|
||||||
|
Warning("Got one of the stdio fds for our mmap handle. map_fd was %d, new one is %d", map_fd, new_map_fd);
|
||||||
|
close(map_fd);
|
||||||
|
map_fd = new_map_fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct stat map_stat;
|
||||||
|
if (fstat(map_fd, &map_stat) < 0) {
|
||||||
|
Error("Can't stat linked memory map file %s: %s", mem_file.c_str(), strerror(errno));
|
||||||
|
disconnect();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (map_stat.st_size == 0) {
|
||||||
|
Error("Linked memory map file %s is empty: %s", mem_file.c_str(), strerror(errno));
|
||||||
|
disconnect();
|
||||||
|
return false;
|
||||||
|
} else if (map_stat.st_size < mem_size) {
|
||||||
|
Error("Got unexpected memory map file size %ld, expected %jd", map_stat.st_size, static_cast<intmax_t>(mem_size));
|
||||||
|
disconnect();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mem_ptr = (unsigned char *)mmap(nullptr, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0);
|
||||||
|
if (mem_ptr == MAP_FAILED) {
|
||||||
|
Error("Can't map file %s (%jd bytes) to memory: %s", mem_file.c_str(), static_cast<intmax_t>(mem_size), strerror(errno));
|
||||||
|
disconnect();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#else // ZM_MEM_MAPPED
|
||||||
|
shm_id = shmget((config.shm_key&0xffff0000)|id, mem_size, 0700);
|
||||||
|
if (shm_id < 0) {
|
||||||
|
Debug(3, "Can't shmget link memory: %s", strerror(errno));
|
||||||
|
connected = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mem_ptr = (unsigned char *)shmat(shm_id, 0, 0);
|
||||||
|
if ((int)mem_ptr == -1) {
|
||||||
|
Debug(3, "Can't shmat link memory: %s", strerror(errno));
|
||||||
|
connected = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif // ZM_MEM_MAPPED
|
||||||
|
|
||||||
|
shared_data = (SharedData *)mem_ptr;
|
||||||
|
trigger_data = (TriggerData *)((char *)shared_data + sizeof(SharedData));
|
||||||
|
|
||||||
|
if (!shared_data->valid) {
|
||||||
|
Debug(3, "Linked memory not initialised by capture daemon");
|
||||||
|
disconnect();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_state = shared_data->state;
|
||||||
|
last_event_id = shared_data->last_event_id;
|
||||||
|
connected = true;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} // end bool Monitor::MonitorLink::connect()
|
||||||
|
|
||||||
|
bool Monitor::MonitorLink::disconnect() {
|
||||||
|
if (connected) {
|
||||||
|
connected = false;
|
||||||
|
|
||||||
|
#if ZM_MEM_MAPPED
|
||||||
|
if (mem_ptr > (void *)0) {
|
||||||
|
msync(mem_ptr, mem_size, MS_ASYNC);
|
||||||
|
munmap(mem_ptr, mem_size);
|
||||||
|
}
|
||||||
|
if (map_fd >= 0)
|
||||||
|
close(map_fd);
|
||||||
|
|
||||||
|
map_fd = -1;
|
||||||
|
#else // ZM_MEM_MAPPED
|
||||||
|
struct shmid_ds shm_data;
|
||||||
|
if (shmctl(shm_id, IPC_STAT, &shm_data) < 0) {
|
||||||
|
Debug(3, "Can't shmctl: %s", strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
shm_id = 0;
|
||||||
|
|
||||||
|
if (shm_data.shm_nattch <= 1) {
|
||||||
|
if (shmctl(shm_id, IPC_RMID, 0) < 0) {
|
||||||
|
Debug(3, "Can't shmctl: %s", strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shmdt(mem_ptr) < 0) {
|
||||||
|
Debug(3, "Can't shmdt: %s", strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif // ZM_MEM_MAPPED
|
||||||
|
mem_size = 0;
|
||||||
|
mem_ptr = nullptr;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Monitor::MonitorLink::isAlarmed() {
|
||||||
|
if (!connected) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return( shared_data->state == ALARM );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Monitor::MonitorLink::inAlarm() {
|
||||||
|
if (!connected) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return( shared_data->state == ALARM || shared_data->state == ALERT );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Monitor::MonitorLink::hasAlarmed() {
|
||||||
|
if (shared_data->state == ALARM) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
last_event_id = shared_data->last_event_id;
|
||||||
|
return false;
|
||||||
|
}
|
Loading…
Reference in New Issue