From 5a561b25589d2340e56c671f4fbd8ac2ce3688a3 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 19 Jul 2019 16:39:18 -0400 Subject: [PATCH 001/394] Some doc updates to matches changes in UI. --- docs/faq.rst | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/docs/faq.rst b/docs/faq.rst index a703fd065..c80d00a8a 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -8,18 +8,21 @@ How can I stop ZoneMinder filling up my disk? --------------------------------------------- Recent versions of ZoneMinder come with a filter you can use for this purpose already included. -The filter is called **PurgeWhenFull** and to find it, choose one of the event counts from the console page, for instance events in the last hour, for one of your monitors. **Note** that this filter is automatically enabled if you do a fresh install of ZoneMinder including creating a new database. If you already have an existing database and are upgrading ZoneMinder, it will retain the settings of the filter (which in earlier releases was disabled by default). So you may want to check if PurgeWhenFull is enabled and if not, enable it. +The filter is called **PurgeWhenFull** and to find it, click on the word **Filters** in the header. +**Note** that this filter is automatically enabled if you do a fresh install of ZoneMinder including creating a new database. If you already have an existing database and are upgrading ZoneMinder, it will retain the settings of the filter (which in earlier releases was disabled by default). So you may want to check if PurgeWhenFull is enabled and if not, enable it. -To enable it, go to Web Console, click on any of your Events of any of your monitors. -This will bring up an event listing and a filter window. +To enable it, go to Web Console, click on the word **Filters** in the UI header. -In the filter window there is a drop down select box labeled 'Use Filter', that lets your select a saved filter. Select 'PurgeWhenFull' and it will load that filter. +In the filter window there is a drop down select box labeled 'Use Filter', that lets you select a saved filter. Select 'PurgeWhenFull' and it will load that filter. Make any modifications you might want, such as the percentage full you want it to kick in, or how many events to delete at a time (it will repeat the filter as many times as needed to clear the space, but will only delete this many events each time to get there). -Then click on 'Save' which will bring up a new window. Make sure the 'Automatically delete' box is checked and press save to save your filter. This will then run in the background to keep your disk within those limits. +Ensure that the Run filter in background checkbox is checked. +Ensure that the Delete all matches checkbox is checked. -After you've done that, you changes will automatically be loaded into zmfilter within a few minutes. +Then click on 'Save'. The filter will immediately begin executing in the background to keep your disk within those limits. + +Please note that that this filter will only affect the default storage location. If you have added other storage areas, you must create a PurgeWhenFull filter for each one, and specify the Storage Area as one of the parameters in the filter. You can duplicate the existing PurgeWhenFull filter by using Save As instead of Save. Check the ``zmfilter.log`` file to make sure it is running as sometimes missing perl modules mean that it never runs but people don't always realize. @@ -42,7 +45,7 @@ Normally an event created as the result of an alarm consists of entries in one o ZM_RUN_AUDIT: -The zmaudit daemon exists to check that the saved information in the database and on the file system match and are consistent with each other. If an error occurs or if you are using 'fast deletes' it may be that database records are deleted but files remain. In this case, and similar, zmaudit will remove redundant information to synchronize the two data stores. This option controls whether zmaudit is run in the background and performs these checks and fixes continuously. This is recommended for most systems however if you have a very large number of events the process of scanning the database and file system may take a long time and impact performance. In this case you may prefer to not have zmaudit running unconditionally and schedule occasional checks at other, more convenient, times. +The zmaudit daemon exists to check that the saved information in the database and on the file system match and are consistent with each other. If an error occurs or if you are using 'fast deletes' it may be that database records are deleted but files remain. In this case, and similar, zmaudit will remove redundant information to synchronize the two data stores. This option controls whether zmaudit is run in the background and performs these checks and fixes continuously. This is not recommended for most systems, as zmaudit.pl is very resource intensive. @@ -280,14 +283,27 @@ Also, please check the value of the ZM_PATH_ZMS setting under the Paths Options Lastly, please look for errors created by the zmc processes. If zmc isn't running, then zms will not be able to get an image from it and will exit. -I have several monitors configured but when I load the Montage view in FireFox why can I only see two? or, Why don't all my cameras display when I use the Montage view in FireFox? +I have several monitors configured but when I load the Montage view why can I only see two? or, Why don't all my cameras display when I use the Montage view? -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -By default FireFox only supports a small number of simultaneous connections. Using the montage view usually requires one persistent connection for each camera plus intermittent connections for other information such as statuses. +By default most browsers only support a small number of simultaneous connections to any given server. Using the montage view usually requires one persistent connection for each camera plus intermittent connections for other information such as statuses. -You will need to increase the number of allowed connections to use the montage view with more than a small number of cameras. Certain FireFox extensions such as FasterFox may also help to achieve the same result. +In firefox you can increase the limit, but other browsers are not configurable in this way. -To resolve this situation, follow the instructions below: +A solution for all browsers is something we call multi-port. We reconfigure apache to operate on ports other than the default of 80(http) or 443(https). You need to pick a range, let's say 30000 to 30010 in order to support 10 cameras. We add lines to your zoneminder apache config file as follows: + +Listen 30000 +Listen 30001 +Listen 30002 +Listen 30003 +etc +Listen 30010 + +If you are using virtualhosts, you will have to add these to the VirtualHost directive as well. + +Then in ZoneMinder config, Go Options -> Network and set MIN_STREAMING_PORT to 30000. Now when generating urls to stream images from ZoneMinder a port will be appended that is 30000 + MonitorId, so Monitor 1 will stream from 30001 and so on. This will allow Montage to stream from all monitors. + +Alternatively if you are in fact using only Firefox, you can increase the limit as follows: Enter ``about:config`` in the address bar From 8aecf89466b01c3274dab013d462ac590e0fd46f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 24 Sep 2019 10:12:08 -0400 Subject: [PATCH 002/394] documentation updates: add missing filter substitutions, rough in storage areas documentation --- docs/userguide/filterevents.rst | 31 ++++++++++++++++--------------- docs/userguide/options.rst | 2 ++ 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/docs/userguide/filterevents.rst b/docs/userguide/filterevents.rst index d3c4955c9..973755641 100644 --- a/docs/userguide/filterevents.rst +++ b/docs/userguide/filterevents.rst @@ -4,7 +4,7 @@ Filtering Events Filters allow you to define complex conditions with associated actions in ZoneMinder. Examples could include: * Send an email each time a new event occurs for a specific monitor -* Delete events that are more than 10 days old +* Delete events that are more than 10 days old And many more. @@ -27,11 +27,16 @@ Here is what the filter window looks like * Archive all matches: sets the archive field to 1 in the Database for the matched events. Think of 'archiving' as grouping them under a special category - you can view archived events later and also make sure archived events don't get deleted, for example + * Update used disk space: calculates how much disk space is taken by the event and updates the db record. + * Create video for all matches: ffmpeg will be used to create a video file out of all the stored jpgs if using jpeg storage. * Email details of all matches: Sends an email to the configured address with details about the event. The email can be customized as per TBD - * Execute command on all matches: Allows you to execute any arbitrary command on the matched events. You can use replacement tokens as subsequent arguents to the command, the last argument will be the absolute path to the event, preceeded by replacement arguents. eg: /usr/bin/script.sh %MN% will excecute as /usr/bin/script.sh MonitorName /path/to/event. + * Message details of all matches: Uses an email to SMS gateway to send an SMS message for each match. + * Execute command on all matches: Allows you to execute any arbitrary command on the matched events. You can use replacement tokens as subsequent arguents to the command, the last argument will be the absolute path to the event, preceeded by replacement arguents. eg: /usr/bin/script.sh %MN% will excecute as /usr/bin/script.sh MonitorName /path/to/event. Please note that urls may contain characters like & that need quoting. So you may need to put quotes around them like /usr/bin/scrupt.sh "%MN%". * Delete all matches: Deletes all the matched events * *E*: Use 'Submit' to 'test' your matching conditions. This will just match and show you what filters match. Use 'Execute' to actually execute the action after matching your conditions. Use 'Save' to save the filter for future use and 'Reset' to clear your settings + * Copy all matches: copies the event files to another location, specified in the Copy To dropdown. The other location must be setup in the Storage Tab under options. + * Move all matches: copies the event files to another location, specified in the Move To dropdown. The other location must be setup in the Storage Tab under options. The files will be delete from the original location. .. NOTE:: More details on filter conditions: @@ -63,8 +68,12 @@ Here is what the filter window looks like * %EPI1% Path to the first alarmed event image * %EPIM% Path to the (first) event image with the highest score * %EI1% Attach first alarmed event image + * %EI1A% Attach first alarmed event analysis image * %EIM% Attach (first) event image with the highest score + * %EIMA% Attach (first) event analysis image with the highest score + * %EIMOD% Attach event image with object detection objects * %EV% Attach event mpeg video + * %EVM% Attach event mpeg video in phone format * %MN% Name of the monitor * %MET% Total number of events for the monitor * %MEH% Number of events for the monitor in the last hour @@ -83,14 +92,7 @@ Here is what the filter window looks like Filtering is a powerful mechanism you can use to eliminate events that fit a certain pattern however in many cases modifying the zone settings will better address this. Where it really comes into its own is generally in applying time filters, so for instance events that happen during weekdays or at certain times of the day are highlighted, uploaded or deleted. Additionally using disk related terms in your filters means you can automatically create filters that delete the oldest events when your disk gets full. Be warned however that if you use this strategy then you should limit the returned results to the amount of events you want deleted in each pass until the disk usage is at an acceptable level. If you do not do this then the first pass when the disk usage is high will match, and then delete, all events unless you have used other criteria inside of limits. ZoneMinder ships with a sample filter already installed, though disabled. The PurgeWhenFull filter can be used to delete the oldest events when your disk starts filling up. To use it you should select and load it in the filter interface, modify it to your requirements, and then save it making you sure you check the ‘Delete all matches’ option. This will then run in the background and ensure that your disk does not fill up with events. - -Saving filters ------------------ - -.. image:: images/filter-save.png - :width: 400px - -When saving filters, if you want the filter to run in the background make sure you select the "Run filter in background" option. When checked, ZoneMinder will make sure the filter is checked regularly. For example, if you want to be notified of new events by email, you should make sure this is checked. Filters that are configured to run in the background have a "*" next to it. + When saving filters, if you want the filter to run in the background make sure you select the "Run filter in background" option. When checked, ZoneMinder will make sure the filter is checked regularly. For example, if you want to be notified of new events by email, you should make sure this is checked. Filters that are configured to run in the background have a "*" next to it in the dropdown. For example: @@ -101,12 +103,11 @@ How filters actually work -------------------------- It is useful to know how filters actually work behind the scenes in ZoneMinder, in the event you find your filter not functioning as intended: -* the primary filter processing process in ZoneMinder is a perl file called ``zmfilter.pl`` which retrieves filters from the Filters database table +* Each filter set to run in the background will be run in it's own process called ``zmfilter.pl`` which retrieves filters from the Filters database table * zmfilter.pl runs every FILTER_EXECUTE_INTERVAL seconds (default is 20s, can be changed in Options->System) -* in each run, it goes through all the filters which are marked as "Run in Background" and if the conditions match performs the specified action -* zmfilter.pl also reloads all the filters every FILTER_RELOAD_DELAY seconds (default is 300s/5mins, can be changed in Options->System) - * So if you have just created a new filter, zmfilter will not see it till the next FILTER_RELOAD_DELAY cycle - * This is also important if you are using "relative times" like 'now' - see :ref:`relative_caveat` +* after each interval the filter will query the database and apply the action to each matching event. +* zmfilter.pl also reloads the filter every FILTER_RELOAD_DELAY seconds (default is 300s/5mins, can be changed in Options->System) +* In previous versions of ZoneMinder filter changes would not take immediate effect, but now the web ui will start/stop/restart filters as appropriate upon editing a filter. Relative items in date strings diff --git a/docs/userguide/options.rst b/docs/userguide/options.rst index 873002a0d..a2ca67463 100644 --- a/docs/userguide/options.rst +++ b/docs/userguide/options.rst @@ -10,7 +10,9 @@ If you have changed the value of an option you should then ‘save’ it. A numb options/options_display options/options_system options/options_config + options/options_api options/options_servers + options/options_storage options/options_paths options/options_web options/options_images From 14381cc4daf891125e6472dc8cd915a18561ad89 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 24 Sep 2019 10:12:28 -0400 Subject: [PATCH 003/394] rough in storage documentation --- docs/userguide/options/options_storage.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 docs/userguide/options/options_storage.rst diff --git a/docs/userguide/options/options_storage.rst b/docs/userguide/options/options_storage.rst new file mode 100644 index 000000000..b84f2e6f6 --- /dev/null +++ b/docs/userguide/options/options_storage.rst @@ -0,0 +1,10 @@ +Options - Storage +----------------- + +.. image:: images/Options_Storage.png + +Storage tab is used for setting up multiple storage areas for storing events. +To add a new server use the Add New Storage button. The storage area will require a name and a path. The path should be absolute and if multiple servers are in use, each should have access to it using the same path. The Url field is used for S3 type storage. S3 storage should be mounted in the filesystem using s3fs and can also be specified in the Url for more efficient access. +The Do Deletes option tell ZoneMinder whether to actually perform delete operations when deleting events. S3fs systems often do deletes in a cron job or other background task and doing the deletes can overload an S3 system. + +To delete a storage area mark that area and click the Delete button. From b7857d7a88287fb1607c46b254e7beaf00710864 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 11 Dec 2020 11:43:17 -0500 Subject: [PATCH 004/394] Add D1-PAL resolution --- web/skins/classic/views/monitor.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index e3c0e4a56..090ef2ad1 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -910,11 +910,13 @@ include('_monitor_source_nvsocket.php'); '320x240'=>'320x240', '320x200'=>'320x200', '352x240'=>'352x240 CIF', + '352x480'=>'352x480', '640x480'=>'640x480', '640x400'=>'640x400', '704x240'=>'704x240 2CIF', '704x480'=>'704x480 4CIF', '720x480'=>'720x480 D1', + '720x576'=>'720x576 D1 PAL', '1280x720'=>'1280x720 720p', '1280x800'=>'1280x800', '1280x960'=>'1280x960 960p', From bd6566a819bfbbed0a255bf48f7de4ab50970fed Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 8 May 2021 12:26:13 -0400 Subject: [PATCH 005/394] Have to clearpacket while holding the lock on snap. Otherwise it can get deleted the moment we unlock it. --- src/zm_monitor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 9c58a8ffb..8b977c8b6 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -2304,6 +2304,7 @@ bool Monitor::Analyse() { snap->image = nullptr; } + packetqueue.clearPackets(snap); packetqueue.unlock(packet_lock); if ( snap->image_index > 0 ) { @@ -2314,7 +2315,6 @@ bool Monitor::Analyse() { UpdateAnalysisFPS(); } shared_data->last_read_time = time(nullptr); - packetqueue.clearPackets(snap); return true; } // end Monitor::Analyse From d3e91da514c61cf6aaf5297cd808266f060fec30 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 8 May 2021 12:35:28 -0400 Subject: [PATCH 006/394] sigh. Move the snap unlock further down protecting another use of snap. Use a video test instead of image count to determine videoness of the snap --- src/zm_monitor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 8b977c8b6..ecacf2987 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -2305,15 +2305,15 @@ bool Monitor::Analyse() { } packetqueue.clearPackets(snap); - packetqueue.unlock(packet_lock); - if ( snap->image_index > 0 ) { + if (snap->codec_type == AVMEDIA_TYPE_VIDEO) { // Only do these if it's a video packet. shared_data->last_read_index = snap->image_index; analysis_image_count++; if ( function == MODECT or function == MOCORD ) UpdateAnalysisFPS(); } + packetqueue.unlock(packet_lock); shared_data->last_read_time = time(nullptr); return true; From bc11e6a95bd124450711688aa2777f9d44b0af79 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 8 May 2021 15:47:29 -0400 Subject: [PATCH 007/394] Include pre event count in min estimated ram use --- web/skins/classic/views/js/monitor.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/skins/classic/views/js/monitor.js b/web/skins/classic/views/js/monitor.js index 4b41d8dd2..341f3f1aa 100644 --- a/web/skins/classic/views/js/monitor.js +++ b/web/skins/classic/views/js/monitor.js @@ -140,7 +140,7 @@ function initPage() { form.submit(); }; }); - document.querySelectorAll('input[name="newMonitor[ImageBufferCount]"],input[name="newMonitor[MaxImageBufferCount]"],input[name="newMonitor[Width]"],input[name="newMonitor[Height]"]').forEach(function(el) { + document.querySelectorAll('input[name="newMonitor[ImageBufferCount]"],input[name="newMonitor[MaxImageBufferCount]"],input[name="newMonitor[Width]"],input[name="newMonitor[Height]"],input[name="newMonitor[PreEventCount]"]').forEach(function(el) { el.oninput = window['update_estimated_ram_use'].bind(el); }); update_estimated_ram_use(); @@ -317,6 +317,7 @@ function update_estimated_ram_use() { var colours = document.querySelectorAll('select[name="newMonitor[Colours]"]')[0].value; var min_buffer_count = parseInt(document.querySelectorAll('input[name="newMonitor[ImageBufferCount]"]')[0].value); + min_buffer_count += parseInt(document.querySelectorAll('input[name="newMonitor[PreEventCount]"]')[0].value); var min_buffer_size = min_buffer_count * width * height * colours; document.getElementById('estimated_ram_use').innerHTML = 'Min: ' + human_filesize(min_buffer_size); From 9ee24170a9174db46d08f697ad603c9fb154a8cd Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 8 May 2021 15:57:00 -0400 Subject: [PATCH 008/394] use packetqueue.unlock instead of deleting the lock so that we wake up people waiting --- src/zm_monitor.cpp | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index ecacf2987..97f20c5df 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1776,12 +1776,13 @@ bool Monitor::Analyse() { // Is it possible for snap->score to be ! -1 ? Not if everything is working correctly if (snap->score != -1) { - delete packet_lock; - packetqueue.increment_it(analysis_it); Error("skipping because score was %d", snap->score); + packetqueue.unlock(packet_lock); + packetqueue.increment_it(analysis_it); return false; } + // Store the it that points to our snap we will need it later packetqueue_iterator snap_it = *analysis_it; packetqueue.increment_it(analysis_it); @@ -1820,7 +1821,7 @@ bool Monitor::Analyse() { Event::StringSet noteSet; noteSet.insert(trigger_data->trigger_text); noteSetMap[trigger_data->trigger_cause] = noteSet; - } // end if trigger_on + } // end if trigger_on // FIXME this snap might not be the one that caused the signal change. Need to store that in the packet. if (signal_change) { @@ -1848,7 +1849,7 @@ bool Monitor::Analyse() { shared_data->active = signal; if ((function == MODECT or function == MOCORD) and snap->image) ref_image.Assign(*(snap->image)); - }// else + } // end if signal change if (signal) { if (snap->codec_type == AVMEDIA_TYPE_VIDEO) { @@ -1858,17 +1859,17 @@ bool Monitor::Analyse() { // FIXME improve logic here bool first_link = true; Event::StringSet noteSet; - for ( int i = 0; i < n_linked_monitors; i++ ) { + 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]->isConnected()) { Debug(1, "Linked monitor %d %s is connected", linked_monitors[i]->Id(), linked_monitors[i]->Name()); - if ( linked_monitors[i]->hasAlarmed() ) { + if (linked_monitors[i]->hasAlarmed()) { Debug(1, "Linked monitor %d %s is alarmed", linked_monitors[i]->Id(), linked_monitors[i]->Name()); - if ( !event ) { - if ( first_link ) { - if ( cause.length() ) + if (!event) { + if (first_link) { + if (cause.length()) cause += ", "; cause += LINKED_CAUSE; first_link = false; @@ -1885,12 +1886,10 @@ bool Monitor::Analyse() { linked_monitors[i]->connect(); } } // end foreach linked_monitor - if ( noteSet.size() > 0 ) + if (noteSet.size() > 0) noteSetMap[LINKED_CAUSE] = noteSet; } // end if linked_monitors - struct timeval *timestamp = snap->timestamp; - /* try to stay behind the decoder. */ if (decoding_enabled) { while (!snap->image and !snap->decoded and !zm_terminate and !analysis_thread->Stopped()) { @@ -1909,6 +1908,8 @@ bool Monitor::Analyse() { } } // end if decoding enabled + struct timeval *timestamp = snap->timestamp; + if (Active() and (function == MODECT or function == MOCORD)) { Debug(3, "signal and active and modect"); Event::StringSet zoneSet; @@ -1923,7 +1924,6 @@ bool Monitor::Analyse() { } if (!(analysis_image_count % (motion_frame_skip+1))) { - if (snap->image) { // decoder may not have been able to provide an image if (!ref_image.Buffer()) { @@ -1969,12 +1969,12 @@ bool Monitor::Analyse() { if (function == RECORD or function == MOCORD) { // If doing record, check to see if we need to close the event or not. if (event) { - Debug(2, "Have event %" PRIu64 " in mocord", event->Id()); + Debug(2, "Have event %" PRIu64 " in record", event->Id()); if (section_length && - ( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= section_length ) + (( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= section_length) && ( - ( (function == MOCORD) && (event_close_mode != CLOSE_TIME) ) + ((function == MOCORD) && (event_close_mode != CLOSE_TIME)) || ( (function == RECORD) && (event_close_mode == CLOSE_TIME) ) || ! ( timestamp->tv_sec % section_length ) @@ -2005,9 +2005,13 @@ bool Monitor::Analyse() { ZMLockedPacket *starting_packet_lock = nullptr; ZMPacket *starting_packet = nullptr; - if ( *start_it != snap_it ) { + if (*start_it != snap_it) { starting_packet_lock = packetqueue.get_packet(start_it); - if (!starting_packet_lock) return false; + if (!starting_packet_lock) { + Warning("Unable to get starting packet lock"); + delete packet_lock; + return false; + } starting_packet = starting_packet_lock->packet_; } else { starting_packet = snap; @@ -2015,12 +2019,12 @@ bool Monitor::Analyse() { event = new Event(this, *(starting_packet->timestamp), "Continuous", noteSetMap); // Write out starting packets, do not modify packetqueue it will garbage collect itself - while ( starting_packet and ((*start_it) != snap_it) ) { + while (starting_packet and ((*start_it) != snap_it)) { event->AddPacket(starting_packet); // Have added the packet, don't want to unlock it until we have locked the next packetqueue.increment_it(start_it); - if ( (*start_it) == snap_it ) { + if ((*start_it) == snap_it) { if (starting_packet_lock) delete starting_packet_lock; break; } From 2cf6ad80895a11148db8c200a5f997426f000e23 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 8 May 2021 21:14:20 -0400 Subject: [PATCH 009/394] Switch ZMPacket * to a shared_ptr. This is so that in LockedPacket we can unlock and then notify and be confident that packet_ won't have been deleted. Change ZMPacket->timestamp to be a timeval instead of timeval *. This might not have been necessary but I like it. No longer cuse the ZMPacket object to wrap the shared image buffers and timestamps. Use a vector for image_buffers. --- dep/RtspServer | 2 +- src/zm_camera.h | 4 +- src/zm_event.cpp | 15 +- src/zm_event.h | 4 +- src/zm_ffmpeg_camera.cpp | 12 +- src/zm_ffmpeg_camera.h | 4 +- src/zm_file_camera.cpp | 4 +- src/zm_file_camera.h | 2 +- src/zm_local_camera.cpp | 30 ++-- src/zm_local_camera.h | 2 +- src/zm_monitor.cpp | 163 ++++++------------ src/zm_monitor.h | 5 +- src/zm_monitorstream.cpp | 51 +++--- src/zm_monitorstream.h | 4 +- src/zm_packet.cpp | 41 +++-- src/zm_packet.h | 9 +- src/zm_packetqueue.cpp | 263 ++++-------------------------- src/zm_packetqueue.h | 22 +-- src/zm_remote_camera.h | 2 +- src/zm_remote_camera_http.cpp | 16 +- src/zm_remote_camera_http.h | 2 +- src/zm_remote_camera_nvsocket.cpp | 14 +- src/zm_remote_camera_nvsocket.h | 2 +- src/zm_remote_camera_rtsp.cpp | 28 ++-- src/zm_remote_camera_rtsp.h | 2 +- src/zm_videostore.cpp | 24 +-- src/zm_videostore.h | 8 +- src/zm_zone.cpp | 2 +- src/zmc.cpp | 1 + web/api/app/Plugin/Crud | 2 +- 30 files changed, 253 insertions(+), 487 deletions(-) diff --git a/dep/RtspServer b/dep/RtspServer index cd7fd49be..d714323e6 160000 --- a/dep/RtspServer +++ b/dep/RtspServer @@ -1 +1 @@ -Subproject commit cd7fd49becad6010a1b8466bfebbd93999a39878 +Subproject commit d714323e693ba106be6af363295d950f50ca15e0 diff --git a/src/zm_camera.h b/src/zm_camera.h index fb117dcfb..f339415e1 100644 --- a/src/zm_camera.h +++ b/src/zm_camera.h @@ -24,6 +24,8 @@ #include #include +#include + class Monitor; class ZMPacket; @@ -133,7 +135,7 @@ public: virtual int PrimeCapture() { return 0; } virtual int PreCapture() = 0; - virtual int Capture(ZMPacket &p) = 0; + virtual int Capture(std::shared_ptr &p) = 0; virtual int PostCapture() = 0; virtual int Close() = 0; }; diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 103320536..c6c16ebd5 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -285,7 +285,7 @@ void Event::createNotes(std::string ¬es) { bool Event::WriteFrameImage( Image *image, - struct timeval timestamp, + timeval timestamp, const char *event_file, bool alarm_frame) const { @@ -299,7 +299,7 @@ bool Event::WriteFrameImage( // stash the image we plan to use in another pointer regardless if timestamped. // exif is only timestamp at present this switches on or off for write Image *ts_image = new Image(*image); - monitor->TimestampImage(ts_image, ×tamp); + monitor->TimestampImage(ts_image, timestamp); rc = ts_image->WriteJpeg(event_file, thisquality, (monitor->Exif() ? timestamp : (timeval){0,0})); delete(ts_image); @@ -311,9 +311,8 @@ bool Event::WriteFrameImage( return rc; } // end Event::WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame ) -bool Event::WritePacket(ZMPacket &packet) { - - if ( videoStore->writePacket(&packet) < 0 ) +bool Event::WritePacket(const std::shared_ptr&packet) { + if (videoStore->writePacket(packet) < 0) return false; return true; } // bool Event::WriteFrameVideo @@ -420,7 +419,7 @@ void Event::updateNotes(const StringSetMap &newNoteSetMap) { } // end if update } // void Event::updateNotes(const StringSetMap &newNoteSetMap) -void Event::AddPacket(ZMPacket *packet) { +void Event::AddPacket(const std::shared_ptr&packet) { have_video_keyframe = have_video_keyframe || ( ( packet->codec_type == AVMEDIA_TYPE_VIDEO ) && @@ -437,8 +436,8 @@ void Event::AddPacket(ZMPacket *packet) { //FIXME if it fails, we should write a jpeg } if ((packet->codec_type == AVMEDIA_TYPE_VIDEO) or packet->image) - AddFrame(packet->image, *(packet->timestamp), packet->zone_stats, packet->score, packet->analysis_image); - end_time = *packet->timestamp; + AddFrame(packet->image, packet->timestamp, packet->zone_stats, packet->score, packet->analysis_image); + end_time = packet->timestamp; return; } diff --git a/src/zm_event.h b/src/zm_event.h index 7d0a334c5..4b058fc8e 100644 --- a/src/zm_event.h +++ b/src/zm_event.h @@ -111,8 +111,8 @@ class Event { const struct timeval &StartTime() const { return start_time; } const struct timeval &EndTime() const { return end_time; } - void AddPacket(ZMPacket *p); - bool WritePacket(ZMPacket &p); + void AddPacket(const std::shared_ptr &p); + bool WritePacket(const std::shared_ptr &p); bool SendFrameImage(const Image *image, bool alarm_frame=false); bool WriteFrameImage( Image *image, diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index b0c1401e7..f6e366911 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -189,7 +189,7 @@ int FfmpegCamera::PreCapture() { return 0; } -int FfmpegCamera::Capture(ZMPacket &zm_packet) { +int FfmpegCamera::Capture(std::shared_ptr &zm_packet) { if (!mCanCapture) return -1; start_read_time = time(nullptr); @@ -236,14 +236,14 @@ int FfmpegCamera::Capture(ZMPacket &zm_packet) { ZM_DUMP_STREAM_PACKET(stream, packet, "ffmpeg_camera in"); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - zm_packet.codec_type = stream->codecpar->codec_type; + zm_packet->codec_type = stream->codecpar->codec_type; #else - zm_packet.codec_type = stream->codec->codec_type; + zm_packet->codec_type = stream->codec->codec_type; #endif bytes += packet.size; - zm_packet.set_packet(&packet); - zm_packet.stream = stream; - zm_packet.pts = av_rescale_q(packet.pts, stream->time_base, AV_TIME_BASE_Q); + zm_packet->set_packet(&packet); + zm_packet->stream = stream; + zm_packet->pts = av_rescale_q(packet.pts, stream->time_base, AV_TIME_BASE_Q); if ( packet.pts != AV_NOPTS_VALUE ) { if ( stream == mVideoStream ) { if (mFirstVideoPTS == AV_NOPTS_VALUE) diff --git a/src/zm_ffmpeg_camera.h b/src/zm_ffmpeg_camera.h index e6f55b634..df1cb3936 100644 --- a/src/zm_ffmpeg_camera.h +++ b/src/zm_ffmpeg_camera.h @@ -22,6 +22,8 @@ #include "zm_camera.h" +#include + #if HAVE_LIBAVUTIL_HWCONTEXT_H typedef struct DecodeContext { AVBufferRef *hw_device_ref; @@ -93,7 +95,7 @@ class FfmpegCamera : public Camera { int PrimeCapture() override; int PreCapture() override; - int Capture(ZMPacket &p) override; + int Capture(std::shared_ptr &p) override; int PostCapture() override; private: static int FfmpegInterruptCallback(void*ctx); diff --git a/src/zm_file_camera.cpp b/src/zm_file_camera.cpp index af494ce38..2563482f1 100644 --- a/src/zm_file_camera.cpp +++ b/src/zm_file_camera.cpp @@ -87,8 +87,8 @@ int FileCamera::PreCapture() { return 0; } -int FileCamera::Capture( ZMPacket &zm_packet ) { - return zm_packet.image->ReadJpeg(path, colours, subpixelorder) ? 1 : -1; +int FileCamera::Capture(std::shared_ptr &zm_packet) { + return zm_packet->image->ReadJpeg(path, colours, subpixelorder) ? 1 : -1; } int FileCamera::PostCapture() { diff --git a/src/zm_file_camera.h b/src/zm_file_camera.h index 11a5cfe3f..fa923ac67 100644 --- a/src/zm_file_camera.h +++ b/src/zm_file_camera.h @@ -51,7 +51,7 @@ public: void Initialise(); void Terminate(); int PreCapture() override; - int Capture(ZMPacket &p) override; + int Capture(std::shared_ptr &p) override; int PostCapture() override; int Close() override { return 0; }; }; diff --git a/src/zm_local_camera.cpp b/src/zm_local_camera.cpp index 7e97ecd08..b72c38015 100644 --- a/src/zm_local_camera.cpp +++ b/src/zm_local_camera.cpp @@ -2036,16 +2036,16 @@ int LocalCamera::PreCapture() { return 1; } -int LocalCamera::Capture(ZMPacket &zm_packet) { +int LocalCamera::Capture(std::shared_ptr &zm_packet) { // We assume that the avpacket is allocated, and just needs to be filled static uint8_t* buffer = nullptr; int buffer_bytesused = 0; int capture_frame = -1; int captures_per_frame = 1; - if ( channel_count > 1 ) + if (channel_count > 1) captures_per_frame = v4l_captures_per_frame; - if ( captures_per_frame <= 0 ) { + if (captures_per_frame <= 0) { captures_per_frame = 1; Warning("Invalid Captures Per Frame setting: %d", captures_per_frame); } @@ -2191,22 +2191,22 @@ int LocalCamera::Capture(ZMPacket &zm_packet) { } /* prime capture */ - if (!zm_packet.image) { + if (!zm_packet->image) { Debug(4, "Allocating image"); - zm_packet.image = new Image(width, height, colours, subpixelorder); + zm_packet->image = new Image(width, height, colours, subpixelorder); } - if ( conversion_type != 0 ) { + if (conversion_type != 0) { Debug(3, "Performing format conversion %d", conversion_type); /* Request a writeable buffer of the target image */ - uint8_t *directbuffer = zm_packet.image->WriteBuffer(width, height, colours, subpixelorder); - if ( directbuffer == nullptr ) { + uint8_t *directbuffer = zm_packet->image->WriteBuffer(width, height, colours, subpixelorder); + if (directbuffer == nullptr) { Error("Failed requesting writeable buffer for the captured image."); return -1; } #if HAVE_LIBSWSCALE - if ( conversion_type == 1 ) { + if (conversion_type == 1) { Debug(9, "Calling sws_scale to perform the conversion"); /* Use swscale to convert the image directly into the shared memory */ #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) @@ -2236,20 +2236,20 @@ int LocalCamera::Capture(ZMPacket &zm_packet) { // Need to store the jpeg data too Debug(9, "Decoding the JPEG image"); /* JPEG decoding */ - zm_packet.image->DecodeJpeg(buffer, buffer_bytesused, colours, subpixelorder); + zm_packet->image->DecodeJpeg(buffer, buffer_bytesused, colours, subpixelorder); } } else { Debug(3, "No format conversion performed. Assigning the image"); /* No conversion was performed, the image is in the V4L buffers and needs to be copied into the shared memory */ - zm_packet.image->Assign(width, height, colours, subpixelorder, buffer, imagesize); + zm_packet->image->Assign(width, height, colours, subpixelorder, buffer, imagesize); } // end if doing conversion or not - zm_packet.packet.stream_index = mVideoStreamId; - zm_packet.stream = mVideoStream; - zm_packet.codec_type = AVMEDIA_TYPE_VIDEO; - zm_packet.keyframe = 1; + zm_packet->packet.stream_index = mVideoStreamId; + zm_packet->stream = mVideoStream; + zm_packet->codec_type = AVMEDIA_TYPE_VIDEO; + zm_packet->keyframe = 1; return 1; } // end int LocalCamera::Capture() diff --git a/src/zm_local_camera.h b/src/zm_local_camera.h index 6d3ff2672..6c64e39d6 100644 --- a/src/zm_local_camera.h +++ b/src/zm_local_camera.h @@ -151,7 +151,7 @@ public: int PrimeCapture() override; int PreCapture() override; - int Capture(ZMPacket &p) override; + int Capture(std::shared_ptr &p) override; int PostCapture() override; int Close() override { return 0; }; diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 97f20c5df..63ce15d60 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -996,12 +996,10 @@ bool Monitor::connect() { if (!camera) LoadCamera(); Debug(3, "Allocating %d image buffers", image_buffer_count); - image_buffer = new ZMPacket[image_buffer_count]; + image_buffer.reserve(image_buffer_count); for (int32_t i = 0; i < image_buffer_count; i++) { - image_buffer[i].image_index = i; - image_buffer[i].timestamp = &(shared_timestamps[i]); - image_buffer[i].image = new Image(width, height, camera->Colours(), camera->SubpixelOrder(), &(shared_images[i*camera->ImageSize()])); - image_buffer[i].image->HoldBuffer(true); /* Don't release the internal buffer or replace it with another */ + image_buffer[i] = new Image(width, height, camera->Colours(), camera->SubpixelOrder(), &(shared_images[i*camera->ImageSize()])); + image_buffer[i]->HoldBuffer(true); /* Don't release the internal buffer or replace it with another */ } if (purpose == CAPTURE) { @@ -1096,16 +1094,10 @@ bool Monitor::disconnect() { } #endif // ZM_MEM_MAPPED - if (image_buffer) { - for ( int32_t i = 0; i < image_buffer_count; i++ ) { - // We delete the image because it is an object pointing to space that won't be free'd. - delete image_buffer[i].image; - image_buffer[i].image = nullptr; - // We don't delete the timestamp because it is just a pointer to shared mem. - image_buffer[i].timestamp = nullptr; - } - delete[] image_buffer; - image_buffer = nullptr; + for ( int32_t i = 0; i < image_buffer_count; i++ ) { + // We delete the image because it is an object pointing to space that won't be free'd. + delete image_buffer[i]; + image_buffer[i] = nullptr; } return true; @@ -1177,19 +1169,18 @@ int Monitor::GetImage(int32_t index, int scale) { Image *image; // If we are going to be modifying the snapshot before writing, then we need to copy it if ( ( scale != ZM_SCALE_BASE ) || ( !config.timestamp_on_capture ) ) { - ZMPacket *snap = &image_buffer[index]; - alarm_image.Assign(*snap->image); + alarm_image.Assign(*image_buffer[index]); - if ( scale != ZM_SCALE_BASE ) { + if (scale != ZM_SCALE_BASE) { alarm_image.Scale(scale); } if ( !config.timestamp_on_capture ) { - TimestampImage(&alarm_image, snap->timestamp); + TimestampImage(&alarm_image, shared_timestamps[index]); } image = &alarm_image; } else { - image = image_buffer[index].image; + image = image_buffer[index]; } static char filename[PATH_MAX]; @@ -1206,15 +1197,15 @@ ZMPacket *Monitor::getSnapshot(int index) const { if ( (index < 0) || (index > image_buffer_count) ) { index = shared_data->last_write_index; } - return &image_buffer[index]; + return new ZMPacket(image_buffer[index], shared_timestamps[index]); return nullptr; } struct timeval Monitor::GetTimestamp(int index) const { ZMPacket *packet = getSnapshot(index); - if ( packet ) - return *packet->timestamp; + if (packet) + return packet->timestamp; static struct timeval null_tv = { 0, 0 }; return null_tv; @@ -1235,58 +1226,6 @@ uint64_t Monitor::GetLastEventId() const { // This function is crap. double Monitor::GetFPS() const { return get_capture_fps(); - // last_write_index is the last capture index. It starts as == image_buffer_count so that the first asignment % image_buffer_count = 0; - int32_t index1 = shared_data->last_write_index; - if ( index1 >= image_buffer_count ) { - // last_write_index only has this value on startup before capturing anything. - return 0.0; - } - Debug(2, "index1(%d)", index1); - ZMPacket *snap1 = &image_buffer[index1]; - if ( !snap1->timestamp->tv_sec ) { - // This should be impossible - Warning("Impossible situation. No timestamp on captured image index was %d, image-buffer_count was (%d)", index1, image_buffer_count); - return 0.0; - } - struct timeval time1 = *snap1->timestamp; - - int32_t fps_image_count = image_buffer_count; - - int32_t index2 = (index1+1)%image_buffer_count; - Debug(2, "index2(%d)", index2); - ZMPacket *snap2 = &image_buffer[index2]; - // the timestamp pointers are initialized on connection, so that's redundant - // tv_sec is probably only zero during the first loop of capturing, so this basically just counts the unused images. - // The problem is that there is no locking, and we set the timestamp before we set last_write_index, - // so there is a small window where the next image can have a timestamp in the future - while ( !snap2->timestamp->tv_sec || tvDiffSec(*snap2->timestamp, *snap1->timestamp) < 0 ) { - if ( index1 == index2 ) { - // All images are uncaptured - return 0.0; - } - index2 = (index2+1)%image_buffer_count; - snap2 = &image_buffer[ index2 ]; - fps_image_count--; - } - struct timeval time2 = *snap2->timestamp; - - double time_diff = tvDiffSec( time2, time1 ); - if ( ! time_diff ) { - Error("No diff between time_diff = %lf (%d:%ld.%ld - %d:%ld.%ld), ibc: %d", - time_diff, index2, time2.tv_sec, time2.tv_usec, index1, time1.tv_sec, time1.tv_usec, image_buffer_count); - return 0.0; - } - double curr_fps = fps_image_count/time_diff; - - if ( curr_fps < 0.0 ) { - Error("Negative FPS %f, time_diff = %lf (%d:%ld.%ld - %d:%ld.%ld), ibc: %d", - curr_fps, time_diff, index2, time2.tv_sec, time2.tv_usec, index1, time1.tv_sec, time1.tv_usec, image_buffer_count); - return 0.0; - } else { - Debug(2, "GetFPS %f, time_diff = %lf (%d:%ld.%ld - %d:%ld.%ld), ibc: %d", - curr_fps, time_diff, index2, time2.tv_sec, time2.tv_usec, index1, time1.tv_sec, time1.tv_usec, image_buffer_count); - } - return curr_fps; } /* I think this returns the # of micro seconds that we should sleep in order to maintain the desired analysis rate */ @@ -1772,7 +1711,7 @@ bool Monitor::Analyse() { // get_analysis_packet will lock the packet and may wait if analysis_it is at the end ZMLockedPacket *packet_lock = packetqueue.get_packet(analysis_it); if (!packet_lock) return false; - ZMPacket *snap = packet_lock->packet_; + std::shared_ptr snap = packet_lock->packet_; // Is it possible for snap->score to be ! -1 ? Not if everything is working correctly if (snap->score != -1) { @@ -1908,7 +1847,7 @@ bool Monitor::Analyse() { } } // end if decoding enabled - struct timeval *timestamp = snap->timestamp; + struct timeval *timestamp = &snap->timestamp; if (Active() and (function == MODECT or function == MOCORD)) { Debug(3, "signal and active and modect"); @@ -2004,7 +1943,7 @@ bool Monitor::Analyse() { // This gets a lock on the starting packet ZMLockedPacket *starting_packet_lock = nullptr; - ZMPacket *starting_packet = nullptr; + std::shared_ptr starting_packet = nullptr; if (*start_it != snap_it) { starting_packet_lock = packetqueue.get_packet(start_it); if (!starting_packet_lock) { @@ -2017,7 +1956,7 @@ bool Monitor::Analyse() { starting_packet = snap; } - event = new Event(this, *(starting_packet->timestamp), "Continuous", noteSetMap); + event = new Event(this, starting_packet->timestamp, "Continuous", noteSetMap); // Write out starting packets, do not modify packetqueue it will garbage collect itself while (starting_packet and ((*start_it) != snap_it)) { event->AddPacket(starting_packet); @@ -2106,7 +2045,7 @@ bool Monitor::Analyse() { (pre_event_count > alarm_frame_count ? pre_event_count : alarm_frame_count) ); ZMLockedPacket *starting_packet_lock = nullptr; - ZMPacket *starting_packet = nullptr; + std::shared_ptr starting_packet = nullptr; if (*start_it != snap_it) { starting_packet_lock = packetqueue.get_packet(start_it); if (!starting_packet_lock) return false; @@ -2115,7 +2054,7 @@ bool Monitor::Analyse() { starting_packet = snap; } - event = new Event(this, *(starting_packet->timestamp), cause, noteSetMap); + event = new Event(this, starting_packet->timestamp, cause, noteSetMap); shared_data->last_event_id = event->Id(); snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile()); video_store_data->recording = event->StartTime(); @@ -2522,13 +2461,17 @@ std::vector> Monitor::LoadFfmpegMonitors(const char *fi int Monitor::Capture() { unsigned int index = image_count % image_buffer_count; - ZMPacket *packet = new ZMPacket(); - packet->timestamp = new struct timeval; + Debug(1, "Packeet"); + std::shared_ptr packet = std::make_shared(); + //= new ZMPacket(); + //packet->timestamp = new struct timeval; packet->image_index = image_count; - gettimeofday(packet->timestamp, nullptr); - shared_data->zmc_heartbeat_time = packet->timestamp->tv_sec; - - int captureResult = camera->Capture(*packet); + Debug(1, "Packeet"); + gettimeofday(&(packet->timestamp), nullptr); + Debug(1, "Packeet"); + shared_data->zmc_heartbeat_time = packet->timestamp.tv_sec; + Debug(1, "Capturing"); + int captureResult = camera->Capture(packet); Debug(4, "Back from capture result=%d image count %d", captureResult, image_count); if (captureResult < 0) { @@ -2543,24 +2486,24 @@ int Monitor::Capture() { capture_image->Fill(signalcolor); shared_data->signal = false; shared_data->last_write_index = index; - shared_data->last_write_time = image_buffer[index].timestamp->tv_sec; - image_buffer[index].image->Assign(*capture_image); - *(image_buffer[index].timestamp) = *(packet->timestamp); + shared_data->last_write_time = shared_timestamps[index].tv_sec; + image_buffer[index]->Assign(*capture_image); + shared_timestamps[index] = packet->timestamp; delete capture_image; image_count++; - delete packet; + //delete packet; // What about timestamping it? // Don't want to do analysis on it, but we won't due to signal return -1; - } else if ( captureResult > 0 ) { + } else if (captureResult > 0) { shared_data->signal = true; // Assume if getting packets that we are getting something useful. CheckSignalPoints can correct this later. // If we captured, let's assume signal, Decode will detect further if (!decoding_enabled) { shared_data->last_write_index = index; - shared_data->last_write_time = packet->timestamp->tv_sec; + shared_data->last_write_time = packet->timestamp.tv_sec; } Debug(2, "Have packet stream_index:%d ?= videostream_id: %d q.vpktcount %d event? %d image_count %d", - packet->packet.stream_index, video_stream_id, packetqueue.packet_count(video_stream_id), ( event ? 1 : 0 ), image_count ); + packet->packet.stream_index, video_stream_id, packetqueue.packet_count(video_stream_id), ( event ? 1 : 0 ), image_count); if (packet->codec_type == AVMEDIA_TYPE_VIDEO) { packet->packet.stream_index = video_stream_id; // Convert to packetQueue's index @@ -2590,14 +2533,14 @@ int Monitor::Capture() { packetqueue.queuePacket(packet); } else { Debug(4, "Not Queueing audio packet"); - delete packet; + //delete packet; } // Don't update last_write_index because that is used for live streaming //shared_data->last_write_time = image_buffer[index].timestamp->tv_sec; return 1; } else { Debug(1, "Unknown codec type %d", packet->codec_type); - delete packet; + //delete packet; return 1; } // end if audio @@ -2605,12 +2548,12 @@ int Monitor::Capture() { // Will only be queued if there are iterators allocated in the queue. if ( !packetqueue.queuePacket(packet) ) { - delete packet; + //delete packet; } UpdateCaptureFPS(); } else { // result == 0 // Question is, do we update last_write_index etc? - delete packet; + //delete packet; return 0; } // end if result @@ -2635,7 +2578,7 @@ int Monitor::Capture() { bool Monitor::Decode() { ZMLockedPacket *packet_lock = packetqueue.get_packet(decoder_it); if (!packet_lock) return false; - ZMPacket *packet = packet_lock->packet_; + std::shared_ptr packet = packet_lock->packet_; packetqueue.increment_it(decoder_it); if (packet->codec_type != AVMEDIA_TYPE_VIDEO) { Debug(4, "Not video"); @@ -2758,25 +2701,25 @@ bool Monitor::Decode() { TimestampImage(packet->image, packet->timestamp); } - image_buffer[index].image->Assign(*(packet->image)); - *(image_buffer[index].timestamp) = *(packet->timestamp); + image_buffer[index]->Assign(*(packet->image)); + shared_timestamps[index] = packet->timestamp; } // end if have image packet->decoded = true; shared_data->signal = ( capture_image and signal_check_points ) ? CheckSignal(capture_image) : true; shared_data->last_write_index = index; - shared_data->last_write_time = packet->timestamp->tv_sec; + shared_data->last_write_time = packet->timestamp.tv_sec; packetqueue.unlock(packet_lock); return true; } // end bool Monitor::Decode() -void Monitor::TimestampImage(Image *ts_image, const struct timeval *ts_time) const { +void Monitor::TimestampImage(Image *ts_image, const timeval &ts_time) const { if ( !label_format[0] ) return; // Expand the strftime macros first char label_time_text[256]; tm ts_tm = {}; - strftime(label_time_text, sizeof(label_time_text), label_format.c_str(), localtime_r(&ts_time->tv_sec, &ts_tm)); + strftime(label_time_text, sizeof(label_time_text), label_format.c_str(), localtime_r(&ts_time.tv_sec, &ts_tm)); char label_text[1024]; const char *s_ptr = label_time_text; char *d_ptr = label_text; @@ -2793,7 +2736,7 @@ void Monitor::TimestampImage(Image *ts_image, const struct timeval *ts_time) con found_macro = true; break; case 'f' : - d_ptr += snprintf(d_ptr, sizeof(label_text)-(d_ptr-label_text), "%02ld", ts_time->tv_usec/10000); + d_ptr += snprintf(d_ptr, sizeof(label_text)-(d_ptr-label_text), "%02ld", ts_time.tv_usec/10000); found_macro = true; break; } @@ -3175,21 +3118,21 @@ void Monitor::get_ref_image() { Debug(1, "Waiting for capture daemon lastwriteindex(%d) lastwritetime(%" PRIi64 ")", shared_data->last_write_index, static_cast(shared_data->last_write_time)); - if ( snap_lock and ! snap_lock->packet_->image ) { + if (snap_lock and ! snap_lock->packet_->image) { delete snap_lock; // can't analyse it anyways, incremement packetqueue.increment_it(analysis_it); } //usleep(10000); } - if ( zm_terminate ) + if (zm_terminate) return; - ZMPacket *snap = snap_lock->packet_; + std::shared_ptr snap = snap_lock->packet_; Debug(1, "get_ref_image: packet.stream %d ?= video_stream %d, packet image id %d packet image %p", snap->packet.stream_index, video_stream_id, snap->image_index, snap->image ); // Might not have been decoded yet FIXME - if ( snap->image ) { + if (snap->image) { ref_image.Assign(width, height, camera->Colours(), camera->SubpixelOrder(), snap->image->Buffer(), camera->ImageSize()); Debug(2, "Have ref image about to unlock"); @@ -3197,7 +3140,7 @@ void Monitor::get_ref_image() { Debug(2, "Have no ref image about to unlock"); } delete snap_lock; -} +} // get_ref_image std::vector Monitor::Groups() { // At the moment, only load groups once. diff --git a/src/zm_monitor.h b/src/zm_monitor.h index 0efc8a130..328c4e941 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -376,8 +376,7 @@ protected: struct timeval *shared_timestamps; unsigned char *shared_images; - ZMPacket *image_buffer; - ZMPacket next_buffer; /* Used by four field deinterlacing */ + std::vector image_buffer; int video_stream_id; // will be filled in PrimeCapture int audio_stream_id; // will be filled in PrimeCapture @@ -583,7 +582,7 @@ public: bool Analyse(); bool Decode(); void DumpImage( Image *dump_image ) const; - void TimestampImage( Image *ts_image, const struct timeval *ts_time ) const; + void TimestampImage(Image *ts_image, const timeval &ts_time) const; void closeEvent(); void Reload(); diff --git a/src/zm_monitorstream.cpp b/src/zm_monitorstream.cpp index 63f85d71b..0df93f963 100644 --- a/src/zm_monitorstream.cpp +++ b/src/zm_monitorstream.cpp @@ -320,13 +320,13 @@ void MonitorStream::processCommand(const CmdMsg *msg) { //updateFrameRate(monitor->GetFPS()); } // end void MonitorStream::processCommand(const CmdMsg *msg) -bool MonitorStream::sendFrame(const char *filepath, struct timeval *timestamp) { +bool MonitorStream::sendFrame(const char *filepath, const timeval ×tamp) { bool send_raw = ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE)); if ( ( type != STREAM_JPEG ) || - ( (!config.timestamp_on_capture) && timestamp ) + (!config.timestamp_on_capture) ) send_raw = false; @@ -352,7 +352,7 @@ bool MonitorStream::sendFrame(const char *filepath, struct timeval *timestamp) { if ( (0 > fprintf(stdout, "Content-Length: %d\r\nX-Timestamp: %d.%06d\r\n\r\n", - img_buffer_size, (int)timestamp->tv_sec, (int)timestamp->tv_usec)) + img_buffer_size, (int)timestamp.tv_sec, (int)timestamp.tv_usec)) || (fwrite(img_buffer, img_buffer_size, 1, stdout) != 1) ) { @@ -379,9 +379,9 @@ bool MonitorStream::sendFrame(const char *filepath, struct timeval *timestamp) { return false; } // end bool MonitorStream::sendFrame(const char *filepath, struct timeval *timestamp) -bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) { +bool MonitorStream::sendFrame(Image *image, const timeval ×tamp) { Image *send_image = prepareImage(image); - if ( !config.timestamp_on_capture && timestamp ) + if (!config.timestamp_on_capture) monitor->TimestampImage(send_image, timestamp); fputs("--" BOUNDARY "\r\n", stdout); @@ -395,8 +395,8 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) { static struct timeval base_time; struct DeltaTimeval delta_time; if ( !frame_count ) - base_time = *timestamp; - DELTA_TIMEVAL(delta_time, *timestamp, base_time, DT_PREC_3); + base_time = timestamp; + DELTA_TIMEVAL(delta_time, timestamp, base_time, DT_PREC_3); /* double pts = */ vid_stream->EncodeFrame(send_image->Buffer(), send_image->Size(), config.mpeg_timed_frames, delta_time.delta); } else #endif // HAVE_LIBAVCODEC @@ -437,7 +437,7 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) { } if ( ( 0 > fprintf(stdout, "Content-Length: %d\r\nX-Timestamp: %d.%06d\r\n\r\n", - img_buffer_size, (int)timestamp->tv_sec, (int)timestamp->tv_usec) ) + img_buffer_size, (int)timestamp.tv_sec, (int)timestamp.tv_usec) ) || (fwrite(img_buffer, img_buffer_size, 1, stdout) != 1) ) { @@ -462,7 +462,7 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) { } // Not mpeg last_frame_sent = TV_2_FLOAT(now); return true; -} // end bool MonitorStream::sendFrame( Image *image, struct timeval *timestamp ) +} // end bool MonitorStream::sendFrame( Image *image, const timeval ×tamp ) void MonitorStream::runStream() { if (type == STREAM_SINGLE) { @@ -595,8 +595,8 @@ void MonitorStream::runStream() { if ( !was_paused ) { int index = monitor->shared_data->last_write_index % monitor->image_buffer_count; Debug(1, "Saving paused image from index %d",index); - paused_image = new Image(*monitor->image_buffer[index].image); - paused_timestamp = *(monitor->image_buffer[index].timestamp); + paused_image = new Image(*monitor->image_buffer[index]); + paused_timestamp = monitor->shared_timestamps[index]; } } else if ( paused_image ) { Debug(1, "Clearing paused_image"); @@ -635,7 +635,7 @@ void MonitorStream::runStream() { if ( temp_index%frame_mod == 0 ) { Debug(2, "Sending delayed frame %d", temp_index); // Send the next frame - if ( ! sendFrame(temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp) ) { + if (!sendFrame(temp_image_buffer[temp_index].file_name, temp_image_buffer[temp_index].timestamp)) { zm_terminate = true; } memcpy(&last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp)); @@ -652,7 +652,7 @@ void MonitorStream::runStream() { // Send the next frame if ( !sendFrame( temp_image_buffer[temp_read_index].file_name, - &temp_image_buffer[temp_read_index].timestamp + temp_image_buffer[temp_read_index].timestamp ) ) { zm_terminate = true; } @@ -672,7 +672,7 @@ void MonitorStream::runStream() { // Send keepalive Debug(2, "Sending keepalive frame %d", temp_index); // Send the next frame - if ( !sendFrame(temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp) ) { + if ( !sendFrame(temp_image_buffer[temp_index].file_name, temp_image_buffer[temp_index].timestamp) ) { zm_terminate = true; } // frame_sent = true; @@ -701,21 +701,21 @@ void MonitorStream::runStream() { index, frame_mod, frame_count, paused, delayed); // Send the next frame // - ZMPacket *snap = &monitor->image_buffer[index]; + // Perhaps we should use NOW instead. + last_frame_timestamp = monitor->shared_timestamps[index]; + Image *image = monitor->image_buffer[index]; - if ( !sendFrame(snap->image, snap->timestamp) ) { + if ( !sendFrame(image, last_frame_timestamp) ) { Debug(2, "sendFrame failed, quiting."); zm_terminate = true; break; } - // Perhaps we should use NOW instead. - last_frame_timestamp = *(snap->timestamp); //frame_sent = true; // if ( frame_count == 0 ) { // Chrome will not display the first frame until it receives another. // Firefox is fine. So just send the first frame twice. - if ( !sendFrame(snap->image, snap->timestamp) ) { + if ( !sendFrame(image, last_frame_timestamp) ) { Debug(2, "sendFrame failed, quiting."); zm_terminate = true; break; @@ -730,9 +730,9 @@ void MonitorStream::runStream() { } if ( last_zoom != zoom ) { Debug(2, "Sending 2 frames because change in zoom %d ?= %d", last_zoom, zoom); - if ( !sendFrame(paused_image, &paused_timestamp) ) + if (!sendFrame(paused_image, paused_timestamp)) zm_terminate = true; - if ( !sendFrame(paused_image, &paused_timestamp) ) + if (!sendFrame(paused_image, paused_timestamp)) zm_terminate = true; } else { double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent; @@ -742,7 +742,7 @@ void MonitorStream::runStream() { Debug(2, "Sending keepalive frame because delta time %.2f > 5", actual_delta_time); // Send the next frame - if ( !sendFrame(paused_image, &paused_timestamp) ) + if (!sendFrame(paused_image, paused_timestamp)) zm_terminate = true; } else { Debug(2, "Would have sent keepalive frame, but had no paused_image"); @@ -767,7 +767,7 @@ void MonitorStream::runStream() { temp_image_buffer[temp_index].valid = true; } temp_image_buffer[temp_index].timestamp = monitor->shared_timestamps[index]; - monitor->image_buffer[index].image->WriteJpeg( + monitor->image_buffer[index]->WriteJpeg( temp_image_buffer[temp_index].file_name, config.jpeg_file_quality ); @@ -865,8 +865,7 @@ void MonitorStream::SingleImage(int scale) { } int index = monitor->shared_data->last_write_index % monitor->image_buffer_count; Debug(1, "write index: %d %d", monitor->shared_data->last_write_index, index); - ZMPacket *snap = &(monitor->image_buffer[index]); - Image *snap_image = snap->image; + Image *snap_image = monitor->image_buffer[index]; if ( scale != ZM_SCALE_BASE ) { scaled_image.Assign(*snap_image); @@ -874,7 +873,7 @@ void MonitorStream::SingleImage(int scale) { snap_image = &scaled_image; } if ( !config.timestamp_on_capture ) { - monitor->TimestampImage(snap_image, snap->timestamp); + monitor->TimestampImage(snap_image, monitor->shared_timestamps[index]); } snap_image->EncodeJpeg(img_buffer, &img_buffer_size); diff --git a/src/zm_monitorstream.h b/src/zm_monitorstream.h index 0c1f92777..5730bfc5a 100644 --- a/src/zm_monitorstream.h +++ b/src/zm_monitorstream.h @@ -45,8 +45,8 @@ class MonitorStream : public StreamBase { protected: bool checkSwapPath(const char *path, bool create_path); - bool sendFrame(const char *filepath, struct timeval *timestamp); - bool sendFrame(Image *image, struct timeval *timestamp); + bool sendFrame(const char *filepath, const timeval ×tamp); + bool sendFrame(Image *image, const timeval ×tamp); void processCommand(const CmdMsg *msg) override; void SingleImage(int scale=100); void SingleImageRaw(int scale=100); diff --git a/src/zm_packet.cpp b/src/zm_packet.cpp index 7aef2ddca..c4b39fb87 100644 --- a/src/zm_packet.cpp +++ b/src/zm_packet.cpp @@ -31,7 +31,7 @@ ZMPacket::ZMPacket() : stream(nullptr), in_frame(nullptr), out_frame(nullptr), - timestamp(nullptr), + timestamp({}), buffer(nullptr), image(nullptr), analysis_image(nullptr), @@ -41,6 +41,28 @@ ZMPacket::ZMPacket() : codec_imgsize(0), pts(0), decoded(0) +{ + Debug(1, "ZMPacket"); + av_init_packet(&packet); + packet.size = 0; // So we can detect whether it has been filled. + Debug(1, "ZMPacket"); +} + +ZMPacket::ZMPacket(Image *i, const timeval &tv) : + keyframe(0), + stream(nullptr), + in_frame(nullptr), + out_frame(nullptr), + timestamp(tv), + buffer(nullptr), + image(i), + analysis_image(nullptr), + score(-1), + codec_type(AVMEDIA_TYPE_UNKNOWN), + image_index(-1), + codec_imgsize(0), + pts(0), + decoded(0) { av_init_packet(&packet); packet.size = 0; // So we can detect whether it has been filled. @@ -51,7 +73,7 @@ ZMPacket::ZMPacket(ZMPacket &p) : stream(nullptr), in_frame(nullptr), out_frame(nullptr), - timestamp(nullptr), + timestamp(p.timestamp), buffer(nullptr), image(nullptr), analysis_image(nullptr), @@ -68,18 +90,15 @@ ZMPacket::ZMPacket(ZMPacket &p) : if ( zm_av_packet_ref(&packet, &p.packet) < 0 ) { Error("error refing packet"); } - timestamp = new struct timeval; - *timestamp = *p.timestamp; } ZMPacket::~ZMPacket() { zm_av_packet_unref(&packet); - if ( in_frame ) av_frame_free(&in_frame); - if ( out_frame ) av_frame_free(&out_frame); - if ( buffer ) av_freep(&buffer); - if ( analysis_image ) delete analysis_image; - if ( image ) delete image; - if ( timestamp ) delete timestamp; + if (in_frame) av_frame_free(&in_frame); + if (out_frame) av_frame_free(&out_frame); + if (buffer) av_freep(&buffer); + if (analysis_image) delete analysis_image; + if (image) delete image; } /* returns < 0 on error, 0 on not ready, int bytes consumed on success @@ -227,7 +246,7 @@ AVPacket *ZMPacket::set_packet(AVPacket *p) { Error("error refing packet"); } //ZM_DUMP_PACKET(packet, "zmpacket:"); - gettimeofday(timestamp, nullptr); + gettimeofday(×tamp, nullptr); keyframe = p->flags & AV_PKT_FLAG_KEY; return &packet; } diff --git a/src/zm_packet.h b/src/zm_packet.h index a7deca325..83f74e6b9 100644 --- a/src/zm_packet.h +++ b/src/zm_packet.h @@ -41,6 +41,7 @@ class ZMPacket { public: std::mutex mutex_; + // The condition has to be in the packet because it is shared between locks std::condition_variable condition_; int keyframe; @@ -48,7 +49,7 @@ class ZMPacket { AVPacket packet; // Input packet, undecoded AVFrame *in_frame; // Input image, decoded Theoretically only filled if needed. AVFrame *out_frame; // output image, Only filled if needed. - struct timeval *timestamp; + timeval timestamp; uint8_t *buffer; // buffer used in image Image *image; Image *analysis_image; @@ -69,7 +70,7 @@ class ZMPacket { int is_keyframe() { return keyframe; }; int decode( AVCodecContext *ctx ); - explicit ZMPacket(Image *image); + explicit ZMPacket(Image *image, const timeval &tv); explicit ZMPacket(ZMPacket &packet); ZMPacket(); ~ZMPacket(); @@ -81,11 +82,11 @@ class ZMPacket { class ZMLockedPacket { public: - ZMPacket *packet_; + std::shared_ptr packet_; std::unique_lock lck_; bool locked; - explicit ZMLockedPacket(ZMPacket *p) : + explicit ZMLockedPacket(std::shared_ptr p) : packet_(p), lck_(packet_->mutex_, std::defer_lock), locked(false) { diff --git a/src/zm_packetqueue.cpp b/src/zm_packetqueue.cpp index a1f149bb4..32b944d2b 100644 --- a/src/zm_packetqueue.cpp +++ b/src/zm_packetqueue.cpp @@ -75,8 +75,7 @@ PacketQueue::~PacketQueue() { * Thus it will ensure that the same packet never gets queued twice. */ -bool PacketQueue::queuePacket(ZMPacket* add_packet) { - Debug(4, "packetqueue queuepacket %p %d", add_packet, add_packet->image_index); +bool PacketQueue::queuePacket(std::shared_ptr add_packet) { if (iterators.empty()) { Debug(4, "No iterators so no one needs us to queue packets."); return false; @@ -97,7 +96,7 @@ bool PacketQueue::queuePacket(ZMPacket* add_packet) { if (add_packet->keyframe) { // Have a new keyframe, so delete everything while ((*pktQueue.begin() != add_packet) and (packet_counts[video_stream_id] > max_video_packet_count)) { - ZMPacket *zm_packet = *pktQueue.begin(); + std::shared_ptr zm_packet = *pktQueue.begin(); ZMLockedPacket *lp = new ZMLockedPacket(zm_packet); if (!lp->trylock()) { Debug(1, "Found locked packet when trying to free up video packets. Can't continue"); @@ -129,7 +128,6 @@ bool PacketQueue::queuePacket(ZMPacket* add_packet) { packet_counts[video_stream_id], max_video_packet_count, pktQueue.size()); - delete zm_packet; } // end while } } // end if too many video packets @@ -166,7 +164,7 @@ bool PacketQueue::queuePacket(ZMPacket* add_packet) { return true; } // end bool PacketQueue::queuePacket(ZMPacket* zm_packet) -void PacketQueue::clearPackets(ZMPacket *add_packet) { +void PacketQueue::clearPackets(const std::shared_ptr &add_packet) { // Only do queueCleaning if we are adding a video keyframe, so that we guarantee that there is one. // No good. Have to satisfy two conditions: // 1. packetqueue starts with a video keyframe @@ -211,7 +209,7 @@ void PacketQueue::clearPackets(ZMPacket *add_packet) { if (!keep_keyframes) { // If not doing passthrough, we don't care about starting with a keyframe so logic is simpler while ((*pktQueue.begin() != add_packet) and (packet_counts[video_stream_id] > pre_event_video_packet_count + tail_count)) { - ZMPacket *zm_packet = *pktQueue.begin(); + std::shared_ptr zm_packet = *pktQueue.begin(); ZMLockedPacket *lp = new ZMLockedPacket(zm_packet); if (!lp->trylock()) break; delete lp; @@ -231,7 +229,7 @@ void PacketQueue::clearPackets(ZMPacket *add_packet) { packet_counts[video_stream_id], pre_event_video_packet_count, pktQueue.size()); - delete zm_packet; + //delete zm_packet; } // end while return; } @@ -241,7 +239,11 @@ void PacketQueue::clearPackets(ZMPacket *add_packet) { int video_packets_to_delete = 0; // This is a count of how many packets we will delete so we know when to stop looking // First packet is special because we know it is a video keyframe and only need to check for lock - ZMPacket *zm_packet = *it; + std::shared_ptr zm_packet = *it; + if (zm_packet == add_packet) { + return; + } + Debug(1, "trying lock on first packet"); ZMLockedPacket *lp = new ZMLockedPacket(zm_packet); if (lp->trylock()) { @@ -288,10 +290,10 @@ void PacketQueue::clearPackets(ZMPacket *add_packet) { ( *it == add_packet ), ( next_front == pktQueue.begin() ) ); - if ( next_front != pktQueue.begin() ) { - while ( pktQueue.begin() != next_front ) { - ZMPacket *zm_packet = *pktQueue.begin(); - if ( !zm_packet ) { + if (next_front != pktQueue.begin()) { + while (pktQueue.begin() != next_front) { + std::shared_ptr zm_packet = *pktQueue.begin(); + if (!zm_packet) { Error("NULL zm_packet in queue"); continue; } @@ -306,7 +308,7 @@ void PacketQueue::clearPackets(ZMPacket *add_packet) { pktQueue.size()); pktQueue.pop_front(); packet_counts[zm_packet->packet.stream_index] -= 1; - delete zm_packet; + //delete zm_packet; } } // end if have at least max_video_packet_count video packets remaining // We signal on every packet because someday we may analyze sound @@ -314,116 +316,6 @@ void PacketQueue::clearPackets(ZMPacket *add_packet) { return; } // end voidPacketQueue::clearPackets(ZMPacket* zm_packet) -ZMLockedPacket* PacketQueue::popPacket( ) { - Debug(4, "pktQueue size %zu", pktQueue.size()); - if ( pktQueue.empty() ) { - return nullptr; - } - Debug(4, "poPacket Mutex locking"); - std::unique_lock lck(mutex); - - ZMPacket *zm_packet = pktQueue.front(); - for ( - std::list::iterator iterators_it = iterators.begin(); - iterators_it != iterators.end(); - ++iterators_it - ) { - packetqueue_iterator *iterator_it = *iterators_it; - // Have to check each iterator and make sure it doesn't point to the packet we are about to delete - if ( *(*iterator_it) == zm_packet ) { - Debug(4, "Bumping it because it is at the front that we are deleting"); - ++(*iterators_it); - } - } // end foreach iterator - - ZMLockedPacket *lp = new ZMLockedPacket (zm_packet); - lp->lock(); - - pktQueue.pop_front(); - packet_counts[zm_packet->packet.stream_index] -= 1; - - return lp; -} // popPacket - - -/* Keeps frames_to_keep frames of the provided stream, which theoretically is the video stream - * Basically it starts at the end, moving backwards until it finds the minimum video frame. - * Then it should probably move forward to find a keyframe. The first video frame must always be a keyframe. - * So really frames_to_keep is a maximum which isn't so awesome.. maybe we should go back farther to find the keyframe in which case - * frames_to_keep in a minimum - */ - -unsigned int PacketQueue::clear(unsigned int frames_to_keep, int stream_id) { - Debug(3, "Clearing all but %d frames, queue has %zu", frames_to_keep, pktQueue.size()); - - if ( pktQueue.empty() ) { - return 0; - } - - // If size is <= frames_to_keep since it could contain audio, we can't possibly do anything - if ( pktQueue.size() <= frames_to_keep ) { - return 0; - } - Debug(5, "Locking in clear"); - std::unique_lock lck(mutex); - - packetqueue_iterator it = pktQueue.end()--; // point to last element instead of end - ZMPacket *zm_packet = nullptr; - - while ( (it != pktQueue.begin()) and frames_to_keep ) { - zm_packet = *it; - AVPacket *av_packet = &(zm_packet->packet); - - Debug(3, "Looking at packet with stream index (%d) with keyframe(%d), Image_index(%d) frames_to_keep is (%d)", - av_packet->stream_index, zm_packet->keyframe, zm_packet->image_index, frames_to_keep ); - - // Want frames_to_keep video keyframes. Otherwise, we may not have enough - if ( av_packet->stream_index == stream_id ) { - frames_to_keep --; - } - it --; - } - - // Either at beginning or frames_to_keep == 0 - - if ( it == pktQueue.begin() ) { - if ( frames_to_keep ) { - Warning("Couldn't remove any packets, needed %d", frames_to_keep); - } - mutex.unlock(); - return 0; - } - - int delete_count = 0; - - // Else not at beginning, are pointing at packet before the last video packet - while ( pktQueue.begin() != it ) { - Debug(4, "Deleting a packet from the front, count is (%d), queue size is %zu", - delete_count, pktQueue.size()); - zm_packet = pktQueue.front(); - for ( - std::list::iterator iterators_it = iterators.begin(); - iterators_it != iterators.end(); - ++iterators_it - ) { - packetqueue_iterator *iterator_it = *iterators_it; - // Have to check each iterator and make sure it doesn't point to the packet we are about to delete - if ( *(*iterator_it) == zm_packet ) { - Debug(4, "Bumping it because it is at the front that we are deleting"); - ++(*iterators_it); - } - } // end foreach iterator - packet_counts[zm_packet->packet.stream_index] --; - pktQueue.pop_front(); - //if ( zm_packet->image_index == -1 ) - delete zm_packet; - - delete_count += 1; - } // while our iterator is not the first packet - Debug(3, "Deleted %d packets, %zu remaining", delete_count, pktQueue.size()); - return delete_count; -} // end unsigned int PacketQueue::clear( unsigned int frames_to_keep, int stream_id ) - void PacketQueue::clear() { deleting = true; condition.notify_all(); @@ -431,13 +323,13 @@ void PacketQueue::clear() { std::unique_lock lck(mutex); while (!pktQueue.empty()) { - ZMPacket *packet = pktQueue.front(); + std::shared_ptr packet = pktQueue.front(); // Someone might have this packet, but not for very long and since we have locked the queue they won't be able to get another one ZMLockedPacket *lp = new ZMLockedPacket(packet); lp->lock(); pktQueue.pop_front(); delete lp; - delete packet; + //delete packet; } for ( @@ -449,119 +341,32 @@ void PacketQueue::clear() { *iterator_it = pktQueue.begin(); } // end foreach iterator - if ( packet_counts ) delete[] packet_counts; + if (packet_counts) delete[] packet_counts; packet_counts = nullptr; max_stream_id = -1; condition.notify_all(); } -// clear queue keeping only specified duration of video -- return number of pkts removed -unsigned int PacketQueue::clear(struct timeval *duration, int streamId) { - - if ( pktQueue.empty() ) { - return 0; - } - Debug(4, "Locking in clear"); - std::unique_lock lck(mutex); - - struct timeval keep_from; - std::list::reverse_iterator it = pktQueue.rbegin(); - - struct timeval *t = (*it)->timestamp; - timersub(t, duration, &keep_from); - ++it; - - Debug(3, "Looking for frame before queue keep time with stream id (%d), queue has %zu packets", - streamId, pktQueue.size()); - for ( ; it != pktQueue.rend(); ++it) { - ZMPacket *zm_packet = *it; - AVPacket *av_packet = &(zm_packet->packet); - if ( - (av_packet->stream_index == streamId) - and - timercmp(zm_packet->timestamp, &keep_from, <=) - ) { - Debug(3, "Found frame before keep time with stream index %d at %" PRIi64 ".%" PRIi64, - av_packet->stream_index, - static_cast(zm_packet->timestamp->tv_sec), - static_cast(zm_packet->timestamp->tv_usec)); - break; - } - } - - if ( it == pktQueue.rend() ) { - Debug(1, "Didn't find a frame before queue preserve time. keeping all"); - mutex.unlock(); - return 0; - } - - Debug(3, "Looking for keyframe"); - for ( ; it != pktQueue.rend(); ++it) { - ZMPacket *zm_packet = *it; - AVPacket *av_packet = &(zm_packet->packet); - if ( - (av_packet->flags & AV_PKT_FLAG_KEY) - and - (av_packet->stream_index == streamId) - ) { - Debug(3, "Found keyframe before start with stream index %d at %" PRIi64 ".%" PRIi64, - av_packet->stream_index, - static_cast(zm_packet->timestamp->tv_sec), - static_cast(zm_packet->timestamp->tv_usec)); - break; - } - } - if ( it == pktQueue.rend() ) { - Debug(1, "Didn't find a keyframe before event starttime. keeping all" ); - return 0; - } - - unsigned int deleted_frames = 0; - ZMPacket *zm_packet = nullptr; - while ( distance(it, pktQueue.rend()) > 1 ) { - zm_packet = pktQueue.front(); - for ( - std::list::iterator iterators_it = iterators.begin(); - iterators_it != iterators.end(); - ++iterators_it - ) { - packetqueue_iterator *iterator_it = *iterators_it; - // Have to check each iterator and make sure it doesn't point to the packet we are about to delete - if ( *(*iterator_it) == zm_packet ) { - Debug(4, "Bumping it because it is at the front that we are deleting"); - ++(*iterators_it); - } - } // end foreach iterator - pktQueue.pop_front(); - packet_counts[zm_packet->packet.stream_index] -= 1; - delete zm_packet; - deleted_frames += 1; - } - Debug(3, "Deleted %d frames", deleted_frames); - return deleted_frames; -} - unsigned int PacketQueue::size() { return pktQueue.size(); } int PacketQueue::packet_count(int stream_id) { - if ( stream_id < 0 or stream_id > max_stream_id ) { + if (stream_id < 0 or stream_id > max_stream_id) { Error("Invalid stream_id %d max is %d", stream_id, max_stream_id); return -1; } return packet_counts[stream_id]; } // end int PacketQueue::packet_count(int stream_id) - // Returns a packet. Packet will be locked ZMLockedPacket *PacketQueue::get_packet(packetqueue_iterator *it) { if (deleting or zm_terminate) return nullptr; - Debug(4, "Locking in get_packet using it %p queue end? %d, packet %p", - std::addressof(*it), (*it == pktQueue.end()), *(*it)); + Debug(4, "Locking in get_packet using it %p queue end? %d", + std::addressof(*it), (*it == pktQueue.end())); std::unique_lock lck(mutex); Debug(4, "Have Lock in get_packet"); @@ -582,13 +387,13 @@ ZMLockedPacket *PacketQueue::get_packet(packetqueue_iterator *it) { return nullptr; } - ZMPacket *p = *(*it); + std::shared_ptr p = *(*it); if (!p) { Error("Null p?!"); return nullptr; } - Debug(4, "get_packet using it %p locking index %d, packet %p", - std::addressof(*it), p->image_index, p); + Debug(4, "get_packet using it %p locking index %d", + std::addressof(*it), p->image_index); // Packets are only deleted by packetqueue, so lock must be held. // We shouldn't have to trylock. Someone else might hold the lock but not for long @@ -655,7 +460,7 @@ packetqueue_iterator *PacketQueue::get_event_start_packet_it( iterators.push_back(it); *it = snapshot_it; - ZMPacket *packet = *(*it); + std::shared_ptr packet = *(*it); ZM_DUMP_PACKET(packet->packet, ""); // Step one count back pre_event_count frames as the minimum // Do not assume that snapshot_it is video @@ -702,9 +507,9 @@ packetqueue_iterator *PacketQueue::get_event_start_packet_it( } // end packetqueue_iterator *PacketQueue::get_event_start_packet_it void PacketQueue::dumpQueue() { - std::list::reverse_iterator it; + std::list>::reverse_iterator it; for ( it = pktQueue.rbegin(); it != pktQueue.rend(); ++ it ) { - ZMPacket *zm_packet = *it; + std::shared_ptr zm_packet = *it; ZM_DUMP_PACKET(zm_packet->packet, ""); } } @@ -732,8 +537,8 @@ packetqueue_iterator * PacketQueue::get_video_it(bool wait) { } } - while ( *it != pktQueue.end() ) { - ZMPacket *zm_packet = *(*it); + while (*it != pktQueue.end()) { + std::shared_ptr zm_packet = *(*it); if (!zm_packet) { Error("Null zmpacket in queue!?"); free_it(it); @@ -764,19 +569,19 @@ void PacketQueue::free_it(packetqueue_iterator *it) { } } -bool PacketQueue::is_there_an_iterator_pointing_to_packet(ZMPacket *zm_packet) { +bool PacketQueue::is_there_an_iterator_pointing_to_packet(const std::shared_ptr &zm_packet) { for ( std::list::iterator iterators_it = iterators.begin(); iterators_it != iterators.end(); ++iterators_it ) { packetqueue_iterator *iterator_it = *iterators_it; - if ( *iterator_it == pktQueue.end() ) { + if (*iterator_it == pktQueue.end()) { continue; } Debug(4, "Checking iterator %p == packet ? %d", std::addressof(*iterator_it), ( *(*iterator_it) == zm_packet )); // Have to check each iterator and make sure it doesn't point to the packet we are about to delete - if ( *(*iterator_it) == zm_packet ) { + if (*(*iterator_it) == zm_packet) { return true; } } // end foreach iterator @@ -786,13 +591,13 @@ bool PacketQueue::is_there_an_iterator_pointing_to_packet(ZMPacket *zm_packet) { void PacketQueue::setMaxVideoPackets(int p) { max_video_packet_count = p; Debug(1, "Setting max_video_packet_count to %d", p); - if ( max_video_packet_count < 0 ) + if (max_video_packet_count < 0) max_video_packet_count = 0 ; } void PacketQueue::setPreEventVideoPackets(int p) { pre_event_video_packet_count = p; Debug(1, "Setting pre_event_video_packet_count to %d", p); - if ( pre_event_video_packet_count < 1 ) + if (pre_event_video_packet_count < 1) pre_event_video_packet_count = 1; // We can simplify a lot of logic in queuePacket if we can assume at least 1 packet in queue } diff --git a/src/zm_packetqueue.h b/src/zm_packetqueue.h index bc9932f24..cc163d2f1 100644 --- a/src/zm_packetqueue.h +++ b/src/zm_packetqueue.h @@ -26,12 +26,12 @@ class ZMPacket; class ZMLockedPacket; -typedef std::list::iterator packetqueue_iterator; +typedef std::list>::iterator packetqueue_iterator; class PacketQueue { public: // For now just to ease development - std::list pktQueue; - std::list::iterator analysis_it; + std::list> pktQueue; + std::list>::iterator analysis_it; int video_stream_id; int max_video_packet_count; // allow a negative value to someday mean unlimited @@ -49,27 +49,21 @@ class PacketQueue { public: PacketQueue(); virtual ~PacketQueue(); - std::list::const_iterator end() const { return pktQueue.end(); } - std::list::const_iterator begin() const { return pktQueue.begin(); } + std::list>::const_iterator end() const { return pktQueue.end(); } + std::list>::const_iterator begin() const { return pktQueue.begin(); } int addStream(); void setMaxVideoPackets(int p); void setPreEventVideoPackets(int p); void setKeepKeyframes(bool k) { keep_keyframes = k; }; - bool queuePacket(ZMPacket* packet); - ZMLockedPacket * popPacket(); - bool popVideoPacket(ZMPacket* packet); - bool popAudioPacket(ZMPacket* packet); - unsigned int clear(unsigned int video_frames_to_keep, int stream_id); - unsigned int clear(struct timeval *duration, int streamid); + bool queuePacket(std::shared_ptr packet); void clear(); void dumpQueue(); unsigned int size(); unsigned int get_packet_count(int stream_id) const { return packet_counts[stream_id]; }; - void clear_unwanted_packets(timeval *recording, int pre_event_count, int mVideoStreamId); - void clearPackets(ZMPacket *); + void clearPackets(const std::shared_ptr &packet); int packet_count(int stream_id); bool increment_it(packetqueue_iterator *it); @@ -83,7 +77,7 @@ class PacketQueue { packetqueue_iterator snapshot_it, unsigned int pre_event_count ); - bool is_there_an_iterator_pointing_to_packet(ZMPacket *zm_packet); + bool is_there_an_iterator_pointing_to_packet(const std::shared_ptr &zm_packet); void unlock(ZMLockedPacket *lp); }; diff --git a/src/zm_remote_camera.h b/src/zm_remote_camera.h index b4d9af3bc..e08975af2 100644 --- a/src/zm_remote_camera.h +++ b/src/zm_remote_camera.h @@ -83,7 +83,7 @@ public: virtual int Disconnect() = 0; virtual int PreCapture() override { return 0; }; virtual int PrimeCapture() override { return 0; }; - virtual int Capture(ZMPacket &p) override = 0; + virtual int Capture(std::shared_ptr &p) override = 0; virtual int PostCapture() override = 0; int Read(int fd, char*buf, int size); }; diff --git a/src/zm_remote_camera_http.cpp b/src/zm_remote_camera_http.cpp index 6eab03fb8..f09db425e 100644 --- a/src/zm_remote_camera_http.cpp +++ b/src/zm_remote_camera_http.cpp @@ -1069,7 +1069,7 @@ int RemoteCameraHttp::PreCapture() { return 1; } // end int RemoteCameraHttp::PreCapture() -int RemoteCameraHttp::Capture(ZMPacket &packet) { +int RemoteCameraHttp::Capture(std::shared_ptr &packet) { int content_length = GetResponse(); if (content_length == 0) { Warning("Unable to capture image, retrying"); @@ -1080,15 +1080,15 @@ int RemoteCameraHttp::Capture(ZMPacket &packet) { return -1; } - if (!packet.image) { + if (!packet->image) { Debug(4, "Allocating image"); - packet.image = new Image(width, height, colours, subpixelorder); + packet->image = new Image(width, height, colours, subpixelorder); } - Image *image = packet.image; - packet.keyframe = 1; - packet.codec_type = AVMEDIA_TYPE_VIDEO; - packet.packet.stream_index = mVideoStreamId; - packet.stream = mVideoStream; + Image *image = packet->image; + packet->keyframe = 1; + packet->codec_type = AVMEDIA_TYPE_VIDEO; + packet->packet.stream_index = mVideoStreamId; + packet->stream = mVideoStream; switch (format) { case JPEG : diff --git a/src/zm_remote_camera_http.h b/src/zm_remote_camera_http.h index dc793459b..794ccba78 100644 --- a/src/zm_remote_camera_http.h +++ b/src/zm_remote_camera_http.h @@ -69,7 +69,7 @@ public: int GetResponse(); int PrimeCapture() override; int PreCapture() override; - int Capture( ZMPacket &p ) override; + int Capture(std::shared_ptr &p) override; int PostCapture() override; int Close() override { Disconnect(); return 0; }; }; diff --git a/src/zm_remote_camera_nvsocket.cpp b/src/zm_remote_camera_nvsocket.cpp index f66d31fcb..92f806eed 100644 --- a/src/zm_remote_camera_nvsocket.cpp +++ b/src/zm_remote_camera_nvsocket.cpp @@ -184,9 +184,9 @@ int RemoteCameraNVSocket::PrimeCapture() { return 0; } -int RemoteCameraNVSocket::Capture( ZMPacket &zm_packet ) { - if ( SendRequest("GetNextImage\n") < 0 ) { - Warning( "Unable to capture image, retrying" ); +int RemoteCameraNVSocket::Capture(std::shared_ptr &zm_packet) { + if (SendRequest("GetNextImage\n") < 0) { + Warning("Unable to capture image, retrying"); return 0; } int bytes_read = Read(sd, buffer, imagesize); @@ -195,17 +195,17 @@ int RemoteCameraNVSocket::Capture( ZMPacket &zm_packet ) { return 0; } uint32_t end; - if ( Read(sd, (char *) &end , sizeof(end)) < 0 ) { + if (Read(sd, (char *) &end , sizeof(end)) < 0) { Warning("Unable to capture image, retrying"); return 0; } - if ( end != 0xFFFFFFFF) { + if (end != 0xFFFFFFFF) { Warning("End Bytes Failed\n"); return 0; } - zm_packet.image->Assign(width, height, colours, subpixelorder, buffer, imagesize); - zm_packet.keyframe = 1; + zm_packet->image->Assign(width, height, colours, subpixelorder, buffer, imagesize); + zm_packet->keyframe = 1; return 1; } diff --git a/src/zm_remote_camera_nvsocket.h b/src/zm_remote_camera_nvsocket.h index 1cd740af2..d3b5ce8e9 100644 --- a/src/zm_remote_camera_nvsocket.h +++ b/src/zm_remote_camera_nvsocket.h @@ -54,7 +54,7 @@ public: int SendRequest(std::string); int GetResponse(); int PrimeCapture() override; - int Capture(ZMPacket &p) override; + int Capture(std::shared_ptr &p) override; int PostCapture() override; int Close() override { return 0; }; }; diff --git a/src/zm_remote_camera_rtsp.cpp b/src/zm_remote_camera_rtsp.cpp index 78c947faa..e35217cc8 100644 --- a/src/zm_remote_camera_rtsp.cpp +++ b/src/zm_remote_camera_rtsp.cpp @@ -218,16 +218,16 @@ int RemoteCameraRtsp::PreCapture() { return 1; } -int RemoteCameraRtsp::Capture(ZMPacket &zm_packet) { +int RemoteCameraRtsp::Capture(std::shared_ptr &zm_packet) { int frameComplete = false; - AVPacket *packet = &zm_packet.packet; - if ( !zm_packet.image ) { + AVPacket *packet = &zm_packet->packet; + if ( !zm_packet->image ) { Debug(1, "Allocating image %dx%d %d colours %d", width, height, colours, subpixelorder); - zm_packet.image = new Image(width, height, colours, subpixelorder); + zm_packet->image = new Image(width, height, colours, subpixelorder); } - while ( !frameComplete ) { + while (!frameComplete) { buffer.clear(); if (!rtspThread || rtspThread->IsStopped()) return -1; @@ -254,7 +254,7 @@ int RemoteCameraRtsp::Capture(ZMPacket &zm_packet) { continue; } else if ( nalType == 5 ) { packet->flags |= AV_PKT_FLAG_KEY; - zm_packet.keyframe = 1; + zm_packet->keyframe = 1; // IDR buffer += lastSps; buffer += lastPps; @@ -275,14 +275,14 @@ int RemoteCameraRtsp::Capture(ZMPacket &zm_packet) { gettimeofday(&now, NULL); packet->pts = packet->dts = now.tv_sec*1000000+now.tv_usec; - int bytes_consumed = zm_packet.decode(mVideoCodecContext); + int bytes_consumed = zm_packet->decode(mVideoCodecContext); if ( bytes_consumed < 0 ) { Error("Error while decoding frame %d", frameCount); //Hexdump(Logger::ERROR, buffer.head(), buffer.size()>256?256:buffer.size()); } buffer -= packet->size; if ( bytes_consumed ) { - zm_dump_video_frame(zm_packet.in_frame, "remote_rtsp_decode"); + zm_dump_video_frame(zm_packet->in_frame, "remote_rtsp_decode"); if ( ! mVideoStream-> #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) codecpar @@ -293,18 +293,18 @@ int RemoteCameraRtsp::Capture(ZMPacket &zm_packet) { zm_dump_codec(mVideoCodecContext); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) zm_dump_codecpar(mVideoStream->codecpar); - mVideoStream->codecpar->width = zm_packet.in_frame->width; - mVideoStream->codecpar->height = zm_packet.in_frame->height; + mVideoStream->codecpar->width = zm_packet->in_frame->width; + mVideoStream->codecpar->height = zm_packet->in_frame->height; #else - mVideoStream->codec->width = zm_packet.in_frame->width; - mVideoStream->codec->height = zm_packet.in_frame->height; + mVideoStream->codec->width = zm_packet->in_frame->width; + mVideoStream->codec->height = zm_packet->in_frame->height; #endif #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) zm_dump_codecpar(mVideoStream->codecpar); #endif } - zm_packet.codec_type = mVideoCodecContext->codec_type; - zm_packet.stream = mVideoStream; + zm_packet->codec_type = mVideoCodecContext->codec_type; + zm_packet->stream = mVideoStream; frameComplete = true; Debug(2, "Frame: %d - %d/%d", frameCount, bytes_consumed, buffer.size()); packet->data = nullptr; diff --git a/src/zm_remote_camera_rtsp.h b/src/zm_remote_camera_rtsp.h index c4734ebee..a815b96b1 100644 --- a/src/zm_remote_camera_rtsp.h +++ b/src/zm_remote_camera_rtsp.h @@ -79,7 +79,7 @@ public: int PrimeCapture() override; int PreCapture() override; - int Capture(ZMPacket &p) override; + int Capture(std::shared_ptr &p) override; int PostCapture() override; int Close() override { return 0; }; diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 843558de3..1c6855eac 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -1002,17 +1002,17 @@ bool VideoStore::setup_resampler() { #endif } // end bool VideoStore::setup_resampler() -int VideoStore::writePacket(ZMPacket *ipkt) { - if ( ipkt->codec_type == AVMEDIA_TYPE_VIDEO ) { +int VideoStore::writePacket(const std::shared_ptr &ipkt) { + if (ipkt->codec_type == AVMEDIA_TYPE_VIDEO) { return writeVideoFramePacket(ipkt); - } else if ( ipkt->codec_type == AVMEDIA_TYPE_AUDIO ) { + } else if (ipkt->codec_type == AVMEDIA_TYPE_AUDIO) { return writeAudioFramePacket(ipkt); } Error("Unknown stream type in packet (%d)", ipkt->codec_type); return 0; } -int VideoStore::writeVideoFramePacket(ZMPacket *zm_packet) { +int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet) { int ret; frame_count += 1; @@ -1056,8 +1056,8 @@ int VideoStore::writeVideoFramePacket(ZMPacket *zm_packet) { swscale.Convert(zm_packet->in_frame, out_frame); } else { - Error("Have neither in_frame or image in packet %p %d!", - zm_packet, zm_packet->image_index); + Error("Have neither in_frame or image in packet %d!", + zm_packet->image_index); return 0; } // end if has packet or image } else { @@ -1104,13 +1104,13 @@ int VideoStore::writeVideoFramePacket(ZMPacket *zm_packet) { frame->pkt_duration = 0; #endif - int64_t in_pts = zm_packet->timestamp->tv_sec * (uint64_t)1000000 + zm_packet->timestamp->tv_usec; + int64_t in_pts = zm_packet->timestamp.tv_sec * (uint64_t)1000000 + zm_packet->timestamp.tv_usec; if ( !video_first_pts ) { video_first_pts = in_pts; Debug(2, "No video_first_pts, set to (%" PRId64 ") secs(%" PRIi64 ") usecs(%" PRIi64 ")", video_first_pts, - static_cast(zm_packet->timestamp->tv_sec), - static_cast(zm_packet->timestamp->tv_usec)); + static_cast(zm_packet->timestamp.tv_sec), + static_cast(zm_packet->timestamp.tv_usec)); frame->pts = 0; } else { uint64_t useconds = in_pts - video_first_pts; @@ -1121,8 +1121,8 @@ int VideoStore::writeVideoFramePacket(ZMPacket *zm_packet) { frame->pts, video_first_pts, useconds, - static_cast(zm_packet->timestamp->tv_sec), - static_cast(zm_packet->timestamp->tv_usec), + static_cast(zm_packet->timestamp.tv_sec), + static_cast(zm_packet->timestamp.tv_usec), video_out_ctx->time_base.num, video_out_ctx->time_base.den); } @@ -1228,7 +1228,7 @@ int VideoStore::writeVideoFramePacket(ZMPacket *zm_packet) { return 1; } // end int VideoStore::writeVideoFramePacket( AVPacket *ipkt ) -int VideoStore::writeAudioFramePacket(ZMPacket *zm_packet) { +int VideoStore::writeAudioFramePacket(const std::shared_ptr &zm_packet) { AVPacket *ipkt = &zm_packet->packet; int ret; diff --git a/src/zm_videostore.h b/src/zm_videostore.h index 47872db2f..185424dcb 100644 --- a/src/zm_videostore.h +++ b/src/zm_videostore.h @@ -6,6 +6,8 @@ #include "zm_ffmpeg.h" #include "zm_swscale.h" +#include + extern "C" { #ifdef HAVE_LIBSWRESAMPLE #include "libswresample/swresample.h" @@ -119,9 +121,9 @@ class VideoStore { void write_video_packet(AVPacket &pkt); void write_audio_packet(AVPacket &pkt); - int writeVideoFramePacket(ZMPacket *pkt); - int writeAudioFramePacket(ZMPacket *pkt); - int writePacket(ZMPacket *pkt); + int writeVideoFramePacket(const std::shared_ptr &pkt); + int writeAudioFramePacket(const std::shared_ptr &pkt); + int writePacket(const std::shared_ptr &pkt); int write_packets(PacketQueue &queue); void flush_codecs(); }; diff --git a/src/zm_zone.cpp b/src/zm_zone.cpp index 106ab2ce0..7cf2252a8 100644 --- a/src/zm_zone.cpp +++ b/src/zm_zone.cpp @@ -109,7 +109,7 @@ void Zone::Setup( } // end Zone::Setup Zone::~Zone() { - if ( image ) + if (image) delete image; delete pg_image; delete[] ranges; diff --git a/src/zmc.cpp b/src/zmc.cpp index c2ee0a071..0ae934f75 100644 --- a/src/zmc.cpp +++ b/src/zmc.cpp @@ -301,6 +301,7 @@ int main(int argc, char *argv[]) { result = -1; break; } + Debug(1, "Capture:"); if (monitors[i]->Capture() < 0) { Error("Failed to capture image from monitor %d %s (%zu/%zu)", monitors[i]->Id(), monitors[i]->Name(), i + 1, monitors.size()); diff --git a/web/api/app/Plugin/Crud b/web/api/app/Plugin/Crud index 14292374c..0bd63fb46 160000 --- a/web/api/app/Plugin/Crud +++ b/web/api/app/Plugin/Crud @@ -1 +1 @@ -Subproject commit 14292374ccf1328f2d5db20897bd06f99ba4d938 +Subproject commit 0bd63fb464957080ead342db58ca9e01532cf1ef From e970c46f7b96a91e59c3c8f0d42f4a2acf46b8cb Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 9 May 2021 07:18:36 -0400 Subject: [PATCH 010/394] fix memleak. Fixes #3217 --- src/zm_monitor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 63ce15d60..7a1311060 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -2629,6 +2629,7 @@ bool Monitor::Decode() { delete packet->image; packet->image = nullptr; } + av_frame_unref(dest_frame); } // end if have convert_context } // end if need transfer to image } else { From 01834d4ddc5f53f7847c91fc5e954b1a94d393e6 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 9 May 2021 07:35:47 -0400 Subject: [PATCH 011/394] finish converting Capture to std::shared_ptr --- src/zm_curl_camera.cpp | 14 +++++++------- src/zm_curl_camera.h | 2 +- src/zm_libvlc_camera.cpp | 8 ++++---- src/zm_libvlc_camera.h | 2 +- src/zm_libvnc_camera.cpp | 16 ++++++++-------- src/zm_libvnc_camera.h | 2 +- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/zm_curl_camera.cpp b/src/zm_curl_camera.cpp index a107d78eb..4c9f7c737 100644 --- a/src/zm_curl_camera.cpp +++ b/src/zm_curl_camera.cpp @@ -165,7 +165,7 @@ int cURLCamera::PreCapture() { return( 0 ); } -int cURLCamera::Capture( ZMPacket &zm_packet ) { +int cURLCamera::Capture(std::shared_ptr &zm_packet) { bool frameComplete = false; /* MODE_STREAM specific variables */ @@ -297,27 +297,27 @@ int cURLCamera::Capture( ZMPacket &zm_packet ) { need_more_data = true; } else { /* All good. decode the image */ - zm_packet.image->DecodeJpeg(databuffer.extract(frame_content_length), frame_content_length, colours, subpixelorder); + zm_packet->image->DecodeJpeg(databuffer.extract(frame_content_length), frame_content_length, colours, subpixelorder); frameComplete = true; } } /* Attempt to get more data */ - if(need_more_data) { + if (need_more_data) { nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex); - if(nRet != 0) { + if (nRet != 0) { Error("Failed waiting for available data condition variable: %s",strerror(nRet)); return -1; } need_more_data = false; } - } else if(mode == MODE_SINGLE) { + } else if (mode == MODE_SINGLE) { /* Check if we have anything */ if (!single_offsets.empty()) { - if( (single_offsets.front() > 0) && (databuffer.size() >= single_offsets.front()) ) { + if ((single_offsets.front() > 0) && (databuffer.size() >= single_offsets.front())) { /* Extract frame */ - zm_packet.image->DecodeJpeg(databuffer.extract(single_offsets.front()), single_offsets.front(), colours, subpixelorder); + zm_packet->image->DecodeJpeg(databuffer.extract(single_offsets.front()), single_offsets.front(), colours, subpixelorder); single_offsets.pop_front(); frameComplete = true; } else { diff --git a/src/zm_curl_camera.h b/src/zm_curl_camera.h index bd2266d5f..302d8f877 100644 --- a/src/zm_curl_camera.h +++ b/src/zm_curl_camera.h @@ -88,7 +88,7 @@ public: int PrimeCapture() override; int PreCapture() override; - int Capture(ZMPacket &p)override; + int Capture(std::shared_ptr &p)override; int PostCapture()override ; size_t data_callback(void *buffer, size_t size, size_t nmemb, void *userdata); diff --git a/src/zm_libvlc_camera.cpp b/src/zm_libvlc_camera.cpp index 59cdc4ccf..beda38498 100644 --- a/src/zm_libvlc_camera.cpp +++ b/src/zm_libvlc_camera.cpp @@ -276,7 +276,7 @@ int LibvlcCamera::PreCapture() { } // Should not return -1 as cancels capture. Always wait for image if available. -int LibvlcCamera::Capture( ZMPacket &zm_packet ) { +int LibvlcCamera::Capture(std::shared_ptr &zm_packet) { // newImage is a mutex/condition based flag to tell us when there is an image available { std::unique_lock lck(mLibvlcData.newImageMutex); @@ -288,9 +288,9 @@ int LibvlcCamera::Capture( ZMPacket &zm_packet ) { return 0; mLibvlcData.mutex.lock(); - zm_packet.image->Assign(width, height, colours, subpixelorder, mLibvlcData.buffer, width * height * mBpp); - zm_packet.packet.stream_index = mVideoStreamId; - zm_packet.stream = mVideoStream; + zm_packet->image->Assign(width, height, colours, subpixelorder, mLibvlcData.buffer, width * height * mBpp); + zm_packet->packet.stream_index = mVideoStreamId; + zm_packet->stream = mVideoStream; mLibvlcData.mutex.unlock(); return 1; diff --git a/src/zm_libvlc_camera.h b/src/zm_libvlc_camera.h index 7d9be27c1..c516a63e0 100644 --- a/src/zm_libvlc_camera.h +++ b/src/zm_libvlc_camera.h @@ -72,7 +72,7 @@ public: int PrimeCapture() override; int PreCapture() override; - int Capture(ZMPacket &p) override; + int Capture(std::shared_ptr &p) override; int PostCapture() override; int Close() override { return 0; }; }; diff --git a/src/zm_libvnc_camera.cpp b/src/zm_libvnc_camera.cpp index faa594a7e..6fb414686 100644 --- a/src/zm_libvnc_camera.cpp +++ b/src/zm_libvnc_camera.cpp @@ -208,21 +208,21 @@ int VncCamera::PreCapture() { return res == TRUE ? 1 : -1; } -int VncCamera::Capture(ZMPacket &zm_packet) { +int VncCamera::Capture(std::shared_ptr &zm_packet) { if (!mVncData.buffer) { Debug(1, "No buffer"); return 0; } - if (!zm_packet.image) { + if (!zm_packet->image) { Debug(1, "Allocating image %dx%d %dcolours = %d", width, height, colours, colours*pixels); - zm_packet.image = new Image(width, height, colours, subpixelorder); + zm_packet->image = new Image(width, height, colours, subpixelorder); } - zm_packet.keyframe = 1; - zm_packet.codec_type = AVMEDIA_TYPE_VIDEO; - zm_packet.packet.stream_index = mVideoStreamId; - zm_packet.stream = mVideoStream; + zm_packet->keyframe = 1; + zm_packet->codec_type = AVMEDIA_TYPE_VIDEO; + zm_packet->packet.stream_index = mVideoStreamId; + zm_packet->stream = mVideoStream; - uint8_t *directbuffer = zm_packet.image->WriteBuffer(width, height, colours, subpixelorder); + uint8_t *directbuffer = zm_packet->image->WriteBuffer(width, height, colours, subpixelorder); Debug(1, "scale src %p, %d, dest %p %d %d %dx%d %dx%d", mVncData.buffer, mRfb->si.framebufferWidth * mRfb->si.framebufferHeight * 4, directbuffer, diff --git a/src/zm_libvnc_camera.h b/src/zm_libvnc_camera.h index caa1bb1da..20cad4f4b 100644 --- a/src/zm_libvnc_camera.h +++ b/src/zm_libvnc_camera.h @@ -53,7 +53,7 @@ public: int PreCapture() override; int PrimeCapture() override; - int Capture(ZMPacket &packet) override; + int Capture(std::shared_ptr &packet) override; int PostCapture() override; int Close() override; }; From 3cd9bdccd5e86864e42af7c8d5f3fc66bb50301e Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 9 May 2021 16:39:38 -0400 Subject: [PATCH 012/394] style and performance improvements reported by cppcheck --- src/zm_fifo.cpp | 3 +- src/zm_monitor.cpp | 13 +- src/zm_monitor.h | 2 +- src/zm_packet.cpp | 2 +- src/zm_packetqueue.cpp | 8 +- src/zm_rtsp_auth.cpp | 2 +- src/zm_rtsp_auth.h | 4 +- src/zm_rtsp_server_device_source.cpp | 2 +- src/zm_rtsp_server_fifo_adts_source.cpp | 2 +- src/zm_rtsp_server_fifo_adts_source.h | 2 +- src/zm_rtsp_server_fifo_h264_source.cpp | 10 +- src/zm_rtsp_server_fifo_h264_source.h | 4 +- src/zm_rtsp_server_fifo_source.cpp | 4 +- src/zm_rtsp_server_fifo_source.h | 2 +- src/zm_rtsp_server_fifo_video_source.cpp | 6 +- src/zm_rtsp_server_fifo_video_source.h | 2 +- src/zm_rtsp_server_h264_device_source.cpp | 241 ------------------ src/zm_rtsp_server_h264_device_source.h | 104 -------- ...zm_rtsp_server_server_media_subsession.cpp | 3 +- src/zm_videostore.cpp | 13 +- 20 files changed, 45 insertions(+), 384 deletions(-) delete mode 100644 src/zm_rtsp_server_h264_device_source.cpp delete mode 100644 src/zm_rtsp_server_h264_device_source.h diff --git a/src/zm_fifo.cpp b/src/zm_fifo.cpp index 731909166..0204d5117 100644 --- a/src/zm_fifo.cpp +++ b/src/zm_fifo.cpp @@ -41,10 +41,9 @@ void Fifo::file_create_if_missing( Debug(5, "Supposed to be a fifo pipe but isn't, unlinking: %s", path); unlink(path); } - int fd; if (!is_fifo) { Debug(5, "Creating non fifo file as requested: %s", path); - fd = ::open(path, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR); + int fd = ::open(path, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR); ::close(fd); return; } diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 7a1311060..0b8903253 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -2381,7 +2381,7 @@ void Monitor::ReloadLinkedMonitors(const char *p_linked_monitors) { } // end if p_linked_monitors } // end void Monitor::ReloadLinkedMonitors(const char *p_linked_monitors) -std::vector> Monitor::LoadMonitors(std::string &where, Purpose purpose) { +std::vector> Monitor::LoadMonitors(const std::string &where, Purpose purpose) { std::string sql = load_monitor_sql + " WHERE " + where; Debug(1, "Loading Monitors with %s", sql.c_str()); @@ -2974,7 +2974,7 @@ bool Monitor::DumpSettings(char *output, bool verbose) { function==MOCORD?"Continuous Record with Motion Detection":( function==NODECT?"Externally Triggered only, no Motion Detection":"Unknown" )))))); - sprintf(output+strlen(output), "Zones : %lu\n", zones.size()); + sprintf(output+strlen(output), "Zones : %zu\n", zones.size()); for (const Zone &zone : zones) { zone.DumpSettings(output+strlen(output), verbose); } @@ -3145,21 +3145,22 @@ void Monitor::get_ref_image() { std::vector Monitor::Groups() { // At the moment, only load groups once. - if ( !groups.size() ) { + if (!groups.size()) { std::string sql = stringtf( "SELECT `Id`, `ParentId`, `Name` FROM `Groups` WHERE `Groups.Id` IN " "(SELECT `GroupId` FROM `Groups_Monitors` WHERE `MonitorId`=%d)", id); MYSQL_RES *result = zmDbFetch(sql.c_str()); - if ( !result ) { + if (!result) { Error("Can't load groups: %s", mysql_error(&dbconn)); return groups; } int n_groups = mysql_num_rows(result); Debug(1, "Got %d groups", n_groups); - while ( MYSQL_ROW dbrow = mysql_fetch_row(result) ) { + groups.reserve(n_groups); + while (MYSQL_ROW dbrow = mysql_fetch_row(result)) { groups.push_back(new Group(dbrow)); } - if ( mysql_errno(&dbconn) ) { + if (mysql_errno(&dbconn)) { Error("Can't fetch row: %s", mysql_error(&dbconn)); } mysql_free_result(result); diff --git a/src/zm_monitor.h b/src/zm_monitor.h index 328c4e941..c0603a19a 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -594,7 +594,7 @@ public: std::vector Groups(); StringVector GroupNames(); - static std::vector> LoadMonitors(std::string &sql, Purpose purpose); // Returns # of Monitors loaded, 0 on failure. + static std::vector> LoadMonitors(const std::string &sql, Purpose purpose); // Returns # of Monitors loaded, 0 on failure. #if ZM_HAS_V4L static std::vector> LoadLocalMonitors(const char *device, Purpose purpose); #endif // ZM_HAS_V4L diff --git a/src/zm_packet.cpp b/src/zm_packet.cpp index c4b39fb87..799a514de 100644 --- a/src/zm_packet.cpp +++ b/src/zm_packet.cpp @@ -282,7 +282,7 @@ AVFrame *ZMPacket::get_out_frame(int width, int height, AVPixelFormat format) { codec_imgsize = avpicture_get_size( format, width, - >height); + height); buffer = (uint8_t *)av_malloc(codec_imgsize); avpicture_fill( (AVPicture *)out_frame, diff --git a/src/zm_packetqueue.cpp b/src/zm_packetqueue.cpp index 32b944d2b..603dada0a 100644 --- a/src/zm_packetqueue.cpp +++ b/src/zm_packetqueue.cpp @@ -236,7 +236,6 @@ void PacketQueue::clearPackets(const std::shared_ptr &add_packet) { packetqueue_iterator it = pktQueue.begin(); packetqueue_iterator next_front = pktQueue.begin(); - int video_packets_to_delete = 0; // This is a count of how many packets we will delete so we know when to stop looking // First packet is special because we know it is a video keyframe and only need to check for lock std::shared_ptr zm_packet = *it; @@ -247,6 +246,7 @@ void PacketQueue::clearPackets(const std::shared_ptr &add_packet) { Debug(1, "trying lock on first packet"); ZMLockedPacket *lp = new ZMLockedPacket(zm_packet); if (lp->trylock()) { + int video_packets_to_delete = 0; // This is a count of how many packets we will delete so we know when to stop looking Debug(1, "Have lock on first packet"); ++it; delete lp; @@ -282,7 +282,7 @@ void PacketQueue::clearPackets(const std::shared_ptr &add_packet) { break; } } - it++; + ++it; } // end while } } // end if first packet not locked @@ -292,7 +292,7 @@ void PacketQueue::clearPackets(const std::shared_ptr &add_packet) { ); if (next_front != pktQueue.begin()) { while (pktQueue.begin() != next_front) { - std::shared_ptr zm_packet = *pktQueue.begin(); + zm_packet = *pktQueue.begin(); if (!zm_packet) { Error("NULL zm_packet in queue"); continue; @@ -478,10 +478,10 @@ packetqueue_iterator *PacketQueue::get_event_start_packet_it( } (*it)--; } + packet = *(*it); } // it either points to beginning or we have seen pre_event_count video packets. - packet = *(*it); if (pre_event_count) { if (packet->image_index < (int)pre_event_count) { // probably just starting up diff --git a/src/zm_rtsp_auth.cpp b/src/zm_rtsp_auth.cpp index 4a1c6b2e2..34fd1921b 100644 --- a/src/zm_rtsp_auth.cpp +++ b/src/zm_rtsp_auth.cpp @@ -95,7 +95,7 @@ std::string Authenticator::quote( const std::string &src ) { return ReplaceAll(ReplaceAll(src, "\\", "\\\\"), "\"", "\\\""); } -std::string Authenticator::getAuthHeader(std::string method, std::string uri) { +std::string Authenticator::getAuthHeader(const std::string &method, const std::string &uri) { std::string result = "Authorization: "; if ( fAuthMethod == AUTH_BASIC ) { result += "Basic " + Base64Encode(username() + ":" + password()); diff --git a/src/zm_rtsp_auth.h b/src/zm_rtsp_auth.h index 701b4b128..4ea664989 100644 --- a/src/zm_rtsp_auth.h +++ b/src/zm_rtsp_auth.h @@ -47,8 +47,8 @@ public: AuthMethod auth_method() const { return fAuthMethod; } std::string computeDigestResponse(const std::string &cmd, const std::string &url); - void authHandleHeader( std::string headerData ); - std::string getAuthHeader( std::string method, std::string path ); + void authHandleHeader(std::string headerData); + std::string getAuthHeader(const std::string &method, const std::string &path); void checkAuthResponse(const std::string &response); private: diff --git a/src/zm_rtsp_server_device_source.cpp b/src/zm_rtsp_server_device_source.cpp index abd1f8381..ba648b2f4 100644 --- a/src/zm_rtsp_server_device_source.cpp +++ b/src/zm_rtsp_server_device_source.cpp @@ -23,13 +23,13 @@ ZoneMinderDeviceSource::ZoneMinderDeviceSource( unsigned int queueSize ) : FramedSource(env), + m_eventTriggerId(envir().taskScheduler().createEventTrigger(ZoneMinderDeviceSource::deliverFrameStub)), m_stream(stream), m_monitor(std::move(monitor)), m_packetqueue(nullptr), m_packetqueue_it(nullptr), m_queueSize(queueSize) { - m_eventTriggerId = envir().taskScheduler().createEventTrigger(ZoneMinderDeviceSource::deliverFrameStub); memset(&m_thid, 0, sizeof(m_thid)); memset(&m_mutex, 0, sizeof(m_mutex)); if ( m_monitor ) { diff --git a/src/zm_rtsp_server_fifo_adts_source.cpp b/src/zm_rtsp_server_fifo_adts_source.cpp index 6c01371e5..bc979d0a6 100644 --- a/src/zm_rtsp_server_fifo_adts_source.cpp +++ b/src/zm_rtsp_server_fifo_adts_source.cpp @@ -22,7 +22,7 @@ ADTS_ZoneMinderFifoSource::ADTS_ZoneMinderFifoSource( std::shared_ptr& rtspServer, xop::MediaSessionId sessionId, xop::MediaChannelId channelId, - std::string fifo + const std::string &fifo ) : ZoneMinderFifoAudioSource(rtspServer, sessionId, channelId, fifo) diff --git a/src/zm_rtsp_server_fifo_adts_source.h b/src/zm_rtsp_server_fifo_adts_source.h index 0bbe0890c..4a8f86e85 100644 --- a/src/zm_rtsp_server_fifo_adts_source.h +++ b/src/zm_rtsp_server_fifo_adts_source.h @@ -26,7 +26,7 @@ class ADTS_ZoneMinderFifoSource : public ZoneMinderFifoAudioSource { std::shared_ptr& rtspServer, xop::MediaSessionId sessionId, xop::MediaChannelId channelId, - std::string fifo + const std::string &fifo ); virtual ~ADTS_ZoneMinderFifoSource() {} diff --git a/src/zm_rtsp_server_fifo_h264_source.cpp b/src/zm_rtsp_server_fifo_h264_source.cpp index 9963ea26c..b96f3da8b 100644 --- a/src/zm_rtsp_server_fifo_h264_source.cpp +++ b/src/zm_rtsp_server_fifo_h264_source.cpp @@ -23,7 +23,7 @@ H264_ZoneMinderFifoSource::H264_ZoneMinderFifoSource( std::shared_ptr& rtspServer, xop::MediaSessionId sessionId, xop::MediaChannelId channelId, - std::string fifo + const std::string &fifo ) : H26X_ZoneMinderFifoSource(rtspServer, sessionId, channelId, fifo) { @@ -96,7 +96,7 @@ H265_ZoneMinderFifoSource::H265_ZoneMinderFifoSource( std::shared_ptr& rtspServer, xop::MediaSessionId sessionId, xop::MediaChannelId channelId, - std::string fifo + const std::string &fifo ) : H26X_ZoneMinderFifoSource(rtspServer, sessionId, channelId, fifo) { @@ -199,12 +199,12 @@ unsigned char* H26X_ZoneMinderFifoSource::extractFrame(unsigned char* frame, si Debug(4, "ExtractFrame: %p %zu", frame, size); outsize = 0; size_t markerLength = 0; - size_t endMarkerLength = 0; m_frameType = 0; unsigned char *startFrame = nullptr; - if ( size >= 3 ) + if (size >= 3) startFrame = this->findMarker(frame, size, markerLength); - if ( startFrame != nullptr ) { + if (startFrame != nullptr) { + size_t endMarkerLength = 0; Debug(4, "startFrame: %p marker Length %zu", startFrame, markerLength); m_frameType = startFrame[markerLength]; diff --git a/src/zm_rtsp_server_fifo_h264_source.h b/src/zm_rtsp_server_fifo_h264_source.h index d771c9ac7..cf605f40a 100644 --- a/src/zm_rtsp_server_fifo_h264_source.h +++ b/src/zm_rtsp_server_fifo_h264_source.h @@ -50,7 +50,7 @@ class H264_ZoneMinderFifoSource : public H26X_ZoneMinderFifoSource { std::shared_ptr& rtspServer, xop::MediaSessionId sessionId, xop::MediaChannelId channelId, - std::string fifo + const std::string &fifo ); // overide ZoneMinderFifoSource @@ -63,7 +63,7 @@ class H265_ZoneMinderFifoSource : public H26X_ZoneMinderFifoSource { std::shared_ptr& rtspServer, xop::MediaSessionId sessionId, xop::MediaChannelId channelId, - std::string fifo + const std::string &fifo ); // overide ZoneMinderFifoSource diff --git a/src/zm_rtsp_server_fifo_source.cpp b/src/zm_rtsp_server_fifo_source.cpp index 6a6066bcf..d83096717 100644 --- a/src/zm_rtsp_server_fifo_source.cpp +++ b/src/zm_rtsp_server_fifo_source.cpp @@ -24,7 +24,7 @@ ZoneMinderFifoSource::ZoneMinderFifoSource( std::shared_ptr& rtspServer, xop::MediaSessionId sessionId, xop::MediaChannelId channelId, - std::string fifo + const std::string &fifo ) : stop_(false), m_rtspServer(rtspServer), @@ -225,7 +225,7 @@ int ZoneMinderFifoSource::getNextFrame() { if (bytes_needed > 0) { Debug(4, "Need another %d bytes. Trying to read them", bytes_needed); while (bytes_needed) { - int bytes_read = m_buffer.read_into(m_fd, bytes_needed); + bytes_read = m_buffer.read_into(m_fd, bytes_needed); if (bytes_read <= 0) { Debug(1, "Failed to read another %d bytes, got %d.", bytes_needed, bytes_read); return -1; diff --git a/src/zm_rtsp_server_fifo_source.h b/src/zm_rtsp_server_fifo_source.h index eb9fd4fe9..eea7c8230 100644 --- a/src/zm_rtsp_server_fifo_source.h +++ b/src/zm_rtsp_server_fifo_source.h @@ -35,7 +35,7 @@ class ZoneMinderFifoSource { std::shared_ptr& rtspServer, xop::MediaSessionId sessionId, xop::MediaChannelId channelId, - std::string fifo + const std::string &fifo ); virtual ~ZoneMinderFifoSource(); diff --git a/src/zm_rtsp_server_fifo_video_source.cpp b/src/zm_rtsp_server_fifo_video_source.cpp index df8c32955..4c5b382bd 100644 --- a/src/zm_rtsp_server_fifo_video_source.cpp +++ b/src/zm_rtsp_server_fifo_video_source.cpp @@ -15,9 +15,11 @@ ZoneMinderFifoVideoSource::ZoneMinderFifoVideoSource( std::shared_ptr& rtspServer, xop::MediaSessionId sessionId, xop::MediaChannelId channelId, - std::string fifo + const std::string &fifo ) : - ZoneMinderFifoSource(rtspServer, sessionId, channelId, fifo) + ZoneMinderFifoSource(rtspServer, sessionId, channelId, fifo), + m_width(0), + m_height(0) { m_timeBase = {1, 90000}; } diff --git a/src/zm_rtsp_server_fifo_video_source.h b/src/zm_rtsp_server_fifo_video_source.h index 765b79092..8bbef12f5 100644 --- a/src/zm_rtsp_server_fifo_video_source.h +++ b/src/zm_rtsp_server_fifo_video_source.h @@ -25,7 +25,7 @@ class ZoneMinderFifoVideoSource: public ZoneMinderFifoSource { std::shared_ptr& rtspServer, xop::MediaSessionId sessionId, xop::MediaChannelId channelId, - std::string fifo + const std::string &fifo ); protected: void PushFrame(const uint8_t *data, size_t size, int64_t pts) override; diff --git a/src/zm_rtsp_server_h264_device_source.cpp b/src/zm_rtsp_server_h264_device_source.cpp deleted file mode 100644 index 71b641021..000000000 --- a/src/zm_rtsp_server_h264_device_source.cpp +++ /dev/null @@ -1,241 +0,0 @@ -/* --------------------------------------------------------------------------- -** -** H264_DeviceSource.cpp -** -** H264 Live555 source -** -** -------------------------------------------------------------------------*/ - -#include "zm_rtsp_server_h264_device_source.h" - -#include "zm_config.h" -#include "zm_logger.h" -#include "zm_rtsp_server_frame.h" -#include -#include - -#if HAVE_RTSP_SERVER -// live555 -#include - -// --------------------------------- -// H264 ZoneMinder FramedSource -// --------------------------------- -// -H264_ZoneMinderDeviceSource::H264_ZoneMinderDeviceSource( - UsageEnvironment& env, - std::shared_ptr monitor, - AVStream *stream, - unsigned int queueSize, - bool repeatConfig, - bool keepMarker) - : H26X_ZoneMinderDeviceSource(env, std::move(monitor), stream, queueSize, repeatConfig, keepMarker) -{ - // extradata appears to simply be the SPS and PPS NAL's - this->splitFrames( -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - m_stream->codecpar->extradata, m_stream->codecpar->extradata_size -#else - m_stream->codec->extradata, m_stream->codec->extradata_size -#endif - ); -} - -// split packet into frames -std::list< std::pair > H264_ZoneMinderDeviceSource::splitFrames(unsigned char* frame, unsigned frameSize) { - std::list< std::pair > frameList; - - size_t bufSize = frameSize; - size_t size = 0; - unsigned char* buffer = this->extractFrame(frame, bufSize, size); - while ( buffer != nullptr ) { - switch ( m_frameType & 0x1F ) { - case 7: - Debug(1, "SPS_Size: %d bufSize %d", size, bufSize); - m_sps.assign((char*)buffer, size); - break; - case 8: - Debug(1, "PPS_Size: %d bufSize %d", size, bufSize); - m_pps.assign((char*)buffer, size); - break; - case 5: - Debug(1, "IDR_Size: %d bufSize %d", size, bufSize); - if ( m_repeatConfig && !m_sps.empty() && !m_pps.empty() ) { - frameList.push_back(std::pair((unsigned char*)m_sps.c_str(), m_sps.size())); - frameList.push_back(std::pair((unsigned char*)m_pps.c_str(), m_pps.size())); - } - break; - default: - Debug(1, "Unknown frametype!? %d %d", m_frameType, m_frameType & 0x1F); - break; - } - - if ( !m_sps.empty() && !m_pps.empty() ) { - u_int32_t profile_level_id = 0; - if ( m_sps.size() >= 4 ) profile_level_id = (m_sps[1]<<16)|(m_sps[2]<<8)|m_sps[3]; - - char* sps_base64 = base64Encode(m_sps.c_str(), m_sps.size()); - char* pps_base64 = base64Encode(m_pps.c_str(), m_pps.size()); - - std::ostringstream os; - os << "profile-level-id=" << std::hex << std::setw(6) << std::setfill('0') << profile_level_id; - os << ";sprop-parameter-sets=" << sps_base64 << "," << pps_base64; - m_auxLine.assign(os.str()); - Debug(1, "auxLine: %s", m_auxLine.c_str()); - - delete [] sps_base64; - delete [] pps_base64; - } - frameList.push_back(std::pair(buffer, size)); - - buffer = this->extractFrame(&buffer[size], bufSize, size); - } // end while buffer - return frameList; -} - -H265_ZoneMinderDeviceSource::H265_ZoneMinderDeviceSource( - UsageEnvironment& env, - std::shared_ptr monitor, - AVStream *stream, - unsigned int queueSize, - bool repeatConfig, - bool keepMarker) - : H26X_ZoneMinderDeviceSource(env, std::move(monitor), stream, queueSize, repeatConfig, keepMarker) -{ - // extradata appears to simply be the SPS and PPS NAL's - this->splitFrames( -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - m_stream->codecpar->extradata, m_stream->codecpar->extradata_size -#else - m_stream->codec->extradata, m_stream->codec->extradata_size -#endif - ); -} - -// split packet in frames -std::list< std::pair > -H265_ZoneMinderDeviceSource::splitFrames(unsigned char* frame, unsigned frameSize) { - std::list< std::pair > frameList; - - size_t bufSize = frameSize; - size_t size = 0; - unsigned char* buffer = this->extractFrame(frame, bufSize, size); - while ( buffer != nullptr ) { - switch ((m_frameType&0x7E)>>1) { - case 32: - Debug(1, "VPS_Size: %d bufSize %d", size, bufSize); - m_vps.assign((char*)buffer,size); - break; - case 33: - Debug(1, "SPS_Size: %d bufSize %d", size, bufSize); - m_sps.assign((char*)buffer,size); - break; - case 34: - Debug(1, "PPS_Size: %d bufSize %d", size, bufSize); - m_pps.assign((char*)buffer,size); - break; - case 19: - case 20: - Debug(1, "IDR_Size: %d bufSize %d", size, bufSize); - if ( m_repeatConfig && !m_vps.empty() && !m_sps.empty() && !m_pps.empty() ) { - frameList.push_back(std::pair((unsigned char*)m_vps.c_str(), m_vps.size())); - frameList.push_back(std::pair((unsigned char*)m_sps.c_str(), m_sps.size())); - frameList.push_back(std::pair((unsigned char*)m_pps.c_str(), m_pps.size())); - } - break; - default: - Debug(1, "Unknown frametype!? %d %d", m_frameType, ((m_frameType & 0x7E) >> 1)); - break; - } - - if ( !m_vps.empty() && !m_sps.empty() && !m_pps.empty() ) { - char* vps_base64 = base64Encode(m_vps.c_str(), m_vps.size()); - char* sps_base64 = base64Encode(m_sps.c_str(), m_sps.size()); - char* pps_base64 = base64Encode(m_pps.c_str(), m_pps.size()); - - std::ostringstream os; - os << "sprop-vps=" << vps_base64; - os << ";sprop-sps=" << sps_base64; - os << ";sprop-pps=" << pps_base64; - m_auxLine.assign(os.str()); - Debug(1, "Assigned auxLine to %s", m_auxLine.c_str()); - - delete [] vps_base64; - delete [] sps_base64; - delete [] pps_base64; - } - frameList.push_back(std::pair(buffer, size)); - - buffer = this->extractFrame(&buffer[size], bufSize, size); - } // end while buffer - if ( bufSize ) { - Debug(1, "%d bytes remaining", bufSize); - } - return frameList; -} // end H265_ZoneMinderDeviceSource::splitFrames(unsigned char* frame, unsigned frameSize) - -unsigned char * H26X_ZoneMinderDeviceSource::findMarker( - unsigned char *frame, size_t size, size_t &length - ) { - //Debug(1, "findMarker %p %d", frame, size); - unsigned char *start = nullptr; - for ( size_t i = 0; i < size-2; i += 1 ) { - //Debug(1, "%d: %d %d %d", i, frame[i], frame[i+1], frame[i+2]); - if ( (frame[i] == 0) and (frame[i+1]) == 0 and (frame[i+2] == 1) ) { - if ( i and (frame[i-1] == 0) ) { - start = frame + i - 1; - length = sizeof(H264marker); - } else { - start = frame + i; - length = sizeof(H264shortmarker); - } - break; - } - } - return start; -} - -// extract a frame -unsigned char* H26X_ZoneMinderDeviceSource::extractFrame(unsigned char* frame, size_t& size, size_t& outsize) { - unsigned char *outFrame = nullptr; - Debug(1, "ExtractFrame: %p %d", frame, size); - outsize = 0; - size_t markerLength = 0; - size_t endMarkerLength = 0; - m_frameType = 0; - unsigned char *startFrame = nullptr; - if ( size >= 3 ) - startFrame = this->findMarker(frame, size, markerLength); - if ( startFrame != nullptr ) { - Debug(1, "startFrame: %p marker Length %d", startFrame, markerLength); - m_frameType = startFrame[markerLength]; - - int remainingSize = size-(startFrame-frame+markerLength); - unsigned char *endFrame = nullptr; - if ( remainingSize > 3 ) { - endFrame = this->findMarker(startFrame+markerLength, remainingSize, endMarkerLength); - } - Debug(1, "endFrame: %p marker Length %d, remaining size %d", endFrame, endMarkerLength, remainingSize); - - if ( m_keepMarker ) { - size -= startFrame-frame; - outFrame = startFrame; - } else { - size -= startFrame-frame+markerLength; - outFrame = &startFrame[markerLength]; - } - - if ( endFrame != nullptr ) { - outsize = endFrame - outFrame; - } else { - outsize = size; - } - size -= outsize; - Debug(1, "Have frame type: %d size %d, keepmarker %d", m_frameType, outsize, m_keepMarker); - } else if ( size >= sizeof(H264shortmarker) ) { - Info("No marker found"); - } - - return outFrame; -} -#endif // HAVE_RTSP_SERVER diff --git a/src/zm_rtsp_server_h264_device_source.h b/src/zm_rtsp_server_h264_device_source.h deleted file mode 100644 index 5c01717c5..000000000 --- a/src/zm_rtsp_server_h264_device_source.h +++ /dev/null @@ -1,104 +0,0 @@ -/* --------------------------------------------------------------------------- -** This software is in the public domain, furnished "as is", without technical -** support, and with no warranty, express or implied, as to its usefulness for -** any purpose. -** -** H264_ZoneMinderDeviceSource.h -** -** H264 ZoneMinder live555 source -** -** -------------------------------------------------------------------------*/ - -#ifndef ZM_RTSP_H264_DEVICE_SOURCE_H -#define ZM_RTSP_H264_DEVICE_SOURCE_H - -#include "zm_config.h" -#include "zm_rtsp_server_device_source.h" - -// --------------------------------- -// H264 ZoneMinder FramedSource -// --------------------------------- -#if HAVE_RTSP_SERVER -class H26X_ZoneMinderDeviceSource : public ZoneMinderDeviceSource { - protected: - H26X_ZoneMinderDeviceSource( - UsageEnvironment& env, - std::shared_ptr monitor, - AVStream *stream, - unsigned int queueSize, - bool repeatConfig, - bool keepMarker) - : - ZoneMinderDeviceSource(env, std::move(monitor), stream, queueSize), - m_repeatConfig(repeatConfig), - m_keepMarker(keepMarker), - m_frameType(0) { } - - virtual ~H26X_ZoneMinderDeviceSource() {} - - virtual unsigned char* extractFrame(unsigned char* frame, size_t& size, size_t& outsize); - virtual unsigned char* findMarker(unsigned char *frame, size_t size, size_t &length); - - protected: - std::string m_sps; - std::string m_pps; - bool m_repeatConfig; - bool m_keepMarker; - int m_frameType; -}; - -class H264_ZoneMinderDeviceSource : public H26X_ZoneMinderDeviceSource { - public: - static H264_ZoneMinderDeviceSource* createNew( - UsageEnvironment& env, - std::shared_ptr monitor, - AVStream *stream, - unsigned int queueSize, - bool repeatConfig, - bool keepMarker) { - return new H264_ZoneMinderDeviceSource(env, std::move(monitor), stream, queueSize, repeatConfig, keepMarker); - } - - protected: - H264_ZoneMinderDeviceSource( - UsageEnvironment& env, - std::shared_ptr monitor, - AVStream *stream, - unsigned int queueSize, - bool repeatConfig, - bool keepMarker); - - // overide ZoneMinderDeviceSource - virtual std::list< std::pair > splitFrames(unsigned char* frame, unsigned frameSize); -}; - -class H265_ZoneMinderDeviceSource : public H26X_ZoneMinderDeviceSource { - public: - static H265_ZoneMinderDeviceSource* createNew( - UsageEnvironment& env, - std::shared_ptr monitor, - AVStream *stream, - unsigned int queueSize, - bool repeatConfig, - bool keepMarker) { - return new H265_ZoneMinderDeviceSource(env, std::move(monitor), stream, queueSize, repeatConfig, keepMarker); - } - - protected: - H265_ZoneMinderDeviceSource( - UsageEnvironment& env, - std::shared_ptr monitor, - AVStream *stream, - unsigned int queueSize, - bool repeatConfig, - bool keepMarker); - - // overide ZoneMinderDeviceSource - virtual std::list< std::pair > splitFrames(unsigned char* frame, unsigned frameSize); - - protected: - std::string m_vps; -}; -#endif // HAVE_RTSP_SERVER - -#endif // ZM_RTSP_H264_DEVICE_SOURCE_H diff --git a/src/zm_rtsp_server_server_media_subsession.cpp b/src/zm_rtsp_server_server_media_subsession.cpp index 141e8dad1..c847b471e 100644 --- a/src/zm_rtsp_server_server_media_subsession.cpp +++ b/src/zm_rtsp_server_server_media_subsession.cpp @@ -66,8 +66,9 @@ RTPSink* BaseServerMediaSubsession::createSink( sink = VP9VideoRTPSink::createNew(env, rtpGroupsock, rtpPayloadTypeIfDynamic); } else if (format == "video/H265") { sink = H265VideoRTPSink::createNew(env, rtpGroupsock, rtpPayloadTypeIfDynamic); + } #endif - } else if (format == "audio/AAC") { + else if (format == "audio/AAC") { ADTS_ZoneMinderFifoSource *adts_source = (ADTS_ZoneMinderFifoSource *)(m_replicator->inputSource()); sink = MPEG4GenericRTPSink::createNew(env, rtpGroupsock, rtpPayloadTypeIfDynamic, diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 1c6855eac..0ecac9609 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -252,13 +252,17 @@ bool VideoStore::open() { ret = av_hwdevice_ctx_create(&hw_device_ctx, codec_data[i].hwdevice_type, nullptr, nullptr, 0); + if (0>ret) { + Error("Failed to create hwdevice_ctx"); + continue; + } AVBufferRef *hw_frames_ref; AVHWFramesContext *frames_ctx = nullptr; if (!(hw_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx))) { Error("Failed to create hwaccel frame context."); - return -1; + continue; } frames_ctx = (AVHWFramesContext *)(hw_frames_ref->data); frames_ctx->format = codec_data[i].hw_pix_fmt; @@ -537,7 +541,6 @@ bool VideoStore::open() { } // end bool VideoStore::open() void VideoStore::flush_codecs() { - int ret; // The codec queues data. We need to send a flush command and out // whatever we get. Failures are not fatal. AVPacket pkt; @@ -555,7 +558,7 @@ void VideoStore::flush_codecs() { #endif ) ) { // Put encoder into flushing mode - while ( (ret = zm_send_frame_receive_packet(video_out_ctx, nullptr, pkt) ) > 0 ) { + while ((zm_send_frame_receive_packet(video_out_ctx, nullptr, pkt)) > 0) { av_packet_rescale_ts(&pkt, video_out_ctx->time_base, video_out_stream->time_base); @@ -1013,7 +1016,6 @@ int VideoStore::writePacket(const std::shared_ptr &ipkt) { } int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet) { - int ret; frame_count += 1; // if we have to transcode @@ -1070,6 +1072,7 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet #if HAVE_LIBAVUTIL_HWCONTEXT_H if (video_out_ctx->hw_frames_ctx) { + int ret; if (!(hw_frame = av_frame_alloc())) { ret = AVERROR(ENOMEM); return ret; @@ -1131,7 +1134,7 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet opkt.data = nullptr; opkt.size = 0; - ret = zm_send_frame_receive_packet(video_out_ctx, frame, opkt); + int ret = zm_send_frame_receive_packet(video_out_ctx, frame, opkt); if (ret <= 0) { if (ret < 0) { Error("Could not send frame (error '%s')", av_make_error_string(ret).c_str()); From 747c714699086e9b2cc8dfdf69c4c3b1a4f6d52d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 9 May 2021 18:15:37 -0400 Subject: [PATCH 013/394] put back up to date RtspServer --- dep/RtspServer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dep/RtspServer b/dep/RtspServer index d714323e6..cd7fd49be 160000 --- a/dep/RtspServer +++ b/dep/RtspServer @@ -1 +1 @@ -Subproject commit d714323e693ba106be6af363295d950f50ca15e0 +Subproject commit cd7fd49becad6010a1b8466bfebbd93999a39878 From b1ac0e61a962df60fe3061f37c5a79fdb7364033 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 9 May 2021 18:18:03 -0400 Subject: [PATCH 014/394] put back upstream version 3.2.0 of Crud --- web/api/app/Plugin/Crud | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/api/app/Plugin/Crud b/web/api/app/Plugin/Crud index 0bd63fb46..14292374c 160000 --- a/web/api/app/Plugin/Crud +++ b/web/api/app/Plugin/Crud @@ -1 +1 @@ -Subproject commit 0bd63fb464957080ead342db58ca9e01532cf1ef +Subproject commit 14292374ccf1328f2d5db20897bd06f99ba4d938 From f3b4ccf7589badf1f66693d3e1f473dd3b5d20ef Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 10 May 2021 11:08:16 -0400 Subject: [PATCH 015/394] Fix scaling when scale to fit --- web/skins/classic/views/js/watch.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index 9a5a4500b..fc5676212 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -116,9 +116,9 @@ function changeScale() { setCookie('zmWatchScale'+monitorId, scale, 3600); var streamImg = $j('#liveStream'+monitorId); - if ( streamImg ) { + if (streamImg) { var oldSrc = streamImg.attr('src'); - var newSrc = oldSrc.replace(/scale=\d+/i, 'scale='+(scale == 'auto' ? autoScale : scale)); + var newSrc = oldSrc.replace(/scale=\d+/i, 'scale='+((scale == 'auto' || scale == '0') ? autoScale : scale)); streamImg.width(newWidth); streamImg.height(newHeight); From b8e3cc33f0143fe12428177931729f4b78cde17f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 10 May 2021 12:54:35 -0400 Subject: [PATCH 016/394] make it so that the queue will more likely be empty on termination. Do not queue more sql if terminate flag is set. --- src/zm_db.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/zm_db.cpp b/src/zm_db.cpp index b296c571b..7224241b4 100644 --- a/src/zm_db.cpp +++ b/src/zm_db.cpp @@ -249,9 +249,11 @@ void zmDbQueue::process() { if (mQueue.empty()) { mCondition.wait(lock); } - if (!mQueue.empty()) { + while (!mQueue.empty()) { std::string sql = mQueue.front(); mQueue.pop(); + // My idea for leaving the locking around each sql statement is to allow + // other db writers to get a chance lock.unlock(); zmDbDo(sql.c_str()); lock.lock(); @@ -260,6 +262,7 @@ void zmDbQueue::process() { } // end void zmDbQueue::process() void zmDbQueue::push(std::string &&sql) { + if (mTerminate) return; std::unique_lock lock(mMutex); mQueue.push(std::move(sql)); mCondition.notify_all(); From 510b3e606b9fd76d1bb912bfa5f032d160949c97 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 11 May 2021 09:33:33 -0400 Subject: [PATCH 017/394] Continue if frame file not found. Output useful text jpeg --- src/zm_eventstream.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp index 7a35f6892..9b4f106e9 100644 --- a/src/zm_eventstream.cpp +++ b/src/zm_eventstream.cpp @@ -715,8 +715,6 @@ bool EventStream::sendFrame(int delta_us) { } else #endif // HAVE_LIBAVCODEC { - - bool send_raw = (type == STREAM_JPEG) && ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE)) && filepath[0]; fprintf(stdout, "--" BOUNDARY "\r\n"); @@ -1046,7 +1044,8 @@ bool EventStream::send_file(const char *filepath) { fdj = fopen(filepath, "rb"); if ( !fdj ) { Error("Can't open %s: %s", filepath, strerror(errno)); - return false; + std::string error_message = stringtf("Can't open %s: %s", filepath, strerror(errno)); + return sendTextFrame(error_message.c_str()); } #if HAVE_SENDFILE static struct stat filestat; From 76171fcc43d3ba19d33570fc0e0259270a32bab3 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 11 May 2021 10:20:40 -0400 Subject: [PATCH 018/394] If not doing passthrough, don't need to find the nearest keyframe --- src/zm_packetqueue.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/zm_packetqueue.cpp b/src/zm_packetqueue.cpp index 603dada0a..153c126bc 100644 --- a/src/zm_packetqueue.cpp +++ b/src/zm_packetqueue.cpp @@ -491,6 +491,9 @@ packetqueue_iterator *PacketQueue::get_event_start_packet_it( } ZM_DUMP_PACKET(packet->packet, ""); return it; + } else if (!keep_keyframes) { + // Are encoding, so don't care about keyframes + return it; } while ((*it) != pktQueue.begin()) { From c080dd8a12a2c574ef375b1c9cc2e4eb05836f3f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 11 May 2021 15:27:36 -0400 Subject: [PATCH 019/394] Fixes #2992. If no definition for a monitor is found, don't modify it. --- scripts/zmpkg.pl.in | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/scripts/zmpkg.pl.in b/scripts/zmpkg.pl.in index 2f6708fc8..3302b0019 100644 --- a/scripts/zmpkg.pl.in +++ b/scripts/zmpkg.pl.in @@ -103,18 +103,17 @@ if ( $command eq 'state' ) { or Fatal("Can't prepare '$sql': ".$dbh->errstr()); my $res = $sth->execute($Config{ZM_SERVER_ID} ? $Config{ZM_SERVER_ID}: ()) or Fatal("Can't execute: ".$sth->errstr()); - while( my $monitor = $sth->fetchrow_hashref() ) { + while ( my $monitor = $sth->fetchrow_hashref() ) { foreach my $definition ( @{$state->{Definitions}} ) { if ( $monitor->{Id} =~ /^$definition->{Id}$/ ) { $monitor->{NewFunction} = $definition->{Function}; $monitor->{NewEnabled} = $definition->{Enabled}; + last; } } - #next if ( !$monitor->{NewFunction} ); - $monitor->{NewFunction} = 'None' - if ( !$monitor->{NewFunction} ); - $monitor->{NewEnabled} = 0 - if ( !$monitor->{NewEnabled} ); + next if ! exists $monitor->{NewFunction}; + $monitor->{NewFunction} = 'None' if !$monitor->{NewFunction}; + $monitor->{NewEnabled} = 0 if !$monitor->{NewEnabled}; if ( $monitor->{Function} ne $monitor->{NewFunction} || $monitor->{Enabled} ne $monitor->{NewEnabled} ) { From bdae5dd9f82a8f8ad751078ba27b3a163b8456e1 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 11 May 2021 14:01:19 -0400 Subject: [PATCH 020/394] Sort field should be StartDateTime not StartTime --- scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in index a1d9521f6..645d5b4d6 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in @@ -2669,7 +2669,7 @@ our @options = ( }, { name => 'ZM_WEB_EVENT_SORT_FIELD', - default => 'StartTime', + default => 'StartDateTime', description => 'Default field the event lists are sorted by', help => q` Events in lists can be initially ordered in any way you want. @@ -2681,7 +2681,7 @@ our @options = ( `, type => { db_type =>'string', - hint =>'Id|Name|Cause|DiskSpace|MonitorName|StartTime|Length|Frames|AlarmFrames|TotScore|AvgScore|MaxScore', + hint =>'Id|Name|Cause|DiskSpace|MonitorName|StartDateTime|Length|Frames|AlarmFrames|TotScore|AvgScore|MaxScore', pattern =>qr|.|, format =>q( $1 ) }, From 363b017977a551480dc2cc881724aa280159de7d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 12 May 2021 10:31:22 -0400 Subject: [PATCH 021/394] If error was unauthorized, do a reload. --- web/skins/classic/js/skin.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/skins/classic/js/skin.js b/web/skins/classic/js/skin.js index 4d496cf51..3268ccf05 100644 --- a/web/skins/classic/js/skin.js +++ b/web/skins/classic/js/skin.js @@ -357,6 +357,9 @@ if ( currentView != 'none' && currentView != 'login' ) { .done(setNavBar) .fail(function(jqxhr, textStatus, error) { console.log("Request Failed: " + textStatus + ", " + error); + if (error == 'Unauthorized') { + window.location.reload(true); + } if ( ! jqxhr.responseText ) { console.log("No responseText in jqxhr"); console.log(jqxhr); From e246083be55abe7d8ede04dbba4ea33a6ee8e483 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 12 May 2021 10:31:58 -0400 Subject: [PATCH 022/394] Add StartDateTime Attr in sort dropdown --- web/skins/classic/views/filter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/filter.php b/web/skins/classic/views/filter.php index d16ebdef3..e6caedcb5 100644 --- a/web/skins/classic/views/filter.php +++ b/web/skins/classic/views/filter.php @@ -374,7 +374,7 @@ $sort_fields = array( 'DiskSpace' => translate('AttrDiskSpace'), 'Notes' => translate('AttrNotes'), 'MonitorName' => translate('AttrMonitorName'), - 'StartTime' => translate('AttrStartDateTime'), + 'StartDateTime' => translate('AttrStartDateTime'), 'Length' => translate('AttrDuration'), 'Frames' => translate('AttrFrames'), 'AlarmFrames' => translate('AttrAlarmFrames'), From 6f977da94d6096f05dee781d19a4f88b75ecdbe4 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 12 May 2021 14:21:54 -0400 Subject: [PATCH 023/394] Fix potential outofbounds access to image_buffer in getSnapshot() --- src/zm_monitor.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 0b8903253..bc6ac8453 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1162,7 +1162,7 @@ void Monitor::AddPrivacyBitmask() { } int Monitor::GetImage(int32_t index, int scale) { - if ( index < 0 || index > image_buffer_count ) { + if (index < 0 || index > image_buffer_count) { index = shared_data->last_write_index; } if ( index != image_buffer_count ) { @@ -1193,12 +1193,14 @@ int Monitor::GetImage(int32_t index, int scale) { } ZMPacket *Monitor::getSnapshot(int index) const { - - if ( (index < 0) || (index > image_buffer_count) ) { + if ((index < 0) || (index >= image_buffer_count)) { index = shared_data->last_write_index; } - return new ZMPacket(image_buffer[index], shared_timestamps[index]); - + if (index != image_buffer_count) { + return new ZMPacket(image_buffer[index], shared_timestamps[index]); + } else { + Error("Unable to generate image, no images in buffer"); + } return nullptr; } From 5a8fa9efc9ed4ca5ecc9b34c2a12dd1933049aee Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 12 May 2021 18:54:07 -0400 Subject: [PATCH 024/394] Remove dead code and useless debug --- src/zm_monitor.cpp | 15 +-------------- src/zmc.cpp | 3 --- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index bc6ac8453..bc5139db9 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -2463,21 +2463,14 @@ std::vector> Monitor::LoadFfmpegMonitors(const char *fi int Monitor::Capture() { unsigned int index = image_count % image_buffer_count; - Debug(1, "Packeet"); std::shared_ptr packet = std::make_shared(); - //= new ZMPacket(); - //packet->timestamp = new struct timeval; packet->image_index = image_count; - Debug(1, "Packeet"); gettimeofday(&(packet->timestamp), nullptr); - Debug(1, "Packeet"); shared_data->zmc_heartbeat_time = packet->timestamp.tv_sec; - Debug(1, "Capturing"); int captureResult = camera->Capture(packet); Debug(4, "Back from capture result=%d image count %d", captureResult, image_count); if (captureResult < 0) { - Debug(2, "failed capture"); // Unable to capture image // Fake a signal loss image // Not sure what to do here. We will close monitor and kill analysis_thread but what about rtsp server? @@ -2493,7 +2486,6 @@ int Monitor::Capture() { shared_timestamps[index] = packet->timestamp; delete capture_image; image_count++; - //delete packet; // What about timestamping it? // Don't want to do analysis on it, but we won't due to signal return -1; @@ -2535,27 +2527,22 @@ int Monitor::Capture() { packetqueue.queuePacket(packet); } else { Debug(4, "Not Queueing audio packet"); - //delete packet; } // Don't update last_write_index because that is used for live streaming //shared_data->last_write_time = image_buffer[index].timestamp->tv_sec; return 1; } else { Debug(1, "Unknown codec type %d", packet->codec_type); - //delete packet; return 1; } // end if audio image_count++; // Will only be queued if there are iterators allocated in the queue. - if ( !packetqueue.queuePacket(packet) ) { - //delete packet; - } + packetqueue.queuePacket(packet); UpdateCaptureFPS(); } else { // result == 0 // Question is, do we update last_write_index etc? - //delete packet; return 0; } // end if result diff --git a/src/zmc.cpp b/src/zmc.cpp index 0ae934f75..7ba91aa30 100644 --- a/src/zmc.cpp +++ b/src/zmc.cpp @@ -301,7 +301,6 @@ int main(int argc, char *argv[]) { result = -1; break; } - Debug(1, "Capture:"); if (monitors[i]->Capture() < 0) { Error("Failed to capture image from monitor %d %s (%zu/%zu)", monitors[i]->Id(), monitors[i]->Name(), i + 1, monitors.size()); @@ -370,8 +369,6 @@ int main(int argc, char *argv[]) { } // end if zm_reload } // end while ! zm_terminate outer connection loop - Debug(1,"Updating Monitor status"); - for (std::shared_ptr &monitor : monitors) { static char sql[ZM_SQL_SML_BUFSIZ]; snprintf(sql, sizeof(sql), From 70946921bc26fc2db60cf6b793988746c832478b Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 13 May 2021 08:54:55 -0400 Subject: [PATCH 025/394] fix code to release lock before notifying --- src/zm_packetqueue.cpp | 65 +++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 35 deletions(-) diff --git a/src/zm_packetqueue.cpp b/src/zm_packetqueue.cpp index 153c126bc..f73ade20d 100644 --- a/src/zm_packetqueue.cpp +++ b/src/zm_packetqueue.cpp @@ -367,48 +367,43 @@ ZMLockedPacket *PacketQueue::get_packet(packetqueue_iterator *it) { Debug(4, "Locking in get_packet using it %p queue end? %d", std::addressof(*it), (*it == pktQueue.end())); - std::unique_lock lck(mutex); - Debug(4, "Have Lock in get_packet"); ZMLockedPacket *lp = nullptr; - while (!lp) { - while (*it == pktQueue.end()) { - if (deleting or zm_terminate) { - Debug(1, "terminated, leaving"); - condition.notify_all(); + { // scope for lock + std::unique_lock lck(mutex); + Debug(4, "Have Lock in get_packet"); + while (!lp) { + while (*it == pktQueue.end() and !(deleting or zm_terminate)) { + Debug(2, "waiting. Queue size %zu it == end? %d", pktQueue.size(), (*it == pktQueue.end())); + condition.wait(lck); + } + if (deleting or zm_terminate) break; + + std::shared_ptr p = *(*it); + if (!p) { + Error("Null p?!"); return nullptr; } + Debug(3, "get_packet using it %p locking index %d", + std::addressof(*it), p->image_index); + + lp = new ZMLockedPacket(p); + if (lp->trylock()) { + Debug(2, "Locked packet %d, unlocking packetqueue mutex", p->image_index); + return lp; + } + delete lp; + lp = nullptr; Debug(2, "waiting. Queue size %zu it == end? %d", pktQueue.size(), (*it == pktQueue.end())); condition.wait(lck); - } - if (deleting or zm_terminate) { - Debug(1, "terminated, leaving"); - condition.notify_all(); - return nullptr; - } + } // end while !lp + } // end scope for lock - std::shared_ptr p = *(*it); - if (!p) { - Error("Null p?!"); - return nullptr; - } - Debug(4, "get_packet using it %p locking index %d", - std::addressof(*it), p->image_index); - // Packets are only deleted by packetqueue, so lock must be held. - // We shouldn't have to trylock. Someone else might hold the lock but not for long - - lp = new ZMLockedPacket(p); - if (lp->trylock()) { - Debug(2, "Locked packet %d, unlocking packetqueue mutex", p->image_index); - - return lp; - } - delete lp; - lp = nullptr; - Debug(2, "waiting. Queue size %zu it == end? %d", pktQueue.size(), (*it == pktQueue.end())); - condition.wait(lck); - } // end while !lp - return nullptr; + if (!lp) { + Debug(1, "terminated, leaving"); + condition.notify_all(); + } + return lp; } // end ZMLockedPacket *PacketQueue::get_packet(it) void PacketQueue::unlock(ZMLockedPacket *lp) { From d52b53e69032ba8c9a934ecda4071b54414adc7c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 13 May 2021 09:31:59 -0400 Subject: [PATCH 026/394] Add further bounds checks when accessing image_buffer --- src/zm_monitor.cpp | 58 ++++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index bc5139db9..2d7a84ec2 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1163,39 +1163,49 @@ void Monitor::AddPrivacyBitmask() { int Monitor::GetImage(int32_t index, int scale) { if (index < 0 || index > image_buffer_count) { + Warning("Invalid index %d passed. image_buffer_count = %d", index, image_buffer_count); index = shared_data->last_write_index; } - if ( index != image_buffer_count ) { - Image *image; - // If we are going to be modifying the snapshot before writing, then we need to copy it - if ( ( scale != ZM_SCALE_BASE ) || ( !config.timestamp_on_capture ) ) { - alarm_image.Assign(*image_buffer[index]); + if (!image_buffer.size() or static_cast(index) >= image_buffer.size()) { + Error("Image Buffer has not been allocated"); + return -1; + } + if ( index == image_buffer_count ) { + Error("Unable to generate image, no images in buffer"); + return 0; + } - if (scale != ZM_SCALE_BASE) { - alarm_image.Scale(scale); - } + Image *image; + // If we are going to be modifying the snapshot before writing, then we need to copy it + if ((scale != ZM_SCALE_BASE) || (!config.timestamp_on_capture)) { + alarm_image.Assign(*image_buffer[index]); - if ( !config.timestamp_on_capture ) { - TimestampImage(&alarm_image, shared_timestamps[index]); - } - image = &alarm_image; - } else { - image = image_buffer[index]; + if (scale != ZM_SCALE_BASE) { + alarm_image.Scale(scale); } - static char filename[PATH_MAX]; - snprintf(filename, sizeof(filename), "Monitor%u.jpg", id); - image->WriteJpeg(filename); + if (!config.timestamp_on_capture) { + TimestampImage(&alarm_image, shared_timestamps[index]); + } + image = &alarm_image; } else { - Error("Unable to generate image, no images in buffer"); + image = image_buffer[index]; } - return 0; + + static char filename[PATH_MAX]; + snprintf(filename, sizeof(filename), "Monitor%u.jpg", id); + image->WriteJpeg(filename); + return 1; } ZMPacket *Monitor::getSnapshot(int index) const { if ((index < 0) || (index >= image_buffer_count)) { index = shared_data->last_write_index; } + if (!image_buffer.size() or static_cast(index) >= image_buffer.size()) { + Error("Image Buffer has not been allocated"); + return nullptr; + } if (index != image_buffer_count) { return new ZMPacket(image_buffer[index], shared_timestamps[index]); } else { @@ -2462,6 +2472,10 @@ std::vector> Monitor::LoadFfmpegMonitors(const char *fi */ int Monitor::Capture() { unsigned int index = image_count % image_buffer_count; + if (!image_buffer.size() or index >= image_buffer.size()) { + Error("Image Buffer is invalid. Check ImageBufferCount"); + return -1; + } std::shared_ptr packet = std::make_shared(); packet->image_index = image_count; @@ -2663,9 +2677,9 @@ bool Monitor::Decode() { } } - if ( orientation != ROTATE_0 ) { + if (orientation != ROTATE_0) { Debug(2, "Doing rotation"); - switch ( orientation ) { + switch (orientation) { case ROTATE_0 : // No action required break; @@ -2703,7 +2717,7 @@ bool Monitor::Decode() { } // end bool Monitor::Decode() void Monitor::TimestampImage(Image *ts_image, const timeval &ts_time) const { - if ( !label_format[0] ) + if (!label_format[0]) return; // Expand the strftime macros first From a56b4053fa4b7197a0214f14bbeb912bf806021f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 13 May 2021 14:24:03 -0400 Subject: [PATCH 027/394] Use resize in stead of reserve so that image_buffer.size actually has a value --- src/zm_monitor.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 2d7a84ec2..8c8ea5943 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -995,12 +995,12 @@ bool Monitor::connect() { } if (!camera) LoadCamera(); - Debug(3, "Allocating %d image buffers", image_buffer_count); - image_buffer.reserve(image_buffer_count); + image_buffer.resize(image_buffer_count); for (int32_t i = 0; i < image_buffer_count; i++) { image_buffer[i] = new Image(width, height, camera->Colours(), camera->SubpixelOrder(), &(shared_images[i*camera->ImageSize()])); image_buffer[i]->HoldBuffer(true); /* Don't release the internal buffer or replace it with another */ } + Debug(3, "Allocated %zu %zu image buffers", image_buffer.capacity(), image_buffer.size()); if (purpose == CAPTURE) { memset(mem_ptr, 0, mem_size); @@ -2472,8 +2472,8 @@ std::vector> Monitor::LoadFfmpegMonitors(const char *fi */ int Monitor::Capture() { unsigned int index = image_count % image_buffer_count; - if (!image_buffer.size() or index >= image_buffer.size()) { - Error("Image Buffer is invalid. Check ImageBufferCount"); + if (image_buffer.empty() or (index >= image_buffer.size())) { + Error("Image Buffer is invalid. Check ImageBufferCount. size is %zu", image_buffer.size()); return -1; } From b17edf3153c5036e6e93a57b5e73c307edfc5c3d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 13 May 2021 19:28:32 -0400 Subject: [PATCH 028/394] Bump version to 1.36.0 --- distros/redhat/zoneminder.spec | 2 +- version | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index 8c0773911..bb95723b7 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -31,7 +31,7 @@ %global _hardened_build 1 Name: zoneminder -Version: 1.35.28 +Version: 1.36.0 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons diff --git a/version b/version index b538e3240..39fc130ef 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.35.28 +1.36.0 From 2afbbb17d2ba2dde81ded4fac905f37e434a6993 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 14 May 2021 10:41:36 -0400 Subject: [PATCH 029/394] Must upload source package. Get rid of -sd --- utils/do_debian_package.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/do_debian_package.sh b/utils/do_debian_package.sh index 289158c77..26c26aa31 100755 --- a/utils/do_debian_package.sh +++ b/utils/do_debian_package.sh @@ -300,7 +300,7 @@ EOF DEBUILD="debuild -i -us -uc -b" else # Source build, don't need build depends. - DEBUILD="debuild -S -sa -sd" + DEBUILD="debuild -S -sa" fi; fi; if [ "$DEBSIGN_KEYID" != "" ]; then From 3fc3a8128667e24fd40ea80a5b9dcca3ae6d419b Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Sun, 2 May 2021 23:28:05 +0200 Subject: [PATCH 030/394] Box+Coord: Remove empty cpp files --- src/CMakeLists.txt | 2 -- src/zm_box.cpp | 22 ---------------------- src/zm_coord.cpp | 22 ---------------------- 3 files changed, 46 deletions(-) delete mode 100644 src/zm_box.cpp delete mode 100644 src/zm_coord.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 281c7ba34..fc01af04d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,12 +6,10 @@ configure_file(zm_config_data.h.in "${CMAKE_BINARY_DIR}/zm_config_data.h" @ONLY) # Group together all the source files that are used by all the binaries (zmc, zmu, zms etc) set(ZM_BIN_SRC_FILES zm_analysis_thread.cpp - zm_box.cpp zm_buffer.cpp zm_camera.cpp zm_comms.cpp zm_config.cpp - zm_coord.cpp zm_curl_camera.cpp zm_crypt.cpp zm.cpp diff --git a/src/zm_box.cpp b/src/zm_box.cpp deleted file mode 100644 index 6129b7a29..000000000 --- a/src/zm_box.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// -// ZoneMinder Box Class Implementation, $Date$, $Revision$ -// Copyright (C) 2001-2008 Philip Coombes -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// - -#include "zm_box.h" - -// This section deliberately left blank diff --git a/src/zm_coord.cpp b/src/zm_coord.cpp deleted file mode 100644 index 0b7ab0e71..000000000 --- a/src/zm_coord.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// -// ZoneMinder Coordinate Class Implementation, $Date$, $Revision$ -// Copyright (C) 2001-2008 Philip Coombes -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// - -#include "zm_coord.h" - -// This section deliberately left blank From 60db1c2eaf27ba8b88e544d69610f8448c817c60 Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Fri, 7 May 2021 09:03:24 +0200 Subject: [PATCH 031/394] Coord: Rename to Vector2 The class is not only used to represent coordinates but also lengths in XY. Vector2 is a more fitting/general name for this purpose. --- src/zm_box.h | 41 ++++++++++---------- src/zm_coord.h | 64 ------------------------------- src/zm_image.cpp | 22 +++++------ src/zm_image.h | 8 ++-- src/zm_monitor.cpp | 6 +-- src/zm_monitor.h | 2 +- src/zm_poly.cpp | 55 ++++++++++++++------------- src/zm_poly.h | 12 +++--- src/zm_vector2.h | 92 +++++++++++++++++++++++++++++++++++++++++++++ src/zm_zone.cpp | 12 +++--- src/zm_zone.h | 14 +++---- src/zm_zone_stats.h | 4 +- 12 files changed, 180 insertions(+), 152 deletions(-) delete mode 100644 src/zm_coord.h create mode 100644 src/zm_vector2.h diff --git a/src/zm_box.h b/src/zm_box.h index 20e30bcd1..4ab4e1270 100644 --- a/src/zm_box.h +++ b/src/zm_box.h @@ -20,7 +20,7 @@ #ifndef ZM_BOX_H #define ZM_BOX_H -#include "zm_coord.h" +#include "zm_vector2.h" #include // @@ -28,40 +28,39 @@ // defined by two coordinates // class Box { -private: - Coord lo, hi; - Coord size; + private: + Vector2 lo, hi; + Vector2 size; -public: - inline Box() : lo(0,0), hi(0,0), size(0,0) { } - explicit inline Box(unsigned int p_size) : lo(0, 0), hi(p_size-1, p_size-1), size(Coord::Range(hi, lo)) { } - inline Box( int p_x_size, int p_y_size ) : lo( 0, 0 ), hi ( p_x_size-1, p_y_size-1 ), size( Coord::Range( hi, lo ) ) { } - inline Box( int lo_x, int lo_y, int hi_x, int hi_y ) : lo( lo_x, lo_y ), hi( hi_x, hi_y ), size( Coord::Range( hi, lo ) ) { } - inline Box( const Coord &p_lo, const Coord &p_hi ) : lo( p_lo ), hi( p_hi ), size( Coord::Range( hi, lo ) ) { } + public: + inline Box() : lo(0, 0), hi(0, 0), size(0, 0) {} + explicit inline Box(unsigned int p_size) : lo(0, 0), hi(p_size - 1, p_size - 1), size(Vector2::Range(hi, lo)) {} + inline Box(int p_x_size, int p_y_size) : lo(0, 0), hi(p_x_size - 1, p_y_size - 1), size(Vector2::Range(hi, lo)) {} + inline Box(int lo_x, int lo_y, int hi_x, int hi_y) : lo(lo_x, lo_y), hi(hi_x, hi_y), size(Vector2::Range(hi, lo)) {} + inline Box(const Vector2 &p_lo, const Vector2 &p_hi) : lo(p_lo), hi(p_hi), size(Vector2::Range(hi, lo)) {} - inline const Coord &Lo() const { return lo; } + inline const Vector2 &Lo() const { return lo; } inline int LoX() const { return lo.X(); } inline int LoX(int p_lo_x) { return lo.X(p_lo_x); } inline int LoY() const { return lo.Y(); } inline int LoY(int p_lo_y) { return lo.Y(p_lo_y); } - inline const Coord &Hi() const { return hi; } + inline const Vector2 &Hi() const { return hi; } inline int HiX() const { return hi.X(); } inline int HiX(int p_hi_x) { return hi.X(p_hi_x); } inline int HiY() const { return hi.Y(); } inline int HiY(int p_hi_y) { return hi.Y(p_hi_y); } - inline const Coord &Size() const { return size; } + inline const Vector2 &Size() const { return size; } inline int Width() const { return size.X(); } inline int Height() const { return size.Y(); } - inline int Area() const { return size.X()*size.Y(); } + inline int Area() const { return size.X() * size.Y(); } - inline const Coord Centre() const { - int mid_x = int(std::round(lo.X()+(size.X()/2.0))); - int mid_y = int(std::round(lo.Y()+(size.Y()/2.0))); - return Coord( mid_x, mid_y ); + inline const Vector2 Centre() const { + int mid_x = int(std::round(lo.X() + (size.X() / 2.0))); + int mid_y = int(std::round(lo.Y() + (size.Y() / 2.0))); + return Vector2(mid_x, mid_y); } - inline bool Inside( const Coord &coord ) const - { - return( coord.X() >= lo.X() && coord.X() <= hi.X() && coord.Y() >= lo.Y() && coord.Y() <= hi.Y() ); + inline bool Inside(const Vector2 &coord) const { + return (coord.X() >= lo.X() && coord.X() <= hi.X() && coord.Y() >= lo.Y() && coord.Y() <= hi.Y()); } }; diff --git a/src/zm_coord.h b/src/zm_coord.h deleted file mode 100644 index b7e8fd046..000000000 --- a/src/zm_coord.h +++ /dev/null @@ -1,64 +0,0 @@ -// -// ZoneMinder Coordinate Class Interface, $Date$, $Revision$ -// Copyright (C) 2001-2008 Philip Coombes -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// - -#ifndef ZM_COORD_H -#define ZM_COORD_H - -#include "zm_define.h" - -// -// Class used for storing an x,y pair, i.e. a coordinate -// -class Coord { -private: - int x, y; - -public: - inline Coord() : x(0), y(0) { } - inline Coord( int p_x, int p_y ) : x(p_x), y(p_y) { } - inline Coord( const Coord &p_coord ) : x(p_coord.x), y(p_coord.y) { } - inline Coord &operator =( const Coord &coord ) { - x = coord.x; - y = coord.y; - return *this; - } - inline int &X(int p_x) { x=p_x; return x; } - inline const int &X() const { return x; } - inline int &Y(int p_y) { y=p_y; return y; } - inline const int &Y() const { return y; } - - inline static Coord Range( const Coord &coord1, const Coord &coord2 ) { - Coord result( (coord1.x-coord2.x)+1, (coord1.y-coord2.y)+1 ); - return result; - } - - inline bool operator==( const Coord &coord ) const { return( x == coord.x && y == coord.y ); } - inline bool operator!=( const Coord &coord ) const { return( x != coord.x || y != coord.y ); } - inline bool operator>( const Coord &coord ) const { return( x > coord.x && y > coord.y ); } - inline bool operator>=( const Coord &coord ) const { return( !(operator<(coord)) ); } - inline bool operator<( const Coord &coord ) const { return( x < coord.x && y < coord.y ); } - inline bool operator<=( const Coord &coord ) const { return( !(operator>(coord)) ); } - inline Coord &operator+=( const Coord &coord ) { x += coord.x; y += coord.y; return( *this ); } - inline Coord &operator-=( const Coord &coord ) { x -= coord.x; y -= coord.y; return( *this ); } - - inline friend Coord operator+( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result += coord2; return( result ); } - inline friend Coord operator-( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result -= coord2; return( result ); } -}; - -#endif // ZM_COORD_H diff --git a/src/zm_image.cpp b/src/zm_image.cpp index 909b4db15..c3d501235 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -250,7 +250,7 @@ int Image::PopulateFrame(AVFrame *frame) { width, height, linesize, colours, size, av_get_pix_fmt_name(imagePixFormat) ); - AVBufferRef *ref = av_buffer_create(buffer, size, + AVBufferRef *ref = av_buffer_create(buffer, size, dont_free, /* Free callback */ nullptr, /* opaque */ 0 /* flags */ @@ -576,7 +576,7 @@ void Image::Initialise() { if ( res == FontLoadError::kFileNotFound ) { Panic("Invalid font location: %s", config.font_file_location); } else if ( res == FontLoadError::kInvalidFile ) { - Panic("Invalid font file."); + Panic("Invalid font file."); } initialised = true; } @@ -781,7 +781,7 @@ void Image::Assign(const Image &image) { return; } } else { - if ( new_size > allocation || !buffer ) { + if (new_size > allocation || !buffer) { // DumpImgBuffer(); This is also done in AllocImgBuffer AllocImgBuffer(new_size); } @@ -1932,7 +1932,7 @@ void Image::Delta(const Image &image, Image* targetimage) const { #endif } -const Coord Image::centreCoord( const char *text, int size=1 ) const { +const Vector2 Image::centreCoord(const char *text, int size = 1) const { int index = 0; int line_no = 0; int text_len = strlen(text); @@ -1957,7 +1957,7 @@ const Coord Image::centreCoord( const char *text, int size=1 ) const { uint16_t char_height = font_variant.GetCharHeight(); int x = (width - (max_line_len * char_width )) / 2; int y = (height - (line_no * char_height) ) / 2; - return Coord(x, y); + return {x, y}; } /* RGB32 compatible: complete */ @@ -2007,7 +2007,7 @@ https://lemire.me/blog/2018/02/21/iterating-over-set-bits-quickly/ */ void Image::Annotate( const std::string &text, - const Coord &coord, + const Vector2 &coord, const uint8 size, const Rgb fg_colour, const Rgb bg_colour) { @@ -2127,7 +2127,7 @@ void Image::Annotate( } } -void Image::Timestamp( const char *label, const time_t when, const Coord &coord, const int size ) { +void Image::Timestamp(const char *label, const time_t when, const Vector2 &coord, const int size) { char time_text[64]; tm when_tm = {}; strftime(time_text, sizeof(time_text), "%y/%m/%d %H:%M:%S", localtime_r(&when, &when_tm)); @@ -2386,8 +2386,8 @@ void Image::Outline( Rgb colour, const Polygon &polygon ) { int n_coords = polygon.getNumCoords(); for ( int j = 0, i = n_coords-1; j < n_coords; i = j++ ) { - const Coord &p1 = polygon.getCoord( i ); - const Coord &p2 = polygon.getCoord( j ); + const Vector2 &p1 = polygon.getCoord(i); + const Vector2 &p2 = polygon.getCoord(j); int x1 = p1.X(); int x2 = p2.X(); @@ -2470,8 +2470,8 @@ void Image::Fill(Rgb colour, int density, const Polygon &polygon) { int n_global_edges = 0; Edge global_edges[n_coords]; for ( int j = 0, i = n_coords-1; j < n_coords; i = j++ ) { - const Coord &p1 = polygon.getCoord(i); - const Coord &p2 = polygon.getCoord(j); + const Vector2 &p1 = polygon.getCoord(i); + const Vector2 &p2 = polygon.getCoord(j); int x1 = p1.X(); int x2 = p2.X(); diff --git a/src/zm_image.h b/src/zm_image.h index 671cc0487..0bf72de89 100644 --- a/src/zm_image.h +++ b/src/zm_image.h @@ -20,12 +20,12 @@ #ifndef ZM_IMAGE_H #define ZM_IMAGE_H -#include "zm_coord.h" #include "zm_ffmpeg.h" #include "zm_jpeg.h" #include "zm_logger.h" #include "zm_mem_utils.h" #include "zm_rgb.h" +#include "zm_vector2.h" #if HAVE_ZLIB_H #include @@ -280,16 +280,16 @@ class Image { //Image *Delta( const Image &image ) const; void Delta( const Image &image, Image* targetimage) const; - const Coord centreCoord(const char *text, const int size) const; + const Vector2 centreCoord(const char *text, const int size) const; void MaskPrivacy( const unsigned char *p_bitmask, const Rgb pixel_colour=0x00222222 ); void Annotate(const std::string &text, - const Coord &coord, + const Vector2 &coord, uint8 size = 1, Rgb fg_colour = kRGBWhite, Rgb bg_colour = kRGBBlack); Image *HighlightEdges( Rgb colour, unsigned int p_colours, unsigned int p_subpixelorder, const Box *limits=0 ); //Image *HighlightEdges( Rgb colour, const Polygon &polygon ); - void Timestamp( const char *label, const time_t when, const Coord &coord, const int size ); + void Timestamp(const char *label, const time_t when, const Vector2 &coord, const int size); void Colourise(const unsigned int p_reqcolours, const unsigned int p_reqsubpixelorder); void DeColourise(); diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 8c8ea5943..dcdecf5aa 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -327,7 +327,7 @@ Monitor::Monitor() record_audio(0), //event_prefix //label_format - label_coord(Coord(0,0)), + label_coord(Vector2(0,0)), label_size(0), image_buffer_count(0), max_image_buffer_count(0), @@ -561,7 +561,7 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) { /* "EventPrefix, LabelFormat, LabelX, LabelY, LabelSize," */ event_prefix = dbrow[col] ? dbrow[col] : ""; col++; label_format = dbrow[col] ? ReplaceAll(dbrow[col], "\\n", "\n") : ""; col++; - label_coord = Coord(atoi(dbrow[col]), atoi(dbrow[col+1])); col += 2; + label_coord = Vector2(atoi(dbrow[col]), atoi(dbrow[col + 1])); col += 2; label_size = atoi(dbrow[col]); col++; /* "ImageBufferCount, `MaxImageBufferCount`, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, " */ @@ -2832,7 +2832,7 @@ unsigned int Monitor::DetectMotion(const Image &comp_image, Event::StringSet &zo } // end if CheckAlarms } // end foreach zone - Coord alarm_centre; + Vector2 alarm_centre; int top_score = -1; if (alarm) { diff --git a/src/zm_monitor.h b/src/zm_monitor.h index c0603a19a..aaf6e528c 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -305,7 +305,7 @@ protected: std::string event_prefix; // The prefix applied to event names as they are created std::string label_format; // The format of the timestamp on the images - Coord label_coord; // The coordinates of the timestamp on the images + Vector2 label_coord; // The coordinates of the timestamp on the images int label_size; // Size of the timestamp on the images int32_t image_buffer_count; // Size of circular image buffer, kept in /dev/shm int32_t max_image_buffer_count; // Max # of video packets to keep in packet queue diff --git a/src/zm_poly.cpp b/src/zm_poly.cpp index 94f153b98..e132e1bef 100644 --- a/src/zm_poly.cpp +++ b/src/zm_poly.cpp @@ -23,58 +23,59 @@ void Polygon::calcArea() { double float_area = 0.0L; - for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ ) { - double trap_area = ((coords[i].X()-coords[j].X())*((coords[i].Y()+coords[j].Y())))/2.0L; + for (int i = 0, j = n_coords - 1; i < n_coords; j = i++) { + double trap_area = ((coords[i].X() - coords[j].X()) * ((coords[i].Y() + coords[j].Y()))) / 2.0L; float_area += trap_area; //printf( "%.2f (%.2f)\n", float_area, trap_area ); } - area = (int)round(fabs(float_area)); + area = (int) round(fabs(float_area)); } void Polygon::calcCentre() { - if ( !area && n_coords ) + if (!area && n_coords) calcArea(); double float_x = 0.0L, float_y = 0.0L; - for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ ) { - float_x += ((coords[i].Y()-coords[j].Y())*((coords[i].X()*2)+(coords[i].X()*coords[j].X())+(coords[j].X()*2))); - float_y += ((coords[j].X()-coords[i].X())*((coords[i].Y()*2)+(coords[i].Y()*coords[j].Y())+(coords[j].Y()*2))); + for (int i = 0, j = n_coords - 1; i < n_coords; j = i++) { + float_x += ((coords[i].Y() - coords[j].Y()) + * ((coords[i].X() * 2) + (coords[i].X() * coords[j].X()) + (coords[j].X() * 2))); + float_y += ((coords[j].X() - coords[i].X()) + * ((coords[i].Y() * 2) + (coords[i].Y() * coords[j].Y()) + (coords[j].Y() * 2))); } - float_x /= (6*area); - float_y /= (6*area); - centre = Coord( (int)round(float_x), (int)round(float_y) ); + float_x /= (6 * area); + float_y /= (6 * area); + centre = Vector2((int) round(float_x), (int) round(float_y)); } -Polygon::Polygon(int p_n_coords, const Coord *p_coords) : n_coords(p_n_coords) { - coords = new Coord[n_coords]; +Polygon::Polygon(int p_n_coords, const Vector2 *p_coords) : n_coords(p_n_coords) { + coords = new Vector2[n_coords]; int min_x = -1; int max_x = -1; int min_y = -1; int max_y = -1; - for ( int i = 0; i < n_coords; i++ ) { + for (int i = 0; i < n_coords; i++) { coords[i] = p_coords[i]; - if ( min_x == -1 || coords[i].X() < min_x ) + if (min_x == -1 || coords[i].X() < min_x) min_x = coords[i].X(); - if ( max_x == -1 || coords[i].X() > max_x ) + if (max_x == -1 || coords[i].X() > max_x) max_x = coords[i].X(); - if ( min_y == -1 || coords[i].Y() < min_y ) + if (min_y == -1 || coords[i].Y() < min_y) min_y = coords[i].Y(); - if ( max_y == -1 || coords[i].Y() > max_y ) + if (max_y == -1 || coords[i].Y() > max_y) max_y = coords[i].Y(); } - extent = Box( min_x, min_y, max_x, max_y ); + extent = Box(min_x, min_y, max_x, max_y); calcArea(); calcCentre(); } Polygon::Polygon(const Polygon &p_polygon) : - n_coords(p_polygon.n_coords), - extent(p_polygon.extent), - area(p_polygon.area), - centre(p_polygon.centre) -{ - coords = new Coord[n_coords]; - for( int i = 0; i < n_coords; i++ ) { + n_coords(p_polygon.n_coords), + extent(p_polygon.extent), + area(p_polygon.area), + centre(p_polygon.centre) { + coords = new Vector2[n_coords]; + for (int i = 0; i < n_coords; i++) { coords[i] = p_polygon.coords[i]; } } @@ -82,7 +83,7 @@ Polygon::Polygon(const Polygon &p_polygon) : Polygon &Polygon::operator=(const Polygon &p_polygon) { n_coords = p_polygon.n_coords; - Coord *new_coords = new Coord[n_coords]; + Vector2 *new_coords = new Vector2[n_coords]; for (int i = 0; i < n_coords; i++) { new_coords[i] = p_polygon.coords[i]; } @@ -95,7 +96,7 @@ Polygon &Polygon::operator=(const Polygon &p_polygon) { return *this; } -bool Polygon::isInside( const Coord &coord ) const { +bool Polygon::isInside(const Vector2 &coord) const { bool inside = false; for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ ) { if ( (((coords[i].Y() <= coord.Y()) && (coord.Y() < coords[j].Y()) ) diff --git a/src/zm_poly.h b/src/zm_poly.h index 21b7f14bb..1c924cd9e 100644 --- a/src/zm_poly.h +++ b/src/zm_poly.h @@ -68,10 +68,10 @@ protected: protected: int n_coords; - Coord *coords; + Vector2 *coords; Box extent; int area; - Coord centre; + Vector2 centre; protected: void initialiseEdges(); @@ -81,7 +81,7 @@ protected: public: inline Polygon() : n_coords(0), coords(nullptr), area(0) { } - Polygon(int p_n_coords, const Coord *p_coords); + Polygon(int p_n_coords, const Vector2 *p_coords); Polygon(const Polygon &p_polygon); ~Polygon() { delete[] coords; @@ -90,7 +90,7 @@ public: Polygon &operator=( const Polygon &p_polygon ); inline int getNumCoords() const { return n_coords; } - inline const Coord &getCoord( int index ) const { + inline const Vector2 &getCoord( int index ) const { return coords[index]; } @@ -107,10 +107,10 @@ public: inline int Height() const { return extent.Height(); } inline int Area() const { return area; } - inline const Coord &Centre() const { + inline const Vector2 &Centre() const { return centre; } - bool isInside( const Coord &coord ) const; + bool isInside(const Vector2 &coord) const; }; #endif // ZM_POLY_H diff --git a/src/zm_vector2.h b/src/zm_vector2.h new file mode 100644 index 000000000..2254b0f6a --- /dev/null +++ b/src/zm_vector2.h @@ -0,0 +1,92 @@ +// +// ZoneMinder Coordinate Class Interface, $Date$, $Revision$ +// Copyright (C) 2001-2008 Philip Coombes +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// + +#ifndef ZM_VECTOR2_H +#define ZM_VECTOR2_H + +#include "zm_define.h" + +// +// Class used for storing an x,y pair, i.e. a coordinate/vector +// +class Vector2 { + public: + inline Vector2() : x(0), y(0) {} + inline Vector2(int p_x, int p_y) : x(p_x), y(p_y) {} + inline Vector2(const Vector2 &p_coord) : x(p_coord.x), y(p_coord.y) {} + + inline Vector2 &operator=(const Vector2 &coord) { + x = coord.x; + y = coord.y; + return *this; + } + + inline int &X(int p_x) { + x = p_x; + return x; + } + inline const int &X() const { return x; } + + inline int &Y(int p_y) { + y = p_y; + return y; + } + inline const int &Y() const { return y; } + + inline static Vector2 Range(const Vector2 &coord1, const Vector2 &coord2) { + Vector2 result((coord1.x - coord2.x) + 1, (coord1.y - coord2.y) + 1); + return result; + } + + inline bool operator==(const Vector2 &coord) const { return (x == coord.x && y == coord.y); } + inline bool operator!=(const Vector2 &coord) const { return (x != coord.x || y != coord.y); } + + inline bool operator>(const Vector2 &coord) const { return (x > coord.x && y > coord.y); } + inline bool operator>=(const Vector2 &coord) const { return (!(operator<(coord))); } + inline bool operator<(const Vector2 &coord) const { return (x < coord.x && y < coord.y); } + inline bool operator<=(const Vector2 &coord) const { return (!(operator>(coord))); } + + inline Vector2 &operator+=(const Vector2 &coord) { + x += coord.x; + y += coord.y; + return *this; + } + inline Vector2 &operator-=(const Vector2 &coord) { + x -= coord.x; + y -= coord.y; + return *this; + } + + inline friend Vector2 operator+(const Vector2 &coord1, const Vector2 &coord2) { + Vector2 result(coord1); + result += coord2; + return result; + } + inline friend Vector2 operator-(const Vector2 &coord1, const Vector2 &coord2) { + Vector2 result(coord1); + result -= coord2; + return result; + } + + private: + int x; + int y; +}; + +#endif // ZM_VECTOR2_H diff --git a/src/zm_zone.cpp b/src/zm_zone.cpp index 7cf2252a8..d48d087fa 100644 --- a/src/zm_zone.cpp +++ b/src/zm_zone.cpp @@ -32,7 +32,7 @@ void Zone::Setup( int p_max_pixel_threshold, int p_min_alarm_pixels, int p_max_alarm_pixels, - const Coord &p_filter_box, + const Vector2 &p_filter_box, int p_min_filter_pixels, int p_max_filter_pixels, int p_min_blob_pixels, @@ -684,11 +684,11 @@ bool Zone::CheckAlarms(const Image *delta_image) { // Now outline the changed region if (stats.score_) { - stats.alarm_box_ = Box(Coord(alarm_lo_x, alarm_lo_y), Coord(alarm_hi_x, alarm_hi_y)); + stats.alarm_box_ = Box(Vector2(alarm_lo_x, alarm_lo_y), Vector2(alarm_hi_x, alarm_hi_y)); //if ( monitor->followMotion() ) if ( true ) { - stats.alarm_centre_ = Coord(alarm_mid_x, alarm_mid_y); + stats.alarm_centre_ = Vector2(alarm_mid_x, alarm_mid_y); } else { stats.alarm_centre_ = stats.alarm_box_.Centre(); } @@ -750,7 +750,7 @@ bool Zone::ParsePolygonString(const char *poly_string, Polygon &polygon) { char *str = (char *)poly_string; int n_coords = 0; int max_n_coords = strlen(str)/4; - Coord *coords = new Coord[max_n_coords]; + Vector2 *coords = new Vector2[max_n_coords]; while (*str != '\0') { char *cp = strchr(str, ','); if (!cp) { @@ -760,7 +760,7 @@ bool Zone::ParsePolygonString(const char *poly_string, Polygon &polygon) { int x = atoi(str); int y = atoi(cp+1); Debug(3, "Got coordinate %d,%d from polygon string", x, y); - coords[n_coords++] = Coord(x, y); + coords[n_coords++] = Vector2(x, y); char *ws = strchr(cp+2, ' '); if (ws) @@ -899,7 +899,7 @@ std::vector Zone::Load(Monitor *monitor) { zones.emplace_back( monitor, Id, Name, Type, polygon, AlarmRGB, CheckMethod, MinPixelThreshold, MaxPixelThreshold, - MinAlarmPixels, MaxAlarmPixels, Coord(FilterX, FilterY), + MinAlarmPixels, MaxAlarmPixels, Vector2(FilterX, FilterY), MinFilterPixels, MaxFilterPixels, MinBlobPixels, MaxBlobPixels, MinBlobs, MaxBlobs, OverloadFrames, ExtendAlarmFrames); diff --git a/src/zm_zone.h b/src/zm_zone.h index 30f7e29eb..741b61ae2 100644 --- a/src/zm_zone.h +++ b/src/zm_zone.h @@ -21,12 +21,12 @@ #define ZM_ZONE_H #include "zm_box.h" -#include "zm_coord.h" #include "zm_define.h" #include "zm_config.h" #include "zm_poly.h" #include "zm_rgb.h" #include "zm_zone_stats.h" +#include "zm_vector2.h" #include #include @@ -77,7 +77,7 @@ class Zone { int min_alarm_pixels; int max_alarm_pixels; - Coord filter_box; + Vector2 filter_box; int min_filter_pixels; int max_filter_pixels; @@ -112,7 +112,7 @@ class Zone { int p_max_pixel_threshold, int p_min_alarm_pixels, int p_max_alarm_pixels, - const Coord &p_filter_box, + const Vector2 &p_filter_box, int p_min_filter_pixels, int p_max_filter_pixels, int p_min_blob_pixels, @@ -137,7 +137,7 @@ class Zone { int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, - const Coord &p_filter_box=Coord( 3, 3 ), + const Vector2 &p_filter_box = Vector2(3, 3), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, @@ -164,7 +164,7 @@ class Zone { blob_stats{}, stats(p_id) { - Setup(Zone::INACTIVE, p_polygon, kRGBBlack, (Zone::CheckMethod)0, 0, 0, 0, 0, Coord(0, 0), 0, 0, 0, 0, 0, 0, 0, 0); + Setup(Zone::INACTIVE, p_polygon, kRGBBlack, (Zone::CheckMethod)0, 0, 0, 0, 0, Vector2(0, 0), 0, 0, 0, 0, 0, 0, 0, 0); } Zone(Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon) : @@ -174,7 +174,7 @@ class Zone { blob_stats{}, stats(p_id) { - Setup(p_type, p_polygon, kRGBBlack, (Zone::CheckMethod)0, 0, 0, 0, 0, Coord( 0, 0 ), 0, 0, 0, 0, 0, 0, 0, 0 ); + Setup(p_type, p_polygon, kRGBBlack, (Zone::CheckMethod)0, 0, 0, 0, 0, Vector2(0, 0), 0, 0, 0, 0, 0, 0, 0, 0 ); } Zone(const Zone &z); @@ -195,7 +195,7 @@ class Zone { inline bool WasAlarmed() const { return was_alarmed; } inline void SetAlarm() { was_alarmed = alarmed; alarmed = true; } inline void ClearAlarm() { was_alarmed = alarmed; alarmed = false; } - inline Coord GetAlarmCentre() const { return stats.alarm_centre_; } + inline Vector2 GetAlarmCentre() const { return stats.alarm_centre_; } inline unsigned int Score() const { return stats.score_; } inline void ResetStats() { diff --git a/src/zm_zone_stats.h b/src/zm_zone_stats.h index f963a0eb4..e9cc37889 100644 --- a/src/zm_zone_stats.h +++ b/src/zm_zone_stats.h @@ -21,8 +21,8 @@ #define ZM_ZONE_STATS_H #include "zm_box.h" -#include "zm_coord.h" #include "zm_logger.h" +#include "zm_vector2.h" class ZoneStats { public: @@ -87,7 +87,7 @@ class ZoneStats { int min_blob_size_; int max_blob_size_; Box alarm_box_; - Coord alarm_centre_; + Vector2 alarm_centre_; unsigned int score_; }; From 707700e24ec9254a4e336b1fc14fa6811ae25ff0 Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Sat, 1 May 2021 16:13:00 +0200 Subject: [PATCH 032/394] Vector2: Add unit tests --- tests/CMakeLists.txt | 3 +- tests/zm_vector2.cpp | 87 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 tests/zm_vector2.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e0c68aeff..426f03766 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -15,7 +15,8 @@ set(TEST_SOURCES zm_comms.cpp zm_crypt.cpp zm_font.cpp - zm_utils.cpp) + zm_utils.cpp + zm_vector2.cpp) add_executable(tests main.cpp ${TEST_SOURCES}) diff --git a/tests/zm_vector2.cpp b/tests/zm_vector2.cpp new file mode 100644 index 000000000..32a0e3d6d --- /dev/null +++ b/tests/zm_vector2.cpp @@ -0,0 +1,87 @@ +/* + * This file is part of the ZoneMinder Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "catch2/catch.hpp" + +#include "zm_vector2.h" + +std::ostream &operator<<(std::ostream &os, Vector2 const &value) { + os << "{ X: " << value.X() << ", Y: " << value.Y() << " }"; + return os; +} + +TEST_CASE("Vector2: default constructor") { + Vector2 c; + REQUIRE(c.X() == 0); + REQUIRE(c.Y() == 0); +} + +TEST_CASE("Vector2: x/y constructor") { + Vector2 c(1, 2); + + REQUIRE(c.X() == 1); + REQUIRE(c.Y() == 2); +} + +TEST_CASE("Vector2: assignment/copy") { + Vector2 c; + Vector2 c2(1, 2); + + REQUIRE(c.X() == 0); + REQUIRE(c.Y() == 0); + + SECTION("assignment operator") { + c = c2; + REQUIRE(c.X() == 1); + REQUIRE(c.Y() == 2); + } + + SECTION("copy constructor") { + Vector2 c3(c2); // NOLINT(performance-unnecessary-copy-initialization) + REQUIRE(c3.X() == 1); + REQUIRE(c3.Y() == 2); + } +} + +TEST_CASE("Vector2: comparison operators") { + Vector2 c1(1, 2); + Vector2 c2(1, 2); + Vector2 c3(1, 3); + + REQUIRE((c1 == c2) == true); + REQUIRE((c1 != c3) == true); +} + +TEST_CASE("Vector2: arithmetic operators") { + Vector2 c(1, 1); + + SECTION("addition") { + Vector2 c1 = c + Vector2(1, 1); + REQUIRE(c1 == Vector2(2, 2)); + + c += {1, 2}; + REQUIRE(c == Vector2(2, 3)); + } + + SECTION("subtraction") { + Vector2 c1 = c - Vector2(1, 1); + REQUIRE(c1 == Vector2(0, 0)); + + c -= {1, 2}; + REQUIRE(c == Vector2(0, -1)); + } +} From 290b88e5acbb396a9089b5131e6f6a710131795d Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Sat, 1 May 2021 16:19:04 +0200 Subject: [PATCH 033/394] Vector2: Delete non-idiomatic comparison operators The "greater than" and "lesser than" operator families don't make sense for coordinates. If lexicographic comparison for ordering purposes is needed, it should be implemented separately. --- src/zm_vector2.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/zm_vector2.h b/src/zm_vector2.h index 2254b0f6a..42e80f234 100644 --- a/src/zm_vector2.h +++ b/src/zm_vector2.h @@ -57,10 +57,11 @@ class Vector2 { inline bool operator==(const Vector2 &coord) const { return (x == coord.x && y == coord.y); } inline bool operator!=(const Vector2 &coord) const { return (x != coord.x || y != coord.y); } - inline bool operator>(const Vector2 &coord) const { return (x > coord.x && y > coord.y); } - inline bool operator>=(const Vector2 &coord) const { return (!(operator<(coord))); } - inline bool operator<(const Vector2 &coord) const { return (x < coord.x && y < coord.y); } - inline bool operator<=(const Vector2 &coord) const { return (!(operator>(coord))); } + // These operators are not idiomatic. If lexicographic comparison is needed, it should be implemented separately. + inline bool operator>(const Vector2 &coord) const = delete; + inline bool operator>=(const Vector2 &coord) const = delete; + inline bool operator<(const Vector2 &coord) const = delete; + inline bool operator<=(const Vector2 &coord) const = delete; inline Vector2 &operator+=(const Vector2 &coord) { x += coord.x; From c8885fe2aa3da295988b8e5937c2bb4d1f41566f Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Fri, 7 May 2021 20:59:51 +0200 Subject: [PATCH 034/394] Vector2: Use default copy/assignment ops --- src/zm_vector2.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/zm_vector2.h b/src/zm_vector2.h index 42e80f234..025ee4963 100644 --- a/src/zm_vector2.h +++ b/src/zm_vector2.h @@ -29,13 +29,6 @@ class Vector2 { public: inline Vector2() : x(0), y(0) {} inline Vector2(int p_x, int p_y) : x(p_x), y(p_y) {} - inline Vector2(const Vector2 &p_coord) : x(p_coord.x), y(p_coord.y) {} - - inline Vector2 &operator=(const Vector2 &coord) { - x = coord.x; - y = coord.y; - return *this; - } inline int &X(int p_x) { x = p_x; From e6c159fb70bd3f7a47e2d44e20ce1d541aea9803 Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Sat, 8 May 2021 12:58:32 +0200 Subject: [PATCH 035/394] Vector2: Make coordinate components public The components were already unconditionally/without side-effects writable. Let's make them public so we don't need the setters. --- src/zm_box.h | 30 +++++++++---------- src/zm_image.cpp | 44 ++++++++++++++-------------- src/zm_monitor.cpp | 9 +++--- src/zm_poly.cpp | 32 +++++++++------------ src/zm_vector2.h | 68 +++++++++++++++++--------------------------- src/zm_zone.cpp | 8 +++--- src/zm_zone_stats.h | 4 +-- tests/zm_vector2.cpp | 22 +++++++------- 8 files changed, 99 insertions(+), 118 deletions(-) diff --git a/src/zm_box.h b/src/zm_box.h index 4ab4e1270..a25b09991 100644 --- a/src/zm_box.h +++ b/src/zm_box.h @@ -40,27 +40,27 @@ class Box { inline Box(const Vector2 &p_lo, const Vector2 &p_hi) : lo(p_lo), hi(p_hi), size(Vector2::Range(hi, lo)) {} inline const Vector2 &Lo() const { return lo; } - inline int LoX() const { return lo.X(); } - inline int LoX(int p_lo_x) { return lo.X(p_lo_x); } - inline int LoY() const { return lo.Y(); } - inline int LoY(int p_lo_y) { return lo.Y(p_lo_y); } + inline int LoX() const { return lo.x_; } + inline int LoX(int p_lo_x) { return lo.x_ = p_lo_x; } + inline int LoY() const { return lo.y_; } + inline int LoY(int p_lo_y) { return lo.y_ = p_lo_y; } inline const Vector2 &Hi() const { return hi; } - inline int HiX() const { return hi.X(); } - inline int HiX(int p_hi_x) { return hi.X(p_hi_x); } - inline int HiY() const { return hi.Y(); } - inline int HiY(int p_hi_y) { return hi.Y(p_hi_y); } + inline int HiX() const { return hi.x_; } + inline int HiX(int p_hi_x) { return hi.x_ = p_hi_x; } + inline int HiY() const { return hi.y_; } + inline int HiY(int p_hi_y) { return hi.y_ = p_hi_y; } inline const Vector2 &Size() const { return size; } - inline int Width() const { return size.X(); } - inline int Height() const { return size.Y(); } - inline int Area() const { return size.X() * size.Y(); } + inline int Width() const { return size.x_; } + inline int Height() const { return size.y_; } + inline int Area() const { return size.x_ * size.y_; } inline const Vector2 Centre() const { - int mid_x = int(std::round(lo.X() + (size.X() / 2.0))); - int mid_y = int(std::round(lo.Y() + (size.Y() / 2.0))); + int mid_x = int(std::round(lo.x_ + (size.x_ / 2.0))); + int mid_y = int(std::round(lo.y_ + (size.y_ / 2.0))); return Vector2(mid_x, mid_y); } - inline bool Inside(const Vector2 &coord) const { - return (coord.X() >= lo.X() && coord.X() <= hi.X() && coord.Y() >= lo.Y() && coord.Y() <= hi.Y()); + inline bool Inside(const Vector2 &coord) const { + return (coord.x_ >= lo.x_ && coord.x_ <= hi.x_ && coord.y_ >= lo.y_ && coord.y_ <= hi.y_); } }; diff --git a/src/zm_image.cpp b/src/zm_image.cpp index c3d501235..155b6fcb5 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -820,10 +820,10 @@ Image *Image::HighlightEdges( /* Set image to all black */ high_image->Clear(); - unsigned int lo_x = limits ? limits->Lo().X() : 0; - unsigned int lo_y = limits ? limits->Lo().Y() : 0; - unsigned int hi_x = limits ? limits->Hi().X() : width-1; - unsigned int hi_y = limits ? limits->Hi().Y() : height-1; + unsigned int lo_x = limits ? limits->Lo().x_ : 0; + unsigned int lo_y = limits ? limits->Lo().y_ : 0; + unsigned int hi_x = limits ? limits->Hi().x_ : width - 1; + unsigned int hi_y = limits ? limits->Hi().y_ : height - 1; if ( p_colours == ZM_COLOUR_GRAY8 ) { for ( unsigned int y = lo_y; y <= hi_y; y++ ) { @@ -2031,8 +2031,8 @@ void Image::Annotate( // Calculate initial coordinates of annotation so that everything is displayed even if the // user set coordinates would prevent that. - uint32 x0 = ZM::clamp(static_cast(coord.X()), 0u, x0_max); - uint32 y0 = ZM::clamp(static_cast(coord.Y()), 0u, y0_max); + uint32 x0 = ZM::clamp(static_cast(coord.x_), 0u, x0_max); + uint32 y0 = ZM::clamp(static_cast(coord.y_), 0u, y0_max); uint32 y = y0; for (const std::string &line : lines) { @@ -2294,10 +2294,10 @@ void Image::Fill( Rgb colour, const Box *limits ) { /* Convert the colour's RGBA subpixel order into the image's subpixel order */ colour = rgb_convert(colour,subpixelorder); - unsigned int lo_x = limits?limits->Lo().X():0; - unsigned int lo_y = limits?limits->Lo().Y():0; - unsigned int hi_x = limits?limits->Hi().X():width-1; - unsigned int hi_y = limits?limits->Hi().Y():height-1; + unsigned int lo_x = limits ? limits->Lo().x_ : 0; + unsigned int lo_y = limits ? limits->Lo().y_ : 0; + unsigned int hi_x = limits ? limits->Hi().x_ : width - 1; + unsigned int hi_y = limits ? limits->Hi().y_ : height - 1; if ( colours == ZM_COLOUR_GRAY8 ) { for ( unsigned int y = lo_y; y <= hi_y; y++ ) { unsigned char *p = &buffer[(y*width)+lo_x]; @@ -2339,10 +2339,10 @@ void Image::Fill( Rgb colour, int density, const Box *limits ) { /* Convert the colour's RGBA subpixel order into the image's subpixel order */ colour = rgb_convert(colour, subpixelorder); - unsigned int lo_x = limits?limits->Lo().X():0; - unsigned int lo_y = limits?limits->Lo().Y():0; - unsigned int hi_x = limits?limits->Hi().X():width-1; - unsigned int hi_y = limits?limits->Hi().Y():height-1; + unsigned int lo_x = limits ? limits->Lo().x_ : 0; + unsigned int lo_y = limits ? limits->Lo().y_ : 0; + unsigned int hi_x = limits ? limits->Hi().x_ : width - 1; + unsigned int hi_y = limits ? limits->Hi().y_ : height - 1; if ( colours == ZM_COLOUR_GRAY8 ) { for ( unsigned int y = lo_y; y <= hi_y; y++ ) { unsigned char *p = &buffer[(y*width)+lo_x]; @@ -2389,10 +2389,10 @@ void Image::Outline( Rgb colour, const Polygon &polygon ) { const Vector2 &p1 = polygon.getCoord(i); const Vector2 &p2 = polygon.getCoord(j); - int x1 = p1.X(); - int x2 = p2.X(); - int y1 = p1.Y(); - int y2 = p2.Y(); + int x1 = p1.x_; + int x2 = p2.x_; + int y1 = p1.y_; + int y2 = p2.y_; double dx = x2 - x1; double dy = y2 - y1; @@ -2473,10 +2473,10 @@ void Image::Fill(Rgb colour, int density, const Polygon &polygon) { const Vector2 &p1 = polygon.getCoord(i); const Vector2 &p2 = polygon.getCoord(j); - int x1 = p1.X(); - int x2 = p2.X(); - int y1 = p1.Y(); - int y2 = p2.Y(); + int x1 = p1.x_; + int x2 = p2.x_; + int y1 = p1.y_; + int y2 = p2.y_; //Debug( 9, "x1:%d,y1:%d x2:%d,y2:%d", x1, y1, x2, y2 ); if ( y1 == y2 ) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index dcdecf5aa..d77638968 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1544,8 +1544,9 @@ bool Monitor::CheckSignal(const Image *image) { if ( !config.timestamp_on_capture || !label_format[0] ) break; // Avoid sampling the rows with timestamp in - if ( index < (label_coord.Y()*width) || index >= (label_coord.Y()+Image::LINE_HEIGHT)*width ) + if (index < (label_coord.y_ * width) || index >= (label_coord.y_ + Image::LINE_HEIGHT) * width) { break; + } } if ( colours == ZM_COLOUR_GRAY8 ) { @@ -2898,8 +2899,8 @@ unsigned int Monitor::DetectMotion(const Image &comp_image, Event::StringSet &zo } // end if alarm if (top_score > 0) { - shared_data->alarm_x = alarm_centre.X(); - shared_data->alarm_y = alarm_centre.Y(); + shared_data->alarm_x = alarm_centre.x_; + shared_data->alarm_y = alarm_centre.y_; Info("Got alarm centre at %d,%d, at count %d", shared_data->alarm_x, shared_data->alarm_y, analysis_image_count); @@ -2954,7 +2955,7 @@ bool Monitor::DumpSettings(char *output, bool verbose) { sprintf(output+strlen(output), "Subpixel Order : %u\n", camera->SubpixelOrder() ); sprintf(output+strlen(output), "Event Prefix : %s\n", event_prefix.c_str() ); sprintf(output+strlen(output), "Label Format : %s\n", label_format.c_str() ); - sprintf(output+strlen(output), "Label Coord : %d,%d\n", label_coord.X(), label_coord.Y() ); + sprintf(output+strlen(output), "Label Coord : %d,%d\n", label_coord.x_, label_coord.y_ ); sprintf(output+strlen(output), "Label Size : %d\n", label_size ); sprintf(output+strlen(output), "Image Buffer Count : %d\n", image_buffer_count ); sprintf(output+strlen(output), "Warmup Count : %d\n", warmup_count ); diff --git a/src/zm_poly.cpp b/src/zm_poly.cpp index e132e1bef..a1fba9e48 100644 --- a/src/zm_poly.cpp +++ b/src/zm_poly.cpp @@ -24,7 +24,7 @@ void Polygon::calcArea() { double float_area = 0.0L; for (int i = 0, j = n_coords - 1; i < n_coords; j = i++) { - double trap_area = ((coords[i].X() - coords[j].X()) * ((coords[i].Y() + coords[j].Y()))) / 2.0L; + double trap_area = ((coords[i].x_ - coords[j].x_) * ((coords[i].y_ + coords[j].y_))) / 2.0L; float_area += trap_area; //printf( "%.2f (%.2f)\n", float_area, trap_area ); } @@ -36,10 +36,8 @@ void Polygon::calcCentre() { calcArea(); double float_x = 0.0L, float_y = 0.0L; for (int i = 0, j = n_coords - 1; i < n_coords; j = i++) { - float_x += ((coords[i].Y() - coords[j].Y()) - * ((coords[i].X() * 2) + (coords[i].X() * coords[j].X()) + (coords[j].X() * 2))); - float_y += ((coords[j].X() - coords[i].X()) - * ((coords[i].Y() * 2) + (coords[i].Y() * coords[j].Y()) + (coords[j].Y() * 2))); + float_x += ((coords[i].y_ - coords[j].y_) * ((coords[i].x_ * 2) + (coords[i].x_ * coords[j].x_) + (coords[j].x_ * 2))); + float_y += ((coords[j].x_ - coords[i].x_) * ((coords[i].y_ * 2) + (coords[i].y_ * coords[j].y_) + (coords[j].y_ * 2))); } float_x /= (6 * area); float_y /= (6 * area); @@ -55,14 +53,14 @@ Polygon::Polygon(int p_n_coords, const Vector2 *p_coords) : n_coords(p_n_coords) int max_y = -1; for (int i = 0; i < n_coords; i++) { coords[i] = p_coords[i]; - if (min_x == -1 || coords[i].X() < min_x) - min_x = coords[i].X(); - if (max_x == -1 || coords[i].X() > max_x) - max_x = coords[i].X(); - if (min_y == -1 || coords[i].Y() < min_y) - min_y = coords[i].Y(); - if (max_y == -1 || coords[i].Y() > max_y) - max_y = coords[i].Y(); + if (min_x == -1 || coords[i].x_ < min_x) + min_x = coords[i].x_; + if (max_x == -1 || coords[i].x_ > max_x) + max_x = coords[i].x_; + if (min_y == -1 || coords[i].y_ < min_y) + min_y = coords[i].y_; + if (max_y == -1 || coords[i].y_ > max_y) + max_y = coords[i].y_; } extent = Box(min_x, min_y, max_x, max_y); calcArea(); @@ -98,11 +96,9 @@ Polygon &Polygon::operator=(const Polygon &p_polygon) { bool Polygon::isInside(const Vector2 &coord) const { bool inside = false; - for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ ) { - if ( (((coords[i].Y() <= coord.Y()) && (coord.Y() < coords[j].Y()) ) - || ((coords[j].Y() <= coord.Y()) && (coord.Y() < coords[i].Y()))) - && (coord.X() < (coords[j].X() - coords[i].X()) * (coord.Y() - coords[i].Y()) / (coords[j].Y() - coords[i].Y()) + coords[i].X())) - { + for (int i = 0, j = n_coords - 1; i < n_coords; j = i++) { + if ((((coords[i].y_ <= coord.y_) && (coord.y_ < coords[j].y_)) || ((coords[j].y_ <= coord.y_) && (coord.y_ < coords[i].y_))) + && (coord.x_ < (coords[j].x_ - coords[i].x_) * (coord.y_ - coords[i].y_) / (coords[j].y_ - coords[i].y_) + coords[i].x_)) { inside = !inside; } } diff --git a/src/zm_vector2.h b/src/zm_vector2.h index 025ee4963..522eee51e 100644 --- a/src/zm_vector2.h +++ b/src/zm_vector2.h @@ -27,60 +27,44 @@ // class Vector2 { public: - inline Vector2() : x(0), y(0) {} - inline Vector2(int p_x, int p_y) : x(p_x), y(p_y) {} + Vector2() : x_(0), y_(0) {} + Vector2(int32 x, int32 y) : x_(x), y_(y) {} - inline int &X(int p_x) { - x = p_x; - return x; - } - inline const int &X() const { return x; } - - inline int &Y(int p_y) { - y = p_y; - return y; - } - inline const int &Y() const { return y; } - - inline static Vector2 Range(const Vector2 &coord1, const Vector2 &coord2) { - Vector2 result((coord1.x - coord2.x) + 1, (coord1.y - coord2.y) + 1); + static Vector2 Range(const Vector2 &coord1, const Vector2 &coord2) { + Vector2 result((coord1.x_ - coord2.x_) + 1, (coord1.y_ - coord2.y_) + 1); return result; } - inline bool operator==(const Vector2 &coord) const { return (x == coord.x && y == coord.y); } - inline bool operator!=(const Vector2 &coord) const { return (x != coord.x || y != coord.y); } + bool operator==(const Vector2 &rhs) const { return (x_ == rhs.x_ && y_ == rhs.y_); } + bool operator!=(const Vector2 &rhs) const { return (x_ != rhs.x_ || y_ != rhs.y_); } // These operators are not idiomatic. If lexicographic comparison is needed, it should be implemented separately. - inline bool operator>(const Vector2 &coord) const = delete; - inline bool operator>=(const Vector2 &coord) const = delete; - inline bool operator<(const Vector2 &coord) const = delete; - inline bool operator<=(const Vector2 &coord) const = delete; + bool operator>(const Vector2 &rhs) const = delete; + bool operator>=(const Vector2 &rhs) const = delete; + bool operator<(const Vector2 &rhs) const = delete; + bool operator<=(const Vector2 &rhs) const = delete; - inline Vector2 &operator+=(const Vector2 &coord) { - x += coord.x; - y += coord.y; + Vector2 operator+(const Vector2 &rhs) const { + return {x_ + rhs.x_, y_ + rhs.y_}; + } + Vector2 operator-(const Vector2 &rhs) const { + return {x_ - rhs.x_, y_ - rhs.y_}; + } + + Vector2 &operator+=(const Vector2 &rhs) { + x_ += rhs.x_; + y_ += rhs.y_; return *this; } - inline Vector2 &operator-=(const Vector2 &coord) { - x -= coord.x; - y -= coord.y; + Vector2 &operator-=(const Vector2 &rhs) { + x_ -= rhs.x_; + y_ -= rhs.y_; return *this; } - inline friend Vector2 operator+(const Vector2 &coord1, const Vector2 &coord2) { - Vector2 result(coord1); - result += coord2; - return result; - } - inline friend Vector2 operator-(const Vector2 &coord1, const Vector2 &coord2) { - Vector2 result(coord1); - result -= coord2; - return result; - } - - private: - int x; - int y; + public: + int32 x_; + int32 y_; }; #endif // ZM_VECTOR2_H diff --git a/src/zm_zone.cpp b/src/zm_zone.cpp index d48d087fa..1ce084b87 100644 --- a/src/zm_zone.cpp +++ b/src/zm_zone.cpp @@ -267,8 +267,8 @@ bool Zone::CheckAlarms(const Image *delta_image) { Debug(5, "Current score is %d", stats.score_); if (check_method >= FILTERED_PIXELS) { - int bx = filter_box.X(); - int by = filter_box.Y(); + int bx = filter_box.x_; + int by = filter_box.y_; int bx1 = bx-1; int by1 = by-1; @@ -924,7 +924,7 @@ bool Zone::DumpSettings(char *output, bool /*verbose*/) const { )))))); sprintf( output+strlen(output), " Shape : %d points\n", polygon.getNumCoords() ); for ( int i = 0; i < polygon.getNumCoords(); i++ ) { - sprintf( output+strlen(output), " %i: %d,%d\n", i, polygon.getCoord( i ).X(), polygon.getCoord( i ).Y() ); + sprintf(output+strlen(output), " %i: %d,%d\n", i, polygon.getCoord( i ).x_, polygon.getCoord(i ).y_ ); } sprintf( output+strlen(output), " Alarm RGB : %06x\n", alarm_rgb ); sprintf( output+strlen(output), " Check Method: %d - %s\n", check_method, @@ -936,7 +936,7 @@ bool Zone::DumpSettings(char *output, bool /*verbose*/) const { sprintf( output+strlen(output), " Max Pixel Threshold : %d\n", max_pixel_threshold ); sprintf( output+strlen(output), " Min Alarm Pixels : %d\n", min_alarm_pixels ); sprintf( output+strlen(output), " Max Alarm Pixels : %d\n", max_alarm_pixels ); - sprintf( output+strlen(output), " Filter Box : %d,%d\n", filter_box.X(), filter_box.Y() ); + sprintf(output+strlen(output), " Filter Box : %d,%d\n", filter_box.x_, filter_box.y_ ); sprintf( output+strlen(output), " Min Filter Pixels : %d\n", min_filter_pixels ); sprintf( output+strlen(output), " Max Filter Pixels : %d\n", max_filter_pixels ); sprintf( output+strlen(output), " Min Blob Pixels : %d\n", min_blob_pixels ); diff --git a/src/zm_zone_stats.h b/src/zm_zone_stats.h index e9cc37889..9d3ab7cef 100644 --- a/src/zm_zone_stats.h +++ b/src/zm_zone_stats.h @@ -71,8 +71,8 @@ class ZoneStats { alarm_box_.LoY(), alarm_box_.HiX(), alarm_box_.HiY(), - alarm_centre_.X(), - alarm_centre_.Y(), + alarm_centre_.x_, + alarm_centre_.y_, score_ ); } diff --git a/tests/zm_vector2.cpp b/tests/zm_vector2.cpp index 32a0e3d6d..75b25350d 100644 --- a/tests/zm_vector2.cpp +++ b/tests/zm_vector2.cpp @@ -20,40 +20,40 @@ #include "zm_vector2.h" std::ostream &operator<<(std::ostream &os, Vector2 const &value) { - os << "{ X: " << value.X() << ", Y: " << value.Y() << " }"; + os << "{ X: " << value.x_ << ", Y: " << value.y_ << " }"; return os; } TEST_CASE("Vector2: default constructor") { Vector2 c; - REQUIRE(c.X() == 0); - REQUIRE(c.Y() == 0); + REQUIRE(c.x_ == 0); + REQUIRE(c.y_ == 0); } TEST_CASE("Vector2: x/y constructor") { Vector2 c(1, 2); - REQUIRE(c.X() == 1); - REQUIRE(c.Y() == 2); + REQUIRE(c.x_ == 1); + REQUIRE(c.y_ == 2); } TEST_CASE("Vector2: assignment/copy") { Vector2 c; Vector2 c2(1, 2); - REQUIRE(c.X() == 0); - REQUIRE(c.Y() == 0); + REQUIRE(c.x_ == 0); + REQUIRE(c.y_ == 0); SECTION("assignment operator") { c = c2; - REQUIRE(c.X() == 1); - REQUIRE(c.Y() == 2); + REQUIRE(c.x_ == 1); + REQUIRE(c.y_ == 2); } SECTION("copy constructor") { Vector2 c3(c2); // NOLINT(performance-unnecessary-copy-initialization) - REQUIRE(c3.X() == 1); - REQUIRE(c3.Y() == 2); + REQUIRE(c3.x_ == 1); + REQUIRE(c3.y_ == 2); } } From f85e3765dbd47c421b9105ae50936e6e6d003dd4 Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Sun, 2 May 2021 23:03:58 +0200 Subject: [PATCH 036/394] Box: Remove unnecessary constructors --- src/zm_box.h | 16 +++++++--------- src/zm_poly.cpp | 2 +- src/zm_stream.cpp | 2 +- src/zm_zone_stats.h | 7 +------ 4 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/zm_box.h b/src/zm_box.h index a25b09991..5799eb898 100644 --- a/src/zm_box.h +++ b/src/zm_box.h @@ -28,16 +28,9 @@ // defined by two coordinates // class Box { - private: - Vector2 lo, hi; - Vector2 size; - public: - inline Box() : lo(0, 0), hi(0, 0), size(0, 0) {} - explicit inline Box(unsigned int p_size) : lo(0, 0), hi(p_size - 1, p_size - 1), size(Vector2::Range(hi, lo)) {} - inline Box(int p_x_size, int p_y_size) : lo(0, 0), hi(p_x_size - 1, p_y_size - 1), size(Vector2::Range(hi, lo)) {} - inline Box(int lo_x, int lo_y, int hi_x, int hi_y) : lo(lo_x, lo_y), hi(hi_x, hi_y), size(Vector2::Range(hi, lo)) {} - inline Box(const Vector2 &p_lo, const Vector2 &p_hi) : lo(p_lo), hi(p_hi), size(Vector2::Range(hi, lo)) {} + Box() = default; + Box(Vector2 p_lo, Vector2 p_hi) : lo(p_lo), hi(p_hi), size(Vector2::Range(hi, lo)) {} inline const Vector2 &Lo() const { return lo; } inline int LoX() const { return lo.x_; } @@ -62,6 +55,11 @@ class Box { inline bool Inside(const Vector2 &coord) const { return (coord.x_ >= lo.x_ && coord.x_ <= hi.x_ && coord.y_ >= lo.y_ && coord.y_ <= hi.y_); } + + private: + Vector2 lo; + Vector2 hi; + Vector2 size; }; #endif // ZM_BOX_H diff --git a/src/zm_poly.cpp b/src/zm_poly.cpp index a1fba9e48..8f52ff14e 100644 --- a/src/zm_poly.cpp +++ b/src/zm_poly.cpp @@ -62,7 +62,7 @@ Polygon::Polygon(int p_n_coords, const Vector2 *p_coords) : n_coords(p_n_coords) if (max_y == -1 || coords[i].y_ > max_y) max_y = coords[i].y_; } - extent = Box(min_x, min_y, max_x, max_y); + extent = Box({min_x, min_y}, {max_x, max_y}); calcArea(); calcCentre(); } diff --git a/src/zm_stream.cpp b/src/zm_stream.cpp index 0968c9514..974ca47cd 100644 --- a/src/zm_stream.cpp +++ b/src/zm_stream.cpp @@ -231,7 +231,7 @@ Image *StreamBase::prepareImage(Image *image) { hi_y = act_image_height - 1; lo_y = hi_y - (send_image_height - 1); } - last_crop = Box( lo_x, lo_y, hi_x, hi_y ); + last_crop = Box({lo_x, lo_y}, {hi_x, hi_y}); } // end if ( mag != last_mag || x != last_x || y != last_y ) Debug(3, "Cropping to %d,%d -> %d,%d", last_crop.LoX(), last_crop.LoY(), last_crop.HiX(), last_crop.HiY()); diff --git a/src/zm_zone_stats.h b/src/zm_zone_stats.h index 9d3ab7cef..5cc20a5db 100644 --- a/src/zm_zone_stats.h +++ b/src/zm_zone_stats.h @@ -35,8 +35,6 @@ class ZoneStats { alarm_blobs_(0), min_blob_size_(0), max_blob_size_(0), - alarm_box_({}), - alarm_centre_({}), score_(0) {}; void Reset() { @@ -47,10 +45,7 @@ class ZoneStats { alarm_blobs_ = 0; min_blob_size_ = 0; max_blob_size_ = 0; - alarm_box_.LoX(0); - alarm_box_.LoY(0); - alarm_box_.HiX(0); - alarm_box_.HiY(0); + alarm_box_ = {}; alarm_centre_ = {}; score_ = 0; } From c2a7f7b593e62bd4d98bf702b7035a27eab3e339 Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Sun, 2 May 2021 23:04:26 +0200 Subject: [PATCH 037/394] tests/Box: Add unit tests --- tests/CMakeLists.txt | 1 + tests/zm_box.cpp | 55 ++++++++++++++++++++++++++++++++++++++++++++ tests/zm_catch2.h | 30 ++++++++++++++++++++++++ tests/zm_comms.cpp | 2 +- tests/zm_crypt.cpp | 2 +- tests/zm_font.cpp | 2 +- tests/zm_utils.cpp | 2 +- tests/zm_vector2.cpp | 7 +----- 8 files changed, 91 insertions(+), 10 deletions(-) create mode 100644 tests/zm_box.cpp create mode 100644 tests/zm_catch2.h diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 426f03766..ea5c24aa5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -12,6 +12,7 @@ include(Catch) set(TEST_SOURCES + zm_box.cpp zm_comms.cpp zm_crypt.cpp zm_font.cpp diff --git a/tests/zm_box.cpp b/tests/zm_box.cpp new file mode 100644 index 000000000..bd47c0bfb --- /dev/null +++ b/tests/zm_box.cpp @@ -0,0 +1,55 @@ +/* + * This file is part of the ZoneMinder Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "zm_catch2.h" + +#include "zm_box.h" + +TEST_CASE("Box: default constructor") { + Box b; + + REQUIRE(b.Lo() == Vector2(0, 0)); + REQUIRE(b.Hi() == Vector2(0, 0)); + REQUIRE(b.Size() == Vector2(0, 0)); + REQUIRE(b.Area() == 0); +} + +TEST_CASE("Box: construct from lo and hi") { + Box b({1, 1}, {5, 5}); + + SECTION("basic properties") { + REQUIRE(b.Lo() == Vector2(1, 1)); + REQUIRE(b.Hi() == Vector2(5, 5)); + // Should be: + // REQUIRE(b.Size() == Vector2(4 ,4)); + REQUIRE(b.Size() == Vector2(5, 5)); + // Should be: + // REQUIRE(b.Area() == 16); + REQUIRE(b.Area() == 25); + // Should be: + // REQUIRE(b.Centre() == Vector2(3, 3)); + REQUIRE(b.Centre() == Vector2(4, 4)); + } + + SECTION("contains") { + REQUIRE(b.Contains({0, 0}) == false); + REQUIRE(b.Contains({1, 1}) == true); + REQUIRE(b.Contains({3, 3}) == true); + REQUIRE(b.Contains({5, 5}) == true); + REQUIRE(b.Contains({6, 6}) == false); + } +} diff --git a/tests/zm_catch2.h b/tests/zm_catch2.h new file mode 100644 index 000000000..5f92277ea --- /dev/null +++ b/tests/zm_catch2.h @@ -0,0 +1,30 @@ +/* + * This file is part of the ZoneMinder Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef ZONEMINDER_TESTS_ZM_CATCH2_H_ +#define ZONEMINDER_TESTS_ZM_CATCH2_H_ + +#include "catch2/catch.hpp" + +#include "zm_vector2.h" + +inline std::ostream &operator<<(std::ostream &os, Vector2 const &value) { + os << "{ X: " << value.x_ << ", Y: " << value.y_ << " }"; + return os; +} + +#endif //ZONEMINDER_TESTS_ZM_CATCH2_H_ diff --git a/tests/zm_comms.cpp b/tests/zm_comms.cpp index dfe98e1bf..1146e69a5 100644 --- a/tests/zm_comms.cpp +++ b/tests/zm_comms.cpp @@ -15,7 +15,7 @@ * with this program. If not, see . */ -#include "catch2/catch.hpp" +#include "zm_catch2.h" #include "zm_comms.h" #include diff --git a/tests/zm_crypt.cpp b/tests/zm_crypt.cpp index d3a315ef4..0d68c1e3c 100644 --- a/tests/zm_crypt.cpp +++ b/tests/zm_crypt.cpp @@ -15,7 +15,7 @@ * with this program. If not, see . */ -#include "catch2/catch.hpp" +#include "zm_catch2.h" #include "zm_crypt.h" diff --git a/tests/zm_font.cpp b/tests/zm_font.cpp index d862c3291..9ddb79a0e 100644 --- a/tests/zm_font.cpp +++ b/tests/zm_font.cpp @@ -15,7 +15,7 @@ * with this program. If not, see . */ -#include "catch2/catch.hpp" +#include "zm_catch2.h" #include "zm_font.h" diff --git a/tests/zm_utils.cpp b/tests/zm_utils.cpp index 408b9f43e..4745ad60b 100644 --- a/tests/zm_utils.cpp +++ b/tests/zm_utils.cpp @@ -15,7 +15,7 @@ * with this program. If not, see . */ -#include "catch2/catch.hpp" +#include "zm_catch2.h" #include "zm_utils.h" #include diff --git a/tests/zm_vector2.cpp b/tests/zm_vector2.cpp index 75b25350d..23930f41a 100644 --- a/tests/zm_vector2.cpp +++ b/tests/zm_vector2.cpp @@ -15,15 +15,10 @@ * with this program. If not, see . */ -#include "catch2/catch.hpp" +#include "zm_catch2.h" #include "zm_vector2.h" -std::ostream &operator<<(std::ostream &os, Vector2 const &value) { - os << "{ X: " << value.x_ << ", Y: " << value.y_ << " }"; - return os; -} - TEST_CASE("Vector2: default constructor") { Vector2 c; REQUIRE(c.x_ == 0); From 8f685b3d66516191c23d56d77e85fe7819e7133a Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Thu, 6 May 2021 19:52:59 +0200 Subject: [PATCH 038/394] Box+Poly: Remove direct accessors to {Hi,Lo}{X,Y} --- src/zm_box.h | 26 +++++++++------------ src/zm_event.cpp | 8 +++---- src/zm_image.cpp | 2 +- src/zm_poly.h | 16 ++++--------- src/zm_stream.cpp | 10 ++++---- src/zm_zone.cpp | 57 ++++++++++++++++++++++++++++----------------- src/zm_zone_stats.h | 8 +++---- 7 files changed, 66 insertions(+), 61 deletions(-) diff --git a/src/zm_box.h b/src/zm_box.h index 5799eb898..6d46f13e3 100644 --- a/src/zm_box.h +++ b/src/zm_box.h @@ -32,22 +32,18 @@ class Box { Box() = default; Box(Vector2 p_lo, Vector2 p_hi) : lo(p_lo), hi(p_hi), size(Vector2::Range(hi, lo)) {} - inline const Vector2 &Lo() const { return lo; } - inline int LoX() const { return lo.x_; } - inline int LoX(int p_lo_x) { return lo.x_ = p_lo_x; } - inline int LoY() const { return lo.y_; } - inline int LoY(int p_lo_y) { return lo.y_ = p_lo_y; } - inline const Vector2 &Hi() const { return hi; } - inline int HiX() const { return hi.x_; } - inline int HiX(int p_hi_x) { return hi.x_ = p_hi_x; } - inline int HiY() const { return hi.y_; } - inline int HiY(int p_hi_y) { return hi.y_ = p_hi_y; } - inline const Vector2 &Size() const { return size; } - inline int Width() const { return size.x_; } - inline int Height() const { return size.y_; } - inline int Area() const { return size.x_ * size.y_; } + const Vector2 &Lo() const { return lo; } + int LoX(int p_lo_x) { return lo.x_ = p_lo_x; } + int LoY(int p_lo_y) { return lo.y_ = p_lo_y; } + const Vector2 &Hi() const { return hi; } + int HiX(int p_hi_x) { return hi.x_ = p_hi_x; } + int HiY(int p_hi_y) { return hi.y_ = p_hi_y; } + const Vector2 &Size() const { return size; } + int Width() const { return size.x_; } + int Height() const { return size.y_; } + int Area() const { return size.x_ * size.y_; } - inline const Vector2 Centre() const { + Vector2 Centre() const { int mid_x = int(std::round(lo.x_ + (size.x_ / 2.0))); int mid_y = int(std::round(lo.y_ + (size.y_ / 2.0))); return Vector2(mid_x, mid_y); diff --git a/src/zm_event.cpp b/src/zm_event.cpp index c6c16ebd5..9bcb2da40 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -473,10 +473,10 @@ void Event::WriteDbFrames() { stats.alarm_blobs_, stats.min_blob_size_, stats.max_blob_size_, - stats.alarm_box_.LoX(), - stats.alarm_box_.LoY(), - stats.alarm_box_.HiX(), - stats.alarm_box_.HiY(), + stats.alarm_box_.Lo().x_, + stats.alarm_box_.Lo().y_, + stats.alarm_box_.Hi().x_, + stats.alarm_box_.Hi().y_, stats.score_); } // end foreach zone stats } // end if recording stats diff --git a/src/zm_image.cpp b/src/zm_image.cpp index 155b6fcb5..d88e2b2a3 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -1508,7 +1508,7 @@ bool Image::Crop( unsigned int lo_x, unsigned int lo_y, unsigned int hi_x, unsig } bool Image::Crop(const Box &limits) { - return Crop(limits.LoX(), limits.LoY(), limits.HiX(), limits.HiY()); + return Crop(limits.Lo().x_, limits.Lo().y_, limits.Hi().x_, limits.Hi().y_); } /* Far from complete */ diff --git a/src/zm_poly.h b/src/zm_poly.h index 1c924cd9e..0514d4756 100644 --- a/src/zm_poly.h +++ b/src/zm_poly.h @@ -94,17 +94,11 @@ public: return coords[index]; } - inline const Box &Extent() const { return extent; } - inline int LoX() const { return extent.LoX(); } - inline int LoX(int p_lo_x) { return extent.LoX(p_lo_x); } - inline int HiX() const { return extent.HiX(); } - inline int HiX(int p_hi_x) { return extent.HiX(p_hi_x); } - inline int LoY() const { return extent.LoY(); } - inline int LoY(int p_lo_y) { return extent.LoY(p_lo_y); } - inline int HiY() const { return extent.HiY(); } - inline int HiY(int p_hi_y) { return extent.HiY(p_hi_y); } - inline int Width() const { return extent.Width(); } - inline int Height() const { return extent.Height(); } + const Box &Extent() const { return extent; } + int LoX(int p_lo_x) { return extent.LoX(p_lo_x); } + int HiX(int p_hi_x) { return extent.HiX(p_hi_x); } + int LoY(int p_lo_y) { return extent.LoY(p_lo_y); } + int HiY(int p_hi_y) { return extent.HiY(p_hi_y); } inline int Area() const { return area; } inline const Vector2 &Centre() const { diff --git a/src/zm_stream.cpp b/src/zm_stream.cpp index 974ca47cd..ce7a4460b 100644 --- a/src/zm_stream.cpp +++ b/src/zm_stream.cpp @@ -204,10 +204,10 @@ Image *StreamBase::prepareImage(Image *image) { last_crop = Box(); // Recalculate crop parameters, as %ges - int click_x = (last_crop.LoX() * 100 ) / last_act_image_width; // Initial crop offset from last image - click_x += ( x * 100 ) / last_virt_image_width; - int click_y = (last_crop.LoY() * 100 ) / last_act_image_height; // Initial crop offset from last image - click_y += ( y * 100 ) / last_virt_image_height; + int click_x = (last_crop.Lo().x_ * 100) / last_act_image_width; // Initial crop offset from last image + click_x += (x * 100) / last_virt_image_width; + int click_y = (last_crop.Lo().y_ * 100) / last_act_image_height; // Initial crop offset from last image + click_y += (y * 100) / last_virt_image_height; Debug(3, "Got adjusted click at %d%%,%d%%", click_x, click_y); // Convert the click locations to the current image pixels @@ -234,7 +234,7 @@ Image *StreamBase::prepareImage(Image *image) { last_crop = Box({lo_x, lo_y}, {hi_x, hi_y}); } // end if ( mag != last_mag || x != last_x || y != last_y ) - Debug(3, "Cropping to %d,%d -> %d,%d", last_crop.LoX(), last_crop.LoY(), last_crop.HiX(), last_crop.HiY()); + Debug(3, "Cropping to %d,%d -> %d,%d", last_crop.Lo().x_, last_crop.Lo().y_, last_crop.Hi().x_, last_crop.Hi().y_); if ( !image_copied ) { static Image copy_image; copy_image.Assign(*image); diff --git a/src/zm_zone.cpp b/src/zm_zone.cpp index 1ce084b87..2ff72b330 100644 --- a/src/zm_zone.cpp +++ b/src/zm_zone.cpp @@ -130,10 +130,10 @@ void Zone::RecordStats(const Event *event) { stats.alarm_blobs_, stats.min_blob_size_, stats.max_blob_size_, - stats.alarm_box_.LoX(), - stats.alarm_box_.LoY(), - stats.alarm_box_.HiX(), - stats.alarm_box_.HiY(), + stats.alarm_box_.Lo().x_, + stats.alarm_box_.Lo().y_, + stats.alarm_box_.Hi().x_, + stats.alarm_box_.Hi().y_, stats.score_ ); zmDbDo(sql); @@ -219,10 +219,10 @@ bool Zone::CheckAlarms(const Image *delta_image) { int alarm_mid_x = -1; int alarm_mid_y = -1; - unsigned int lo_y = polygon.LoY(); - unsigned int lo_x = polygon.LoX(); - unsigned int hi_x = polygon.HiX(); - unsigned int hi_y = polygon.HiY(); + unsigned int lo_x = polygon.Extent().Lo().x_; + unsigned int lo_y = polygon.Extent().Lo().y_; + unsigned int hi_x = polygon.Extent().Hi().x_; + unsigned int hi_y = polygon.Extent().Hi().y_; Debug(4, "Checking alarms for zone %d/%s in lines %d -> %d", id, label.c_str(), lo_y, hi_y); @@ -630,10 +630,10 @@ bool Zone::CheckAlarms(const Image *delta_image) { stats.score_ = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */ Debug(5, "Current score is %d", stats.score_); - alarm_lo_x = polygon.HiX()+1; - alarm_hi_x = polygon.LoX()-1; - alarm_lo_y = polygon.HiY()+1; - alarm_hi_y = polygon.LoY()-1; + alarm_lo_x = polygon.Extent().Hi().x_ + 1; + alarm_hi_x = polygon.Extent().Lo().x_ - 1; + alarm_lo_y = polygon.Extent().Hi().y_ + 1; + alarm_hi_y = polygon.Extent().Lo().y_ - 1; for (uint32 i = 1; i < kWhite; i++) { BlobStats *bs = &blob_stats[i]; @@ -872,14 +872,29 @@ std::vector Zone::Load(Monitor *monitor) { continue; } - if ( polygon.LoX() < 0 || polygon.HiX() >= (int)monitor->Width() - || polygon.LoY() < 0 || polygon.HiY() >= (int)monitor->Height() ) { + if (polygon.Extent().Lo().x_ < 0 || polygon.Extent().Hi().x_ >= (int) monitor->Width() + || polygon.Extent().Lo().y_ < 0 || polygon.Extent().Hi().y_ >= (int) monitor->Height()) { Error("Zone %d/%s for monitor %s extends outside of image dimensions, (%d,%d), (%d,%d), fixing", - Id, Name, monitor->Name(), polygon.LoX(), polygon.LoY(), polygon.HiX(), polygon.HiY()); - if ( polygon.LoX() < 0 ) polygon.LoX(0); - if ( polygon.HiX() >= (int)monitor->Width()) polygon.HiX((int)monitor->Width()); - if ( polygon.LoY() < 0 ) polygon.LoY(0); - if ( polygon.HiY() >= (int)monitor->Height() ) polygon.HiY((int)monitor->Height()); + Id, + Name, + monitor->Name(), + polygon.Extent().Lo().x_, + polygon.Extent().Lo().y_, + polygon.Extent().Hi().x_, + polygon.Extent().Hi().y_); + + if (polygon.Extent().Lo().x_ < 0) { + polygon.LoX(0); + } + if (polygon.Extent().Hi().x_ >= (int) monitor->Width()) { + polygon.HiX((int) monitor->Width()); + } + if (polygon.Extent().Lo().y_ < 0) { + polygon.LoY(0); + } + if (polygon.Extent().Hi().y_ >= (int) monitor->Height()) { + polygon.HiY((int) monitor->Height()); + } } if ( false && !strcmp( Units, "Percent" ) ) { @@ -960,8 +975,8 @@ void Zone::std_alarmedpixels( if ( max_pixel_threshold ) calc_max_pixel_threshold = max_pixel_threshold; - lo_y = polygon.LoY(); - hi_y = polygon.HiY(); + lo_y = polygon.Extent().Lo().y_; + hi_y = polygon.Extent().Hi().y_; for ( unsigned int y = lo_y; y <= hi_y; y++ ) { unsigned int lo_x = ranges[y].lo_x; unsigned int hi_x = ranges[y].hi_x; diff --git a/src/zm_zone_stats.h b/src/zm_zone_stats.h index 5cc20a5db..6243f53b6 100644 --- a/src/zm_zone_stats.h +++ b/src/zm_zone_stats.h @@ -62,10 +62,10 @@ class ZoneStats { alarm_blobs_, min_blob_size_, max_blob_size_, - alarm_box_.LoX(), - alarm_box_.LoY(), - alarm_box_.HiX(), - alarm_box_.HiY(), + alarm_box_.Lo().x_, + alarm_box_.Lo().y_, + alarm_box_.Hi().x_, + alarm_box_.Hi().y_, alarm_centre_.x_, alarm_centre_.y_, score_ From 783dc09b4e06a0794b23e831c3cee0d928011ece Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Fri, 7 May 2021 20:45:09 +0200 Subject: [PATCH 039/394] Box: Rename variables according to the styleguide --- src/zm_box.h | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/zm_box.h b/src/zm_box.h index 6d46f13e3..971fbc36d 100644 --- a/src/zm_box.h +++ b/src/zm_box.h @@ -30,32 +30,32 @@ class Box { public: Box() = default; - Box(Vector2 p_lo, Vector2 p_hi) : lo(p_lo), hi(p_hi), size(Vector2::Range(hi, lo)) {} + Box(Vector2 lo, Vector2 hi) : lo_(lo), hi_(hi), size_(Vector2::Range(hi, lo)) {} - const Vector2 &Lo() const { return lo; } - int LoX(int p_lo_x) { return lo.x_ = p_lo_x; } - int LoY(int p_lo_y) { return lo.y_ = p_lo_y; } - const Vector2 &Hi() const { return hi; } - int HiX(int p_hi_x) { return hi.x_ = p_hi_x; } - int HiY(int p_hi_y) { return hi.y_ = p_hi_y; } - const Vector2 &Size() const { return size; } - int Width() const { return size.x_; } - int Height() const { return size.y_; } - int Area() const { return size.x_ * size.y_; } + const Vector2 &Lo() const { return lo_; } + int32 LoX(int lo_x) { return lo_.x_ = lo_x; } + int32 LoY(int lo_y) { return lo_.y_ = lo_y; } + const Vector2 &Hi() const { return hi_; } + int32 HiX(int hi_x) { return hi_.x_ = hi_x; } + int32 HiY(int hi_y) { return hi_.y_ = hi_y; } + + const Vector2 &Size() const { return size_; } + int32 Area() const { return size_.x_ * size_.y_; } Vector2 Centre() const { - int mid_x = int(std::round(lo.x_ + (size.x_ / 2.0))); - int mid_y = int(std::round(lo.y_ + (size.y_ / 2.0))); - return Vector2(mid_x, mid_y); + int32 mid_x = static_cast(std::lround(lo_.x_ + (size_.x_ / 2.0))); + int32 mid_y = static_cast(std::lround(lo_.y_ + (size_.y_ / 2.0))); + return {mid_x, mid_y}; } - inline bool Inside(const Vector2 &coord) const { - return (coord.x_ >= lo.x_ && coord.x_ <= hi.x_ && coord.y_ >= lo.y_ && coord.y_ <= hi.y_); + + bool Contains(const Vector2 &coord) const { + return (coord.x_ >= lo_.x_ && coord.x_ <= hi_.x_ && coord.y_ >= lo_.y_ && coord.y_ <= hi_.y_); } private: - Vector2 lo; - Vector2 hi; - Vector2 size; + Vector2 lo_; + Vector2 hi_; + Vector2 size_; }; #endif // ZM_BOX_H From 26bdf4ab1ba17a8f145f702a4935a5acd1541ee2 Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Sat, 8 May 2021 18:20:33 +0200 Subject: [PATCH 040/394] Deduplicate Edge struct --- src/zm_image.h | 19 ------------------- src/zm_poly.h | 36 ++++++++++++++++-------------------- 2 files changed, 16 insertions(+), 39 deletions(-) diff --git a/src/zm_image.h b/src/zm_image.h index 0bf72de89..bb12a60a1 100644 --- a/src/zm_image.h +++ b/src/zm_image.h @@ -32,7 +32,6 @@ #endif // HAVE_ZLIB_H class Box; -class Image; class Polygon; #define ZM_BUFTYPE_DONTFREE 0 @@ -96,24 +95,6 @@ class Image { void update_function_pointers(); protected: - struct Edge { - int min_y; - int max_y; - double min_x; - double _1_m; - - static bool CompareYX(const Edge &e1, const Edge &e2) { - if ( e1.min_y == e2.min_y ) - return e1.min_x < e2.min_x; - return e1.min_y < e2.min_y; - } - - static bool CompareX(const Edge &e1, const Edge &e2) { - return e1.min_x < e2.min_x; - } - }; - - inline void AllocImgBuffer(size_t p_bufsize) { if ( buffer ) DumpImgBuffer(); diff --git a/src/zm_poly.h b/src/zm_poly.h index 0514d4756..f2c133c21 100644 --- a/src/zm_poly.h +++ b/src/zm_poly.h @@ -22,7 +22,22 @@ #include "zm_box.h" -class Coord; +struct Edge { + int min_y; + int max_y; + double min_x; + double _1_m; + + static bool CompareYX(const Edge &e1, const Edge &e2) { + if (e1.min_y == e2.min_y) + return e1.min_x < e2.min_x; + return e1.min_y < e2.min_y; + } + + static bool CompareX(const Edge &e1, const Edge &e2) { + return e1.min_x < e2.min_x; + } +}; // // Class used for storing a box, which is defined as a region @@ -30,25 +45,6 @@ class Coord; // class Polygon { protected: - struct Edge { - int min_y; - int max_y; - double min_x; - double _1_m; - - static int CompareYX( const void *p1, const void *p2 ) { - const Edge *e1 = reinterpret_cast(p1), *e2 = reinterpret_cast(p2); - if ( e1->min_y == e2->min_y ) - return int(e1->min_x - e2->min_x); - else - return int(e1->min_y - e2->min_y); - } - static int CompareX( const void *p1, const void *p2 ) { - const Edge *e1 = reinterpret_cast(p1), *e2 = reinterpret_cast(p2); - return int(e1->min_x - e2->min_x); - } - }; - struct Slice { int min_x; int max_x; From eaf91fcf1f04a3410c380fde5717fd5d1b4ba2fc Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Sat, 8 May 2021 23:03:38 +0200 Subject: [PATCH 041/394] zm_define: Include cstddef so we have size_t --- src/zm_define.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/zm_define.h b/src/zm_define.h index 45ace017e..2c23ab3b9 100644 --- a/src/zm_define.h +++ b/src/zm_define.h @@ -29,6 +29,7 @@ #endif #include +#include typedef std::int64_t int64; typedef std::int32_t int32; From 5af6d6af3ddaff89b7c9d3d9ca90be8bf07ecf37 Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Sat, 8 May 2021 22:25:53 +0200 Subject: [PATCH 042/394] Polygon: Use std::vector to store the vertices --- src/zm_image.cpp | 18 ++++----- src/zm_monitor.cpp | 15 ++++---- src/zm_poly.cpp | 92 ++++++++++++++++------------------------------ src/zm_poly.h | 66 ++++++++++----------------------- src/zm_zone.cpp | 46 ++++++++++++----------- 5 files changed, 92 insertions(+), 145 deletions(-) diff --git a/src/zm_image.cpp b/src/zm_image.cpp index d88e2b2a3..f61974004 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -2382,12 +2382,12 @@ void Image::Outline( Rgb colour, const Polygon &polygon ) { } /* Convert the colour's RGBA subpixel order into the image's subpixel order */ - colour = rgb_convert(colour,subpixelorder); + colour = rgb_convert(colour, subpixelorder); - int n_coords = polygon.getNumCoords(); - for ( int j = 0, i = n_coords-1; j < n_coords; i = j++ ) { - const Vector2 &p1 = polygon.getCoord(i); - const Vector2 &p2 = polygon.getCoord(j); + size_t n_coords = polygon.GetVertices().size(); + for (size_t j = 0, i = n_coords - 1; j < n_coords; i = j++) { + const Vector2 &p1 = polygon.GetVertices()[i]; + const Vector2 &p2 = polygon.GetVertices()[j]; int x1 = p1.x_; int x2 = p2.x_; @@ -2466,12 +2466,12 @@ void Image::Fill(Rgb colour, int density, const Polygon &polygon) { /* Convert the colour's RGBA subpixel order into the image's subpixel order */ colour = rgb_convert(colour, subpixelorder); - int n_coords = polygon.getNumCoords(); + size_t n_coords = polygon.GetVertices().size(); int n_global_edges = 0; Edge global_edges[n_coords]; - for ( int j = 0, i = n_coords-1; j < n_coords; i = j++ ) { - const Vector2 &p1 = polygon.getCoord(i); - const Vector2 &p2 = polygon.getCoord(j); + for (size_t j = 0, i = n_coords - 1; j < n_coords; i = j++) { + const Vector2 &p1 = polygon.GetVertices()[i]; + const Vector2 &p2 = polygon.GetVertices()[j]; int x1 = p1.x_; int x2 = p2.x_; diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index d77638968..56cbf868a 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1482,20 +1482,21 @@ void Monitor::DumpZoneImage(const char *zone_string) { } for (const Zone &zone : zones) { - if ( exclude_id && (!extra_colour || extra_zone.getNumCoords()) && zone.Id() == exclude_id ) + if (exclude_id && (!extra_colour || !extra_zone.GetVertices().empty()) && zone.Id() == exclude_id) { continue; + } Rgb colour; - if ( exclude_id && !extra_zone.getNumCoords() && zone.Id() == exclude_id ) { + if (exclude_id && extra_zone.GetVertices().empty() && zone.Id() == exclude_id) { colour = extra_colour; } else { - if ( zone.IsActive() ) { + if (zone.IsActive()) { colour = kRGBRed; - } else if ( zone.IsInclusive() ) { + } else if (zone.IsInclusive()) { colour = kRGBOrange; - } else if ( zone.IsExclusive() ) { + } else if (zone.IsExclusive()) { colour = kRGBPurple; - } else if ( zone.IsPreclusive() ) { + } else if (zone.IsPreclusive()) { colour = kRGBBlue; } else { colour = kRGBWhite; @@ -1505,7 +1506,7 @@ void Monitor::DumpZoneImage(const char *zone_string) { zone_image->Outline(colour, zone.GetPolygon()); } - if ( extra_zone.getNumCoords() ) { + if (!extra_zone.GetVertices().empty()) { zone_image->Fill(extra_colour, 2, extra_zone); zone_image->Outline(extra_colour, extra_zone); } diff --git a/src/zm_poly.cpp b/src/zm_poly.cpp index 8f52ff14e..fbeacd06b 100644 --- a/src/zm_poly.cpp +++ b/src/zm_poly.cpp @@ -21,84 +21,54 @@ #include +Polygon::Polygon(std::vector vertices) : vertices_(std::move(vertices)) { + int min_x = -1; + int max_x = -1; + int min_y = -1; + int max_y = -1; + for (const Vector2 &vertex : vertices_) { + if (min_x == -1 || vertex.x_ < min_x) + min_x = vertex.x_; + if (max_x == -1 || vertex.x_ > max_x) + max_x = vertex.x_; + if (min_y == -1 || vertex.y_ < min_y) + min_y = vertex.y_; + if (max_y == -1 || vertex.y_ > max_y) + max_y = vertex.y_; + } + extent = Box({min_x, min_y}, {max_x, max_y}); + + calcArea(); + calcCentre(); +} + void Polygon::calcArea() { double float_area = 0.0L; - for (int i = 0, j = n_coords - 1; i < n_coords; j = i++) { - double trap_area = ((coords[i].x_ - coords[j].x_) * ((coords[i].y_ + coords[j].y_))) / 2.0L; + for (size_t i = 0, j = vertices_.size() - 1; i < vertices_.size(); j = i++) { + double trap_area = ((vertices_[i].x_ - vertices_[j].x_) * ((vertices_[i].y_ + vertices_[j].y_))) / 2.0L; float_area += trap_area; - //printf( "%.2f (%.2f)\n", float_area, trap_area ); } area = (int) round(fabs(float_area)); } void Polygon::calcCentre() { - if (!area && n_coords) + if (!area && !vertices_.empty()) calcArea(); double float_x = 0.0L, float_y = 0.0L; - for (int i = 0, j = n_coords - 1; i < n_coords; j = i++) { - float_x += ((coords[i].y_ - coords[j].y_) * ((coords[i].x_ * 2) + (coords[i].x_ * coords[j].x_) + (coords[j].x_ * 2))); - float_y += ((coords[j].x_ - coords[i].x_) * ((coords[i].y_ * 2) + (coords[i].y_ * coords[j].y_) + (coords[j].y_ * 2))); + for (size_t i = 0, j = vertices_.size() - 1; i < vertices_.size(); j = i++) { + float_x += ((vertices_[i].y_ - vertices_[j].y_) * ((vertices_[i].x_ * 2) + (vertices_[i].x_ * vertices_[j].x_) + (vertices_[j].x_ * 2))); + float_y += ((vertices_[j].x_ - vertices_[i].x_) * ((vertices_[i].y_ * 2) + (vertices_[i].y_ * vertices_[j].y_) + (vertices_[j].y_ * 2))); } float_x /= (6 * area); float_y /= (6 * area); centre = Vector2((int) round(float_x), (int) round(float_y)); } -Polygon::Polygon(int p_n_coords, const Vector2 *p_coords) : n_coords(p_n_coords) { - coords = new Vector2[n_coords]; - - int min_x = -1; - int max_x = -1; - int min_y = -1; - int max_y = -1; - for (int i = 0; i < n_coords; i++) { - coords[i] = p_coords[i]; - if (min_x == -1 || coords[i].x_ < min_x) - min_x = coords[i].x_; - if (max_x == -1 || coords[i].x_ > max_x) - max_x = coords[i].x_; - if (min_y == -1 || coords[i].y_ < min_y) - min_y = coords[i].y_; - if (max_y == -1 || coords[i].y_ > max_y) - max_y = coords[i].y_; - } - extent = Box({min_x, min_y}, {max_x, max_y}); - calcArea(); - calcCentre(); -} - -Polygon::Polygon(const Polygon &p_polygon) : - n_coords(p_polygon.n_coords), - extent(p_polygon.extent), - area(p_polygon.area), - centre(p_polygon.centre) { - coords = new Vector2[n_coords]; - for (int i = 0; i < n_coords; i++) { - coords[i] = p_polygon.coords[i]; - } -} - -Polygon &Polygon::operator=(const Polygon &p_polygon) { - n_coords = p_polygon.n_coords; - - Vector2 *new_coords = new Vector2[n_coords]; - for (int i = 0; i < n_coords; i++) { - new_coords[i] = p_polygon.coords[i]; - } - delete[] coords; - coords = new_coords; - - extent = p_polygon.extent; - area = p_polygon.area; - centre = p_polygon.centre; - return *this; -} - -bool Polygon::isInside(const Vector2 &coord) const { +bool Polygon::Contains(const Vector2 &coord) const { bool inside = false; - for (int i = 0, j = n_coords - 1; i < n_coords; j = i++) { - if ((((coords[i].y_ <= coord.y_) && (coord.y_ < coords[j].y_)) || ((coords[j].y_ <= coord.y_) && (coord.y_ < coords[i].y_))) - && (coord.x_ < (coords[j].x_ - coords[i].x_) * (coord.y_ - coords[i].y_) / (coords[j].y_ - coords[i].y_) + coords[i].x_)) { + for (size_t i = 0, j = vertices_.size() - 1; i < vertices_.size(); j = i++) { + if ((((vertices_[i].y_ <= coord.y_) && (coord.y_ < vertices_[j].y_)) || ((vertices_[j].y_ <= coord.y_) && (coord.y_ < vertices_[i].y_))) + && (coord.x_ < (vertices_[j].x_ - vertices_[i].x_) * (coord.y_ - vertices_[i].y_) / (vertices_[j].y_ - vertices_[i].y_) + vertices_[i].x_)) { inside = !inside; } } diff --git a/src/zm_poly.h b/src/zm_poly.h index f2c133c21..bbfd2fc0c 100644 --- a/src/zm_poly.h +++ b/src/zm_poly.h @@ -21,6 +21,7 @@ #define ZM_POLY_H #include "zm_box.h" +#include struct Edge { int min_y; @@ -44,50 +45,12 @@ struct Edge { // defined by two coordinates // class Polygon { -protected: - struct Slice { - int min_x; - int max_x; - int n_edges; - int *edges; + public: + Polygon() : area(0) {} + explicit Polygon(std::vector vertices); - Slice() { - min_x = 0; - max_x = 0; - n_edges = 0; - edges = nullptr; - } - ~Slice() { - delete edges; - } - }; - -protected: - int n_coords; - Vector2 *coords; - Box extent; - int area; - Vector2 centre; - -protected: - void initialiseEdges(); - void calcArea(); - void calcCentre(); - -public: - inline Polygon() : n_coords(0), coords(nullptr), area(0) { - } - Polygon(int p_n_coords, const Vector2 *p_coords); - Polygon(const Polygon &p_polygon); - ~Polygon() { - delete[] coords; - } - - Polygon &operator=( const Polygon &p_polygon ); - - inline int getNumCoords() const { return n_coords; } - inline const Vector2 &getCoord( int index ) const { - return coords[index]; + const std::vector &GetVertices() const { + return vertices_; } const Box &Extent() const { return extent; } @@ -96,11 +59,22 @@ public: int LoY(int p_lo_y) { return extent.LoY(p_lo_y); } int HiY(int p_hi_y) { return extent.HiY(p_hi_y); } - inline int Area() const { return area; } - inline const Vector2 &Centre() const { + int Area() const { return area; } + const Vector2 &Centre() const { return centre; } - bool isInside(const Vector2 &coord) const; + + bool Contains(const Vector2 &coord) const; + + private: + void calcArea(); + void calcCentre(); + + private: + std::vector vertices_; + Box extent; + int area; + Vector2 centre; }; #endif // ZM_POLY_H diff --git a/src/zm_zone.cpp b/src/zm_zone.cpp index 2ff72b330..2f8f51538 100644 --- a/src/zm_zone.cpp +++ b/src/zm_zone.cpp @@ -748,37 +748,39 @@ bool Zone::CheckAlarms(const Image *delta_image) { bool Zone::ParsePolygonString(const char *poly_string, Polygon &polygon) { char *str = (char *)poly_string; - int n_coords = 0; int max_n_coords = strlen(str)/4; - Vector2 *coords = new Vector2[max_n_coords]; + + std::vector vertices; + vertices.reserve(max_n_coords); + while (*str != '\0') { char *cp = strchr(str, ','); if (!cp) { Error("Bogus coordinate %s found in polygon string", str); break; - } + } + int x = atoi(str); - int y = atoi(cp+1); + int y = atoi(cp + 1); Debug(3, "Got coordinate %d,%d from polygon string", x, y); - coords[n_coords++] = Vector2(x, y); + vertices.emplace_back(x, y); - char *ws = strchr(cp+2, ' '); - if (ws) - str = ws+1; - else + char *ws = strchr(cp + 2, ' '); + if (ws) { + str = ws + 1; + } else { break; - } // end while ! end of string - - if (n_coords > 2) { - Debug(3, "Successfully parsed polygon string %s", str); - polygon = Polygon(n_coords, coords); - } else { - Error("Not enough coordinates to form a polygon!"); - n_coords = 0; + } } - delete[] coords; - return n_coords ? true : false; + if (vertices.size() > 2) { + Debug(3, "Successfully parsed polygon string %s", str); + polygon = Polygon(vertices); + } else { + Error("Not enough coordinates to form a polygon!"); + } + + return !vertices.empty(); } // end bool Zone::ParsePolygonString(const char *poly_string, Polygon &polygon) bool Zone::ParseZoneString(const char *zone_string, int &zone_id, int &colour, Polygon &polygon) { @@ -937,9 +939,9 @@ bool Zone::DumpSettings(char *output, bool /*verbose*/) const { type==INACTIVE?"Inactive":( type==PRIVACY?"Privacy":"Unknown" )))))); - sprintf( output+strlen(output), " Shape : %d points\n", polygon.getNumCoords() ); - for ( int i = 0; i < polygon.getNumCoords(); i++ ) { - sprintf(output+strlen(output), " %i: %d,%d\n", i, polygon.getCoord( i ).x_, polygon.getCoord(i ).y_ ); + sprintf( output+strlen(output), " Shape : %zu points\n", polygon.GetVertices().size() ); + for (size_t i = 0; i < polygon.GetVertices().size(); i++) { + sprintf(output + strlen(output), " %zu: %d,%d\n", i, polygon.GetVertices()[i].x_, polygon.GetVertices()[i].y_); } sprintf( output+strlen(output), " Alarm RGB : %06x\n", alarm_rgb ); sprintf( output+strlen(output), " Check Method: %d - %s\n", check_method, From 3c85d63655e776651c8d51ebadfb7015d7e210bf Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Fri, 14 May 2021 15:11:50 +0200 Subject: [PATCH 043/394] Polygon: Implement clipping to a boundary box Using the Sutherland-Hodgman algorithms convex and concave subject polygons can be clipped by convex clip polygons. For now we only need clipping to rectangles (Box), so limit our implementation to that. If needed this can be trivially extended to convex clip polygons (a check whether the clip polygon is actually convex has to be added). If convex clip polygons are needed we have to switch to e.g the Vatti algorithm. --- src/zm_box.h | 23 ++++++++++++- src/zm_line.h | 64 +++++++++++++++++++++++++++++++++++ src/zm_poly.cpp | 33 ++++++++++++++++++ src/zm_poly.h | 18 +++++----- src/zm_vector2.h | 15 +++++++++ tests/CMakeLists.txt | 1 + tests/zm_box.cpp | 2 ++ tests/zm_poly.cpp | 79 ++++++++++++++++++++++++++++++++++++++++++++ tests/zm_vector2.cpp | 12 +++++++ 9 files changed, 236 insertions(+), 11 deletions(-) create mode 100644 src/zm_line.h create mode 100644 tests/zm_poly.cpp diff --git a/src/zm_box.h b/src/zm_box.h index 971fbc36d..7094f2cd0 100644 --- a/src/zm_box.h +++ b/src/zm_box.h @@ -20,8 +20,10 @@ #ifndef ZM_BOX_H #define ZM_BOX_H +#include "zm_line.h" #include "zm_vector2.h" #include +#include // // Class used for storing a box, which is defined as a region @@ -48,7 +50,26 @@ class Box { return {mid_x, mid_y}; } - bool Contains(const Vector2 &coord) const { + // Get vertices of the box in a counter-clockwise order + std::vector Vertices() const { + return {lo_, {hi_.x_, lo_.y_}, hi_, {lo_.x_, hi_.y_}}; + } + + // Get edges of the box in a counter-clockwise order + std::vector Edges() const { + std::vector edges; + edges.reserve(4); + + std::vector v = Vertices(); + edges.emplace_back(v[0], v[1]); + edges.emplace_back(v[1], v[2]); + edges.emplace_back(v[2], v[3]); + edges.emplace_back(v[3], v[0]); + + return edges; + } + + bool Contains(const Vector2 &coord) const { return (coord.x_ >= lo_.x_ && coord.x_ <= hi_.x_ && coord.y_ >= lo_.y_ && coord.y_ <= hi_.y_); } diff --git a/src/zm_line.h b/src/zm_line.h new file mode 100644 index 000000000..ec09fc48e --- /dev/null +++ b/src/zm_line.h @@ -0,0 +1,64 @@ +/* + * This file is part of the ZoneMinder Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef ZONEMINDER_SRC_ZM_LINE_H_ +#define ZONEMINDER_SRC_ZM_LINE_H_ + +#include "zm_vector2.h" + +// Represents a part of a line bounded by two end points +class LineSegment { + public: + LineSegment(Vector2 start, Vector2 end) : start_(start), end_(end) {} + + public: + Vector2 start_; + Vector2 end_; +}; + +// Represents an infinite line +class Line { + public: + Line(Vector2 p1, Vector2 p2) : position_(p1), direction_(p2 - p1) {} + explicit Line(LineSegment segment) : Line(segment.start_, segment.end_) {}; + + bool IsPointLeftOfOrColinear(Vector2 p) const { + int32 det = direction_.Determinant(p - position_); + + return det >= 0; + } + + Vector2 Intersection(Line const &line) const { + int32 det = direction_.Determinant(line.direction_); + + if (det == 0) { + // lines are parallel or overlap, no intersection + return Vector2::Inf(); + } + + Vector2 c = line.position_ - position_; + double t = c.Determinant(line.direction_) / static_cast(det); + + return position_ + direction_ * t; + } + + private: + Vector2 position_; + Vector2 direction_; +}; + +#endif //ZONEMINDER_SRC_ZM_LINE_H_ diff --git a/src/zm_poly.cpp b/src/zm_poly.cpp index fbeacd06b..0bef5a4fa 100644 --- a/src/zm_poly.cpp +++ b/src/zm_poly.cpp @@ -19,6 +19,7 @@ #include "zm_poly.h" +#include "zm_line.h" #include Polygon::Polygon(std::vector vertices) : vertices_(std::move(vertices)) { @@ -74,3 +75,35 @@ bool Polygon::Contains(const Vector2 &coord) const { } return inside; } + +// Clip the polygon to a rectangular boundary box using the Sutherland-Hodgman algorithm +Polygon Polygon::GetClipped(const Box &boundary) { + std::vector clipped_vertices = vertices_; + + for (LineSegment const& clip_edge : boundary.Edges()) { + // convert our line segment to an infinite line + Line clip_line = Line(clip_edge); + + std::vector to_clip = clipped_vertices; + clipped_vertices.clear(); + + for (size_t i = 0; i < to_clip.size(); ++i) { + Vector2 vert1 = to_clip[i]; + Vector2 vert2 = to_clip[(i + 1) % to_clip.size()]; + + bool vert1_left = clip_line.IsPointLeftOfOrColinear(vert1); + bool vert2_left = clip_line.IsPointLeftOfOrColinear(vert2); + + if (vert2_left) { + if (!vert1_left) { + clipped_vertices.push_back(Line(vert1, vert2).Intersection(clip_line)); + } + clipped_vertices.push_back(vert2); + } else if (vert1_left) { + clipped_vertices.push_back(Line(vert1, vert2).Intersection(clip_line)); + } + } + } + + return Polygon(clipped_vertices); +} diff --git a/src/zm_poly.h b/src/zm_poly.h index bbfd2fc0c..36d7048cc 100644 --- a/src/zm_poly.h +++ b/src/zm_poly.h @@ -40,10 +40,6 @@ struct Edge { } }; -// -// Class used for storing a box, which is defined as a region -// defined by two coordinates -// class Polygon { public: Polygon() : area(0) {} @@ -54,18 +50,20 @@ class Polygon { } const Box &Extent() const { return extent; } - int LoX(int p_lo_x) { return extent.LoX(p_lo_x); } - int HiX(int p_hi_x) { return extent.HiX(p_hi_x); } - int LoY(int p_lo_y) { return extent.LoY(p_lo_y); } - int HiY(int p_hi_y) { return extent.HiY(p_hi_y); } + int32 LoX(int p_lo_x) { return extent.LoX(p_lo_x); } + int32 HiX(int p_hi_x) { return extent.HiX(p_hi_x); } + int32 LoY(int p_lo_y) { return extent.LoY(p_lo_y); } + int32 HiY(int p_hi_y) { return extent.HiY(p_hi_y); } - int Area() const { return area; } + int32 Area() const { return area; } const Vector2 &Centre() const { return centre; } bool Contains(const Vector2 &coord) const; + Polygon GetClipped(const Box &boundary); + private: void calcArea(); void calcCentre(); @@ -73,7 +71,7 @@ class Polygon { private: std::vector vertices_; Box extent; - int area; + int32 area; Vector2 centre; }; diff --git a/src/zm_vector2.h b/src/zm_vector2.h index 522eee51e..0daa51d77 100644 --- a/src/zm_vector2.h +++ b/src/zm_vector2.h @@ -21,6 +21,8 @@ #define ZM_VECTOR2_H #include "zm_define.h" +#include +#include // // Class used for storing an x,y pair, i.e. a coordinate/vector @@ -30,6 +32,11 @@ class Vector2 { Vector2() : x_(0), y_(0) {} Vector2(int32 x, int32 y) : x_(x), y_(y) {} + static Vector2 Inf() { + static const Vector2 inf = {std::numeric_limits::max(), std::numeric_limits::max()}; + return inf; + } + static Vector2 Range(const Vector2 &coord1, const Vector2 &coord2) { Vector2 result((coord1.x_ - coord2.x_) + 1, (coord1.y_ - coord2.y_) + 1); return result; @@ -50,6 +57,9 @@ class Vector2 { Vector2 operator-(const Vector2 &rhs) const { return {x_ - rhs.x_, y_ - rhs.y_}; } + Vector2 operator*(double rhs) const { + return {static_cast(std::lround(x_ * rhs)), static_cast(std::lround(y_ * rhs))}; + } Vector2 &operator+=(const Vector2 &rhs) { x_ += rhs.x_; @@ -62,6 +72,11 @@ class Vector2 { return *this; } + // Calculated the determinant of the 2x2 matrix as given by [[x_, y_], [v.x_y, v.y_]] + int32 Determinant(Vector2 const &v) const { + return (x_ * v.y_) - (y_ * v.x_); + } + public: int32 x_; int32 y_; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ea5c24aa5..880a3d3db 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -16,6 +16,7 @@ set(TEST_SOURCES zm_comms.cpp zm_crypt.cpp zm_font.cpp + zm_poly.cpp zm_utils.cpp zm_vector2.cpp) diff --git a/tests/zm_box.cpp b/tests/zm_box.cpp index bd47c0bfb..8141bfa54 100644 --- a/tests/zm_box.cpp +++ b/tests/zm_box.cpp @@ -43,6 +43,8 @@ TEST_CASE("Box: construct from lo and hi") { // Should be: // REQUIRE(b.Centre() == Vector2(3, 3)); REQUIRE(b.Centre() == Vector2(4, 4)); + + REQUIRE(b.Vertices() == std::vector{{1, 1}, {5, 1}, {5, 5}, {1, 5}}); } SECTION("contains") { diff --git a/tests/zm_poly.cpp b/tests/zm_poly.cpp new file mode 100644 index 000000000..817b5a32c --- /dev/null +++ b/tests/zm_poly.cpp @@ -0,0 +1,79 @@ +/* + * This file is part of the ZoneMinder Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "zm_catch2.h" + +#include "zm_poly.h" + +TEST_CASE("Polygon: default constructor") { + Polygon p; + + REQUIRE(p.Area() == 0); + REQUIRE(p.Centre() == Vector2(0, 0)); +} + +TEST_CASE("Polygon: construct from vertices") { + std::vector vertices{{{0, 0}, {6, 0}, {0, 6}}}; + Polygon p(vertices); + + REQUIRE(p.Area() == 18); + //REQUIRE(p.Centre() == Vector2(2, 2)); + // Mathematically should be: + //REQUIRE(p.Extent().Size() == Vector2(6, 6)); + REQUIRE(p.Extent().Size() == Vector2(7, 7)); +} + +TEST_CASE("Polygon: clipping") { + // This a concave polygon in a shape resembling a "W" + std::vector v = { + {3, 1}, + {5, 1}, + {6, 3}, + {7, 1}, + {9, 1}, + {10, 8}, + {8, 8}, + {7, 5}, + {5, 5}, + {4, 8}, + {2, 8} + }; + + Polygon p(v); + + REQUIRE(p.GetVertices().size() == 11); + REQUIRE(p.Extent().Size() == Vector2(9, 8)); + // should be: + // REQUIRE(p.Extent().Size() == Vector2(8, 7)); + // related to Vector2::Range + + SECTION("boundary box larger than polygon") { + Polygon c = p.GetClipped(Box({1, 0}, {11, 9})); + + REQUIRE(c.GetVertices().size() == 11); + REQUIRE(c.Extent().Size() == Vector2(9, 8)); + } + + SECTION("boundary box smaller than polygon") { + Polygon c = p.GetClipped(Box({2, 4}, {10, 7})); + + REQUIRE(c.GetVertices().size() == 8); + REQUIRE(c.Extent().Size() == Vector2(9, 4)); + // should be: + // REQUIRE(c.Extent().Size() == Vector2(8, 3)); + } +} diff --git a/tests/zm_vector2.cpp b/tests/zm_vector2.cpp index 23930f41a..625420486 100644 --- a/tests/zm_vector2.cpp +++ b/tests/zm_vector2.cpp @@ -79,4 +79,16 @@ TEST_CASE("Vector2: arithmetic operators") { c -= {1, 2}; REQUIRE(c == Vector2(0, -1)); } + + SECTION("scalar multiplication") { + c = c * 2; + REQUIRE(c == Vector2(2, 2)); + } +} + +TEST_CASE("Vector2: determinate") { + Vector2 v(1, 1); + REQUIRE(v.Determinant({0, 0}) == 0); + REQUIRE(v.Determinant({1, 1}) == 0); + REQUIRE(v.Determinant({1, 2}) == 1); } From ef7a083891d96f1f107691bcd5e16b2e71606cf9 Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Fri, 14 May 2021 18:35:07 +0200 Subject: [PATCH 044/394] Zone: Actually clip the zone if it larger than the image Until now only the boundary box was manually adjusted without actually clipping the polygon. --- src/zm_zone.cpp | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/zm_zone.cpp b/src/zm_zone.cpp index 2f8f51538..413a407c5 100644 --- a/src/zm_zone.cpp +++ b/src/zm_zone.cpp @@ -23,7 +23,7 @@ #include "zm_fifo_debug.h" #include "zm_monitor.h" -void Zone::Setup( +void Zone::Setup( ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, @@ -122,7 +122,7 @@ void Zone::RecordStats(const Event *event) { "PixelDiff=%d, AlarmPixels=%d, FilterPixels=%d, BlobPixels=%d, " "Blobs=%d, MinBlobSize=%d, MaxBlobSize=%d, " "MinX=%d, MinY=%d, MaxX=%d, MaxY=%d, Score=%d", - monitor->Id(), id, event->Id(), event->Frames(), + monitor->Id(), id, event->Id(), event->Frames(), stats.pixel_diff_, stats.alarm_pixels_, stats.alarm_filter_pixels_, @@ -620,12 +620,12 @@ bool Zone::CheckAlarms(const Image *delta_image) { stats.score_ = 0; return false; } - + if (max_blob_pixels != 0) stats.score_ = (100*stats.alarm_blob_pixels_)/max_blob_pixels; - else + else stats.score_ = (100*stats.alarm_blob_pixels_)/polygon.Area(); - + if (stats.score_ < 1) stats.score_ = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */ Debug(5, "Current score is %d", stats.score_); @@ -885,18 +885,10 @@ std::vector Zone::Load(Monitor *monitor) { polygon.Extent().Hi().x_, polygon.Extent().Hi().y_); - if (polygon.Extent().Lo().x_ < 0) { - polygon.LoX(0); - } - if (polygon.Extent().Hi().x_ >= (int) monitor->Width()) { - polygon.HiX((int) monitor->Width()); - } - if (polygon.Extent().Lo().y_ < 0) { - polygon.LoY(0); - } - if (polygon.Extent().Hi().y_ >= (int) monitor->Height()) { - polygon.HiY((int) monitor->Height()); - } + polygon = polygon.GetClipped(Box( + {0, 0}, + {static_cast(monitor->Width()), static_cast(monitor->Height())} + )); } if ( false && !strcmp( Units, "Percent" ) ) { From dc79ec52c20f653e7787b9c90145a31b7255848f Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Fri, 14 May 2021 20:08:25 +0200 Subject: [PATCH 045/394] Polygon: Remove API to set manually extent The extent has to be calculated from the vertices and shouldn't be modified manually. --- src/zm_box.h | 4 ---- src/zm_poly.h | 9 +-------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/src/zm_box.h b/src/zm_box.h index 7094f2cd0..9b9e33bc2 100644 --- a/src/zm_box.h +++ b/src/zm_box.h @@ -35,11 +35,7 @@ class Box { Box(Vector2 lo, Vector2 hi) : lo_(lo), hi_(hi), size_(Vector2::Range(hi, lo)) {} const Vector2 &Lo() const { return lo_; } - int32 LoX(int lo_x) { return lo_.x_ = lo_x; } - int32 LoY(int lo_y) { return lo_.y_ = lo_y; } const Vector2 &Hi() const { return hi_; } - int32 HiX(int hi_x) { return hi_.x_ = hi_x; } - int32 HiY(int hi_y) { return hi_.y_ = hi_y; } const Vector2 &Size() const { return size_; } int32 Area() const { return size_.x_ * size_.y_; } diff --git a/src/zm_poly.h b/src/zm_poly.h index 36d7048cc..63b85b8da 100644 --- a/src/zm_poly.h +++ b/src/zm_poly.h @@ -50,15 +50,8 @@ class Polygon { } const Box &Extent() const { return extent; } - int32 LoX(int p_lo_x) { return extent.LoX(p_lo_x); } - int32 HiX(int p_hi_x) { return extent.HiX(p_hi_x); } - int32 LoY(int p_lo_y) { return extent.LoY(p_lo_y); } - int32 HiY(int p_hi_y) { return extent.HiY(p_hi_y); } - int32 Area() const { return area; } - const Vector2 &Centre() const { - return centre; - } + const Vector2 &Centre() const { return centre; } bool Contains(const Vector2 &coord) const; From 29488900a1b51c721091584f2b913cb7ca93e3d8 Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Sat, 15 May 2021 00:57:35 +0200 Subject: [PATCH 046/394] Box: Make range calculations mathematically correct --- src/zm_box.h | 2 +- src/zm_image.cpp | 9 +++++---- src/zm_vector2.h | 5 ----- src/zm_zone.cpp | 4 ++-- tests/zm_box.cpp | 13 ++++--------- tests/zm_poly.cpp | 16 ++++------------ 6 files changed, 16 insertions(+), 33 deletions(-) diff --git a/src/zm_box.h b/src/zm_box.h index 9b9e33bc2..22210f2f8 100644 --- a/src/zm_box.h +++ b/src/zm_box.h @@ -32,7 +32,7 @@ class Box { public: Box() = default; - Box(Vector2 lo, Vector2 hi) : lo_(lo), hi_(hi), size_(Vector2::Range(hi, lo)) {} + Box(Vector2 lo, Vector2 hi) : lo_(lo), hi_(hi), size_(hi - lo) {} const Vector2 &Lo() const { return lo_; } const Vector2 &Hi() const { return hi_; } diff --git a/src/zm_image.cpp b/src/zm_image.cpp index f61974004..5fc53253f 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -2389,10 +2389,11 @@ void Image::Outline( Rgb colour, const Polygon &polygon ) { const Vector2 &p1 = polygon.GetVertices()[i]; const Vector2 &p2 = polygon.GetVertices()[j]; - int x1 = p1.x_; - int x2 = p2.x_; - int y1 = p1.y_; - int y2 = p2.y_; + // The last pixel we can draw is width/height - 1. Clamp to that value. + int x1 = ZM::clamp(p1.x_, 0, static_cast(width - 1)); + int x2 = ZM::clamp(p2.x_, 0, static_cast(width - 1)); + int y1 = ZM::clamp(p1.y_, 0, static_cast(height - 1)); + int y2 = ZM::clamp(p2.y_, 0, static_cast(height - 1)); double dx = x2 - x1; double dy = y2 - y1; diff --git a/src/zm_vector2.h b/src/zm_vector2.h index 0daa51d77..c7630ad36 100644 --- a/src/zm_vector2.h +++ b/src/zm_vector2.h @@ -37,11 +37,6 @@ class Vector2 { return inf; } - static Vector2 Range(const Vector2 &coord1, const Vector2 &coord2) { - Vector2 result((coord1.x_ - coord2.x_) + 1, (coord1.y_ - coord2.y_) + 1); - return result; - } - bool operator==(const Vector2 &rhs) const { return (x_ == rhs.x_ && y_ == rhs.y_); } bool operator!=(const Vector2 &rhs) const { return (x_ != rhs.x_ || y_ != rhs.y_); } diff --git a/src/zm_zone.cpp b/src/zm_zone.cpp index 413a407c5..7a73b93ed 100644 --- a/src/zm_zone.cpp +++ b/src/zm_zone.cpp @@ -874,8 +874,8 @@ std::vector Zone::Load(Monitor *monitor) { continue; } - if (polygon.Extent().Lo().x_ < 0 || polygon.Extent().Hi().x_ >= (int) monitor->Width() - || polygon.Extent().Lo().y_ < 0 || polygon.Extent().Hi().y_ >= (int) monitor->Height()) { + if (polygon.Extent().Lo().x_ < 0 || polygon.Extent().Hi().x_ > static_cast(monitor->Width()) + || polygon.Extent().Lo().y_ < 0 || polygon.Extent().Hi().y_ > static_cast(monitor->Height())) { Error("Zone %d/%s for monitor %s extends outside of image dimensions, (%d,%d), (%d,%d), fixing", Id, Name, diff --git a/tests/zm_box.cpp b/tests/zm_box.cpp index 8141bfa54..e85056c85 100644 --- a/tests/zm_box.cpp +++ b/tests/zm_box.cpp @@ -34,15 +34,10 @@ TEST_CASE("Box: construct from lo and hi") { SECTION("basic properties") { REQUIRE(b.Lo() == Vector2(1, 1)); REQUIRE(b.Hi() == Vector2(5, 5)); - // Should be: - // REQUIRE(b.Size() == Vector2(4 ,4)); - REQUIRE(b.Size() == Vector2(5, 5)); - // Should be: - // REQUIRE(b.Area() == 16); - REQUIRE(b.Area() == 25); - // Should be: - // REQUIRE(b.Centre() == Vector2(3, 3)); - REQUIRE(b.Centre() == Vector2(4, 4)); + + REQUIRE(b.Size() == Vector2(4 ,4)); + REQUIRE(b.Area() == 16); + REQUIRE(b.Centre() == Vector2(3, 3)); REQUIRE(b.Vertices() == std::vector{{1, 1}, {5, 1}, {5, 5}, {1, 5}}); } diff --git a/tests/zm_poly.cpp b/tests/zm_poly.cpp index 817b5a32c..8ce775d6c 100644 --- a/tests/zm_poly.cpp +++ b/tests/zm_poly.cpp @@ -31,10 +31,7 @@ TEST_CASE("Polygon: construct from vertices") { Polygon p(vertices); REQUIRE(p.Area() == 18); - //REQUIRE(p.Centre() == Vector2(2, 2)); - // Mathematically should be: - //REQUIRE(p.Extent().Size() == Vector2(6, 6)); - REQUIRE(p.Extent().Size() == Vector2(7, 7)); + REQUIRE(p.Extent().Size() == Vector2(6, 6)); } TEST_CASE("Polygon: clipping") { @@ -56,24 +53,19 @@ TEST_CASE("Polygon: clipping") { Polygon p(v); REQUIRE(p.GetVertices().size() == 11); - REQUIRE(p.Extent().Size() == Vector2(9, 8)); - // should be: - // REQUIRE(p.Extent().Size() == Vector2(8, 7)); - // related to Vector2::Range + REQUIRE(p.Extent().Size() == Vector2(8, 7)); SECTION("boundary box larger than polygon") { Polygon c = p.GetClipped(Box({1, 0}, {11, 9})); REQUIRE(c.GetVertices().size() == 11); - REQUIRE(c.Extent().Size() == Vector2(9, 8)); + REQUIRE(c.Extent().Size() == Vector2(8, 7)); } SECTION("boundary box smaller than polygon") { Polygon c = p.GetClipped(Box({2, 4}, {10, 7})); REQUIRE(c.GetVertices().size() == 8); - REQUIRE(c.Extent().Size() == Vector2(9, 4)); - // should be: - // REQUIRE(c.Extent().Size() == Vector2(8, 3)); + REQUIRE(c.Extent().Size() == Vector2(8, 3)); } } From 09665b139f10afb5e2f68f153d56b828b3dcffc3 Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Sun, 16 May 2021 15:17:40 +0200 Subject: [PATCH 047/394] Monitor: Clip zone polygon to image size when dumping The polygon vertex coordinated of extra_zone are directly parsed from user input. Clip the resulting polygon to the image size so we don't try to draw outside of the image. This is already done for zone polygons stored and loaded from the DB. --- src/zm_monitor.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 56cbf868a..36d04bbec 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1481,6 +1481,10 @@ void Monitor::DumpZoneImage(const char *zone_string) { zone_image->Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB); } + extra_zone = extra_zone.GetClipped(Box({0, 0}, + {static_cast(zone_image->Width()), + static_cast(zone_image->Height())})); + for (const Zone &zone : zones) { if (exclude_id && (!extra_colour || !extra_zone.GetVertices().empty()) && zone.Id() == exclude_id) { continue; From 63cea992a0f28a8a683d5f4159d57c57d5ec2e30 Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Sun, 16 May 2021 15:20:16 +0200 Subject: [PATCH 048/394] Image: Fix a dynamic-stack-buffer-overflow when filling polygons Make sure we don't read past the end of global_edges when i = 0. We are moving the elements backwards so at most n_global_edges - 1 elements can be moved. ==6818==ERROR: AddressSanitizer: dynamic-stack-buffer-overflow on address 0x7ffff888ae00 at pc 0x7fe4fd7be8ae bp 0x7ffff888ac90 sp 0x7ffff888a440 READ of size 96 at 0x7ffff888ae00 thread T0 #0 0x7fe4fd7be8ad in __interceptor_memmove (/lib/x86_64-linux-gnu/libasan.so.5+0x378ad) #1 0x56524b2dba31 in Image::Fill(unsigned int, int, Polygon const&) /root/zoneminder/src/zm_image.cpp:2514 #2 0x56524af55530 in Monitor::DumpZoneImage(char const*) /root/zoneminder/src/zm_monitor.cpp:1510 #3 0x56524aeb38cb in main /root/zoneminder/src/zmu.cpp:574 #4 0x7fe4fb2b009a in __libc_start_main ../csu/libc-start.c:308 #5 0x56524aeb87a9 in _start (/root/zoneminder/cmake-build-relwithdebinfo-remote/src/zmu+0xf87a9) --- src/zm_image.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/zm_image.cpp b/src/zm_image.cpp index 5fc53253f..44311d74a 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -2512,8 +2512,7 @@ void Image::Fill(Rgb colour, int density, const Polygon &polygon) { Debug(9, "Moving global edge"); active_edges[n_active_edges++] = global_edges[i]; if ( i < (n_global_edges-1) ) { - //memcpy( &global_edges[i], &global_edges[i+1], sizeof(*global_edges)*(n_global_edges-i) ); - memmove( &global_edges[i], &global_edges[i+1], sizeof(*global_edges)*(n_global_edges-i) ); + memmove(&global_edges[i], &global_edges[i + 1], sizeof(*global_edges) * (n_global_edges - i - 1)); i--; } n_global_edges--; From b1de2209584d5effd4d6d705efd62bbd7f3731e9 Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Sun, 16 May 2021 16:24:37 +0200 Subject: [PATCH 049/394] Polygon: Perform clip operation on existing object instead of returning a new clipped one --- src/zm_monitor.cpp | 7 +++-- src/zm_poly.cpp | 76 +++++++++++++++++++++++++++------------------- src/zm_poly.h | 8 +++-- src/zm_zone.cpp | 2 +- tests/zm_poly.cpp | 12 ++++---- 5 files changed, 60 insertions(+), 45 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 36d04bbec..179c32b54 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1481,9 +1481,10 @@ void Monitor::DumpZoneImage(const char *zone_string) { zone_image->Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB); } - extra_zone = extra_zone.GetClipped(Box({0, 0}, - {static_cast(zone_image->Width()), - static_cast(zone_image->Height())})); + extra_zone.Clip(Box( + {0, 0}, + {static_cast(zone_image->Width()), static_cast(zone_image->Height())} + )); for (const Zone &zone : zones) { if (exclude_id && (!extra_colour || !extra_zone.GetVertices().empty()) && zone.Id() == exclude_id) { diff --git a/src/zm_poly.cpp b/src/zm_poly.cpp index 0bef5a4fa..6485bc192 100644 --- a/src/zm_poly.cpp +++ b/src/zm_poly.cpp @@ -22,47 +22,56 @@ #include "zm_line.h" #include -Polygon::Polygon(std::vector vertices) : vertices_(std::move(vertices)) { - int min_x = -1; - int max_x = -1; - int min_y = -1; - int max_y = -1; - for (const Vector2 &vertex : vertices_) { - if (min_x == -1 || vertex.x_ < min_x) - min_x = vertex.x_; - if (max_x == -1 || vertex.x_ > max_x) - max_x = vertex.x_; - if (min_y == -1 || vertex.y_ < min_y) - min_y = vertex.y_; - if (max_y == -1 || vertex.y_ > max_y) - max_y = vertex.y_; - } - extent = Box({min_x, min_y}, {max_x, max_y}); - - calcArea(); - calcCentre(); +Polygon::Polygon(std::vector vertices) : vertices_(std::move(vertices)), area(0) { + UpdateExtent(); + UpdateArea(); + UpdateCentre(); } -void Polygon::calcArea() { - double float_area = 0.0L; +void Polygon::UpdateExtent() { + if (vertices_.empty()) + return; + + int min_x = vertices_[0].x_; + int max_x = 0; + int min_y = vertices_[0].y_; + int max_y = 0; + for (const Vector2 &vertex : vertices_) { + min_x = std::min(min_x, vertex.x_); + max_x = std::max(max_x, vertex.x_); + min_y = std::min(min_y, vertex.y_); + max_y = std::max(max_y, vertex.y_); + } + + extent = Box({min_x, min_y}, {max_x, max_y}); +} + +void Polygon::UpdateArea() { + double float_area = 0.0; for (size_t i = 0, j = vertices_.size() - 1; i < vertices_.size(); j = i++) { - double trap_area = ((vertices_[i].x_ - vertices_[j].x_) * ((vertices_[i].y_ + vertices_[j].y_))) / 2.0L; + double trap_area = ((vertices_[i].x_ - vertices_[j].x_) * ((vertices_[i].y_ + vertices_[j].y_))) / 2.0; float_area += trap_area; } - area = (int) round(fabs(float_area)); + + area = static_cast(std::lround(std::fabs(float_area))); } -void Polygon::calcCentre() { +void Polygon::UpdateCentre() { if (!area && !vertices_.empty()) - calcArea(); - double float_x = 0.0L, float_y = 0.0L; + UpdateArea(); + + double float_x = 0.0; + double float_y = 0.0; for (size_t i = 0, j = vertices_.size() - 1; i < vertices_.size(); j = i++) { - float_x += ((vertices_[i].y_ - vertices_[j].y_) * ((vertices_[i].x_ * 2) + (vertices_[i].x_ * vertices_[j].x_) + (vertices_[j].x_ * 2))); - float_y += ((vertices_[j].x_ - vertices_[i].x_) * ((vertices_[i].y_ * 2) + (vertices_[i].y_ * vertices_[j].y_) + (vertices_[j].y_ * 2))); + float_x += ((vertices_[i].y_ - vertices_[j].y_) + * ((vertices_[i].x_ * 2) + (vertices_[i].x_ * vertices_[j].x_) + (vertices_[j].x_ * 2))); + float_y += ((vertices_[j].x_ - vertices_[i].x_) + * ((vertices_[i].y_ * 2) + (vertices_[i].y_ * vertices_[j].y_) + (vertices_[j].y_ * 2))); } float_x /= (6 * area); float_y /= (6 * area); - centre = Vector2((int) round(float_x), (int) round(float_y)); + + centre = Vector2(static_cast(std::lround(float_x)), static_cast(std::lround(float_y))); } bool Polygon::Contains(const Vector2 &coord) const { @@ -77,10 +86,10 @@ bool Polygon::Contains(const Vector2 &coord) const { } // Clip the polygon to a rectangular boundary box using the Sutherland-Hodgman algorithm -Polygon Polygon::GetClipped(const Box &boundary) { +void Polygon::Clip(const Box &boundary) { std::vector clipped_vertices = vertices_; - for (LineSegment const& clip_edge : boundary.Edges()) { + for (LineSegment const &clip_edge : boundary.Edges()) { // convert our line segment to an infinite line Line clip_line = Line(clip_edge); @@ -105,5 +114,8 @@ Polygon Polygon::GetClipped(const Box &boundary) { } } - return Polygon(clipped_vertices); + vertices_ = clipped_vertices; + UpdateExtent(); + UpdateArea(); + UpdateCentre(); } diff --git a/src/zm_poly.h b/src/zm_poly.h index 63b85b8da..0d1981ef2 100644 --- a/src/zm_poly.h +++ b/src/zm_poly.h @@ -40,6 +40,7 @@ struct Edge { } }; +// This class represents convex or concave non-self-intersecting polygons. class Polygon { public: Polygon() : area(0) {} @@ -55,11 +56,12 @@ class Polygon { bool Contains(const Vector2 &coord) const; - Polygon GetClipped(const Box &boundary); + void Clip(const Box &boundary); private: - void calcArea(); - void calcCentre(); + void UpdateExtent(); + void UpdateArea(); + void UpdateCentre(); private: std::vector vertices_; diff --git a/src/zm_zone.cpp b/src/zm_zone.cpp index 7a73b93ed..41174857f 100644 --- a/src/zm_zone.cpp +++ b/src/zm_zone.cpp @@ -885,7 +885,7 @@ std::vector Zone::Load(Monitor *monitor) { polygon.Extent().Hi().x_, polygon.Extent().Hi().y_); - polygon = polygon.GetClipped(Box( + polygon.Clip(Box( {0, 0}, {static_cast(monitor->Width()), static_cast(monitor->Height())} )); diff --git a/tests/zm_poly.cpp b/tests/zm_poly.cpp index 8ce775d6c..c7e37673b 100644 --- a/tests/zm_poly.cpp +++ b/tests/zm_poly.cpp @@ -56,16 +56,16 @@ TEST_CASE("Polygon: clipping") { REQUIRE(p.Extent().Size() == Vector2(8, 7)); SECTION("boundary box larger than polygon") { - Polygon c = p.GetClipped(Box({1, 0}, {11, 9})); + p.Clip(Box({1, 0}, {11, 9})); - REQUIRE(c.GetVertices().size() == 11); - REQUIRE(c.Extent().Size() == Vector2(8, 7)); + REQUIRE(p.GetVertices().size() == 11); + REQUIRE(p.Extent().Size() == Vector2(8, 7)); } SECTION("boundary box smaller than polygon") { - Polygon c = p.GetClipped(Box({2, 4}, {10, 7})); + p.Clip(Box({2, 4}, {10, 7})); - REQUIRE(c.GetVertices().size() == 8); - REQUIRE(c.Extent().Size() == Vector2(8, 3)); + REQUIRE(p.GetVertices().size() == 8); + REQUIRE(p.Extent().Size() == Vector2(8, 3)); } } From bd7a4fd552bdf85bd043391f6784f9fb7b0cbcaf Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 16 May 2021 13:53:56 -0400 Subject: [PATCH 050/394] Add closing the codec to hopefully free the file handles in use in hwaccel --- src/zm_videostore.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 0ecac9609..ac84a5692 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -674,6 +674,7 @@ VideoStore::~VideoStore() { if ( video_out_stream ) { video_in_ctx = nullptr; + avcodec_close(video_out_ctx); Debug(4, "Freeing video_out_ctx"); avcodec_free_context(&video_out_ctx); } // end if video_out_stream @@ -688,6 +689,7 @@ VideoStore::~VideoStore() { if ( audio_out_ctx ) { Debug(4, "Success closing audio_out_ctx"); + avcodec_close(audio_out_ctx); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) avcodec_free_context(&audio_out_ctx); #endif From e18f9676cd7b83c7398b9d501875099c8c96b75f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 16 May 2021 13:55:01 -0400 Subject: [PATCH 051/394] Bump version to new 1.37 dev version --- distros/redhat/zoneminder.spec | 2 +- version | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index bb95723b7..5cfa7175a 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -31,7 +31,7 @@ %global _hardened_build 1 Name: zoneminder -Version: 1.36.0 +Version: 1.37.0 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons diff --git a/version b/version index 39fc130ef..bf50e910e 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.36.0 +1.37.0 From 2d9161551d9524baaa36cde4b34e5288f8787492 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 16 May 2021 17:05:40 -0400 Subject: [PATCH 052/394] code style and remove orientation setting when encoding. frames are already rotated --- src/zm_videostore.cpp | 300 ++++++++++++++++++------------------------ 1 file changed, 131 insertions(+), 169 deletions(-) diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 0ecac9609..080cc0998 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -115,7 +115,7 @@ bool VideoStore::open() { Debug(1, "Opening video storage stream %s format: %s", filename, format); int ret = avformat_alloc_output_context2(&oc, nullptr, nullptr, filename); - if ( ret < 0 ) { + if (ret < 0) { Warning( "Could not create video storage stream %s as no out ctx" " could be assigned based on filename: %s", @@ -123,9 +123,9 @@ bool VideoStore::open() { } // Couldn't deduce format from filename, trying from format name - if ( !oc ) { + if (!oc) { avformat_alloc_output_context2(&oc, nullptr, format, filename); - if ( !oc ) { + if (!oc) { Error( "Could not create video storage stream %s as no out ctx" " could not be assigned based on filename or format %s", @@ -142,11 +142,11 @@ bool VideoStore::open() { out_format = oc->oformat; out_format->flags |= AVFMT_TS_NONSTRICT; // allow non increasing dts - if ( video_in_stream ) { + if (video_in_stream) { #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) zm_dump_codecpar(video_in_stream->codecpar); #endif - if ( monitor->GetOptVideoWriter() == Monitor::PASSTHROUGH ) { + if (monitor->GetOptVideoWriter() == Monitor::PASSTHROUGH) { // Don't care what codec, just copy parameters video_out_ctx = avcodec_alloc_context3(nullptr); // There might not be a useful video_in_stream. v4l in might not populate this very @@ -155,12 +155,12 @@ bool VideoStore::open() { #else ret = avcodec_copy_context(video_out_ctx, video_in_ctx); #endif - if ( ret < 0 ) { + if (ret < 0) { Error("Could not initialize ctx parameters"); return false; } video_out_ctx->pix_fmt = fix_deprecated_pix_fmt(video_out_ctx->pix_fmt); - if ( oc->oformat->flags & AVFMT_GLOBALHEADER ) { + if (oc->oformat->flags & AVFMT_GLOBALHEADER) { #if LIBAVCODEC_VERSION_CHECK(56, 35, 0, 64, 0) video_out_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; #else @@ -168,19 +168,19 @@ bool VideoStore::open() { #endif } video_out_ctx->time_base = video_in_ctx->time_base; - if ( ! (video_out_ctx->time_base.num && video_out_ctx->time_base.den) ) { + if (!(video_out_ctx->time_base.num && video_out_ctx->time_base.den)) { Debug(2,"No timebase found in video in context, defaulting to Q"); video_out_ctx->time_base = AV_TIME_BASE_Q; } - } else if ( monitor->GetOptVideoWriter() == Monitor::ENCODE ) { + } else if (monitor->GetOptVideoWriter() == Monitor::ENCODE) { int wanted_codec = monitor->OutputCodec(); - if ( !wanted_codec ) { + if (!wanted_codec) { // default to h264 //Debug(2, "Defaulting to H264"); //wanted_codec = AV_CODEC_ID_H264; // FIXME what is the optimal codec? Probably low latency h264 which is effectively mjpeg } else { - if ( AV_CODEC_ID_H264 != 27 and wanted_codec > 3 ) { + if (AV_CODEC_ID_H264 != 27 and wanted_codec > 3) { // Older ffmpeg had AV_CODEC_ID_MPEG2VIDEO_XVMC at position 3 has been deprecated wanted_codec += 1; } @@ -233,14 +233,14 @@ bool VideoStore::open() { video_out_ctx->height = monitor->Height(); video_out_ctx->codec_type = AVMEDIA_TYPE_VIDEO; - if ( video_out_ctx->codec_id == AV_CODEC_ID_H264 ) { + if (video_out_ctx->codec_id == AV_CODEC_ID_H264) { video_out_ctx->bit_rate = 2000000; video_out_ctx->gop_size = 12; video_out_ctx->max_b_frames = 1; - } else if ( video_out_ctx->codec_id == AV_CODEC_ID_MPEG2VIDEO ) { + } else if (video_out_ctx->codec_id == AV_CODEC_ID_MPEG2VIDEO) { /* just for testing, we also add B frames */ video_out_ctx->max_b_frames = 2; - } else if ( video_out_ctx->codec_id == AV_CODEC_ID_MPEG1VIDEO ) { + } else if (video_out_ctx->codec_id == AV_CODEC_ID_MPEG1VIDEO) { /* Needed to avoid using macroblocks in which some coeffs overflow. * This does not happen with normal video, it just happens here as * the motion of the chroma plane does not match the luma plane. */ @@ -281,7 +281,7 @@ bool VideoStore::open() { } } av_buffer_unref(&hw_frames_ref); - } + } // end if hwdevice_type != NONE #endif AVDictionary *opts = 0; @@ -289,7 +289,7 @@ bool VideoStore::open() { Debug(2, "Options? %s", Options.c_str()); ret = av_dict_parse_string(&opts, Options.c_str(), "=", ",#\n", 0); if (ret < 0) { - Warning("Could not parse ffmpeg encoder options list '%s'\n", Options.c_str()); + Warning("Could not parse ffmpeg encoder options list '%s'", Options.c_str()); } else { AVDictionaryEntry *e = nullptr; while ((e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != nullptr) { @@ -318,16 +318,15 @@ bool VideoStore::open() { Warning("Encoder Option %s not recognized by ffmpeg codec", e->key); } if (video_out_codec) break; +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + // We allocate and copy in newer ffmpeg, so need to free it avcodec_free_context(&video_out_ctx); if (hw_device_ctx) av_buffer_unref(&hw_device_ctx); +#endif } // end foreach codec if (!video_out_codec) { Error("Can't open video codec!"); -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - // We allocate and copy in newer ffmpeg, so need to free it - avcodec_free_context(&video_out_ctx); -#endif return false; } // end if can't open codec Debug(2, "Success opening codec"); @@ -336,7 +335,7 @@ bool VideoStore::open() { } // end if video_in_stream video_out_stream = avformat_new_stream(oc, video_out_codec); - if ( !video_out_stream ) { + if (!video_out_stream) { Error("Unable to create video out stream"); return false; } @@ -350,40 +349,14 @@ bool VideoStore::open() { #else avcodec_copy_context(video_out_stream->codec, video_out_ctx); #endif - // Only set orientation if doing passthrough, otherwise the frame image will be rotated - Monitor::Orientation orientation = monitor->getOrientation(); - if ( orientation ) { - Debug(3, "Have orientation %d", orientation); - if ( orientation == Monitor::ROTATE_0 ) { - } else if ( orientation == Monitor::ROTATE_90 ) { - ret = av_dict_set(&video_out_stream->metadata, "rotate", "90", 0); - if ( ret < 0 ) Warning("%s:%d: title set failed", __FILE__, __LINE__); - } else if ( orientation == Monitor::ROTATE_180 ) { - ret = av_dict_set(&video_out_stream->metadata, "rotate", "180", 0); - if ( ret < 0 ) Warning("%s:%d: title set failed", __FILE__, __LINE__); - } else if ( orientation == Monitor::ROTATE_270 ) { - ret = av_dict_set(&video_out_stream->metadata, "rotate", "270", 0); - if ( ret < 0 ) Warning("%s:%d: title set failed", __FILE__, __LINE__); - } else { - Warning("Unsupported Orientation(%d)", orientation); - } - } // end if orientation video_out_stream->time_base = video_in_stream ? video_in_stream->time_base : AV_TIME_BASE_Q; - if ( audio_in_stream and audio_in_ctx ) { + if (audio_in_stream and audio_in_ctx) { Debug(2, "Have audio_in_stream %p", audio_in_stream); - if ( -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - audio_in_stream->codecpar->codec_id -#else - audio_in_stream->codec->codec_id -#endif - != AV_CODEC_ID_AAC - ) { - + if (CODEC(audio_in_stream)->codec_id != AV_CODEC_ID_AAC) { audio_out_codec = avcodec_find_encoder(AV_CODEC_ID_AAC); - if ( !audio_out_codec ) { + if (!audio_out_codec) { Error("Could not find codec for AAC"); } else { #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) @@ -397,7 +370,7 @@ bool VideoStore::open() { #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) audio_out_ctx = avcodec_alloc_context3(audio_out_codec); - if ( !audio_out_ctx ) { + if (!audio_out_ctx) { Error("could not allocate codec ctx for AAC"); return false; } @@ -407,7 +380,7 @@ bool VideoStore::open() { audio_out_stream = avformat_new_stream(oc, audio_out_codec); audio_out_stream->time_base = audio_in_stream->time_base; - if ( !setup_resampler() ) { + if (!setup_resampler()) { return false; } } // end if found AAC codec @@ -417,7 +390,7 @@ bool VideoStore::open() { // normally we want to pass params from codec in here // but since we are doing audio passthrough we don't care audio_out_stream = avformat_new_stream(oc, audio_out_codec); - if ( !audio_out_stream ) { + if (!audio_out_stream) { Error("Could not allocate new stream"); return false; } @@ -426,7 +399,7 @@ bool VideoStore::open() { #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) // Just use the ctx to copy the parameters over audio_out_ctx = avcodec_alloc_context3(audio_out_codec); - if ( !audio_out_ctx ) { + if (!audio_out_ctx) { Error("Could not allocate new output_context"); return false; } @@ -437,20 +410,20 @@ bool VideoStore::open() { // Copy params from instream to ctx ret = avcodec_parameters_to_context( audio_out_ctx, audio_in_stream->codecpar); - if ( ret < 0 ) { + if (ret < 0) { Error("Unable to copy audio params to ctx %s", av_make_error_string(ret).c_str()); } ret = avcodec_parameters_from_context( audio_out_stream->codecpar, audio_out_ctx); - if ( ret < 0 ) { + if (ret < 0) { Error("Unable to copy audio params to stream %s", av_make_error_string(ret).c_str()); } #else audio_out_ctx = audio_out_stream->codec; ret = avcodec_copy_context(audio_out_ctx, audio_in_stream->codec); - if ( ret < 0 ) { + if (ret < 0) { Error("Unable to copy audio ctx %s", av_make_error_string(ret).c_str()); audio_out_stream = nullptr; @@ -459,7 +432,7 @@ bool VideoStore::open() { audio_out_ctx->codec_tag = 0; #endif - if ( audio_out_ctx->channels > 1 ) { + if (audio_out_ctx->channels > 1) { Warning("Audio isn't mono, changing it."); audio_out_ctx->channels = 1; } else { @@ -467,7 +440,7 @@ bool VideoStore::open() { } } // end if is AAC - if ( oc->oformat->flags & AVFMT_GLOBALHEADER ) { + if (oc->oformat->flags & AVFMT_GLOBALHEADER) { #if LIBAVCODEC_VERSION_CHECK(56, 35, 0, 64, 0) audio_out_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; #else @@ -481,14 +454,14 @@ bool VideoStore::open() { //max_stream_index is 0-based, so add 1 next_dts = new int64_t[max_stream_index+1]; - for ( int i = 0; i <= max_stream_index; i++ ) { + for (int i = 0; i <= max_stream_index; i++) { next_dts[i] = 0; } /* open the out file, if needed */ - if ( !(out_format->flags & AVFMT_NOFILE) ) { + if (!(out_format->flags & AVFMT_NOFILE)) { ret = avio_open2(&oc->pb, filename, AVIO_FLAG_WRITE, nullptr, nullptr); - if ( ret < 0 ) { + if (ret < 0) { Error("Could not open out file '%s': %s", filename, av_make_error_string(ret).c_str()); return false; @@ -496,39 +469,39 @@ bool VideoStore::open() { } zm_dump_stream_format(oc, 0, 0, 1); - if ( audio_out_stream ) zm_dump_stream_format(oc, 1, 0, 1); + if (audio_out_stream) zm_dump_stream_format(oc, 1, 0, 1); AVDictionary *opts = nullptr; std::string option_string = monitor->GetEncoderOptions(); - ret = av_dict_parse_string(&opts, option_string.c_str(), "=", ",\n", 0); - if ( ret < 0 ) { + ret = av_dict_parse_string(&opts, option_string.c_str(), "=", "#,\n", 0); + if (ret < 0) { Warning("Could not parse ffmpeg output options '%s'", option_string.c_str()); } const AVDictionaryEntry *movflags_entry = av_dict_get(opts, "movflags", nullptr, AV_DICT_MATCH_CASE); - if ( !movflags_entry ) { + if (!movflags_entry) { Debug(1, "setting movflags to frag_keyframe+empty_moov"); // Shiboleth reports that this may break seeking in mp4 before it downloads av_dict_set(&opts, "movflags", "frag_keyframe+empty_moov", 0); } else { Debug(1, "using movflags %s", movflags_entry->value); } - if ( (ret = avformat_write_header(oc, &opts)) < 0 ) { + if ((ret = avformat_write_header(oc, &opts)) < 0) { Warning("Unable to set movflags trying with defaults."); ret = avformat_write_header(oc, nullptr); - } else if ( av_dict_count(opts) != 0 ) { + } else if (av_dict_count(opts) != 0) { Info("some options not used, turn on debugging for a list."); AVDictionaryEntry *e = nullptr; - while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != nullptr ) { + while ((e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != nullptr) { Debug(1, "Encoder Option %s=>%s", e->key, e->value); - if ( !e->value ) { + if (!e->value) { av_dict_set(&opts, e->key, nullptr, 0); } } } - if ( opts ) av_dict_free(&opts); - if ( ret < 0 ) { + if (opts) av_dict_free(&opts); + if (ret < 0) { Error("Error occurred when writing out file header to %s: %s", filename, av_make_error_string(ret).c_str()); avio_closep(&oc->pb); @@ -550,13 +523,13 @@ void VideoStore::flush_codecs() { av_init_packet(&pkt); // I got crashes if the codec didn't do DELAY, so let's test for it. - if ( video_out_ctx->codec && ( video_out_ctx->codec->capabilities & + if (video_out_ctx->codec && ( video_out_ctx->codec->capabilities & #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) AV_CODEC_CAP_DELAY #else CODEC_CAP_DELAY #endif - ) ) { + )) { // Put encoder into flushing mode while ((zm_send_frame_receive_packet(video_out_ctx, nullptr, pkt)) > 0) { av_packet_rescale_ts(&pkt, @@ -568,7 +541,7 @@ void VideoStore::flush_codecs() { Debug(1, "Done writing buffered video."); } // end if have delay capability - if ( audio_out_codec ) { + if (audio_out_codec) { // The codec queues data. We need to send a flush command and out // whatever we get. Failures are not fatal. @@ -576,13 +549,13 @@ void VideoStore::flush_codecs() { /* * At the end of the file, we pass the remaining samples to * the encoder. */ - while ( zm_resample_get_delay(resample_ctx, audio_out_ctx->sample_rate) ) { + while (zm_resample_get_delay(resample_ctx, audio_out_ctx->sample_rate)) { zm_resample_audio(resample_ctx, nullptr, out_frame); - if ( zm_add_samples_to_fifo(fifo, out_frame) ) { + if (zm_add_samples_to_fifo(fifo, out_frame)) { // Should probably set the frame size to what is reported FIXME - if ( zm_get_samples_from_fifo(fifo, out_frame) ) { - if ( zm_send_frame_receive_packet(audio_out_ctx, out_frame, pkt) > 0 ) { + if (zm_get_samples_from_fifo(fifo, out_frame)) { + if (zm_send_frame_receive_packet(audio_out_ctx, out_frame, pkt) > 0) { av_packet_rescale_ts(&pkt, audio_out_ctx->time_base, audio_out_stream->time_base); @@ -591,11 +564,10 @@ void VideoStore::flush_codecs() { } } // end if data returned from fifo } - } // end while have buffered samples in the resampler Debug(2, "av_audio_fifo_size = %d", av_audio_fifo_size(fifo)); - while ( av_audio_fifo_size(fifo) > 0 ) { + while (av_audio_fifo_size(fifo) > 0) { /* Take one frame worth of audio samples from the FIFO buffer, * encode it and write it to the output file. */ @@ -603,8 +575,8 @@ void VideoStore::flush_codecs() { frame_size, av_audio_fifo_size(fifo)); // SHould probably set the frame size to what is reported FIXME - if ( av_audio_fifo_read(fifo, (void **)out_frame->data, frame_size) ) { - if ( zm_send_frame_receive_packet(audio_out_ctx, out_frame, pkt) ) { + if (av_audio_fifo_read(fifo, (void **)out_frame->data, frame_size)) { + if (zm_send_frame_receive_packet(audio_out_ctx, out_frame, pkt)) { pkt.stream_index = audio_out_stream->index; av_packet_rescale_ts(&pkt, @@ -622,7 +594,7 @@ void VideoStore::flush_codecs() { #endif while (1) { - if ( 0 >= zm_receive_packet(audio_out_ctx, pkt) ) { + if (0 >= zm_receive_packet(audio_out_ctx, pkt)) { Debug(1, "No more packets"); break; } @@ -637,11 +609,11 @@ void VideoStore::flush_codecs() { } // end flush_codecs VideoStore::~VideoStore() { - if ( oc->pb ) { + if (oc->pb) { flush_codecs(); // Flush Queues - Debug(1, "Flushing interleaved queues"); + Debug(4, "Flushing interleaved queues"); av_interleaved_write_frame(oc, nullptr); Debug(1, "Writing trailer"); @@ -653,17 +625,17 @@ VideoStore::~VideoStore() { } // When will we not be using a file ? - if ( !(out_format->flags & AVFMT_NOFILE) ) { + if (!(out_format->flags & AVFMT_NOFILE)) { /* Close the out file. */ - Debug(2, "Closing"); - if ( int rc = avio_close(oc->pb) ) { + Debug(4, "Closing"); + if (int rc = avio_close(oc->pb)) { Error("Error closing avio %s", av_err2str(rc)); } } else { Debug(3, "Not closing avio because we are not writing to a file."); } oc->pb = nullptr; - } // end if oc->pb + } // end if oc->pb // I wonder if we should be closing the file first. // I also wonder if we really need to be doing all the ctx @@ -671,14 +643,14 @@ VideoStore::~VideoStore() { // Just do a file open/close/writeheader/etc. // What if we were only doing audio recording? - if ( video_out_stream ) { + if (video_out_stream) { video_in_ctx = nullptr; Debug(4, "Freeing video_out_ctx"); avcodec_free_context(&video_out_ctx); } // end if video_out_stream - if ( audio_out_stream ) { + if (audio_out_stream) { #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) // We allocate and copy in newer ffmpeg, so need to free it //avcodec_free_context(&audio_in_ctx); @@ -686,7 +658,7 @@ VideoStore::~VideoStore() { //Debug(4, "Success freeing audio_in_ctx"); audio_in_codec = nullptr; - if ( audio_out_ctx ) { + if (audio_out_ctx) { Debug(4, "Success closing audio_out_ctx"); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) avcodec_free_context(&audio_out_ctx); @@ -694,8 +666,8 @@ VideoStore::~VideoStore() { } #if defined(HAVE_LIBAVRESAMPLE) || defined(HAVE_LIBSWRESAMPLE) - if ( resample_ctx ) { - if ( fifo ) { + if (resample_ctx) { + if (fifo) { av_audio_fifo_free(fifo); fifo = nullptr; } @@ -708,15 +680,15 @@ VideoStore::~VideoStore() { #endif #endif } - if ( in_frame ) { + if (in_frame) { av_frame_free(&in_frame); in_frame = nullptr; } - if ( out_frame ) { + if (out_frame) { av_frame_free(&out_frame); out_frame = nullptr; } - if ( converted_in_samples ) { + if (converted_in_samples) { av_free(converted_in_samples); converted_in_samples = nullptr; } @@ -732,7 +704,7 @@ VideoStore::~VideoStore() { bool VideoStore::setup_resampler() { #if !defined(HAVE_LIBSWRESAMPLE) && !defined(HAVE_LIBAVRESAMPLE) - Error("%s", "Not built with resample library. Cannot do audio conversion to AAC"); + Error("Not built with resample library. Cannot do audio conversion to AAC"); return false; #else int ret; @@ -740,17 +712,14 @@ bool VideoStore::setup_resampler() { #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) // Newer ffmpeg wants to keep everything separate... so have to lookup our own // decoder, can't reuse the one from the camera. - audio_in_codec = - avcodec_find_decoder(audio_in_stream->codecpar->codec_id); + audio_in_codec = avcodec_find_decoder(audio_in_stream->codecpar->codec_id); audio_in_ctx = avcodec_alloc_context3(audio_in_codec); // Copy params from instream to ctx - ret = avcodec_parameters_to_context( - audio_in_ctx, audio_in_stream->codecpar); - if ( ret < 0 ) { + ret = avcodec_parameters_to_context(audio_in_ctx, audio_in_stream->codecpar); + if (ret < 0) { Error("Unable to copy audio params to ctx %s", av_make_error_string(ret).c_str()); } - #else // codec is already open in ffmpeg_camera audio_in_ctx = audio_in_stream->codec; @@ -764,7 +733,7 @@ bool VideoStore::setup_resampler() { #endif // if the codec is already open, nothing is done. - if ( (ret = avcodec_open2(audio_in_ctx, audio_in_codec, nullptr)) < 0 ) { + if ((ret = avcodec_open2(audio_in_ctx, audio_in_codec, nullptr)) < 0) { Error("Can't open audio in codec!"); return false; } @@ -772,7 +741,7 @@ bool VideoStore::setup_resampler() { Debug(2, "Got something other than AAC (%s)", audio_in_codec->name); // Some formats (i.e. WAV) do not produce the proper channel layout - if ( audio_in_ctx->channel_layout == 0 ) { + if (audio_in_ctx->channel_layout == 0) { Debug(2, "Setting input channel layout to mono"); // Perhaps we should not be modifying the audio_in_ctx.... audio_in_ctx->channel_layout = av_get_channel_layout("mono"); @@ -786,7 +755,7 @@ bool VideoStore::setup_resampler() { audio_out_ctx->channel_layout = audio_in_ctx->channel_layout; audio_out_ctx->sample_fmt = audio_in_ctx->sample_fmt; #if LIBAVCODEC_VERSION_CHECK(56, 8, 0, 60, 100) - if ( !audio_out_ctx->channel_layout ) { + if (!audio_out_ctx->channel_layout) { Debug(3, "Correcting channel layout from (%" PRIi64 ") to (%" PRIi64 ")", audio_out_ctx->channel_layout, av_get_default_channel_layout(audio_out_ctx->channels) @@ -794,27 +763,26 @@ bool VideoStore::setup_resampler() { audio_out_ctx->channel_layout = av_get_default_channel_layout(audio_out_ctx->channels); } #endif - if ( audio_out_codec->supported_samplerates ) { + if (audio_out_codec->supported_samplerates) { int found = 0; - for ( unsigned int i = 0; audio_out_codec->supported_samplerates[i]; i++ ) { - if ( audio_out_ctx->sample_rate == - audio_out_codec->supported_samplerates[i] ) { + for (unsigned int i = 0; audio_out_codec->supported_samplerates[i]; i++) { + if (audio_out_ctx->sample_rate == + audio_out_codec->supported_samplerates[i]) { found = 1; break; } } - if ( found ) { + if (found) { Debug(3, "Sample rate is good %d", audio_out_ctx->sample_rate); } else { - audio_out_ctx->sample_rate = - audio_out_codec->supported_samplerates[0]; + audio_out_ctx->sample_rate = audio_out_codec->supported_samplerates[0]; Debug(1, "Sample rate is no good, setting to (%d)", audio_out_codec->supported_samplerates[0]); } } /* check that the encoder supports s16 pcm in */ - if ( !check_sample_fmt(audio_out_codec, audio_out_ctx->sample_fmt) ) { + if (!check_sample_fmt(audio_out_codec, audio_out_ctx->sample_fmt)) { Debug(3, "Encoder does not support sample format %s, setting to FLTP", av_get_sample_fmt_name(audio_out_ctx->sample_fmt)); audio_out_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP; @@ -825,12 +793,12 @@ bool VideoStore::setup_resampler() { AVDictionary *opts = nullptr; // Needed to allow AAC - if ( (ret = av_dict_set(&opts, "strict", "experimental", 0)) < 0 ) { + if ((ret = av_dict_set(&opts, "strict", "experimental", 0)) < 0) { Error("Couldn't set experimental"); } ret = avcodec_open2(audio_out_ctx, audio_out_codec, &opts); av_dict_free(&opts); - if ( ret < 0 ) { + if (ret < 0) { Error("could not open codec (%d) (%s)", ret, av_make_error_string(ret).c_str()); audio_out_codec = nullptr; @@ -886,7 +854,7 @@ bool VideoStore::setup_resampler() { #endif /** Create a new frame to store the audio samples. */ - if ( ! in_frame ) { + if (!in_frame) { if (!(in_frame = zm_av_frame_alloc())) { Error("Could not allocate in frame"); return false; @@ -894,16 +862,16 @@ bool VideoStore::setup_resampler() { } /** Create a new frame to store the audio samples. */ - if ( !(out_frame = zm_av_frame_alloc()) ) { + if (!(out_frame = zm_av_frame_alloc())) { Error("Could not allocate out frame"); av_frame_free(&in_frame); return false; } out_frame->sample_rate = audio_out_ctx->sample_rate; - if ( !(fifo = av_audio_fifo_alloc( + if (!(fifo = av_audio_fifo_alloc( audio_out_ctx->sample_fmt, - audio_out_ctx->channels, 1)) ) { + audio_out_ctx->channels, 1))) { Error("Could not allocate FIFO"); return false; } @@ -916,13 +884,13 @@ bool VideoStore::setup_resampler() { audio_in_ctx->sample_fmt, audio_in_ctx->sample_rate, 0, nullptr); - if ( !resample_ctx ) { + if (!resample_ctx) { Error("Could not allocate resample context"); av_frame_free(&in_frame); av_frame_free(&out_frame); return false; } - if ( (ret = swr_init(resample_ctx)) < 0 ) { + if ((ret = swr_init(resample_ctx)) < 0) { Error("Could not open resampler"); av_frame_free(&in_frame); av_frame_free(&out_frame); @@ -935,7 +903,7 @@ bool VideoStore::setup_resampler() { // Setup the audio resampler resample_ctx = avresample_alloc_context(); - if ( !resample_ctx ) { + if (!resample_ctx) { Error("Could not allocate resample ctx"); av_frame_free(&in_frame); av_frame_free(&out_frame); @@ -959,7 +927,7 @@ bool VideoStore::setup_resampler() { av_opt_set_int(resample_ctx, "out_channels", audio_out_ctx->channels, 0); - if ( (ret = avresample_open(resample_ctx)) < 0 ) { + if ((ret = avresample_open(resample_ctx)) < 0) { Error("Could not open resample ctx"); return false; } else { @@ -984,7 +952,7 @@ bool VideoStore::setup_resampler() { audio_out_ctx->sample_fmt, 0); converted_in_samples = reinterpret_cast(av_malloc(audioSampleBuffer_size)); - if ( !converted_in_samples ) { + if (!converted_in_samples) { Error("Could not allocate converted in sample pointers"); return false; } else { @@ -992,11 +960,11 @@ bool VideoStore::setup_resampler() { } // Setup the data pointers in the AVFrame - if ( avcodec_fill_audio_frame( + if (avcodec_fill_audio_frame( out_frame, audio_out_ctx->channels, audio_out_ctx->sample_fmt, (const uint8_t *)converted_in_samples, - audioSampleBuffer_size, 0) < 0 ) { + audioSampleBuffer_size, 0) < 0) { Error("Could not allocate converted in sample pointers"); return false; } @@ -1046,17 +1014,16 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet video_out_ctx->width, video_out_ctx->height ); - } else if ( !zm_packet->in_frame ) { + } else if (!zm_packet->in_frame) { Debug(4, "Have no in_frame"); if (zm_packet->packet.size and !zm_packet->decoded) { Debug(4, "Decoding"); - if ( !zm_packet->decode(video_in_ctx) ) { + if (!zm_packet->decode(video_in_ctx)) { Debug(2, "unable to decode yet."); return 0; } // Go straight to out frame swscale.Convert(zm_packet->in_frame, out_frame); - } else { Error("Have neither in_frame or image in packet %d!", zm_packet->image_index); @@ -1108,7 +1075,7 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet #endif int64_t in_pts = zm_packet->timestamp.tv_sec * (uint64_t)1000000 + zm_packet->timestamp.tv_usec; - if ( !video_first_pts ) { + if (!video_first_pts) { video_first_pts = in_pts; Debug(2, "No video_first_pts, set to (%" PRId64 ") secs(%" PRIi64 ") usecs(%" PRIi64 ")", video_first_pts, @@ -1144,9 +1111,9 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet ZM_DUMP_PACKET(opkt, "packet returned by codec"); // Need to adjust pts/dts values from codec time to stream time - if ( opkt.pts != AV_NOPTS_VALUE ) + if (opkt.pts != AV_NOPTS_VALUE) opkt.pts = av_rescale_q(opkt.pts, video_out_ctx->time_base, video_out_stream->time_base); - if ( opkt.dts != AV_NOPTS_VALUE ) + if (opkt.dts != AV_NOPTS_VALUE) opkt.dts = av_rescale_q(opkt.dts, video_out_ctx->time_base, video_out_stream->time_base); Debug(1, "Timebase conversions using %d/%d -> %d/%d", video_out_ctx->time_base.num, @@ -1154,10 +1121,9 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet video_out_stream->time_base.num, video_out_stream->time_base.den); - int64_t duration = 0; - if ( zm_packet->in_frame ) { - if ( zm_packet->in_frame->pkt_duration ) { + if (zm_packet->in_frame) { + if (zm_packet->in_frame->pkt_duration) { duration = av_rescale_q( zm_packet->in_frame->pkt_duration, video_in_stream->time_base, @@ -1171,9 +1137,8 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet video_out_stream->time_base.num, video_out_stream->time_base.den ); - } else if ( video_last_pts != AV_NOPTS_VALUE ) { - duration = - av_rescale_q( + } else if (video_last_pts != AV_NOPTS_VALUE) { + duration = av_rescale_q( zm_packet->in_frame->pts - video_last_pts, video_in_stream->time_base, video_out_stream->time_base); @@ -1183,8 +1148,10 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet zm_packet->in_frame->pts - video_last_pts, duration ); - if ( duration <= 0 ) { - duration = zm_packet->in_frame->pkt_duration ? zm_packet->in_frame->pkt_duration : av_rescale_q(1, video_in_stream->time_base, video_out_stream->time_base); + if (duration <= 0) { + duration = zm_packet->in_frame->pkt_duration ? + zm_packet->in_frame->pkt_duration : + av_rescale_q(1, video_in_stream->time_base, video_out_stream->time_base); } } // end if in_frmae->pkt_duration video_last_pts = zm_packet->in_frame->pts; @@ -1192,7 +1159,6 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet //duration = av_rescale_q(zm_packet->out_frame->pts - video_last_pts, video_in_stream->time_base, video_out_stream->time_base); } // end if in_frmae opkt.duration = duration; - } else { // Passthrough AVPacket *ipkt = &zm_packet->packet; ZM_DUMP_STREAM_PACKET(video_in_stream, (*ipkt), "Doing passthrough, just copy packet"); @@ -1203,8 +1169,8 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet opkt.flags = ipkt->flags; opkt.duration = ipkt->duration; - if ( ipkt->dts != AV_NOPTS_VALUE ) { - if ( !video_first_dts ) { + if (ipkt->dts != AV_NOPTS_VALUE) { + if (!video_first_dts) { Debug(2, "Starting video first_dts will become %" PRId64, ipkt->dts); video_first_dts = ipkt->dts; } @@ -1213,14 +1179,13 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet opkt.dts = next_dts[video_out_stream->index] ? av_rescale_q(next_dts[video_out_stream->index], video_out_stream->time_base, video_in_stream->time_base) : 0; Debug(3, "Setting dts to video_next_dts %" PRId64 " from %" PRId64, opkt.dts, next_dts[video_out_stream->index]); } - if ( ipkt->pts != AV_NOPTS_VALUE ) { + if (ipkt->pts != AV_NOPTS_VALUE) { opkt.pts = ipkt->pts - video_first_dts; } else { opkt.pts = AV_NOPTS_VALUE; } av_packet_rescale_ts(&opkt, video_in_stream->time_base, video_out_stream->time_base); - ZM_DUMP_STREAM_PACKET(video_out_stream, opkt, "after pts adjustment"); } // end if codec matches @@ -1232,18 +1197,17 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet } // end int VideoStore::writeVideoFramePacket( AVPacket *ipkt ) int VideoStore::writeAudioFramePacket(const std::shared_ptr &zm_packet) { - - AVPacket *ipkt = &zm_packet->packet; - int ret; - - if ( !audio_out_stream ) { + if (!audio_out_stream) { Debug(1, "Called writeAudioFramePacket when no audio_out_stream"); return 0; // FIXME -ve return codes do not free packet in ffmpeg_camera at the moment } + + AVPacket *ipkt = &zm_packet->packet; + int ret; ZM_DUMP_STREAM_PACKET(audio_in_stream, (*ipkt), "input packet"); - if ( !audio_first_dts ) { + if (!audio_first_dts) { audio_first_dts = ipkt->dts; audio_next_pts = audio_out_ctx->frame_size; } @@ -1251,10 +1215,10 @@ int VideoStore::writeAudioFramePacket(const std::shared_ptr &zm_packet Debug(3, "audio first_dts to %" PRId64, audio_first_dts); // Need to adjust pts before feeding to decoder.... should really copy the pkt instead of modifying it - if ( audio_out_codec ) { + if (audio_out_codec) { // I wonder if we can get multiple frames per packet? Probably ret = zm_send_packet_receive_frame(audio_in_ctx, in_frame, *ipkt); - if ( ret < 0 ) { + if (ret < 0) { Debug(3, "failed to receive frame code: %d", ret); return 0; } @@ -1262,15 +1226,15 @@ int VideoStore::writeAudioFramePacket(const std::shared_ptr &zm_packet AVFrame *input_frame = in_frame; - while ( zm_resample_audio(resample_ctx, input_frame, out_frame) ) { + while (zm_resample_audio(resample_ctx, input_frame, out_frame)) { //out_frame->pkt_duration = in_frame->pkt_duration; // resampling doesn't alter duration - if ( zm_add_samples_to_fifo(fifo, out_frame) <= 0 ) + if (zm_add_samples_to_fifo(fifo, out_frame) <= 0) break; // We put the samples into the fifo so we are basically resetting the frame out_frame->nb_samples = audio_out_ctx->frame_size; - if ( zm_get_samples_from_fifo(fifo, out_frame) <= 0 ) + if (zm_get_samples_from_fifo(fifo, out_frame) <= 0) break; out_frame->pts = audio_next_pts; @@ -1279,7 +1243,7 @@ int VideoStore::writeAudioFramePacket(const std::shared_ptr &zm_packet zm_dump_frame(out_frame, "Out frame after resample"); av_init_packet(&opkt); - if ( zm_send_frame_receive_packet(audio_out_ctx, out_frame, opkt) <= 0 ) + if (zm_send_frame_receive_packet(audio_out_ctx, out_frame, opkt) <= 0) break; // Scale the PTS of the outgoing packet to be the correct time base @@ -1290,12 +1254,11 @@ int VideoStore::writeAudioFramePacket(const std::shared_ptr &zm_packet write_packet(&opkt, audio_out_stream); zm_av_packet_unref(&opkt); - if ( zm_resample_get_delay(resample_ctx, out_frame->sample_rate) < out_frame->nb_samples) + if (zm_resample_get_delay(resample_ctx, out_frame->sample_rate) < out_frame->nb_samples) break; // This will send a null frame, emptying out the resample buffer input_frame = nullptr; - } // end while there is data in the resampler - + } // end while there is data in the resampler } else { av_init_packet(&opkt); opkt.data = ipkt->data; @@ -1321,15 +1284,15 @@ int VideoStore::write_packet(AVPacket *pkt, AVStream *stream) { pkt->pos = -1; pkt->stream_index = stream->index; - if ( pkt->dts == AV_NOPTS_VALUE ) { + if (pkt->dts == AV_NOPTS_VALUE) { Debug(1, "undef dts, fixing by setting to stream cur_dts %" PRId64, stream->cur_dts); pkt->dts = stream->cur_dts; - } else if ( pkt->dts < stream->cur_dts ) { + } else if (pkt->dts < stream->cur_dts) { Debug(1, "non increasing dts, fixing. our dts %" PRId64 " stream cur_dts %" PRId64, pkt->dts, stream->cur_dts); pkt->dts = stream->cur_dts; } - if ( pkt->dts > pkt->pts ) { + if (pkt->dts > pkt->pts) { Debug(1, "pkt.dts(%" PRId64 ") must be <= pkt.pts(%" PRId64 ")." "Decompression must happen before presentation.", @@ -1343,9 +1306,8 @@ int VideoStore::write_packet(AVPacket *pkt, AVStream *stream) { stream->index, next_dts[stream->index]); int ret = av_interleaved_write_frame(oc, pkt); - if ( ret != 0 ) { - Error("Error writing packet: %s", - av_make_error_string(ret).c_str()); + if (ret != 0) { + Error("Error writing packet: %s", av_make_error_string(ret).c_str()); } else { Debug(4, "Success writing packet"); } From a335e740f3b8abcd3085afaa06820074e4f20e95 Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Mon, 17 May 2021 10:46:50 +0200 Subject: [PATCH 053/394] Utils: Fix possible name clash between std::clamp and ZM::clamp The naming was ambiguous when compinling in C++17 mode. --- src/zm_utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zm_utils.h b/src/zm_utils.h index 88e00a785..13bbc2dcf 100644 --- a/src/zm_utils.h +++ b/src/zm_utils.h @@ -96,7 +96,7 @@ constexpr const T &clamp(const T &v, const T &lo, const T &hi, Compare comp) { template constexpr const T &clamp(const T &v, const T &lo, const T &hi) { - return clamp(v, lo, hi, std::less{}); + return ZM::clamp(v, lo, hi, std::less{}); } } From 6642ca45155a436c6208529aaffbc9f7903a720e Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Sun, 16 May 2021 19:16:32 +0200 Subject: [PATCH 054/394] Image: Eliminate VLAs from polygon fill algorithm implementation --- src/zm_image.cpp | 148 ++++++++++++++++++++--------------------------- src/zm_image.h | 4 +- src/zm_poly.h | 15 +++-- 3 files changed, 74 insertions(+), 93 deletions(-) diff --git a/src/zm_image.cpp b/src/zm_image.cpp index 44311d74a..b0380f7c1 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -2458,9 +2458,9 @@ void Image::Outline( Rgb colour, const Polygon &polygon ) { } // end foreach coordinate in the polygon } -/* RGB32 compatible: complete */ +// Polygon filling is based on the Scan-line Polygon filling algorithm void Image::Fill(Rgb colour, int density, const Polygon &polygon) { - if ( !(colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB24 || colours == ZM_COLOUR_RGB32 ) ) { + if (!(colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB24 || colours == ZM_COLOUR_RGB32)) { Panic("Attempt to fill image with unexpected colours %d", colours); } @@ -2468,115 +2468,91 @@ void Image::Fill(Rgb colour, int density, const Polygon &polygon) { colour = rgb_convert(colour, subpixelorder); size_t n_coords = polygon.GetVertices().size(); - int n_global_edges = 0; - Edge global_edges[n_coords]; + + std::vector global_edges; + global_edges.reserve(n_coords); for (size_t j = 0, i = n_coords - 1; j < n_coords; i = j++) { const Vector2 &p1 = polygon.GetVertices()[i]; const Vector2 &p2 = polygon.GetVertices()[j]; - int x1 = p1.x_; - int x2 = p2.x_; - int y1 = p1.y_; - int y2 = p2.y_; - - //Debug( 9, "x1:%d,y1:%d x2:%d,y2:%d", x1, y1, x2, y2 ); - if ( y1 == y2 ) + // Do not add horizontal edges to the global edge table. + if (p1.y_ == p2.y_) continue; - double dx = x2 - x1; - double dy = y2 - y1; + Vector2 d = p2 - p1; + + global_edges.emplace_back(std::min(p1.y_, p2.y_), + std::max(p1.y_, p2.y_), + p1.y_ < p2.y_ ? p1.x_ : p2.x_, + d.x_ / static_cast(d.y_)); - global_edges[n_global_edges].min_y = y1= Logger::DEBUG9 ) { - for ( int i = 0; i < n_global_edges; i++ ) { - Debug(9, "%d: min_y: %d, max_y:%d, min_x:%.2f, 1/m:%.2f", - i, global_edges[i].min_y, global_edges[i].max_y, global_edges[i].min_x, global_edges[i]._1_m); - } - } -#endif + std::vector active_edges; + active_edges.reserve(global_edges.size()); - int n_active_edges = 0; - Edge active_edges[n_global_edges]; - int y = global_edges[0].min_y; - do { - for ( int i = 0; i < n_global_edges; i++ ) { - if ( global_edges[i].min_y == y ) { - Debug(9, "Moving global edge"); - active_edges[n_active_edges++] = global_edges[i]; - if ( i < (n_global_edges-1) ) { - memmove(&global_edges[i], &global_edges[i + 1], sizeof(*global_edges) * (n_global_edges - i - 1)); - i--; - } - n_global_edges--; + int32 scan_line = global_edges[0].min_y; + while (!global_edges.empty() || !active_edges.empty()) { + // Deactivate edges with max_y < current scan line + for (auto it = active_edges.begin(); it != active_edges.end();) { + if (scan_line >= it->max_y) { + it = active_edges.erase(it); } else { - break; + it->min_x += it->_1_m; + ++it; } } - std::sort(active_edges, active_edges + n_active_edges, Edge::CompareX); -#ifndef ZM_DBG_OFF - if ( logLevel() >= Logger::DEBUG9 ) { - for ( int i = 0; i < n_active_edges; i++ ) { - Debug(9, "%d - %d: min_y: %d, max_y:%d, min_x:%.2f, 1/m:%.2f", - y, i, active_edges[i].min_y, active_edges[i].max_y, active_edges[i].min_x, active_edges[i]._1_m ); + + // Activate edges with min_y == current scan line + for (auto it = global_edges.begin(); it != global_edges.end();) { + if (it->min_y == scan_line) { + active_edges.emplace_back(*it); + it = global_edges.erase(it); + } else { + ++it; } } -#endif - if ( !(y%density) ) { - //Debug( 9, "%d", y ); - for ( int i = 0; i < n_active_edges; ) { - int lo_x = int(round(active_edges[i++].min_x)); - int hi_x = int(round(active_edges[i++].min_x)); - if ( colours == ZM_COLOUR_GRAY8 ) { - unsigned char *p = &buffer[(y*width)+lo_x]; - for ( int x = lo_x; x <= hi_x; x++, p++) { - if ( !(x%density) ) { - //Debug( 9, " %d", x ); + std::sort(active_edges.begin(), active_edges.end(), Edge::CompareX); + + if (!(scan_line % density)) { + for (auto it = active_edges.begin(); it != active_edges.end(); ++it) { + int32 lo_x = static_cast(it->min_x); + int32 hi_x = static_cast(std::next(it)->min_x); + if (colours == ZM_COLOUR_GRAY8) { + uint8 *p = &buffer[(scan_line * width) + lo_x]; + + for (int32 x = lo_x; x <= hi_x; x++, p++) { + if (!(x % density)) { *p = colour; } } - } else if ( colours == ZM_COLOUR_RGB24 ) { - unsigned char *p = &buffer[colours*((y*width)+lo_x)]; - for ( int x = lo_x; x <= hi_x; x++, p += 3) { - if ( !(x%density) ) { - RED_PTR_RGBA(p) = RED_VAL_RGBA(colour); - GREEN_PTR_RGBA(p) = GREEN_VAL_RGBA(colour); - BLUE_PTR_RGBA(p) = BLUE_VAL_RGBA(colour); + } else if (colours == ZM_COLOUR_RGB24) { + constexpr uint8 bytesPerPixel = 3; + uint8 *ptr = &buffer[((scan_line * width) + lo_x) * bytesPerPixel]; + + for (int32 x = lo_x; x <= hi_x; x++, ptr += bytesPerPixel) { + if (!(x % density)) { + RED_PTR_RGBA(ptr) = RED_VAL_RGBA(colour); + GREEN_PTR_RGBA(ptr) = GREEN_VAL_RGBA(colour); + BLUE_PTR_RGBA(ptr) = BLUE_VAL_RGBA(colour); } } - } else if( colours == ZM_COLOUR_RGB32 ) { - Rgb *p = (Rgb*)&buffer[((y*width)+lo_x)<<2]; - for ( int x = lo_x; x <= hi_x; x++, p++) { - if ( !(x%density) ) { - /* Fast, copies the entire pixel in a single pass */ - *p = colour; + } else if (colours == ZM_COLOUR_RGB32) { + constexpr uint8 bytesPerPixel = 4; + Rgb *ptr = reinterpret_cast(&buffer[((scan_line * width) + lo_x) * bytesPerPixel]); + + for (int32 x = lo_x; x <= hi_x; x++, ptr++) { + if (!(x % density)) { + *ptr = colour; } } } } } - y++; - for ( int i = n_active_edges-1; i >= 0; i-- ) { - if ( y >= active_edges[i].max_y ) { - // Or >= as per sheets - Debug(9, "Deleting active_edge"); - if ( i < (n_active_edges-1) ) { - //memcpy( &active_edges[i], &active_edges[i+1], sizeof(*active_edges)*(n_active_edges-i) ); - memmove( &active_edges[i], &active_edges[i+1], sizeof(*active_edges)*(n_active_edges-i) ); - } - n_active_edges--; - } else { - active_edges[i].min_x += active_edges[i]._1_m; - } - } - } while ( n_global_edges || n_active_edges ); + + scan_line++; + } } void Image::Fill(Rgb colour, const Polygon &polygon) { diff --git a/src/zm_image.h b/src/zm_image.h index bb12a60a1..71a211fa2 100644 --- a/src/zm_image.h +++ b/src/zm_image.h @@ -278,8 +278,8 @@ class Image { void Fill( Rgb colour, const Box *limits=0 ); void Fill( Rgb colour, int density, const Box *limits=0 ); void Outline( Rgb colour, const Polygon &polygon ); - void Fill( Rgb colour, const Polygon &polygon ); - void Fill( Rgb colour, int density, const Polygon &polygon ); + void Fill(Rgb colour, const Polygon &polygon); + void Fill(Rgb colour, int density, const Polygon &polygon); void Rotate( int angle ); void Flip( bool leftright ); diff --git a/src/zm_poly.h b/src/zm_poly.h index 0d1981ef2..fa91f58d1 100644 --- a/src/zm_poly.h +++ b/src/zm_poly.h @@ -23,11 +23,10 @@ #include "zm_box.h" #include -struct Edge { - int min_y; - int max_y; - double min_x; - double _1_m; +class Edge { + public: + Edge() = default; + Edge(int32 min_y, int32 max_y, double min_x, double _1_m) : min_y(min_y), max_y(max_y), min_x(min_x), _1_m(_1_m) {} static bool CompareYX(const Edge &e1, const Edge &e2) { if (e1.min_y == e2.min_y) @@ -38,6 +37,12 @@ struct Edge { static bool CompareX(const Edge &e1, const Edge &e2) { return e1.min_x < e2.min_x; } + + public: + int32 min_y; + int32 max_y; + double min_x; + double _1_m; }; // This class represents convex or concave non-self-intersecting polygons. From c0017a52638a9f1f17aba482c7a298ffdd698813 Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Sun, 16 May 2021 23:24:47 +0200 Subject: [PATCH 055/394] Image: Move Edge class to its own namespace The Edge class is an implementation detail of the scan-line polygon filling algorithm. Tuck it away in the newly created PolygonFill namespace. --- src/zm_image.cpp | 12 ++++-------- src/zm_image.h | 27 ++++++++++++++++++++++++++- src/zm_poly.h | 22 ---------------------- 3 files changed, 30 insertions(+), 31 deletions(-) diff --git a/src/zm_image.cpp b/src/zm_image.cpp index b0380f7c1..1acb49e03 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -2469,7 +2469,7 @@ void Image::Fill(Rgb colour, int density, const Polygon &polygon) { size_t n_coords = polygon.GetVertices().size(); - std::vector global_edges; + std::vector global_edges; global_edges.reserve(n_coords); for (size_t j = 0, i = n_coords - 1; j < n_coords; i = j++) { const Vector2 &p1 = polygon.GetVertices()[i]; @@ -2487,9 +2487,9 @@ void Image::Fill(Rgb colour, int density, const Polygon &polygon) { d.x_ / static_cast(d.y_)); } - std::sort(global_edges.begin(), global_edges.end(), Edge::CompareYX); + std::sort(global_edges.begin(), global_edges.end(), PolygonFill::Edge::CompareYX); - std::vector active_edges; + std::vector active_edges; active_edges.reserve(global_edges.size()); int32 scan_line = global_edges[0].min_y; @@ -2513,7 +2513,7 @@ void Image::Fill(Rgb colour, int density, const Polygon &polygon) { ++it; } } - std::sort(active_edges.begin(), active_edges.end(), Edge::CompareX); + std::sort(active_edges.begin(), active_edges.end(), PolygonFill::Edge::CompareX); if (!(scan_line % density)) { for (auto it = active_edges.begin(); it != active_edges.end(); ++it) { @@ -2555,10 +2555,6 @@ void Image::Fill(Rgb colour, int density, const Polygon &polygon) { } } -void Image::Fill(Rgb colour, const Polygon &polygon) { - Fill(colour, 1, polygon); -} - void Image::Rotate(int angle) { angle %= 360; diff --git a/src/zm_image.h b/src/zm_image.h index 71a211fa2..2a3c68232 100644 --- a/src/zm_image.h +++ b/src/zm_image.h @@ -278,7 +278,7 @@ class Image { void Fill( Rgb colour, const Box *limits=0 ); void Fill( Rgb colour, int density, const Box *limits=0 ); void Outline( Rgb colour, const Polygon &polygon ); - void Fill(Rgb colour, const Polygon &polygon); + void Fill(Rgb colour, const Polygon &polygon) { Fill(colour, 1, polygon); }; void Fill(Rgb colour, int density, const Polygon &polygon); void Rotate( int angle ); @@ -292,6 +292,31 @@ class Image { void Deinterlace_4Field(const Image* next_image, unsigned int threshold); }; +// Scan-line polygon fill algorithm +namespace PolygonFill { +class Edge { + public: + Edge() = default; + Edge(int32 min_y, int32 max_y, double min_x, double _1_m) : min_y(min_y), max_y(max_y), min_x(min_x), _1_m(_1_m) {} + + static bool CompareYX(const Edge &e1, const Edge &e2) { + if (e1.min_y == e2.min_y) + return e1.min_x < e2.min_x; + return e1.min_y < e2.min_y; + } + + static bool CompareX(const Edge &e1, const Edge &e2) { + return e1.min_x < e2.min_x; + } + + public: + int32 min_y; + int32 max_y; + double min_x; + double _1_m; +}; +} + #endif // ZM_IMAGE_H /* Blend functions */ diff --git a/src/zm_poly.h b/src/zm_poly.h index fa91f58d1..c25033262 100644 --- a/src/zm_poly.h +++ b/src/zm_poly.h @@ -23,28 +23,6 @@ #include "zm_box.h" #include -class Edge { - public: - Edge() = default; - Edge(int32 min_y, int32 max_y, double min_x, double _1_m) : min_y(min_y), max_y(max_y), min_x(min_x), _1_m(_1_m) {} - - static bool CompareYX(const Edge &e1, const Edge &e2) { - if (e1.min_y == e2.min_y) - return e1.min_x < e2.min_x; - return e1.min_y < e2.min_y; - } - - static bool CompareX(const Edge &e1, const Edge &e2) { - return e1.min_x < e2.min_x; - } - - public: - int32 min_y; - int32 max_y; - double min_x; - double _1_m; -}; - // This class represents convex or concave non-self-intersecting polygons. class Polygon { public: From 77068163a322462a5ffb56ffc3a9cbf425957f9d Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Mon, 17 May 2021 00:20:05 +0200 Subject: [PATCH 056/394] LocalCamera: Fix some format warnings reported by clang `capturePixFormat` and `imagePixFormat` are no enum entries. Do no try to log them as FourCC. --- src/zm_local_camera.cpp | 46 +++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/src/zm_local_camera.cpp b/src/zm_local_camera.cpp index b72c38015..3ff3608c0 100644 --- a/src/zm_local_camera.cpp +++ b/src/zm_local_camera.cpp @@ -381,7 +381,11 @@ LocalCamera::LocalCamera( } else { if ( capture ) { Info("Selected capture palette: %s (0x%02hhx%02hhx%02hhx%02hhx)", - palette_desc, (palette>>24)&0xff, (palette>>16)&0xff, (palette>>8)&0xff, (palette)&0xff); + palette_desc, + static_cast((palette >> 24) & 0xff), + static_cast((palette >> 16) & 0xff), + static_cast((palette >> 8) & 0xff), + static_cast((palette) & 0xff)); } } } @@ -438,8 +442,10 @@ LocalCamera::LocalCamera( } else { if ( capture ) { #if HAVE_LIBSWSCALE - Info("No direct match for the selected palette (0x%02hhx%02hhx%02hhx%02hhx) and target colorspace (%02u). Format conversion is required, performance penalty expected", - (capturePixFormat>>24)&0xff,((capturePixFormat>>16)&0xff),((capturePixFormat>>8)&0xff),((capturePixFormat)&0xff), colours); + Info( + "No direct match for the selected palette (%d) and target colorspace (%02u). Format conversion is required, performance penalty expected", + capturePixFormat, + colours); #else Info("No direct match for the selected palette and target colorspace. Format conversion is required, performance penalty expected"); #endif @@ -463,13 +469,11 @@ LocalCamera::LocalCamera( if ( capture ) { #if LIBSWSCALE_VERSION_CHECK(0, 8, 0, 8, 0) if ( !sws_isSupportedInput(capturePixFormat) ) { - Error("swscale does not support the used capture format: 0x%02hhx%02hhx%02hhx%02hhx", - (capturePixFormat>>24)&0xff,((capturePixFormat>>16)&0xff),((capturePixFormat>>8)&0xff),((capturePixFormat)&0xff)); + Error("swscale does not support the used capture format: %d", capturePixFormat); conversion_type = 2; /* Try ZM format conversions */ } if ( !sws_isSupportedOutput(imagePixFormat) ) { - Error("swscale does not support the target format: 0x%02hhx%02hhx%02hhx%02hhx", - (imagePixFormat>>24)&0xff,((imagePixFormat>>16)&0xff),((imagePixFormat>>8)&0xff),((imagePixFormat)&0xff)); + Error("swscale does not support the target format: 0x%d", imagePixFormat); conversion_type = 2; /* Try ZM format conversions */ } #endif @@ -1212,14 +1216,14 @@ uint32_t LocalCamera::AutoSelectFormat(int p_colours) { /* Got a format. Copy it to the array */ strcpy(fmt_desc[nIndex], (const char*)(fmtinfo.description)); fmt_fcc[nIndex] = fmtinfo.pixelformat; - + Debug(3, "Got format: %s (0x%02hhx%02hhx%02hhx%02hhx) at index %d", - fmt_desc[nIndex], - (fmt_fcc[nIndex]>>24)&0xff, - (fmt_fcc[nIndex]>>16)&0xff, - (fmt_fcc[nIndex]>>8)&0xff, - (fmt_fcc[nIndex])&0xff, - nIndex); + fmt_desc[nIndex], + static_cast((fmt_fcc[nIndex] >> 24) & 0xff), + static_cast((fmt_fcc[nIndex] >> 16) & 0xff), + static_cast((fmt_fcc[nIndex] >> 8) & 0xff), + static_cast((fmt_fcc[nIndex]) & 0xff), + nIndex); /* Proceed to the next index */ memset(&fmtinfo, 0, sizeof(fmtinfo)); @@ -1248,13 +1252,23 @@ uint32_t LocalCamera::AutoSelectFormat(int p_colours) { for ( unsigned int j=0; j < nIndex; j++ ) { if ( preferedformats[i] == fmt_fcc[j] ) { Debug(6, "Choosing format: %s (0x%02hhx%02hhx%02hhx%02hhx) at index %u", - fmt_desc[j],fmt_fcc[j]&0xff, (fmt_fcc[j]>>8)&0xff, (fmt_fcc[j]>>16)&0xff, (fmt_fcc[j]>>24)&0xff ,j); + fmt_desc[j], + static_cast(fmt_fcc[j] & 0xff), + static_cast((fmt_fcc[j] >> 8) & 0xff), + static_cast((fmt_fcc[j] >> 16) & 0xff), + static_cast((fmt_fcc[j] >> 24) & 0xff), + j); /* Found a format! */ nIndexUsed = j; break; } else { Debug(6, "No match for format: %s (0x%02hhx%02hhx%02hhx%02hhx) at index %u", - fmt_desc[j],fmt_fcc[j]&0xff, (fmt_fcc[j]>>8)&0xff, (fmt_fcc[j]>>16)&0xff, (fmt_fcc[j]>>24)&0xff ,j); + fmt_desc[j], + static_cast(fmt_fcc[j] & 0xff), + static_cast((fmt_fcc[j] >> 8) & 0xff), + static_cast((fmt_fcc[j] >> 16) & 0xff), + static_cast((fmt_fcc[j] >> 24) & 0xff), + j); } } } From 298415fff3ceac53d3d972700b584c08672f91eb Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Sun, 16 May 2021 19:16:08 +0200 Subject: [PATCH 057/394] Remove remaining usages of VLAs --- src/zm_comms.cpp | 8 ++++---- src/zm_comms.h | 22 +++++++++++----------- src/zm_logger.cpp | 8 +++++--- src/zm_rtsp_auth.cpp | 6 +++--- src/zm_user.cpp | 4 ++-- tests/zm_comms.cpp | 35 +++++++++++++++++++++++++++-------- 6 files changed, 52 insertions(+), 31 deletions(-) diff --git a/src/zm_comms.cpp b/src/zm_comms.cpp index 7f933d16f..bef10164d 100644 --- a/src/zm_comms.cpp +++ b/src/zm_comms.cpp @@ -36,7 +36,7 @@ int ZM::CommsBase::readV(int iovcnt, /* const void *, int, */ ...) { va_list arg_ptr; - iovec iov[iovcnt]; + std::vector iov(iovcnt); va_start(arg_ptr, iovcnt); for (int i = 0; i < iovcnt; i++) { @@ -45,7 +45,7 @@ int ZM::CommsBase::readV(int iovcnt, /* const void *, int, */ ...) { } va_end(arg_ptr); - int nBytes = ::readv(mRd, iov, iovcnt); + int nBytes = ::readv(mRd, iov.data(), iovcnt); if (nBytes < 0) { Debug(1, "Readv of %d buffers max on rd %d failed: %s", iovcnt, mRd, strerror(errno)); } @@ -54,7 +54,7 @@ int ZM::CommsBase::readV(int iovcnt, /* const void *, int, */ ...) { int ZM::CommsBase::writeV(int iovcnt, /* const void *, int, */ ...) { va_list arg_ptr; - iovec iov[iovcnt]; + std::vector iov(iovcnt); va_start(arg_ptr, iovcnt); for (int i = 0; i < iovcnt; i++) { @@ -63,7 +63,7 @@ int ZM::CommsBase::writeV(int iovcnt, /* const void *, int, */ ...) { } va_end(arg_ptr); - ssize_t nBytes = ::writev(mWd, iov, iovcnt); + ssize_t nBytes = ::writev(mWd, iov.data(), iovcnt); if (nBytes < 0) { Debug(1, "Writev of %d buffers on wd %d failed: %s", iovcnt, mWd, strerror(errno)); } diff --git a/src/zm_comms.h b/src/zm_comms.h index 9126e5373..070ed5395 100644 --- a/src/zm_comms.h +++ b/src/zm_comms.h @@ -243,27 +243,27 @@ class Socket : public CommsBase { return nBytes; } - virtual int recv(std::string &msg) const { - char buffer[msg.capacity()]; - int nBytes = 0; - if ((nBytes = ::recv(mSd, buffer, sizeof(buffer), 0)) < 0) { - Debug(1, "Recv of %zd bytes max to string on sd %d failed: %s", sizeof(buffer), mSd, strerror(errno)); + virtual ssize_t recv(std::string &msg) const { + std::vector buffer(msg.capacity()); + ssize_t nBytes; + if ((nBytes = ::recv(mSd, buffer.data(), buffer.size(), 0)) < 0) { + Debug(1, "Recv of %zd bytes max to string on sd %d failed: %s", msg.size(), mSd, strerror(errno)); return nBytes; } buffer[nBytes] = '\0'; - msg = buffer; + msg = {buffer.begin(), buffer.begin() + nBytes}; return nBytes; } - virtual int recv(std::string &msg, size_t maxLen) const { - char buffer[maxLen]; - int nBytes = 0; - if ((nBytes = ::recv(mSd, buffer, sizeof(buffer), 0)) < 0) { + virtual ssize_t recv(std::string &msg, size_t maxLen) const { + std::vector buffer(maxLen); + ssize_t nBytes; + if ((nBytes = ::recv(mSd, buffer.data(), buffer.size(), 0)) < 0) { Debug(1, "Recv of %zd bytes max to string on sd %d failed: %s", maxLen, mSd, strerror(errno)); return nBytes; } buffer[nBytes] = '\0'; - msg = buffer; + msg = {buffer.begin(), buffer.begin() + nBytes}; return nBytes; } diff --git a/src/zm_logger.cpp b/src/zm_logger.cpp index dd03ec9f2..872247701 100644 --- a/src/zm_logger.cpp +++ b/src/zm_logger.cpp @@ -529,15 +529,17 @@ void Logger::logPrint(bool hex, const char *filepath, int line, int level, const if (level <= mDatabaseLevel) { if (zmDbConnected) { int syslogSize = syslogEnd-syslogStart; - char escapedString[(syslogSize*2)+1]; - mysql_real_escape_string(&dbconn, escapedString, syslogStart, syslogSize); + std::string escapedString; + escapedString.reserve((syslogSize * 2) + 1); + mysql_real_escape_string(&dbconn, &escapedString[0], syslogStart, syslogSize); + escapedString.resize(std::strlen(escapedString.c_str())); std::string sql_string = stringtf( "INSERT INTO `Logs` " "( `TimeKey`, `Component`, `ServerId`, `Pid`, `Level`, `Code`, `Message`, `File`, `Line` )" " VALUES " "( %ld.%06ld, '%s', %d, %d, %d, '%s', '%s', '%s', %d )", - timeVal.tv_sec, timeVal.tv_usec, mId.c_str(), staticConfig.SERVER_ID, tid, level, classString, escapedString, file, line + timeVal.tv_sec, timeVal.tv_usec, mId.c_str(), staticConfig.SERVER_ID, tid, level, classString, escapedString.c_str(), file, line ); dbQueue.push(std::move(sql_string)); } else { diff --git a/src/zm_rtsp_auth.cpp b/src/zm_rtsp_auth.cpp index 34fd1921b..1a7ea629b 100644 --- a/src/zm_rtsp_auth.cpp +++ b/src/zm_rtsp_auth.cpp @@ -131,9 +131,9 @@ std::string Authenticator::computeDigestResponse(const std::string &method, cons #if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT // The "response" field is computed as: // md5(md5(::)::md5(:)) - size_t md5len = 16; - unsigned char md5buf[md5len]; - char md5HexBuf[md5len*2+1]; + constexpr size_t md5len = 16; + uint8 md5buf[md5len]; + char md5HexBuf[md5len * 2 + 1]; // Step 1: md5(::) std::string ha1Data = username() + ":" + realm() + ":" + password(); diff --git a/src/zm_user.cpp b/src/zm_user.cpp index 254663a04..95f61da4e 100644 --- a/src/zm_user.cpp +++ b/src/zm_user.cpp @@ -236,8 +236,8 @@ User *zmLoadAuthUser(const char *auth, bool use_remote_addr) { } char auth_key[512] = ""; char auth_md5[32+1] = ""; - size_t md5len = 16; - unsigned char md5sum[md5len]; + constexpr size_t md5len = 16; + uint8 md5sum[md5len]; const char * hex = "0123456789abcdef"; while ( MYSQL_ROW dbrow = mysql_fetch_row(result) ) { diff --git a/tests/zm_comms.cpp b/tests/zm_comms.cpp index 1146e69a5..f23e8c987 100644 --- a/tests/zm_comms.cpp +++ b/tests/zm_comms.cpp @@ -224,23 +224,42 @@ TEST_CASE("ZM::UdpUnixSocket send/recv") { ZM::UdpUnixSocket srv_socket; ZM::UdpUnixSocket client_socket; - std::array msg = {'a', 'b', 'c'}; - std::array rcv{}; + SECTION("send/recv byte buffer") { + std::array msg = {'a', 'b', 'c'}; + std::array rcv{}; - SECTION("send/recv on unbound socket") { - REQUIRE(client_socket.send(msg.data(), msg.size()) == -1); - REQUIRE(srv_socket.recv(rcv.data(), rcv.size()) == -1); + SECTION("on unbound socket") { + REQUIRE(client_socket.send(msg.data(), msg.size()) == -1); + REQUIRE(srv_socket.recv(rcv.data(), rcv.size()) == -1); + } + + SECTION("on bound socket") { + REQUIRE(srv_socket.bind(sock_path.c_str()) == true); + REQUIRE(srv_socket.isOpen() == true); + + REQUIRE(client_socket.connect(sock_path.c_str()) == true); + REQUIRE(client_socket.isConnected() == true); + + REQUIRE(client_socket.send(msg.data(), msg.size()) == msg.size()); + REQUIRE(srv_socket.recv(rcv.data(), rcv.size()) == msg.size()); + + REQUIRE(rcv == msg); + } } - SECTION("send/recv") { + SECTION("send/recv string") { + std::string msg = "abc"; + std::string rcv; + rcv.reserve(msg.length()); + REQUIRE(srv_socket.bind(sock_path.c_str()) == true); REQUIRE(srv_socket.isOpen() == true); REQUIRE(client_socket.connect(sock_path.c_str()) == true); REQUIRE(client_socket.isConnected() == true); - REQUIRE(client_socket.send(msg.data(), msg.size()) == msg.size()); - REQUIRE(srv_socket.recv(rcv.data(), rcv.size()) == msg.size()); + REQUIRE(client_socket.send(msg) == static_cast(msg.size())); + REQUIRE(srv_socket.recv(rcv) == static_cast(msg.size())); REQUIRE(rcv == msg); } From c8f2a813e3e9a32e07ed5404eb818d742c82338f Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Sun, 25 Apr 2021 23:52:32 +0200 Subject: [PATCH 058/394] Build: Enable VLA warnings VLAs (variable-length arrays) are a C99 feature and were never standardised for C++. There are better alternatives in C++ (mainly std::vector). Enable the warnings so no new ones can be introduced. --- cmake/compiler/clang/settings.cmake | 3 ++- cmake/compiler/gcc/settings.cmake | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cmake/compiler/clang/settings.cmake b/cmake/compiler/clang/settings.cmake index 6c5419a94..d5ba4204a 100644 --- a/cmake/compiler/clang/settings.cmake +++ b/cmake/compiler/clang/settings.cmake @@ -3,7 +3,8 @@ target_compile_options(zm-warning-interface -Wall -Wextra -Wimplicit-fallthrough - -Wno-unused-parameter) + -Wno-unused-parameter + -Wvla) if(ENABLE_WERROR) target_compile_options(zm-warning-interface diff --git a/cmake/compiler/gcc/settings.cmake b/cmake/compiler/gcc/settings.cmake index 097270d51..ce84dcc1a 100644 --- a/cmake/compiler/gcc/settings.cmake +++ b/cmake/compiler/gcc/settings.cmake @@ -7,7 +7,8 @@ target_compile_options(zm-warning-interface -Wno-cast-function-type $<$,11>:-Wno-clobbered> -Wno-unused-parameter - -Woverloaded-virtual) + -Woverloaded-virtual + -Wvla) if(ENABLE_WERROR) target_compile_options(zm-warning-interface From 56a1e8d13b4618fcf353b01ba64537c6ba566947 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 17 May 2021 16:29:57 -0400 Subject: [PATCH 059/394] cleanup CheckSignal, set usedsubpixorder --- src/zm_monitor.cpp | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 179c32b54..ed744fd60 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1039,6 +1039,7 @@ bool Monitor::connect() { // Uh, why nothing? Why not nullptr? snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "nothing"); video_store_data->size = sizeof(VideoStoreData); + usedsubpixorder = camera->SubpixelOrder(); // Used in CheckSignal shared_data->valid = true; } else if ( !shared_data->valid ) { Error("Shared data not initialised by capture daemon for monitor %s", name.c_str()); @@ -1543,41 +1544,45 @@ bool Monitor::CheckSignal(const Image *image) { int colours = image->Colours(); int index = 0; - for ( int i = 0; i < signal_check_points; i++ ) { - while ( true ) { + for (int i = 0; i < signal_check_points; i++) { + while (true) { // Why the casting to long long? also note that on a 64bit cpu, long long is 128bits index = (int)(((long long)rand()*(long long)(pixels-1))/RAND_MAX); - if ( !config.timestamp_on_capture || !label_format[0] ) + if (!config.timestamp_on_capture || !label_format[0]) break; // Avoid sampling the rows with timestamp in - if (index < (label_coord.y_ * width) || index >= (label_coord.y_ + Image::LINE_HEIGHT) * width) { + if ( + index < (label_coord.y_ * width) + || + index >= (label_coord.y_ + Image::LINE_HEIGHT) * width + ) { break; } } - if ( colours == ZM_COLOUR_GRAY8 ) { - if ( *(buffer+index) != grayscale_val ) + if (colours == ZM_COLOUR_GRAY8) { + if (*(buffer+index) != grayscale_val) return true; - } else if ( colours == ZM_COLOUR_RGB24 ) { + } else if (colours == ZM_COLOUR_RGB24) { const uint8_t *ptr = buffer+(index*colours); - if ( usedsubpixorder == ZM_SUBPIX_ORDER_BGR ) { - if ( (RED_PTR_BGRA(ptr) != red_val) || (GREEN_PTR_BGRA(ptr) != green_val) || (BLUE_PTR_BGRA(ptr) != blue_val) ) + if (usedsubpixorder == ZM_SUBPIX_ORDER_BGR) { + if ((RED_PTR_BGRA(ptr) != red_val) || (GREEN_PTR_BGRA(ptr) != green_val) || (BLUE_PTR_BGRA(ptr) != blue_val)) return true; } else { /* Assume RGB */ - if ( (RED_PTR_RGBA(ptr) != red_val) || (GREEN_PTR_RGBA(ptr) != green_val) || (BLUE_PTR_RGBA(ptr) != blue_val) ) + if ((RED_PTR_RGBA(ptr) != red_val) || (GREEN_PTR_RGBA(ptr) != green_val) || (BLUE_PTR_RGBA(ptr) != blue_val)) return true; } - } else if ( colours == ZM_COLOUR_RGB32 ) { - if ( usedsubpixorder == ZM_SUBPIX_ORDER_ARGB || usedsubpixorder == ZM_SUBPIX_ORDER_ABGR ) { - if ( ARGB_ABGR_ZEROALPHA(*(((const Rgb*)buffer)+index)) != ARGB_ABGR_ZEROALPHA(colour_val) ) + } else if (colours == ZM_COLOUR_RGB32) { + if (usedsubpixorder == ZM_SUBPIX_ORDER_ARGB || usedsubpixorder == ZM_SUBPIX_ORDER_ABGR) { + if (ARGB_ABGR_ZEROALPHA(*(((const Rgb*)buffer)+index)) != ARGB_ABGR_ZEROALPHA(colour_val)) return true; } else { /* Assume RGBA or BGRA */ - if ( RGBA_BGRA_ZEROALPHA(*(((const Rgb*)buffer)+index)) != RGBA_BGRA_ZEROALPHA(colour_val) ) + if (RGBA_BGRA_ZEROALPHA(*(((const Rgb*)buffer)+index)) != RGBA_BGRA_ZEROALPHA(colour_val)) return true; } } From e01fcf1e94936353202c85858f5353b2b1fca603 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 17 May 2021 16:30:33 -0400 Subject: [PATCH 060/394] Only set rotation if doing passthrough. undef hw_device_ctx in Close. --- src/zm_videostore.cpp | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index f4559e48a..990a98ed6 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -312,7 +312,6 @@ bool VideoStore::open() { video_out_codec = nullptr; } - Debug(1, "Success"); AVDictionaryEntry *e = nullptr; while ((e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != nullptr) { Warning("Encoder Option %s not recognized by ffmpeg codec", e->key); @@ -349,7 +348,28 @@ bool VideoStore::open() { #else avcodec_copy_context(video_out_stream->codec, video_out_ctx); #endif + video_out_stream->time_base = video_in_stream ? video_in_stream->time_base : AV_TIME_BASE_Q; + if (monitor->GetOptVideoWriter() == Monitor::PASSTHROUGH) { + // Only set orientation if doing passthrough, otherwise the frame image will be rotated + Monitor::Orientation orientation = monitor->getOrientation(); + if (orientation) { + Debug(3, "Have orientation %d", orientation); + if (orientation == Monitor::ROTATE_0) { + } else if (orientation == Monitor::ROTATE_90) { + ret = av_dict_set(&video_out_stream->metadata, "rotate", "90", 0); + if (ret < 0) Warning("%s:%d: title set failed", __FILE__, __LINE__); + } else if (orientation == Monitor::ROTATE_180) { + ret = av_dict_set(&video_out_stream->metadata, "rotate", "180", 0); + if (ret < 0) Warning("%s:%d: title set failed", __FILE__, __LINE__); + } else if (orientation == Monitor::ROTATE_270) { + ret = av_dict_set(&video_out_stream->metadata, "rotate", "270", 0); + if (ret < 0) Warning("%s:%d: title set failed", __FILE__, __LINE__); + } else { + Warning("Unsupported Orientation(%d)", orientation); + } + } // end if orientation + } // end if passthrough if (audio_in_stream and audio_in_ctx) { Debug(2, "Have audio_in_stream %p", audio_in_stream); @@ -647,9 +667,10 @@ VideoStore::~VideoStore() { video_in_ctx = nullptr; avcodec_close(video_out_ctx); + if (hw_device_ctx) av_buffer_unref(&hw_device_ctx); Debug(4, "Freeing video_out_ctx"); avcodec_free_context(&video_out_ctx); - } // end if video_out_stream + } // end if video_out_stream if (audio_out_stream) { #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) @@ -993,10 +1014,11 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet Debug(3, "Have encoding video frame count (%d)", frame_count); if (!zm_packet->out_frame) { - Debug(3, "Have no out frame. codec is %s sw_pf %d %s hw_pf %d %s", + Debug(3, "Have no out frame. codec is %s sw_pf %d %s hw_pf %d %s %dx%d", chosen_codec_data->codec_name, chosen_codec_data->sw_pix_fmt, av_get_pix_fmt_name(chosen_codec_data->sw_pix_fmt), - chosen_codec_data->hw_pix_fmt, av_get_pix_fmt_name(chosen_codec_data->hw_pix_fmt) + chosen_codec_data->hw_pix_fmt, av_get_pix_fmt_name(chosen_codec_data->hw_pix_fmt), + video_out_ctx->width, video_out_ctx->height ); AVFrame *out_frame = zm_packet->get_out_frame(video_out_ctx->width, video_out_ctx->height, chosen_codec_data->sw_pix_fmt); if (!out_frame) { From 26a9797909f9f3a824765eff2c81199cd05af574 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 16 May 2021 17:05:40 -0400 Subject: [PATCH 061/394] code style and remove orientation setting when encoding. frames are already rotated --- src/zm_videostore.cpp | 300 ++++++++++++++++++------------------------ 1 file changed, 131 insertions(+), 169 deletions(-) diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 0ecac9609..080cc0998 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -115,7 +115,7 @@ bool VideoStore::open() { Debug(1, "Opening video storage stream %s format: %s", filename, format); int ret = avformat_alloc_output_context2(&oc, nullptr, nullptr, filename); - if ( ret < 0 ) { + if (ret < 0) { Warning( "Could not create video storage stream %s as no out ctx" " could be assigned based on filename: %s", @@ -123,9 +123,9 @@ bool VideoStore::open() { } // Couldn't deduce format from filename, trying from format name - if ( !oc ) { + if (!oc) { avformat_alloc_output_context2(&oc, nullptr, format, filename); - if ( !oc ) { + if (!oc) { Error( "Could not create video storage stream %s as no out ctx" " could not be assigned based on filename or format %s", @@ -142,11 +142,11 @@ bool VideoStore::open() { out_format = oc->oformat; out_format->flags |= AVFMT_TS_NONSTRICT; // allow non increasing dts - if ( video_in_stream ) { + if (video_in_stream) { #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) zm_dump_codecpar(video_in_stream->codecpar); #endif - if ( monitor->GetOptVideoWriter() == Monitor::PASSTHROUGH ) { + if (monitor->GetOptVideoWriter() == Monitor::PASSTHROUGH) { // Don't care what codec, just copy parameters video_out_ctx = avcodec_alloc_context3(nullptr); // There might not be a useful video_in_stream. v4l in might not populate this very @@ -155,12 +155,12 @@ bool VideoStore::open() { #else ret = avcodec_copy_context(video_out_ctx, video_in_ctx); #endif - if ( ret < 0 ) { + if (ret < 0) { Error("Could not initialize ctx parameters"); return false; } video_out_ctx->pix_fmt = fix_deprecated_pix_fmt(video_out_ctx->pix_fmt); - if ( oc->oformat->flags & AVFMT_GLOBALHEADER ) { + if (oc->oformat->flags & AVFMT_GLOBALHEADER) { #if LIBAVCODEC_VERSION_CHECK(56, 35, 0, 64, 0) video_out_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; #else @@ -168,19 +168,19 @@ bool VideoStore::open() { #endif } video_out_ctx->time_base = video_in_ctx->time_base; - if ( ! (video_out_ctx->time_base.num && video_out_ctx->time_base.den) ) { + if (!(video_out_ctx->time_base.num && video_out_ctx->time_base.den)) { Debug(2,"No timebase found in video in context, defaulting to Q"); video_out_ctx->time_base = AV_TIME_BASE_Q; } - } else if ( monitor->GetOptVideoWriter() == Monitor::ENCODE ) { + } else if (monitor->GetOptVideoWriter() == Monitor::ENCODE) { int wanted_codec = monitor->OutputCodec(); - if ( !wanted_codec ) { + if (!wanted_codec) { // default to h264 //Debug(2, "Defaulting to H264"); //wanted_codec = AV_CODEC_ID_H264; // FIXME what is the optimal codec? Probably low latency h264 which is effectively mjpeg } else { - if ( AV_CODEC_ID_H264 != 27 and wanted_codec > 3 ) { + if (AV_CODEC_ID_H264 != 27 and wanted_codec > 3) { // Older ffmpeg had AV_CODEC_ID_MPEG2VIDEO_XVMC at position 3 has been deprecated wanted_codec += 1; } @@ -233,14 +233,14 @@ bool VideoStore::open() { video_out_ctx->height = monitor->Height(); video_out_ctx->codec_type = AVMEDIA_TYPE_VIDEO; - if ( video_out_ctx->codec_id == AV_CODEC_ID_H264 ) { + if (video_out_ctx->codec_id == AV_CODEC_ID_H264) { video_out_ctx->bit_rate = 2000000; video_out_ctx->gop_size = 12; video_out_ctx->max_b_frames = 1; - } else if ( video_out_ctx->codec_id == AV_CODEC_ID_MPEG2VIDEO ) { + } else if (video_out_ctx->codec_id == AV_CODEC_ID_MPEG2VIDEO) { /* just for testing, we also add B frames */ video_out_ctx->max_b_frames = 2; - } else if ( video_out_ctx->codec_id == AV_CODEC_ID_MPEG1VIDEO ) { + } else if (video_out_ctx->codec_id == AV_CODEC_ID_MPEG1VIDEO) { /* Needed to avoid using macroblocks in which some coeffs overflow. * This does not happen with normal video, it just happens here as * the motion of the chroma plane does not match the luma plane. */ @@ -281,7 +281,7 @@ bool VideoStore::open() { } } av_buffer_unref(&hw_frames_ref); - } + } // end if hwdevice_type != NONE #endif AVDictionary *opts = 0; @@ -289,7 +289,7 @@ bool VideoStore::open() { Debug(2, "Options? %s", Options.c_str()); ret = av_dict_parse_string(&opts, Options.c_str(), "=", ",#\n", 0); if (ret < 0) { - Warning("Could not parse ffmpeg encoder options list '%s'\n", Options.c_str()); + Warning("Could not parse ffmpeg encoder options list '%s'", Options.c_str()); } else { AVDictionaryEntry *e = nullptr; while ((e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != nullptr) { @@ -318,16 +318,15 @@ bool VideoStore::open() { Warning("Encoder Option %s not recognized by ffmpeg codec", e->key); } if (video_out_codec) break; +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + // We allocate and copy in newer ffmpeg, so need to free it avcodec_free_context(&video_out_ctx); if (hw_device_ctx) av_buffer_unref(&hw_device_ctx); +#endif } // end foreach codec if (!video_out_codec) { Error("Can't open video codec!"); -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - // We allocate and copy in newer ffmpeg, so need to free it - avcodec_free_context(&video_out_ctx); -#endif return false; } // end if can't open codec Debug(2, "Success opening codec"); @@ -336,7 +335,7 @@ bool VideoStore::open() { } // end if video_in_stream video_out_stream = avformat_new_stream(oc, video_out_codec); - if ( !video_out_stream ) { + if (!video_out_stream) { Error("Unable to create video out stream"); return false; } @@ -350,40 +349,14 @@ bool VideoStore::open() { #else avcodec_copy_context(video_out_stream->codec, video_out_ctx); #endif - // Only set orientation if doing passthrough, otherwise the frame image will be rotated - Monitor::Orientation orientation = monitor->getOrientation(); - if ( orientation ) { - Debug(3, "Have orientation %d", orientation); - if ( orientation == Monitor::ROTATE_0 ) { - } else if ( orientation == Monitor::ROTATE_90 ) { - ret = av_dict_set(&video_out_stream->metadata, "rotate", "90", 0); - if ( ret < 0 ) Warning("%s:%d: title set failed", __FILE__, __LINE__); - } else if ( orientation == Monitor::ROTATE_180 ) { - ret = av_dict_set(&video_out_stream->metadata, "rotate", "180", 0); - if ( ret < 0 ) Warning("%s:%d: title set failed", __FILE__, __LINE__); - } else if ( orientation == Monitor::ROTATE_270 ) { - ret = av_dict_set(&video_out_stream->metadata, "rotate", "270", 0); - if ( ret < 0 ) Warning("%s:%d: title set failed", __FILE__, __LINE__); - } else { - Warning("Unsupported Orientation(%d)", orientation); - } - } // end if orientation video_out_stream->time_base = video_in_stream ? video_in_stream->time_base : AV_TIME_BASE_Q; - if ( audio_in_stream and audio_in_ctx ) { + if (audio_in_stream and audio_in_ctx) { Debug(2, "Have audio_in_stream %p", audio_in_stream); - if ( -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - audio_in_stream->codecpar->codec_id -#else - audio_in_stream->codec->codec_id -#endif - != AV_CODEC_ID_AAC - ) { - + if (CODEC(audio_in_stream)->codec_id != AV_CODEC_ID_AAC) { audio_out_codec = avcodec_find_encoder(AV_CODEC_ID_AAC); - if ( !audio_out_codec ) { + if (!audio_out_codec) { Error("Could not find codec for AAC"); } else { #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) @@ -397,7 +370,7 @@ bool VideoStore::open() { #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) audio_out_ctx = avcodec_alloc_context3(audio_out_codec); - if ( !audio_out_ctx ) { + if (!audio_out_ctx) { Error("could not allocate codec ctx for AAC"); return false; } @@ -407,7 +380,7 @@ bool VideoStore::open() { audio_out_stream = avformat_new_stream(oc, audio_out_codec); audio_out_stream->time_base = audio_in_stream->time_base; - if ( !setup_resampler() ) { + if (!setup_resampler()) { return false; } } // end if found AAC codec @@ -417,7 +390,7 @@ bool VideoStore::open() { // normally we want to pass params from codec in here // but since we are doing audio passthrough we don't care audio_out_stream = avformat_new_stream(oc, audio_out_codec); - if ( !audio_out_stream ) { + if (!audio_out_stream) { Error("Could not allocate new stream"); return false; } @@ -426,7 +399,7 @@ bool VideoStore::open() { #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) // Just use the ctx to copy the parameters over audio_out_ctx = avcodec_alloc_context3(audio_out_codec); - if ( !audio_out_ctx ) { + if (!audio_out_ctx) { Error("Could not allocate new output_context"); return false; } @@ -437,20 +410,20 @@ bool VideoStore::open() { // Copy params from instream to ctx ret = avcodec_parameters_to_context( audio_out_ctx, audio_in_stream->codecpar); - if ( ret < 0 ) { + if (ret < 0) { Error("Unable to copy audio params to ctx %s", av_make_error_string(ret).c_str()); } ret = avcodec_parameters_from_context( audio_out_stream->codecpar, audio_out_ctx); - if ( ret < 0 ) { + if (ret < 0) { Error("Unable to copy audio params to stream %s", av_make_error_string(ret).c_str()); } #else audio_out_ctx = audio_out_stream->codec; ret = avcodec_copy_context(audio_out_ctx, audio_in_stream->codec); - if ( ret < 0 ) { + if (ret < 0) { Error("Unable to copy audio ctx %s", av_make_error_string(ret).c_str()); audio_out_stream = nullptr; @@ -459,7 +432,7 @@ bool VideoStore::open() { audio_out_ctx->codec_tag = 0; #endif - if ( audio_out_ctx->channels > 1 ) { + if (audio_out_ctx->channels > 1) { Warning("Audio isn't mono, changing it."); audio_out_ctx->channels = 1; } else { @@ -467,7 +440,7 @@ bool VideoStore::open() { } } // end if is AAC - if ( oc->oformat->flags & AVFMT_GLOBALHEADER ) { + if (oc->oformat->flags & AVFMT_GLOBALHEADER) { #if LIBAVCODEC_VERSION_CHECK(56, 35, 0, 64, 0) audio_out_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; #else @@ -481,14 +454,14 @@ bool VideoStore::open() { //max_stream_index is 0-based, so add 1 next_dts = new int64_t[max_stream_index+1]; - for ( int i = 0; i <= max_stream_index; i++ ) { + for (int i = 0; i <= max_stream_index; i++) { next_dts[i] = 0; } /* open the out file, if needed */ - if ( !(out_format->flags & AVFMT_NOFILE) ) { + if (!(out_format->flags & AVFMT_NOFILE)) { ret = avio_open2(&oc->pb, filename, AVIO_FLAG_WRITE, nullptr, nullptr); - if ( ret < 0 ) { + if (ret < 0) { Error("Could not open out file '%s': %s", filename, av_make_error_string(ret).c_str()); return false; @@ -496,39 +469,39 @@ bool VideoStore::open() { } zm_dump_stream_format(oc, 0, 0, 1); - if ( audio_out_stream ) zm_dump_stream_format(oc, 1, 0, 1); + if (audio_out_stream) zm_dump_stream_format(oc, 1, 0, 1); AVDictionary *opts = nullptr; std::string option_string = monitor->GetEncoderOptions(); - ret = av_dict_parse_string(&opts, option_string.c_str(), "=", ",\n", 0); - if ( ret < 0 ) { + ret = av_dict_parse_string(&opts, option_string.c_str(), "=", "#,\n", 0); + if (ret < 0) { Warning("Could not parse ffmpeg output options '%s'", option_string.c_str()); } const AVDictionaryEntry *movflags_entry = av_dict_get(opts, "movflags", nullptr, AV_DICT_MATCH_CASE); - if ( !movflags_entry ) { + if (!movflags_entry) { Debug(1, "setting movflags to frag_keyframe+empty_moov"); // Shiboleth reports that this may break seeking in mp4 before it downloads av_dict_set(&opts, "movflags", "frag_keyframe+empty_moov", 0); } else { Debug(1, "using movflags %s", movflags_entry->value); } - if ( (ret = avformat_write_header(oc, &opts)) < 0 ) { + if ((ret = avformat_write_header(oc, &opts)) < 0) { Warning("Unable to set movflags trying with defaults."); ret = avformat_write_header(oc, nullptr); - } else if ( av_dict_count(opts) != 0 ) { + } else if (av_dict_count(opts) != 0) { Info("some options not used, turn on debugging for a list."); AVDictionaryEntry *e = nullptr; - while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != nullptr ) { + while ((e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != nullptr) { Debug(1, "Encoder Option %s=>%s", e->key, e->value); - if ( !e->value ) { + if (!e->value) { av_dict_set(&opts, e->key, nullptr, 0); } } } - if ( opts ) av_dict_free(&opts); - if ( ret < 0 ) { + if (opts) av_dict_free(&opts); + if (ret < 0) { Error("Error occurred when writing out file header to %s: %s", filename, av_make_error_string(ret).c_str()); avio_closep(&oc->pb); @@ -550,13 +523,13 @@ void VideoStore::flush_codecs() { av_init_packet(&pkt); // I got crashes if the codec didn't do DELAY, so let's test for it. - if ( video_out_ctx->codec && ( video_out_ctx->codec->capabilities & + if (video_out_ctx->codec && ( video_out_ctx->codec->capabilities & #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) AV_CODEC_CAP_DELAY #else CODEC_CAP_DELAY #endif - ) ) { + )) { // Put encoder into flushing mode while ((zm_send_frame_receive_packet(video_out_ctx, nullptr, pkt)) > 0) { av_packet_rescale_ts(&pkt, @@ -568,7 +541,7 @@ void VideoStore::flush_codecs() { Debug(1, "Done writing buffered video."); } // end if have delay capability - if ( audio_out_codec ) { + if (audio_out_codec) { // The codec queues data. We need to send a flush command and out // whatever we get. Failures are not fatal. @@ -576,13 +549,13 @@ void VideoStore::flush_codecs() { /* * At the end of the file, we pass the remaining samples to * the encoder. */ - while ( zm_resample_get_delay(resample_ctx, audio_out_ctx->sample_rate) ) { + while (zm_resample_get_delay(resample_ctx, audio_out_ctx->sample_rate)) { zm_resample_audio(resample_ctx, nullptr, out_frame); - if ( zm_add_samples_to_fifo(fifo, out_frame) ) { + if (zm_add_samples_to_fifo(fifo, out_frame)) { // Should probably set the frame size to what is reported FIXME - if ( zm_get_samples_from_fifo(fifo, out_frame) ) { - if ( zm_send_frame_receive_packet(audio_out_ctx, out_frame, pkt) > 0 ) { + if (zm_get_samples_from_fifo(fifo, out_frame)) { + if (zm_send_frame_receive_packet(audio_out_ctx, out_frame, pkt) > 0) { av_packet_rescale_ts(&pkt, audio_out_ctx->time_base, audio_out_stream->time_base); @@ -591,11 +564,10 @@ void VideoStore::flush_codecs() { } } // end if data returned from fifo } - } // end while have buffered samples in the resampler Debug(2, "av_audio_fifo_size = %d", av_audio_fifo_size(fifo)); - while ( av_audio_fifo_size(fifo) > 0 ) { + while (av_audio_fifo_size(fifo) > 0) { /* Take one frame worth of audio samples from the FIFO buffer, * encode it and write it to the output file. */ @@ -603,8 +575,8 @@ void VideoStore::flush_codecs() { frame_size, av_audio_fifo_size(fifo)); // SHould probably set the frame size to what is reported FIXME - if ( av_audio_fifo_read(fifo, (void **)out_frame->data, frame_size) ) { - if ( zm_send_frame_receive_packet(audio_out_ctx, out_frame, pkt) ) { + if (av_audio_fifo_read(fifo, (void **)out_frame->data, frame_size)) { + if (zm_send_frame_receive_packet(audio_out_ctx, out_frame, pkt)) { pkt.stream_index = audio_out_stream->index; av_packet_rescale_ts(&pkt, @@ -622,7 +594,7 @@ void VideoStore::flush_codecs() { #endif while (1) { - if ( 0 >= zm_receive_packet(audio_out_ctx, pkt) ) { + if (0 >= zm_receive_packet(audio_out_ctx, pkt)) { Debug(1, "No more packets"); break; } @@ -637,11 +609,11 @@ void VideoStore::flush_codecs() { } // end flush_codecs VideoStore::~VideoStore() { - if ( oc->pb ) { + if (oc->pb) { flush_codecs(); // Flush Queues - Debug(1, "Flushing interleaved queues"); + Debug(4, "Flushing interleaved queues"); av_interleaved_write_frame(oc, nullptr); Debug(1, "Writing trailer"); @@ -653,17 +625,17 @@ VideoStore::~VideoStore() { } // When will we not be using a file ? - if ( !(out_format->flags & AVFMT_NOFILE) ) { + if (!(out_format->flags & AVFMT_NOFILE)) { /* Close the out file. */ - Debug(2, "Closing"); - if ( int rc = avio_close(oc->pb) ) { + Debug(4, "Closing"); + if (int rc = avio_close(oc->pb)) { Error("Error closing avio %s", av_err2str(rc)); } } else { Debug(3, "Not closing avio because we are not writing to a file."); } oc->pb = nullptr; - } // end if oc->pb + } // end if oc->pb // I wonder if we should be closing the file first. // I also wonder if we really need to be doing all the ctx @@ -671,14 +643,14 @@ VideoStore::~VideoStore() { // Just do a file open/close/writeheader/etc. // What if we were only doing audio recording? - if ( video_out_stream ) { + if (video_out_stream) { video_in_ctx = nullptr; Debug(4, "Freeing video_out_ctx"); avcodec_free_context(&video_out_ctx); } // end if video_out_stream - if ( audio_out_stream ) { + if (audio_out_stream) { #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) // We allocate and copy in newer ffmpeg, so need to free it //avcodec_free_context(&audio_in_ctx); @@ -686,7 +658,7 @@ VideoStore::~VideoStore() { //Debug(4, "Success freeing audio_in_ctx"); audio_in_codec = nullptr; - if ( audio_out_ctx ) { + if (audio_out_ctx) { Debug(4, "Success closing audio_out_ctx"); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) avcodec_free_context(&audio_out_ctx); @@ -694,8 +666,8 @@ VideoStore::~VideoStore() { } #if defined(HAVE_LIBAVRESAMPLE) || defined(HAVE_LIBSWRESAMPLE) - if ( resample_ctx ) { - if ( fifo ) { + if (resample_ctx) { + if (fifo) { av_audio_fifo_free(fifo); fifo = nullptr; } @@ -708,15 +680,15 @@ VideoStore::~VideoStore() { #endif #endif } - if ( in_frame ) { + if (in_frame) { av_frame_free(&in_frame); in_frame = nullptr; } - if ( out_frame ) { + if (out_frame) { av_frame_free(&out_frame); out_frame = nullptr; } - if ( converted_in_samples ) { + if (converted_in_samples) { av_free(converted_in_samples); converted_in_samples = nullptr; } @@ -732,7 +704,7 @@ VideoStore::~VideoStore() { bool VideoStore::setup_resampler() { #if !defined(HAVE_LIBSWRESAMPLE) && !defined(HAVE_LIBAVRESAMPLE) - Error("%s", "Not built with resample library. Cannot do audio conversion to AAC"); + Error("Not built with resample library. Cannot do audio conversion to AAC"); return false; #else int ret; @@ -740,17 +712,14 @@ bool VideoStore::setup_resampler() { #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) // Newer ffmpeg wants to keep everything separate... so have to lookup our own // decoder, can't reuse the one from the camera. - audio_in_codec = - avcodec_find_decoder(audio_in_stream->codecpar->codec_id); + audio_in_codec = avcodec_find_decoder(audio_in_stream->codecpar->codec_id); audio_in_ctx = avcodec_alloc_context3(audio_in_codec); // Copy params from instream to ctx - ret = avcodec_parameters_to_context( - audio_in_ctx, audio_in_stream->codecpar); - if ( ret < 0 ) { + ret = avcodec_parameters_to_context(audio_in_ctx, audio_in_stream->codecpar); + if (ret < 0) { Error("Unable to copy audio params to ctx %s", av_make_error_string(ret).c_str()); } - #else // codec is already open in ffmpeg_camera audio_in_ctx = audio_in_stream->codec; @@ -764,7 +733,7 @@ bool VideoStore::setup_resampler() { #endif // if the codec is already open, nothing is done. - if ( (ret = avcodec_open2(audio_in_ctx, audio_in_codec, nullptr)) < 0 ) { + if ((ret = avcodec_open2(audio_in_ctx, audio_in_codec, nullptr)) < 0) { Error("Can't open audio in codec!"); return false; } @@ -772,7 +741,7 @@ bool VideoStore::setup_resampler() { Debug(2, "Got something other than AAC (%s)", audio_in_codec->name); // Some formats (i.e. WAV) do not produce the proper channel layout - if ( audio_in_ctx->channel_layout == 0 ) { + if (audio_in_ctx->channel_layout == 0) { Debug(2, "Setting input channel layout to mono"); // Perhaps we should not be modifying the audio_in_ctx.... audio_in_ctx->channel_layout = av_get_channel_layout("mono"); @@ -786,7 +755,7 @@ bool VideoStore::setup_resampler() { audio_out_ctx->channel_layout = audio_in_ctx->channel_layout; audio_out_ctx->sample_fmt = audio_in_ctx->sample_fmt; #if LIBAVCODEC_VERSION_CHECK(56, 8, 0, 60, 100) - if ( !audio_out_ctx->channel_layout ) { + if (!audio_out_ctx->channel_layout) { Debug(3, "Correcting channel layout from (%" PRIi64 ") to (%" PRIi64 ")", audio_out_ctx->channel_layout, av_get_default_channel_layout(audio_out_ctx->channels) @@ -794,27 +763,26 @@ bool VideoStore::setup_resampler() { audio_out_ctx->channel_layout = av_get_default_channel_layout(audio_out_ctx->channels); } #endif - if ( audio_out_codec->supported_samplerates ) { + if (audio_out_codec->supported_samplerates) { int found = 0; - for ( unsigned int i = 0; audio_out_codec->supported_samplerates[i]; i++ ) { - if ( audio_out_ctx->sample_rate == - audio_out_codec->supported_samplerates[i] ) { + for (unsigned int i = 0; audio_out_codec->supported_samplerates[i]; i++) { + if (audio_out_ctx->sample_rate == + audio_out_codec->supported_samplerates[i]) { found = 1; break; } } - if ( found ) { + if (found) { Debug(3, "Sample rate is good %d", audio_out_ctx->sample_rate); } else { - audio_out_ctx->sample_rate = - audio_out_codec->supported_samplerates[0]; + audio_out_ctx->sample_rate = audio_out_codec->supported_samplerates[0]; Debug(1, "Sample rate is no good, setting to (%d)", audio_out_codec->supported_samplerates[0]); } } /* check that the encoder supports s16 pcm in */ - if ( !check_sample_fmt(audio_out_codec, audio_out_ctx->sample_fmt) ) { + if (!check_sample_fmt(audio_out_codec, audio_out_ctx->sample_fmt)) { Debug(3, "Encoder does not support sample format %s, setting to FLTP", av_get_sample_fmt_name(audio_out_ctx->sample_fmt)); audio_out_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP; @@ -825,12 +793,12 @@ bool VideoStore::setup_resampler() { AVDictionary *opts = nullptr; // Needed to allow AAC - if ( (ret = av_dict_set(&opts, "strict", "experimental", 0)) < 0 ) { + if ((ret = av_dict_set(&opts, "strict", "experimental", 0)) < 0) { Error("Couldn't set experimental"); } ret = avcodec_open2(audio_out_ctx, audio_out_codec, &opts); av_dict_free(&opts); - if ( ret < 0 ) { + if (ret < 0) { Error("could not open codec (%d) (%s)", ret, av_make_error_string(ret).c_str()); audio_out_codec = nullptr; @@ -886,7 +854,7 @@ bool VideoStore::setup_resampler() { #endif /** Create a new frame to store the audio samples. */ - if ( ! in_frame ) { + if (!in_frame) { if (!(in_frame = zm_av_frame_alloc())) { Error("Could not allocate in frame"); return false; @@ -894,16 +862,16 @@ bool VideoStore::setup_resampler() { } /** Create a new frame to store the audio samples. */ - if ( !(out_frame = zm_av_frame_alloc()) ) { + if (!(out_frame = zm_av_frame_alloc())) { Error("Could not allocate out frame"); av_frame_free(&in_frame); return false; } out_frame->sample_rate = audio_out_ctx->sample_rate; - if ( !(fifo = av_audio_fifo_alloc( + if (!(fifo = av_audio_fifo_alloc( audio_out_ctx->sample_fmt, - audio_out_ctx->channels, 1)) ) { + audio_out_ctx->channels, 1))) { Error("Could not allocate FIFO"); return false; } @@ -916,13 +884,13 @@ bool VideoStore::setup_resampler() { audio_in_ctx->sample_fmt, audio_in_ctx->sample_rate, 0, nullptr); - if ( !resample_ctx ) { + if (!resample_ctx) { Error("Could not allocate resample context"); av_frame_free(&in_frame); av_frame_free(&out_frame); return false; } - if ( (ret = swr_init(resample_ctx)) < 0 ) { + if ((ret = swr_init(resample_ctx)) < 0) { Error("Could not open resampler"); av_frame_free(&in_frame); av_frame_free(&out_frame); @@ -935,7 +903,7 @@ bool VideoStore::setup_resampler() { // Setup the audio resampler resample_ctx = avresample_alloc_context(); - if ( !resample_ctx ) { + if (!resample_ctx) { Error("Could not allocate resample ctx"); av_frame_free(&in_frame); av_frame_free(&out_frame); @@ -959,7 +927,7 @@ bool VideoStore::setup_resampler() { av_opt_set_int(resample_ctx, "out_channels", audio_out_ctx->channels, 0); - if ( (ret = avresample_open(resample_ctx)) < 0 ) { + if ((ret = avresample_open(resample_ctx)) < 0) { Error("Could not open resample ctx"); return false; } else { @@ -984,7 +952,7 @@ bool VideoStore::setup_resampler() { audio_out_ctx->sample_fmt, 0); converted_in_samples = reinterpret_cast(av_malloc(audioSampleBuffer_size)); - if ( !converted_in_samples ) { + if (!converted_in_samples) { Error("Could not allocate converted in sample pointers"); return false; } else { @@ -992,11 +960,11 @@ bool VideoStore::setup_resampler() { } // Setup the data pointers in the AVFrame - if ( avcodec_fill_audio_frame( + if (avcodec_fill_audio_frame( out_frame, audio_out_ctx->channels, audio_out_ctx->sample_fmt, (const uint8_t *)converted_in_samples, - audioSampleBuffer_size, 0) < 0 ) { + audioSampleBuffer_size, 0) < 0) { Error("Could not allocate converted in sample pointers"); return false; } @@ -1046,17 +1014,16 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet video_out_ctx->width, video_out_ctx->height ); - } else if ( !zm_packet->in_frame ) { + } else if (!zm_packet->in_frame) { Debug(4, "Have no in_frame"); if (zm_packet->packet.size and !zm_packet->decoded) { Debug(4, "Decoding"); - if ( !zm_packet->decode(video_in_ctx) ) { + if (!zm_packet->decode(video_in_ctx)) { Debug(2, "unable to decode yet."); return 0; } // Go straight to out frame swscale.Convert(zm_packet->in_frame, out_frame); - } else { Error("Have neither in_frame or image in packet %d!", zm_packet->image_index); @@ -1108,7 +1075,7 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet #endif int64_t in_pts = zm_packet->timestamp.tv_sec * (uint64_t)1000000 + zm_packet->timestamp.tv_usec; - if ( !video_first_pts ) { + if (!video_first_pts) { video_first_pts = in_pts; Debug(2, "No video_first_pts, set to (%" PRId64 ") secs(%" PRIi64 ") usecs(%" PRIi64 ")", video_first_pts, @@ -1144,9 +1111,9 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet ZM_DUMP_PACKET(opkt, "packet returned by codec"); // Need to adjust pts/dts values from codec time to stream time - if ( opkt.pts != AV_NOPTS_VALUE ) + if (opkt.pts != AV_NOPTS_VALUE) opkt.pts = av_rescale_q(opkt.pts, video_out_ctx->time_base, video_out_stream->time_base); - if ( opkt.dts != AV_NOPTS_VALUE ) + if (opkt.dts != AV_NOPTS_VALUE) opkt.dts = av_rescale_q(opkt.dts, video_out_ctx->time_base, video_out_stream->time_base); Debug(1, "Timebase conversions using %d/%d -> %d/%d", video_out_ctx->time_base.num, @@ -1154,10 +1121,9 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet video_out_stream->time_base.num, video_out_stream->time_base.den); - int64_t duration = 0; - if ( zm_packet->in_frame ) { - if ( zm_packet->in_frame->pkt_duration ) { + if (zm_packet->in_frame) { + if (zm_packet->in_frame->pkt_duration) { duration = av_rescale_q( zm_packet->in_frame->pkt_duration, video_in_stream->time_base, @@ -1171,9 +1137,8 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet video_out_stream->time_base.num, video_out_stream->time_base.den ); - } else if ( video_last_pts != AV_NOPTS_VALUE ) { - duration = - av_rescale_q( + } else if (video_last_pts != AV_NOPTS_VALUE) { + duration = av_rescale_q( zm_packet->in_frame->pts - video_last_pts, video_in_stream->time_base, video_out_stream->time_base); @@ -1183,8 +1148,10 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet zm_packet->in_frame->pts - video_last_pts, duration ); - if ( duration <= 0 ) { - duration = zm_packet->in_frame->pkt_duration ? zm_packet->in_frame->pkt_duration : av_rescale_q(1, video_in_stream->time_base, video_out_stream->time_base); + if (duration <= 0) { + duration = zm_packet->in_frame->pkt_duration ? + zm_packet->in_frame->pkt_duration : + av_rescale_q(1, video_in_stream->time_base, video_out_stream->time_base); } } // end if in_frmae->pkt_duration video_last_pts = zm_packet->in_frame->pts; @@ -1192,7 +1159,6 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet //duration = av_rescale_q(zm_packet->out_frame->pts - video_last_pts, video_in_stream->time_base, video_out_stream->time_base); } // end if in_frmae opkt.duration = duration; - } else { // Passthrough AVPacket *ipkt = &zm_packet->packet; ZM_DUMP_STREAM_PACKET(video_in_stream, (*ipkt), "Doing passthrough, just copy packet"); @@ -1203,8 +1169,8 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet opkt.flags = ipkt->flags; opkt.duration = ipkt->duration; - if ( ipkt->dts != AV_NOPTS_VALUE ) { - if ( !video_first_dts ) { + if (ipkt->dts != AV_NOPTS_VALUE) { + if (!video_first_dts) { Debug(2, "Starting video first_dts will become %" PRId64, ipkt->dts); video_first_dts = ipkt->dts; } @@ -1213,14 +1179,13 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet opkt.dts = next_dts[video_out_stream->index] ? av_rescale_q(next_dts[video_out_stream->index], video_out_stream->time_base, video_in_stream->time_base) : 0; Debug(3, "Setting dts to video_next_dts %" PRId64 " from %" PRId64, opkt.dts, next_dts[video_out_stream->index]); } - if ( ipkt->pts != AV_NOPTS_VALUE ) { + if (ipkt->pts != AV_NOPTS_VALUE) { opkt.pts = ipkt->pts - video_first_dts; } else { opkt.pts = AV_NOPTS_VALUE; } av_packet_rescale_ts(&opkt, video_in_stream->time_base, video_out_stream->time_base); - ZM_DUMP_STREAM_PACKET(video_out_stream, opkt, "after pts adjustment"); } // end if codec matches @@ -1232,18 +1197,17 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet } // end int VideoStore::writeVideoFramePacket( AVPacket *ipkt ) int VideoStore::writeAudioFramePacket(const std::shared_ptr &zm_packet) { - - AVPacket *ipkt = &zm_packet->packet; - int ret; - - if ( !audio_out_stream ) { + if (!audio_out_stream) { Debug(1, "Called writeAudioFramePacket when no audio_out_stream"); return 0; // FIXME -ve return codes do not free packet in ffmpeg_camera at the moment } + + AVPacket *ipkt = &zm_packet->packet; + int ret; ZM_DUMP_STREAM_PACKET(audio_in_stream, (*ipkt), "input packet"); - if ( !audio_first_dts ) { + if (!audio_first_dts) { audio_first_dts = ipkt->dts; audio_next_pts = audio_out_ctx->frame_size; } @@ -1251,10 +1215,10 @@ int VideoStore::writeAudioFramePacket(const std::shared_ptr &zm_packet Debug(3, "audio first_dts to %" PRId64, audio_first_dts); // Need to adjust pts before feeding to decoder.... should really copy the pkt instead of modifying it - if ( audio_out_codec ) { + if (audio_out_codec) { // I wonder if we can get multiple frames per packet? Probably ret = zm_send_packet_receive_frame(audio_in_ctx, in_frame, *ipkt); - if ( ret < 0 ) { + if (ret < 0) { Debug(3, "failed to receive frame code: %d", ret); return 0; } @@ -1262,15 +1226,15 @@ int VideoStore::writeAudioFramePacket(const std::shared_ptr &zm_packet AVFrame *input_frame = in_frame; - while ( zm_resample_audio(resample_ctx, input_frame, out_frame) ) { + while (zm_resample_audio(resample_ctx, input_frame, out_frame)) { //out_frame->pkt_duration = in_frame->pkt_duration; // resampling doesn't alter duration - if ( zm_add_samples_to_fifo(fifo, out_frame) <= 0 ) + if (zm_add_samples_to_fifo(fifo, out_frame) <= 0) break; // We put the samples into the fifo so we are basically resetting the frame out_frame->nb_samples = audio_out_ctx->frame_size; - if ( zm_get_samples_from_fifo(fifo, out_frame) <= 0 ) + if (zm_get_samples_from_fifo(fifo, out_frame) <= 0) break; out_frame->pts = audio_next_pts; @@ -1279,7 +1243,7 @@ int VideoStore::writeAudioFramePacket(const std::shared_ptr &zm_packet zm_dump_frame(out_frame, "Out frame after resample"); av_init_packet(&opkt); - if ( zm_send_frame_receive_packet(audio_out_ctx, out_frame, opkt) <= 0 ) + if (zm_send_frame_receive_packet(audio_out_ctx, out_frame, opkt) <= 0) break; // Scale the PTS of the outgoing packet to be the correct time base @@ -1290,12 +1254,11 @@ int VideoStore::writeAudioFramePacket(const std::shared_ptr &zm_packet write_packet(&opkt, audio_out_stream); zm_av_packet_unref(&opkt); - if ( zm_resample_get_delay(resample_ctx, out_frame->sample_rate) < out_frame->nb_samples) + if (zm_resample_get_delay(resample_ctx, out_frame->sample_rate) < out_frame->nb_samples) break; // This will send a null frame, emptying out the resample buffer input_frame = nullptr; - } // end while there is data in the resampler - + } // end while there is data in the resampler } else { av_init_packet(&opkt); opkt.data = ipkt->data; @@ -1321,15 +1284,15 @@ int VideoStore::write_packet(AVPacket *pkt, AVStream *stream) { pkt->pos = -1; pkt->stream_index = stream->index; - if ( pkt->dts == AV_NOPTS_VALUE ) { + if (pkt->dts == AV_NOPTS_VALUE) { Debug(1, "undef dts, fixing by setting to stream cur_dts %" PRId64, stream->cur_dts); pkt->dts = stream->cur_dts; - } else if ( pkt->dts < stream->cur_dts ) { + } else if (pkt->dts < stream->cur_dts) { Debug(1, "non increasing dts, fixing. our dts %" PRId64 " stream cur_dts %" PRId64, pkt->dts, stream->cur_dts); pkt->dts = stream->cur_dts; } - if ( pkt->dts > pkt->pts ) { + if (pkt->dts > pkt->pts) { Debug(1, "pkt.dts(%" PRId64 ") must be <= pkt.pts(%" PRId64 ")." "Decompression must happen before presentation.", @@ -1343,9 +1306,8 @@ int VideoStore::write_packet(AVPacket *pkt, AVStream *stream) { stream->index, next_dts[stream->index]); int ret = av_interleaved_write_frame(oc, pkt); - if ( ret != 0 ) { - Error("Error writing packet: %s", - av_make_error_string(ret).c_str()); + if (ret != 0) { + Error("Error writing packet: %s", av_make_error_string(ret).c_str()); } else { Debug(4, "Success writing packet"); } From 68fb87c8616eb760f7dd3e04daec2e1377d65b64 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 17 May 2021 16:30:47 -0400 Subject: [PATCH 062/394] Remove debug --- src/zm_packet.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/zm_packet.cpp b/src/zm_packet.cpp index 799a514de..5b33266e7 100644 --- a/src/zm_packet.cpp +++ b/src/zm_packet.cpp @@ -42,10 +42,8 @@ ZMPacket::ZMPacket() : pts(0), decoded(0) { - Debug(1, "ZMPacket"); av_init_packet(&packet); packet.size = 0; // So we can detect whether it has been filled. - Debug(1, "ZMPacket"); } ZMPacket::ZMPacket(Image *i, const timeval &tv) : From 24b3f4c1b75a78bd964ad1271197b3ea2491e949 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 17 May 2021 16:32:19 -0400 Subject: [PATCH 063/394] When de-interlacing, need to wait for decoder. Fixes race --- src/zm_monitor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 179c32b54..dd5c82503 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1850,7 +1850,7 @@ bool Monitor::Analyse() { /* try to stay behind the decoder. */ if (decoding_enabled) { - while (!snap->image and !snap->decoded and !zm_terminate and !analysis_thread->Stopped()) { + while ((!snap->image or deinterlacing_value) and !snap->decoded and !zm_terminate and !analysis_thread->Stopped()) { // Need to wait for the decoder thread. Debug(1, "Waiting for decode"); packet_lock->wait(); From deb12f5613a1351eb78b09f61593eae64005b2d3 Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Tue, 18 May 2021 00:18:26 +0200 Subject: [PATCH 064/394] Restore GnuTLS support after VLA removal 298415fff3ceac53d3d972700b584c08672f91eb made variables constexpr which led to build failures with GnuTLS. --- src/zm_rtsp_auth.cpp | 19 +++++++++++++------ src/zm_user.cpp | 7 +++++-- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/zm_rtsp_auth.cpp b/src/zm_rtsp_auth.cpp index 1a7ea629b..ae05e1e7d 100644 --- a/src/zm_rtsp_auth.cpp +++ b/src/zm_rtsp_auth.cpp @@ -20,6 +20,7 @@ #include "zm_logger.h" #include "zm_utils.h" +#include #include namespace zm { @@ -141,8 +142,10 @@ std::string Authenticator::computeDigestResponse(const std::string &method, cons #if HAVE_DECL_MD5 MD5((unsigned char*)ha1Data.c_str(), ha1Data.length(), md5buf); #elif HAVE_DECL_GNUTLS_FINGERPRINT - gnutls_datum_t md5dataha1 = { (unsigned char*)ha1Data.c_str(), (unsigned int)ha1Data.length() }; - gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha1, md5buf, &md5len ); + gnutls_datum_t md5dataha1 = {(unsigned char *) ha1Data.c_str(), (unsigned int) ha1Data.length()}; + size_t md5_len_tmp = md5len; + gnutls_fingerprint(GNUTLS_DIG_MD5, &md5dataha1, md5buf, &md5_len_tmp); + assert(md5_len_tmp == md5len); #endif for ( unsigned int j = 0; j < md5len; j++ ) { sprintf(&md5HexBuf[2*j], "%02x", md5buf[j] ); @@ -156,8 +159,10 @@ std::string Authenticator::computeDigestResponse(const std::string &method, cons #if HAVE_DECL_MD5 MD5((unsigned char*)ha2Data.c_str(), ha2Data.length(), md5buf ); #elif HAVE_DECL_GNUTLS_FINGERPRINT - gnutls_datum_t md5dataha2 = { (unsigned char*)ha2Data.c_str(), (unsigned int)ha2Data.length() }; - gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha2, md5buf, &md5len ); + gnutls_datum_t md5dataha2 = {(unsigned char *) ha2Data.c_str(), (unsigned int) ha2Data.length()}; + md5_len_tmp = md5len; + gnutls_fingerprint(GNUTLS_DIG_MD5, &md5dataha2, md5buf, &md5_len_tmp); + assert(md5_len_tmp == md5len); #endif for ( unsigned int j = 0; j < md5len; j++ ) { sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] ); @@ -177,8 +182,10 @@ std::string Authenticator::computeDigestResponse(const std::string &method, cons #if HAVE_DECL_MD5 MD5((unsigned char*)digestData.c_str(), digestData.length(), md5buf); #elif HAVE_DECL_GNUTLS_FINGERPRINT - gnutls_datum_t md5datadigest = { (unsigned char*)digestData.c_str(), (unsigned int)digestData.length() }; - gnutls_fingerprint( GNUTLS_DIG_MD5, &md5datadigest, md5buf, &md5len ); + gnutls_datum_t md5datadigest = {(unsigned char *) digestData.c_str(), (unsigned int) digestData.length()}; + md5_len_tmp = md5len; + gnutls_fingerprint(GNUTLS_DIG_MD5, &md5datadigest, md5buf, &md5_len_tmp); + assert(md5_len_tmp == md5len); #endif for ( unsigned int j = 0; j < md5len; j++ ) { sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] ); diff --git a/src/zm_user.cpp b/src/zm_user.cpp index 95f61da4e..a17db9e0f 100644 --- a/src/zm_user.cpp +++ b/src/zm_user.cpp @@ -22,6 +22,7 @@ #include "zm_crypt.h" #include "zm_logger.h" #include "zm_utils.h" +#include #include #if HAVE_GNUTLS_GNUTLS_H @@ -262,8 +263,10 @@ User *zmLoadAuthUser(const char *auth, bool use_remote_addr) { #if HAVE_DECL_MD5 MD5((unsigned char *)auth_key, strlen(auth_key), md5sum); #elif HAVE_DECL_GNUTLS_FINGERPRINT - gnutls_datum_t md5data = { (unsigned char *)auth_key, (unsigned int)strlen(auth_key) }; - gnutls_fingerprint(GNUTLS_DIG_MD5, &md5data, md5sum, &md5len); + gnutls_datum_t md5data = {(unsigned char *) auth_key, (unsigned int) strlen(auth_key)}; + size_t md5_len_tmp = md5len; + gnutls_fingerprint(GNUTLS_DIG_MD5, &md5data, md5sum, &md5_len_tmp); + assert(md5_len_tmp == md5len); #endif unsigned char *md5sum_ptr = md5sum; char *auth_md5_ptr = auth_md5; From c1260028afd85c686dc079331bac0769c3a203b9 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 18 May 2021 10:10:19 -0400 Subject: [PATCH 065/394] fix warnings not being displayed --- web/skins/classic/views/js/monitor.js.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/views/js/monitor.js.php b/web/skins/classic/views/js/monitor.js.php index 6b0ac3b87..3a1493909 100644 --- a/web/skins/classic/views/js/monitor.js.php +++ b/web/skins/classic/views/js/monitor.js.php @@ -45,6 +45,7 @@ rtspStreamNames[\''.validJsStr($row['RTSPStreamName']).'\'] = true; function validateForm( form ) { var errors = new Array(); + var warnings = new Array(); if ( form.elements['newMonitor[Name]'].value.search( /[^\w\-\.\(\)\:\/ ]/ ) >= 0 ) errors[errors.length] = ""; @@ -74,7 +75,7 @@ function validateForm( form ) { } else if ( form.elements['newMonitor[Type]'].value == 'Ffmpeg' ) { if ( !form.elements['newMonitor[Path]'].value ) { errors[errors.length] = ""; - } else if ( form.elements['newMonitor[Path]'].value.match( /[\!\*'\(\)\$ ,#\[\]]/) ) { + } else if (form.elements['newMonitor[Path]'].value.match(/[\!\*'\(\)\$ ,#\[\]]/)) { warnings[warnings.length] = ""; } @@ -162,7 +163,6 @@ function validateForm( form ) { return false; } - var warnings = new Array(); if ( (form.elements['newMonitor[Function]'].value != 'Monitor') && (form.elements['newMonitor[Function]'].value != 'None') ) { if ( (form.elements['newMonitor[SaveJPEGs]'].value == '0') && (form.elements['newMonitor[VideoWriter]'].value == '0') ) { warnings[warnings.length] = ""; From fffe72a3fa6c187f029a2223d45b065ab1b0e56a Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 18 May 2021 10:51:29 -0400 Subject: [PATCH 066/394] Split calculateAuthHash out from generateAuthHash. API is sessionless, so we just want provide a means of getting the auth hash without caching or do anything else fancy with it. --- web/includes/auth.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/web/includes/auth.php b/web/includes/auth.php index 224f88abf..7e85e4ba2 100644 --- a/web/includes/auth.php +++ b/web/includes/auth.php @@ -208,6 +208,14 @@ function getAuthUser($auth) { return null; } // end getAuthUser($auth) +function calculateAuthHash($remoteAddr) { + global $user; + $local_time = localtime(); + $authKey = ZM_AUTH_HASH_SECRET.$user['Username'].$user['Password'].$remoteAddr.$local_time[2].$local_time[3].$local_time[4].$local_time[5]; + #ZM\Debug("Generated using hour:".$local_time[2] . ' mday:' . $local_time[3] . ' month:'.$local_time[4] . ' year: ' . $local_time[5] ); + return md5($authKey); +} + function generateAuthHash($useRemoteAddr, $force=false) { global $user; if (ZM_OPT_USE_AUTH and (ZM_AUTH_RELAY == 'hashed') and isset($user['Username']) and isset($user['Password']) and isset($_SESSION)) { @@ -218,16 +226,8 @@ function generateAuthHash($useRemoteAddr, $force=false) { # Appending the remoteAddr prevents us from using an auth hash generated for a different ip if ($force or ( !isset($_SESSION['AuthHash'.$_SESSION['remoteAddr']]) ) or ( $_SESSION['AuthHashGeneratedAt'] < $mintime )) { + $auth = calculateAuthHash($useRemoteAddr?$_SESSION['remoteAddr']:''); # Don't both regenerating Auth Hash if an hour hasn't gone by yet - $local_time = localtime(); - $authKey = ''; - if ($useRemoteAddr) { - $authKey = ZM_AUTH_HASH_SECRET.$user['Username'].$user['Password'].$_SESSION['remoteAddr'].$local_time[2].$local_time[3].$local_time[4].$local_time[5]; - } else { - $authKey = ZM_AUTH_HASH_SECRET.$user['Username'].$user['Password'].$local_time[2].$local_time[3].$local_time[4].$local_time[5]; - } - #ZM\Debug("Generated using hour:".$local_time[2] . ' mday:' . $local_time[3] . ' month:'.$local_time[4] . ' year: ' . $local_time[5] ); - $auth = md5($authKey); $_SESSION['AuthHash'.$_SESSION['remoteAddr']] = $auth; $_SESSION['AuthHashGeneratedAt'] = $time; # Because we don't write out the session, it shouldn't actually get written out to disk. However if it does, the GeneratedAt should protect us. From 8c7e00418700afe6f299c02d6bdb5b6df91d624f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 18 May 2021 10:52:07 -0400 Subject: [PATCH 067/394] Use calculateAuthHash instead of generateAuthHash because we are sessionless and improve output on success and failure. Fixes #2329 --- web/api/app/Controller/MonitorsController.php | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/web/api/app/Controller/MonitorsController.php b/web/api/app/Controller/MonitorsController.php index 4ecca6236..eb4eecf1f 100644 --- a/web/api/app/Controller/MonitorsController.php +++ b/web/api/app/Controller/MonitorsController.php @@ -266,7 +266,7 @@ class MonitorsController extends AppController { if ( $mToken ) { $auth = ' -T '.$mToken; } else if ( ZM_AUTH_RELAY == 'hashed' ) { - $auth = ' -A '.generateAuthHash(ZM_AUTH_HASH_IPS); + $auth = ' -A '.calculateAuthHash(ZM_AUTH_HASH_IPS?$_SERVER['REMOTE_ADDR']:''); } else if ( ZM_AUTH_RELAY == 'plain' ) { # Plain requires the plain text password which must either be in request or stored in session $password = $this->request->query('pass') ? $this->request->query('pass') : $this->request->data('pass');; @@ -290,12 +290,19 @@ class MonitorsController extends AppController { } $shellcmd = escapeshellcmd(ZM_PATH_BIN."/zmu $verbose -m$id $q $auth"); - $status = exec ($shellcmd); - - $this->set(array( - 'status' => $status, - '_serialize' => array('status'), - )); + $status = exec($shellcmd, $output, $rc); + if ($status) { + $this->set(array( + 'status'=>$rc, + 'error'=>$output, + '_serialize' => array('status','error'), + )); + } else { + $this->set(array( + 'status' => 'Ok', + '_serialize' => array('status'), + )); + } } // Check if a daemon is running for the monitor id From 63501843460247ae0c0759e700a005c452b4f732 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 18 May 2021 11:12:37 -0400 Subject: [PATCH 068/394] Add Event_Summary model so that it is included in monitor listing --- web/api/app/Model/Event_Summary.php | 49 +++++++++++++++++++++++++++++ web/api/app/Model/Monitor.php | 5 +++ 2 files changed, 54 insertions(+) create mode 100644 web/api/app/Model/Event_Summary.php diff --git a/web/api/app/Model/Event_Summary.php b/web/api/app/Model/Event_Summary.php new file mode 100644 index 000000000..b9395ab1b --- /dev/null +++ b/web/api/app/Model/Event_Summary.php @@ -0,0 +1,49 @@ + array( + 'numeric' => array( + 'rule' => array('numeric'), + //'message' => 'Your custom message here', + //'allowEmpty' => false, + //'required' => false, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + ); +} diff --git a/web/api/app/Model/Monitor.php b/web/api/app/Model/Monitor.php index 1a80fed41..cc4ef1375 100644 --- a/web/api/app/Model/Monitor.php +++ b/web/api/app/Model/Monitor.php @@ -134,6 +134,11 @@ class Monitor extends AppModel { 'className' => 'Monitor_Status', 'foreignKey' => 'MonitorId', 'joinTable' => 'Monitor_Status', + ), + 'Event_Summary' => array( + 'className' => 'Event_Summary', + 'foreignKey' => 'MonitorId', + 'joinTable' => 'Event_Summaries', ) ); From bfd3131e63c265fdb70daa61d49eae77c3dbd61c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 19 May 2021 10:04:54 -0400 Subject: [PATCH 069/394] try additional unrefs of hw_device_ctx and unref'ing after freeing codec --- src/zm_videostore.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index e97e0b3e9..92c482764 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -272,7 +272,7 @@ bool VideoStore::open() { frames_ctx->initial_pool_size = 20; if ((ret = av_hwframe_ctx_init(hw_frames_ref)) < 0) { Error("Failed to initialize hwaccel frame context." - "Error code: %s",av_err2str(ret)); + "Error code: %s", av_err2str(ret)); av_buffer_unref(&hw_frames_ref); } else { video_out_ctx->hw_frames_ctx = av_buffer_ref(hw_frames_ref); @@ -281,6 +281,7 @@ bool VideoStore::open() { } } av_buffer_unref(&hw_frames_ref); + av_buffer_unref(&hw_device_ctx); } // end if hwdevice_type != NONE #endif @@ -666,9 +667,12 @@ VideoStore::~VideoStore() { video_in_ctx = nullptr; avcodec_close(video_out_ctx); - if (hw_device_ctx) av_buffer_unref(&hw_device_ctx); - Debug(4, "Freeing video_out_ctx"); + Debug(3, "Freeing video_out_ctx"); avcodec_free_context(&video_out_ctx); + if (hw_device_ctx) { + Debug(3, "Freeing hw_device_ctx"); + av_buffer_unref(&hw_device_ctx); + } } // end if video_out_stream if (audio_out_stream) { From 31adca440f1f51673c2725a3759cf80d89e9bf81 Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Mon, 17 May 2021 10:46:50 +0200 Subject: [PATCH 070/394] Utils: Fix possible name clash between std::clamp and ZM::clamp The naming was ambiguous when compinling in C++17 mode. (cherry picked from commit a335e740f3b8abcd3085afaa06820074e4f20e95) --- src/zm_utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zm_utils.h b/src/zm_utils.h index 88e00a785..13bbc2dcf 100644 --- a/src/zm_utils.h +++ b/src/zm_utils.h @@ -96,7 +96,7 @@ constexpr const T &clamp(const T &v, const T &lo, const T &hi, Compare comp) { template constexpr const T &clamp(const T &v, const T &lo, const T &hi) { - return clamp(v, lo, hi, std::less{}); + return ZM::clamp(v, lo, hi, std::less{}); } } From bcb2f63fab0c6439b371180fae029bc562486066 Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Sun, 16 May 2021 15:20:16 +0200 Subject: [PATCH 071/394] Image: Fix a dynamic-stack-buffer-overflow when filling polygons Make sure we don't read past the end of global_edges when i = 0. We are moving the elements backwards so at most n_global_edges - 1 elements can be moved. ==6818==ERROR: AddressSanitizer: dynamic-stack-buffer-overflow on address 0x7ffff888ae00 at pc 0x7fe4fd7be8ae bp 0x7ffff888ac90 sp 0x7ffff888a440 READ of size 96 at 0x7ffff888ae00 thread T0 #0 0x7fe4fd7be8ad in __interceptor_memmove (/lib/x86_64-linux-gnu/libasan.so.5+0x378ad) #1 0x56524b2dba31 in Image::Fill(unsigned int, int, Polygon const&) /root/zoneminder/src/zm_image.cpp:2514 #2 0x56524af55530 in Monitor::DumpZoneImage(char const*) /root/zoneminder/src/zm_monitor.cpp:1510 #3 0x56524aeb38cb in main /root/zoneminder/src/zmu.cpp:574 #4 0x7fe4fb2b009a in __libc_start_main ../csu/libc-start.c:308 #5 0x56524aeb87a9 in _start (/root/zoneminder/cmake-build-relwithdebinfo-remote/src/zmu+0xf87a9) (cherry picked from commit 63cea992a0f28a8a683d5f4159d57c57d5ec2e30) --- src/zm_image.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/zm_image.cpp b/src/zm_image.cpp index 909b4db15..6b6fe8ef1 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -2511,8 +2511,7 @@ void Image::Fill(Rgb colour, int density, const Polygon &polygon) { Debug(9, "Moving global edge"); active_edges[n_active_edges++] = global_edges[i]; if ( i < (n_global_edges-1) ) { - //memcpy( &global_edges[i], &global_edges[i+1], sizeof(*global_edges)*(n_global_edges-i) ); - memmove( &global_edges[i], &global_edges[i+1], sizeof(*global_edges)*(n_global_edges-i) ); + memmove(&global_edges[i], &global_edges[i + 1], sizeof(*global_edges) * (n_global_edges - i - 1)); i--; } n_global_edges--; From 730a057e18f98def0bbf25171df2988020e12d5e Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Mon, 17 May 2021 00:20:05 +0200 Subject: [PATCH 072/394] LocalCamera: Fix some format warnings reported by clang `capturePixFormat` and `imagePixFormat` are no enum entries. Do no try to log them as FourCC. (cherry picked from commit 77068163a322462a5ffb56ffc3a9cbf425957f9d) --- src/zm_local_camera.cpp | 46 +++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/src/zm_local_camera.cpp b/src/zm_local_camera.cpp index b72c38015..3ff3608c0 100644 --- a/src/zm_local_camera.cpp +++ b/src/zm_local_camera.cpp @@ -381,7 +381,11 @@ LocalCamera::LocalCamera( } else { if ( capture ) { Info("Selected capture palette: %s (0x%02hhx%02hhx%02hhx%02hhx)", - palette_desc, (palette>>24)&0xff, (palette>>16)&0xff, (palette>>8)&0xff, (palette)&0xff); + palette_desc, + static_cast((palette >> 24) & 0xff), + static_cast((palette >> 16) & 0xff), + static_cast((palette >> 8) & 0xff), + static_cast((palette) & 0xff)); } } } @@ -438,8 +442,10 @@ LocalCamera::LocalCamera( } else { if ( capture ) { #if HAVE_LIBSWSCALE - Info("No direct match for the selected palette (0x%02hhx%02hhx%02hhx%02hhx) and target colorspace (%02u). Format conversion is required, performance penalty expected", - (capturePixFormat>>24)&0xff,((capturePixFormat>>16)&0xff),((capturePixFormat>>8)&0xff),((capturePixFormat)&0xff), colours); + Info( + "No direct match for the selected palette (%d) and target colorspace (%02u). Format conversion is required, performance penalty expected", + capturePixFormat, + colours); #else Info("No direct match for the selected palette and target colorspace. Format conversion is required, performance penalty expected"); #endif @@ -463,13 +469,11 @@ LocalCamera::LocalCamera( if ( capture ) { #if LIBSWSCALE_VERSION_CHECK(0, 8, 0, 8, 0) if ( !sws_isSupportedInput(capturePixFormat) ) { - Error("swscale does not support the used capture format: 0x%02hhx%02hhx%02hhx%02hhx", - (capturePixFormat>>24)&0xff,((capturePixFormat>>16)&0xff),((capturePixFormat>>8)&0xff),((capturePixFormat)&0xff)); + Error("swscale does not support the used capture format: %d", capturePixFormat); conversion_type = 2; /* Try ZM format conversions */ } if ( !sws_isSupportedOutput(imagePixFormat) ) { - Error("swscale does not support the target format: 0x%02hhx%02hhx%02hhx%02hhx", - (imagePixFormat>>24)&0xff,((imagePixFormat>>16)&0xff),((imagePixFormat>>8)&0xff),((imagePixFormat)&0xff)); + Error("swscale does not support the target format: 0x%d", imagePixFormat); conversion_type = 2; /* Try ZM format conversions */ } #endif @@ -1212,14 +1216,14 @@ uint32_t LocalCamera::AutoSelectFormat(int p_colours) { /* Got a format. Copy it to the array */ strcpy(fmt_desc[nIndex], (const char*)(fmtinfo.description)); fmt_fcc[nIndex] = fmtinfo.pixelformat; - + Debug(3, "Got format: %s (0x%02hhx%02hhx%02hhx%02hhx) at index %d", - fmt_desc[nIndex], - (fmt_fcc[nIndex]>>24)&0xff, - (fmt_fcc[nIndex]>>16)&0xff, - (fmt_fcc[nIndex]>>8)&0xff, - (fmt_fcc[nIndex])&0xff, - nIndex); + fmt_desc[nIndex], + static_cast((fmt_fcc[nIndex] >> 24) & 0xff), + static_cast((fmt_fcc[nIndex] >> 16) & 0xff), + static_cast((fmt_fcc[nIndex] >> 8) & 0xff), + static_cast((fmt_fcc[nIndex]) & 0xff), + nIndex); /* Proceed to the next index */ memset(&fmtinfo, 0, sizeof(fmtinfo)); @@ -1248,13 +1252,23 @@ uint32_t LocalCamera::AutoSelectFormat(int p_colours) { for ( unsigned int j=0; j < nIndex; j++ ) { if ( preferedformats[i] == fmt_fcc[j] ) { Debug(6, "Choosing format: %s (0x%02hhx%02hhx%02hhx%02hhx) at index %u", - fmt_desc[j],fmt_fcc[j]&0xff, (fmt_fcc[j]>>8)&0xff, (fmt_fcc[j]>>16)&0xff, (fmt_fcc[j]>>24)&0xff ,j); + fmt_desc[j], + static_cast(fmt_fcc[j] & 0xff), + static_cast((fmt_fcc[j] >> 8) & 0xff), + static_cast((fmt_fcc[j] >> 16) & 0xff), + static_cast((fmt_fcc[j] >> 24) & 0xff), + j); /* Found a format! */ nIndexUsed = j; break; } else { Debug(6, "No match for format: %s (0x%02hhx%02hhx%02hhx%02hhx) at index %u", - fmt_desc[j],fmt_fcc[j]&0xff, (fmt_fcc[j]>>8)&0xff, (fmt_fcc[j]>>16)&0xff, (fmt_fcc[j]>>24)&0xff ,j); + fmt_desc[j], + static_cast(fmt_fcc[j] & 0xff), + static_cast((fmt_fcc[j] >> 8) & 0xff), + static_cast((fmt_fcc[j] >> 16) & 0xff), + static_cast((fmt_fcc[j] >> 24) & 0xff), + j); } } } From db944cbca83a779cf33ddcb5774b6891f38a1466 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 19 May 2021 16:11:25 -0400 Subject: [PATCH 073/394] add debug of frame and align to 32bytes --- src/zm_swscale.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/zm_swscale.cpp b/src/zm_swscale.cpp index b5cf6eded..cdec77108 100644 --- a/src/zm_swscale.cpp +++ b/src/zm_swscale.cpp @@ -159,11 +159,11 @@ int SWScale::Convert( } #endif - int alignment = 1; + int alignment = width % 32 ? 1 : 32; /* Check the buffer sizes */ size_t needed_insize = GetBufferSize(in_pf, width, height); - if ( needed_insize > in_buffer_size ) { - Debug(1, + if (needed_insize > in_buffer_size) { + Warning( "The input buffer size does not match the expected size for the input format. Required: %zu for %dx%d %d Available: %zu", needed_insize, width, @@ -172,7 +172,7 @@ int SWScale::Convert( in_buffer_size); } size_t needed_outsize = GetBufferSize(out_pf, new_width, new_height); - if ( needed_outsize > out_buffer_size ) { + if (needed_outsize > out_buffer_size) { Error("The output buffer is undersized for the output format. Required: %zu Available: %zu", needed_outsize, out_buffer_size); @@ -200,6 +200,7 @@ int SWScale::Convert( Error("Failed filling input frame with input buffer"); return -7; } + zm_dump_frame(input_avframe, "convert in frame"); #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) if (av_image_fill_arrays(output_avframe->data, output_avframe->linesize, out_buffer, out_pf, new_width, new_height, alignment) <= 0) { @@ -210,6 +211,7 @@ int SWScale::Convert( Error("Failed filling output frame with output buffer"); return -8; } + zm_dump_frame(output_avframe, "convert out frame"); /* Do the conversion */ if ( !sws_scale(swscale_ctx, From f6a271c90df7ce68f863fb3d88834bee836da611 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 18 May 2021 10:53:16 -0400 Subject: [PATCH 074/394] Merge pull request #3240 from connortechnology/fix_3237 fix warnings not being displayed --- web/skins/classic/views/js/monitor.js.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/views/js/monitor.js.php b/web/skins/classic/views/js/monitor.js.php index 6b0ac3b87..3a1493909 100644 --- a/web/skins/classic/views/js/monitor.js.php +++ b/web/skins/classic/views/js/monitor.js.php @@ -45,6 +45,7 @@ rtspStreamNames[\''.validJsStr($row['RTSPStreamName']).'\'] = true; function validateForm( form ) { var errors = new Array(); + var warnings = new Array(); if ( form.elements['newMonitor[Name]'].value.search( /[^\w\-\.\(\)\:\/ ]/ ) >= 0 ) errors[errors.length] = ""; @@ -74,7 +75,7 @@ function validateForm( form ) { } else if ( form.elements['newMonitor[Type]'].value == 'Ffmpeg' ) { if ( !form.elements['newMonitor[Path]'].value ) { errors[errors.length] = ""; - } else if ( form.elements['newMonitor[Path]'].value.match( /[\!\*'\(\)\$ ,#\[\]]/) ) { + } else if (form.elements['newMonitor[Path]'].value.match(/[\!\*'\(\)\$ ,#\[\]]/)) { warnings[warnings.length] = ""; } @@ -162,7 +163,6 @@ function validateForm( form ) { return false; } - var warnings = new Array(); if ( (form.elements['newMonitor[Function]'].value != 'Monitor') && (form.elements['newMonitor[Function]'].value != 'None') ) { if ( (form.elements['newMonitor[SaveJPEGs]'].value == '0') && (form.elements['newMonitor[VideoWriter]'].value == '0') ) { warnings[warnings.length] = ""; From a4df8df5e517f692de4a7afb69eea045b9387572 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 19 May 2021 20:29:31 -0400 Subject: [PATCH 075/394] Fix status of delete button when archived. Change title to say that you can't delete due to archived --- web/skins/classic/views/js/event.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js index b3819cf30..53e42dcc8 100644 --- a/web/skins/classic/views/js/event.js +++ b/web/skins/classic/views/js/event.js @@ -936,7 +936,8 @@ function initPage() { editBtn.prop('disabled', !canEdit.Events); exportBtn.prop('disabled', !canView.Events); downloadBtn.prop('disabled', !canView.Events); - deleteBtn.prop('disabled', !canEdit.Events); + deleteBtn.prop('disabled', !(!eventData.Archived && canEdit.Events)); + deleteBtn.prop('title', eventData.Archived ? "You cannot delete an archived event." : ""); // Don't enable the back button if there is no previous zm page to go back to backBtn.prop('disabled', !document.referrer.length); @@ -979,14 +980,14 @@ function initPage() { // Manage the UNARCHIVE button bindButton('#unarchiveBtn', 'click', null, function onUnarchiveClick(evt) { - if ( ! canEdit.Events ) { + if (!canEdit.Events) { enoperm(); return; } evt.preventDefault(); $j.getJSON(thisUrl + '?request=events&task=unarchive&eids[]='+eventData.Id) .done( function(data) { - //FIXME: update the status of the unarchive button reather than reload the whole page + //FIXME: update the status of the unarchive button rather than reload the whole page window.location.reload(true); }) .fail(logAjaxFail); From 1abbc187e9c40320079faf3dcc2c9f0da0494de2 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 18 May 2021 12:04:15 -0400 Subject: [PATCH 076/394] Merge pull request #3242 from connortechnology/fix_3232_api_monitor_event_summaries Add Event_Summary model so that it is included in monitor listing --- web/api/app/Model/Event_Summary.php | 49 +++++++++++++++++++++++++++++ web/api/app/Model/Monitor.php | 5 +++ 2 files changed, 54 insertions(+) create mode 100644 web/api/app/Model/Event_Summary.php diff --git a/web/api/app/Model/Event_Summary.php b/web/api/app/Model/Event_Summary.php new file mode 100644 index 000000000..b9395ab1b --- /dev/null +++ b/web/api/app/Model/Event_Summary.php @@ -0,0 +1,49 @@ + array( + 'numeric' => array( + 'rule' => array('numeric'), + //'message' => 'Your custom message here', + //'allowEmpty' => false, + //'required' => false, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + ); +} diff --git a/web/api/app/Model/Monitor.php b/web/api/app/Model/Monitor.php index 1a80fed41..cc4ef1375 100644 --- a/web/api/app/Model/Monitor.php +++ b/web/api/app/Model/Monitor.php @@ -134,6 +134,11 @@ class Monitor extends AppModel { 'className' => 'Monitor_Status', 'foreignKey' => 'MonitorId', 'joinTable' => 'Monitor_Status', + ), + 'Event_Summary' => array( + 'className' => 'Event_Summary', + 'foreignKey' => 'MonitorId', + 'joinTable' => 'Event_Summaries', ) ); From 0b4c35c58e74a65e3d9941766ef2c59212012468 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 18 May 2021 12:04:30 -0400 Subject: [PATCH 077/394] Merge pull request #3241 from connortechnology/fix_3239 Fix 3239 --- web/api/app/Controller/MonitorsController.php | 21 ++++++++++++------- web/includes/auth.php | 18 ++++++++-------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/web/api/app/Controller/MonitorsController.php b/web/api/app/Controller/MonitorsController.php index 4ecca6236..eb4eecf1f 100644 --- a/web/api/app/Controller/MonitorsController.php +++ b/web/api/app/Controller/MonitorsController.php @@ -266,7 +266,7 @@ class MonitorsController extends AppController { if ( $mToken ) { $auth = ' -T '.$mToken; } else if ( ZM_AUTH_RELAY == 'hashed' ) { - $auth = ' -A '.generateAuthHash(ZM_AUTH_HASH_IPS); + $auth = ' -A '.calculateAuthHash(ZM_AUTH_HASH_IPS?$_SERVER['REMOTE_ADDR']:''); } else if ( ZM_AUTH_RELAY == 'plain' ) { # Plain requires the plain text password which must either be in request or stored in session $password = $this->request->query('pass') ? $this->request->query('pass') : $this->request->data('pass');; @@ -290,12 +290,19 @@ class MonitorsController extends AppController { } $shellcmd = escapeshellcmd(ZM_PATH_BIN."/zmu $verbose -m$id $q $auth"); - $status = exec ($shellcmd); - - $this->set(array( - 'status' => $status, - '_serialize' => array('status'), - )); + $status = exec($shellcmd, $output, $rc); + if ($status) { + $this->set(array( + 'status'=>$rc, + 'error'=>$output, + '_serialize' => array('status','error'), + )); + } else { + $this->set(array( + 'status' => 'Ok', + '_serialize' => array('status'), + )); + } } // Check if a daemon is running for the monitor id diff --git a/web/includes/auth.php b/web/includes/auth.php index 224f88abf..7e85e4ba2 100644 --- a/web/includes/auth.php +++ b/web/includes/auth.php @@ -208,6 +208,14 @@ function getAuthUser($auth) { return null; } // end getAuthUser($auth) +function calculateAuthHash($remoteAddr) { + global $user; + $local_time = localtime(); + $authKey = ZM_AUTH_HASH_SECRET.$user['Username'].$user['Password'].$remoteAddr.$local_time[2].$local_time[3].$local_time[4].$local_time[5]; + #ZM\Debug("Generated using hour:".$local_time[2] . ' mday:' . $local_time[3] . ' month:'.$local_time[4] . ' year: ' . $local_time[5] ); + return md5($authKey); +} + function generateAuthHash($useRemoteAddr, $force=false) { global $user; if (ZM_OPT_USE_AUTH and (ZM_AUTH_RELAY == 'hashed') and isset($user['Username']) and isset($user['Password']) and isset($_SESSION)) { @@ -218,16 +226,8 @@ function generateAuthHash($useRemoteAddr, $force=false) { # Appending the remoteAddr prevents us from using an auth hash generated for a different ip if ($force or ( !isset($_SESSION['AuthHash'.$_SESSION['remoteAddr']]) ) or ( $_SESSION['AuthHashGeneratedAt'] < $mintime )) { + $auth = calculateAuthHash($useRemoteAddr?$_SESSION['remoteAddr']:''); # Don't both regenerating Auth Hash if an hour hasn't gone by yet - $local_time = localtime(); - $authKey = ''; - if ($useRemoteAddr) { - $authKey = ZM_AUTH_HASH_SECRET.$user['Username'].$user['Password'].$_SESSION['remoteAddr'].$local_time[2].$local_time[3].$local_time[4].$local_time[5]; - } else { - $authKey = ZM_AUTH_HASH_SECRET.$user['Username'].$user['Password'].$local_time[2].$local_time[3].$local_time[4].$local_time[5]; - } - #ZM\Debug("Generated using hour:".$local_time[2] . ' mday:' . $local_time[3] . ' month:'.$local_time[4] . ' year: ' . $local_time[5] ); - $auth = md5($authKey); $_SESSION['AuthHash'.$_SESSION['remoteAddr']] = $auth; $_SESSION['AuthHashGeneratedAt'] = $time; # Because we don't write out the session, it shouldn't actually get written out to disk. However if it does, the GeneratedAt should protect us. From 1cd59c189dcf8305954ec882690d945f7359603f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 17 May 2021 16:30:47 -0400 Subject: [PATCH 078/394] Remove debug --- src/zm_packet.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/zm_packet.cpp b/src/zm_packet.cpp index 799a514de..5b33266e7 100644 --- a/src/zm_packet.cpp +++ b/src/zm_packet.cpp @@ -42,10 +42,8 @@ ZMPacket::ZMPacket() : pts(0), decoded(0) { - Debug(1, "ZMPacket"); av_init_packet(&packet); packet.size = 0; // So we can detect whether it has been filled. - Debug(1, "ZMPacket"); } ZMPacket::ZMPacket(Image *i, const timeval &tv) : From 1d5cdf996577a79712f5519b0fe30d48c7f49f3c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 19 May 2021 21:41:35 -0400 Subject: [PATCH 079/394] use ubuntu2004 for hirsute --- utils/do_debian_package.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/do_debian_package.sh b/utils/do_debian_package.sh index 26c26aa31..8fdefe5a6 100755 --- a/utils/do_debian_package.sh +++ b/utils/do_debian_package.sh @@ -229,7 +229,7 @@ IFS=',' ;for DISTRO in `echo "$DISTROS"`; do fi; # Generate Changlog - if [ "$DISTRO" == "focal" ] || [ "$DISTRO" == "buster" ]; then + if [ "$DISTRO" == "focal" ] || [ "$DISTRO" == "buster" ] || [ "$DISTRO" == "hirsute" ]; then cp -Rpd distros/ubuntu2004 debian elif [ "$DISTRO" == "beowulf" ] then From 7ad92e51c3faec69f89a5864e5468b0c035e7388 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 20 May 2021 15:56:15 -0400 Subject: [PATCH 080/394] Fix initializers to compile on centos7 --- src/zm_monitor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index dd5c82503..4606737d4 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -380,7 +380,7 @@ Monitor::Monitor() event_close_mode(CLOSE_IDLE), #if ZM_MEM_MAPPED map_fd(-1), - mem_file(""), + mem_file({}), #else // ZM_MEM_MAPPED shm_id(-1), #endif // ZM_MEM_MAPPED @@ -388,7 +388,7 @@ Monitor::Monitor() mem_ptr(nullptr), shared_data(nullptr), trigger_data(nullptr), - video_store_data(nullptr), + video_store_data({}), shared_timestamps(nullptr), shared_images(nullptr), video_stream_id(-1), From 16d98197965fd162680817fc7c7f24a459033f37 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 20 May 2021 15:56:31 -0400 Subject: [PATCH 081/394] include memory to fix build on centos 7 --- src/zm_packetqueue.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/zm_packetqueue.h b/src/zm_packetqueue.h index cc163d2f1..a2f57dcfe 100644 --- a/src/zm_packetqueue.h +++ b/src/zm_packetqueue.h @@ -22,6 +22,7 @@ #include #include #include +#include class ZMPacket; class ZMLockedPacket; From 2f78916ab8fcf7d8eabfd186b1ded28af1c53b48 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 20 May 2021 16:28:22 -0400 Subject: [PATCH 082/394] change mem_file to std::string to resolve initialization problems --- src/zm_monitor.cpp | 42 +++++++++++++++++++++--------------------- src/zm_monitor.h | 4 ++-- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 4606737d4..839a03c5d 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -124,7 +124,7 @@ Monitor::MonitorLink::MonitorLink(unsigned int p_id, const char *p_name) : #if ZM_MEM_MAPPED map_fd = -1; - snprintf(mem_file, sizeof(mem_file), "%s/zm.mmap.%u", staticConfig.PATH_MAP.c_str(), id); + mem_file = stringtf("%s/zm.mmap.%u", staticConfig.PATH_MAP.c_str(), id); #else // ZM_MEM_MAPPED shm_id = 0; #endif // ZM_MEM_MAPPED @@ -150,9 +150,9 @@ bool Monitor::MonitorLink::connect() { Debug(1, "link.mem.size=%jd", mem_size); #if ZM_MEM_MAPPED - map_fd = open(mem_file, O_RDWR, (mode_t)0600); + map_fd = open(mem_file.c_str(), O_RDWR, (mode_t)0600); if ( map_fd < 0 ) { - Debug(3, "Can't open linked memory map file %s: %s", mem_file, strerror(errno)); + Debug(3, "Can't open linked memory map file %s: %s", mem_file.c_str(), strerror(errno)); disconnect(); return false; } @@ -165,13 +165,13 @@ bool Monitor::MonitorLink::connect() { struct stat map_stat; if ( fstat(map_fd, &map_stat) < 0 ) { - Error("Can't stat linked memory map file %s: %s", mem_file, strerror(errno)); + Error("Can't stat linked memory map file %s: %s", mem_file.c_str(), strerror(errno)); disconnect(); return false; } if ( map_stat.st_size == 0 ) { - Error("Linked memory map file %s is empty: %s", mem_file, strerror(errno)); + Error("Linked memory map file %s is empty: %s", mem_file.c_str(), strerror(errno)); disconnect(); return false; } else if ( map_stat.st_size < mem_size ) { @@ -182,7 +182,7 @@ bool Monitor::MonitorLink::connect() { mem_ptr = (unsigned char *)mmap(nullptr, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0); if ( mem_ptr == MAP_FAILED ) { - Error("Can't map file %s (%jd bytes) to memory: %s", mem_file, mem_size, strerror(errno)); + Error("Can't map file %s (%jd bytes) to memory: %s", mem_file.c_str(), mem_size, strerror(errno)); disconnect(); return false; } @@ -380,7 +380,7 @@ Monitor::Monitor() event_close_mode(CLOSE_IDLE), #if ZM_MEM_MAPPED map_fd(-1), - mem_file({}), + mem_file(""), #else // ZM_MEM_MAPPED shm_id(-1), #endif // ZM_MEM_MAPPED @@ -388,7 +388,7 @@ Monitor::Monitor() mem_ptr(nullptr), shared_data(nullptr), trigger_data(nullptr), - video_store_data({}), + video_store_data(nullptr), shared_timestamps(nullptr), shared_images(nullptr), video_stream_id(-1), @@ -905,23 +905,23 @@ bool Monitor::connect() { } Debug(3, "Connecting to monitor. Purpose is %d", purpose); #if ZM_MEM_MAPPED - snprintf(mem_file, sizeof(mem_file), "%s/zm.mmap.%u", staticConfig.PATH_MAP.c_str(), id); + mem_file = stringtf("%s/zm.mmap.%u", staticConfig.PATH_MAP.c_str(), id); if (purpose != CAPTURE) { - map_fd = open(mem_file, O_RDWR); + map_fd = open(mem_file.c_str(), O_RDWR); } else { - map_fd = open(mem_file, O_RDWR|O_CREAT, (mode_t)0660); + map_fd = open(mem_file.c_str(), O_RDWR|O_CREAT, (mode_t)0660); } if (map_fd < 0) { - Error("Can't open memory map file %s: %s", mem_file, strerror(errno)); + Error("Can't open memory map file %s: %s", mem_file.c_str(), strerror(errno)); return false; } else { - Debug(3, "Success opening mmap file at (%s)", mem_file); + Debug(3, "Success opening mmap file at (%s)", mem_file.c_str()); } struct stat map_stat; if (fstat(map_fd, &map_stat) < 0) { - Error("Can't stat memory map file %s: %s, is the zmc process for this monitor running?", mem_file, strerror(errno)); + Error("Can't stat memory map file %s: %s, is the zmc process for this monitor running?", mem_file.c_str(), strerror(errno)); close(map_fd); map_fd = -1; return false; @@ -931,7 +931,7 @@ bool Monitor::connect() { if (purpose == CAPTURE) { // Allocate the size if (ftruncate(map_fd, mem_size) < 0) { - Error("Can't extend memory map file %s to %jd bytes: %s", mem_file, mem_size, strerror(errno)); + Error("Can't extend memory map file %s to %jd bytes: %s", mem_file.c_str(), mem_size, strerror(errno)); close(map_fd); map_fd = -1; return false; @@ -954,18 +954,18 @@ bool Monitor::connect() { mem_ptr = (unsigned char *)mmap(nullptr, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, map_fd, 0); if (mem_ptr == MAP_FAILED) { if (errno == EAGAIN) { - Debug(1, "Unable to map file %s (%jd bytes) to locked memory, trying unlocked", mem_file, mem_size); + Debug(1, "Unable to map file %s (%jd bytes) to locked memory, trying unlocked", mem_file.c_str(), mem_size); #endif mem_ptr = (unsigned char *)mmap(nullptr, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0); - Debug(1, "Mapped file %s (%jd bytes) to unlocked memory", mem_file, mem_size); + Debug(1, "Mapped file %s (%jd bytes) to unlocked memory", mem_file.c_str(), mem_size); #ifdef MAP_LOCKED } else { - Error("Unable to map file %s (%jd bytes) to locked memory (%s)", mem_file, mem_size, strerror(errno)); + Error("Unable to map file %s (%jd bytes) to locked memory (%s)", mem_file.c_str(), mem_size, strerror(errno)); } } #endif if ((mem_ptr == MAP_FAILED) or (mem_ptr == nullptr)) { - Error("Can't map file %s (%jd bytes) to memory: %s(%d)", mem_file, mem_size, strerror(errno), errno); + Error("Can't map file %s (%jd bytes) to memory: %s(%d)", mem_file.c_str(), mem_size, strerror(errno), errno); close(map_fd); map_fd = -1; mem_ptr = nullptr; @@ -1071,8 +1071,8 @@ bool Monitor::disconnect() { mem_ptr = nullptr; shared_data = nullptr; - if (purpose == CAPTURE and (unlink(mem_file) < 0) ) { - Warning("Can't unlink '%s': %s", mem_file, strerror(errno)); + if (purpose == CAPTURE and (unlink(mem_file.c_str()) < 0) ) { + Warning("Can't unlink '%s': %s", mem_file.c_str(), strerror(errno)); } #else // ZM_MEM_MAPPED struct shmid_ds shm_data; diff --git a/src/zm_monitor.h b/src/zm_monitor.h index aaf6e528c..69fff8c59 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -211,7 +211,7 @@ protected: #if ZM_MEM_MAPPED int map_fd; - char mem_file[PATH_MAX]; + std::string mem_file; #else // ZM_MEM_MAPPED int shm_id; #endif // ZM_MEM_MAPPED @@ -364,7 +364,7 @@ protected: #if ZM_MEM_MAPPED int map_fd; - char mem_file[PATH_MAX]; + std::string mem_file; #else // ZM_MEM_MAPPED int shm_id; #endif // ZM_MEM_MAPPED From f947c041df5ccef8d2aa4fff92990bc619e053ab Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 19 May 2021 20:29:31 -0400 Subject: [PATCH 083/394] Fix status of delete button when archived. Change title to say that you can't delete due to archived --- web/skins/classic/views/js/event.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js index b3819cf30..53e42dcc8 100644 --- a/web/skins/classic/views/js/event.js +++ b/web/skins/classic/views/js/event.js @@ -936,7 +936,8 @@ function initPage() { editBtn.prop('disabled', !canEdit.Events); exportBtn.prop('disabled', !canView.Events); downloadBtn.prop('disabled', !canView.Events); - deleteBtn.prop('disabled', !canEdit.Events); + deleteBtn.prop('disabled', !(!eventData.Archived && canEdit.Events)); + deleteBtn.prop('title', eventData.Archived ? "You cannot delete an archived event." : ""); // Don't enable the back button if there is no previous zm page to go back to backBtn.prop('disabled', !document.referrer.length); @@ -979,14 +980,14 @@ function initPage() { // Manage the UNARCHIVE button bindButton('#unarchiveBtn', 'click', null, function onUnarchiveClick(evt) { - if ( ! canEdit.Events ) { + if (!canEdit.Events) { enoperm(); return; } evt.preventDefault(); $j.getJSON(thisUrl + '?request=events&task=unarchive&eids[]='+eventData.Id) .done( function(data) { - //FIXME: update the status of the unarchive button reather than reload the whole page + //FIXME: update the status of the unarchive button rather than reload the whole page window.location.reload(true); }) .fail(logAjaxFail); From 294d29580e10a11e185c4ae82cae1f310c622cca Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 20 May 2021 15:56:15 -0400 Subject: [PATCH 084/394] Fix initializers to compile on centos7 --- src/zm_monitor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 8c8ea5943..2bb9508b0 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -380,7 +380,7 @@ Monitor::Monitor() event_close_mode(CLOSE_IDLE), #if ZM_MEM_MAPPED map_fd(-1), - mem_file(""), + mem_file({}), #else // ZM_MEM_MAPPED shm_id(-1), #endif // ZM_MEM_MAPPED @@ -388,7 +388,7 @@ Monitor::Monitor() mem_ptr(nullptr), shared_data(nullptr), trigger_data(nullptr), - video_store_data(nullptr), + video_store_data({}), shared_timestamps(nullptr), shared_images(nullptr), video_stream_id(-1), From dd99d9ff2218ac87089b97c4a50a2d1452064af5 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 20 May 2021 15:56:31 -0400 Subject: [PATCH 085/394] include memory to fix build on centos 7 --- src/zm_packetqueue.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/zm_packetqueue.h b/src/zm_packetqueue.h index cc163d2f1..a2f57dcfe 100644 --- a/src/zm_packetqueue.h +++ b/src/zm_packetqueue.h @@ -22,6 +22,7 @@ #include #include #include +#include class ZMPacket; class ZMLockedPacket; From 3540101c686b5e36b9e8ba1c471fcaec2c71d990 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 20 May 2021 16:28:22 -0400 Subject: [PATCH 086/394] change mem_file to std::string to resolve initialization problems --- src/zm_monitor.cpp | 42 +++++++++++++++++++++--------------------- src/zm_monitor.h | 4 ++-- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 2bb9508b0..d96f0e589 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -124,7 +124,7 @@ Monitor::MonitorLink::MonitorLink(unsigned int p_id, const char *p_name) : #if ZM_MEM_MAPPED map_fd = -1; - snprintf(mem_file, sizeof(mem_file), "%s/zm.mmap.%u", staticConfig.PATH_MAP.c_str(), id); + mem_file = stringtf("%s/zm.mmap.%u", staticConfig.PATH_MAP.c_str(), id); #else // ZM_MEM_MAPPED shm_id = 0; #endif // ZM_MEM_MAPPED @@ -150,9 +150,9 @@ bool Monitor::MonitorLink::connect() { Debug(1, "link.mem.size=%jd", mem_size); #if ZM_MEM_MAPPED - map_fd = open(mem_file, O_RDWR, (mode_t)0600); + map_fd = open(mem_file.c_str(), O_RDWR, (mode_t)0600); if ( map_fd < 0 ) { - Debug(3, "Can't open linked memory map file %s: %s", mem_file, strerror(errno)); + Debug(3, "Can't open linked memory map file %s: %s", mem_file.c_str(), strerror(errno)); disconnect(); return false; } @@ -165,13 +165,13 @@ bool Monitor::MonitorLink::connect() { struct stat map_stat; if ( fstat(map_fd, &map_stat) < 0 ) { - Error("Can't stat linked memory map file %s: %s", mem_file, strerror(errno)); + Error("Can't stat linked memory map file %s: %s", mem_file.c_str(), strerror(errno)); disconnect(); return false; } if ( map_stat.st_size == 0 ) { - Error("Linked memory map file %s is empty: %s", mem_file, strerror(errno)); + Error("Linked memory map file %s is empty: %s", mem_file.c_str(), strerror(errno)); disconnect(); return false; } else if ( map_stat.st_size < mem_size ) { @@ -182,7 +182,7 @@ bool Monitor::MonitorLink::connect() { mem_ptr = (unsigned char *)mmap(nullptr, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0); if ( mem_ptr == MAP_FAILED ) { - Error("Can't map file %s (%jd bytes) to memory: %s", mem_file, mem_size, strerror(errno)); + Error("Can't map file %s (%jd bytes) to memory: %s", mem_file.c_str(), mem_size, strerror(errno)); disconnect(); return false; } @@ -380,7 +380,7 @@ Monitor::Monitor() event_close_mode(CLOSE_IDLE), #if ZM_MEM_MAPPED map_fd(-1), - mem_file({}), + mem_file(""), #else // ZM_MEM_MAPPED shm_id(-1), #endif // ZM_MEM_MAPPED @@ -388,7 +388,7 @@ Monitor::Monitor() mem_ptr(nullptr), shared_data(nullptr), trigger_data(nullptr), - video_store_data({}), + video_store_data(nullptr), shared_timestamps(nullptr), shared_images(nullptr), video_stream_id(-1), @@ -905,23 +905,23 @@ bool Monitor::connect() { } Debug(3, "Connecting to monitor. Purpose is %d", purpose); #if ZM_MEM_MAPPED - snprintf(mem_file, sizeof(mem_file), "%s/zm.mmap.%u", staticConfig.PATH_MAP.c_str(), id); + mem_file = stringtf("%s/zm.mmap.%u", staticConfig.PATH_MAP.c_str(), id); if (purpose != CAPTURE) { - map_fd = open(mem_file, O_RDWR); + map_fd = open(mem_file.c_str(), O_RDWR); } else { - map_fd = open(mem_file, O_RDWR|O_CREAT, (mode_t)0660); + map_fd = open(mem_file.c_str(), O_RDWR|O_CREAT, (mode_t)0660); } if (map_fd < 0) { - Error("Can't open memory map file %s: %s", mem_file, strerror(errno)); + Error("Can't open memory map file %s: %s", mem_file.c_str(), strerror(errno)); return false; } else { - Debug(3, "Success opening mmap file at (%s)", mem_file); + Debug(3, "Success opening mmap file at (%s)", mem_file.c_str()); } struct stat map_stat; if (fstat(map_fd, &map_stat) < 0) { - Error("Can't stat memory map file %s: %s, is the zmc process for this monitor running?", mem_file, strerror(errno)); + Error("Can't stat memory map file %s: %s, is the zmc process for this monitor running?", mem_file.c_str(), strerror(errno)); close(map_fd); map_fd = -1; return false; @@ -931,7 +931,7 @@ bool Monitor::connect() { if (purpose == CAPTURE) { // Allocate the size if (ftruncate(map_fd, mem_size) < 0) { - Error("Can't extend memory map file %s to %jd bytes: %s", mem_file, mem_size, strerror(errno)); + Error("Can't extend memory map file %s to %jd bytes: %s", mem_file.c_str(), mem_size, strerror(errno)); close(map_fd); map_fd = -1; return false; @@ -954,18 +954,18 @@ bool Monitor::connect() { mem_ptr = (unsigned char *)mmap(nullptr, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, map_fd, 0); if (mem_ptr == MAP_FAILED) { if (errno == EAGAIN) { - Debug(1, "Unable to map file %s (%jd bytes) to locked memory, trying unlocked", mem_file, mem_size); + Debug(1, "Unable to map file %s (%jd bytes) to locked memory, trying unlocked", mem_file.c_str(), mem_size); #endif mem_ptr = (unsigned char *)mmap(nullptr, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0); - Debug(1, "Mapped file %s (%jd bytes) to unlocked memory", mem_file, mem_size); + Debug(1, "Mapped file %s (%jd bytes) to unlocked memory", mem_file.c_str(), mem_size); #ifdef MAP_LOCKED } else { - Error("Unable to map file %s (%jd bytes) to locked memory (%s)", mem_file, mem_size, strerror(errno)); + Error("Unable to map file %s (%jd bytes) to locked memory (%s)", mem_file.c_str(), mem_size, strerror(errno)); } } #endif if ((mem_ptr == MAP_FAILED) or (mem_ptr == nullptr)) { - Error("Can't map file %s (%jd bytes) to memory: %s(%d)", mem_file, mem_size, strerror(errno), errno); + Error("Can't map file %s (%jd bytes) to memory: %s(%d)", mem_file.c_str(), mem_size, strerror(errno), errno); close(map_fd); map_fd = -1; mem_ptr = nullptr; @@ -1071,8 +1071,8 @@ bool Monitor::disconnect() { mem_ptr = nullptr; shared_data = nullptr; - if (purpose == CAPTURE and (unlink(mem_file) < 0) ) { - Warning("Can't unlink '%s': %s", mem_file, strerror(errno)); + if (purpose == CAPTURE and (unlink(mem_file.c_str()) < 0) ) { + Warning("Can't unlink '%s': %s", mem_file.c_str(), strerror(errno)); } #else // ZM_MEM_MAPPED struct shmid_ds shm_data; diff --git a/src/zm_monitor.h b/src/zm_monitor.h index c0603a19a..3f398bd9d 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -211,7 +211,7 @@ protected: #if ZM_MEM_MAPPED int map_fd; - char mem_file[PATH_MAX]; + std::string mem_file; #else // ZM_MEM_MAPPED int shm_id; #endif // ZM_MEM_MAPPED @@ -364,7 +364,7 @@ protected: #if ZM_MEM_MAPPED int map_fd; - char mem_file[PATH_MAX]; + std::string mem_file; #else // ZM_MEM_MAPPED int shm_id; #endif // ZM_MEM_MAPPED From 4fcdeb7287f9402cffe87b970ff1bf0316c28085 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 21 May 2021 10:37:51 -0400 Subject: [PATCH 087/394] Fix alignment in get_out_frame. Fixes #3233. If width % 32 then we can't use the more efficient 32 aligned scaling. --- src/zm_packet.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/zm_packet.cpp b/src/zm_packet.cpp index 5b33266e7..b50ce275f 100644 --- a/src/zm_packet.cpp +++ b/src/zm_packet.cpp @@ -107,7 +107,7 @@ ZMPacket::~ZMPacket() { int ZMPacket::decode(AVCodecContext *ctx) { Debug(4, "about to decode video, image_index is (%d)", image_index); - if ( in_frame ) { + if (in_frame) { Error("Already have a frame?"); } else { in_frame = zm_av_frame_alloc(); @@ -117,8 +117,8 @@ int ZMPacket::decode(AVCodecContext *ctx) { //av_packet_rescale_ts(&packet, AV_TIME_BASE_Q, ctx->time_base); int ret = zm_send_packet_receive_frame(ctx, in_frame, packet); - if ( ret < 0 ) { - if ( AVERROR(EAGAIN) != ret ) { + if (ret < 0) { + if (AVERROR(EAGAIN) != ret) { Warning("Unable to receive frame : code %d %s.", ret, av_make_error_string(ret).c_str()); } @@ -126,7 +126,7 @@ int ZMPacket::decode(AVCodecContext *ctx) { return 0; } int bytes_consumed = ret; - if ( ret > 0 ) { + if (ret > 0) { zm_dump_video_frame(in_frame, "got frame"); #if HAVE_LIBAVUTIL_HWCONTEXT_H @@ -258,9 +258,11 @@ AVFrame *ZMPacket::get_out_frame(int width, int height, AVPixelFormat format) { } #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + int alignment = 32; + if (width%alignment) alignment = 1; codec_imgsize = av_image_get_buffer_size( - format, width, height, 32); + format, width, height, alignment); Debug(1, "buffer size %u from %s %dx%d", codec_imgsize, av_get_pix_fmt_name(format), width, height); buffer = (uint8_t *)av_malloc(codec_imgsize); int ret; @@ -271,7 +273,7 @@ AVFrame *ZMPacket::get_out_frame(int width, int height, AVPixelFormat format) { format, width, height, - 32))<0) { + alignment))<0) { Error("Failed to fill_arrays %s", av_make_error_string(ret).c_str()); av_frame_free(&out_frame); return nullptr; From a0df6aacaf94f2cf1a451106af262902522107b0 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 21 May 2021 10:38:55 -0400 Subject: [PATCH 088/394] Spacing --- src/zm_swscale.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/zm_swscale.cpp b/src/zm_swscale.cpp index cdec77108..c4aa6b738 100644 --- a/src/zm_swscale.cpp +++ b/src/zm_swscale.cpp @@ -128,11 +128,11 @@ int SWScale::Convert( in_buffer, in_buffer_size, out_buffer, out_buffer_size, width, height, new_width, new_height, in_pf, out_pf); /* Parameter checking */ - if ( in_buffer == nullptr ) { + if (in_buffer == nullptr) { Error("NULL Input buffer"); return -1; } - if ( out_buffer == nullptr ) { + if (out_buffer == nullptr) { Error("NULL output buffer"); return -1; } @@ -140,7 +140,7 @@ int SWScale::Convert( // Error("Invalid input or output pixel formats"); // return -2; // } - if ( !width || !height || !new_height || !new_width ) { + if (!width || !height || !new_height || !new_width) { Error("Invalid width or height"); return -3; } @@ -149,11 +149,11 @@ int SWScale::Convert( #if LIBSWSCALE_VERSION_CHECK(0, 8, 0, 8, 0) /* Warn if the input or output pixelformat is not supported */ - if ( !sws_isSupportedInput(in_pf) ) { + if (!sws_isSupportedInput(in_pf)) { Warning("swscale does not support the input format: %c%c%c%c", (in_pf)&0xff,((in_pf)&0xff),((in_pf>>16)&0xff),((in_pf>>24)&0xff)); } - if ( !sws_isSupportedOutput(out_pf) ) { + if (!sws_isSupportedOutput(out_pf)) { Warning("swscale does not support the output format: %c%c%c%c", (out_pf)&0xff,((out_pf>>8)&0xff),((out_pf>>16)&0xff),((out_pf>>24)&0xff)); } @@ -184,11 +184,19 @@ int SWScale::Convert( width, height, in_pf, new_width, new_height, out_pf, SWS_FAST_BILINEAR, nullptr, nullptr, nullptr); - if ( swscale_ctx == nullptr ) { + if (swscale_ctx == nullptr) { Error("Failed getting swscale context"); return -6; } + /* + input_avframe->format = in_pf; + input_avframe->width = width; + input_avframe->height = height; + output_avframe->format = out_pf; + output_avframe->width = new_width; + output_avframe->height = new_height; + */ /* Fill in the buffers */ #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) if (av_image_fill_arrays(input_avframe->data, input_avframe->linesize, @@ -200,7 +208,6 @@ int SWScale::Convert( Error("Failed filling input frame with input buffer"); return -7; } - zm_dump_frame(input_avframe, "convert in frame"); #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) if (av_image_fill_arrays(output_avframe->data, output_avframe->linesize, out_buffer, out_pf, new_width, new_height, alignment) <= 0) { @@ -211,7 +218,6 @@ int SWScale::Convert( Error("Failed filling output frame with output buffer"); return -8; } - zm_dump_frame(output_avframe, "convert out frame"); /* Do the conversion */ if ( !sws_scale(swscale_ctx, From 2afeaefec494aaedd61938824cb9def5d9849738 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 21 May 2021 10:46:56 -0400 Subject: [PATCH 089/394] Set version to 1.36.1 for release --- distros/redhat/zoneminder.spec | 2 +- version | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index 5cfa7175a..dc401e2d6 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -31,7 +31,7 @@ %global _hardened_build 1 Name: zoneminder -Version: 1.37.0 +Version: 1.36.1 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons diff --git a/version b/version index bf50e910e..f107550c2 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.37.0 +1.36.1 From eb387b5453296024551009994c13c2381566e37d Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Fri, 21 May 2021 15:08:40 -0500 Subject: [PATCH 090/394] use latest rtspserver commit --- web/api/app/Plugin/Crud | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/api/app/Plugin/Crud b/web/api/app/Plugin/Crud index 14292374c..0bd63fb46 160000 --- a/web/api/app/Plugin/Crud +++ b/web/api/app/Plugin/Crud @@ -1 +1 @@ -Subproject commit 14292374ccf1328f2d5db20897bd06f99ba4d938 +Subproject commit 0bd63fb464957080ead342db58ca9e01532cf1ef From cb0a421d252b423cf52a0fccfef71997a6748ba9 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Fri, 21 May 2021 15:08:40 -0500 Subject: [PATCH 091/394] use latest rtspserver commit --- web/api/app/Plugin/Crud | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/api/app/Plugin/Crud b/web/api/app/Plugin/Crud index 14292374c..0bd63fb46 160000 --- a/web/api/app/Plugin/Crud +++ b/web/api/app/Plugin/Crud @@ -1 +1 @@ -Subproject commit 14292374ccf1328f2d5db20897bd06f99ba4d938 +Subproject commit 0bd63fb464957080ead342db58ca9e01532cf1ef From e881e97c067f5c883b52c9a5a2eacb74fc5c5e83 Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Sat, 22 May 2021 10:52:57 +0200 Subject: [PATCH 092/394] Logger: Fix DB log messages not being populated We need to `resize` the buffer string since `reserve` does not guarantee that the buffer is actually available. Follow-up on 298415fff3ceac53d3d972700b584c08672f91eb --- src/zm_logger.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/zm_logger.cpp b/src/zm_logger.cpp index 872247701..266342ae4 100644 --- a/src/zm_logger.cpp +++ b/src/zm_logger.cpp @@ -528,9 +528,9 @@ void Logger::logPrint(bool hex, const char *filepath, int line, int level, const if (level <= mDatabaseLevel) { if (zmDbConnected) { - int syslogSize = syslogEnd-syslogStart; + int syslogSize = syslogEnd - syslogStart; std::string escapedString; - escapedString.reserve((syslogSize * 2) + 1); + escapedString.resize((syslogSize * 2) + 1); mysql_real_escape_string(&dbconn, &escapedString[0], syslogStart, syslogSize); escapedString.resize(std::strlen(escapedString.c_str())); From 8d8b87743e6d2a0215240912662df3c41b721a9c Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Sat, 22 May 2021 06:55:52 -0500 Subject: [PATCH 093/394] sync rpm specfile changelog with rpmfusion --- distros/redhat/zoneminder.spec | 93 ++++++++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 3 deletions(-) diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index 5cfa7175a..b493ebe7a 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -425,9 +425,96 @@ ln -sf %{_sysconfdir}/zm/www/zoneminder.nginx.conf %{_sysconfdir}/zm/www/zonemin %dir %attr(755,nginx,nginx) %{_localstatedir}/log/zoneminder %changelog -* Wed Apr 07 2021 Andrew Bauer - 1.35.23-1 -- 1.35.23 Development snapshot -- Build against rtspserver +* Fri May 21 2021 Andrew Bauer - 1.36.1-1 +- 1.36.1 release +- add rtspserver submodule + +* Wed Apr 21 2021 Andrew Bauer - 1.34.26-1 +- 1.34.26 Release + +* Sun Apr 18 2021 Andrew Bauer - 1.34.24-1 +- 1.34.24 Release + +* Thu Feb 04 2021 RPM Fusion Release Engineering - 1.34.23-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Sun Jan 31 2021 Andrew Bauer - 1.34.23-1 +- 1.34.23 Release +- create ssl cert in all cases, not just nginx + +* Fri Jan 1 2021 Leigh Scott - 1.34.22-3 +- Rebuilt for new ffmpeg snapshot + +* Fri Nov 27 2020 Sérgio Basto - 1.34.22-2 +- Mass rebuild for x264-0.161 + +* Mon Sep 28 2020 Andrew Bauer - 1.34.22-1 +- 1.34.22 Release + +* Mon Sep 28 2020 Andrew Bauer - 1.34.21-1 +- 1.34.21 Release + +* Sun Aug 23 2020 Andrew Bauer - 1.34.20-1 +- 1.34.20 Release +- Buildrequire epel-rpm-macros on rhel +- Update license references for js libraries + +* Wed Aug 19 2020 RPM Fusion Release Engineering - 1.34.18-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Thu Aug 06 2020 Andrew Bauer - 1.34.18-1 +- 1.34.18 Release + +* Thu Aug 06 2020 Andrew Bauer - 1.34.17-2 +- Disable LTO due to top level asm + +* Wed Aug 05 2020 Andrew Bauer - 1.34.17-1 +- 1.34.17 Release + +* Tue Aug 04 2020 Andrew Bauer - 1.34.16-4 +- Use new cmake build macros for f33 compat + +* Tue Jul 07 2020 Sérgio Basto - 1.34.16-3 +- Mass rebuild for x264 + +* Fri Jul 03 2020 Leigh Scott - 1.34.16-2 +- Perl 5.32 rebuild + +* Fri Jun 05 2020 Andrew Bauer - 1.34.16-1 +- 1.34.16 Release + +* Fri May 15 2020 Andrew Bauer - 1.34.14-1 +- 1.34.14 Release + +* Thu May 14 2020 Andrew Bauer - 1.34.13-1 +- 1.34.13 Release + +* Sun May 10 2020 Andrew Bauer - 1.34.12-1 +- 1.34.12 Release + +* Sat May 2 2020 Andrew Bauer - 1.34.11-1 +- 1.34.11 Release + +* Sun Apr 26 2020 Andrew Bauer - 1.34.10-1 +- 1.34.10 Release + +* Mon Apr 6 2020 Andrew Bauer - 1.34.9-1 +- 1.34.9 Release + +* Sat Mar 28 2020 Andrew Bauer - 1.34.7-1 +- 1.34.7 Release + +* Mon Mar 23 2020 Andrew Bauer - 1.34.6-1 +- 1.34.6 Release + +* Sat Feb 29 2020 Andrew Bauer - 1.34.5-1 +- 1.34.5 Release + +* Sun Feb 23 2020 Andrew Bauer - 1.34.3-1 +- 1.34.3 Release + +* Sat Feb 22 2020 RPM Fusion Release Engineering - 1.34.2-2 +- Rebuild for ffmpeg-4.3 git * Tue Feb 04 2020 Andrew Bauer - 1.34.2-1 - 1.34.2 Release From 2e12bc08cd2a4a4e62400e5c9250916333bc45dc Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Sat, 22 May 2021 06:58:44 -0500 Subject: [PATCH 094/394] add 1.37 changelog entry to rpm specfile --- distros/redhat/zoneminder.spec | 3 +++ 1 file changed, 3 insertions(+) diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index b493ebe7a..52a7bc514 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -425,6 +425,9 @@ ln -sf %{_sysconfdir}/zm/www/zoneminder.nginx.conf %{_sysconfdir}/zm/www/zonemin %dir %attr(755,nginx,nginx) %{_localstatedir}/log/zoneminder %changelog +* Sat May 22 2021 Andrew Bauer - 1.37.1-1 +- 1.37.x master branch develpment build. NOT FOR PRODUCTION. + * Fri May 21 2021 Andrew Bauer - 1.36.1-1 - 1.36.1 release - add rtspserver submodule From 800a8ecc2e771c0aa329cb88e76d77b971b5c11c Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Sat, 22 May 2021 06:55:52 -0500 Subject: [PATCH 095/394] sync rpm specfile changelog with rpmfusion --- distros/redhat/zoneminder.spec | 93 ++++++++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 3 deletions(-) diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index dc401e2d6..d8ce4a742 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -425,9 +425,96 @@ ln -sf %{_sysconfdir}/zm/www/zoneminder.nginx.conf %{_sysconfdir}/zm/www/zonemin %dir %attr(755,nginx,nginx) %{_localstatedir}/log/zoneminder %changelog -* Wed Apr 07 2021 Andrew Bauer - 1.35.23-1 -- 1.35.23 Development snapshot -- Build against rtspserver +* Fri May 21 2021 Andrew Bauer - 1.36.1-1 +- 1.36.1 release +- add rtspserver submodule + +* Wed Apr 21 2021 Andrew Bauer - 1.34.26-1 +- 1.34.26 Release + +* Sun Apr 18 2021 Andrew Bauer - 1.34.24-1 +- 1.34.24 Release + +* Thu Feb 04 2021 RPM Fusion Release Engineering - 1.34.23-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Sun Jan 31 2021 Andrew Bauer - 1.34.23-1 +- 1.34.23 Release +- create ssl cert in all cases, not just nginx + +* Fri Jan 1 2021 Leigh Scott - 1.34.22-3 +- Rebuilt for new ffmpeg snapshot + +* Fri Nov 27 2020 Sérgio Basto - 1.34.22-2 +- Mass rebuild for x264-0.161 + +* Mon Sep 28 2020 Andrew Bauer - 1.34.22-1 +- 1.34.22 Release + +* Mon Sep 28 2020 Andrew Bauer - 1.34.21-1 +- 1.34.21 Release + +* Sun Aug 23 2020 Andrew Bauer - 1.34.20-1 +- 1.34.20 Release +- Buildrequire epel-rpm-macros on rhel +- Update license references for js libraries + +* Wed Aug 19 2020 RPM Fusion Release Engineering - 1.34.18-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Thu Aug 06 2020 Andrew Bauer - 1.34.18-1 +- 1.34.18 Release + +* Thu Aug 06 2020 Andrew Bauer - 1.34.17-2 +- Disable LTO due to top level asm + +* Wed Aug 05 2020 Andrew Bauer - 1.34.17-1 +- 1.34.17 Release + +* Tue Aug 04 2020 Andrew Bauer - 1.34.16-4 +- Use new cmake build macros for f33 compat + +* Tue Jul 07 2020 Sérgio Basto - 1.34.16-3 +- Mass rebuild for x264 + +* Fri Jul 03 2020 Leigh Scott - 1.34.16-2 +- Perl 5.32 rebuild + +* Fri Jun 05 2020 Andrew Bauer - 1.34.16-1 +- 1.34.16 Release + +* Fri May 15 2020 Andrew Bauer - 1.34.14-1 +- 1.34.14 Release + +* Thu May 14 2020 Andrew Bauer - 1.34.13-1 +- 1.34.13 Release + +* Sun May 10 2020 Andrew Bauer - 1.34.12-1 +- 1.34.12 Release + +* Sat May 2 2020 Andrew Bauer - 1.34.11-1 +- 1.34.11 Release + +* Sun Apr 26 2020 Andrew Bauer - 1.34.10-1 +- 1.34.10 Release + +* Mon Apr 6 2020 Andrew Bauer - 1.34.9-1 +- 1.34.9 Release + +* Sat Mar 28 2020 Andrew Bauer - 1.34.7-1 +- 1.34.7 Release + +* Mon Mar 23 2020 Andrew Bauer - 1.34.6-1 +- 1.34.6 Release + +* Sat Feb 29 2020 Andrew Bauer - 1.34.5-1 +- 1.34.5 Release + +* Sun Feb 23 2020 Andrew Bauer - 1.34.3-1 +- 1.34.3 Release + +* Sat Feb 22 2020 RPM Fusion Release Engineering - 1.34.2-2 +- Rebuild for ffmpeg-4.3 git * Tue Feb 04 2020 Andrew Bauer - 1.34.2-1 - 1.34.2 Release From bc384d6fbe3134aa2f7adda1ef744c8709b93e9d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 22 May 2021 11:34:09 -0400 Subject: [PATCH 096/394] spacing, code style, code comments --- web/skins/classic/views/js/event.js | 269 ++++++++++++++-------------- 1 file changed, 139 insertions(+), 130 deletions(-) diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js index 53e42dcc8..b6de480a4 100644 --- a/web/skins/classic/views/js/event.js +++ b/web/skins/classic/views/js/event.js @@ -215,14 +215,14 @@ function changeReplayMode() { function changeRate() { var rate = parseInt($j('select[name="rate"]').val()); - if ( ! rate ) { + if (!rate) { pauseClicked(); - } else if ( rate < 0 ) { - if ( vid ) { //There is no reverse play with mp4. Set the speed to 0 and manually set the time back. + } else if (rate < 0) { + if (vid) { //There is no reverse play with mp4. Set the speed to 0 and manually set the time back. revSpeed = rates[rates.indexOf(-1*rate)-1]/100; clearInterval(intervalRewind); intervalRewind = setInterval(function() { - if ( vid.currentTime() <= 0 ) { + if (vid.currentTime() <= 0) { clearInterval(intervalRewind); vid.pause(); } else { @@ -254,26 +254,24 @@ function getCmdResponse(respObj, respText) { zmsBroke = false; - if ( streamCmdTimer ) { - streamCmdTimer = clearTimeout(streamCmdTimer); - } + if (streamCmdTimer) streamCmdTimer = clearTimeout(streamCmdTimer); streamStatus = respObj.status; if (!streamStatus) { console.log('No status in respObj'); console.log(respObj); return; - } else if ( streamStatus.duration && ( streamStatus.duration != parseFloat(eventData.Length) ) ) { + } else if (streamStatus.duration && ( streamStatus.duration != parseFloat(eventData.Length) )) { eventData.Length = streamStatus.duration; } - if ( streamStatus.progress > parseFloat(eventData.Length) ) { + if (streamStatus.progress > parseFloat(eventData.Length)) { console.log("Limiting progress to " + streamStatus.progress + ' >= ' + parseFloat(eventData.Length) ); streamStatus.progress = parseFloat(eventData.Length); } //Limit progress to reality var eventId = streamStatus.event; - if ( lastEventId ) { - if ( eventId != lastEventId ) { + if (lastEventId) { + if (eventId != lastEventId) { //Doesn't run on first load, prevents a double hit on event and nearEvents ajax eventQuery(eventId); initialAlarmCues(eventId); //zms uses this instead of a page reload, must call ajax+render @@ -283,7 +281,7 @@ function getCmdResponse(respObj, respText) { lastEventId = eventId; //Only fires on first load. } - if ( streamStatus.paused == true ) { + if (streamStatus.paused == true) { streamPause( ); } else { $j('select[name="rate"]').val(streamStatus.rate*100); @@ -292,19 +290,19 @@ function getCmdResponse(respObj, respText) { } $j('#progressValue').html(secsToTime(parseInt(streamStatus.progress))); $j('#zoomValue').html(streamStatus.zoom); - if ( streamStatus.zoom == "1.0" ) { - setButtonState( 'zoomOutBtn', 'unavail' ); + if (streamStatus.zoom == '1.0') { + setButtonState('zoomOutBtn', 'unavail'); } else { - setButtonState( 'zoomOutBtn', 'inactive' ); + setButtonState('zoomOutBtn', 'inactive'); } updateProgressBar(); - if ( streamStatus.auth ) { + if (streamStatus.auth) { // Try to reload the image stream. var streamImg = $j('#evtStream'); - if ( streamImg ) { - streamImg.src = streamImg.src.replace( /auth=\w+/i, 'auth='+streamStatus.auth ); + if (streamImg) { + streamImg.src = streamImg.src.replace(/auth=\w+/i, 'auth='+streamStatus.auth); } } // end if haev a new auth hash @@ -312,8 +310,8 @@ function getCmdResponse(respObj, respText) { } // end function getCmdResponse( respObj, respText ) function pauseClicked() { - if ( vid ) { - if ( intervalRewind ) { + if (vid) { + if (intervalRewind) { stopFastRev(); } vid.pause(); @@ -325,22 +323,22 @@ function pauseClicked() { function streamPause() { $j('#modeValue').html('Paused'); - setButtonState( 'pauseBtn', 'active' ); - setButtonState( 'playBtn', 'inactive' ); - setButtonState( 'fastFwdBtn', 'unavail' ); - setButtonState( 'slowFwdBtn', 'inactive' ); - setButtonState( 'slowRevBtn', 'inactive' ); - setButtonState( 'fastRevBtn', 'unavail' ); + setButtonState('pauseBtn', 'active'); + setButtonState('playBtn', 'inactive'); + setButtonState('fastFwdBtn', 'unavail'); + setButtonState('slowFwdBtn', 'inactive'); + setButtonState('slowRevBtn', 'inactive'); + setButtonState('fastRevBtn', 'unavail'); } function playClicked( ) { var rate_select = $j('select[name="rate"]'); - if ( ! rate_select.val() ) { + if (!rate_select.val()) { $j('select[name="rate"]').val(100); } - if ( vid ) { - if ( vid.paused() ) { + if (vid) { + if (vid.paused()) { vid.play(); } else { vjsPlay(); //handles fast forward and rewind @@ -352,7 +350,7 @@ function playClicked( ) { } function vjsPlay() { //catches if we change mode programatically - if ( intervalRewind ) { + if (intervalRewind) { stopFastRev(); } $j('select[name="rate"]').val(vid.playbackRate()*100); @@ -361,25 +359,25 @@ function vjsPlay() { //catches if we change mode programatically } function streamPlay( ) { - setButtonState( 'pauseBtn', 'inactive' ); - setButtonState( 'playBtn', 'active' ); - setButtonState( 'fastFwdBtn', 'inactive' ); - setButtonState( 'slowFwdBtn', 'unavail' ); - setButtonState( 'slowRevBtn', 'unavail' ); - setButtonState( 'fastRevBtn', 'inactive' ); + setButtonState('pauseBtn', 'inactive'); + setButtonState('playBtn', 'active'); + setButtonState('fastFwdBtn', 'inactive'); + setButtonState('slowFwdBtn', 'unavail'); + setButtonState('slowRevBtn', 'unavail'); + setButtonState('fastRevBtn', 'inactive'); } function streamFastFwd( action ) { - setButtonState( 'pauseBtn', 'inactive' ); - setButtonState( 'playBtn', 'inactive' ); - setButtonState( 'fastFwdBtn', 'active' ); - setButtonState( 'slowFwdBtn', 'unavail' ); - setButtonState( 'slowRevBtn', 'unavail' ); - setButtonState( 'fastRevBtn', 'inactive' ); - if ( vid ) { - if ( revSpeed != .5 ) stopFastRev(); + setButtonState('pauseBtn', 'inactive'); + setButtonState('playBtn', 'inactive'); + setButtonState('fastFwdBtn', 'active'); + setButtonState('slowFwdBtn', 'unavail'); + setButtonState('slowRevBtn', 'unavail'); + setButtonState('fastRevBtn', 'inactive'); + if (vid) { + if (revSpeed != .5) stopFastRev(); vid.playbackRate(rates[rates.indexOf(vid.playbackRate()*100)-1]/100); - if ( rates.indexOf(vid.playbackRate()*100)-1 == -1 ) { + if (rates.indexOf(vid.playbackRate()*100)-1 == -1) { setButtonState('fastFwdBtn', 'unavail'); } $j('select[name="rate"]').val(vid.playbackRate()*100); @@ -389,17 +387,16 @@ function streamFastFwd( action ) { } } - -function streamSlowFwd( action ) { - if ( vid ) { +function streamSlowFwd(action) { + if (vid) { vid.currentTime(vid.currentTime() + spf); } else { streamReq({command: CMD_SLOWFWD}); } } -function streamSlowRev( action ) { - if ( vid ) { +function streamSlowRev(action) { + if (vid) { vid.currentTime(vid.currentTime() - spf); } else { streamReq({command: CMD_SLOWREV}); @@ -414,17 +411,20 @@ function stopFastRev() { revSpeed = .5; } -function streamFastRev( action ) { - setButtonState( 'pauseBtn', 'inactive' ); - setButtonState( 'playBtn', 'inactive' ); - setButtonState( 'fastFwdBtn', 'inactive' ); - setButtonState( 'slowFwdBtn', 'unavail' ); - setButtonState( 'slowRevBtn', 'unavail' ); - setButtonState( 'fastRevBtn', 'active' ); - if ( vid ) { //There is no reverse play with mp4. Set the speed to 0 and manually set the time back. +/* Called when rewind button is clicked + * should cycle through the reverse rates including pause + */ +function streamFastRev(action) { + setButtonState('pauseBtn', 'inactive'); + setButtonState('playBtn', 'inactive'); + setButtonState('fastFwdBtn', 'inactive'); + setButtonState('slowFwdBtn', 'unavail'); + setButtonState('slowRevBtn', 'unavail'); + setButtonState('fastRevBtn', 'active'); + if (vid) { //There is no reverse play with mp4. Set the speed to 0 and manually set the time back. revSpeed = rates[rates.indexOf(revSpeed*100)-1]/100; - if ( rates.indexOf(revSpeed*100) == 0 ) { - setButtonState( 'fastRevBtn', 'unavail' ); + if (rates.indexOf(revSpeed*100) == 0) { + setButtonState('fastRevBtn', 'unavail'); } clearInterval(intervalRewind); $j('select[name="rate"]').val(-revSpeed*100); @@ -444,11 +444,12 @@ function streamFastRev( action ) { } function streamPrev(action) { - if ( action ) { + if (action) { $j(".vjsMessage").remove(); location.replace(thisUrl + '?view=event&eid=' + prevEventId + filterQuery + sortQuery); return; + /* Ideally I'd like to get back to this style if ( vid && PrevEventDefVideoPath.indexOf("view_video") > 0 ) { CurEventDefVideoPath = PrevEventDefVideoPath; eventQuery(prevEventId); @@ -458,36 +459,44 @@ function streamPrev(action) { streamReq({command: CMD_PREV}); streamPlay(); } + */ } } function streamNext(action) { - if ( action ) { - $j(".vjsMessage").remove();//This shouldn't happen - if ( nextEventId == 0 ) { //handles deleting last event. - pauseClicked(); - var hideContainer = $j('#eventVideo'); - var hideStream = $j(vid ? "#videoobj" : "#evtStream").height() + (vid ? 0 :$j("#progressBar").height()); - hideContainer.prepend('

No more events

'); - if ( vid == null ) zmsBroke = true; - return; - } - // We used to try to dynamically update all the bits in the page, which is really complex - // How about we just reload the page? - // - location.replace(thisUrl + '?view=event&eid=' + nextEventId + filterQuery + sortQuery); + if (!action) { return; - if ( vid && ( NextEventDefVideoPath.indexOf('view_video') > 0 ) ) { //on and staying with videojs - CurEventDefVideoPath = NextEventDefVideoPath; - eventQuery(nextEventId); - } else if ( zmsBroke || (vid && NextEventDefVideoPath.indexOf("view_video") < 0) || NextEventDefVideoPath.indexOf("view_video") > 0) {//reload zms, leaving vjs, moving to vjs - location.replace(thisUrl + '?view=event&eid=' + nextEventId + filterQuery + sortQuery); - } else { - streamReq({command: CMD_NEXT}); - streamPlay(); - } } -} + + $j(".vjsMessage").remove();//This shouldn't happen + if (nextEventId == 0) { //handles deleting last event. + pauseClicked(); + var hideContainer = $j('#eventVideo'); + var hideStream = $j(vid ? "#videoobj" : "#evtStream").height() + (vid ? 0 :$j("#progressBar").height()); + hideContainer.prepend('

No more events

'); + if (vid == null) zmsBroke = true; + return; + } + // We used to try to dynamically update all the bits in the page, which is really complex + // How about we just reload the page? + // + location.replace(thisUrl + '?view=event&eid=' + nextEventId + filterQuery + sortQuery); + return; + if (vid && ( NextEventDefVideoPath.indexOf('view_video') > 0 )) { + // on and staying with videojs + CurEventDefVideoPath = NextEventDefVideoPath; + eventQuery(nextEventId); + } else if ( + zmsBroke || + (vid && NextEventDefVideoPath.indexOf("view_video") < 0) || + NextEventDefVideoPath.indexOf("view_video") > 0 + ) {//reload zms, leaving vjs, moving to vjs + location.replace(thisUrl + '?view=event&eid=' + nextEventId + filterQuery + sortQuery); + } else { + streamReq({command: CMD_NEXT}); + streamPlay(); + } +} // end function streamNext(action) function vjsPanZoom(action, x, y) { //Pan and zoom with centering where the click occurs var outer = $j('#videoobj'); @@ -530,7 +539,7 @@ function vjsPanZoom(action, x, y) { //Pan and zoom with centering where the clic video.css('transform', 'matrix('+zoom+', 0, 0, '+zoom+', '+x+', '+y+')'); } -function streamZoomIn( x, y ) { +function streamZoomIn(x, y) { if (vid) { vjsPanZoom('zoom', x, y); } else { @@ -546,11 +555,11 @@ function streamZoomOut() { } } -function streamScale( scale ) { +function streamScale(scale) { streamReq({command: CMD_SCALE, scale: scale}); } -function streamPan( x, y ) { +function streamPan(x, y) { if (vid) { vjsPanZoom('pan', x, y); } else { @@ -558,7 +567,7 @@ function streamPan( x, y ) { } } -function streamSeek( offset ) { +function streamSeek(offset) { streamReq({command: CMD_SEEK, offset: offset}); } @@ -619,7 +628,7 @@ function getEventResponse(respObj, respText) { function eventQuery(eventId) { var data = {}; data.id = eventId; - if ( auth_hash ) data.auth = auth_hash; + if (auth_hash) data.auth = auth_hash; $j.getJSON(thisUrl + '?view=request&request=status&entity=event', data) .done(getEventResponse) @@ -627,7 +636,7 @@ function eventQuery(eventId) { } function getNearEventsResponse(respObj, respText) { - if ( checkStreamForErrors('getNearEventsResponse', respObj) ) { + if (checkStreamForErrors('getNearEventsResponse', respObj)) { return; } console.log(respObj); @@ -649,25 +658,25 @@ function nearEventsQuery(eventId) { } function getFrameResponse(respObj, respText) { - if ( checkStreamForErrors('getFrameResponse', respObj) ) { + if (checkStreamForErrors('getFrameResponse', respObj)) { return; } var frame = respObj.frameimage; - if ( !eventData ) { + if (!eventData) { console.error('No event '+frame.EventId+' found'); return; } - if ( !eventData['frames'] ) { + if (!eventData['frames']) { eventData['frames'] = {}; } eventData['frames'][frame.FrameId] = frame; } -function frameQuery( eventId, frameId, loadImage ) { +function frameQuery(eventId, frameId, loadImage) { var data = {}; data.loopback = loadImage; data.id = {eventId, frameId}; @@ -678,33 +687,33 @@ function frameQuery( eventId, frameId, loadImage ) { } function prevEvent() { - if ( prevEventId ) { - eventQuery( prevEventId ); - streamPrev( true ); + if (prevEventId) { + eventQuery(prevEventId); + streamPrev(true); } } function nextEvent() { - if ( nextEventId ) { - eventQuery( nextEventId ); - streamNext( true ); + if (nextEventId) { + eventQuery(nextEventId); + streamNext(true); } } -function getActResponse( respObj, respText ) { - if ( checkStreamForErrors( "getActResponse", respObj ) ) { +function getActResponse(respObj, respText) { + if (checkStreamForErrors('getActResponse', respObj)) { return; } - if ( respObj.refreshEvent ) { - eventQuery( eventData.Id ); + if (respObj.refreshEvent) { + eventQuery(eventData.Id); } } function actQuery(action, parms) { var data = {}; - if ( parms ) data = parms; - if ( auth_hash ) data.auth = auth_hash; + if (parms) data = parms; + if (auth_hash) data.auth = auth_hash; data.id = eventData.Id; data.action = action; @@ -740,7 +749,7 @@ function drawProgressBar() { // Shows current stream progress. function updateProgressBar() { - if ( ! ( eventData && streamStatus ) ) { + if (!(eventData && streamStatus)) { return; } // end if ! eventData && streamStatus var curWidth = (streamStatus.progress / parseFloat(eventData.Length)) * 100; @@ -756,10 +765,10 @@ function progressBarNav() { }); } -function handleClick( event ) { +function handleClick(event) { var target = event.target; var rect = target.getBoundingClientRect(); - if ( vid ) { + if (vid) { if (target.id != 'videoobj') return; // ignore clicks on control bar var x = event.offsetX; var y = event.offsetY; @@ -768,9 +777,9 @@ function handleClick( event ) { var y = event.page.y - rect.top; } - if ( event.shift || event.shiftKey ) { // handle both jquery and mootools + if (event.shift || event.shiftKey) { // handle both jquery and mootools streamPan(x, y); - } else if ( vid && event.ctrlKey ) { // allow zoom out by control click. useful in fullscreen + } else if (vid && event.ctrlKey) { // allow zoom out by control click. useful in fullscreen vjsPanZoom('zoomOut', x, y); } else { streamZoomIn(x, y); @@ -803,7 +812,7 @@ function getEvtStatsCookie() { var cookie = 'zmEventStats'; var stats = getCookie(cookie); - if ( !stats ) { + if (!stats) { stats = 'on'; setCookie(cookie, stats, 10*365); } @@ -845,7 +854,7 @@ function onStatsResize(vidWidth) { var minWidth = 300; // An arbitrary value in pixels used to hide the stats table var scale = $j('#scale').val(); - if ( parseInt(scale) ) { + if (parseInt(scale)) { vidWidth = vidWidth * (scale/100); } @@ -853,14 +862,14 @@ function onStatsResize(vidWidth) { //console.log("Width: " + width + " = window.width " + $j(window).width() + "- vidWidth" + vidWidth); // Hide the stats table if we have run out of room to show it properly - if ( width < minWidth ) { + if (width < minWidth) { statsBtn.prop('disabled', true); - if ( table.is(':visible') ) { + if (table.is(':visible')) { table.toggle(false); wasHidden = true; } // Show the stats table if we hid it previously and sufficient room becomes available - } else if ( width >= minWidth ) { + } else if (width >= minWidth) { statsBtn.prop('disabled', false); if ( !table.is(':visible') && wasHidden ) { table.toggle(true); @@ -873,14 +882,14 @@ function initPage() { // Load the event stats getStat(); - if ( getEvtStatsCookie() != 'on' ) { + if (getEvtStatsCookie() != 'on') { table.toggle(false); } else { onStatsResize(eventData.Width); } //FIXME prevent blocking...not sure what is happening or best way to unblock - if ( $j('#videoobj').length ) { + if ($j('#videoobj').length) { vid = videojs('videoobj'); addVideoTimingTrack(vid, LabelFormat, eventData.MonitorName, eventData.Length, eventData.StartDateTime); $j('.vjs-progress-control').append('
');//add a place for videojs only on first load @@ -894,26 +903,26 @@ function initPage() { setCookie('volume', vid.volume(), 3600); }); var cookie = getCookie('volume'); - if ( cookie ) vid.volume(cookie); + if (cookie) vid.volume(cookie); vid.on('timeupdate', function() { $j('#progressValue').html(secsToTime(Math.floor(vid.currentTime()))); }); // rate is in % so 100 would be 1x - if ( rate > 0 ) { + if (rate > 0) { // rate should be 100 = 1x, etc. vid.playbackRate(rate/100); } } else { progressBarNav(); streamCmdTimer = setTimeout(streamQuery, 500); - if ( canStreamNative ) { - if ( !$j('#imageFeed') ) { + if (canStreamNative) { + if (!$j('#imageFeed')) { console.log('No element with id tag imageFeed found.'); } else { var streamImg = $j('#imageFeed img'); - if ( !streamImg ) { + if (!streamImg) { streamImg = $j('#imageFeed object'); } $j(streamImg).click(function(event) { @@ -921,10 +930,10 @@ function initPage() { }); } } - } + } // end if videojs or mjpeg stream nearEventsQuery(eventData.Id); initialAlarmCues(eventData.Id); //call ajax+renderAlarmCues - if ( scale == '0' || scale == 'auto' ) changeScale(); + if (scale == '0' || scale == 'auto') changeScale(); document.querySelectorAll('select[name="rate"]').forEach(function(el) { el.onchange = window['changeRate']; }); @@ -995,7 +1004,7 @@ function initPage() { // Manage the EDIT button bindButton('#editBtn', 'click', null, function onEditClick(evt) { - if ( ! canEdit.Events ) { + if (!canEdit.Events) { enoperm(); return; } @@ -1039,7 +1048,7 @@ function initPage() { var cookie = 'zmEventStats'; // Toggle the visiblity of the stats table and write an appropriate cookie - if ( table.is(':visible') ) { + if (table.is(':visible')) { setCookie(cookie, 'off', 10*365); table.toggle(false); } else { @@ -1056,13 +1065,13 @@ function initPage() { // Manage the DELETE button bindButton('#deleteBtn', 'click', null, function onDeleteClick(evt) { - if ( !canEdit.Events ) { + if (!canEdit.Events) { enoperm(); return; } evt.preventDefault(); - if ( ! $j('#deleteConfirm').length ) { + if (!$j('#deleteConfirm').length) { // Load the delete confirmation modal into the DOM $j.getJSON(thisUrl + '?request=modal&modal=delconfirm') .done(function(data) { From 317ce7692eb79bff388257b89502129c74132d4d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 22 May 2021 13:38:52 -0400 Subject: [PATCH 097/394] fix behaviour of reverse and fast forward buttons. Fixes #3251 --- web/skins/classic/views/js/event.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js index b6de480a4..0a73dd9d4 100644 --- a/web/skins/classic/views/js/event.js +++ b/web/skins/classic/views/js/event.js @@ -367,7 +367,7 @@ function streamPlay( ) { setButtonState('fastRevBtn', 'inactive'); } -function streamFastFwd( action ) { +function streamFastFwd(action) { setButtonState('pauseBtn', 'inactive'); setButtonState('playBtn', 'inactive'); setButtonState('fastFwdBtn', 'active'); @@ -376,8 +376,8 @@ function streamFastFwd( action ) { setButtonState('fastRevBtn', 'inactive'); if (vid) { if (revSpeed != .5) stopFastRev(); - vid.playbackRate(rates[rates.indexOf(vid.playbackRate()*100)-1]/100); - if (rates.indexOf(vid.playbackRate()*100)-1 == -1) { + vid.playbackRate(rates[rates.indexOf(vid.playbackRate()*100)+1]/100); + if (rates.indexOf(vid.playbackRate()*100)+1 == rates.length) { setButtonState('fastFwdBtn', 'unavail'); } $j('select[name="rate"]').val(vid.playbackRate()*100); @@ -422,8 +422,9 @@ function streamFastRev(action) { setButtonState('slowRevBtn', 'unavail'); setButtonState('fastRevBtn', 'active'); if (vid) { //There is no reverse play with mp4. Set the speed to 0 and manually set the time back. - revSpeed = rates[rates.indexOf(revSpeed*100)-1]/100; - if (rates.indexOf(revSpeed*100) == 0) { + revSpeed = -1*(rates[rates.indexOf(revSpeed*-100)-1]/100); + if (rates.indexOf(revSpeed*-100) == 0) { + console.log("paused?"); setButtonState('fastRevBtn', 'unavail'); } clearInterval(intervalRewind); From e16d71b9c61e537477c96c0315e94abbc91e1bab Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 22 May 2021 11:34:09 -0400 Subject: [PATCH 098/394] spacing, code style, code comments --- web/skins/classic/views/js/event.js | 269 ++++++++++++++-------------- 1 file changed, 139 insertions(+), 130 deletions(-) diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js index 53e42dcc8..b6de480a4 100644 --- a/web/skins/classic/views/js/event.js +++ b/web/skins/classic/views/js/event.js @@ -215,14 +215,14 @@ function changeReplayMode() { function changeRate() { var rate = parseInt($j('select[name="rate"]').val()); - if ( ! rate ) { + if (!rate) { pauseClicked(); - } else if ( rate < 0 ) { - if ( vid ) { //There is no reverse play with mp4. Set the speed to 0 and manually set the time back. + } else if (rate < 0) { + if (vid) { //There is no reverse play with mp4. Set the speed to 0 and manually set the time back. revSpeed = rates[rates.indexOf(-1*rate)-1]/100; clearInterval(intervalRewind); intervalRewind = setInterval(function() { - if ( vid.currentTime() <= 0 ) { + if (vid.currentTime() <= 0) { clearInterval(intervalRewind); vid.pause(); } else { @@ -254,26 +254,24 @@ function getCmdResponse(respObj, respText) { zmsBroke = false; - if ( streamCmdTimer ) { - streamCmdTimer = clearTimeout(streamCmdTimer); - } + if (streamCmdTimer) streamCmdTimer = clearTimeout(streamCmdTimer); streamStatus = respObj.status; if (!streamStatus) { console.log('No status in respObj'); console.log(respObj); return; - } else if ( streamStatus.duration && ( streamStatus.duration != parseFloat(eventData.Length) ) ) { + } else if (streamStatus.duration && ( streamStatus.duration != parseFloat(eventData.Length) )) { eventData.Length = streamStatus.duration; } - if ( streamStatus.progress > parseFloat(eventData.Length) ) { + if (streamStatus.progress > parseFloat(eventData.Length)) { console.log("Limiting progress to " + streamStatus.progress + ' >= ' + parseFloat(eventData.Length) ); streamStatus.progress = parseFloat(eventData.Length); } //Limit progress to reality var eventId = streamStatus.event; - if ( lastEventId ) { - if ( eventId != lastEventId ) { + if (lastEventId) { + if (eventId != lastEventId) { //Doesn't run on first load, prevents a double hit on event and nearEvents ajax eventQuery(eventId); initialAlarmCues(eventId); //zms uses this instead of a page reload, must call ajax+render @@ -283,7 +281,7 @@ function getCmdResponse(respObj, respText) { lastEventId = eventId; //Only fires on first load. } - if ( streamStatus.paused == true ) { + if (streamStatus.paused == true) { streamPause( ); } else { $j('select[name="rate"]').val(streamStatus.rate*100); @@ -292,19 +290,19 @@ function getCmdResponse(respObj, respText) { } $j('#progressValue').html(secsToTime(parseInt(streamStatus.progress))); $j('#zoomValue').html(streamStatus.zoom); - if ( streamStatus.zoom == "1.0" ) { - setButtonState( 'zoomOutBtn', 'unavail' ); + if (streamStatus.zoom == '1.0') { + setButtonState('zoomOutBtn', 'unavail'); } else { - setButtonState( 'zoomOutBtn', 'inactive' ); + setButtonState('zoomOutBtn', 'inactive'); } updateProgressBar(); - if ( streamStatus.auth ) { + if (streamStatus.auth) { // Try to reload the image stream. var streamImg = $j('#evtStream'); - if ( streamImg ) { - streamImg.src = streamImg.src.replace( /auth=\w+/i, 'auth='+streamStatus.auth ); + if (streamImg) { + streamImg.src = streamImg.src.replace(/auth=\w+/i, 'auth='+streamStatus.auth); } } // end if haev a new auth hash @@ -312,8 +310,8 @@ function getCmdResponse(respObj, respText) { } // end function getCmdResponse( respObj, respText ) function pauseClicked() { - if ( vid ) { - if ( intervalRewind ) { + if (vid) { + if (intervalRewind) { stopFastRev(); } vid.pause(); @@ -325,22 +323,22 @@ function pauseClicked() { function streamPause() { $j('#modeValue').html('Paused'); - setButtonState( 'pauseBtn', 'active' ); - setButtonState( 'playBtn', 'inactive' ); - setButtonState( 'fastFwdBtn', 'unavail' ); - setButtonState( 'slowFwdBtn', 'inactive' ); - setButtonState( 'slowRevBtn', 'inactive' ); - setButtonState( 'fastRevBtn', 'unavail' ); + setButtonState('pauseBtn', 'active'); + setButtonState('playBtn', 'inactive'); + setButtonState('fastFwdBtn', 'unavail'); + setButtonState('slowFwdBtn', 'inactive'); + setButtonState('slowRevBtn', 'inactive'); + setButtonState('fastRevBtn', 'unavail'); } function playClicked( ) { var rate_select = $j('select[name="rate"]'); - if ( ! rate_select.val() ) { + if (!rate_select.val()) { $j('select[name="rate"]').val(100); } - if ( vid ) { - if ( vid.paused() ) { + if (vid) { + if (vid.paused()) { vid.play(); } else { vjsPlay(); //handles fast forward and rewind @@ -352,7 +350,7 @@ function playClicked( ) { } function vjsPlay() { //catches if we change mode programatically - if ( intervalRewind ) { + if (intervalRewind) { stopFastRev(); } $j('select[name="rate"]').val(vid.playbackRate()*100); @@ -361,25 +359,25 @@ function vjsPlay() { //catches if we change mode programatically } function streamPlay( ) { - setButtonState( 'pauseBtn', 'inactive' ); - setButtonState( 'playBtn', 'active' ); - setButtonState( 'fastFwdBtn', 'inactive' ); - setButtonState( 'slowFwdBtn', 'unavail' ); - setButtonState( 'slowRevBtn', 'unavail' ); - setButtonState( 'fastRevBtn', 'inactive' ); + setButtonState('pauseBtn', 'inactive'); + setButtonState('playBtn', 'active'); + setButtonState('fastFwdBtn', 'inactive'); + setButtonState('slowFwdBtn', 'unavail'); + setButtonState('slowRevBtn', 'unavail'); + setButtonState('fastRevBtn', 'inactive'); } function streamFastFwd( action ) { - setButtonState( 'pauseBtn', 'inactive' ); - setButtonState( 'playBtn', 'inactive' ); - setButtonState( 'fastFwdBtn', 'active' ); - setButtonState( 'slowFwdBtn', 'unavail' ); - setButtonState( 'slowRevBtn', 'unavail' ); - setButtonState( 'fastRevBtn', 'inactive' ); - if ( vid ) { - if ( revSpeed != .5 ) stopFastRev(); + setButtonState('pauseBtn', 'inactive'); + setButtonState('playBtn', 'inactive'); + setButtonState('fastFwdBtn', 'active'); + setButtonState('slowFwdBtn', 'unavail'); + setButtonState('slowRevBtn', 'unavail'); + setButtonState('fastRevBtn', 'inactive'); + if (vid) { + if (revSpeed != .5) stopFastRev(); vid.playbackRate(rates[rates.indexOf(vid.playbackRate()*100)-1]/100); - if ( rates.indexOf(vid.playbackRate()*100)-1 == -1 ) { + if (rates.indexOf(vid.playbackRate()*100)-1 == -1) { setButtonState('fastFwdBtn', 'unavail'); } $j('select[name="rate"]').val(vid.playbackRate()*100); @@ -389,17 +387,16 @@ function streamFastFwd( action ) { } } - -function streamSlowFwd( action ) { - if ( vid ) { +function streamSlowFwd(action) { + if (vid) { vid.currentTime(vid.currentTime() + spf); } else { streamReq({command: CMD_SLOWFWD}); } } -function streamSlowRev( action ) { - if ( vid ) { +function streamSlowRev(action) { + if (vid) { vid.currentTime(vid.currentTime() - spf); } else { streamReq({command: CMD_SLOWREV}); @@ -414,17 +411,20 @@ function stopFastRev() { revSpeed = .5; } -function streamFastRev( action ) { - setButtonState( 'pauseBtn', 'inactive' ); - setButtonState( 'playBtn', 'inactive' ); - setButtonState( 'fastFwdBtn', 'inactive' ); - setButtonState( 'slowFwdBtn', 'unavail' ); - setButtonState( 'slowRevBtn', 'unavail' ); - setButtonState( 'fastRevBtn', 'active' ); - if ( vid ) { //There is no reverse play with mp4. Set the speed to 0 and manually set the time back. +/* Called when rewind button is clicked + * should cycle through the reverse rates including pause + */ +function streamFastRev(action) { + setButtonState('pauseBtn', 'inactive'); + setButtonState('playBtn', 'inactive'); + setButtonState('fastFwdBtn', 'inactive'); + setButtonState('slowFwdBtn', 'unavail'); + setButtonState('slowRevBtn', 'unavail'); + setButtonState('fastRevBtn', 'active'); + if (vid) { //There is no reverse play with mp4. Set the speed to 0 and manually set the time back. revSpeed = rates[rates.indexOf(revSpeed*100)-1]/100; - if ( rates.indexOf(revSpeed*100) == 0 ) { - setButtonState( 'fastRevBtn', 'unavail' ); + if (rates.indexOf(revSpeed*100) == 0) { + setButtonState('fastRevBtn', 'unavail'); } clearInterval(intervalRewind); $j('select[name="rate"]').val(-revSpeed*100); @@ -444,11 +444,12 @@ function streamFastRev( action ) { } function streamPrev(action) { - if ( action ) { + if (action) { $j(".vjsMessage").remove(); location.replace(thisUrl + '?view=event&eid=' + prevEventId + filterQuery + sortQuery); return; + /* Ideally I'd like to get back to this style if ( vid && PrevEventDefVideoPath.indexOf("view_video") > 0 ) { CurEventDefVideoPath = PrevEventDefVideoPath; eventQuery(prevEventId); @@ -458,36 +459,44 @@ function streamPrev(action) { streamReq({command: CMD_PREV}); streamPlay(); } + */ } } function streamNext(action) { - if ( action ) { - $j(".vjsMessage").remove();//This shouldn't happen - if ( nextEventId == 0 ) { //handles deleting last event. - pauseClicked(); - var hideContainer = $j('#eventVideo'); - var hideStream = $j(vid ? "#videoobj" : "#evtStream").height() + (vid ? 0 :$j("#progressBar").height()); - hideContainer.prepend('

No more events

'); - if ( vid == null ) zmsBroke = true; - return; - } - // We used to try to dynamically update all the bits in the page, which is really complex - // How about we just reload the page? - // - location.replace(thisUrl + '?view=event&eid=' + nextEventId + filterQuery + sortQuery); + if (!action) { return; - if ( vid && ( NextEventDefVideoPath.indexOf('view_video') > 0 ) ) { //on and staying with videojs - CurEventDefVideoPath = NextEventDefVideoPath; - eventQuery(nextEventId); - } else if ( zmsBroke || (vid && NextEventDefVideoPath.indexOf("view_video") < 0) || NextEventDefVideoPath.indexOf("view_video") > 0) {//reload zms, leaving vjs, moving to vjs - location.replace(thisUrl + '?view=event&eid=' + nextEventId + filterQuery + sortQuery); - } else { - streamReq({command: CMD_NEXT}); - streamPlay(); - } } -} + + $j(".vjsMessage").remove();//This shouldn't happen + if (nextEventId == 0) { //handles deleting last event. + pauseClicked(); + var hideContainer = $j('#eventVideo'); + var hideStream = $j(vid ? "#videoobj" : "#evtStream").height() + (vid ? 0 :$j("#progressBar").height()); + hideContainer.prepend('

No more events

'); + if (vid == null) zmsBroke = true; + return; + } + // We used to try to dynamically update all the bits in the page, which is really complex + // How about we just reload the page? + // + location.replace(thisUrl + '?view=event&eid=' + nextEventId + filterQuery + sortQuery); + return; + if (vid && ( NextEventDefVideoPath.indexOf('view_video') > 0 )) { + // on and staying with videojs + CurEventDefVideoPath = NextEventDefVideoPath; + eventQuery(nextEventId); + } else if ( + zmsBroke || + (vid && NextEventDefVideoPath.indexOf("view_video") < 0) || + NextEventDefVideoPath.indexOf("view_video") > 0 + ) {//reload zms, leaving vjs, moving to vjs + location.replace(thisUrl + '?view=event&eid=' + nextEventId + filterQuery + sortQuery); + } else { + streamReq({command: CMD_NEXT}); + streamPlay(); + } +} // end function streamNext(action) function vjsPanZoom(action, x, y) { //Pan and zoom with centering where the click occurs var outer = $j('#videoobj'); @@ -530,7 +539,7 @@ function vjsPanZoom(action, x, y) { //Pan and zoom with centering where the clic video.css('transform', 'matrix('+zoom+', 0, 0, '+zoom+', '+x+', '+y+')'); } -function streamZoomIn( x, y ) { +function streamZoomIn(x, y) { if (vid) { vjsPanZoom('zoom', x, y); } else { @@ -546,11 +555,11 @@ function streamZoomOut() { } } -function streamScale( scale ) { +function streamScale(scale) { streamReq({command: CMD_SCALE, scale: scale}); } -function streamPan( x, y ) { +function streamPan(x, y) { if (vid) { vjsPanZoom('pan', x, y); } else { @@ -558,7 +567,7 @@ function streamPan( x, y ) { } } -function streamSeek( offset ) { +function streamSeek(offset) { streamReq({command: CMD_SEEK, offset: offset}); } @@ -619,7 +628,7 @@ function getEventResponse(respObj, respText) { function eventQuery(eventId) { var data = {}; data.id = eventId; - if ( auth_hash ) data.auth = auth_hash; + if (auth_hash) data.auth = auth_hash; $j.getJSON(thisUrl + '?view=request&request=status&entity=event', data) .done(getEventResponse) @@ -627,7 +636,7 @@ function eventQuery(eventId) { } function getNearEventsResponse(respObj, respText) { - if ( checkStreamForErrors('getNearEventsResponse', respObj) ) { + if (checkStreamForErrors('getNearEventsResponse', respObj)) { return; } console.log(respObj); @@ -649,25 +658,25 @@ function nearEventsQuery(eventId) { } function getFrameResponse(respObj, respText) { - if ( checkStreamForErrors('getFrameResponse', respObj) ) { + if (checkStreamForErrors('getFrameResponse', respObj)) { return; } var frame = respObj.frameimage; - if ( !eventData ) { + if (!eventData) { console.error('No event '+frame.EventId+' found'); return; } - if ( !eventData['frames'] ) { + if (!eventData['frames']) { eventData['frames'] = {}; } eventData['frames'][frame.FrameId] = frame; } -function frameQuery( eventId, frameId, loadImage ) { +function frameQuery(eventId, frameId, loadImage) { var data = {}; data.loopback = loadImage; data.id = {eventId, frameId}; @@ -678,33 +687,33 @@ function frameQuery( eventId, frameId, loadImage ) { } function prevEvent() { - if ( prevEventId ) { - eventQuery( prevEventId ); - streamPrev( true ); + if (prevEventId) { + eventQuery(prevEventId); + streamPrev(true); } } function nextEvent() { - if ( nextEventId ) { - eventQuery( nextEventId ); - streamNext( true ); + if (nextEventId) { + eventQuery(nextEventId); + streamNext(true); } } -function getActResponse( respObj, respText ) { - if ( checkStreamForErrors( "getActResponse", respObj ) ) { +function getActResponse(respObj, respText) { + if (checkStreamForErrors('getActResponse', respObj)) { return; } - if ( respObj.refreshEvent ) { - eventQuery( eventData.Id ); + if (respObj.refreshEvent) { + eventQuery(eventData.Id); } } function actQuery(action, parms) { var data = {}; - if ( parms ) data = parms; - if ( auth_hash ) data.auth = auth_hash; + if (parms) data = parms; + if (auth_hash) data.auth = auth_hash; data.id = eventData.Id; data.action = action; @@ -740,7 +749,7 @@ function drawProgressBar() { // Shows current stream progress. function updateProgressBar() { - if ( ! ( eventData && streamStatus ) ) { + if (!(eventData && streamStatus)) { return; } // end if ! eventData && streamStatus var curWidth = (streamStatus.progress / parseFloat(eventData.Length)) * 100; @@ -756,10 +765,10 @@ function progressBarNav() { }); } -function handleClick( event ) { +function handleClick(event) { var target = event.target; var rect = target.getBoundingClientRect(); - if ( vid ) { + if (vid) { if (target.id != 'videoobj') return; // ignore clicks on control bar var x = event.offsetX; var y = event.offsetY; @@ -768,9 +777,9 @@ function handleClick( event ) { var y = event.page.y - rect.top; } - if ( event.shift || event.shiftKey ) { // handle both jquery and mootools + if (event.shift || event.shiftKey) { // handle both jquery and mootools streamPan(x, y); - } else if ( vid && event.ctrlKey ) { // allow zoom out by control click. useful in fullscreen + } else if (vid && event.ctrlKey) { // allow zoom out by control click. useful in fullscreen vjsPanZoom('zoomOut', x, y); } else { streamZoomIn(x, y); @@ -803,7 +812,7 @@ function getEvtStatsCookie() { var cookie = 'zmEventStats'; var stats = getCookie(cookie); - if ( !stats ) { + if (!stats) { stats = 'on'; setCookie(cookie, stats, 10*365); } @@ -845,7 +854,7 @@ function onStatsResize(vidWidth) { var minWidth = 300; // An arbitrary value in pixels used to hide the stats table var scale = $j('#scale').val(); - if ( parseInt(scale) ) { + if (parseInt(scale)) { vidWidth = vidWidth * (scale/100); } @@ -853,14 +862,14 @@ function onStatsResize(vidWidth) { //console.log("Width: " + width + " = window.width " + $j(window).width() + "- vidWidth" + vidWidth); // Hide the stats table if we have run out of room to show it properly - if ( width < minWidth ) { + if (width < minWidth) { statsBtn.prop('disabled', true); - if ( table.is(':visible') ) { + if (table.is(':visible')) { table.toggle(false); wasHidden = true; } // Show the stats table if we hid it previously and sufficient room becomes available - } else if ( width >= minWidth ) { + } else if (width >= minWidth) { statsBtn.prop('disabled', false); if ( !table.is(':visible') && wasHidden ) { table.toggle(true); @@ -873,14 +882,14 @@ function initPage() { // Load the event stats getStat(); - if ( getEvtStatsCookie() != 'on' ) { + if (getEvtStatsCookie() != 'on') { table.toggle(false); } else { onStatsResize(eventData.Width); } //FIXME prevent blocking...not sure what is happening or best way to unblock - if ( $j('#videoobj').length ) { + if ($j('#videoobj').length) { vid = videojs('videoobj'); addVideoTimingTrack(vid, LabelFormat, eventData.MonitorName, eventData.Length, eventData.StartDateTime); $j('.vjs-progress-control').append('
');//add a place for videojs only on first load @@ -894,26 +903,26 @@ function initPage() { setCookie('volume', vid.volume(), 3600); }); var cookie = getCookie('volume'); - if ( cookie ) vid.volume(cookie); + if (cookie) vid.volume(cookie); vid.on('timeupdate', function() { $j('#progressValue').html(secsToTime(Math.floor(vid.currentTime()))); }); // rate is in % so 100 would be 1x - if ( rate > 0 ) { + if (rate > 0) { // rate should be 100 = 1x, etc. vid.playbackRate(rate/100); } } else { progressBarNav(); streamCmdTimer = setTimeout(streamQuery, 500); - if ( canStreamNative ) { - if ( !$j('#imageFeed') ) { + if (canStreamNative) { + if (!$j('#imageFeed')) { console.log('No element with id tag imageFeed found.'); } else { var streamImg = $j('#imageFeed img'); - if ( !streamImg ) { + if (!streamImg) { streamImg = $j('#imageFeed object'); } $j(streamImg).click(function(event) { @@ -921,10 +930,10 @@ function initPage() { }); } } - } + } // end if videojs or mjpeg stream nearEventsQuery(eventData.Id); initialAlarmCues(eventData.Id); //call ajax+renderAlarmCues - if ( scale == '0' || scale == 'auto' ) changeScale(); + if (scale == '0' || scale == 'auto') changeScale(); document.querySelectorAll('select[name="rate"]').forEach(function(el) { el.onchange = window['changeRate']; }); @@ -995,7 +1004,7 @@ function initPage() { // Manage the EDIT button bindButton('#editBtn', 'click', null, function onEditClick(evt) { - if ( ! canEdit.Events ) { + if (!canEdit.Events) { enoperm(); return; } @@ -1039,7 +1048,7 @@ function initPage() { var cookie = 'zmEventStats'; // Toggle the visiblity of the stats table and write an appropriate cookie - if ( table.is(':visible') ) { + if (table.is(':visible')) { setCookie(cookie, 'off', 10*365); table.toggle(false); } else { @@ -1056,13 +1065,13 @@ function initPage() { // Manage the DELETE button bindButton('#deleteBtn', 'click', null, function onDeleteClick(evt) { - if ( !canEdit.Events ) { + if (!canEdit.Events) { enoperm(); return; } evt.preventDefault(); - if ( ! $j('#deleteConfirm').length ) { + if (!$j('#deleteConfirm').length) { // Load the delete confirmation modal into the DOM $j.getJSON(thisUrl + '?request=modal&modal=delconfirm') .done(function(data) { From 1caeb67a3e6c30ddced73d6dffb4431e5ad03133 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 22 May 2021 13:38:52 -0400 Subject: [PATCH 099/394] fix behaviour of reverse and fast forward buttons. Fixes #3251 --- web/skins/classic/views/js/event.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js index b6de480a4..0a73dd9d4 100644 --- a/web/skins/classic/views/js/event.js +++ b/web/skins/classic/views/js/event.js @@ -367,7 +367,7 @@ function streamPlay( ) { setButtonState('fastRevBtn', 'inactive'); } -function streamFastFwd( action ) { +function streamFastFwd(action) { setButtonState('pauseBtn', 'inactive'); setButtonState('playBtn', 'inactive'); setButtonState('fastFwdBtn', 'active'); @@ -376,8 +376,8 @@ function streamFastFwd( action ) { setButtonState('fastRevBtn', 'inactive'); if (vid) { if (revSpeed != .5) stopFastRev(); - vid.playbackRate(rates[rates.indexOf(vid.playbackRate()*100)-1]/100); - if (rates.indexOf(vid.playbackRate()*100)-1 == -1) { + vid.playbackRate(rates[rates.indexOf(vid.playbackRate()*100)+1]/100); + if (rates.indexOf(vid.playbackRate()*100)+1 == rates.length) { setButtonState('fastFwdBtn', 'unavail'); } $j('select[name="rate"]').val(vid.playbackRate()*100); @@ -422,8 +422,9 @@ function streamFastRev(action) { setButtonState('slowRevBtn', 'unavail'); setButtonState('fastRevBtn', 'active'); if (vid) { //There is no reverse play with mp4. Set the speed to 0 and manually set the time back. - revSpeed = rates[rates.indexOf(revSpeed*100)-1]/100; - if (rates.indexOf(revSpeed*100) == 0) { + revSpeed = -1*(rates[rates.indexOf(revSpeed*-100)-1]/100); + if (rates.indexOf(revSpeed*-100) == 0) { + console.log("paused?"); setButtonState('fastRevBtn', 'unavail'); } clearInterval(intervalRewind); From 4c99baf725bd9c60534856513e1ed350f3b8657f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 22 May 2021 13:59:10 -0400 Subject: [PATCH 100/394] Use data-onclick instead of data-on-click-this. Load form by id instead of passed in element. Fixes #3250 --- web/skins/classic/views/js/export.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/web/skins/classic/views/js/export.js b/web/skins/classic/views/js/export.js index 5cad88629..b947c8b40 100644 --- a/web/skins/classic/views/js/export.js +++ b/web/skins/classic/views/js/export.js @@ -1,11 +1,15 @@ var exportTimer = null; -function configureExportButton(element) { - var form = element.form; +function configureExportButton() { + var form = $j('#contentForm')[0]; + if (!form) { + console.error("Form contentForm not found by jquery."); + return; + } var eventCount = 0; document.querySelectorAll('input[name="eids[]"]').forEach(function(el) { - if ( el.checked ) { + if (el.checked) { eventCount ++; } }); @@ -93,7 +97,7 @@ function getEventDetailModal(eid) { } function initPage() { - configureExportButton(this); + configureExportButton(); if ( exportReady ) { setTimeout(startDownload, 1500, exportFile); } From f6fcc2144f5505f2857bc44ecff3f2d87424a0aa Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 22 May 2021 13:59:32 -0400 Subject: [PATCH 101/394] jquery.js doesn't exist, so link jquery.min.js instead --- web/skins/classic/includes/export_functions.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/skins/classic/includes/export_functions.php b/web/skins/classic/includes/export_functions.php index ee48708d5..0a1fd97b4 100644 --- a/web/skins/classic/includes/export_functions.php +++ b/web/skins/classic/includes/export_functions.php @@ -34,7 +34,7 @@ function exportHeader($title) { } ?> - + @@ -912,7 +911,7 @@ function xhtmlFooter() { -