From 3fc3a8128667e24fd40ea80a5b9dcca3ae6d419b Mon Sep 17 00:00:00 2001 From: Peter Keresztes Schmidt Date: Sun, 2 May 2021 23:28:05 +0200 Subject: [PATCH 01/22] 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 02/22] 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 03/22] 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 04/22] 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 05/22] 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 06/22] 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 07/22] 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 08/22] 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 09/22] 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 10/22] 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 11/22] 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 12/22] 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 13/22] 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 14/22] 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 15/22] 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 16/22] 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 17/22] 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 18/22] 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 19/22] 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 20/22] 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 e18f9676cd7b83c7398b9d501875099c8c96b75f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 16 May 2021 13:55:01 -0400 Subject: [PATCH 21/22] 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 22/22] 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"); }