From 7671f59d2fdb6d29c7e9690e145a19632b625a44 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 9 Apr 2019 12:27:40 -0400 Subject: [PATCH 01/45] Add error counting on decoding --- src/zm_ffmpeg_camera.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index 152f426f2..dc2a32475 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -947,9 +947,15 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event if ( ret < 0 ) { av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE); Warning("Unable to receive frame %d: %s, continuing", frameCount, errbuf); + error_count += 1; + if ( error_count > 100 ) { + Error("Error count over 100, going to close and re-open stream"); + return -1; + } zm_av_packet_unref(&packet); continue; } + if ( error_count > 0 ) error_count --; #if HAVE_AVUTIL_HWCONTEXT_H } From 6923382485133b63e3c0d1d44b4f71dcf3e380e4 Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Wed, 17 Apr 2019 13:33:38 -0400 Subject: [PATCH 02/45] Alarm cause fix (#2580) * move alarm cause code to when the alarm flag is set * formatting * added temp info log * char* not string in log --- src/zm_monitor.cpp | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 09aaeeb87..ab3cd13d3 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1550,6 +1550,19 @@ bool Monitor::Analyse() { Info("%s: %03d - Gone into alarm state PreAlarmCount: %u > AlarmFrameCount:%u", name, image_count, Event::PreAlarmCount(), alarm_frame_count); shared_data->state = state = ALARM; + // lets construct alarm cause. It will contain cause + names of zones alarmed + std::string alarm_cause=""; + for ( int i=0; i < n_zones; i++) { + if (zones[i]->Alarmed()) { + alarm_cause += std::string(zones[i]->Label()); + if (i < n_zones-1) { + alarm_cause +=","; + } + } + } + alarm_cause = cause+" "+alarm_cause; + strncpy(shared_data->alarm_cause,alarm_cause.c_str(), sizeof(shared_data->alarm_cause)-1); + Info ("Recorded alarm cause as: %s", alarm_cause.c_str()); if ( signal_change || (function != MOCORD && state != ALERT) ) { int pre_index; int pre_event_images = pre_event_count; @@ -1594,18 +1607,7 @@ bool Monitor::Analyse() { event = new Event(this, *(image_buffer[pre_index].timestamp), cause, noteSetMap); } shared_data->last_event = event->Id(); - // lets construct alarm cause. It will contain cause + names of zones alarmed - std::string alarm_cause=""; - for ( int i=0; i < n_zones; i++) { - if (zones[i]->Alarmed()) { - alarm_cause += std::string(zones[i]->Label()); - if (i < n_zones-1) { - alarm_cause +=","; - } - } - } - alarm_cause = cause+" "+alarm_cause; - strncpy(shared_data->alarm_cause,alarm_cause.c_str(), sizeof(shared_data->alarm_cause)-1); + //set up video store data snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile()); video_store_data->recording = event->StartTime(); From eb76cd87bbe959e6e8f334bd749f55a17fd06c67 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 17 Apr 2019 13:53:11 -0400 Subject: [PATCH 03/45] Revert "Alarm cause fix (#2580)" (#2581) This reverts commit 6923382485133b63e3c0d1d44b4f71dcf3e380e4. --- src/zm_monitor.cpp | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index ab3cd13d3..09aaeeb87 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1550,19 +1550,6 @@ bool Monitor::Analyse() { Info("%s: %03d - Gone into alarm state PreAlarmCount: %u > AlarmFrameCount:%u", name, image_count, Event::PreAlarmCount(), alarm_frame_count); shared_data->state = state = ALARM; - // lets construct alarm cause. It will contain cause + names of zones alarmed - std::string alarm_cause=""; - for ( int i=0; i < n_zones; i++) { - if (zones[i]->Alarmed()) { - alarm_cause += std::string(zones[i]->Label()); - if (i < n_zones-1) { - alarm_cause +=","; - } - } - } - alarm_cause = cause+" "+alarm_cause; - strncpy(shared_data->alarm_cause,alarm_cause.c_str(), sizeof(shared_data->alarm_cause)-1); - Info ("Recorded alarm cause as: %s", alarm_cause.c_str()); if ( signal_change || (function != MOCORD && state != ALERT) ) { int pre_index; int pre_event_images = pre_event_count; @@ -1607,7 +1594,18 @@ bool Monitor::Analyse() { event = new Event(this, *(image_buffer[pre_index].timestamp), cause, noteSetMap); } shared_data->last_event = event->Id(); - + // lets construct alarm cause. It will contain cause + names of zones alarmed + std::string alarm_cause=""; + for ( int i=0; i < n_zones; i++) { + if (zones[i]->Alarmed()) { + alarm_cause += std::string(zones[i]->Label()); + if (i < n_zones-1) { + alarm_cause +=","; + } + } + } + alarm_cause = cause+" "+alarm_cause; + strncpy(shared_data->alarm_cause,alarm_cause.c_str(), sizeof(shared_data->alarm_cause)-1); //set up video store data snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile()); video_store_data->recording = event->StartTime(); From 41cadf3a007e38f9dfaea4c9ac39df6a6445eed9 Mon Sep 17 00:00:00 2001 From: Alex Fornuto Date: Thu, 18 Apr 2019 18:51:06 -0500 Subject: [PATCH 04/45] Update Debian Instructions When downloading the repository GPG key, `wget` does not require `sudo`. The root permissions required are provided by `sudo` after the pipe. --- docs/installationguide/debian.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installationguide/debian.rst b/docs/installationguide/debian.rst index 9f26411e0..a6a27aa19 100644 --- a/docs/installationguide/debian.rst +++ b/docs/installationguide/debian.rst @@ -65,7 +65,7 @@ Because ZoneMinder's package repository provides a secure connection through HTT Finally, download the GPG key for ZoneMinder's repository: :: - sudo wget -O - https://zmrepo.zoneminder.com/debian/archive-keyring.gpg | sudo apt-key add - + wget -O - https://zmrepo.zoneminder.com/debian/archive-keyring.gpg | sudo apt-key add - **Step 5:** Install ZoneMinder From 8195c4e395642655198b87aa1703b09b398e580f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Gonz=C3=A1lez=20Calleja?= Date: Sat, 20 Apr 2019 17:19:27 +0200 Subject: [PATCH 05/45] Fixing video export view (#2585) --- web/skins/classic/views/video.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/video.php b/web/skins/classic/views/video.php index aab9278d6..ca4c46f4b 100644 --- a/web/skins/classic/views/video.php +++ b/web/skins/classic/views/video.php @@ -46,7 +46,7 @@ if ( isset($_REQUEST['scale']) ) else $scale = reScale(SCALE_BASE, $event['DefaultScale'], ZM_WEB_DEFAULT_SCALE); -$Event = new Event($event['Id']); +$Event = new ZM\Event($event['Id']); $eventPath = $Event->Path(); $videoFormats = array(); From 0d4651c2d60a18b73cddf5826a3f44b81fe8552b Mon Sep 17 00:00:00 2001 From: Steve Root Date: Tue, 23 Apr 2019 15:58:28 +0100 Subject: [PATCH 06/45] Update url to donate page (#2586) --- docs/userguide/introduction.rst | 2 +- web/lang/ba_ba.php | 2 +- web/lang/big5_big5.php | 2 +- web/lang/cn_zh.php | 2 +- web/lang/cs_cz.php | 2 +- web/lang/de_de.php | 2 +- web/lang/dk_dk.php | 2 +- web/lang/en_gb.php | 2 +- web/lang/es_ar.php | 2 +- web/lang/es_es.php | 2 +- web/lang/et_ee.php | 2 +- web/lang/fr_fr.php | 2 +- web/lang/he_il.php | 2 +- web/lang/hu_hu.php | 2 +- web/lang/it_it.php | 2 +- web/lang/ja_jp.php | 2 +- web/lang/nl_nl.php | 2 +- web/lang/pl_pl.php | 2 +- web/lang/pt_br.php | 2 +- web/lang/ro_ro.php | 2 +- web/lang/se_se.php | 2 +- 21 files changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/userguide/introduction.rst b/docs/userguide/introduction.rst index 2d9985eb1..a6573a24d 100644 --- a/docs/userguide/introduction.rst +++ b/docs/userguide/introduction.rst @@ -11,4 +11,4 @@ A fast video interface core, a user-friendly and comprehensive PHP based web int The core of ZoneMinder is the capture and analysis of images and a highly configurable set of parameters that eliminate false positives whilst ensuring minimum loss of footage. For example, you can define a set of 'zones' for each camera of varying sensitivity and functionality. This eliminates zones that you don't wish to track or define areas that will alarm if various thresholds are exceeded in conjunction with other zones. -ZoneMinder is free under GPL License, but if you do find it useful, then please feel free to visit http://www.zoneminder.com/donate.html and help us fund our future improvements. +ZoneMinder is free under GPL License, but if you do find it useful, then please feel free to visit https://zoneminder.com/donate/ and help us fund our future improvements. diff --git a/web/lang/ba_ba.php b/web/lang/ba_ba.php index 02ad8e5f3..96a3ca4d1 100644 --- a/web/lang/ba_ba.php +++ b/web/lang/ba_ba.php @@ -294,7 +294,7 @@ $SLANG = array( 'Display' => 'Prikaz', 'Displaying' => 'Prikazujem', 'DonateAlready' => 'Ne, već sam napravio donaciju.', - 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', + 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', 'Donate' => 'Molimo donirajte', 'DonateRemindDay' => 'Ne još, podsjetime za 1 dan', 'DonateRemindHour' => 'Ne još, podsjetime za 1 sat', diff --git a/web/lang/big5_big5.php b/web/lang/big5_big5.php index 518cbe57c..922140698 100644 --- a/web/lang/big5_big5.php +++ b/web/lang/big5_big5.php @@ -293,7 +293,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'Donate' => 'Please Donate', 'DonateAlready' => 'No, I\'ve already donated', - 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', + 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', 'DonateRemindDay' => 'Not yet, remind again in 1 day', 'DonateRemindHour' => 'Not yet, remind again in 1 hour', 'DonateRemindMonth' => 'Not yet, remind again in 1 month', diff --git a/web/lang/cn_zh.php b/web/lang/cn_zh.php index 8e2bfa8f9..0a751023e 100644 --- a/web/lang/cn_zh.php +++ b/web/lang/cn_zh.php @@ -289,7 +289,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'Donate' => '请捐款', 'DonateAlready' => '不,我已经捐赠过了', - 'DonateEnticement' => '迄今,您已经运行ZoneMinder有一阵子了,希望它能够有助于增强您家或者办公区域的安全。尽管ZoneMinder是,并将保持免费和开源,该项目依然在研发和支持中投入了资金和精力。如果您愿意支持今后的开发和新功能,那么请考虑为该项目捐款。捐款不是必须的,任何数量的捐赠,我们都很感谢。

如果您愿意捐款,请选择下列选项,或者访问 http://www.zoneminder.com/donate.html 捐赠主页。

感谢您使用ZoneMinder,并且不要忘记访问访问ZoneMinder.com的论坛以获得支持或建议,这可以提升您的ZoneMinder的体验。', + 'DonateEnticement' => '迄今,您已经运行ZoneMinder有一阵子了,希望它能够有助于增强您家或者办公区域的安全。尽管ZoneMinder是,并将保持免费和开源,该项目依然在研发和支持中投入了资金和精力。如果您愿意支持今后的开发和新功能,那么请考虑为该项目捐款。捐款不是必须的,任何数量的捐赠,我们都很感谢。

如果您愿意捐款,请选择下列选项,或者访问 https://zoneminder.com/donate/ 捐赠主页。

感谢您使用ZoneMinder,并且不要忘记访问访问ZoneMinder.com的论坛以获得支持或建议,这可以提升您的ZoneMinder的体验。', 'DonateRemindDay' => '现在不,1天内再次提醒我', 'DonateRemindHour' => '现在不,1小时内再次提醒我', 'DonateRemindMonth' => '现在不,1个月内再次提醒我', diff --git a/web/lang/cs_cz.php b/web/lang/cs_cz.php index 8e76b20ba..c6340ae76 100644 --- a/web/lang/cs_cz.php +++ b/web/lang/cs_cz.php @@ -289,7 +289,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'Donate' => 'Prosím podpořte', 'DonateAlready' => 'Ne, už jsem podpořil', - 'DonateEnticement' => 'Již nějakou dobu používáte software ZoneMinder k ochraně svého majetku a předpokládám, že jej shledáváte užitečným. Přestože je ZoneMinder, znovu připomínám, zdarma a volně šířený software, stojí jeho vývoj a podpora nějaké peníze. Pokud byste chtěl/a podpořit budoucí vývoj a nové možnosti softwaru, prosím zvažte darování finanční pomoci. Darování je, samozřejmě, dobrovolné, ale zato velmi ceněné můžete přispět jakou částkou chcete.

Pokud máte zájem podpořit náš tým, prosím, vyberte níže uvedenou možnost, nebo navštivte http://www.zoneminder.com/donate.html.

Děkuji Vám že jste si vybral/a software ZoneMinder a nezapomeňte navštívit fórum na ZoneMinder.com pro podporu a návrhy jak udělat ZoneMinder ještě lepším než je dnes.', + 'DonateEnticement' => 'Již nějakou dobu používáte software ZoneMinder k ochraně svého majetku a předpokládám, že jej shledáváte užitečným. Přestože je ZoneMinder, znovu připomínám, zdarma a volně šířený software, stojí jeho vývoj a podpora nějaké peníze. Pokud byste chtěl/a podpořit budoucí vývoj a nové možnosti softwaru, prosím zvažte darování finanční pomoci. Darování je, samozřejmě, dobrovolné, ale zato velmi ceněné můžete přispět jakou částkou chcete.

Pokud máte zájem podpořit náš tým, prosím, vyberte níže uvedenou možnost, nebo navštivte https://zoneminder.com/donate/.

Děkuji Vám že jste si vybral/a software ZoneMinder a nezapomeňte navštívit fórum na ZoneMinder.com pro podporu a návrhy jak udělat ZoneMinder ještě lepším než je dnes.', 'DonateRemindDay' => 'Nyní ne, připomenout za 1 den', 'DonateRemindHour' => 'Nyní ne, připomenout za hodinu', 'DonateRemindMonth' => 'Nyní ne, připomenout za měsíc', diff --git a/web/lang/de_de.php b/web/lang/de_de.php index 37ea4d81c..e8bb8218a 100644 --- a/web/lang/de_de.php +++ b/web/lang/de_de.php @@ -291,7 +291,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'Donate' => 'Bitte spenden Sie.', 'DonateAlready' => 'Nein, ich habe schon gespendet', - 'DonateEnticement' => 'Sie benutzen ZoneMinder nun schon eine Weile und es ist hoffentlich eine nützliche Applikation zur Verbesserung Ihrer Heim- oder Arbeitssicherheit. Obwohl ZoneMinder eine freie Open-Source-Software ist und bleiben wird, entstehen Kosten bei der Entwicklung und dem Support.

Falls Sie ZoneMinder für Weiterentwicklung in der Zukunft unterstützen möchten, denken Sie bitte über eine Spende für das Projekt unter der Webadresse http://www.zoneminder.com/donate.html oder über nachfolgend stehende Option nach. Spenden sind, wie der Name schon sagt, immer freiwillig. Dem Projekt helfen kleine genauso wie größere Spenden sehr weiter und ein herzlicher Dank ist jedem Spender sicher.

Vielen Dank dafür, dass sie ZoneMinder benutzen. Vergessen Sie nicht die Foren unter ZoneMinder.com, um Support zu erhalten und Ihre Erfahrung mit ZoneMinder zu verbessern!', + 'DonateEnticement' => 'Sie benutzen ZoneMinder nun schon eine Weile und es ist hoffentlich eine nützliche Applikation zur Verbesserung Ihrer Heim- oder Arbeitssicherheit. Obwohl ZoneMinder eine freie Open-Source-Software ist und bleiben wird, entstehen Kosten bei der Entwicklung und dem Support.

Falls Sie ZoneMinder für Weiterentwicklung in der Zukunft unterstützen möchten, denken Sie bitte über eine Spende für das Projekt unter der Webadresse https://zoneminder.com/donate/ oder über nachfolgend stehende Option nach. Spenden sind, wie der Name schon sagt, immer freiwillig. Dem Projekt helfen kleine genauso wie größere Spenden sehr weiter und ein herzlicher Dank ist jedem Spender sicher.

Vielen Dank dafür, dass sie ZoneMinder benutzen. Vergessen Sie nicht die Foren unter ZoneMinder.com, um Support zu erhalten und Ihre Erfahrung mit ZoneMinder zu verbessern!', 'DonateRemindDay' => 'Noch nicht, erinnere mich in einem Tag noch mal.', 'DonateRemindHour' => 'Noch nicht, erinnere mich in einer Stunde noch mal.', 'DonateRemindMonth' => 'Noch nicht, erinnere mich in einem Monat noch mal.', diff --git a/web/lang/dk_dk.php b/web/lang/dk_dk.php index 26533a7fb..045b5345e 100644 --- a/web/lang/dk_dk.php +++ b/web/lang/dk_dk.php @@ -290,7 +290,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'Donate' => 'Venligst Donér', 'DonateAlready' => 'Nej, jeg har allerede doneret', - 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', + 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', 'DonateRemindDay' => 'Ikke endnu, påmind igen on 1 dag', 'DonateRemindHour' => 'Ikke endnu, påmind igen on 1 time', 'DonateRemindMonth' => 'Ikke endnu, påmind igen on 1 måned', diff --git a/web/lang/en_gb.php b/web/lang/en_gb.php index 4eb493630..945d26bea 100644 --- a/web/lang/en_gb.php +++ b/web/lang/en_gb.php @@ -296,7 +296,7 @@ $SLANG = array( 'Display' => 'Display', 'Displaying' => 'Displaying', 'DonateAlready' => 'No, I\'ve already donated', - 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', + 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', 'Donate' => 'Please Donate', 'DonateRemindDay' => 'Not yet, remind again in 1 day', 'DonateRemindHour' => 'Not yet, remind again in 1 hour', diff --git a/web/lang/es_ar.php b/web/lang/es_ar.php index 114002620..a8da807be 100644 --- a/web/lang/es_ar.php +++ b/web/lang/es_ar.php @@ -240,7 +240,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'Donate' => 'Please Donate', 'DonateAlready' => 'No, I\'ve already donated', - 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', + 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', 'DonateRemindDay' => 'Not yet, remind again in 1 day', 'DonateRemindHour' => 'Not yet, remind again in 1 hour', 'DonateRemindMonth' => 'Not yet, remind again in 1 month', diff --git a/web/lang/es_es.php b/web/lang/es_es.php index 767b58d97..f97ea0ff1 100644 --- a/web/lang/es_es.php +++ b/web/lang/es_es.php @@ -289,7 +289,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', // Added - 2015-04-18 'Donate' => 'Por favor, done', 'DonateAlready' => 'No, ya he donado', - 'DonateEnticement' => 'Ha estado ejecutando ZoneMinder por un tiempo y con suerte le resultará un útil complemento para su seguridad en hogar y trabajo. Aunque ZoneMinder es, y será, libre y de código abierto, cuesta dinero desarrollarlo y mantenerlo. Si quiere ayudar a mantener un futuro desarrollo y nuevas funciones entonces considere hacer un donativo por favor. Donar es, por supuesto, opcional pero muy apreciado y puede donar tanto como desee sin importar la cantidad.

Si desea hacer una donación por favor seleccione la opción de debajo o vaya a http://www.zoneminder.com/donate.html en su navegador.

Muchas gracias por usar ZoneMinder y no se olvide de vistar los foros en ZoneMinder.com para obtener soporte o hacer sugerencias sobre cómo mejorar su experiencia con ZoneMinder aún más.', + 'DonateEnticement' => 'Ha estado ejecutando ZoneMinder por un tiempo y con suerte le resultará un útil complemento para su seguridad en hogar y trabajo. Aunque ZoneMinder es, y será, libre y de código abierto, cuesta dinero desarrollarlo y mantenerlo. Si quiere ayudar a mantener un futuro desarrollo y nuevas funciones entonces considere hacer un donativo por favor. Donar es, por supuesto, opcional pero muy apreciado y puede donar tanto como desee sin importar la cantidad.

Si desea hacer una donación por favor seleccione la opción de debajo o vaya a https://zoneminder.com/donate/ en su navegador.

Muchas gracias por usar ZoneMinder y no se olvide de vistar los foros en ZoneMinder.com para obtener soporte o hacer sugerencias sobre cómo mejorar su experiencia con ZoneMinder aún más.', 'DonateRemindDay' => 'Aún no, recordarme de nuevo en 1 día', 'DonateRemindHour' => 'Aún no, recordarme de nuevo en 1 hora', 'DonateRemindMonth' => 'Aún no, recordarme de nuevo en 1 mes', diff --git a/web/lang/et_ee.php b/web/lang/et_ee.php index 19d6e777b..99a277144 100644 --- a/web/lang/et_ee.php +++ b/web/lang/et_ee.php @@ -296,7 +296,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', // Added - 2015-04-18 'Donate' => 'Palun Anneta', 'DonateAlready' => 'EI, Ma olen juba annetanud', - 'DonateEnticement' => 'Sa oled juba kasutanud ZoneMinderit juba mõnda aega. Nüüd kus sa oled leidnud, et see on kasulik lisa sinu kodule või sinu töökohale. Kuigi ZoneMinder on, jääb alatiseks, vabaks ja avatud lähtekoodiks, siiski selle arendamiseks kulub aega ja raha. Kui sa soovid meid aidata, siis toeta meid tuleviku arendusteks ja uute lisade loomiseks. Palun mõelge annetuse peale. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', + 'DonateEnticement' => 'Sa oled juba kasutanud ZoneMinderit juba mõnda aega. Nüüd kus sa oled leidnud, et see on kasulik lisa sinu kodule või sinu töökohale. Kuigi ZoneMinder on, jääb alatiseks, vabaks ja avatud lähtekoodiks, siiski selle arendamiseks kulub aega ja raha. Kui sa soovid meid aidata, siis toeta meid tuleviku arendusteks ja uute lisade loomiseks. Palun mõelge annetuse peale. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', 'DonateRemindDay' => 'Ei veel, tuleta meelde ühe päeva pärast', 'DonateRemindHour' => 'Ei veel, tuleta meelde ühe tunni pärast', 'DonateRemindMonth' => 'Ei veel, tuleta meelde ühe kuu pärast', diff --git a/web/lang/fr_fr.php b/web/lang/fr_fr.php index 03ffc70ba..f379b04cd 100644 --- a/web/lang/fr_fr.php +++ b/web/lang/fr_fr.php @@ -295,7 +295,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Réaliser détection native', 'Donate' => 'Veuillez faire un don', 'DonateAlready' => 'Non, j\'ai déjà donné', - 'DonateEnticement' => 'Vous utilisez ZoneMinder depuis quelque temps et nous espérons que vous trouvez cette solution utile. Bien que ZoneMinder est, et restera, une solution libre et ouverte (open source), son développement et son maintien nécessitent des moyens financiers. Si vous voulez aider au développement et à l\'ajout de fonctionnalités, veuillez considérer la possibilité d\'effectuer un don. Les dons sont bien sûr optionnels mais grandement appréciés et vous pouvez donner le montant que vous désirez.

Si vous voulez effectuer un don, veuillez sélectionner l\'option ci-dessous ou veuillez vous rendre sur http://www.zoneminder.com/donate.html à l\'aide de votre navigateur internet.

Merci d\'utiliser ZoneMinder et n\'oubliez pas de visiter les forums sur ZoneMinder.com pour le support ou des suggestions pour rendre votre expérience de ZoneMinder encore meilleure.', + 'DonateEnticement' => 'Vous utilisez ZoneMinder depuis quelque temps et nous espérons que vous trouvez cette solution utile. Bien que ZoneMinder est, et restera, une solution libre et ouverte (open source), son développement et son maintien nécessitent des moyens financiers. Si vous voulez aider au développement et à l\'ajout de fonctionnalités, veuillez considérer la possibilité d\'effectuer un don. Les dons sont bien sûr optionnels mais grandement appréciés et vous pouvez donner le montant que vous désirez.

Si vous voulez effectuer un don, veuillez sélectionner l\'option ci-dessous ou veuillez vous rendre sur https://zoneminder.com/donate/ à l\'aide de votre navigateur internet.

Merci d\'utiliser ZoneMinder et n\'oubliez pas de visiter les forums sur ZoneMinder.com pour le support ou des suggestions pour rendre votre expérience de ZoneMinder encore meilleure.', 'DonateRemindDay' => 'Pas encore, me rappeler dans 1 jour', 'DonateRemindHour' => 'Pas encore, me rappeler dans 1 heure', 'DonateRemindMonth' => 'Pas encore, me rappeler dans 1 mois', diff --git a/web/lang/he_il.php b/web/lang/he_il.php index 26e8bcea6..b3e03c225 100644 --- a/web/lang/he_il.php +++ b/web/lang/he_il.php @@ -289,7 +289,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'Donate' => 'úøåí áá÷ùä', 'DonateAlready' => 'ìà, úøîúé ëáø', - 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', + 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', 'DonateRemindDay' => 'òãééï ìà, äæëø ìà áòåã éåí àçã', 'DonateRemindHour' => 'òãééï ìà, äæëø ìé áòåã ùòä àçú', 'DonateRemindMonth' => 'òãééï ìà, äæëø ìé áòåã çåãù àçã', diff --git a/web/lang/hu_hu.php b/web/lang/hu_hu.php index 15cf72aab..7ef51dab3 100644 --- a/web/lang/hu_hu.php +++ b/web/lang/hu_hu.php @@ -332,7 +332,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', // Added - 2015-04-18 'Donate' => 'Kérem támogasson', 'DonateAlready' => 'Nem, én már támogattam', - 'DonateEnticement' => 'Ön már jó ideje használja a ZoneMindert, és reméljük hasznos eszköznek tartja háza vagy munkahelye biztonságában. Bár a ZoneMinder egy szabad, nyílt forráskódú termék és az is marad, a fejlesztése pénzbe kerül. Ha van lehetősége támogatni a jövőbeni fejlesztéseket és az új funkciókat kérem, tegye meg. A támogatás teljesen önkéntes, de nagyon megbecsült és mértéke is tetszőleges.

Ha támogatni szertne, kérem, válasszon az alábbi lehetőségekből vagy látogassa meg a http://www.zoneminder.com/donate.html oldalt.

