Adds Janus options Profile-ID Override and Use RTSP Restream to work around camera issues

pull/3572/head
Jonathan Bennett 2022-08-08 23:45:53 -05:00
parent cad80eb37d
commit d41792ae00
9 changed files with 113 additions and 19 deletions

View File

@ -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 '',

37
db/zm_update-1.37.20.sql Normal file
View File

@ -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;

View File

@ -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++;

View File

@ -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;

View File

@ -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;

View File

@ -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' => '',

View File

@ -91,6 +91,7 @@ if ($action == 'save') {
'DecodingEnabled' => 0,
'JanusEnabled' => 0,
'JanusAudioEnabled' => 0,
'Janus_Use_RTSP_Restream' => 0,
'Exif' => 0,
'RTSPDescribe' => 0,
'V4LMultiBuffer' => '',

View File

@ -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.'

View File

@ -1180,6 +1180,7 @@ echo htmlSelect('newMonitor[OutputContainer]', $videowriter_containers, $monitor
?>
</td>
</tr>
<div name="JanusSettings">
<tr id="FunctionJanusAudioEnabled">
<td class="text-right pr-3"><?php echo translate('Janus Live Stream Audio') ?></td>
<td><input type="checkbox" name="newMonitor[JanusAudioEnabled]" value="1"<?php echo $monitor->JanusAudioEnabled() ? ' checked="checked"' : '' ?>/>
@ -1190,6 +1191,27 @@ echo htmlSelect('newMonitor[OutputContainer]', $videowriter_containers, $monitor
?>
</td>
</tr>
<tr id="FunctionJanusProfileOverride">
<td class="text-right pr-3"><?php echo translate('Janus Profile-ID Override') ?></td>
<td><input type="text" name="newMonitor[Janu_Profile_Override]" value="<?php echo $monitor->Janus_Profile_Override()?>"/>
<?php
if ( isset($OLANG['FUNCTION_JANUS_PROFILE_OVERRIDE']) ) {
echo '<div class="form-text">'.$OLANG['FUNCTION_JANUS_PROFILE_OVERRIDE']['Help'].'</div>';
}
?>
</td>
</tr>
<tr id="FunctionJanusUseRTSPRestream">
<td class="text-right pr-3"><?php echo translate('Janus Use RTSP Restream') ?></td>
<td><input type="checkbox" name="newMonitor[Janus_Use_RTSP_Restream]" value="1"<?php echo $monitor->Janus_Use_RTSP_Restream() ? ' checked="checked"' : '' ?>/>
<?php
if ( isset($OLANG['FUNCTION_JANUS_USE_RTSP_RESTREAM']) ) {
echo '<div class="form-text">'.$OLANG['FUNCTION_JANUS_USE_RTSP_RESTREAM']['Help'].'</div>';
}
?>
</td>
</tr>
</div>
<tr>
<td class="text-right pr-3"><?php echo translate('DefaultRate') ?></td>
<td><?php echo htmlSelect('newMonitor[DefaultRate]', $rates, $monitor->DefaultRate()); ?></td>