diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 283e2bde3..03aca0594 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -294,17 +294,18 @@ bool Event::WriteFrameImage( bool rc; - if ( !config.timestamp_on_capture ) { + SystemTimePoint jpeg_timestamp = + monitor->Exif() ? SystemTimePoint(zm::chrono::duration_cast(timestamp)) : SystemTimePoint(); + + if (!config.timestamp_on_capture) { // stash the image we plan to use in another pointer regardless if timestamped. // exif is only timestamp at present this switches on or off for write Image *ts_image = new Image(*image); monitor->TimestampImage(ts_image, timestamp); - rc = ts_image->WriteJpeg(event_file, thisquality, - (monitor->Exif() ? timestamp : (timeval){0,0})); - delete(ts_image); + rc = ts_image->WriteJpeg(event_file, thisquality, jpeg_timestamp); + delete ts_image; } else { - rc = image->WriteJpeg(event_file, thisquality, - (monitor->Exif() ? timestamp : (timeval){0,0})); + rc = image->WriteJpeg(event_file, thisquality, jpeg_timestamp); } return rc; diff --git a/src/zm_image.cpp b/src/zm_image.cpp index 550acccdd..e0a7aa4e8 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -1061,23 +1061,26 @@ cinfo->out_color_space = JCS_RGB; // Note quality=zero means default bool Image::WriteJpeg(const char *filename, int quality_override) const { - return Image::WriteJpeg(filename, quality_override, (timeval){0,0}, false); + return Image::WriteJpeg(filename, quality_override, {}, false); } bool Image::WriteJpeg(const char *filename) const { - return Image::WriteJpeg(filename, 0, (timeval){0,0}, false); + return Image::WriteJpeg(filename, 0, {}, false); } bool Image::WriteJpeg(const char *filename, bool on_blocking_abort) const { - return Image::WriteJpeg(filename, 0, (timeval){0,0}, on_blocking_abort); + return Image::WriteJpeg(filename, 0, {}, on_blocking_abort); } -bool Image::WriteJpeg(const char *filename, struct timeval timestamp) const { +bool Image::WriteJpeg(const char *filename, SystemTimePoint timestamp) const { return Image::WriteJpeg(filename, 0, timestamp, false); } -bool Image::WriteJpeg(const char *filename, int quality_override, struct timeval timestamp) const { +bool Image::WriteJpeg(const char *filename, int quality_override, SystemTimePoint timestamp) const { return Image::WriteJpeg(filename, quality_override, timestamp, false); } -bool Image::WriteJpeg(const char *filename, int quality_override, struct timeval timestamp, bool on_blocking_abort) const { +bool Image::WriteJpeg(const char *filename, + int quality_override, + SystemTimePoint timestamp, + bool on_blocking_abort) const { if ( config.colour_jpeg_files && (colours == ZM_COLOUR_GRAY8) ) { Image temp_image(*this); temp_image.Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB); @@ -1196,7 +1199,7 @@ cinfo->out_color_space = JCS_RGB; } // If we have a non-zero time (meaning a parameter was passed in), then form a simple exif segment with that time as DateTimeOriginal and SubsecTimeOriginal // No timestamp just leave off the exif section. - if ( timestamp.tv_sec ) { + if (timestamp.time_since_epoch() > Seconds(0)) { #define EXIFTIMES_MS_OFFSET 0x36 // three decimal digits for milliseconds #define EXIFTIMES_MS_LEN 0x03 #define EXIFTIMES_OFFSET 0x3E // 19 characters format '2015:07:21 13:14:45' not including quotes @@ -1205,9 +1208,15 @@ cinfo->out_color_space = JCS_RGB; // This is a lot of stuff to allocate on the stack. Recommend char *timebuf[64]; char timebuf[64], msbuf[64]; + tm timestamp_tm = {}; - strftime(timebuf, sizeof timebuf, "%Y:%m:%d %H:%M:%S", localtime_r(×tamp.tv_sec, ×tamp_tm)); - snprintf(msbuf, sizeof msbuf, "%06d",(int)(timestamp.tv_usec)); // we only use milliseconds because that's all defined in exif, but this is the whole microseconds because we have it + time_t timestamp_t = std::chrono::system_clock::to_time_t(timestamp); + strftime(timebuf, sizeof timebuf, "%Y:%m:%d %H:%M:%S", localtime_r(×tamp_t, ×tamp_tm)); + Seconds ts_sec = std::chrono::duration_cast(timestamp.time_since_epoch()); + Microseconds ts_usec = std::chrono::duration_cast(timestamp.time_since_epoch() - ts_sec); + // we only use milliseconds because that's all defined in exif, but this is the whole microseconds because we have it + snprintf(msbuf, sizeof msbuf, "%06d", static_cast(ts_usec.count())); + unsigned char exiftimes[82] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x69, 0x87, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1722,11 +1731,6 @@ void Image::Overlay( const Image &image, unsigned int x, unsigned int y ) { } // end void Image::Overlay( const Image &image, unsigned int x, unsigned int y ) void Image::Blend( const Image &image, int transparency ) { -#ifdef ZM_IMAGE_PROFILING - struct timespec start,end,diff; - unsigned long long executetime; - unsigned long milpixels; -#endif uint8_t* new_buffer; if ( !( @@ -1744,19 +1748,21 @@ void Image::Blend( const Image &image, int transparency ) { new_buffer = AllocBuffer(size); #ifdef ZM_IMAGE_PROFILING - clock_gettime(CLOCK_THREAD_CPUTIME_ID,&start); + TimePoint start = std::chrono::steady_clock::now(); #endif /* Do the blending */ (*blend)(buffer, image.buffer, new_buffer, size, transparency); #ifdef ZM_IMAGE_PROFILING - clock_gettime(CLOCK_THREAD_CPUTIME_ID,&end); - TimespecDiff(&start,&end,&diff); + TimePoint end = std::chrono::steady_clock::now(); - executetime = (1000000000ull * diff.tv_sec) + diff.tv_nsec; - milpixels = (unsigned long)((long double)size)/((((long double)executetime)/1000)); - Debug(5, "Blend: %u colours blended in %llu nanoseconds, %lu million colours/s\n",size,executetime,milpixels); + std::chrono::nanoseconds diff = std::chrono::duration_cast(end - start); + uint64 mil_pixels = static_cast(size / (static_cast(diff.count()) / 1000)); + Debug(5, "Blend: %u colours blended in %" PRIi64 " nanoseconds, % " PRIi64 " million colours/s\n", + size, + static_cast(diff.count()), + mil_pixels); #endif AssignDirect(width, height, colours, subpixelorder, new_buffer, size, ZM_BUFTYPE_ZM); @@ -1860,12 +1866,6 @@ Image *Image::Highlight( unsigned int n_images, Image *images[], const Rgb thres /* New function to allow buffer re-using instead of allocationg memory for the delta image every time */ void Image::Delta(const Image &image, Image* targetimage) const { -#ifdef ZM_IMAGE_PROFILING - struct timespec start,end,diff; - unsigned long long executetime; - unsigned long milpixels; -#endif - if ( !(width == image.width && height == image.height && colours == image.colours && subpixelorder == image.subpixelorder) ) { Panic( "Attempt to get delta of different sized images, expected %dx%dx%d %d, got %dx%dx%d %d", width, height, colours, subpixelorder, image.width, image.height, image.colours, image.subpixelorder); @@ -1878,7 +1878,7 @@ void Image::Delta(const Image &image, Image* targetimage) const { } #ifdef ZM_IMAGE_PROFILING - clock_gettime(CLOCK_THREAD_CPUTIME_ID,&start); + TimePoint start = std::chrono::steady_clock::now(); #endif switch ( colours ) { @@ -1915,12 +1915,14 @@ void Image::Delta(const Image &image, Image* targetimage) const { } #ifdef ZM_IMAGE_PROFILING - clock_gettime(CLOCK_THREAD_CPUTIME_ID,&end); - TimespecDiff(&start,&end,&diff); + TimePoint end = std::chrono::steady_clock::now(); - executetime = (1000000000ull * diff.tv_sec) + diff.tv_nsec; - milpixels = (unsigned long)((long double)pixels)/((((long double)executetime)/1000)); - Debug(5, "Delta: %u delta pixels generated in %llu nanoseconds, %lu million pixels/s",pixels,executetime,milpixels); + std::chrono::nanoseconds diff = std::chrono::duration_cast(end - start); + uint64 mil_pixels = static_cast(size / (static_cast(diff.count()) / 1000)); + Debug(5, "Delta: %u delta pixels generated in %" PRIi64 " nanoseconds, % " PRIi64 " million pixels/s", + size, + static_cast(diff.count()), + mil_pixels); #endif } @@ -2119,17 +2121,18 @@ void Image::Annotate( } } -void Image::Timestamp(const char *label, const time_t when, const Vector2 &coord, const int size) { +void Image::Timestamp(const char *label, SystemTimePoint when, const Vector2 &coord, int label_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)); - if ( label ) { + time_t when_t = std::chrono::system_clock::to_time_t(when); + strftime(time_text, sizeof(time_text), "%y/%m/%d %H:%M:%S", localtime_r(&when_t, &when_tm)); + if (label) { // Assume label is max 64, + ' - ' + 64 chars of time_text char text[132]; snprintf(text, sizeof(text), "%s - %s", label, time_text); - Annotate(text, coord, size); + Annotate(text, coord, label_size); } else { - Annotate(time_text, coord, size); + Annotate(time_text, coord, label_size); } } diff --git a/src/zm_image.h b/src/zm_image.h index 2a3c68232..8322f9c17 100644 --- a/src/zm_image.h +++ b/src/zm_image.h @@ -25,6 +25,7 @@ #include "zm_logger.h" #include "zm_mem_utils.h" #include "zm_rgb.h" +#include "zm_time.h" #include "zm_vector2.h" #if HAVE_ZLIB_H @@ -237,9 +238,9 @@ class Image { bool WriteJpeg(const char *filename) const; bool WriteJpeg(const char *filename, bool on_blocking_abort) const; bool WriteJpeg(const char *filename, int quality_override) const; - bool WriteJpeg(const char *filename, struct timeval timestamp) const; - bool WriteJpeg(const char *filename, int quality_override, struct timeval timestamp) const; - bool WriteJpeg(const char *filename, int quality_override, struct timeval timestamp, bool on_blocking_abort) const; + bool WriteJpeg(const char *filename, SystemTimePoint timestamp) const; + bool WriteJpeg(const char *filename, int quality_override, SystemTimePoint timestamp) const; + bool WriteJpeg(const char *filename, int quality_override, SystemTimePoint timestamp, bool on_blocking_abort) const; bool DecodeJpeg(const JOCTET *inbuffer, int inbuffer_size, unsigned int p_colours, unsigned int p_subpixelorder); bool EncodeJpeg(JOCTET *outbuffer, int *outbuffer_size, int quality_override=0) const; @@ -270,7 +271,7 @@ class Image { 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 Vector2 &coord, const int size); + void Timestamp(const char *label, SystemTimePoint when, const Vector2 &coord, int label_size); void Colourise(const unsigned int p_reqcolours, const unsigned int p_reqsubpixelorder); void DeColourise(); @@ -300,8 +301,9 @@ class Edge { Edge(int32 min_y, int32 max_y, double min_x, double _1_m) : min_y(min_y), max_y(max_y), min_x(min_x), _1_m(_1_m) {} static bool CompareYX(const Edge &e1, const Edge &e2) { - if (e1.min_y == e2.min_y) + if (e1.min_y == e2.min_y) { return e1.min_x < e2.min_x; + } return e1.min_y < e2.min_y; } diff --git a/src/zm_utils.cpp b/src/zm_utils.cpp index 1a7085364..58726778e 100644 --- a/src/zm_utils.cpp +++ b/src/zm_utils.cpp @@ -180,16 +180,6 @@ std::string Base64Encode(const std::string &str) { return outString; } -void TimespecDiff(struct timespec *start, struct timespec *end, struct timespec *diff) { - if (((end->tv_nsec) - (start->tv_nsec)) < 0) { - diff->tv_sec = end->tv_sec - start->tv_sec - 1; - diff->tv_nsec = 1000000000 + end->tv_nsec - start->tv_nsec; - } else { - diff->tv_sec = end->tv_sec - start->tv_sec; - diff->tv_nsec = end->tv_nsec - start->tv_nsec; - } -} - std::string TimevalToString(timeval tv) { tm now = {}; std::array tm_buf = {}; diff --git a/src/zm_utils.h b/src/zm_utils.h index 3962aa5ef..f4af1f1d4 100644 --- a/src/zm_utils.h +++ b/src/zm_utils.h @@ -76,7 +76,6 @@ std::string ByteArrayToHexString(nonstd::span bytes); std::string Base64Encode(const std::string &str); -void TimespecDiff(timespec *start, timespec *end, timespec *diff); std::string TimevalToString(timeval tv); extern unsigned int sse_version;