Köszönjük, hogy használja a ZoneMinder-t és ne felejtse el meglátogatni a fórumokat a ZoneMinder.com oldalon támogatásért és ötletekért, hogy a jövőben is még jobban ki tudja használni a ZoneMinder lehetőségeit.', + 'DonateEnticement' => 'Ön már jó ideje használja a ZoneMindert, és reméljük hasznos eszköznek tartja háza vagy munkahelye biztonságában. Bár a ZoneMinder egy szabad, nyílt forráskódú termék és az is marad, a fejlesztése pénzbe kerül. Ha van lehetősége támogatni a jövőbeni fejlesztéseket és az új funkciókat kérem, tegye meg. A támogatás teljesen önkéntes, de nagyon megbecsült és mértéke is tetszőleges.

Ha támogatni szertne, kérem, válasszon az alábbi lehetőségekből vagy látogassa meg a https://zoneminder.com/donate/ oldalt.

Köszönjük, hogy használja a ZoneMinder-t és ne felejtse el meglátogatni a fórumokat a ZoneMinder.com oldalon támogatásért és ötletekért, hogy a jövőben is még jobban ki tudja használni a ZoneMinder lehetőségeit.', 'DonateRemindDay' => 'Nem most, figyelmeztessen egy nap múlva', 'DonateRemindHour' => 'Nem most, figyelmeztessen egy óra múlva', 'DonateRemindMonth' => 'Nem most, figyelmeztessen egy hónap múlva', diff --git a/web/lang/it_it.php b/web/lang/it_it.php index 9049b3c2f..cf2dbab8d 100644 --- a/web/lang/it_it.php +++ b/web/lang/it_it.php @@ -294,7 +294,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'Donate' => 'Donate,per favore', 'DonateAlready' => 'No, ho gia donato... ', - 'DonateEnticement' => 'Stai usando ZoneMinder da un po\' di tempo e spero che tu lo stia trovando utile per la sicurezza di casa tua o del tuo posto di lavoro..Anche se ZoneMinder e\' distribuito liberamente come software libero,costa soldi sia svilupparlo che supportarlo. Se preferisci che questo software continui ad avere supporto e sviluppo in futuro allora considera l\idea di fare una piccola donazione. Donare e\' ovviamente opzionale, ma apprezzato e puoi donare quanto vuoi,quel poco o tanto che tu desideri.

Se hai voglia per cortesia seleziona l\'opzione sotto o punta il tuo browser a http://www.zoneminder.com/donate.html .

Grazie per usare ZoneMinder e non dimenticare di visitare il forum in ZoneMinder.com se cerchi supporto o hai suggerimenti riguardo a come rendere migliore Zoneminder.', + 'DonateEnticement' => 'Stai usando ZoneMinder da un po\' di tempo e spero che tu lo stia trovando utile per la sicurezza di casa tua o del tuo posto di lavoro..Anche se ZoneMinder e\' distribuito liberamente come software libero,costa soldi sia svilupparlo che supportarlo. Se preferisci che questo software continui ad avere supporto e sviluppo in futuro allora considera l\idea di fare una piccola donazione. Donare e\' ovviamente opzionale, ma apprezzato e puoi donare quanto vuoi,quel poco o tanto che tu desideri.

Se hai voglia per cortesia seleziona l\'opzione sotto o punta il tuo browser a https://zoneminder.com/donate/ .

Grazie per usare ZoneMinder e non dimenticare di visitare il forum in ZoneMinder.com se cerchi supporto o hai suggerimenti riguardo a come rendere migliore Zoneminder.', 'DonateRemindDay' => 'Non ancora, ricordamelo ancora tra 1 giorno', 'DonateRemindHour' => 'Non ancora, ricordamelo ancora tra 1 ora', 'DonateRemindMonth' => 'Non ancora, ricordamelo ancora tra 1 mese', diff --git a/web/lang/ja_jp.php b/web/lang/ja_jp.php index b34c9f3f0..55b13ae0a 100644 --- a/web/lang/ja_jp.php +++ b/web/lang/ja_jp.php @@ -290,7 +290,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'Donate' => 'Please Donate', 'DonateAlready' => 'No, I\'ve already donated', - 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', + 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', 'DonateRemindDay' => 'Not yet, remind again in 1 day', 'DonateRemindHour' => 'Not yet, remind again in 1 hour', 'DonateRemindMonth' => 'Not yet, remind again in 1 month', diff --git a/web/lang/nl_nl.php b/web/lang/nl_nl.php index a2003b1c3..be741a6ac 100644 --- a/web/lang/nl_nl.php +++ b/web/lang/nl_nl.php @@ -290,7 +290,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', // Added - 2015-04-18 'Donate' => 'Geef a.u.b. een donatie', 'DonateAlready' => 'Nee, ik heb al gedoneerd', - 'DonateEnticement' => 'U gebruikt ZoneMinder nu voor een geruime tijd, hopelijk vindt u het een nuttige toevoeging voor uw huis- of werkplekbeveiliging. Natuurlijk is en blijft ZoneMinder gratis en open source software, maar het kost geld om te ontwikkelen, ondersteunen, en te onderhouden. Wij vragen u dan ook om er over na te denken een donatie te doen om zo de ontwikkeling van ZoneMinder te ondersteunen. Natuurlijk bent u hier vrij in, en elke donatie hoe klein dan ook wordt erg gewaardeerd.

Als u wilt doneren geef dat hieronder dan aan of ga naar http://www.zoneminder.com/donate.html in uw browser.

Bedankt voor het gebruiken van ZoneMinder en vergeet niet om ons forum op ZoneMinder.com te bezoeken voor ondersteuning of suggesties waarmee uw ZoneMinder beleving nog beter wordt.', + 'DonateEnticement' => 'U gebruikt ZoneMinder nu voor een geruime tijd, hopelijk vindt u het een nuttige toevoeging voor uw huis- of werkplekbeveiliging. Natuurlijk is en blijft ZoneMinder gratis en open source software, maar het kost geld om te ontwikkelen, ondersteunen, en te onderhouden. Wij vragen u dan ook om er over na te denken een donatie te doen om zo de ontwikkeling van ZoneMinder te ondersteunen. Natuurlijk bent u hier vrij in, en elke donatie hoe klein dan ook wordt erg gewaardeerd.

Als u wilt doneren geef dat hieronder dan aan of ga naar https://zoneminder.com/donate/ in uw browser.

