diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 659064fe3..75f264a0a 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -465,6 +465,8 @@ CREATE TABLE `Monitors` ( `Decoding` enum('None','Ondemand','KeyFrames','KeyFrames+Ondemand', 'Always') NOT NULL default 'Always', `JanusEnabled` BOOLEAN NOT NULL default false, `JanusAudioEnabled` BOOLEAN NOT NULL default false, + `Janus_Profile_Override` VARCHAR(30) NOT NULL DEFAULT '', + `Janus_Use_RTSP_Restream` BOOLEAN NOT NULL default false, `LinkedMonitors` varchar(255), `Triggers` set('X10') NOT NULL default '', `EventStartCommand` VARCHAR(255) NOT NULL DEFAULT '', diff --git a/db/zm_update-1.37.20.sql b/db/zm_update-1.37.20.sql new file mode 100644 index 000000000..b74e9d064 --- /dev/null +++ b/db/zm_update-1.37.20.sql @@ -0,0 +1,37 @@ +-- +-- Update Monitors Table to include Janus_Profile_Override +-- + +SELECT 'Checking for Janus_Profile_Override in Monitors'; +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Monitors' + AND table_schema = DATABASE() + AND column_name = 'Janus_Profile_Override' + ) > 0, +"SELECT 'Column Janus_Profile_Override already exists in Monitors'", +"ALTER TABLE Monitors ADD Janus_Profile_Override varchar(30) DEFAULT '' AFTER `JanusAudioEnabled`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +-- +-- Update Monitors Table to include Janus_Use_RTSP_Restream +-- + +SELECT 'Checking for Janus_Use_RTSP_Restream in Monitors'; +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Monitors' + AND table_schema = DATABASE() + AND column_name = 'Janus_Use_RTSP_Restream' + ) > 0, +"SELECT 'Column Janus_Use_RTSP_Restream already exists in Monitors'", +"ALTER TABLE Monitors ADD Janus_Use_RTSP_Restream BOOLEAN NOT NULL DEFAULT false AFTER `Janus_Profile_Override`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 420551071..fb88a5a39 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -82,7 +82,7 @@ struct Namespace namespaces[] = std::string load_monitor_sql = "SELECT `Id`, `Name`, `ServerId`, `StorageId`, `Type`, `Capturing`+0, `Analysing`+0, `AnalysisSource`+0, `AnalysisImage`+0," "`Recording`+0, `RecordingSource`+0, `Decoding`+0, " -"`JanusEnabled`, `JanusAudioEnabled`, " +"`JanusEnabled`, `JanusAudioEnabled`, `Janus_Profile_Override`, `Janus_Use_RTSP_Restream`," "`LinkedMonitors`, `EventStartCommand`, `EventEndCommand`, `AnalysisFPSLimit`, `AnalysisUpdateDelay`, `MaxFPS`, `AlarmMaxFPS`," "`Device`, `Channel`, `Format`, `V4LMultiBuffer`, `V4LCapturesPerFrame`, " // V4L Settings "`Protocol`, `Method`, `Options`, `User`, `Pass`, `Host`, `Port`, `Path`, `SecondPath`, `Width`, `Height`, `Colours`, `Palette`, `Orientation`+0, `Deinterlacing`, " @@ -166,6 +166,8 @@ Monitor::Monitor() decoding(DECODING_ALWAYS), janus_enabled(false), janus_audio_enabled(false), + janus_profile_override(""), + janus_use_rtsp_restream(false), //protocol //method //options @@ -313,8 +315,7 @@ Monitor::Monitor() /* std::string load_monitor_sql = "SELECT `Id`, `Name`, `ServerId`, `StorageId`, `Type`, `Capturing`+0, `Analysing`+0, `AnalysisSource`+0, `AnalysisImage`+0," - "`Recording`+0, `RecordingSource`+0, - `Decoding`+0, JanusEnabled, JanusAudioEnabled, " + "`Recording`+0, `RecordingSource`+0, `Decoding`+0, JanusEnabled, JanusAudioEnabled, Janus_Profile_Override, Janus_Use_RTSP_Restream" "LinkedMonitors, `EventStartCommand`, `EventEndCommand`, " "AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS," "Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, " // V4L Settings @@ -375,6 +376,8 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) { // See below after save_jpegs for a recalculation of decoding_enabled janus_enabled = dbrow[col] ? atoi(dbrow[col]) : false; col++; janus_audio_enabled = dbrow[col] ? atoi(dbrow[col]) : false; col++; + janus_profile_override = std::string(dbrow[col] ? dbrow[col] : ""); col++; + janus_use_rtsp_restream = dbrow[col] ? atoi(dbrow[col]) : false; col++; linked_monitors_string = dbrow[col] ? dbrow[col] : ""; col++; event_start_command = dbrow[col] ? dbrow[col] : ""; col++; diff --git a/src/zm_monitor.h b/src/zm_monitor.h index 29d80ec77..0635516f8 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -338,6 +338,7 @@ protected: //helper class for CURL static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp); bool Janus_Healthy; + bool Use_RTSP_Restream; std::string janus_session; std::string janus_handle; std::string janus_endpoint; @@ -345,6 +346,7 @@ protected: std::string rtsp_username; std::string rtsp_password; std::string rtsp_path; + std::string profile_override; public: explicit JanusManager(Monitor *parent_); @@ -375,6 +377,8 @@ protected: DecodingOption decoding; // Whether the monitor will decode h264/h265 packets bool janus_enabled; // Whether we set the h264/h265 stream up on janus bool janus_audio_enabled; // Whether we tell Janus to try to include audio. + std::string janus_profile_override; // The Profile-ID to force the stream to use. + bool janus_use_rtsp_restream; // Point Janus at the ZM RTSP output, rather than the camera directly. std::string protocol; std::string method; diff --git a/src/zm_monitor_janus.cpp b/src/zm_monitor_janus.cpp index 0994b014f..d4c4d007f 100644 --- a/src/zm_monitor_janus.cpp +++ b/src/zm_monitor_janus.cpp @@ -25,7 +25,9 @@ Monitor::JanusManager::JanusManager(Monitor *parent_) : Janus_Healthy(false) { //constructor takes care of init and calls add_to - parent = parent_; + //parent = parent_; + Use_RTSP_Restream = parent->janus_use_rtsp_restream; + profile_override = parent->janus_profile_override; if ((config.janus_path != nullptr) && (config.janus_path[0] != '\0')) { janus_endpoint = config.janus_path; //remove the trailing slash if present @@ -33,22 +35,29 @@ Monitor::JanusManager::JanusManager(Monitor *parent_) : } else { janus_endpoint = "127.0.0.1:8088/janus"; } - std::size_t at_pos = parent->path.find("@", 7); - if (at_pos != std::string::npos) { - //If we find an @ symbol, we have a username/password. Otherwise, passwordless login. - std::size_t colon_pos = parent->path.find(":", 7); //Search for the colon, but only after the rtsp:// text. - if (colon_pos == std::string::npos) { - //Looks like an invalid url - throw std::runtime_error("Cannot Parse URL for Janus."); - } - rtsp_username = parent->path.substr(7, colon_pos-7); - rtsp_password = parent->path.substr(colon_pos+1, at_pos - colon_pos - 1); - rtsp_path = "rtsp://"; - rtsp_path += parent->path.substr(at_pos + 1); - } else { + if (Use_RTSP_Restream) { + int restream_port = config.min_rtsp_port; rtsp_username = ""; rtsp_password = ""; - rtsp_path = parent->path; + rtsp_path = "rtsp://127.0.0.1:" + std::to_string(restream_port) + "/" + parent->rtsp_streamname; + } else { + std::size_t at_pos = parent->path.find("@", 7); + if (at_pos != std::string::npos) { + //If we find an @ symbol, we have a username/password. Otherwise, passwordless login. + std::size_t colon_pos = parent->path.find(":", 7); //Search for the colon, but only after the rtsp:// text. + if (colon_pos == std::string::npos) { + //Looks like an invalid url + throw std::runtime_error("Cannot Parse URL for Janus."); + } + rtsp_username = parent->path.substr(7, colon_pos-7); + rtsp_password = parent->path.substr(colon_pos+1, at_pos - colon_pos - 1); + rtsp_path = "rtsp://"; + rtsp_path += parent->path.substr(at_pos + 1); + } else { + rtsp_username = ""; + rtsp_password = ""; + rtsp_path = parent->path; + } } } @@ -108,7 +117,7 @@ int Monitor::JanusManager::check_janus() { curl_easy_cleanup(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)); + Warning("Attempted to send %s to %s and got %s", postData.c_str(), endpoint.c_str(), curl_easy_strerror(res)); janus_session = ""; janus_handle = ""; return -1; @@ -151,6 +160,10 @@ int Monitor::JanusManager::add_to_janus() { postData += "\", \"type\" : \"rtsp\", \"rtsp_quirk\" : true, "; postData += "\"url\" : \""; postData += rtsp_path; + if (profile_override[0] != '\0') { + postData += "\", \"videofmtp\" : \""; + postData += profile_override; + } if (rtsp_username != "") { postData += "\", \"rtsp_user\" : \""; postData += rtsp_username; diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index 92f342c4a..655a1e6c2 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -146,6 +146,8 @@ public static function getStatuses() { 'Decoding' => 'Always', 'JanusEnabled' => array('type'=>'boolean','default'=>0), 'JanusAudioEnabled' => array('type'=>'boolean','default'=>0), + 'Janus_Profile_Override' => '', + 'Janus_Use_RTSP_Restream' => array('type'=>'boolean','default'=>0), 'LinkedMonitors' => array('type'=>'set', 'default'=>null), 'Triggers' => array('type'=>'set','default'=>''), 'EventStartCommand' => '', diff --git a/web/includes/actions/monitor.php b/web/includes/actions/monitor.php index b27ff4443..f22221bcb 100644 --- a/web/includes/actions/monitor.php +++ b/web/includes/actions/monitor.php @@ -91,6 +91,7 @@ if ($action == 'save') { 'DecodingEnabled' => 0, 'JanusEnabled' => 0, 'JanusAudioEnabled' => 0, + 'Janus_Use_RTSP_Restream' => 0, 'Exif' => 0, 'RTSPDescribe' => 0, 'V4LMultiBuffer' => '', diff --git a/web/lang/en_gb.php b/web/lang/en_gb.php index d93207f5d..d1a3d7b1e 100644 --- a/web/lang/en_gb.php +++ b/web/lang/en_gb.php @@ -891,6 +891,16 @@ None: No frames will be decoded, live view and thumbnails will not be available~ Attempt to enable audio in the Janus stream. Has no effect for cameras without audio support, but can prevent a stream playing if your camera sends an audio format unsupported by the browser.' ), + 'FUNCTION_JANUS_PROFILE_OVERRIDE' => array( + 'Help' => ' + Manually set a Profile-ID, which can force a browser to try to play a given stream. Try "42e01f" + for a universally supported value, or leave this blank to use the Profile-ID specified by the source.' + ), + 'FUNCTION_JANUS_USE_RTSP_RESTREAM' => array( + 'Help' => ' + If your camera will not work under Janus with any other options, enable this to use the ZoneMinder + RTSP restream as the Janus source.' + ), 'ImageBufferCount' => array( 'Help' => ' Number of raw images available in /dev/shm. Currently should be set in the 3-5 range. Used for live viewing.' diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 01a231c18..e5b88aec8 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -1180,6 +1180,7 @@ echo htmlSelect('newMonitor[OutputContainer]', $videowriter_containers, $monitor ?> +
JanusAudioEnabled() ? ' checked="checked"' : '' ?>/> @@ -1190,6 +1191,27 @@ echo htmlSelect('newMonitor[OutputContainer]', $videowriter_containers, $monitor ?> + + + +'.$OLANG['FUNCTION_JANUS_PROFILE_OVERRIDE']['Help'].'
'; + } +?> + + + + + Janus_Use_RTSP_Restream() ? ' checked="checked"' : '' ?>/> +'.$OLANG['FUNCTION_JANUS_USE_RTSP_RESTREAM']['Help'].''; + } +?> + + + DefaultRate()); ?>