From 52e48c02b61bcb9fb2fedc79b5d8b546d74d2b2c Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Fri, 21 Jan 2022 22:23:41 -0600 Subject: [PATCH 1/6] Add janus_path and janus_secret, allowing for more secure and flexible Janus installs --- .../lib/ZoneMinder/ConfigData.pm.in | 22 ++++++++++++++++ src/zm_monitor.cpp | 26 +++++++++++++++---- web/includes/functions.php | 2 +- web/js/MonitorStream.js | 4 ++- web/skins/classic/views/cycle.php | 1 + web/skins/classic/views/js/cycle.js | 9 ++++++- web/skins/classic/views/montage.php | 1 + web/skins/classic/views/watch.php | 1 + 8 files changed, 58 insertions(+), 8 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in index 48d494614..9b8a4e61e 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in @@ -370,6 +370,28 @@ our @options = ( type => $types{boolean}, category => 'system', }, + { + name => 'ZM_JANUS_SECRET', + default => '', + description => 'Password for Janus streaming administration.', + help => q`This value should be set to a secure password, + and match the admin_key value in janus.plugin.streaming.config. + `, + type => $types{string}, + category => 'system', + }, + { + name => 'ZM_JANUS_PATH', + default => '', + description => 'URL for Janus HTTP/S port', + help => q`Janus requires HTTP/S communication to administer + and initiate h.264 streams. If left blank, this will default to + the ZM hostname, port 8088/janus. This setting is particularly + useful for putting janus behind a reverse proxy. + `, + type => $types{string}, + category => 'system', + }, { name => 'ZM_ENABLE_CSRF_MAGIC', default => 'yes', diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 2179197e8..9a1b747b6 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -3383,7 +3383,12 @@ size_t Monitor::WriteCallback(void *contents, size_t size, size_t nmemb, void *u 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 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; @@ -3425,6 +3430,7 @@ int Monitor::add_to_janus() { if (pos == std::string::npos) return -1; janus_id = response.substr(pos + 6, 16); response = ""; + endpoint += "/"; endpoint += janus_id; postData = "{\"janus\" : \"attach\", \"plugin\" : \"janus.plugin.streaming\", \"transaction\" : \"randomString\"}"; curl_easy_setopt(curl, CURLOPT_URL,endpoint.c_str()); @@ -3445,8 +3451,10 @@ int Monitor::add_to_janus() { //Assemble our actual request postData = "{\"janus\" : \"message\", \"transaction\" : \"randomString\", \"body\" : {"; - postData += "\"request\" : \"create\", \"admin_key\" : \"supersecret\", \"type\" : \"rtsp\", "; - postData += "\"url\" : \""; + postData += "\"request\" : \"create\", \"admin_key\" : \""; + postData += config.janus_secret; + postData += "\", \"type\" : \"rtsp\", "; + postData += "\"url\" : \""; postData += rtsp_path; postData += "\", \"rtsp_user\" : \""; postData += rtsp_username; @@ -3474,7 +3482,12 @@ int Monitor::add_to_janus() { 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 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; @@ -3495,6 +3508,7 @@ int Monitor::remove_from_janus() { if (pos == std::string::npos) return -1; std::string janus_id = response.substr(pos + 6, 16); response = ""; + endpoint += "/"; endpoint += janus_id; postData = "{\"janus\" : \"attach\", \"plugin\" : \"janus.plugin.streaming\", \"transaction\" : \"randomString\"}"; curl_easy_setopt(curl, CURLOPT_URL,endpoint.c_str()); @@ -3512,7 +3526,9 @@ int Monitor::remove_from_janus() { //Assemble our actual request postData = "{\"janus\" : \"message\", \"transaction\" : \"randomString\", \"body\" : {"; - postData += "\"request\" : \"destroy\", \"admin_key\" : \"supersecret\", \"id\" : "; + postData += "\"request\" : \"destroy\", \"admin_key\" : \""; + postData += config.janus_secret; + postData += "\", \"id\" : "; postData += std::to_string(id); postData += "}}"; diff --git a/web/includes/functions.php b/web/includes/functions.php index f9b8abf72..e346ceb34 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -2095,7 +2095,7 @@ function getStreamHTML($monitor, $options = array()) { ) ); return getVideoStreamHTML( 'liveStream'.$monitor->Id(), $streamSrc, $options['width'], $options['height'], ZM_MPEG_LIVE_FORMAT, $monitor->Name() ); } else if ( $monitor->JanusEnabled() ) { - return ''; + return ''; } else if ( $options['mode'] == 'stream' and canStream() ) { $options['mode'] = 'jpeg'; $streamSrc = $monitor->getStreamSrc($options); diff --git a/web/js/MonitorStream.js b/web/js/MonitorStream.js index 17f43c4b9..373902e1f 100644 --- a/web/js/MonitorStream.js +++ b/web/js/MonitorStream.js @@ -92,7 +92,9 @@ function MonitorStream(monitorData) { if (this.janusEnabled) { var id = parseInt(this.id); var server; - if (window.location.protocol=='https:') { + if (ZM_JANUS_PATH) { + server = ZM_JANUS_PATH; + } else if (window.location.protocol=='https:') { // Assume reverse proxy setup for now server = "https://" + window.location.hostname + "/janus"; } else { diff --git a/web/skins/classic/views/cycle.php b/web/skins/classic/views/cycle.php index ee9e03d4f..50bb42045 100644 --- a/web/skins/classic/views/cycle.php +++ b/web/skins/classic/views/cycle.php @@ -192,6 +192,7 @@ xhtmlHeaders(__FILE__, translate('CycleWatch')); + diff --git a/web/skins/classic/views/js/cycle.js b/web/skins/classic/views/js/cycle.js index 670671faa..9e128d0ab 100644 --- a/web/skins/classic/views/js/cycle.js +++ b/web/skins/classic/views/js/cycle.js @@ -51,7 +51,14 @@ function initCycle() { if ( scale == '0' || scale == 'auto' ) changeScale(); if (monitorData[monIdx].janusEnabled) { - server = "http://" + window.location.hostname + ":8088/janus"; + if (ZM_JANUS_PATH) { + server = ZM_JANUS_PATH; + } else if (window.location.protocol=='https:') { + // Assume reverse proxy setup for now + server = "https://" + window.location.hostname + "/janus"; + } else { + server = "http://" + window.location.hostname + ":8088/janus"; + } opaqueId = "streamingtest-"+Janus.randomString(12); Janus.init({debug: "all", callback: function() { janus = new Janus({ diff --git a/web/skins/classic/views/montage.php b/web/skins/classic/views/montage.php index a96e8a88e..c59ce9f66 100644 --- a/web/skins/classic/views/montage.php +++ b/web/skins/classic/views/montage.php @@ -318,6 +318,7 @@ foreach (array_reverse($zones) as $zone) { + diff --git a/web/skins/classic/views/watch.php b/web/skins/classic/views/watch.php index 340ee8e33..46ee11f90 100644 --- a/web/skins/classic/views/watch.php +++ b/web/skins/classic/views/watch.php @@ -396,6 +396,7 @@ if ( ZM_WEB_SOUND_ON_ALARM ) { ?> + From 2e9bda1af190a3f18ae06eb7bbe10b8f8f08f49c Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Fri, 21 Jan 2022 23:21:41 -0600 Subject: [PATCH 2/6] Add firefox specific workaround for Janus streaming --- web/js/MonitorStream.js | 3 +++ web/skins/classic/views/js/cycle.js | 3 +++ 2 files changed, 6 insertions(+) diff --git a/web/js/MonitorStream.js b/web/js/MonitorStream.js index 373902e1f..b5f9d0b6a 100644 --- a/web/js/MonitorStream.js +++ b/web/js/MonitorStream.js @@ -521,6 +521,9 @@ async function attachVideo(id) { if (jsep !== undefined && jsep !== null) { Janus.debug("Handling SDP as well..."); Janus.debug(jsep); + if ((navigator.userAgent.toLowerCase().indexOf('firefox') > -1) && (jsep["sdp"].includes("420029"))) { //because firefox devs are stubborn + jsep["sdp"] = jsep["sdp"].replace("420029", "42e01f"); + } // Offer from the plugin, let's answer streaming[id].createAnswer({ jsep: jsep, diff --git a/web/skins/classic/views/js/cycle.js b/web/skins/classic/views/js/cycle.js index 9e128d0ab..7e7bd0147 100644 --- a/web/skins/classic/views/js/cycle.js +++ b/web/skins/classic/views/js/cycle.js @@ -91,6 +91,9 @@ function initCycle() { if (jsep !== undefined && jsep !== null) { Janus.debug("Handling SDP as well..."); Janus.debug(jsep); + if ((navigator.userAgent.toLowerCase().indexOf('firefox') > -1) && (jsep["sdp"].includes("420029"))) { //because firefox devs are stubborn + jsep["sdp"] = jsep["sdp"].replace("420029", "42e01f"); + } // Offer from the plugin, let's answer streaming2.createAnswer({ jsep: jsep, From 07c5b23aa6668e33d8b21204953e408994d9a9db Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 22 Jan 2022 11:53:44 -0600 Subject: [PATCH 3/6] Adds Janus streaming checks to polling thread --- src/zm_monitor.cpp | 91 ++++++++++++++++++++++++++++++++++++++++------ src/zm_monitor.h | 1 + 2 files changed, 80 insertions(+), 12 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 9a1b747b6..cc426586c 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -52,6 +52,7 @@ #include #include #include +#include #if ZM_MEM_MAPPED #include @@ -1123,9 +1124,7 @@ bool Monitor::connect() { #if HAVE_LIBCURL //janus setup. Depends on libcurl. if (janus_enabled && (path.find("rtsp://") != std::string::npos)) { if (add_to_janus() != 0) { - if (add_to_janus() != 0) { //The initial attempt may fail. This is a temporary workaround. - Warning("Failed to add monitor stream to Janus!"); - } + Warning("Failed to add monitor stream to Janus!"); //The first attempt may fail. Will be reattempted in the Poller thread } } #else @@ -1797,6 +1796,8 @@ void Monitor::UpdateFPS() { //Thread where ONVIF polling, and other similar status polling can happen. //Since these can be blocking, run here to avoid intefering with other processing bool Monitor::Poll() { + //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(); #ifdef WITH_GSOAP if (ONVIF_Healthy) { @@ -1838,10 +1839,14 @@ bool Monitor::Poll() { } } } - } else { - std::this_thread::sleep_for (std::chrono::seconds(1)); //thread sleep to avoid the busy loop. } #endif + if (janus_enabled) { + if (check_janus() != 1) { + add_to_janus(); + } + } + std::this_thread::sleep_until(loop_start_time + std::chrono::seconds(5)); return TRUE; } //end Poll @@ -3166,10 +3171,8 @@ int Monitor::PrimeCapture() { } } // end if rtsp_server -#ifdef WITH_GSOAP //For now, just don't run the thread if no ONVIF support. This may change if we add other long polling options. - //ONVIF Thread - - if (onvif_event_listener && ONVIF_Healthy) { + //Poller Thread + if (onvif_event_listener || janus_enabled) { if (!Poller) { Poller = zm::make_unique(this); @@ -3177,7 +3180,7 @@ int Monitor::PrimeCapture() { Poller->Start(); } } -#endif + if (decoding_enabled) { if (!decoder_it) decoder_it = packetqueue.get_video_it(false); if (!decoder) { @@ -3381,7 +3384,6 @@ size_t Monitor::WriteCallback(void *contents, size_t size, size_t nmemb, void *u } int Monitor::add_to_janus() { - //TODO clean this up, add error checking, etc std::string response; std::string endpoint; if ((config.janus_path != nullptr) && (config.janus_path[0] != '\0')) { @@ -3479,8 +3481,73 @@ int Monitor::add_to_janus() { 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 = "{\"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 += "/"; + 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\" : \"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) return -1; + + Debug(1, "Queried for stream status: %s", response.c_str()); + curl_easy_cleanup(curl); + if (response.find("No such mountpoint") == std::string::npos) { + return 1; + } else { + return 0; + } +} int Monitor::remove_from_janus() { - //TODO clean this up, add error checking, etc std::string response; std::string endpoint; if ((config.janus_path != nullptr) && (config.janus_path[0] != '\0')) { diff --git a/src/zm_monitor.h b/src/zm_monitor.h index 898349eb3..381855c97 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -521,6 +521,7 @@ public: bool OnvifEnabled() { return onvif_event_listener; } + int check_janus(); //returns 1 for healthy, 0 for success but missing stream, negative for error. #ifdef WITH_GSOAP bool OnvifHealthy() { return ONVIF_Healthy; From abbd27d1cba082ea9bb4ec16ebb75c528f8d7bb9 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 22 Jan 2022 13:31:47 -0600 Subject: [PATCH 4/6] Remove hard-coded config Vars --- web/skins/classic/views/cycle.php | 1 - web/skins/classic/views/montage.php | 1 - web/skins/classic/views/watch.php | 1 - 3 files changed, 3 deletions(-) diff --git a/web/skins/classic/views/cycle.php b/web/skins/classic/views/cycle.php index 50bb42045..ee9e03d4f 100644 --- a/web/skins/classic/views/cycle.php +++ b/web/skins/classic/views/cycle.php @@ -192,7 +192,6 @@ xhtmlHeaders(__FILE__, translate('CycleWatch')); - diff --git a/web/skins/classic/views/montage.php b/web/skins/classic/views/montage.php index c59ce9f66..a96e8a88e 100644 --- a/web/skins/classic/views/montage.php +++ b/web/skins/classic/views/montage.php @@ -318,7 +318,6 @@ foreach (array_reverse($zones) as $zone) { - diff --git a/web/skins/classic/views/watch.php b/web/skins/classic/views/watch.php index 46ee11f90..340ee8e33 100644 --- a/web/skins/classic/views/watch.php +++ b/web/skins/classic/views/watch.php @@ -396,7 +396,6 @@ if ( ZM_WEB_SOUND_ON_ALARM ) { ?> - From d11098793571b6c9b0dc17f5aead5c5d3ad32e2d Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 22 Jan 2022 13:37:44 -0600 Subject: [PATCH 5/6] Fix indentation for ESLint --- web/skins/classic/views/js/cycle.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/web/skins/classic/views/js/cycle.js b/web/skins/classic/views/js/cycle.js index 7e7bd0147..19f4497be 100644 --- a/web/skins/classic/views/js/cycle.js +++ b/web/skins/classic/views/js/cycle.js @@ -51,14 +51,14 @@ function initCycle() { if ( scale == '0' || scale == 'auto' ) changeScale(); if (monitorData[monIdx].janusEnabled) { - if (ZM_JANUS_PATH) { - server = ZM_JANUS_PATH; - } else if (window.location.protocol=='https:') { - // Assume reverse proxy setup for now - server = "https://" + window.location.hostname + "/janus"; - } else { - server = "http://" + window.location.hostname + ":8088/janus"; - } + if (ZM_JANUS_PATH) { + server = ZM_JANUS_PATH; + } else if (window.location.protocol=='https:') { + // Assume reverse proxy setup for now + server = "https://" + window.location.hostname + "/janus"; + } else { + server = "http://" + window.location.hostname + ":8088/janus"; + } opaqueId = "streamingtest-"+Janus.randomString(12); Janus.init({debug: "all", callback: function() { janus = new Janus({ From 5485c04bc6fdb0151b44a30ee7a2a95ca842189f Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sun, 23 Jan 2022 01:07:48 -0600 Subject: [PATCH 6/6] Rework of the Janus polling loop --- src/zm_monitor.cpp | 200 +++++++++++++++++++++++---------------------- src/zm_monitor.h | 2 + 2 files changed, 104 insertions(+), 98 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index cc426586c..25a8c9f73 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1123,6 +1123,7 @@ bool Monitor::connect() { #if HAVE_LIBCURL //janus setup. Depends on libcurl. if (janus_enabled && (path.find("rtsp://") != std::string::npos)) { + 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 } @@ -1842,7 +1843,10 @@ bool Monitor::Poll() { } #endif if (janus_enabled) { - if (check_janus() != 1) { + if (janus_session.empty()) { + get_janus_session(); + } + if (check_janus() == 0) { add_to_janus(); } } @@ -3395,7 +3399,6 @@ int Monitor::add_to_janus() { 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; @@ -3416,40 +3419,8 @@ int Monitor::add_to_janus() { 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) { - Error("Failed to curl_easy_perform getting session/handle id"); - curl_easy_cleanup(curl); - return -1; - } - - pos = response.find("\"id\": "); - if (pos == std::string::npos) return -1; - janus_id = response.substr(pos + 6, 16); - response = ""; endpoint += "/"; - 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) { - Error("Failed to curl_easy_perform attaching"); - curl_easy_cleanup(curl); - 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; + endpoint += janus_session; //Assemble our actual request postData = "{\"janus\" : \"message\", \"transaction\" : \"randomString\", \"body\" : {"; @@ -3477,6 +3448,13 @@ int Monitor::add_to_janus() { 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; @@ -3490,41 +3468,15 @@ int Monitor::check_janus() { } else { endpoint = "127.0.0.1:8088/janus/"; } - std::string postData = "{\"janus\" : \"create\", \"transaction\" : \"randomString\"}"; - std::size_t pos; + std::string postData; + //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 += "/"; - 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; + endpoint += janus_session; //Assemble our actual request postData = "{\"janus\" : \"message\", \"transaction\" : \"randomString\", \"body\" : {"; @@ -3537,16 +3489,28 @@ int Monitor::check_janus() { 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; + 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; + } - Debug(1, "Queried for stream status: %s", response.c_str()); curl_easy_cleanup(curl); - if (response.find("No such mountpoint") == std::string::npos) { - return 1; - } else { + 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; @@ -3556,40 +3520,14 @@ int Monitor::remove_from_janus() { endpoint = "127.0.0.1:8088/janus/"; } std::string postData = "{\"janus\" : \"create\", \"transaction\" : \"randomString\"}"; - std::size_t pos; + //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 += "/"; - 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; + endpoint += janus_session; //Assemble our actual request postData = "{\"janus\" : \"message\", \"transaction\" : \"randomString\", \"body\" : {"; @@ -3604,9 +3542,75 @@ int Monitor::remove_from_janus() { 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; + 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 diff --git a/src/zm_monitor.h b/src/zm_monitor.h index 381855c97..98c36e5a9 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -455,6 +455,8 @@ protected: 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 uint8_t red_val;