Bedankt voor het gebruiken van ZoneMinder en vergeet niet om ons forum op ZoneMinder.com te bezoeken voor ondersteuning of suggesties waarmee uw ZoneMinder beleving nog beter wordt.', 'DonateRemindDay' => 'Nu niet, herinner mij over 1 dag hieraan', 'DonateRemindHour' => 'Nu niet, herinner mij over een uur hieraan', 'DonateRemindMonth' => 'Nu niet, herinner mij over een maand hieraan', diff --git a/web/lang/pl_pl.php b/web/lang/pl_pl.php index d41242317..0b36af11b 100644 --- a/web/lang/pl_pl.php +++ b/web/lang/pl_pl.php @@ -304,7 +304,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'Donate' => 'Please Donate', 'DonateAlready' => 'No, I\'ve already donated', - 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', + 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', 'DonateRemindDay' => 'Not yet, remind again in 1 day', 'DonateRemindHour' => 'Not yet, remind again in 1 hour', 'DonateRemindMonth' => 'Not yet, remind again in 1 month', diff --git a/web/lang/pt_br.php b/web/lang/pt_br.php index 3d5a2cf83..80ded9e3d 100644 --- a/web/lang/pt_br.php +++ b/web/lang/pt_br.php @@ -229,7 +229,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'Donate' => 'Please Donate', 'DonateAlready' => 'No, I\'ve already donated', - 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', + 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', 'DonateRemindDay' => 'Not yet, remind again in 1 day', 'DonateRemindHour' => 'Not yet, remind again in 1 hour', 'DonateRemindMonth' => 'Not yet, remind again in 1 month', diff --git a/web/lang/ro_ro.php b/web/lang/ro_ro.php index 97e07b836..20b2bf647 100644 --- a/web/lang/ro_ro.php +++ b/web/lang/ro_ro.php @@ -260,7 +260,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'Donate' => 'Please Donate', 'DonateAlready' => 'No, I\'ve already donated', - 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', + 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', 'DonateRemindDay' => 'Not yet, remind again in 1 day', 'DonateRemindHour' => 'Not yet, remind again in 1 hour', 'DonateRemindMonth' => 'Not yet, remind again in 1 month', diff --git a/web/lang/se_se.php b/web/lang/se_se.php index 4245ae974..53dd25178 100644 --- a/web/lang/se_se.php +++ b/web/lang/se_se.php @@ -290,7 +290,7 @@ $SLANG = array( 'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'Donate' => 'Var vänlig och donera', 'DonateAlready' => 'Nej, Jag har redan donerat', - 'DonateEnticement' => 'Du har kört ZoneMinder ett tag nu och förhoppningsvis har du sett att det fungerar bra hemma eller på ditt företag. Även om ZoneMinder är, och kommer att vara, fri programvara och öppen kallkod, så kostar det pengar att utveckla och underhålla. Om du vill hjälpa till med framtida utveckling och nya funktioner så var vanlig och bidrag med en slant. Bidragen är naturligtvis en option men mycket uppskattade och du kan bidra med precis hur mycket du vill.

Om du vill ge ett bidrag väljer du nedan eller surfar till http://www.zoneminder.com/donate.html.

Tack för att du använder ZoneMinder, glöm inte att besöka forumen på ZoneMinder.com för support och förslag om hur du får din ZoneMinder att fungera lite bättre.', + 'DonateEnticement' => 'Du har kört ZoneMinder ett tag nu och förhoppningsvis har du sett att det fungerar bra hemma eller på ditt företag. Även om ZoneMinder är, och kommer att vara, fri programvara och öppen kallkod, så kostar det pengar att utveckla och underhålla. Om du vill hjälpa till med framtida utveckling och nya funktioner så var vanlig och bidrag med en slant. Bidragen är naturligtvis en option men mycket uppskattade och du kan bidra med precis hur mycket du vill.

Om du vill ge ett bidrag väljer du nedan eller surfar till https://zoneminder.com/donate/.

Tack för att du använder ZoneMinder, glöm inte att besöka forumen på ZoneMinder.com för support och förslag om hur du får din ZoneMinder att fungera lite bättre.', 'DonateRemindDay' => 'Inte än, påminn om 1 dag', 'DonateRemindHour' => 'Inte än, påminn om en 1 timme', 'DonateRemindMonth' => 'Inte än, påminn om 1 månad', From ea7c38ceff7b1231c416cd45f96657c979020c98 Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Wed, 24 Apr 2019 13:55:57 -0400 Subject: [PATCH 07/45] Alarm cause fix (#2582) * move alarm cause code to when the alarm flag is set * formatting * added temp info log * char* not string in log * merged alarm clause into info message about alarm * add a comma only if there are more active zones * JB tweak to slightly optimize leading comma processing --- src/zm_monitor.cpp | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 09aaeeb87..2987d08f2 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1547,9 +1547,19 @@ bool Monitor::Analyse() { if ( score ) { if ( state == IDLE || state == TAPE || state == PREALARM ) { if ( (!pre_event_count) || (Event::PreAlarmCount() >= alarm_frame_count) ) { - Info("%s: %03d - Gone into alarm state PreAlarmCount: %u > AlarmFrameCount:%u", - name, image_count, Event::PreAlarmCount(), alarm_frame_count); shared_data->state = state = ALARM; + // lets construct alarm cause. It will contain cause + names of zones alarmed + std::string alarm_cause=""; + for ( int i=0; i < n_zones; i++) { + if (zones[i]->Alarmed()) { + alarm_cause = alarm_cause+ ","+ std::string(zones[i]->Label()); + } + } + if (!alarm_cause.empty()) alarm_cause[0]=' '; + alarm_cause = cause+alarm_cause; + strncpy(shared_data->alarm_cause,alarm_cause.c_str(), sizeof(shared_data->alarm_cause)-1); + Info("%s: %03d - Gone into alarm state PreAlarmCount: %u > AlarmFrameCount:%u Cause:%s", + name, image_count, Event::PreAlarmCount(), alarm_frame_count, shared_data->alarm_cause); if ( signal_change || (function != MOCORD && state != ALERT) ) { int pre_index; int pre_event_images = pre_event_count; @@ -1594,18 +1604,7 @@ bool Monitor::Analyse() { event = new Event(this, *(image_buffer[pre_index].timestamp), cause, noteSetMap); } shared_data->last_event = event->Id(); - // lets construct alarm cause. It will contain cause + names of zones alarmed - std::string alarm_cause=""; - for ( int i=0; i < n_zones; i++) { - if (zones[i]->Alarmed()) { - alarm_cause += std::string(zones[i]->Label()); - if (i < n_zones-1) { - alarm_cause +=","; - } - } - } - alarm_cause = cause+" "+alarm_cause; - strncpy(shared_data->alarm_cause,alarm_cause.c_str(), sizeof(shared_data->alarm_cause)-1); + //set up video store data snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile()); video_store_data->recording = event->StartTime(); From 18285e1b94a2b3ce5eeb515ff26dd588c358c5a3 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 25 Apr 2019 18:45:43 -0400 Subject: [PATCH 08/45] fix using in_frame->nb_samples instead of out_frame->nb_samples in resample fifo. --- src/zm_videostore.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index a8f3e3041..0d3e22b4e 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -1005,7 +1005,7 @@ int VideoStore::writeAudioFramePacket(AVPacket *ipkt) { zm_dump_frame(in_frame, "In frame from decode"); - if ( ! resample_audio() ) { + if ( !resample_audio() ) { //av_frame_unref(in_frame); return 0; } @@ -1172,7 +1172,7 @@ int VideoStore::writeAudioFramePacket(AVPacket *ipkt) { } // end int VideoStore::writeAudioFramePacket(AVPacket *ipkt) int VideoStore::resample_audio() { - // Resample the in into the audioSampleBuffer until we process the whole + // Resample the in_frame into the audioSampleBuffer until we process the whole // decoded data. Note: pts does not survive resampling or converting #if defined(HAVE_LIBSWRESAMPLE) || defined(HAVE_LIBAVRESAMPLE) #if defined(HAVE_LIBSWRESAMPLE) @@ -1192,8 +1192,8 @@ int VideoStore::resample_audio() { } /** Store the new samples in the FIFO buffer. */ ret = av_audio_fifo_write(fifo, (void **)out_frame->data, out_frame->nb_samples); - if ( ret < in_frame->nb_samples ) { - Error("Could not write data to FIFO on %d written", ret); + if ( ret < out_frame->nb_samples ) { + Error("Could not write data to FIFO on %d written, expecting %d", ret, out_frame->nb_samples); return 0; } From 6a250d61e3dacd889021b7c3e164eed4119d029a Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 26 Apr 2019 10:25:32 -0400 Subject: [PATCH 09/45] cache_bust logger.js and overlay.js --- web/skins/classic/includes/functions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/includes/functions.php b/web/skins/classic/includes/functions.php index fa57411cd..2d71b2397 100644 --- a/web/skins/classic/includes/functions.php +++ b/web/skins/classic/includes/functions.php @@ -173,12 +173,12 @@ echo output_link_if_exists( array( - + - + Date: Fri, 26 Apr 2019 10:26:16 -0400 Subject: [PATCH 10/45] dsiable form submit on enter on the monitor view --- web/skins/classic/views/js/monitor.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/views/js/monitor.js b/web/skins/classic/views/js/monitor.js index e0ca4f1d4..483f1b23b 100644 --- a/web/skins/classic/views/js/monitor.js +++ b/web/skins/classic/views/js/monitor.js @@ -64,6 +64,15 @@ function initPage() { return false; }; }); -} -window.addEventListener( 'DOMContentLoaded', initPage ); + // Disable form submit on enter + $j('#contentForm').on('keyup keypress', function(e) { + var keyCode = e.keyCode || e.which; + if (keyCode === 13) { + e.preventDefault(); + return false; + } + }); +} // end function initPage() + +window.addEventListener('DOMContentLoaded', initPage); From a0dbb70af6d04f2cd824975de32ad6987dc17b57 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 26 Apr 2019 10:40:11 -0400 Subject: [PATCH 11/45] filter the form submit on enter to only affect input elements, not textareas --- web/skins/classic/views/js/monitor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/js/monitor.js b/web/skins/classic/views/js/monitor.js index 483f1b23b..ff57b774f 100644 --- a/web/skins/classic/views/js/monitor.js +++ b/web/skins/classic/views/js/monitor.js @@ -66,7 +66,7 @@ function initPage() { }); // Disable form submit on enter - $j('#contentForm').on('keyup keypress', function(e) { + $j('#contentForm input').on('keyup keypress', function(e) { var keyCode = e.keyCode || e.which; if (keyCode === 13) { e.preventDefault(); From 28269eccc34a0d49911dfa82fe9ccad903cf8e36 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 28 Apr 2019 12:05:32 -0400 Subject: [PATCH 12/45] Fix Remote RTSP Method on newer ffmpeg --- src/zm_remote_camera_rtsp.cpp | 27 ++++- src/zm_remote_camera_rtsp.h | 3 +- src/zm_rtp_ctrl.cpp | 156 ++++++++++--------------- src/zm_rtp_ctrl.h | 39 +++---- src/zm_rtp_source.cpp | 160 +++++++++---------------- src/zm_rtsp.cpp | 213 ++++++++++++++++------------------ src/zm_sdp.cpp | 197 +++++++++++++++---------------- 7 files changed, 350 insertions(+), 445 deletions(-) diff --git a/src/zm_remote_camera_rtsp.cpp b/src/zm_remote_camera_rtsp.cpp index 7e2143203..1f94f4849 100644 --- a/src/zm_remote_camera_rtsp.cpp +++ b/src/zm_remote_camera_rtsp.cpp @@ -28,7 +28,22 @@ #include #include -RemoteCameraRtsp::RemoteCameraRtsp( unsigned int p_monitor_id, const std::string &p_method, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, bool p_rtsp_describe, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) : +RemoteCameraRtsp::RemoteCameraRtsp( + unsigned int p_monitor_id, + const std::string &p_method, + const std::string &p_host, + const std::string &p_port, + const std::string &p_path, + int p_width, + int p_height, + bool p_rtsp_describe, + int p_colours, + int p_brightness, + int p_contrast, + int p_hue, + int p_colour, + bool p_capture, + bool p_record_audio ) : RemoteCamera( p_monitor_id, "rtsp", p_host, p_port, p_path, p_width, p_height, p_colours, p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio ), rtsp_describe( p_rtsp_describe ), rtspThread( 0 ) @@ -63,13 +78,13 @@ RemoteCameraRtsp::RemoteCameraRtsp( unsigned int p_monitor_id, const std::string mConvertContext = NULL; #endif /* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */ - if(colours == ZM_COLOUR_RGB32) { + if ( colours == ZM_COLOUR_RGB32 ) { subpixelorder = ZM_SUBPIX_ORDER_RGBA; imagePixFormat = AV_PIX_FMT_RGBA; - } else if(colours == ZM_COLOUR_RGB24) { + } else if ( colours == ZM_COLOUR_RGB24 ) { subpixelorder = ZM_SUBPIX_ORDER_RGB; imagePixFormat = AV_PIX_FMT_RGB24; - } else if(colours == ZM_COLOUR_GRAY8) { + } else if ( colours == ZM_COLOUR_GRAY8 ) { subpixelorder = ZM_SUBPIX_ORDER_NONE; imagePixFormat = AV_PIX_FMT_GRAY8; } else { @@ -169,7 +184,7 @@ int RemoteCameraRtsp::PrimeCapture() { } else { Debug(2, "Have another video stream." ); } - } + } else #if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0)) if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO ) #else @@ -181,6 +196,8 @@ int RemoteCameraRtsp::PrimeCapture() { } else { Debug(2, "Have another audio stream." ); } + } else { + Debug(1, "Have unknown codec type in stream %d : %d", i, mFormatContext->streams[i]->codec->codec_type); } } // end foreach stream diff --git a/src/zm_remote_camera_rtsp.h b/src/zm_remote_camera_rtsp.h index b3e392a77..5fa5a0778 100644 --- a/src/zm_remote_camera_rtsp.h +++ b/src/zm_remote_camera_rtsp.h @@ -34,8 +34,7 @@ // accessed over a network connection using rtsp protocol // (Real Time Streaming Protocol) // -class RemoteCameraRtsp : public RemoteCamera -{ +class RemoteCameraRtsp : public RemoteCamera { protected: struct sockaddr_in rtsp_sa; struct sockaddr_in rtcp_sa; diff --git a/src/zm_rtp_ctrl.cpp b/src/zm_rtp_ctrl.cpp index b586c979f..bc6725c5d 100644 --- a/src/zm_rtp_ctrl.cpp +++ b/src/zm_rtp_ctrl.cpp @@ -28,12 +28,12 @@ #include -RtpCtrlThread::RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource ) : mRtspThread( rtspThread ), mRtpSource( rtpSource ), mStop( false ) +RtpCtrlThread::RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource ) + : mRtspThread( rtspThread ), mRtpSource( rtpSource ), mStop( false ) { } -int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen ) -{ +int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen ) { const RtcpPacket *rtcpPacket; rtcpPacket = (RtcpPacket *)packet; @@ -48,33 +48,24 @@ int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen ) int pt = rtcpPacket->header.pt; int len = ntohs(rtcpPacket->header.lenN); - Debug( 5, "RTCP Ver: %d", ver ); - Debug( 5, "RTCP Count: %d", count ); - Debug( 5, "RTCP Pt: %d", pt ); - Debug( 5, "RTCP len: %d", len ); + Debug( 5, "RTCP Ver: %d Count: %d Pt: %d len: %d", ver, count, pt, len); - switch( pt ) - { + switch( pt ) { case RTCP_SR : { uint32_t ssrc = ntohl(rtcpPacket->body.sr.ssrcN); Debug( 5, "RTCP Got SR (%x)", ssrc ); - if ( mRtpSource.getSsrc() ) - { - if ( ssrc != mRtpSource.getSsrc() ) - { + if ( mRtpSource.getSsrc() ) { + if ( ssrc != mRtpSource.getSsrc() ) { Warning( "Discarding packet for unrecognised ssrc %x", ssrc ); return( -1 ); } - } - else if ( ssrc ) - { + } else if ( ssrc ) { mRtpSource.setSsrc( ssrc ); } - if ( len > 1 ) - { + if ( len > 1 ) { //printf( "NTPts:%d.%d, RTPts:%d\n", $ntptsmsb, $ntptslsb, $rtpts ); uint16_t ntptsmsb = ntohl(rtcpPacket->body.sr.ntpSecN); uint16_t ntptslsb = ntohl(rtcpPacket->body.sr.ntpFracN); @@ -89,25 +80,21 @@ int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen ) case RTCP_SDES : { ssize_t contentLen = packetLen - sizeof(rtcpPacket->header); - while ( contentLen ) - { + while ( contentLen ) { Debug( 5, "RTCP CL: %zd", contentLen ); uint32_t ssrc = ntohl(rtcpPacket->body.sdes.srcN); Debug( 5, "RTCP Got SDES (%x), %d items", ssrc, count ); - if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) ) - { + if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) ) { Warning( "Discarding packet for unrecognised ssrc %x", ssrc ); return( -1 ); } unsigned char *sdesPtr = (unsigned char *)&rtcpPacket->body.sdes.item; - for ( int i = 0; i < count; i++ ) - { + for ( int i = 0; i < count; i++ ) { RtcpSdesItem *item = (RtcpSdesItem *)sdesPtr; Debug( 5, "RTCP Item length %d", item->len ); - switch( item->type ) - { + switch( item->type ) { case RTCP_SDES_CNAME : { std::string cname( item->data, item->len ); @@ -123,50 +110,39 @@ int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen ) case RTCP_SDES_NOTE : case RTCP_SDES_PRIV : default : - { Error( "Received unexpected SDES item type %d, ignoring", item->type ); - return( -1 ); - } + return -1; } int paddedLen = 4+2+item->len+1; // Add null byte paddedLen = (((paddedLen-1)/4)+1)*4; // Round to nearest multiple of 4 - Debug( 5, "RTCP PL:%d", paddedLen ); + Debug(5, "RTCP PL:%d", paddedLen); sdesPtr += paddedLen; contentLen = ( paddedLen <= contentLen ) ? ( contentLen - paddedLen ) : 0; } - } + } // end whiel contentLen break; } case RTCP_BYE : - { - Debug( 5, "RTCP Got BYE" ); + Debug(5, "RTCP Got BYE"); mStop = true; break; - } case RTCP_APP : - { // Ignoring as per RFC 3550 - Debug( 5, "Received RTCP_APP packet, ignoring."); + Debug(5, "Received RTCP_APP packet, ignoring."); break; - } case RTCP_RR : - { - Error( "Received RTCP_RR packet." ); - return( -1 ); - } + Error("Received RTCP_RR packet."); + return -1; default : - { // Ignore unknown packet types. Some cameras do this by design. - Debug( 5, "Received unexpected packet type %d, ignoring", pt ); + Debug(5, "Received unexpected packet type %d, ignoring", pt); break; - } } consumed = sizeof(uint32_t)*(len+1); - return( consumed ); + return consumed; } -int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen ) -{ +int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen ) { RtcpPacket *rtcpPacket = (RtcpPacket *)packet; int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.rr)+sizeof(rtcpPacket->body.rr.rr[0]); @@ -180,11 +156,13 @@ int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen ) mRtpSource.updateRtcpStats(); - Debug( 5, "Ssrc = %d", mRtspThread.getSsrc()+1 ); - Debug( 5, "Ssrc_1 = %d", mRtpSource.getSsrc() ); - Debug( 5, "Last Seq = %d", mRtpSource.getMaxSeq() ); - Debug( 5, "Jitter = %d", mRtpSource.getJitter() ); - Debug( 5, "Last SR = %d", mRtpSource.getLastSrTimestamp() ); + Debug(5, "Ssrc = %d Ssrc_1 = %d Last Seq = %d Jitter = %d Last SR = %d", + mRtspThread.getSsrc()+1, + mRtpSource.getSsrc(), + mRtpSource.getMaxSeq(), + mRtpSource.getJitter(), + mRtpSource.getLastSrTimestamp() + ); rtcpPacket->body.rr.ssrcN = htonl(mRtspThread.getSsrc()+1); rtcpPacket->body.rr.rr[0].ssrcN = htonl(mRtpSource.getSsrc()); @@ -195,11 +173,10 @@ int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen ) rtcpPacket->body.rr.rr[0].lsrN = htonl(mRtpSource.getLastSrTimestamp()); rtcpPacket->body.rr.rr[0].dlsrN = 0; - return( wordLen*sizeof(uint32_t) ); -} + return wordLen*sizeof(uint32_t); +} // end RtpCtrlThread::generateRr -int RtpCtrlThread::generateSdes( const unsigned char *packet, ssize_t packetLen ) -{ +int RtpCtrlThread::generateSdes( const unsigned char *packet, ssize_t packetLen ) { RtcpPacket *rtcpPacket = (RtcpPacket *)packet; const std::string &cname = mRtpSource.getCname(); @@ -218,11 +195,10 @@ int RtpCtrlThread::generateSdes( const unsigned char *packet, ssize_t packetLen rtcpPacket->body.sdes.item[0].len = cname.size(); memcpy( rtcpPacket->body.sdes.item[0].data, cname.data(), cname.size() ); - return( wordLen*sizeof(uint32_t) ); -} + return wordLen*sizeof(uint32_t); +} // end RtpCtrlThread::generateSdes -int RtpCtrlThread::generateBye( const unsigned char *packet, ssize_t packetLen ) -{ +int RtpCtrlThread::generateBye( const unsigned char *packet, ssize_t packetLen ) { RtcpPacket *rtcpPacket = (RtcpPacket *)packet; int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.bye)+sizeof(rtcpPacket->body.bye.srcN[0]); @@ -236,11 +212,10 @@ int RtpCtrlThread::generateBye( const unsigned char *packet, ssize_t packetLen ) rtcpPacket->body.bye.srcN[0] = htonl(mRtpSource.getSsrc()); - return( wordLen*sizeof(uint32_t) ); -} + return wordLen*sizeof(uint32_t); +} // end RtpCtrolThread::generateBye -int RtpCtrlThread::recvPackets( unsigned char *buffer, ssize_t nBytes ) -{ +int RtpCtrlThread::recvPackets( unsigned char *buffer, ssize_t nBytes ) { unsigned char *bufferPtr = buffer; // u_int32 len; /* length of compound RTCP packet in words */ @@ -259,33 +234,28 @@ int RtpCtrlThread::recvPackets( unsigned char *buffer, ssize_t nBytes ) // /* something wrong with packet format */ // } - while ( nBytes > 0 ) - { + while ( nBytes > 0 ) { int consumed = recvPacket( bufferPtr, nBytes ); if ( consumed <= 0 ) break; bufferPtr += consumed; nBytes -= consumed; } - return( nBytes ); + return nBytes; } -int RtpCtrlThread::run() -{ +int RtpCtrlThread::run() { Debug( 2, "Starting control thread %x on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalCtrlPort() ); SockAddrInet localAddr, remoteAddr; bool sendReports; UdpInetSocket rtpCtrlServer; - if ( mRtpSource.getLocalHost() != "" ) - { + if ( mRtpSource.getLocalHost() != "" ) { if ( !rtpCtrlServer.bind( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ) ) Fatal( "Failed to bind RTCP server" ); sendReports = false; Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ); - } - else - { + } else { if ( !rtpCtrlServer.bind( mRtspThread.getAddressFamily() == AF_INET6 ? "::" : "0.0.0.0", mRtpSource.getLocalCtrlPort() ) ) Fatal( "Failed to bind RTCP server" ); Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ); @@ -309,17 +279,17 @@ int RtpCtrlThread::run() time_t now = time(NULL); Select::CommsList readable = select.getReadable(); - if ( readable.size() == 0 ) - { + if ( readable.size() == 0 ) { if ( ! timeout ) { // With this code here, we will send an SDES and RR packet every 10 seconds ssize_t nBytes; unsigned char *bufferPtr = buffer; bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) ); bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) ); - Debug( 3, "Preventing timeout by sending %zd bytes on sd %d. Time since last receive: %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc(), ( now-last_receive) ); - if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 ) - Error( "Unable to send: %s", strerror( errno ) ); + Debug( 3, "Preventing timeout by sending %zd bytes on sd %d. Time since last receive: %d", + bufferPtr-buffer, rtpCtrlServer.getWriteDesc(), ( now-last_receive) ); + if ( (nBytes = rtpCtrlServer.send(buffer, bufferPtr-buffer)) < 0 ) + Error("Unable to send: %s", strerror(errno)); timeout = true; continue; } else { @@ -332,25 +302,21 @@ int RtpCtrlThread::run() timeout = false; last_receive = time(NULL); } - for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); ++iter ) - { - if ( UdpInetSocket *socket = dynamic_cast(*iter) ) - { + for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); ++iter ) { + if ( UdpInetSocket *socket = dynamic_cast(*iter) ) { ssize_t nBytes = socket->recv( buffer, sizeof(buffer) ); Debug( 4, "Read %zd bytes on sd %d", nBytes, socket->getReadDesc() ); - if ( nBytes ) - { + if ( nBytes ) { recvPackets( buffer, nBytes ); - if ( sendReports ) - { + if ( sendReports ) { unsigned char *bufferPtr = buffer; bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) ); bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) ); - Debug( 3, "Sending %zd bytes on sd %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc() ); + Debug(3, "Sending %zd bytes on sd %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc()); if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 ) - Error( "Unable to send: %s", strerror( errno ) ); + Error("Unable to send: %s", strerror(errno)); //Debug( 4, "Sent %d bytes on sd %d", nBytes, rtpCtrlServer.getWriteDesc() ); } } else { @@ -358,16 +324,14 @@ int RtpCtrlThread::run() mStop = true; break; } - } - else - { - Panic( "Barfed" ); - } - } + } else { + Panic("Barfed"); + } // end if socket + } // end foeach comms iterator } rtpCtrlServer.close(); mRtspThread.stop(); - return( 0 ); + return 0; } #endif // HAVE_LIBAVFORMAT diff --git a/src/zm_rtp_ctrl.h b/src/zm_rtp_ctrl.h index 6d8f3024c..9e5306f92 100644 --- a/src/zm_rtp_ctrl.h +++ b/src/zm_rtp_ctrl.h @@ -34,13 +34,11 @@ class RtspThread; class RtpSource; -class RtpCtrlThread : public Thread -{ +class RtpCtrlThread : public Thread { friend class RtspThread; private: - typedef enum - { + typedef enum { RTCP_SR = 200, RTCP_RR = 201, RTCP_SDES = 202, @@ -48,8 +46,7 @@ private: RTCP_APP = 204 } RtcpType; - typedef enum - { + typedef enum { RTCP_SDES_END = 0, RTCP_SDES_CNAME = 1, RTCP_SDES_NAME = 2, @@ -61,8 +58,7 @@ private: RTCP_SDES_PRIV = 8 } RtcpSdesType; - struct RtcpCommonHeader - { + struct RtcpCommonHeader { uint8_t count:5; // varies by packet type uint8_t p:1; // padding flag uint8_t version:2; // protocol version @@ -71,8 +67,7 @@ private: }; // Reception report block - struct RtcpRr - { + struct RtcpRr { uint32_t ssrcN; // data source being reported int32_t lost:24; // cumul. no. pkts lost (signed!) uint32_t fraction:8; // fraction lost since last SR/RR @@ -83,22 +78,18 @@ private: }; // SDES item - struct RtcpSdesItem - { + struct RtcpSdesItem { uint8_t type; // type of item (rtcp_sdes_type_t) uint8_t len; // length of item (in octets) char data[]; // text, not null-terminated }; // RTCP packet - struct RtcpPacket - { + struct RtcpPacket { RtcpCommonHeader header; // common header - union - { + union { // Sender Report (SR) - struct Sr - { + struct Sr { uint32_t ssrcN; // sender generating this report, network order uint32_t ntpSecN; // NTP timestamp, network order uint32_t ntpFracN; @@ -109,22 +100,19 @@ private: } sr; // Reception Report (RR) - struct Rr - { + struct Rr { uint32_t ssrcN; // receiver generating this report RtcpRr rr[]; // variable-length list } rr; // source description (SDES) - struct Sdes - { + struct Sdes { uint32_t srcN; // first SSRC/CSRC RtcpSdesItem item[]; // list of SDES items } sdes; // BYE - struct - { + struct { uint32_t srcN[]; // list of sources // can't express trailing text for reason (what does this mean? it's not even english!) } bye; @@ -148,8 +136,7 @@ private: public: RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource ); - void stop() - { + void stop() { mStop = true; } }; diff --git a/src/zm_rtp_source.cpp b/src/zm_rtp_source.cpp index 0cd838902..02e8ed0c5 100644 --- a/src/zm_rtp_source.cpp +++ b/src/zm_rtp_source.cpp @@ -26,7 +26,17 @@ #if HAVE_LIBAVCODEC -RtpSource::RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId ) : +RtpSource::RtpSource( + int id, + const std::string &localHost, + int localPortBase, + const std::string &remoteHost, + int remotePortBase, + uint32_t ssrc, + uint16_t seq, + uint32_t rtpClock, + uint32_t rtpTime, + _AVCODECID codecId ) : mId( id ), mSsrc( ssrc ), mLocalHost( localHost ), @@ -65,13 +75,12 @@ RtpSource::RtpSource( int id, const std::string &localHost, int localPortBase, c mLastSrTimeNtp = tvZero(); mLastSrTimeRtp = 0; - if(mCodecId != AV_CODEC_ID_H264 && mCodecId != AV_CODEC_ID_MPEG4) - Warning( "The device is using a codec that may not be supported. Do not be surprised if things don't work." ); + if ( mCodecId != AV_CODEC_ID_H264 && mCodecId != AV_CODEC_ID_MPEG4 ) + Warning("The device is using a codec (%d) that may not be supported. Do not be surprised if things don't work.", mCodecId); } -void RtpSource::init( uint16_t seq ) -{ - Debug( 3, "Initialising sequence" ); +void RtpSource::init( uint16_t seq ) { + Debug(3, "Initialising sequence"); mBaseSeq = seq; mMaxSeq = seq; mBadSeq = RTP_SEQ_MOD + 1; // so seq == mBadSeq is false @@ -84,77 +93,58 @@ void RtpSource::init( uint16_t seq ) mTransit = 0; } -bool RtpSource::updateSeq( uint16_t seq ) -{ +bool RtpSource::updateSeq( uint16_t seq ) { uint16_t uDelta = seq - mMaxSeq; // Source is not valid until MIN_SEQUENTIAL packets with // sequential sequence numbers have been received. Debug( 5, "Seq: %d", seq ); - if ( mProbation) - { + if ( mProbation) { // packet is in sequence - if ( seq == mMaxSeq + 1) - { + if ( seq == mMaxSeq + 1) { Debug( 3, "Sequence in probation %d, in sequence", mProbation ); mProbation--; mMaxSeq = seq; - if ( mProbation == 0 ) - { + if ( mProbation == 0 ) { init( seq ); mReceivedPackets++; return( true ); } - } - else - { + } else { Warning( "Sequence in probation %d, out of sequence", mProbation ); mProbation = MIN_SEQUENTIAL - 1; mMaxSeq = seq; return( false ); } return( true ); - } - else if ( uDelta < MAX_DROPOUT ) - { - if ( uDelta == 1 ) - { + } else if ( uDelta < MAX_DROPOUT ) { + if ( uDelta == 1 ) { Debug( 4, "Packet in sequence, gap %d", uDelta ); - } - else - { + } else { Warning( "Packet in sequence, gap %d", uDelta ); } // in order, with permissible gap - if ( seq < mMaxSeq ) - { + if ( seq < mMaxSeq ) { // Sequence number wrapped - count another 64K cycle. mCycles += RTP_SEQ_MOD; } mMaxSeq = seq; - } - else if ( uDelta <= RTP_SEQ_MOD - MAX_MISORDER ) - { + } else if ( uDelta <= RTP_SEQ_MOD - MAX_MISORDER ) { Warning( "Packet out of sequence, gap %d", uDelta ); // the sequence number made a very large jump - if ( seq == mBadSeq ) - { + if ( seq == mBadSeq ) { Debug( 3, "Restarting sequence" ); // Two sequential packets -- assume that the other side // restarted without telling us so just re-sync // (i.e., pretend this was the first packet). init( seq ); - } - else - { + } else { mBadSeq = (seq + 1) & (RTP_SEQ_MOD-1); return( false ); } - } - else - { + } else { Warning( "Packet duplicate or reordered, gap %d", uDelta ); // duplicate or reordered packet return( false ); @@ -163,10 +153,8 @@ bool RtpSource::updateSeq( uint16_t seq ) return( uDelta==1?true:false ); } -void RtpSource::updateJitter( const RtpDataHeader *header ) -{ - if ( mRtpFactor > 0 ) - { +void RtpSource::updateJitter( const RtpDataHeader *header ) { + if ( mRtpFactor > 0 ) { Debug( 5, "Delta rtp = %.6f", tvDiffSec( mBaseTimeReal ) ); uint32_t localTimeRtp = mBaseTimeRtp + uint32_t( tvDiffSec( mBaseTimeReal ) * mRtpFactor ); Debug( 5, "Local RTP time = %x", localTimeRtp ); @@ -174,8 +162,7 @@ void RtpSource::updateJitter( const RtpDataHeader *header ) uint32_t packetTransit = localTimeRtp - ntohl(header->timestampN); Debug( 5, "Packet transit RTP time = %x", packetTransit ); - if ( mTransit > 0 ) - { + if ( mTransit > 0 ) { // Jitter int d = packetTransit - mTransit; Debug( 5, "Jitter D = %d", d ); @@ -185,28 +172,22 @@ void RtpSource::updateJitter( const RtpDataHeader *header ) mJitter += d - ((mJitter + 8) >> 4); } mTransit = packetTransit; - } - else - { + } else { mJitter = 0; } Debug( 5, "RTP Jitter: %d", mJitter ); } -void RtpSource::updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime ) -{ +void RtpSource::updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime ) { struct timeval ntpTime = tvMake( ntpTimeSecs, suseconds_t((USEC_PER_SEC*(ntpTimeFrac>>16))/(1<<16)) ); Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime ); - if ( mBaseTimeNtp.tv_sec == 0 ) - { + if ( mBaseTimeNtp.tv_sec == 0 ) { mBaseTimeReal = tvNow(); mBaseTimeNtp = ntpTime; mBaseTimeRtp = rtpTime; - } - else if ( !mRtpClock ) - { + } else if ( !mRtpClock ) { Debug( 5, "lastSrNtpTime: %ld.%06ld, rtpTime: %x", mLastSrTimeNtp.tv_sec, mLastSrTimeNtp.tv_usec, rtpTime ); Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime ); @@ -227,8 +208,7 @@ void RtpSource::updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint mLastSrTimeRtp = rtpTime; } -void RtpSource::updateRtcpStats() -{ +void RtpSource::updateRtcpStats() { uint32_t extendedMax = mCycles + mMaxSeq; mExpectedPackets = extendedMax - mBaseSeq + 1; @@ -255,8 +235,7 @@ void RtpSource::updateRtcpStats() Debug( 5, "Lost fraction = %d", mLostFraction ); } -bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen ) -{ +bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen ) { const RtpDataHeader *rtpHeader; rtpHeader = (RtpDataHeader *)packet; int rtpHeaderSize = 12 + rtpHeader->cc * 4; @@ -269,39 +248,29 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen ) // that there is no marker bit by changing the number of bits in the payload type field. bool thisM = rtpHeader->m || h264FragmentEnd; - if ( updateSeq( ntohs(rtpHeader->seqN) ) ) - { + if ( updateSeq( ntohs(rtpHeader->seqN) ) ) { Hexdump( 4, packet+rtpHeaderSize, 16 ); - if ( mFrameGood ) - { + if ( mFrameGood ) { int extraHeader = 0; - if( mCodecId == AV_CODEC_ID_H264 ) - { + if ( mCodecId == AV_CODEC_ID_H264 ) { int nalType = (packet[rtpHeaderSize] & 0x1f); Debug( 3, "Have H264 frame: nal type is %d", nalType ); - switch (nalType) - { + switch (nalType) { case 24: // STAP-A - { extraHeader = 2; break; - } case 25: // STAP-B case 26: // MTAP-16 case 27: // MTAP-24 - { extraHeader = 3; break; - } // FU-A and FU-B case 28: case 29: - { // Is this NAL the first NAL in fragmentation sequence - if ( packet[rtpHeaderSize+1] & 0x80 ) - { + if ( packet[rtpHeaderSize+1] & 0x80 ) { // Now we will form new header of frame mFrame.append( "\x0\x0\x1\x0", 4 ); // Reconstruct NAL header from FU headers @@ -311,17 +280,14 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen ) extraHeader = 2; break; - } default: - { Debug(3, "Unhandled nalType %d", nalType ); - } } // Append NAL frame start code if ( !mFrame.size() ) mFrame.append( "\x0\x0\x1", 3 ); - } + } // end if H264 mFrame.append( packet+rtpHeaderSize+extraHeader, packetLen-rtpHeaderSize-extraHeader ); } else { Debug( 3, "NOT H264 frame: type is %d", mCodecId ); @@ -329,16 +295,13 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen ) Hexdump( 4, mFrame.head(), 16 ); - if ( thisM ) - { - if ( mFrameGood ) - { + if ( thisM ) { + if ( mFrameGood ) { Debug( 3, "Got new frame %d, %d bytes", mFrameCount, mFrame.size() ); mFrameProcessed.setValueImmediate( false ); mFrameReady.updateValueSignal( true ); - if ( !mFrameProcessed.getValueImmediate() ) - { + if ( !mFrameProcessed.getValueImmediate() ) { // What is the point of this for loop? Is it just me, or will it call getUpdatedValue once or twice? Could it not be better written as // if ( ! mFrameProcessed.getUpdatedValue( 1 ) && mFrameProcessed.getUpdatedValue( 1 ) ) return false; @@ -347,45 +310,34 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen ) return( false ); } mFrameCount++; - } - else - { + } else { Warning( "Discarding incomplete frame %d, %d bytes", mFrameCount, mFrame.size() ); } mFrame.clear(); } - } - else - { - if ( mFrame.size() ) - { + } else { + if ( mFrame.size() ) { Warning( "Discarding partial frame %d, %d bytes", mFrameCount, mFrame.size() ); - } - else - { + } else { Warning( "Discarding frame %d", mFrameCount ); } mFrameGood = false; mFrame.clear(); } - if ( thisM ) - { + if ( thisM ) { mFrameGood = true; prevM = true; - } - else + } else prevM = false; updateJitter( rtpHeader ); - return( true ); + return true; } -bool RtpSource::getFrame( Buffer &buffer ) -{ +bool RtpSource::getFrame( Buffer &buffer ) { Debug( 3, "Getting frame" ); - if ( !mFrameReady.getValueImmediate() ) - { + if ( !mFrameReady.getValueImmediate() ) { // Allow for a couple of spurious returns for ( int count = 0; !mFrameReady.getUpdatedValue( 1 ); count++ ) if ( count > 1 ) @@ -395,7 +347,7 @@ bool RtpSource::getFrame( Buffer &buffer ) mFrameReady.setValueImmediate( false ); mFrameProcessed.updateValueSignal( true ); Debug( 4, "Copied %d bytes", buffer.size() ); - return( true ); + return true; } #endif // HAVE_LIBAVCODEC diff --git a/src/zm_rtsp.cpp b/src/zm_rtsp.cpp index 703328e2e..5e2858e4f 100644 --- a/src/zm_rtsp.cpp +++ b/src/zm_rtsp.cpp @@ -46,53 +46,54 @@ bool RtspThread::sendCommand( std::string message ) { message += stringtf( "CSeq: %d\r\n\r\n", ++mSeq ); Debug( 2, "Sending RTSP message: %s", message.c_str() ); if ( mMethod == RTP_RTSP_HTTP ) { - message = base64Encode( message ); - Debug( 2, "Sending encoded RTSP message: %s", message.c_str() ); - if ( mRtspSocket2.send( message.c_str(), message.size() ) != (int)message.length() ) { - Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) ); - return( false ); + message = base64Encode(message); + Debug(2, "Sending encoded RTSP message: %s", message.c_str()); + if ( mRtspSocket2.send(message.c_str(), message.size()) != (int)message.length() ) { + Error("Unable to send message '%s': %s", message.c_str(), strerror(errno)); + return false; } } else { - if ( mRtspSocket.send( message.c_str(), message.size() ) != (int)message.length() ) { - Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) ); - return( false ); + if ( mRtspSocket.send(message.c_str(), message.size()) != (int)message.length() ) { + Error("Unable to send message '%s': %s", message.c_str(), strerror(errno)); + return false; } } - return( true ); + return true; } bool RtspThread::recvResponse( std::string &response ) { if ( mRtspSocket.recv( response ) < 0 ) - Error( "Recv failed; %s", strerror(errno) ); - Debug( 2, "Received RTSP response: %s (%zd bytes)", response.c_str(), response.size() ); + Error("Recv failed; %s", strerror(errno)); + Debug(2, "Received RTSP response: %s (%zd bytes)", response.c_str(), response.size()); float respVer = 0; respCode = -1; char respText[ZM_NETWORK_BUFSIZ]; - if ( sscanf( response.c_str(), "RTSP/%f %3d %[^\r\n]\r\n", &respVer, &respCode, respText ) != 3 ) { + if ( sscanf(response.c_str(), "RTSP/%f %3d %[^\r\n]\r\n", &respVer, &respCode, respText) != 3 ) { if ( isalnum(response[0]) ) { - Error( "Response parse failure in '%s'", response.c_str() ); + Error("Response parse failure in '%s'", response.c_str()); } else { - Error( "Response parse failure, %zd bytes follow", response.size() ); + Error("Response parse failure, %zd bytes follow", response.size()); if ( response.size() ) Hexdump( Logger::ERROR, response.data(), min(response.size(),16) ); } - return( false ); + return false; } - if ( respCode == 401) { + if ( respCode == 401 ) { Debug( 2, "Got 401 access denied response code, check WWW-Authenticate header and retry"); mAuthenticator->checkAuthResponse(response); mNeedAuth = true; - return( false ); + return false; } else if ( respCode != 200 ) { - Error( "Unexpected response code %d, text is '%s'", respCode, respText ); - return( false ); + Error("Unexpected response code %d, text is '%s'", respCode, respText); + return false; } - return( true ); -} + return true; +} // end RtspThread::recResponse int RtspThread::requestPorts() { if ( !smMinDataPort ) { char sql[ZM_SQL_SML_BUFSIZ]; + //FIXME Why not load specifically by Id? This will get ineffeicient with a lot of monitors strncpy( sql, "select Id from Monitors where Function != 'None' and Type = 'Remote' and Protocol = 'rtsp' and Method = 'rtpUni' order by Id asc", sizeof(sql) ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); @@ -107,7 +108,7 @@ int RtspThread::requestPorts() { int nMonitors = mysql_num_rows( result ); int position = 0; if ( nMonitors ) { - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) { + for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) { int id = atoi(dbrow[0]); if ( mId == id ) { position = i; @@ -126,22 +127,30 @@ int RtspThread::requestPorts() { Debug( 2, "Assigned RTP port range is %d-%d", smMinDataPort, smMaxDataPort ); } for ( int i = smMinDataPort; i <= smMaxDataPort; i++ ) { - PortSet::const_iterator iter = smAssignedPorts.find( i ); + PortSet::const_iterator iter = smAssignedPorts.find(i); if ( iter == smAssignedPorts.end() ) { - smAssignedPorts.insert( i ); - return( i ); + smAssignedPorts.insert(i); + return i; } } - Panic( "Can assign RTP port, no ports left in pool" ); - return( -1 ); + Panic("Can assign RTP port, no ports left in pool"); + return -1; } void RtspThread::releasePorts( int port ) { if ( port > 0 ) - smAssignedPorts.erase( port ); + smAssignedPorts.erase(port); } -RtspThread::RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, const std::string &port, const std::string &path, const std::string &auth, bool rtsp_describe) : +RtspThread::RtspThread( + int id, + RtspMethod method, + const std::string &protocol, + const std::string &host, + const std::string &port, + const std::string &path, + const std::string &auth, + bool rtsp_describe) : mId( id ), mMethod( method ), mProtocol( protocol ), @@ -168,10 +177,10 @@ RtspThread::RtspThread( int id, RtspMethod method, const std::string &protocol, mSsrc = rand(); - Debug( 2, "RTSP Local SSRC is %x", mSsrc ); + Debug(2, "RTSP Local SSRC is %x", mSsrc); if ( mMethod == RTP_RTSP_HTTP ) - mHttpSession = stringtf( "%d", rand() ); + mHttpSession = stringtf("%d", rand()); mNeedAuth = false; StringVector parts = split(auth,":"); @@ -216,8 +225,8 @@ int RtspThread::run() { bool authTried = false; if ( mMethod == RTP_RTSP_HTTP ) { - if ( !mRtspSocket2.connect( mHost.c_str(), mPort.c_str() ) ) - Fatal( "Unable to connect auxiliary RTSP/HTTP socket" ); + if ( !mRtspSocket2.connect(mHost.c_str(), mPort.c_str()) ) + Fatal("Unable to connect auxiliary RTSP/HTTP socket"); //Select select( 0.25 ); //select.addReader( &mRtspSocket2 ); //while ( select.wait() ) @@ -240,15 +249,15 @@ int RtspThread::run() { message += "\r\n"; Debug( 2, "Sending HTTP message: %s", message.c_str() ); if ( mRtspSocket.send( message.c_str(), message.size() ) != (int)message.length() ) { - Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) ); - return( -1 ); + Error("Unable to send message '%s': %s", message.c_str(), strerror(errno)); + return -1; } if ( mRtspSocket.recv( response ) < 0 ) { - Error( "Recv failed; %s", strerror(errno) ); - return( -1 ); + Error("Recv failed; %s", strerror(errno)); + return -1; } - Debug( 2, "Received HTTP response: %s (%zd bytes)", response.c_str(), response.size() ); + Debug(2, "Received HTTP response: %s (%zd bytes)", response.c_str(), response.size()); float respVer = 0; respCode = -1; if ( sscanf( response.c_str(), "HTTP/%f %3d %[^\r\n]\r\n", &respVer, &respCode, respText ) != 3 ) { @@ -259,25 +268,25 @@ int RtspThread::run() { if ( response.size() ) Hexdump( Logger::ERROR, response.data(), min(response.size(),16) ); } - return( -1 ); + return -1; } // If Server requests authentication, check WWW-Authenticate header and fill required fields // for requested authentication method - if (respCode == 401 && !authTried) { + if ( respCode == 401 && !authTried ) { mNeedAuth = true; mAuthenticator->checkAuthResponse(response); Debug(2, "Processed 401 response"); mRtspSocket.close(); - if ( !mRtspSocket.connect( mHost.c_str(), mPort.c_str() ) ) - Fatal( "Unable to reconnect RTSP socket" ); + if ( !mRtspSocket.connect(mHost.c_str(), mPort.c_str()) ) + Fatal("Unable to reconnect RTSP socket"); Debug(2, "connection should be reopened now"); } } while (respCode == 401 && !authTried); if ( respCode != 200 ) { - Error( "Unexpected response code %d, text is '%s'", respCode, respText ); - return( -1 ); + Error("Unexpected response code %d, text is '%s'", respCode, respText); + return -1; } message = "POST "+mPath+" HTTP/1.0\r\n"; @@ -300,25 +309,25 @@ int RtspThread::run() { // Request supported RTSP commands by the server message = "OPTIONS "+mUrl+" RTSP/1.0\r\n"; if ( !sendCommand( message ) ) - return( -1 ); + return -1; // A negative return here may indicate auth failure, but we will have setup the auth mechanisms so we need to retry. - if ( !recvResponse( response ) ) { + if ( !recvResponse(response) ) { if ( mNeedAuth ) { Debug( 2, "Resending OPTIONS due to possible auth requirement" ); - if ( !sendCommand( message ) ) - return( -1 ); - if ( !recvResponse( response ) ) - return( -1 ); + if ( !sendCommand(message) ) + return -1; + if ( !recvResponse(response) ) + return -1; } else { - return( -1 ); + return -1; } } // end if failed response maybe due to auth char publicLine[256] = ""; - StringVector lines = split( response, "\r\n" ); + StringVector lines = split(response, "\r\n"); for ( size_t i = 0; i < lines.size(); i++ ) - sscanf( lines[i].c_str(), "Public: %[^\r\n]\r\n", publicLine ); + sscanf(lines[i].c_str(), "Public: %[^\r\n]\r\n", publicLine); // Check if the server supports the GET_PARAMETER command // If yes, it is likely that the server will request this command as a keepalive message @@ -331,44 +340,45 @@ int RtspThread::run() { do { if (mNeedAuth) authTried = true; - sendCommand( message ); - sleep( 1 ); - res = recvResponse( response ); - if (!res && respCode==401) + sendCommand(message); + // FIXME WHy sleep 1? + sleep(1); + res = recvResponse(response); + if ( !res && respCode==401 ) mNeedAuth = true; } while (!res && respCode==401 && !authTried); const std::string endOfHeaders = "\r\n\r\n"; - size_t sdpStart = response.find( endOfHeaders ); - if( sdpStart == std::string::npos ) - return( -1 ); + size_t sdpStart = response.find(endOfHeaders); + if ( sdpStart == std::string::npos ) + return -1; if ( mRtspDescribe ) { - std::string DescHeader = response.substr( 0,sdpStart ); - Debug( 1, "Processing DESCRIBE response header '%s'", DescHeader.c_str() ); + std::string DescHeader = response.substr(0, sdpStart); + Debug(1, "Processing DESCRIBE response header '%s'", DescHeader.c_str()); - lines = split( DescHeader, "\r\n" ); + lines = split(DescHeader, "\r\n"); for ( size_t i = 0; i < lines.size(); i++ ) { - // If the device sends us a url value for Content-Base in the response header, we should use that instead - if ( ( lines[i].size() > 13 ) && ( lines[i].substr( 0, 13 ) == "Content-Base:" ) ) { - mUrl = trimSpaces( lines[i].substr( 13 ) ); - Info("Received new Content-Base in DESCRIBE response header. Updated device Url to: '%s'", mUrl.c_str() ); - break; - } + // If the device sends us a url value for Content-Base in the response header, we should use that instead + if ( ( lines[i].size() > 13 ) && ( lines[i].substr( 0, 13 ) == "Content-Base:" ) ) { + mUrl = trimSpaces( lines[i].substr( 13 ) ); + Info("Received new Content-Base in DESCRIBE response header. Updated device Url to: '%s'", mUrl.c_str() ); + break; } - } + } // end foreach line + } // end if mRtspDescribe sdpStart += endOfHeaders.length(); - std::string sdp = response.substr( sdpStart ); - Debug( 1, "Processing SDP '%s'", sdp.c_str() ); + std::string sdp = response.substr(sdpStart); + Debug(1, "Processing SDP '%s'", sdp.c_str()); try { mSessDesc = new SessionDescriptor( mUrl, sdp ); mFormatContext = mSessDesc->generateFormatContext(); } catch( const Exception &e ) { Error( e.getMessage().c_str() ); - return( -1 ); + return -1; } #if 0 @@ -672,51 +682,36 @@ Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepali Hexdump( 4, (char *)buffer, 16 ); rtpDataThread.recvPacket( buffer+4, len ); Debug( 4, "Received" ); - } - else if ( channel == remoteChannels[1] ) - { + } else if ( channel == remoteChannels[1] ) { // len = ntohs( *((unsigned short *)(buffer+2)) ); // Debug( 4, "Got %d bytes on control channel %d", nBytes, channel ); Debug( 4, "Got %d bytes on control channel %d, packet length is %d", buffer.size(), channel, len ); Hexdump( 4, (char *)buffer, 16 ); rtpCtrlThread.recvPackets( buffer+4, len ); - } - else - { + } else { Error( "Unexpected channel selector %d in RTSP interleaved data", buffer[1] ); buffer.clear(); break; } buffer.consume( len+4 ); nBytes -= len+4; - } - else - { - if ( keepaliveResponse.compare( 0, keepaliveResponse.size(), (char *)buffer, keepaliveResponse.size() ) == 0 ) - { + } else { + if ( keepaliveResponse.compare( 0, keepaliveResponse.size(), (char *)buffer, keepaliveResponse.size() ) == 0 ) { Debug( 4, "Got keepalive response '%s'", (char *)buffer ); //buffer.consume( keepaliveResponse.size() ); - if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) ) - { + if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) ) { int discardBytes = charPtr-(char *)buffer; buffer -= discardBytes; - } - else - { + } else { buffer.clear(); } - } - else - { - if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) ) - { + } else { + if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) ) { int discardBytes = charPtr-(char *)buffer; Warning( "Unexpected format RTSP interleaved data, resyncing by %d bytes", discardBytes ); Hexdump( -1, (char *)buffer, discardBytes ); buffer -= discardBytes; - } - else - { + } else { Warning( "Unexpected format RTSP interleaved data, dumping %d bytes", buffer.size() ); Hexdump( -1, (char *)buffer, 32 ); buffer.clear(); @@ -764,16 +759,14 @@ Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepali rtpDataThread.start(); rtpCtrlThread.start(); - while( !mStop ) - { + while ( !mStop ) { // Send a keepalive message if the server supports this feature and we are close to the timeout expiration - if ( sendKeepalive && (timeout > 0) && ((time(NULL)-lastKeepalive) > (timeout-5)) ) - { + if ( sendKeepalive && (timeout > 0) && ((time(NULL)-lastKeepalive) > (timeout-5)) ) { if ( !sendCommand( message ) ) - return( -1 ); + return -1; lastKeepalive = time(NULL); } - usleep( 100000 ); + usleep(100000); } #if 0 message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; @@ -783,10 +776,10 @@ Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepali return( -1 ); #endif message = "TEARDOWN "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; - if ( !sendCommand( message ) ) - return( -1 ); - if ( !recvResponse( response ) ) - return( -1 ); + if ( !sendCommand(message) ) + return -1; + if ( !recvResponse(response) ) + return -1; rtpDataThread.stop(); rtpCtrlThread.stop(); @@ -801,13 +794,11 @@ Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepali break; } default: - { - Panic( "Got unexpected method %d", mMethod ); + Panic("Got unexpected method %d", mMethod); break; - } } - return( 0 ); + return 0; } #endif // HAVE_LIBAVFORMAT diff --git a/src/zm_sdp.cpp b/src/zm_sdp.cpp index af293d64a..c7bf1b578 100644 --- a/src/zm_sdp.cpp +++ b/src/zm_sdp.cpp @@ -26,17 +26,17 @@ #if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0)) SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = { { 0, "PCMU", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_MULAW, 8000, 1 }, - { 3, "GSM", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, + { 3, "GSM", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, { 4, "G723", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, { 5, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, { 6, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 16000, 1 }, - { 7, "LPC", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, + { 7, "LPC", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, { 8, "PCMA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_ALAW, 8000, 1 }, { 9, "G722", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, { 10, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 2 }, { 11, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 1 }, { 12, "QCELP", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_QCELP, 8000, 1 }, - { 13, "CN", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, + { 13, "CN", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, { 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP2, -1, -1 }, { 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3, -1, -1 }, { 15, "G728", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, @@ -45,36 +45,36 @@ SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = { { 18, "G729", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, { 25, "CelB", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 }, { 26, "JPEG", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MJPEG, 90000, -1 }, - { 28, "nv", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 }, + { 28, "nv", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 }, { 31, "H261", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H261, 90000, -1 }, { 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG1VIDEO, 90000, -1 }, { 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO, 90000, -1 }, { 33, "MP2T", AVMEDIA_TYPE_DATA, AV_CODEC_ID_MPEG2TS, 90000, -1 }, { 34, "H263", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H263, 90000, -1 }, - { -1, "", AVMEDIA_TYPE_UNKNOWN, AV_CODEC_ID_NONE, -1, -1 } + { -1, "", AVMEDIA_TYPE_UNKNOWN, AV_CODEC_ID_NONE, -1, -1 } }; SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = { - { "MP4V-ES", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 }, - { "mpeg4-generic", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC }, - { "H264", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 }, - { "AMR", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AMR_NB }, + { "MP4V-ES", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 }, + { "mpeg4-generic", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC }, + { "H264", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 }, + { "AMR", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AMR_NB }, { "vnd.onvif.metadata", AVMEDIA_TYPE_DATA, AV_CODEC_ID_NONE } }; #else SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = { { 0, "PCMU", CODEC_TYPE_AUDIO, CODEC_ID_PCM_MULAW, 8001, 1 }, - { 3, "GSM", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, + { 3, "GSM", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 4, "G723", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 5, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 6, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 16000, 1 }, - { 7, "LPC", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, + { 7, "LPC", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 8, "PCMA", CODEC_TYPE_AUDIO, CODEC_ID_PCM_ALAW, 8000, 1 }, { 9, "G722", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 10, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 2 }, { 11, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 1 }, { 12, "QCELP", CODEC_TYPE_AUDIO, CODEC_ID_QCELP, 8000, 1 }, - { 13, "CN", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, + { 13, "CN", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP2, -1, -1 }, { 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP3, -1, -1 }, { 15, "G728", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, @@ -83,7 +83,7 @@ SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = { { 18, "G729", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 25, "CelB", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 }, { 26, "JPEG", CODEC_TYPE_VIDEO, CODEC_ID_MJPEG, 90000, -1 }, - { 28, "nv", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 }, + { 28, "nv", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 }, { 31, "H261", CODEC_TYPE_VIDEO, CODEC_ID_H261, 90000, -1 }, { 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG1VIDEO, 90000, -1 }, { 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO, 90000, -1 }, @@ -105,7 +105,7 @@ SessionDescriptor::ConnInfo::ConnInfo( const std::string &connInfo ) : mTtl( 16 ), mNoAddresses( 0 ) { - StringVector tokens = split( connInfo, " " ); + StringVector tokens = split(connInfo, " "); if ( tokens.size() < 3 ) throw Exception( "Unable to parse SDP connection info from '"+connInfo+"'" ); mNetworkType = tokens[0]; @@ -136,7 +136,12 @@ SessionDescriptor::BandInfo::BandInfo( const std::string &bandInfo ) : mValue = atoi(tokens[1].c_str()); } -SessionDescriptor::MediaDescriptor::MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType ) : +SessionDescriptor::MediaDescriptor::MediaDescriptor( + const std::string &type, + int port, + int numPorts, + const std::string &transport, + int payloadType ) : mType( type ), mPort( port ), mNumPorts( numPorts ), @@ -164,14 +169,13 @@ SessionDescriptor::SessionDescriptor( const std::string &url, const std::string if ( line.empty() ) break; - Debug( 3, "Processing SDP line '%s'", line.c_str() ); + Debug(3, "Processing SDP line '%s'", line.c_str()); const char sdpType = line[0]; if ( line[1] != '=' ) - throw Exception( "Invalid SDP format at '"+line+"'" ); + throw Exception("Invalid SDP format at '"+line+"'"); - line.erase( 0, 2 ); - switch( sdpType ) - { + line.erase(0, 2); + switch( sdpType ) { case 'v' : mVersion = line; break; @@ -204,19 +208,13 @@ SessionDescriptor::SessionDescriptor( const std::string &url, const std::string mAttributes.push_back( line ); StringVector tokens = split( line, ":", 2 ); std::string attrName = tokens[0]; - if ( currMedia ) - { - if ( attrName == "control" ) - { + if ( currMedia ) { + if ( attrName == "control" ) { if ( tokens.size() < 2 ) throw Exception( "Unable to parse SDP control attribute '"+line+"' for media '"+currMedia->getType()+"'" ); currMedia->setControlUrl( tokens[1] ); - } - else if ( attrName == "range" ) - { - } - else if ( attrName == "rtpmap" ) - { + } else if ( attrName == "range" ) { + } else if ( attrName == "rtpmap" ) { // a=rtpmap:96 MP4V-ES/90000 if ( tokens.size() < 2 ) throw Exception( "Unable to parse SDP rtpmap attribute '"+line+"' for media '"+currMedia->getType()+"'" ); @@ -226,53 +224,46 @@ SessionDescriptor::SessionDescriptor( const std::string &url, const std::string throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) ); std::string payloadDesc = attrTokens[1]; //currMedia->setPayloadType( payloadType ); - if ( attrTokens.size() > 1 ) - { + if ( attrTokens.size() > 1 ) { StringVector payloadTokens = split( attrTokens[1], "/" ); std::string payloadDesc = payloadTokens[0]; int payloadClock = atoi(payloadTokens[1].c_str()); currMedia->setPayloadDesc( payloadDesc ); currMedia->setClock( payloadClock ); } - } - else if ( attrName == "framesize" ) - { + } else if ( attrName == "framesize" ) { // a=framesize:96 320-240 if ( tokens.size() < 2 ) - throw Exception( "Unable to parse SDP framesize attribute '"+line+"' for media '"+currMedia->getType()+"'" ); - StringVector attrTokens = split( tokens[1], " " ); + throw Exception("Unable to parse SDP framesize attribute '"+line+"' for media '"+currMedia->getType()+"'"); + StringVector attrTokens = split(tokens[1], " "); int payloadType = atoi(attrTokens[0].c_str()); if ( payloadType != currMedia->getPayloadType() ) - throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) ); + throw Exception( stringtf("Payload type mismatch, expected %d, got %d in '%s'", + currMedia->getPayloadType(), payloadType, line.c_str())); //currMedia->setPayloadType( payloadType ); - StringVector sizeTokens = split( attrTokens[1], "-" ); + StringVector sizeTokens = split(attrTokens[1], "-"); int width = atoi(sizeTokens[0].c_str()); int height = atoi(sizeTokens[1].c_str()); - currMedia->setFrameSize( width, height ); - } - else if ( attrName == "framerate" ) - { + currMedia->setFrameSize(width, height); + } else if ( attrName == "framerate" ) { // a=framerate:5.0 if ( tokens.size() < 2 ) - throw Exception( "Unable to parse SDP framerate attribute '"+line+"' for media '"+currMedia->getType()+"'" ); + throw Exception("Unable to parse SDP framerate attribute '"+line+"' for media '"+currMedia->getType()+"'"); double frameRate = atof(tokens[1].c_str()); - currMedia->setFrameRate( frameRate ); - } - else if ( attrName == "fmtp" ) - { + currMedia->setFrameRate(frameRate); + } else if ( attrName == "fmtp" ) { // a=fmtp:96 profile-level-id=247; config=000001B0F7000001B509000001000000012008D48D8803250F042D14440F if ( tokens.size() < 2 ) - throw Exception( "Unable to parse SDP fmtp attribute '"+line+"' for media '"+currMedia->getType()+"'" ); - StringVector attrTokens = split( tokens[1], " ", 2 ); + throw Exception("Unable to parse SDP fmtp attribute '"+line+"' for media '"+currMedia->getType()+"'"); + StringVector attrTokens = split(tokens[1], " ", 2); int payloadType = atoi(attrTokens[0].c_str()); if ( payloadType != currMedia->getPayloadType() ) - throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) ); + throw Exception(stringtf("Payload type mismatch, expected %d, got %d in '%s'", + currMedia->getPayloadType(), payloadType, line.c_str())); //currMedia->setPayloadType( payloadType ); - if ( attrTokens.size() > 1 ) - { + if ( attrTokens.size() > 1 ) { StringVector attr2Tokens = split( attrTokens[1], "; " ); - for ( unsigned int i = 0; i < attr2Tokens.size(); i++ ) - { + for ( unsigned int i = 0; i < attr2Tokens.size(); i++ ) { StringVector attr3Tokens = split( attr2Tokens[i], "=" ); //Info( "Name = %s, Value = %s", attr3Tokens[0].c_str(), attr3Tokens[1].c_str() ); if ( attr3Tokens[0] == "profile-level-id" ) { @@ -292,40 +283,39 @@ SessionDescriptor::SessionDescriptor( const std::string &url, const std::string } else if ( attrName == "mpeg4-esid" ) { // a=mpeg4-esid:201 } else { - Debug( 3, "Ignoring SDP attribute '%s' for media '%s'", line.c_str(), currMedia->getType().c_str() ) + Debug(3, "Ignoring SDP attribute '%s' for media '%s'", line.c_str(), currMedia->getType().c_str()); } } else { - Debug( 3, "Ignoring general SDP attribute '%s'", line.c_str() ); + Debug(3, "Ignoring general SDP attribute '%s'", line.c_str()); } break; } case 'm' : { - StringVector tokens = split( line, " " ); + StringVector tokens = split(line, " "); if ( tokens.size() < 4 ) - throw Exception( "Can't parse SDP media description '"+line+"'" ); + throw Exception("Can't parse SDP media description '"+line+"'"); std::string mediaType = tokens[0]; if ( mediaType != "audio" && mediaType != "video" && mediaType != "application" ) - throw Exception( "Unsupported media type '"+mediaType+"' in SDP media attribute '"+line+"'" ); - StringVector portTokens = split( tokens[1], "/" ); + throw Exception("Unsupported media type '"+mediaType+"' in SDP media attribute '"+line+"'"); + StringVector portTokens = split(tokens[1], "/"); int mediaPort = atoi(portTokens[0].c_str()); int mediaNumPorts = 1; if ( portTokens.size() > 1 ) mediaNumPorts = atoi(portTokens[1].c_str()); std::string mediaTransport = tokens[2]; if ( mediaTransport != "RTP/AVP" ) - throw Exception( "Unsupported media transport '"+mediaTransport+"' in SDP media attribute '"+line+"'" ); + throw Exception("Unsupported media transport '"+mediaTransport+"' in SDP media attribute '"+line+"'"); int payloadType = atoi(tokens[3].c_str()); - currMedia = new MediaDescriptor( mediaType, mediaPort, mediaNumPorts, mediaTransport, payloadType ); - mMediaList.push_back( currMedia ); + currMedia = new MediaDescriptor(mediaType, mediaPort, mediaNumPorts, mediaTransport, payloadType); + mMediaList.push_back(currMedia); break; } - } - } + } // end switch + } // end foreach line } -SessionDescriptor::~SessionDescriptor() -{ +SessionDescriptor::~SessionDescriptor() { if ( mConnInfo ) delete mConnInfo; if ( mBandInfo ) @@ -334,8 +324,7 @@ SessionDescriptor::~SessionDescriptor() delete mMediaList[i]; } -AVFormatContext *SessionDescriptor::generateFormatContext() const -{ +AVFormatContext *SessionDescriptor::generateFormatContext() const { AVFormatContext *formatContext = avformat_alloc_context(); #if (LIBAVFORMAT_VERSION_CHECK(58, 12, 0, 0, 100)) @@ -353,35 +342,40 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const for ( unsigned int i = 0; i < mMediaList.size(); i++ ) { const MediaDescriptor *mediaDesc = mMediaList[i]; #if !LIBAVFORMAT_VERSION_CHECK(53, 10, 0, 17, 0) - AVStream *stream = av_new_stream( formatContext, i ); + AVStream *stream = av_new_stream(formatContext, i); #else - AVStream *stream = avformat_new_stream( formatContext, NULL ); + AVStream *stream = avformat_new_stream(formatContext, NULL); stream->id = i; #endif #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) AVCodecContext *codec_context = avcodec_alloc_context3(NULL); avcodec_parameters_to_context(codec_context, stream->codecpar); + stream->codec = codec_context; #else AVCodecContext *codec_context = stream->codec; #endif - Debug( 1, "Looking for codec for %s payload type %d / %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() ); + std::string type = mediaDesc->getType(); + Debug(1, "Looking for codec for %s payload type %d / %s", + type.c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str()); #if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0)) - if ( mediaDesc->getType() == "video" ) + if ( type == "video" ) codec_context->codec_type = AVMEDIA_TYPE_VIDEO; - else if ( mediaDesc->getType() == "audio" ) + else if ( type == "audio" ) codec_context->codec_type = AVMEDIA_TYPE_AUDIO; - else if ( mediaDesc->getType() == "application" ) + else if ( type == "application" ) codec_context->codec_type = AVMEDIA_TYPE_DATA; #else - if ( mediaDesc->getType() == "video" ) + if ( type == "video" ) codec_context->codec_type = CODEC_TYPE_VIDEO; - else if ( mediaDesc->getType() == "audio" ) + else if ( type == "audio" ) codec_context->codec_type = CODEC_TYPE_AUDIO; - else if ( mediaDesc->getType() == "application" ) + else if ( type == "application" ) codec_context->codec_type = CODEC_TYPE_DATA; #endif + else + Warning("Unknown media_type %s", type.c_str()); #if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103) std::string codec_name; @@ -392,9 +386,9 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const if ( smStaticPayloads[i].payloadType == mediaDesc->getPayloadType() ) { Debug( 1, "Got static payload type %d, %s", smStaticPayloads[i].payloadType, smStaticPayloads[i].payloadName ); #if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103) - codec_name = std::string( smStaticPayloads[i].payloadName ); + codec_name = std::string(smStaticPayloads[i].payloadName); #else - strncpy( codec_context->codec_name, smStaticPayloads[i].payloadName, sizeof(codec_context->codec_name) );; + strncpy(codec_context->codec_name, smStaticPayloads[i].payloadName, sizeof(codec_context->codec_name)); #endif codec_context->codec_type = smStaticPayloads[i].codecType; codec_context->codec_id = smStaticPayloads[i].codecId; @@ -406,11 +400,11 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const // Look in dynamic table for ( unsigned int i = 0; i < (sizeof(smDynamicPayloads)/sizeof(*smDynamicPayloads)); i++ ) { if ( smDynamicPayloads[i].payloadName == mediaDesc->getPayloadDesc() ) { - Debug( 1, "Got dynamic payload type %d, %s", mediaDesc->getPayloadType(), smDynamicPayloads[i].payloadName ); + Debug(1, "Got dynamic payload type %d, %s", mediaDesc->getPayloadType(), smDynamicPayloads[i].payloadName); #if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103) - codec_name = std::string( smStaticPayloads[i].payloadName ); + codec_name = std::string(smStaticPayloads[i].payloadName); #else - strncpy( codec_context->codec_name, smDynamicPayloads[i].payloadName, sizeof(codec_context->codec_name) );; + strncpy(codec_context->codec_name, smDynamicPayloads[i].payloadName, sizeof(codec_context->codec_name)); #endif codec_context->codec_type = smDynamicPayloads[i].codecType; codec_context->codec_id = smDynamicPayloads[i].codecId; @@ -418,7 +412,7 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const break; } } - } + } /// end if static or dynamic #if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103) if ( codec_name.empty() ) @@ -426,7 +420,8 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const if ( !stream->codec->codec_name[0] ) #endif { - Warning( "Can't find payload details for %s payload type %d, name %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() ); + Warning( "Can't find payload details for %s payload type %d, name %s", + mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() ); //return( 0 ); } if ( mediaDesc->getWidth() ) @@ -449,11 +444,11 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const while (*value && *value != ',' && (dst - base64packet) < (long)(sizeof(base64packet)) - 1) { - *dst++ = *value++; + *dst++ = *value++; } *dst++ = '\0'; - if (*value == ',') + if ( *value == ',' ) value++; packet_size= av_base64_decode(decoded_packet, (const char *)base64packet, (int)sizeof(decoded_packet)); @@ -468,23 +463,23 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const FF_INPUT_BUFFER_PADDING_SIZE #endif ); - if(dest) { - if(codec_context->extradata_size) { - // av_realloc? + if ( dest ) { + if ( codec_context->extradata_size ) { + // av_realloc? memcpy(dest, codec_context->extradata, codec_context->extradata_size); - av_free(codec_context->extradata); - } + av_free(codec_context->extradata); + } - memcpy(dest+codec_context->extradata_size, start_sequence, sizeof(start_sequence)); - memcpy(dest+codec_context->extradata_size+sizeof(start_sequence), decoded_packet, packet_size); - memset(dest+codec_context->extradata_size+sizeof(start_sequence)+ - packet_size, 0, + memcpy(dest+codec_context->extradata_size, start_sequence, sizeof(start_sequence)); + memcpy(dest+codec_context->extradata_size+sizeof(start_sequence), decoded_packet, packet_size); + memset(dest+codec_context->extradata_size+sizeof(start_sequence)+ + packet_size, 0, #if LIBAVCODEC_VERSION_CHECK(57, 0, 0, 0, 0) - AV_INPUT_BUFFER_PADDING_SIZE + AV_INPUT_BUFFER_PADDING_SIZE #else - FF_INPUT_BUFFER_PADDING_SIZE + FF_INPUT_BUFFER_PADDING_SIZE #endif -); + ); codec_context->extradata= dest; codec_context->extradata_size+= sizeof(start_sequence)+packet_size; @@ -497,7 +492,7 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const } } - return( formatContext ); + return formatContext; } #endif // HAVE_LIBAVFORMAT From 940338ea126f4f763a2ff99bdd9cc0a21c444776 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 29 Apr 2019 12:51:02 -0400 Subject: [PATCH 13/45] namespace escape Error calls --- web/index.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/index.php b/web/index.php index df190a700..a4f6160d4 100644 --- a/web/index.php +++ b/web/index.php @@ -93,7 +93,7 @@ if ( isset($_GET['skin']) ) { $skins = array_map('basename', glob('skins/*', GLOB_ONLYDIR)); if ( ! in_array($skin, $skins) ) { - Error("Invalid skin '$skin' setting to " . $skins[0]); + ZM\Error("Invalid skin '$skin' setting to " . $skins[0]); $skin = $skins[0]; } @@ -109,7 +109,7 @@ if ( isset($_GET['css']) ) { $css_skins = array_map('basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR)); if ( !in_array($css, $css_skins) ) { - Error("Invalid skin css '$css' setting to " . $css_skins[0]); + ZM\Error("Invalid skin css '$css' setting to " . $css_skins[0]); $css = $css_skins[0]; } From b3fb934fb506d0dc491f9bc01e1d7b427011f830 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 29 Apr 2019 14:16:55 -0400 Subject: [PATCH 14/45] add namespace to Logging calls --- web/includes/actions/monitor.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/web/includes/actions/monitor.php b/web/includes/actions/monitor.php index cfb89a892..f97441fd9 100644 --- a/web/includes/actions/monitor.php +++ b/web/includes/actions/monitor.php @@ -68,6 +68,9 @@ if ( $action == 'monitor' ) { $columns = getTableColumns('Monitors'); $changes = getFormChanges($monitor, $_REQUEST['newMonitor'], $types, $columns); +ZM\Logger::Debug("Columns:". print_r($columns,true)); +ZM\Logger::Debug("Changes:". print_r($changes,true)); +ZM\Logger::Debug("newMonitor:". print_r($_REQUEST['newMonitor'],true)); if ( count($changes) ) { if ( $mid ) { @@ -88,12 +91,12 @@ if ( $action == 'monitor' ) { $NewStorage = new ZM\Storage($_REQUEST['newMonitor']['StorageId']); if ( !file_exists($NewStorage->Path().'/'.$mid) ) { if ( !mkdir($NewStorage->Path().'/'.$mid, 0755) ) { - Error('Unable to mkdir ' . $NewStorage->Path().'/'.$mid); + ZM\Error('Unable to mkdir ' . $NewStorage->Path().'/'.$mid); } } $saferNewName = basename($_REQUEST['newMonitor']['Name']); if ( !symlink($NewStorage->Path().'/'.$mid, $NewStorage->Path().'/'.$saferNewName) ) { - Warning('Unable to symlink ' . $NewStorage->Path().'/'.$mid . ' to ' . $NewStorage->Path().'/'.$saferNewName); + ZM\Warning('Unable to symlink ' . $NewStorage->Path().'/'.$mid . ' to ' . $NewStorage->Path().'/'.$saferNewName); } } if ( isset($changes['Width']) || isset($changes['Height']) ) { From 76dd4113417c6b6a828aaf837683380da3584622 Mon Sep 17 00:00:00 2001 From: redaco <50115235+redaco@users.noreply.github.com> Date: Mon, 29 Apr 2019 21:56:55 +0200 Subject: [PATCH 15/45] Netcat ONVIF: Added support for "profile token" (#2589) --- .../lib/ZoneMinder/Control/Netcat.pm | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/Netcat.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/Netcat.pm index efffd2d19..977662ceb 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/Netcat.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/Netcat.pm @@ -36,6 +36,8 @@ our @ISA = qw(ZoneMinder::Control); our %CamParams = (); +our $profileToken; + # ========================================================================== # # Netcat IP Control Protocol @@ -79,6 +81,9 @@ sub open { $self->loadMonitor(); + $profileToken = $self->{Monitor}->{ControlDevice}; + if ($profileToken eq '') { $profileToken = '000'; } + use LWP::UserAgent; $self->{ua} = LWP::UserAgent->new; $self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION); @@ -160,7 +165,7 @@ sub autoStop { if ( $autostop ) { Debug('Auto Stop'); my $cmd = 'onvif/PTZ'; - my $msg = '000truefalse'; + my $msg = '' . $profileToken . 'truefalse'; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; usleep($autostop); $self->sendCmd($cmd, $msg, $content_type); @@ -182,7 +187,7 @@ sub moveConUp { Debug('Move Up'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='000'; + my $msg ='' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -193,7 +198,7 @@ sub moveConDown { Debug('Move Down'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='000'; + my $msg ='' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -204,7 +209,7 @@ sub moveConLeft { Debug('Move Left'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='000'; + my $msg ='' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -215,7 +220,7 @@ sub moveConRight { Debug('Move Right'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='000'; + my $msg ='' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -226,7 +231,7 @@ sub zoomConTele { Debug('Zoom Tele'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='000'; + my $msg ='' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -237,7 +242,7 @@ sub zoomConWide { Debug('Zoom Wide'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='000'; + my $msg ='' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -249,7 +254,7 @@ sub moveConUpRight { Debug('Move Diagonally Up Right'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='000'; + my $msg ='' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -261,7 +266,7 @@ sub moveConDownRight { Debug('Move Diagonally Down Right'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='000'; + my $msg ='' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -273,7 +278,7 @@ sub moveConUpLeft { Debug('Move Diagonally Up Left'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='000'; + my $msg ='' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -285,7 +290,7 @@ sub moveConDownLeft { Debug('Move Diagonally Down Left'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='000'; + my $msg ='' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -296,7 +301,7 @@ sub moveStop { Debug('Move Stop'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='000truefalse'; + my $msg ='' . $profileToken . 'truefalse'; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); } @@ -308,7 +313,7 @@ sub presetSet { my $preset = $self->getParam($params, 'preset'); Debug("Set Preset $preset"); my $cmd = 'onvif/PTZ'; - my $msg ='000'.$preset.''; + my $msg ='' . $profileToken . ''.$preset.''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/SetPreset"'; $self->sendCmd($cmd, $msg, $content_type); } @@ -320,7 +325,7 @@ sub presetGoto { my $preset = $self->getParam($params, 'preset'); Debug("Goto Preset $preset"); my $cmd = 'onvif/PTZ'; - my $msg ='000'.$preset.''; + my $msg ='' . $profileToken . ''.$preset.''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/GotoPreset"'; $self->sendCmd( $cmd, $msg, $content_type ); } From dfa997a989a125703ffb72d5fef3e4f585e54d73 Mon Sep 17 00:00:00 2001 From: cnighswonger Date: Mon, 18 Mar 2019 13:28:00 -0400 Subject: [PATCH 16/45] Add camera relative iris control methods This set of methods invoke realtive iris size in the direction indicated by the portion of their name. They accept no arguments. NOTE: This only just does work. The Dahua API specifies "multiples" as the input. We pass in a 1 for that as it does not seem to matter what number (0-8) is provided, the camera iris behaves the same. --- .../lib/ZoneMinder/Control/Dahua.pm | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/Dahua.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/Dahua.pm index e55a6da1c..23245a2ea 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/Dahua.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/Dahua.pm @@ -381,6 +381,22 @@ sub focusRelFar Debug("focusRelFar response: " . $response); } +sub irisRelOpen +{ + my $self = shift; + + my $response = $self->_sendPtzCommand("start", "IrisLarge", 0, 1, 0, 0); + Debug("irisRelOpen response: " . $response); +} + +sub irisRelClose +{ + my $self = shift; + + my $response = $self->_sendPtzCommand("start", "IrisSmall", 0, 1, 0, 0); + Debug("irisRelClose response: " . $response); +} + sub moveStop { my $self = shift; @@ -592,6 +608,17 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. We pass in a 1 for that as it does not seem to matter what number (0-8) is provided, the camera focus behaves the same. +=head2 irisRel + + This set of methods invoke realtive iris size in the direction indicated by + the portion of their name. They accept no arguments. + + NOTE: + + This only just does work. The Dahua API specifies "multiples" as the input. + We pass in a 1 for that as it does not seem to matter what number (0-8) is + provided, the camera iris behaves the same. + =head2 moveStop This method attempts to stop the camera. The problem is that if continuous From ff738a99ba2383528471203aec14b23216be4bf5 Mon Sep 17 00:00:00 2001 From: cnighswonger Date: Mon, 29 Apr 2019 16:06:41 -0400 Subject: [PATCH 17/45] Enabling relative iris methods --- db/zm_create.sql.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index c197d09ab..5ed80c7ed 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -789,7 +789,7 @@ INSERT INTO `Controls` VALUES (NULL,'IOS Camera','Ffmpeg','IPCAMIOS',0,0,0,0,0,0 INSERT INTO `Controls` VALUES (NULL,'Dericam P2','Ffmpeg','DericamP2',0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,45,0,0,1,0,0,0,0,1,1,45,0,0,0,0); INSERT INTO `Controls` VALUES (NULL,'Trendnet','Remote','Trendnet',1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); INSERT INTO `Controls` VALUES (NULL,'PSIA','Remote','PSIA',0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,0,1,1,1,0,0,1,0,1,0,0,0,0,1,-100,100,0,0,1,0,0,0,0,1,-100,100,0,0,0,0); -INSERT INTO `Controls` VALUES (NULL,'Dahua','Remote','Dahua',0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,0,1,1,1,0,0,1,0,1,0,0,0,0,1,1,8,0,0,1,0,0,0,0,1,1,8,0,0,0,0); +NSERT INTO `Controls` VALUES (NULL,'Dahua','Ffmpeg','Dahua',0,0,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,1,1,1,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); INSERT INTO `Controls` VALUES (NULL,'FOSCAMR2C','Libvlc','FOSCAMR2C',1,1,1,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,12,0,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,4,0,NULL,1,NULL,NULL,NULL,NULL,1,0,4,0,NULL,0,0); INSERT INTO `Controls` VALUES (NULL,'Amcrest HTTP API','Ffmpeg','Amcrest_HTTP',0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,5); From 16697565bf6fe20748c1215cee539706e176a502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Da=20Costa?= Date: Tue, 30 Apr 2019 07:48:47 +0200 Subject: [PATCH 18/45] Netcat ONVIF: adding ONVIF authentication --- .../lib/ZoneMinder/Control/Netcat.pm | 86 +++++++++++++------ 1 file changed, 62 insertions(+), 24 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/Netcat.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/Netcat.pm index 977662ceb..2d9573299 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/Netcat.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/Netcat.pm @@ -28,6 +28,9 @@ package ZoneMinder::Control::Netcat; use 5.006; use strict; use warnings; +use MIME::Base64; +use Digest::SHA; +use DateTime; require ZoneMinder::Base; require ZoneMinder::Control; @@ -36,7 +39,7 @@ our @ISA = qw(ZoneMinder::Control); our %CamParams = (); -our $profileToken; +our ($profileToken, $address, $port, %identity); # ========================================================================== # @@ -52,7 +55,6 @@ our $profileToken; # # # Possible future improvements (for anyone to improve upon): -# - Onvif authentication # - Build the SOAP commands at runtime rather than use templates # - Implement previously mentioned advanced features # @@ -60,9 +62,10 @@ our $profileToken; # more dependencies to ZoneMinder is always a concern. # # On ControlAddress use the format : -# ADDRESS:PORT +# [USERNAME:PASSWORD@]ADDRESS:PORT # eg : 10.1.2.1:8899 # 10.0.100.1:8899 +# username:password@10.0.100.1:8899 # # Use port 8899 for the Netcat camera # @@ -84,6 +87,8 @@ sub open { $profileToken = $self->{Monitor}->{ControlDevice}; if ($profileToken eq '') { $profileToken = '000'; } + parseControlAddress($self->{Monitor}->{ControlAddress}); + use LWP::UserAgent; $self->{ua} = LWP::UserAgent->new; $self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION); @@ -91,6 +96,39 @@ sub open { $self->{state} = 'open'; } +sub parseControlAddress +{ + my $controlAddress = shift; + my ($usernamepassword, $addressport) = split /@/, $controlAddress; + if ( !defined $addressport ) { + # If value of "Control address" does not consist of two parts, then only address is given + $addressport = $usernamepassword; + } else { + my ($username , $password) = split /:/, $usernamepassword; + %identity = (username => "$username", password => "$password"); + } + ($address, $port) = split /:/, $addressport; +} + +sub digestBase64 +{ + my ($nonce, $date, $password) = @_; + my $shaGenerator = Digest::SHA->new(1); + $shaGenerator->add($nonce . $date . $password); + return encode_base64($shaGenerator->digest, ""); +} + +sub authentificationHeader +{ + my ($username, $password) = @_; + my $nonce; + $nonce .= chr(int(rand(254))) for (0 .. 20); + my $nonceBase64 = encode_base64($nonce, ""); + my $currentDate = DateTime->now()->iso8601().'Z'; + + return '' . $username . '' . digestBase64($nonce, $currentDate, $password) . '' . $nonceBase64 . '' . $currentDate . ''; +} + sub printMsg { my $self = shift; my $msg = shift; @@ -108,10 +146,10 @@ sub sendCmd { printMsg($cmd, 'Tx'); - my $server_endpoint = 'http://'.$self->{Monitor}->{ControlAddress}.'/'.$cmd; + my $server_endpoint = 'http://' . $address . ':' . $port . "/$cmd"; my $req = HTTP::Request->new(POST => $server_endpoint); $req->header('content-type' => $content_type); - $req->header('Host' => $self->{Monitor}->{ControlAddress}); + $req->header('Host' => $address . ':' . $port); $req->header('content-length' => length($msg)); $req->header('accept-encoding' => 'gzip, deflate'); $req->header('connection' => 'Close'); @@ -130,10 +168,10 @@ sub sendCmd { sub getCamParams { my $self = shift; my $msg = '000'; - my $server_endpoint = 'http://'.$self->{Monitor}->{ControlAddress}.'/onvif/imaging'; + my $server_endpoint = 'http://' . $address . ':' . $port . '/onvif/imaging'; my $req = HTTP::Request->new(POST => $server_endpoint); $req->header('content-type' => 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/GetImagingSettings"'); - $req->header('Host' => $self->{Monitor}->{ControlAddress}); + $req->header('Host' => $address . ':' . $port); $req->header('content-length' => length($msg)); $req->header('accept-encoding' => 'gzip, deflate'); $req->header('connection' => 'Close'); @@ -165,7 +203,7 @@ sub autoStop { if ( $autostop ) { Debug('Auto Stop'); my $cmd = 'onvif/PTZ'; - my $msg = '' . $profileToken . 'truefalse'; + my $msg = '' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '' . $profileToken . 'truefalse'; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; usleep($autostop); $self->sendCmd($cmd, $msg, $content_type); @@ -177,7 +215,7 @@ sub reset { Debug('Camera Reset'); my $self = shift; my $cmd = ''; - my $msg = ''; + my $msg = '' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver10/device/wsdl/SystemReboot"'; $self->sendCmd($cmd, $msg, $content_type); } @@ -187,7 +225,7 @@ sub moveConUp { Debug('Move Up'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='' . $profileToken . ''; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -209,7 +247,7 @@ sub moveConLeft { Debug('Move Left'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='' . $profileToken . ''; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -220,7 +258,7 @@ sub moveConRight { Debug('Move Right'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='' . $profileToken . ''; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -231,7 +269,7 @@ sub zoomConTele { Debug('Zoom Tele'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='' . $profileToken . ''; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -242,7 +280,7 @@ sub zoomConWide { Debug('Zoom Wide'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='' . $profileToken . ''; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -254,7 +292,7 @@ sub moveConUpRight { Debug('Move Diagonally Up Right'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='' . $profileToken . ''; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -266,7 +304,7 @@ sub moveConDownRight { Debug('Move Diagonally Down Right'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='' . $profileToken . ''; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -290,7 +328,7 @@ sub moveConDownLeft { Debug('Move Diagonally Down Left'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='' . $profileToken . ''; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -301,7 +339,7 @@ sub moveStop { Debug('Move Stop'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='' . $profileToken . 'truefalse'; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '' . $profileToken . 'truefalse'; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); } @@ -313,7 +351,7 @@ sub presetSet { my $preset = $self->getParam($params, 'preset'); Debug("Set Preset $preset"); my $cmd = 'onvif/PTZ'; - my $msg ='' . $profileToken . ''.$preset.''; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '' . $profileToken . ''.$preset.''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/SetPreset"'; $self->sendCmd($cmd, $msg, $content_type); } @@ -325,7 +363,7 @@ sub presetGoto { my $preset = $self->getParam($params, 'preset'); Debug("Goto Preset $preset"); my $cmd = 'onvif/PTZ'; - my $msg ='' . $profileToken . ''.$preset.''; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '' . $profileToken . ''.$preset.''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/GotoPreset"'; $self->sendCmd( $cmd, $msg, $content_type ); } @@ -367,7 +405,7 @@ sub irisAbsOpen { $CamParams{Brightness} = $max if ($CamParams{Brightness} > $max); my $cmd = 'onvif/imaging'; - my $msg ='000'.$CamParams{Brightness}.'true'; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '000'.$CamParams{Brightness}.'true'; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; $self->sendCmd( $cmd, $msg, $content_type ); } @@ -386,7 +424,7 @@ sub irisAbsClose $CamParams{Brightness} = $min if ($CamParams{Brightness} < $min); my $cmd = 'onvif/imaging'; - my $msg ='000'.$CamParams{Brightness}.'true'; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '000'.$CamParams{Brightness}.'true'; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; $self->sendCmd( $cmd, $msg, $content_type ); } @@ -404,7 +442,7 @@ sub whiteAbsIn { $CamParams{Contrast} = $max if ($CamParams{Contrast} > $max); my $cmd = 'onvif/imaging'; - my $msg ='000'.$CamParams{Contrast}.'true'; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '000'.$CamParams{Contrast}.'true'; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; } @@ -421,7 +459,7 @@ sub whiteAbsOut { $CamParams{Contrast} = $min if ($CamParams{Contrast} < $min); my $cmd = 'onvif/imaging'; - my $msg ='000'.$CamParams{Contrast}.'true'; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '000'.$CamParams{Contrast}.'true'; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; } From 27344df373d835a46c5a64b08b12e71172aee4f8 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Tue, 30 Apr 2019 07:15:00 -0500 Subject: [PATCH 19/45] fix typo --- db/zm_create.sql.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 5ed80c7ed..787854091 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -789,7 +789,7 @@ INSERT INTO `Controls` VALUES (NULL,'IOS Camera','Ffmpeg','IPCAMIOS',0,0,0,0,0,0 INSERT INTO `Controls` VALUES (NULL,'Dericam P2','Ffmpeg','DericamP2',0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,45,0,0,1,0,0,0,0,1,1,45,0,0,0,0); INSERT INTO `Controls` VALUES (NULL,'Trendnet','Remote','Trendnet',1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); INSERT INTO `Controls` VALUES (NULL,'PSIA','Remote','PSIA',0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,0,1,1,1,0,0,1,0,1,0,0,0,0,1,-100,100,0,0,1,0,0,0,0,1,-100,100,0,0,0,0); -NSERT INTO `Controls` VALUES (NULL,'Dahua','Ffmpeg','Dahua',0,0,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,1,1,1,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); +INSERT INTO `Controls` VALUES (NULL,'Dahua','Ffmpeg','Dahua',0,0,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,1,1,1,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); INSERT INTO `Controls` VALUES (NULL,'FOSCAMR2C','Libvlc','FOSCAMR2C',1,1,1,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,12,0,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,4,0,NULL,1,NULL,NULL,NULL,NULL,1,0,4,0,NULL,0,0); INSERT INTO `Controls` VALUES (NULL,'Amcrest HTTP API','Ffmpeg','Amcrest_HTTP',0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,5); From 75b4f4f2b3f02aeaf8d50a189d94513e2b7600c6 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 30 Apr 2019 17:29:18 -0400 Subject: [PATCH 20/45] Add a few missing GREATESTs in the triggers. --- db/triggers.sql | 6 +- db/zm_update-1.33.8.sql | 283 ++++++++++++++++++++++++++++++++++++++++ version | 2 +- 3 files changed, 287 insertions(+), 4 deletions(-) create mode 100644 db/zm_update-1.33.8.sql diff --git a/db/triggers.sql b/db/triggers.sql index fb7a32b67..87c8465b4 100644 --- a/db/triggers.sql +++ b/db/triggers.sql @@ -4,7 +4,7 @@ DROP TRIGGER IF EXISTS Events_Hour_delete_trigger// CREATE TRIGGER Events_Hour_delete_trigger BEFORE DELETE ON Events_Hour FOR EACH ROW BEGIN UPDATE Monitors SET - HourEvents = COALESCE(HourEvents,1)-1, + HourEvents = GREATEST(COALESCE(HourEvents,1)-1,0), HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Id=OLD.MonitorId; END; @@ -62,7 +62,7 @@ DROP TRIGGER IF EXISTS Events_Week_delete_trigger// CREATE TRIGGER Events_Week_delete_trigger BEFORE DELETE ON Events_Week FOR EACH ROW BEGIN UPDATE Monitors SET - WeekEvents = COALESCE(WeekEvents,1)-1, + WeekEvents = GREATEST(COALESCE(WeekEvents,1)-1,0), WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Id=OLD.MonitorId; END; @@ -90,7 +90,7 @@ DROP TRIGGER IF EXISTS Events_Month_delete_trigger// CREATE TRIGGER Events_Month_delete_trigger BEFORE DELETE ON Events_Month FOR EACH ROW BEGIN UPDATE Monitors SET - MonthEvents = COALESCE(MonthEvents,1)-1, + MonthEvents = GREATEST(COALESCE(MonthEvents,1)-1,0), MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Id=OLD.MonitorId; END; diff --git a/db/zm_update-1.33.8.sql b/db/zm_update-1.33.8.sql new file mode 100644 index 000000000..a3fed9629 --- /dev/null +++ b/db/zm_update-1.33.8.sql @@ -0,0 +1,283 @@ + +delimiter // +DROP TRIGGER IF EXISTS Events_Hour_delete_trigger// +CREATE TRIGGER Events_Hour_delete_trigger BEFORE DELETE ON Events_Hour +FOR EACH ROW BEGIN + UPDATE Monitors SET + HourEvents = GREATEST(COALESCE(HourEvents,1)-1,0), + HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; +END; +// + +DROP TRIGGER IF EXISTS Events_Hour_update_trigger// + +CREATE TRIGGER Events_Hour_update_trigger AFTER UPDATE ON Events_Hour +FOR EACH ROW + BEGIN + declare diff BIGINT default 0; + + set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); + IF ( diff ) THEN + IF ( NEW.MonitorID != OLD.MonitorID ) THEN + UPDATE Monitors SET HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId; + UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId; + ELSE + UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId; + END IF; + END IF; + END; +// + +DROP TRIGGER IF EXISTS Events_Day_delete_trigger// +CREATE TRIGGER Events_Day_delete_trigger BEFORE DELETE ON Events_Day +FOR EACH ROW BEGIN + UPDATE Monitors SET + DayEvents = GREATEST(COALESCE(DayEvents,1)-1,0), + DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; +END; +// + +DROP TRIGGER IF EXISTS Events_Day_update_trigger; +CREATE TRIGGER Events_Day_update_trigger AFTER UPDATE ON Events_Day +FOR EACH ROW + BEGIN + declare diff BIGINT default 0; + + set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); + IF ( diff ) THEN + IF ( NEW.MonitorID != OLD.MonitorID ) THEN + UPDATE Monitors SET DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId; + UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId; + ELSE + UPDATE Monitors SET DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId; + END IF; + END IF; + END; + // + + +DROP TRIGGER IF EXISTS Events_Week_delete_trigger// +CREATE TRIGGER Events_Week_delete_trigger BEFORE DELETE ON Events_Week +FOR EACH ROW BEGIN + UPDATE Monitors SET + WeekEvents = GREATEST(COALESCE(WeekEvents,1)-1,0), + WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; +END; +// + +DROP TRIGGER IF EXISTS Events_Week_update_trigger; +CREATE TRIGGER Events_Week_update_trigger AFTER UPDATE ON Events_Week +FOR EACH ROW + BEGIN + declare diff BIGINT default 0; + + set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); + IF ( diff ) THEN + IF ( NEW.MonitorID != OLD.MonitorID ) THEN + UPDATE Monitors SET WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId; + UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId; + ELSE + UPDATE Monitors SET WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId; + END IF; + END IF; + END; + // + +DROP TRIGGER IF EXISTS Events_Month_delete_trigger// +CREATE TRIGGER Events_Month_delete_trigger BEFORE DELETE ON Events_Month +FOR EACH ROW BEGIN + UPDATE Monitors SET + MonthEvents = GREATEST(COALESCE(MonthEvents,1)-1,0), + MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; +END; +// + +DROP TRIGGER IF EXISTS Events_Month_update_trigger; +CREATE TRIGGER Events_Month_update_trigger AFTER UPDATE ON Events_Month +FOR EACH ROW + BEGIN + declare diff BIGINT default 0; + + set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); + IF ( diff ) THEN + IF ( NEW.MonitorID != OLD.MonitorID ) THEN + UPDATE Monitors SET MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace),0) WHERE Monitors.Id=OLD.MonitorId; + UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+COALESCE(NEW.DiskSpace) WHERE Monitors.Id=NEW.MonitorId; + ELSE + UPDATE Monitors SET MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId; + END IF; + END IF; + END; + // + +drop procedure if exists update_storage_stats// + +drop trigger if exists event_update_trigger// + +CREATE TRIGGER event_update_trigger AFTER UPDATE ON Events +FOR EACH ROW +BEGIN + declare diff BIGINT default 0; + + set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); + IF ( NEW.StorageId = OLD.StorageID ) THEN + IF ( diff ) THEN + UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) + diff,0) WHERE Id = OLD.StorageId; + END IF; + ELSE + IF ( NEW.DiskSpace ) THEN + UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) + NEW.DiskSpace WHERE Id = NEW.StorageId; + END IF; + IF ( OLD.DiskSpace ) THEN + UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) - OLD.DiskSpace,0) WHERE Id = OLD.StorageId; + END IF; + END IF; + + UPDATE Events_Hour SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + UPDATE Events_Day SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + UPDATE Events_Week SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + UPDATE Events_Month SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + + IF ( NEW.Archived != OLD.Archived ) THEN + IF ( NEW.Archived ) THEN + INSERT INTO Events_Archived (EventId,MonitorId,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.DiskSpace); + UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)+1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=NEW.MonitorId; + ELSEIF ( OLD.Archived ) THEN + DELETE FROM Events_Archived WHERE EventId=OLD.Id; + UPDATE Monitors + SET + ArchivedEvents = GREATEST(COALESCE(ArchivedEvents,0)-1,0), + ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; + ELSE + IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN + UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + UPDATE Monitors SET + ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; + END IF; + END IF; + ELSEIF ( NEW.Archived AND diff ) THEN + UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + END IF; + + IF ( diff ) THEN + UPDATE Monitors + SET + TotalEventDiskSpace = GREATEST(COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; + END IF; + +END; + +// + +DROP TRIGGER IF EXISTS event_insert_trigger// + +/* The assumption is that when an Event is inserted, it has no size yet, so don't bother updating the DiskSpace, just the count. + * The DiskSpace will get update in the Event Update Trigger + */ +CREATE TRIGGER event_insert_trigger AFTER INSERT ON Events +FOR EACH ROW + BEGIN + + INSERT INTO Events_Hour (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); + INSERT INTO Events_Day (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); + INSERT INTO Events_Week (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); + INSERT INTO Events_Month (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); + UPDATE Monitors SET + HourEvents = COALESCE(HourEvents,0)+1, + DayEvents = COALESCE(DayEvents,0)+1, + WeekEvents = COALESCE(WeekEvents,0)+1, + MonthEvents = COALESCE(MonthEvents,0)+1, + TotalEvents = COALESCE(TotalEvents,0)+1 + WHERE Id=NEW.MonitorId; +END; +// + +DROP TRIGGER IF EXISTS event_delete_trigger// + +CREATE TRIGGER event_delete_trigger BEFORE DELETE ON Events +FOR EACH ROW +BEGIN + IF ( OLD.DiskSpace ) THEN + UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) - COALESCE(OLD.DiskSpace,0),0) WHERE Id = OLD.StorageId; + END IF; + DELETE FROM Events_Hour WHERE EventId=OLD.Id; + DELETE FROM Events_Day WHERE EventId=OLD.Id; + DELETE FROM Events_Week WHERE EventId=OLD.Id; + DELETE FROM Events_Month WHERE EventId=OLD.Id; + IF ( OLD.Archived ) THEN + DELETE FROM Events_Archived WHERE EventId=OLD.Id; + UPDATE Monitors SET + ArchivedEvents = GREATEST(COALESCE(ArchivedEvents,1) - 1,0), + ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0), + TotalEvents = GREATEST(COALESCE(TotalEvents,1) - 1,0), + TotalEventDiskSpace = GREATEST(COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; + ELSE + UPDATE Monitors SET + TotalEvents = GREATEST(COALESCE(TotalEvents,1)-1,0), + TotalEventDiskSpace=GREATEST(COALESCE(TotalEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) + WHERE Id=OLD.MonitorId; + END IF; +END; + +// + +DROP TRIGGER IF EXISTS Zone_Insert_Trigger// +CREATE TRIGGER Zone_Insert_Trigger AFTER INSERT ON Zones +FOR EACH ROW + BEGIN + UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=NEW.MonitorId) WHERE Id=NEW.MonitorID; + END +// +DROP TRIGGER IF EXISTS Zone_Delete_Trigger// +CREATE TRIGGER Zone_Delete_Trigger AFTER DELETE ON Zones +FOR EACH ROW + BEGIN + UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=OLD.MonitorId) WHERE Id=OLD.MonitorID; + END +// + +DELIMITER ; + +REPLACE INTO Events_Hour SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 hour); +REPLACE INTO Events_Day SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 day); +REPLACE INTO Events_Week SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 week); +REPLACE INTO Events_Month SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 month); +REPLACE INTO Events_Archived SELECT Id,MonitorId,DiskSpace FROM Events WHERE Archived=1; + +UPDATE Monitors INNER JOIN ( + SELECT MonitorId, + COUNT(Id) AS TotalEvents, + SUM(DiskSpace) AS TotalEventDiskSpace, + SUM(IF(Archived,1,0)) AS ArchivedEvents, + SUM(IF(Archived,DiskSpace,0)) AS ArchivedEventDiskSpace, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),1,0)) AS HourEvents, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),DiskSpace,0)) AS HourEventDiskSpace, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),1,0)) AS DayEvents, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),DiskSpace,0)) AS DayEventDiskSpace, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),1,0)) AS WeekEvents, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),DiskSpace,0)) AS WeekEventDiskSpace, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),1,0)) AS MonthEvents, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),DiskSpace,0)) AS MonthEventDiskSpace + FROM Events GROUP BY MonitorId + ) AS E ON E.MonitorId=Monitors.Id SET + Monitors.TotalEvents = E.TotalEvents, + Monitors.TotalEventDiskSpace = E.TotalEventDiskSpace, + Monitors.ArchivedEvents = E.ArchivedEvents, + Monitors.ArchivedEventDiskSpace = E.ArchivedEventDiskSpace, + Monitors.HourEvents = E.HourEvents, + Monitors.HourEventDiskSpace = E.HourEventDiskSpace, + Monitors.DayEvents = E.DayEvents, + Monitors.DayEventDiskSpace = E.DayEventDiskSpace, + Monitors.WeekEvents = E.WeekEvents, + Monitors.WeekEventDiskSpace = E.WeekEventDiskSpace, + Monitors.MonthEvents = E.MonthEvents, + Monitors.MonthEventDiskSpace = E.MonthEventDiskSpace; + diff --git a/version b/version index 1304b01de..692c2e30d 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.33.7 +1.33.8 From ed02ef39f182aca60d152c3722dc0136b3fade1c Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Tue, 30 Apr 2019 18:11:38 -0500 Subject: [PATCH 21/45] bump rpm specfile --- distros/redhat/zoneminder.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index 440b05acc..8e542f658 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -23,7 +23,7 @@ %global _hardened_build 1 Name: zoneminder -Version: 1.33.6 +Version: 1.33.8 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons @@ -410,6 +410,9 @@ EOF %dir %attr(755,nginx,nginx) %{_localstatedir}/spool/zoneminder-upload %changelog +* Tue Apr 30 2019 Andrew Bauer - 1.33.8-1 +- Bump to 1.33.8 Development + * Sun Apr 07 2019 Andrew Bauer - 1.33.6-1 - Bump to 1.33.6 Development From 8b1565c41d1211e25852e2f1caa85d19432e714e Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 2 May 2019 10:52:19 -0400 Subject: [PATCH 22/45] Change pid path to /run/zm/zm.pid instead of /var/run/zm/zm.pid. systemd now complains about the use of a legacy directory, so this quiets that. --- distros/ubuntu1604/zoneminder.service | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distros/ubuntu1604/zoneminder.service b/distros/ubuntu1604/zoneminder.service index ac719b733..cb2d6791e 100644 --- a/distros/ubuntu1604/zoneminder.service +++ b/distros/ubuntu1604/zoneminder.service @@ -13,7 +13,7 @@ Type=forking ExecStart=/usr/bin/zmpkg.pl start ExecReload=/usr/bin/zmpkg.pl restart ExecStop=/usr/bin/zmpkg.pl stop -PIDFile=/var/run/zm/zm.pid +PIDFile=/run/zm/zm.pid Restart=always RestartSec=10 Environment=TZ=:/etc/localtime From 8733f89b297da43414e62ec3c52d3f0b76b76198 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Thu, 2 May 2019 20:09:15 -0500 Subject: [PATCH 23/45] eol f27 support --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6c41f1123..80541eaab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,7 +33,6 @@ install: env: - SMPFLAGS=-j4 OS=el DIST=7 - - SMPFLAGS=-j4 OS=fedora DIST=27 DOCKER_REPO=knnniggett/packpack - SMPFLAGS=-j4 OS=fedora DIST=28 DOCKER_REPO=knnniggett/packpack - SMPFLAGS=-j4 OS=fedora DIST=29 DOCKER_REPO=knnniggett/packpack - SMPFLAGS=-j4 OS=ubuntu DIST=trusty From fee95c23166231d8607fb1ce534adaa618554bce Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 3 May 2019 09:04:31 -0400 Subject: [PATCH 24/45] ifdef HAVE_ZLIB_H around code that uses Image->Zip --- src/zm_monitorstream.cpp | 8 ++++++++ src/zm_monitorstream.h | 2 ++ 2 files changed, 10 insertions(+) diff --git a/src/zm_monitorstream.cpp b/src/zm_monitorstream.cpp index 0b2811656..124994a48 100644 --- a/src/zm_monitorstream.cpp +++ b/src/zm_monitorstream.cpp @@ -394,10 +394,15 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) { img_buffer_size = send_image->Size(); break; case STREAM_ZIP : +#if HAVE_ZLIB_H fputs("Content-Type: image/x-rgbz\r\n",stdout); unsigned long zip_buffer_size; send_image->Zip(img_buffer, &zip_buffer_size); img_buffer_size = zip_buffer_size; +#else + Error("zlib is required for zipped images. Falling back to raw image"); + type = STREAM_RAW; +#endif // HAVE_ZLIB_H break; default : Error("Unexpected frame type %d", type); @@ -794,6 +799,8 @@ void MonitorStream::SingleImageRaw( int scale ) { fwrite( snap_image->Buffer(), snap_image->Size(), 1, stdout ); } + +#ifdef HAVE_ZLIB_H void MonitorStream::SingleImageZip( int scale ) { unsigned long img_buffer_size = 0; static Bytef img_buffer[ZM_MAX_IMAGE_SIZE]; @@ -816,3 +823,4 @@ void MonitorStream::SingleImageZip( int scale ) { fprintf( stdout, "Content-Type: image/x-rgbz\r\n\r\n" ); fwrite( img_buffer, img_buffer_size, 1, stdout ); } +#endif // HAVE_ZLIB_H diff --git a/src/zm_monitorstream.h b/src/zm_monitorstream.h index f3351d3b7..e120331af 100644 --- a/src/zm_monitorstream.h +++ b/src/zm_monitorstream.h @@ -55,7 +55,9 @@ class MonitorStream : public StreamBase { void processCommand( const CmdMsg *msg ); void SingleImage( int scale=100 ); void SingleImageRaw( int scale=100 ); +#ifdef HAVE_ZLIB_H void SingleImageZip( int scale=100 ); +#endif public: MonitorStream() : From f6b6daafab5cc78c6cef46a20ed4d639ba5ec9f9 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 3 May 2019 09:41:29 -0400 Subject: [PATCH 25/45] close and reopen event when we hit section_length --- src/zm_monitor.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 2987d08f2..53efd1485 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1725,6 +1725,8 @@ bool Monitor::Analyse() { timestamp->tv_sec - video_store_data->recording.tv_sec, section_length ); + closeEvent(); + event = new Event(this, *timestamp, cause, noteSetMap); } } // end if event From 74bd8126321d8f985d21fc4f8c5049a46145f26b Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Fri, 3 May 2019 09:07:19 -0500 Subject: [PATCH 26/45] rpm packaging - buildrequire zlib-devel --- distros/redhat/zoneminder.spec | 1 + 1 file changed, 1 insertion(+) diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index 8e542f658..f22b518f9 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -73,6 +73,7 @@ BuildRequires: libcurl-devel BuildRequires: libv4l-devel BuildRequires: desktop-file-utils BuildRequires: gzip +BuildRequires: zlib-devel # ZoneMinder looks for and records the location of the ffmpeg binary during build BuildRequires: ffmpeg From aec8311debef43c2af0945c544018908d9834585 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 3 May 2019 13:48:05 -0400 Subject: [PATCH 27/45] implement sorting incoming packets in the packetqueue --- src/zm_ffmpeg.cpp | 24 ++++++++++++++ src/zm_ffmpeg.h | 1 + src/zm_packetqueue.cpp | 74 ++++++++++++++++++++++++++++++++++++------ 3 files changed, 89 insertions(+), 10 deletions(-) diff --git a/src/zm_ffmpeg.cpp b/src/zm_ffmpeg.cpp index d49210cbf..98509f92f 100644 --- a/src/zm_ffmpeg.cpp +++ b/src/zm_ffmpeg.cpp @@ -571,3 +571,27 @@ void dumpPacket(AVStream *stream, AVPacket *pkt, const char *text) { pkt->duration); Debug(2, "%s:%d:%s: %s", __FILE__, __LINE__, text, b); } + +void dumpPacket(AVPacket *pkt, const char *text) { + char b[10240]; + + snprintf(b, sizeof(b), + " pts: %" PRId64 ", dts: %" PRId64 + ", size: %d, stream_index: %d, flags: %04x, keyframe(%d) pos: %" PRId64 + ", duration: %" +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + PRId64 +#else + "d" +#endif + "\n", + pkt->pts, + pkt->dts, + pkt->size, + pkt->stream_index, + pkt->flags, + pkt->flags & AV_PKT_FLAG_KEY, + pkt->pos, + pkt->duration); + Debug(2, "%s:%d:%s: %s", __FILE__, __LINE__, text, b); +} diff --git a/src/zm_ffmpeg.h b/src/zm_ffmpeg.h index 37a239509..7c4414db0 100644 --- a/src/zm_ffmpeg.h +++ b/src/zm_ffmpeg.h @@ -333,4 +333,5 @@ bool is_audio_context(AVCodec *); int zm_receive_frame( AVCodecContext *context, AVFrame *frame, AVPacket &packet ); void dumpPacket(AVStream *, AVPacket *,const char *text=""); +void dumpPacket(AVPacket *,const char *text=""); #endif // ZM_FFMPEG_H diff --git a/src/zm_packetqueue.cpp b/src/zm_packetqueue.cpp index 485ef8ffa..b5e53499a 100644 --- a/src/zm_packetqueue.cpp +++ b/src/zm_packetqueue.cpp @@ -35,19 +35,73 @@ zm_packetqueue::~zm_packetqueue() { } bool zm_packetqueue::queuePacket(ZMPacket* zm_packet) { - pktQueue.push_back(zm_packet); - packet_counts[zm_packet->packet.stream_index] += 1; - return true; -} + + if ( + ( zm_packet->packet.dts == AV_NOPTS_VALUE ) + || + ( packet_counts[zm_packet->packet.stream_index] <= 0 ) + ) { + Debug(2,"Inserting packet with dts %" PRId64 " because queue is empty or invalid dts", zm_packet->packet.dts); + // No dts value, can't so much with it + pktQueue.push_back(zm_packet); + packet_counts[zm_packet->packet.stream_index] += 1; + return true; + } + + std::list::reverse_iterator it = pktQueue.rbegin(); + + // Scan through the queue looking for a packet for our stream with a dts <= ours. + while ( it != pktQueue.rend() ) { + AVPacket *av_packet = &((*it)->packet); + + Debug(2, "Looking at packet with stream index (%d) with dts %" PRId64, + av_packet->stream_index, av_packet->dts); + if ( + ( av_packet->stream_index == zm_packet->packet.stream_index ) + && + ( av_packet->dts != AV_NOPTS_VALUE ) + && + ( av_packet->dts <= zm_packet->packet.dts) + ) { + Debug(2, "break packet with stream index (%d) with dts %" PRId64, + (*it)->packet.stream_index, (*it)->packet.dts); + break; + } + it++; + } // end while not the end of the queue + + if ( it != pktQueue.rend() ) { + Debug(2, "Found packet with stream index (%d) with dts %" PRId64, + (*it)->packet.stream_index, (*it)->packet.dts); + //it --; + //Debug(2, "Found packet with stream index (%d) with dts %" PRId64, + //(*it)->packet.stream_index, (*it)->packet.dts); + if ( it == pktQueue.rbegin() ) { + Debug(2,"Inserting packet with dts %" PRId64 " at end", zm_packet->packet.dts); + // No dts value, can't so much with it + pktQueue.push_back(zm_packet); + packet_counts[zm_packet->packet.stream_index] += 1; + return true; + } + // Convert to a forward iterator so that we can insert at end + std::list::iterator f_it = it.base(); + + Debug(2, "Insert packet with stream index (%d) with dts %" PRId64 " for dts %" PRId64, + (*f_it)->packet.stream_index, (*f_it)->packet.dts, zm_packet->packet.dts); + + pktQueue.insert(f_it, zm_packet); + + packet_counts[zm_packet->packet.stream_index] += 1; + return true; + } + Warning("Unable to insert packet for stream %d with dts %" PRId64 " into queue.", + zm_packet->packet.stream_index, zm_packet->packet.dts); + return false; +} // end bool zm_packetqueue::queuePacket(ZMPacket* zm_packet) bool zm_packetqueue::queuePacket(AVPacket* av_packet) { - ZMPacket *zm_packet = new ZMPacket(av_packet); - - pktQueue.push_back(zm_packet); - packet_counts[zm_packet->packet.stream_index] += 1; - - return true; + return queuePacket(zm_packet); } ZMPacket* zm_packetqueue::popPacket( ) { From c91da4a7f5b71b3f9a60d3dcb305a4dbfc37a49c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 3 May 2019 14:58:29 -0400 Subject: [PATCH 28/45] if no packet found, still append to end --- src/zm_packetqueue.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/zm_packetqueue.cpp b/src/zm_packetqueue.cpp index b5e53499a..c1fcc3db9 100644 --- a/src/zm_packetqueue.cpp +++ b/src/zm_packetqueue.cpp @@ -94,9 +94,11 @@ bool zm_packetqueue::queuePacket(ZMPacket* zm_packet) { packet_counts[zm_packet->packet.stream_index] += 1; return true; } - Warning("Unable to insert packet for stream %d with dts %" PRId64 " into queue.", + Debug(1,"Unable to insert packet for stream %d with dts %" PRId64 " into queue.", zm_packet->packet.stream_index, zm_packet->packet.dts); - return false; + pktQueue.push_back(zm_packet); + packet_counts[zm_packet->packet.stream_index] += 1; + return true; } // end bool zm_packetqueue::queuePacket(ZMPacket* zm_packet) bool zm_packetqueue::queuePacket(AVPacket* av_packet) { From 1e08b333b4705ff69ba8604fa6294048bad913f0 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 3 May 2019 14:59:09 -0400 Subject: [PATCH 29/45] choose cur_dts instead of 0 for dts --- src/zm_videostore.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index a8f3e3041..29a30a88e 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -911,7 +911,8 @@ int VideoStore::writeVideoFramePacket(AVPacket *ipkt) { video_last_pts = ipkt->pts; } else { Debug(3, "opkt.pts = undef"); - opkt.pts = 0; + opkt.pts = AV_NOPTS_VALUE; +// can't set 0, it will get rejected //AV_NOPTS_VALUE; } // Just because the in stream wraps, doesn't mean the out needs to. @@ -943,7 +944,8 @@ int VideoStore::writeVideoFramePacket(AVPacket *ipkt) { } } else { Debug(3, "opkt.dts = undef"); - opkt.dts = 0; + opkt.dts = video_out_stream->cur_dts; + //opkt.dts = 0; } # if 0 From f0e5a435cfab40d17a9edaec4a16e92a40824a49 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 6 May 2019 10:04:53 -0400 Subject: [PATCH 30/45] spacing and quotes, but the main change is using aud_print instead of Info --- scripts/zmaudit.pl.in | 64 +++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/scripts/zmaudit.pl.in b/scripts/zmaudit.pl.in index 41c8b0e75..d704e169b 100644 --- a/scripts/zmaudit.pl.in +++ b/scripts/zmaudit.pl.in @@ -488,21 +488,21 @@ MAIN: while( $loop ) { my $monitor_links; foreach my $link ( glob('*') ) { - next if ( !-l $link ); - next if ( -e $link ); + next if !-l $link; + next if -e $link; - aud_print( "Filesystem monitor link '$link' does not point to valid monitor directory" ); + aud_print("Filesystem monitor link '$link' does not point to valid monitor directory"); if ( confirm() ) { ( $link ) = ( $link =~ /^(.*)$/ ); # De-taint my $command = qq`rm "$link"`; - executeShellCommand( $command ); + executeShellCommand($command); $cleaned = 1; } - } + } # end foreach monitor link } # end foreach Storage Area if ( $cleaned ) { - Debug("Events were deleted, starting again."); + Debug('Events were deleted, starting again.'); redo MAIN; } @@ -559,8 +559,8 @@ EVENT: while ( my ( $db_event, $age ) = each( %$db_events ) ) { Warning("Event $$Event{Id} is Archived. Taking no further action on it."); next; } - if ( ! $Event->StartTime() ) { - Info("Event $$Event{Id} has no start time."); + if ( !$Event->StartTime() ) { + aud_print("Event $$Event{Id} has no start time."); if ( confirm() ) { $Event->delete(); $cleaned = 1; @@ -569,7 +569,7 @@ EVENT: while ( my ( $db_event, $age ) = each( %$db_events ) ) { } if ( ! $Event->EndTime() ) { if ( $age > $Config{ZM_AUDIT_MIN_AGE} ) { - Info("Event $$Event{Id} has no end time and is $age seconds old. deleting it."); + aud_print("Event $$Event{Id} has no end time and is $age seconds old. Deleting it."); if ( confirm() ) { $Event->delete(); $cleaned = 1; @@ -578,7 +578,7 @@ EVENT: while ( my ( $db_event, $age ) = each( %$db_events ) ) { } } if ( $Event->check_for_in_filesystem() ) { - Debug("Database event $$Event{Id} apparently exists at " . $Event->Path() ); + Debug("Database event $$Event{Id} apparently exists at " . $Event->Path()); } else { if ( $age > $Config{ZM_AUDIT_MIN_AGE} ) { aud_print("Database event '$db_monitor/$db_event' does not exist at " . $Event->Path().' in filesystem, deleting'); @@ -587,14 +587,14 @@ EVENT: while ( my ( $db_event, $age ) = each( %$db_events ) ) { $cleaned = 1; } } else { - aud_print( "Database event '".$Event->Path()." monitor:$db_monitor event:$db_event' does not exist in filesystem but too young to delete age: $age > MIN $Config{ZM_AUDIT_MIN_AGE}.\n" ); + aud_print("Database event '".$Event->Path()." monitor:$db_monitor event:$db_event' does not exist in filesystem but too young to delete age: $age > MIN $Config{ZM_AUDIT_MIN_AGE}."); } } # end if exists in filesystem } else { Debug("Found fs event for id $db_event, $age seconds old at " . $$fs_events{$db_event}->Path()); my $Event = ZoneMinder::Event->find_one( Id=>$db_event ); if ( $Event and ! $Event->check_for_in_filesystem() ) { - Warning("Not found at " . $Event->Path() . ' was found at ' . $$fs_events{$db_event}->Path() ); + Warning('Not found at ' . $Event->Path() . ' was found at ' . $$fs_events{$db_event}->Path()); Warning($Event->to_string()); Warning($$fs_events{$db_event}->to_string()); $$Event{Scheme} = '' if ! defined $$Event{Scheme}; @@ -622,7 +622,7 @@ EVENT: while ( my ( $db_event, $age ) = each( %$db_events ) ) { } # foreach db_event } # end foreach db_monitor if ( $cleaned ) { - Debug("Have done some cleaning, restarting."); + Debug('Have done some cleaning, restarting.'); redo MAIN; } @@ -904,25 +904,25 @@ FROM Frames WHERE EventId=?'; Monitors.MonthEvents = E.MonthEvents, Monitors.MonthEventDiskSpace = E.MonthEventDiskSpace `; - my $eventcounts_hour_sth = $dbh->prepare_cached( $eventcounts_hour_sql ); - my $eventcounts_day_sth = $dbh->prepare_cached( $eventcounts_day_sql ); - my $eventcounts_week_sth = $dbh->prepare_cached( $eventcounts_week_sql ); - my $eventcounts_month_sth = $dbh->prepare_cached( $eventcounts_month_sql ); - $eventcounts_hour_sth->execute( ) or Error( "Can't execute: ".$eventcounts_sth->errstr() ); - $eventcounts_day_sth->execute( ) or Error( "Can't execute: ".$eventcounts_sth->errstr() ); - $eventcounts_week_sth->execute( ) or Error( "Can't execute: ".$eventcounts_sth->errstr() ); - $eventcounts_month_sth->execute( ) or Error( "Can't execute: ".$eventcounts_sth->errstr() ); - sleep( $Config{ZM_AUDIT_CHECK_INTERVAL} ) if $continuous; + my $eventcounts_hour_sth = $dbh->prepare_cached($eventcounts_hour_sql); + my $eventcounts_day_sth = $dbh->prepare_cached($eventcounts_day_sql); + my $eventcounts_week_sth = $dbh->prepare_cached($eventcounts_week_sql); + my $eventcounts_month_sth = $dbh->prepare_cached($eventcounts_month_sql); + $eventcounts_hour_sth->execute() or Error("Can't execute: ".$eventcounts_sth->errstr()); + $eventcounts_day_sth->execute() or Error("Can't execute: ".$eventcounts_sth->errstr()); + $eventcounts_week_sth->execute() or Error("Can't execute: ".$eventcounts_sth->errstr()); + $eventcounts_month_sth->execute() or Error("Can't execute: ".$eventcounts_sth->errstr()); + sleep($Config{ZM_AUDIT_CHECK_INTERVAL}) if $continuous; }; Term(); sub aud_print { my $string = shift; - if ( ! $continuous ) { - print( $string ); + if ( !$continuous ) { + print($string); } else { - Info( $string ); + Info($string); } } @@ -932,13 +932,13 @@ sub confirm { my $yesno = 0; if ( $report ) { - print( "\n" ); + print("\n"); } elsif ( $interactive ) { - print( ", $prompt Y/n/q: " ); + print(", $prompt Y/n/q: "); my $char = <>; - chomp( $char ); + chomp($char); if ( $char eq 'q' ) { - exit( 0 ); + exit(0); } if ( !$char ) { $char = 'y'; @@ -946,13 +946,13 @@ sub confirm { $yesno = ( $char =~ /[yY]/ ); } else { if ( !$continuous ) { - print( ", $action\n" ); + print(", $action\n"); } else { - Info( $action ); + Info($action); } $yesno = 1; } - return( $yesno ); + return $yesno; } sub deleteSwapImage { From 1ca5eee53addfb071737167fe6b18bfe8983ee44 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 6 May 2019 10:45:40 -0400 Subject: [PATCH 31/45] spacing --- web/skins/classic/views/options.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/web/skins/classic/views/options.php b/web/skins/classic/views/options.php index 389d49fe3..06948eafb 100644 --- a/web/skins/classic/views/options.php +++ b/web/skins/classic/views/options.php @@ -353,18 +353,18 @@ foreach ( array_map('basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI checked="checked"/> 3 ) { + $options = explode('|', $value['Hint']); + if ( count($options) > 3 ) { ?> checked="checked"/> + checked="checked"/> Date: Mon, 6 May 2019 10:49:18 -0400 Subject: [PATCH 32/45] spacing --- web/includes/Storage.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/web/includes/Storage.php b/web/includes/Storage.php index 67264f298..b3caa3ffa 100644 --- a/web/includes/Storage.php +++ b/web/includes/Storage.php @@ -44,7 +44,7 @@ class Storage { } public function Path() { - if ( isset( $this->{'Path'} ) and ( $this->{'Path'} != '' ) ) { + if ( isset($this->{'Path'}) and ( $this->{'Path'} != '' ) ) { return $this->{'Path'}; } else if ( ! isset($this->{'Id'}) ) { $path = ZM_DIR_EVENTS; @@ -58,7 +58,7 @@ class Storage { return $this->{'Name'}; } public function Name() { - if ( isset( $this->{'Name'} ) and ( $this->{'Name'} != '' ) ) { + if ( isset($this->{'Name'}) and ( $this->{'Name'} != '' ) ) { return $this->{'Name'}; } else if ( ! isset($this->{'Id'}) ) { return 'Default'; @@ -73,7 +73,7 @@ class Storage { if ( array_key_exists($fn, $this) ) return $this->{$fn}; - if ( array_key_exists( $fn, $this->defaults ) ) + if ( array_key_exists($fn, $this->defaults) ) return $this->defaults{$fn}; $backTrace = debug_backtrace(); @@ -96,7 +96,7 @@ class Storage { $results = Storage::find($parameters, $options); if ( count($results) > 1 ) { - Error("Storage Returned more than 1"); + Error('Storage Returned more than 1'); return $results[0]; } else if ( count($results) ) { return $results[0]; @@ -116,7 +116,7 @@ class Storage { $fields[] = $field.' IS NULL'; } else if ( is_array($value) ) { $func = function(){return '?';}; - $fields[] = $field.' IN ('.implode(',', array_map($func, $value)). ')'; + $fields[] = $field.' IN ('.implode(',', array_map($func, $value)).')'; $values += $value; } else { @@ -165,11 +165,11 @@ class Storage { $total = $this->disk_total_space(); if ( ! $total ) { - Error('disk_total_space returned false for ' . $path ); + Error('disk_total_space returned false for ' . $path); return 0; } $used = $this->disk_used_space(); - $usage = round( ($used / $total) * 100); + $usage = round(($used / $total) * 100); //Logger::Debug("Used $usage = round( ( $used / $total ) * 100 )"); return $usage; } @@ -208,7 +208,7 @@ class Storage { public function event_disk_space() { # This isn't a function like this in php, so we have to add up the space used in each event. if ( (! array_key_exists('DiskSpace', $this)) or (!$this->{'DiskSpace'}) ) { - $used = dbFetchOne('SELECT SUM(DiskSpace) AS DiskSpace FROM Events WHERE StorageId=? AND DiskSpace IS NOT NULL', 'DiskSpace', array($this->Id()) ); + $used = dbFetchOne('SELECT SUM(DiskSpace) AS DiskSpace FROM Events WHERE StorageId=? AND DiskSpace IS NOT NULL', 'DiskSpace', array($this->Id())); foreach ( Event::find(array('StorageId'=>$this->Id(), 'DiskSpace'=>null)) as $Event ) { $Event->Storage($this); // Prevent further db hit @@ -221,7 +221,7 @@ class Storage { public function Server() { if ( ! array_key_exists('Server',$this) ) { - $this->{'Server'}= new Server( $this->{'ServerId'} ); + $this->{'Server'}= new Server($this->{'ServerId'}); } return $this->{'Server'}; } @@ -239,5 +239,5 @@ class Storage { } return json_encode($json); } -} +} // end class Storage ?> From 9ef912f2ba6eb44cbb24fc20888a3c3cefa95903 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 6 May 2019 10:50:12 -0400 Subject: [PATCH 33/45] add missing new event status info --- src/zm_monitor.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 53efd1485..f58d59783 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1727,6 +1727,11 @@ bool Monitor::Analyse() { ); closeEvent(); event = new Event(this, *timestamp, cause, noteSetMap); + shared_data->last_event = event->Id(); + //set up video store data + snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile()); + video_store_data->recording = event->StartTime(); + } } // end if event From faaec9e1d6742f747a5e327c7e79ab29b47cc2ac Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 6 May 2019 12:14:03 -0400 Subject: [PATCH 34/45] Another attempt to fix SQL Control values (#2600) --- db/zm_create.sql.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 787854091..a5f5cb70c 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -789,7 +789,7 @@ INSERT INTO `Controls` VALUES (NULL,'IOS Camera','Ffmpeg','IPCAMIOS',0,0,0,0,0,0 INSERT INTO `Controls` VALUES (NULL,'Dericam P2','Ffmpeg','DericamP2',0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,45,0,0,1,0,0,0,0,1,1,45,0,0,0,0); INSERT INTO `Controls` VALUES (NULL,'Trendnet','Remote','Trendnet',1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); INSERT INTO `Controls` VALUES (NULL,'PSIA','Remote','PSIA',0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,0,1,1,1,0,0,1,0,1,0,0,0,0,1,-100,100,0,0,1,0,0,0,0,1,-100,100,0,0,0,0); -INSERT INTO `Controls` VALUES (NULL,'Dahua','Ffmpeg','Dahua',0,0,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,1,1,1,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); +INSERT INTO `Controls` VALUES (NULL,'Dahua','Ffmpeg','Dahua',0,0,1,1,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,1,1,1,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); INSERT INTO `Controls` VALUES (NULL,'FOSCAMR2C','Libvlc','FOSCAMR2C',1,1,1,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,12,0,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,4,0,NULL,1,NULL,NULL,NULL,NULL,1,0,4,0,NULL,0,0); INSERT INTO `Controls` VALUES (NULL,'Amcrest HTTP API','Ffmpeg','Amcrest_HTTP',0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,5); From 3a7b49560af03aaae460b7d8df445b22ddadb419 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 6 May 2019 12:16:06 -0400 Subject: [PATCH 35/45] spacing --- src/zm_monitor.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index d587eebc5..55ef699f0 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -780,17 +780,17 @@ int Monitor::GetImage( int index, int scale ) { Snapshot *snap = &image_buffer[index]; Image *snap_image = snap->image; - alarm_image.Assign( *snap_image ); + alarm_image.Assign(*snap_image); //write_image.Assign( *snap_image ); if ( scale != ZM_SCALE_BASE ) { - alarm_image.Scale( scale ); + alarm_image.Scale(scale); } if ( !config.timestamp_on_capture ) { - TimestampImage( &alarm_image, snap->timestamp ); + TimestampImage(&alarm_image, snap->timestamp); } image = &alarm_image; } else { @@ -798,12 +798,12 @@ int Monitor::GetImage( int index, int scale ) { } static char filename[PATH_MAX]; - snprintf( filename, sizeof(filename), "Monitor%d.jpg", id ); - image->WriteJpeg( filename ); + snprintf(filename, sizeof(filename), "Monitor%d.jpg", id); + image->WriteJpeg(filename); } else { - Error( "Unable to generate image, no images in buffer" ); + Error("Unable to generate image, no images in buffer"); } - return( 0 ); + return 0; } struct timeval Monitor::GetTimestamp( int index ) const { @@ -814,11 +814,11 @@ struct timeval Monitor::GetTimestamp( int index ) const { if ( index != image_buffer_count ) { Snapshot *snap = &image_buffer[index]; - return( *(snap->timestamp) ); + return *(snap->timestamp); } else { static struct timeval null_tv = { 0, 0 }; - return( null_tv ); + return null_tv; } } From 1498027f12677368e7daf0be664e07b8b33d5aab Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 8 May 2019 22:45:04 -0400 Subject: [PATCH 36/45] Add option to attach the objdetect image in emails --- scripts/zmfilter.pl.in | 71 ++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/scripts/zmfilter.pl.in b/scripts/zmfilter.pl.in index 3f7e720c3..9a290ccd8 100644 --- a/scripts/zmfilter.pl.in +++ b/scripts/zmfilter.pl.in @@ -546,7 +546,7 @@ sub uploadArchFile { } if ( $archError ) { - return( 0 ); + return 0; } else { if ( $Config{ZM_UPLOAD_PROTOCOL} eq 'ftp' ) { Info('Uploading to '.$Config{ZM_UPLOAD_HOST}.' using FTP'); @@ -680,47 +680,58 @@ sub substituteTags { if ( $first_alarm_frame ) { $text =~ s/%EPI1%/$url?view=frame&mid=$Event->{MonitorId}&eid=$Event->{Id}&fid=$first_alarm_frame->{FrameId}/g; $text =~ s/%EPIM%/$url?view=frame&mid=$Event->{MonitorId}&eid=$Event->{Id}&fid=$max_alarm_frame->{FrameId}/g; - if ( $attachments_ref && $text =~ s/%EI1%//g ) { - my $path = generateImage($Event, $first_alarm_frame); - if ( -e $path ) { - push @$attachments_ref, { type=>'image/jpeg', path=>$path }; + if ( $attachments_ref ) { + if ( $text =~ s/%EI1%//g ) { + my $path = generateImage($Event, $first_alarm_frame); + if ( -e $path ) { + push @$attachments_ref, { type=>'image/jpeg', path=>$path }; + } } - } - if ( $attachments_ref && ( $text =~ s/%EIM%//g ) ) { - # Don't attach the same image twice - if ( !@$attachments_ref + if ( $text =~ s/%EIM%//g ) { + # Don't attach the same image twice + if ( !@$attachments_ref || ( $first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId} ) - ) { - my $path = generateImage($Event, $max_alarm_frame); + ) { + my $path = generateImage($Event, $max_alarm_frame); + if ( -e $path ) { + push @$attachments_ref, { type=>'image/jpeg', path=>$path }; + } else { + Warning("No image for EIM"); + } + } + } + if ( $text =~ s/%EI1A%//g ) { + my $path = generateImage($Event, $first_alarm_frame, 'analyse'); if ( -e $path ) { push @$attachments_ref, { type=>'image/jpeg', path=>$path }; } else { - Warning("No image for EIM"); + Warning("No image for EI1A"); } } - } - if ( $attachments_ref && $text =~ s/%EI1A%//g ) { - my $path = generateImage($Event, $first_alarm_frame, 'analyse'); - if ( -e $path ) { - push @$attachments_ref, { type=>'image/jpeg', path=>$path }; - } else { - Warning("No image for EI1A"); - } - } - if ( $attachments_ref && $text =~ s/%EIMA%//g ) { - # Don't attach the same image twice - if ( !@$attachments_ref + if ( $text =~ s/%EIMA%//g ) { + # Don't attach the same image twice + if ( !@$attachments_ref || ($first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId}) - ) { - my $path = generateImage($Event, $max_alarm_frame, 'analyse'); + ) { + my $path = generateImage($Event, $max_alarm_frame, 'analyse'); + if ( -e $path ) { + push @$attachments_ref, { type=>'image/jpeg', path=>$path }; + } else { + Warning('No image for EIMA'); + } + } + } + if ( $text =~ s/%EIMOD%//g ) { + my $path = $Event->Path().'/objdetect.jpg'; if ( -e $path ) { push @$attachments_ref, { type=>'image/jpeg', path=>$path }; } else { - Warning('No image for EIMA'); + Warning('No image for EIMOD at ' . $path); } } - } + + } # end if attachments_ref } # end if $first_alarm_frame if ( $attachments_ref ) { @@ -732,7 +743,7 @@ sub substituteTags { if ( !$format ) { return undef; } - push( @$attachments_ref, { type=>"video/$format", path=>$path } ); + push @$attachments_ref, { type=>"video/$format", path=>$path }; } } if ( $text =~ s/%EVM%//g ) { @@ -806,7 +817,7 @@ sub sendEmail { $ssmtp_location = qx('which ssmtp'); } if ( !$ssmtp_location ) { - Debug('Unable to find ssmtp, trying MIME::Lite->send'); + Warning('Unable to find ssmtp, trying MIME::Lite->send'); MIME::Lite->send('smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60); $mail->send(); } else { From d3a680aaa36e30e3970b0a3fc0c8c32933319854 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 10 May 2019 12:31:10 -0400 Subject: [PATCH 37/45] Set out_frame duration when resampling. Better error message if failed to write to fifo --- src/zm_videostore.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 0d3e22b4e..f8a04ff1c 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -1181,6 +1181,9 @@ int VideoStore::resample_audio() { ret = swr_convert_frame(resample_ctx, out_frame, in_frame); zm_dump_frame(out_frame, "Out frame after convert"); + // resampling doesn't change the duration, or set it. + out_frame->duration = in_frame->duration; + if ( ret < 0 ) { Error("Could not resample frame (error '%s')", av_make_error_string(ret).c_str()); @@ -1193,7 +1196,8 @@ int VideoStore::resample_audio() { /** Store the new samples in the FIFO buffer. */ ret = av_audio_fifo_write(fifo, (void **)out_frame->data, out_frame->nb_samples); if ( ret < out_frame->nb_samples ) { - Error("Could not write data to FIFO on %d written, expecting %d", ret, out_frame->nb_samples); + Error("Could not write data to FIFO. %d written, expecting %d. Reason %s", + ret, out_frame->nb_samples, av_make_error_string(ret).c_str()); return 0; } From 67c20aa976e529d5f00ca8ac7903e04af064983a Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 10 May 2019 12:58:54 -0400 Subject: [PATCH 38/45] fix frame->duration to frame->pkt_duration --- src/zm_videostore.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index f8a04ff1c..978dfd328 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -1182,7 +1182,7 @@ int VideoStore::resample_audio() { zm_dump_frame(out_frame, "Out frame after convert"); // resampling doesn't change the duration, or set it. - out_frame->duration = in_frame->duration; + out_frame->pkt_duration = in_frame->pkt_duration; if ( ret < 0 ) { Error("Could not resample frame (error '%s')", From d9f7e93df3a17842f351a39a005e4c52efc356e4 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 10 May 2019 14:27:51 -0400 Subject: [PATCH 39/45] Fix typo gegress to degrees. Fixes #2601 --- scripts/ZoneMinder/lib/ZoneMinder/Control/Amcrest_HTTP.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/Amcrest_HTTP.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/Amcrest_HTTP.pm index 7a89f353e..e95f86cba 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/Amcrest_HTTP.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/Amcrest_HTTP.pm @@ -189,7 +189,7 @@ sub moveAbs ## Up, Down, Left, Right, etc. ??? Doesn't make sense here... my $tilt_degrees = shift || 0; my $speed = shift || 1; Debug( "Move ABS" ); - $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1='.$pan_degress.'&arg2='.$tilt_degrees.'&arg3=0&arg4='.$speed ); + $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1='.$pan_degrees.'&arg2='.$tilt_degrees.'&arg3=0&arg4='.$speed ); } sub moveConUp From aada171440022296d2e660d99b0bc69d50a3726d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 12 May 2019 09:35:48 -0400 Subject: [PATCH 40/45] clean up some logic in Analyse --- src/zm_monitor.cpp | 198 +++++++++++++++++++++++---------------------- 1 file changed, 100 insertions(+), 98 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index d587eebc5..835dc4222 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1432,118 +1432,120 @@ bool Monitor::Analyse() { shared_data->active = signal; } // end if signal change - if ( (!signal_change && signal) && n_linked_monitors > 0 ) { - bool first_link = true; - Event::StringSet noteSet; - for ( int i = 0; i < n_linked_monitors; i++ ) { - // TODO: Shouldn't we try to connect? - if ( linked_monitors[i]->isConnected() ) { - if ( linked_monitors[i]->hasAlarmed() ) { - if ( !event ) { - if ( first_link ) { - if ( cause.length() ) - cause += ", "; - cause += LINKED_CAUSE; - first_link = false; + if ( (!signal_change) && signal) { + if ( n_linked_monitors > 0 ) { + bool first_link = true; + Event::StringSet noteSet; + for ( int i = 0; i < n_linked_monitors; i++ ) { + // TODO: Shouldn't we try to connect? + if ( linked_monitors[i]->isConnected() ) { + if ( linked_monitors[i]->hasAlarmed() ) { + if ( !event ) { + if ( first_link ) { + if ( cause.length() ) + cause += ", "; + cause += LINKED_CAUSE; + first_link = false; + } } - } - noteSet.insert(linked_monitors[i]->Name()); - score += 50; - } - } else { - linked_monitors[i]->connect(); - } - } - if ( noteSet.size() > 0 ) - noteSetMap[LINKED_CAUSE] = noteSet; - } - - //TODO: What happens is the event closes and sets recording to false then recording to true again so quickly that our capture daemon never picks it up. Maybe need a refresh flag? - if ( (!signal_change && signal) && (function == RECORD || function == MOCORD) ) { - if ( event ) { - Debug(3, "Have signal and recording with open event at (%d.%d)", timestamp->tv_sec, timestamp->tv_usec); - - if ( section_length - && ( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= section_length ) - && ( ! ( timestamp->tv_sec % section_length ) ) - ) { - Info("%s: %03d - Closing event %" PRIu64 ", section end forced %d - %d = %d >= %d", - name, image_count, event->Id(), - timestamp->tv_sec, video_store_data->recording.tv_sec, - timestamp->tv_sec - video_store_data->recording.tv_sec, - section_length - ); - closeEvent(); - } // end if section_length - } // end if event - - if ( ! event ) { - - // Create event - event = new Event(this, *timestamp, "Continuous", noteSetMap, videoRecording); - shared_data->last_event = event->Id(); - //set up video store data - snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile()); - video_store_data->recording = event->StartTime(); - - Info("%s: %03d - Opening new event %" PRIu64 ", section start", name, image_count, event->Id()); - - /* To prevent cancelling out an existing alert\prealarm\alarm state */ - if ( state == IDLE ) { - shared_data->state = state = TAPE; - } - - //if ( config.overlap_timed_events ) - if ( false ) { - int pre_index; - int pre_event_images = pre_event_count; - - if ( analysis_fps ) { - // If analysis fps is set, - // compute the index for pre event images in the dedicated buffer - pre_index = pre_event_buffer_count ? image_count%pre_event_buffer_count : 0; - - // Seek forward the next filled slot in to the buffer (oldest data) - // from the current position - while ( pre_event_images && !pre_event_buffer[pre_index].timestamp->tv_sec ) { - pre_index = (pre_index + 1)%pre_event_buffer_count; - // Slot is empty, removing image from counter - pre_event_images--; + noteSet.insert(linked_monitors[i]->Name()); + score += 50; } } else { - // If analysis fps is not set (analysis performed at capturing framerate), - // compute the index for pre event images in the capturing buffer - pre_index = ((index + image_buffer_count) - pre_event_count)%image_buffer_count; + linked_monitors[i]->connect(); + } + } // end foreach linked_monit + if ( noteSet.size() > 0 ) + noteSetMap[LINKED_CAUSE] = noteSet; + } // end if linked_monitors - // Seek forward the next filled slot in to the buffer (oldest data) - // from the current position - while ( pre_event_images && !image_buffer[pre_index].timestamp->tv_sec ) { - pre_index = (pre_index + 1)%image_buffer_count; - // Slot is empty, removing image from counter - pre_event_images--; - } + //TODO: What happens is the event closes and sets recording to false then recording to true again so quickly that our capture daemon never picks it up. Maybe need a refresh flag? + if ( function == RECORD || function == MOCORD ) { + if ( event ) { + Debug(3, "Have signal and recording with open event at (%d.%d)", timestamp->tv_sec, timestamp->tv_usec); + + if ( section_length + && ( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= section_length ) + && ( ! ( timestamp->tv_sec % section_length ) ) + ) { + Info("%s: %03d - Closing event %" PRIu64 ", section end forced %d - %d = %d >= %d", + name, image_count, event->Id(), + timestamp->tv_sec, video_store_data->recording.tv_sec, + timestamp->tv_sec - video_store_data->recording.tv_sec, + section_length + ); + closeEvent(); + } // end if section_length + } // end if event + + if ( ! event ) { + + // Create event + event = new Event(this, *timestamp, "Continuous", noteSetMap, videoRecording); + shared_data->last_event = event->Id(); + //set up video store data + snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile()); + video_store_data->recording = event->StartTime(); + + Info("%s: %03d - Opening new event %" PRIu64 ", section start", name, image_count, event->Id()); + + /* To prevent cancelling out an existing alert\prealarm\alarm state */ + if ( state == IDLE ) { + shared_data->state = state = TAPE; } - if ( pre_event_images ) { + //if ( config.overlap_timed_events ) + if ( false ) { + int pre_index; + int pre_event_images = pre_event_count; + if ( analysis_fps ) { - for ( int i = 0; i < pre_event_images; i++ ) { - timestamps[i] = pre_event_buffer[pre_index].timestamp; - images[i] = pre_event_buffer[pre_index].image; + // If analysis fps is set, + // compute the index for pre event images in the dedicated buffer + pre_index = pre_event_buffer_count ? image_count%pre_event_buffer_count : 0; + + // Seek forward the next filled slot in to the buffer (oldest data) + // from the current position + while ( pre_event_images && !pre_event_buffer[pre_index].timestamp->tv_sec ) { pre_index = (pre_index + 1)%pre_event_buffer_count; + // Slot is empty, removing image from counter + pre_event_images--; } } else { - for ( int i = 0; i < pre_event_images; i++ ) { - timestamps[i] = image_buffer[pre_index].timestamp; - images[i] = image_buffer[pre_index].image; + // If analysis fps is not set (analysis performed at capturing framerate), + // compute the index for pre event images in the capturing buffer + pre_index = ((index + image_buffer_count) - pre_event_count)%image_buffer_count; + + // Seek forward the next filled slot in to the buffer (oldest data) + // from the current position + while ( pre_event_images && !image_buffer[pre_index].timestamp->tv_sec ) { pre_index = (pre_index + 1)%image_buffer_count; + // Slot is empty, removing image from counter + pre_event_images--; } } - event->AddFrames( pre_event_images, images, timestamps ); - } - } // end if false or config.overlap_timed_events - } // end if ! event - } // end if ( (!signal_change && signal) && (function == RECORD || function == MOCORD) ) { + if ( pre_event_images ) { + if ( analysis_fps ) { + for ( int i = 0; i < pre_event_images; i++ ) { + timestamps[i] = pre_event_buffer[pre_index].timestamp; + images[i] = pre_event_buffer[pre_index].image; + pre_index = (pre_index + 1)%pre_event_buffer_count; + } + } else { + for ( int i = 0; i < pre_event_images; i++ ) { + timestamps[i] = image_buffer[pre_index].timestamp; + images[i] = image_buffer[pre_index].image; + pre_index = (pre_index + 1)%image_buffer_count; + } + } + + event->AddFrames( pre_event_images, images, timestamps ); + } + } // end if false or config.overlap_timed_events + } // end if ! event + } // end if function == RECORD || function == MOCORD) + } // end if !signal_change && signal if ( score ) { if ( state == IDLE || state == TAPE || state == PREALARM ) { From 453bc2afd8e401217b2b3231345ce2b70bd78d16 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 12 May 2019 09:36:26 -0400 Subject: [PATCH 41/45] more frame dumping in resample --- src/zm_videostore.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 6d7fba13e..01e5b7e6c 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -1011,9 +1011,9 @@ int VideoStore::writeAudioFramePacket(AVPacket *ipkt) { //av_frame_unref(in_frame); return 0; } - zm_dump_frame(out_frame, "Out frame after resample"); out_frame->pts = in_frame->pts; + zm_dump_frame(out_frame, "Out frame after resample"); // out_frame pts is in the input pkt pts... needs to be adjusted before sending to the encoder if ( out_frame->pts != AV_NOPTS_VALUE ) { if ( !audio_first_pts ) { @@ -1022,6 +1022,7 @@ int VideoStore::writeAudioFramePacket(AVPacket *ipkt) { out_frame->pts = 0; } else { out_frame->pts = out_frame->pts - audio_first_pts; + zm_dump_frame(out_frame, "Out frame after pts adjustment"); } // } else { @@ -1042,14 +1043,12 @@ int VideoStore::writeAudioFramePacket(AVPacket *ipkt) { if ( (ret = avcodec_receive_packet(audio_out_ctx, &opkt)) < 0 ) { if ( AVERROR(EAGAIN) == ret ) { // The codec may need more samples than it has, perfectly valid - Debug(3, "Could not recieve packet (error '%s')", - av_make_error_string(ret).c_str()); + Debug(2, "Codec not ready to give us a packet"); } else { Error("Could not recieve packet (error %d = '%s')", ret, av_make_error_string(ret).c_str()); } zm_av_packet_unref(&opkt); - // av_frame_unref( out_frame ); return 0; } #else @@ -1067,6 +1066,7 @@ int VideoStore::writeAudioFramePacket(AVPacket *ipkt) { return 0; } #endif + dumpPacket(audio_out_stream, &opkt, "raw opkt"); opkt.duration = av_rescale_q(opkt.duration, audio_in_stream->time_base, audio_out_stream->time_base); From 22c5d46c65b2b94bf25f977f1e0319cfe0462ad0 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 12 May 2019 12:14:03 -0400 Subject: [PATCH 42/45] rescale audio packet duration and pts before feeding to codec after resample --- src/zm_videostore.cpp | 41 +++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 978dfd328..2130db03b 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -1011,7 +1011,6 @@ int VideoStore::writeAudioFramePacket(AVPacket *ipkt) { } zm_dump_frame(out_frame, "Out frame after resample"); - out_frame->pts = in_frame->pts; // out_frame pts is in the input pkt pts... needs to be adjusted before sending to the encoder if ( out_frame->pts != AV_NOPTS_VALUE ) { if ( !audio_first_pts ) { @@ -1065,6 +1064,8 @@ int VideoStore::writeAudioFramePacket(AVPacket *ipkt) { return 0; } #endif +#if 0 + // These should be set by encoder. They may not directly relate to ipkt due to buffering in codec. opkt.duration = av_rescale_q(opkt.duration, audio_in_stream->time_base, audio_out_stream->time_base); @@ -1074,6 +1075,7 @@ int VideoStore::writeAudioFramePacket(AVPacket *ipkt) { opkt.dts = av_rescale_q(opkt.dts, audio_in_stream->time_base, audio_out_stream->time_base); +#endif dumpPacket(audio_out_stream, &opkt, "raw opkt"); } else { @@ -1179,16 +1181,32 @@ int VideoStore::resample_audio() { Debug(2, "Converting %d to %d samples using swresample", in_frame->nb_samples, out_frame->nb_samples); ret = swr_convert_frame(resample_ctx, out_frame, in_frame); - zm_dump_frame(out_frame, "Out frame after convert"); - - // resampling doesn't change the duration, or set it. - out_frame->pkt_duration = in_frame->pkt_duration; - if ( ret < 0 ) { Error("Could not resample frame (error '%s')", av_make_error_string(ret).c_str()); return 0; } + zm_dump_frame(out_frame, "Out frame after convert"); + + +#if 0 + // out_frame pts is in the input pkt pts... needs to be adjusted before sending to the encoder + if ( out_frame->pts != AV_NOPTS_VALUE ) { + if ( !audio_first_pts ) { + audio_first_pts = out_frame->pts; + Debug(1, "No audio_first_pts setting to %" PRId64, audio_first_pts); + out_frame->pts = 0; + } else { + out_frame->pts = out_frame->pts - audio_first_pts; + } + // + } else { + // sending AV_NOPTS_VALUE doesn't really work but we seem to get it in ffmpeg 2.8 + out_frame->pts = audio_next_pts; + } + audio_next_pts = out_frame->pts + out_frame->nb_samples; +#endif + if ((ret = av_audio_fifo_realloc(fifo, av_audio_fifo_size(fifo) + out_frame->nb_samples)) < 0) { Error("Could not reallocate FIFO"); return 0; @@ -1214,6 +1232,17 @@ int VideoStore::resample_audio() { return 0; } out_frame->nb_samples = frame_size; + // resampling changes the duration because the timebase is 1/samples + if ( in_frame->pts != AV_NOPTS_VALUE ) { + out_frame->pkt_duration = av_rescale_q( + in_frame->pkt_duration, + audio_in_stream->time_base, + audio_out_stream->time_base); + out_frame->pts = av_rescale_q( + in_frame->pts, + audio_in_stream->time_base, + audio_out_stream->time_base); + } #else #if defined(HAVE_LIBAVRESAMPLE) ret = avresample_convert(resample_ctx, NULL, 0, 0, in_frame->data, From 74d9f4f1aaae736132296083effedcb734996309 Mon Sep 17 00:00:00 2001 From: Jonathan Meredith <35303639+jimender2@users.noreply.github.com> Date: Mon, 13 May 2019 07:58:18 -0400 Subject: [PATCH 43/45] Spelling and grammar fixes in help (#2603) * Edit Help array to make it match others below. This should not affect the results * Misc. grammer and spelling fixes along with removing some duplicated words. This should not affect compilation. * More grammer and spelling errors * Replace Javascript with ZoneMinder because it did not make sense there. * More spelling and grammar edits --- .../lib/ZoneMinder/ConfigData.pm.in | 83 ++++++++++--------- web/lang/en_gb.php | 42 +++++----- web/skins/classic/views/monitor.php | 24 +++--- 3 files changed, 75 insertions(+), 74 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in index c6ec697f1..4d96f11db 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in @@ -197,8 +197,8 @@ our @options = ( name => 'ZM_BANDWIDTH_DEFAULT', default => 'high', description => 'Default setting for bandwidth profile used by web interface', - help => q`The classic skin for ZoneMinder has different - profiles to use for low medium or high bandwidth connections. + help => q`The classic skin for ZoneMinder has different + profiles to use for low, medium, or high bandwidth connections. `, type => $types{string}, category => 'system', @@ -547,7 +547,7 @@ our @options = ( higher quality setting than the ordinary file setting. If set to a lower value then it is ignored. Thus leaving it at the default of 0 effectively means to use the regular file quality - setting for all saved images. This is to prevent acccidentally + setting for all saved images. This is to prevent accidentally saving important images at a worse quality setting. `, type => $types{integer}, @@ -671,7 +671,7 @@ our @options = ( Internet Explorer that don't natively support this format. If you use this browser it is highly recommended to install this from the [cambozola project site](http://www.charliemouse.com/code/cambozola/). - However, if it is not installed still images at a lower refresh rate can + However, if it is not installed still images at a lower refresh rate can still be viewed. `, type => $types{boolean}, @@ -926,10 +926,10 @@ our @options = ( help => q` Due to browsers only wanting to open 6 connections, if you have more than 6 monitors, you can have trouble viewing more than 6. This setting - specified the beginning of a port range that will be used to contact ZM + specified the beginning of a port range that will be used to contact ZM on. Each monitor will use this value plus the Monitor Id to stream - content. So a value of 2000 here will cause a stream for Monitor 1 to - hit port 2001. Please ensure that you configure apache appropriately + content. So a value of 2000 here will cause a stream for Monitor 1 to + hit port 2001. Please ensure that you configure apache appropriately to respond on these ports.`, type => $types{integer}, category => 'network', @@ -1065,12 +1065,12 @@ our @options = ( default => '0', description => 'Save logging output to the system log', help => q` - ZoneMinder logging is now more more integrated between + ZoneMinder logging is now more integrated between components and allows you to specify the destination for logging output and the individual levels for each. This option lets you control the level of logging output that goes to the system log. ZoneMinder binaries have always logged to the - system log but now scripts and web logging is also included. To + system log but script and web logging is now included. To preserve the previous behaviour you should ensure this value is set to Info or Warning. This option controls the maximum level of logging that will be written, so Info includes Warnings and @@ -1092,7 +1092,7 @@ our @options = ( default => '-5', description => 'Save logging output to component files', help => q` - ZoneMinder logging is now more more integrated between + ZoneMinder logging is now more integrated between components and allows you to specify the destination for logging output and the individual levels for each. This option lets you control the level of logging output that goes to @@ -1122,7 +1122,7 @@ our @options = ( default => '-5', description => 'Save logging output to the weblog', help => q` - ZoneMinder logging is now more more integrated between + ZoneMinder logging is now more integrated between components and allows you to specify the destination for logging output and the individual levels for each. This option lets you control the level of logging output from the web @@ -1149,7 +1149,7 @@ our @options = ( default => '0', description => 'Save logging output to the database', help => q` - ZoneMinder logging is now more more integrated between + ZoneMinder logging is now more integrated between components and allows you to specify the destination for logging output and the individual levels for each. This option lets you control the level of logging output that is written to @@ -1204,7 +1204,7 @@ our @options = ( help => q` When enabled (default is on), this option will log FFMPEG messages. FFMPEG messages can be useful when debugging streaming issues. However, - depending on your distro and FFMPEG version, this may also result in + depending on your distro and FFMPEG version, this may also result in more logs than you'd typically like to see. If all your streams are working well, you may choose to turn this off. `, @@ -1219,7 +1219,7 @@ our @options = ( ZoneMinder components usually support debug logging available to help with diagnosing problems. Binary components have several levels of debug whereas more other components have only - one. Normally this is disabled to minimise performance + one. Normally this is disabled to minimize performance penalties and avoid filling logs too quickly. This option lets you switch on other options that allow you to configure additional debug information to be output. Components will pick @@ -1480,8 +1480,8 @@ our @options = ( default => 'ZoneMinder', description => 'The title displayed wherever the site references itself.', help => q` - If you want the site to identify as something other than ZoneMinder, change this here. - It can be used to more accurately identify this installation from others. + If you want the site to identify as something other than ZoneMinder, change this here. + It can be used to more accurately identify this installation from others. `, type => $types{string}, category => 'web', @@ -1504,8 +1504,8 @@ our @options = ( default => 'http://zoneminder.com', description => 'The url used in the home/logo area of the navigation bar.', help => q` - By default this takes you to the zoneminder.com website, - but perhaps you would prefer it to take you somewhere else. + By default this takes you to the zoneminder.com website, + but perhaps you would prefer it to take you somewhere else. `, type => $types{string}, category => 'web', @@ -1515,7 +1515,7 @@ our @options = ( default => 'ZoneMinder', description => 'The content of the home button.', help => q` - You may wish to set this to empty if you are using css to put a background image on it. + You may wish to set this to empty if you are using css to put a background image on it. `, type => $types{string}, category => 'web', @@ -1538,7 +1538,8 @@ our @options = ( name => 'ZM_WEB_EVENT_DISK_SPACE', default => 'no', description => 'Whether to show disk space used by each event.', - help => q`Adds another column to the listing of events + help => q` + Adds another column to the listing of events showing the disk space used by the event. This will impart a small overhead as it will call du on the event directory. In practice this overhead is fairly small but may be noticeable on IO-constrained @@ -1555,7 +1556,7 @@ our @options = ( Traditionally the main ZoneMinder web console window has resized itself to shrink to a size small enough to list only the monitors that are actually present. This is intended to - make the window more unobtrusize but may not be to everyones + make the window more unobtrusize but may not be to everyone's tastes, especially if opened in a tab in browsers which support this kind if layout. Switch this option off to have the console window size left to the users preference @@ -2105,7 +2106,7 @@ our @options = ( a remote ftp server. This option indicates that ftp transfers should be done in passive mode. This uses a single connection for all ftp activity and, whilst slower than active transfers, - is more robust and likely to work from behind filewalls. This + is more robust and likely to work from behind firewalls. This option is ignored for SFTP transfers. `, requires => [ { name => 'ZM_OPT_UPLOAD', value => 'yes' } ], @@ -2631,7 +2632,7 @@ our @options = ( help => q` As event images are captured they are stored to the filesystem with a numerical index. By default this index has three digits - so the numbers start 001, 002 etc. This works works for most + so the numbers start 001, 002 etc. This works for most scenarios as events with more than 999 frames are rarely captured. However if you have extremely long events and use external applications then you may wish to increase this to @@ -2728,7 +2729,7 @@ our @options = ( it to the ZoneMinder development team. This data will be used to determine things like who and where our customers are, how big their systems are, the underlying hardware and operating system, etc. - This is being done for the sole purpoase of creating a better + This is being done for the sole purpose of creating a better product for our target audience. This script is intended to be completely transparent to the end user, and can be disabled from the web console under Options. For more details on what information @@ -2752,7 +2753,7 @@ our @options = ( { name => 'ZM_TELEMETRY_LAST_UPLOAD', default => '', - description => 'When the last ZoneMinder telemetry upload ocurred', + description => 'When the last ZoneMinder telemetry upload occurred', help => '', type => $types{integer}, readonly => 1, @@ -2810,7 +2811,7 @@ our @options = ( default => 'javascript', description => 'What method windows should use to refresh themselves', help => q` - Many windows in Javascript need to refresh themselves to keep + Many windows in ZoneMinder need to refresh themselves to keep their information current. This option determines what method they should use to do this. Choosing 'javascript' means that each window will have a short JavaScript statement in with a @@ -2944,7 +2945,7 @@ our @options = ( some indication of the type of content. However this is not a standard part of HTML. The official method is to use OBJECT tags which are able to give more information allowing the - correct media viewers etc to be loaded. However these are less + correct media viewers etc. to be loaded. However these are less widely supported and content may be specifically tailored to a particular platform or player. This option controls whether media content is enclosed in EMBED tags only or whether, where @@ -2968,7 +2969,7 @@ our @options = ( browsers. When this condition occurs, ZoneMinder will write a warning to the log file. To get around this, one can install a browser plugin or extension to ignore X-Frame headers, and then the page will - display properly. Once the plugin or extenstion has ben installed, + display properly. Once the plugin or extension has ben installed, the end user may choose to turn this warning off. `, type => $types{boolean}, @@ -3082,7 +3083,7 @@ our @options = ( description => 'How often (in seconds) the event listing is refreshed in the watch window', help => q` The monitor window is actually made from several frames. The - lower framme contains a listing of the last few events for easy + lower frame contains a listing of the last few events for easy access. This option determines how often this is refreshed. `, type => $types{integer}, @@ -3212,12 +3213,12 @@ our @options = ( { name => 'ZM_WEB_H_SCALE_THUMBS', default => 'no', - description => 'Scale thumbnails in events, bandwidth versus cpu in rescaling', + description => 'Scale thumbnails in events, bandwidth versus CPU in rescaling', help => q` If unset, this option sends the whole image to the browser which resizes it in the window. If set the image is scaled down on the server before sending a reduced size image to the - browser to conserve bandwidth at the cost of cpu on the server. + browser to conserve bandwidth at the cost of CPU on the server. Note that ZM can only perform the resizing if the appropriate PHP graphics functionality is installed. This is usually available in the php-gd package. @@ -3251,7 +3252,7 @@ our @options = ( help => q` When viewing events an event navigation panel and progress bar is shown below the event itself. This allows you to jump to - specific points in the event, but can can also dynamically + specific points in the event, but can also dynamically update to display the current progress of the event replay itself. This progress is calculated from the actual event duration and is not directly linked to the replay itself, so on @@ -3355,7 +3356,7 @@ our @options = ( description => 'How often (in seconds) the event listing is refreshed in the watch window', help => q` The monitor window is actually made from several frames. The - lower framme contains a listing of the last few events for easy + lower frame contains a listing of the last few events for easy access. This option determines how often this is refreshed. `, type => $types{integer}, @@ -3485,12 +3486,12 @@ our @options = ( { name => 'ZM_WEB_M_SCALE_THUMBS', default => 'yes', - description => 'Scale thumbnails in events, bandwidth versus cpu in rescaling', + description => 'Scale thumbnails in events, bandwidth versus CPU in rescaling', help => q` If unset, this option sends the whole image to the browser which resizes it in the window. If set the image is scaled down on the server before sending a reduced size image to the - browser to conserve bandwidth at the cost of cpu on the server. + browser to conserve bandwidth at the cost of CPU on the server. Note that ZM can only perform the resizing if the appropriate PHP graphics functionality is installed. This is usually available in the php-gd package. @@ -3524,7 +3525,7 @@ our @options = ( help => q` When viewing events an event navigation panel and progress bar is shown below the event itself. This allows you to jump to - specific points in the event, but can can also dynamically + specific points in the event, but can also dynamically update to display the current progress of the event replay itself. This progress is calculated from the actual event duration and is not directly linked to the replay itself, so on @@ -3628,7 +3629,7 @@ our @options = ( description => 'How often (in seconds) the event listing is refreshed in the watch window', help => q` The monitor window is actually made from several frames. The - lower framme contains a listing of the last few events for easy + lower frame contains a listing of the last few events for easy access. This option determines how often this is refreshed. `, type => $types{integer}, @@ -3757,12 +3758,12 @@ our @options = ( { name => 'ZM_WEB_L_SCALE_THUMBS', default => 'yes', - description => 'Scale thumbnails in events, bandwidth versus cpu in rescaling', + description => 'Scale thumbnails in events, bandwidth versus CPU in rescaling', help => q` If unset, this option sends the whole image to the browser which resizes it in the window. If set the image is scaled down on the server before sending a reduced size image to the - browser to conserve bandwidth at the cost of cpu on the server. + browser to conserve bandwidth at the cost of CPU on the server. Note that ZM can only perform the resizing if the appropriate PHP graphics functionality is installed. This is usually available in the php-gd package. @@ -3796,7 +3797,7 @@ our @options = ( help => q` When viewing events an event navigation panel and progress bar is shown below the event itself. This allows you to jump to - specific points in the event, but can can also dynamically + specific points in the event, but can also dynamically update to display the current progress of the event replay itself. This progress is calculated from the actual event duration and is not directly linked to the replay itself, so on @@ -3988,7 +3989,7 @@ saveConfigToDB(); The ZoneMinder:ConfigData module contains the master definition of the ZoneMinder configuration options as well as helper methods. This module is -intended for specialist confguration management and would not normally be +intended for specialist configuration management and would not normally be used by end users. The configuration held in this module, which was previously in zmconfig.pl, diff --git a/web/lang/en_gb.php b/web/lang/en_gb.php index 945d26bea..72d6de8ab 100644 --- a/web/lang/en_gb.php +++ b/web/lang/en_gb.php @@ -32,7 +32,7 @@ // a formatting string. If the dynamic element is a number you will usually need to use a variable // replacement also as described below. // c) Variable replacements are used in conjunction with complex replacements and involve the generation -// of a singular or plural noun depending on the number passed into the zmVlang function. See the +// of a singular or plural noun depending on the number passed into the zmVlang function. See the // the zmVlang section below for a further description of this. // d) Optional strings which can be used to replace the prompts and/or help text for the Options section // of the web interface. These are not listed below as they are quite large and held in the database @@ -40,7 +40,7 @@ // quite easily from the Config table in the database if necessary. // 3. The tokens listed below are not used to build up phrases or sentences from single words. Therefore // you can safely assume that a single word token will only be used in that context. -// 4. In new language files, or if you are changing only a few words or phrases it makes sense from a +// 4. In new language files, or if you are changing only a few words or phrases it makes sense from a // maintenance point of view to include the original language file and override the old definitions rather // than copy all the language tokens across. To do this change the line below to whatever your base language // is and uncomment it. @@ -57,10 +57,10 @@ // If you do need to change your locale, be aware that the format of this function // is subtlely different in versions of PHP before and after 4.3.0, see // http://uk2.php.net/manual/en/function.setlocale.php for details. -// Also be aware that changing the whole locale may affect some floating point or decimal +// Also be aware that changing the whole locale may affect some floating point or decimal // arithmetic in the database, if this is the case change only the individual locale areas // that don't affect this rather than all at once. See the examples below. -// Finally, depending on your setup, PHP may not enjoy have multiple locales in a shared +// Finally, depending on your setup, PHP may not enjoy have multiple locales in a shared // threaded environment, if you get funny errors it may be this. // // Examples @@ -768,7 +768,7 @@ $SLANG = array( 'Update' => 'Update', 'Upload' => 'Upload', 'Updated' => 'Updated', - 'UsedPlugins' => 'Used Plugins', + 'UsedPlugins' => 'Used Plugins', 'UseFilterExprsPost' => ' filter expressions', // This is used at the end of the phrase 'use N filter expressions' 'UseFilterExprsPre' => 'Use ', // This is used at the beginning of the phrase 'use N filter expressions' 'UseFilter' => 'Use Filter', @@ -847,7 +847,7 @@ $CLANG = array( 'VersionMismatch' => 'Version mismatch, system is version %1$s, database is %2$s.', ); -// The next section allows you to describe a series of word ending and counts used to +// The next section allows you to describe a series of word ending and counts used to // generate the correctly conjugated forms of words depending on a count that is associated // with that word. // This intended to allow phrases such a '0 potatoes', '1 potato', '2 potatoes' etc to @@ -888,7 +888,7 @@ $VLANG = array( // with variable counts. This is used to conjugate the Vlang arrays above with a number passed // in to generate the correct noun form. // -// In languages such as English this is fairly simple +// In languages such as English this is fairly simple // Note this still has to be used with printf etc to get the right formatting function zmVlang( $langVarArray, $count ) { @@ -906,9 +906,9 @@ function zmVlang( $langVarArray, $count ) // This is an version that could be used in the Russian example above // The rules are that the first word form is used if the count ends in // 0, 5-9 or 11-19. The second form is used then the count ends in 1 -// (not including 11 as above) and the third form is used when the +// (not including 11 as above) and the third form is used when the // count ends in 2-4, again excluding any values ending in 12-14. -// +// // function zmVlang( $langVarArray, $count ) // { // $secondlastdigit = substr( $count, -2, 1 ); @@ -916,7 +916,7 @@ function zmVlang( $langVarArray, $count ) // // or // // $secondlastdigit = ($count/10)%10; // // $lastdigit = $count%10; -// +// // // Get rid of the special cases first, the teens // if ( $secondlastdigit == 1 && $lastdigit != 0 ) // { @@ -950,7 +950,7 @@ function zmVlang( $langVarArray, $count ) // die( 'Error, unable to correlate variable language string' ); // } -// This is an example of how the function is used in the code which you can uncomment and +// This is an example of how the function is used in the code which you can uncomment and // use to test your custom function. //$monitors = array(); //$monitors[] = 1; // Choose any number @@ -967,17 +967,17 @@ $OLANG = array( "\"reorder_queue_size=nnn\" Set number of packets to buffer for handling of reordered packets~~~~". "\"loglevel=debug\" Set verbosity of FFmpeg (quiet, panic, fatal, error, warning, info, verbose, debug)" ), - 'OPTIONS_RTSPTrans' => array( + 'OPTIONS_RTSPTrans' => array( 'Help' => "This sets the RTSP Transport Protocol for FFmpeg.~~ ". - "TCP - Use TCP (interleaving within the RTSP control channel) as transport protocol.~~". - "UDP - Use UDP as transport protocol. Higher resolution cameras have experienced some 'smearing' while using UDP, if so try TCP~~". - "UDP Multicast - Use UDP Multicast as transport protocol~~". - "HTTP - Use HTTP tunneling as transport protocol, which is useful for passing proxies.~~" + "TCP - Use TCP (interleaving within the RTSP control channel) as transport protocol.~~". + "UDP - Use UDP as transport protocol. Higher resolution cameras have experienced some 'smearing' while using UDP, if so try TCP~~". + "UDP Multicast - Use UDP Multicast as transport protocol~~". + "HTTP - Use HTTP tunneling as transport protocol, which is useful for passing proxies.~~" ), 'OPTIONS_LIBVLC' => array( 'Help' => "Parameters in this field are passed on to libVLC. Multiple parameters can be separated by ,~~ ". "Examples (do not enter quotes)~~~~". - "\"--rtp-client-port=nnn\" Set local port to use for rtp data~~~~". + "\"--rtp-client-port=nnn\" Set local port to use for rtp data~~~~". "\"--verbose=2\" Set verbosity of libVLC" ), 'OPTIONS_EXIF' => array( @@ -986,7 +986,7 @@ $OLANG = array( 'OPTIONS_RTSPDESCRIBE' => array( 'Help' => "Sometimes, during the initial RTSP handshake, the camera will send an updated media URL. ". "Enable this option to tell ZoneMinder to use this URL. Disable this option to ignore the ". - "value from the camera and use the value as entered in the monitor configuration~~~~". + "value from the camera and use the value as entered in the monitor configuration~~~~". "Generally this should be enabled. However, there are cases where the camera can get its". "own URL incorrect, such as when the camera is streaming through a firewall"), 'OPTIONS_MAXFPS' => array( @@ -994,12 +994,12 @@ $OLANG = array( "Failure to adhere to these limitations will cause a delay in live video, irregular frame skipping, ". "and missed events~~". "For streaming IP cameras, do not use this field to reduce the frame rate. Set the frame rate in the". - " camera, instead. You can, however, use a value that is slightly higher than the frame rate in the camera. ". - "In this case, this helps keep the cpu from being overtaxed in the event of a network problem.~~". + " camera, instead. You can, however, use a value that is slightly higher than the frame rate in the camera. ". + "In this case, this helps keep the cpu from being overtaxed in the event of a network problem.~~". "Some, mostly older, IP cameras support snapshot mode. In this case ZoneMinder is actively polling the camera ". "for new images. In this case, it is safe to use the field." ), - + // 'LANG_DEFAULT' => array( // 'Prompt' => "This is a new prompt for this option", // 'Help' => "This is some new help for this option which will be displayed in the popup window when the ? is clicked" diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index a0d6c7e91..d6d7780b7 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -39,7 +39,7 @@ if ( ! empty($_REQUEST['mid']) ) { $monitor = new ZM\Monitor( $_REQUEST['mid'] ); if ( $monitor and ZM_OPT_X10 ) $x10Monitor = dbFetchOne('SELECT * FROM TriggersX10 WHERE MonitorId = ?', NULL, array($_REQUEST['mid'])); -} +} if ( ! $monitor ) { $nextId = getTableAutoInc('Monitors'); @@ -132,9 +132,9 @@ if ( ! $monitor ) { if ( ZM_OPT_X10 && empty($x10Monitor) ) { $x10Monitor = array( - 'Activation' => '', - 'AlarmInput' => '', - 'AlarmOutput' => '', + 'Activation' => '', + 'AlarmInput' => '', + 'AlarmOutput' => '', ); } @@ -166,7 +166,7 @@ if ( $monitor->AlarmMaxFPS() == '0.00' ) if ( !empty($_REQUEST['preset']) ) { $preset = dbFetchOne( 'SELECT Type, Device, Channel, Format, Protocol, Method, Host, Port, Path, Width, Height, Palette, MaxFPS, Controllable, ControlId, ControlDevice, ControlAddress, DefaultRate, DefaultScale FROM MonitorPresets WHERE Id = ?', NULL, array($_REQUEST['preset']) ); foreach ( $preset as $name=>$value ) { - # Does isset handle NULL's? I don't think this code is correct. + # Does isset handle NULL's? I don't think this code is correct. if ( isset($value) ) { $monitor->$name = $value; } @@ -176,7 +176,7 @@ if ( !empty($_REQUEST['probe']) ) { $probe = json_decode(base64_decode($_REQUEST['probe'])); foreach ( $probe as $name=>$value ) { if ( isset($value) ) { - # Does isset handle NULL's? I don't think this code is correct. + # Does isset handle NULL's? I don't think this code is correct. $monitor->$name = urldecode($value); } } @@ -683,7 +683,7 @@ switch ( $tab ) { ?> -'None','auto'=>'Auto'); foreach ( ZM\Server::find(NULL, array('order'=>'lower(Name)')) as $Server ) { $servers[$Server->Id()] = $Server->Name(); @@ -763,7 +763,7 @@ echo htmlOptions(ZM\Group::get_dropdown_options( ), $monitor->GroupIds() ); - + Type() == 'NVSocket' ) { include('_monitor_source_nvsocket.php'); } else if ( $monitor->Type() == 'Remote' ) { @@ -894,10 +894,10 @@ if ( $monitor->Type() != 'NVSocket' && $monitor->Type() != 'WebSite' ) { () () - + Orientation() );?> Type() == 'Local' ) { ?> @@ -920,7 +920,7 @@ if ( $monitor->Type() == 'Local' ) { ?> - 'Disabled', ); From 7f704263d8a8fd420c3f3886198f7ae0db60e284 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 13 May 2019 10:30:41 -0400 Subject: [PATCH 44/45] If running a custom run state, show the state instead of Running. Also select the running state in the state change popup. --- web/skins/classic/includes/functions.php | 13 ++++++++++--- web/skins/classic/views/state.php | 4 +++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/web/skins/classic/includes/functions.php b/web/skins/classic/includes/functions.php index 2d71b2397..40bf18b30 100644 --- a/web/skins/classic/includes/functions.php +++ b/web/skins/classic/includes/functions.php @@ -233,7 +233,12 @@ function getNavBarHTML($reload = null) { ob_start(); if ( $running == null ) $running = daemonCheck(); - $status = $running?translate('Running'):translate('Stopped'); + if ( $running ) { + $state = dbFetchOne('SELECT Name FROM States WHERE isActive=1', 'Name'); + if ( $state == 'default' ) + $state = ''; + } + $status = $running ? ($state ? $state : translate('Running')) : translate('Stopped'); ?>