From 72a92ee9d58d51ceb9778b8035b1794b03c41cef Mon Sep 17 00:00:00 2001 From: Linwood-F Date: Thu, 23 Jul 2015 19:34:39 -0400 Subject: [PATCH 1/9] Add exif to jpg images saved --- src/zm_event.cpp | 30 ++++++++++++------------------ src/zm_event.h | 6 ++++++ src/zm_image.cpp | 45 ++++++++++++++++++++++++++++++++++++++++++--- src/zm_image.h | 7 ++++++- 4 files changed, 66 insertions(+), 22 deletions(-) diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 3e723fa48..f6c3f90da 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -349,27 +349,21 @@ bool Event::SendFrameImage( const Image *image, bool alarm_frame ) bool Event::WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame ) { - if ( config.timestamp_on_capture ) + Image* ImgToWrite; + Image ts_image( *image ); + + if ( config.timestamp_on_capture ) // stash the image we plan to use in another pointer regardless if timestamped. { - if ( !config.opt_frame_server || !SendFrameImage( image, alarm_frame) ) - { - if ( alarm_frame && (config.jpeg_alarm_file_quality > config.jpeg_file_quality) ) - image->WriteJpeg( event_file, config.jpeg_alarm_file_quality ); - else - image->WriteJpeg( event_file ); - } + monitor->TimestampImage( &ts_image, ×tamp ); + ImgToWrite=&ts_image; } else + ImgToWrite=image; + + if ( !config.opt_frame_server || !SendFrameImage(ImgToWrite, alarm_frame) ) { - Image ts_image( *image ); - monitor->TimestampImage( &ts_image, ×tamp ); - if ( !config.opt_frame_server || !SendFrameImage( &ts_image, alarm_frame) ) - { - if ( alarm_frame && (config.jpeg_alarm_file_quality > config.jpeg_file_quality) ) - ts_image.WriteJpeg( event_file, config.jpeg_alarm_file_quality ); - else - ts_image.WriteJpeg( event_file ); - } + int thisquality = ( alarm_frame && (config.jpeg_alarm_file_quality > config.jpeg_file_quality) ) ? config.jpeg_alarm_file_quality : 0 ; // quality to use, zero is default + ImgToWrite->WriteJpeg( event_file, thisquality, timestamp ); } return( true ); } @@ -885,7 +879,7 @@ void EventStream::processCommand( const CmdMsg *msg ) } // If we are in single event mode and at the last frame, replay the current event - if ( (mode == MODE_SINGLE) && (curr_frame_id == event_data->frame_count) ) + if ( (mode == MODE_SINGLE) && ((unsigned int)curr_frame_id == event_data->frame_count) ) curr_frame_id = 1; replay_rate = ZM_RATE_BASE; diff --git a/src/zm_event.h b/src/zm_event.h index f50a5bf06..412baa0bb 100644 --- a/src/zm_event.h +++ b/src/zm_event.h @@ -59,6 +59,12 @@ protected: protected: static int sd; +private: + int UpldCount; + char UpldPath[PATH_MAX]; + int UpldWriteEvery; + int UpldWriteEveryAlarm; + public: typedef std::set StringSet; typedef std::map StringSetMap; diff --git a/src/zm_image.cpp b/src/zm_image.cpp index 8f91ab28b..82c48a92d 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -787,15 +787,30 @@ bool Image::ReadJpeg( const char *filename, unsigned int p_colours, unsigned int return( true ); } -bool Image::WriteJpeg( const char *filename, int quality_override ) const +// Multiple calling formats to permit inclusion (or not) of both quality_override and timestamp (exif), with suitable defaults. +// Note quality=zero means default + +bool Image::WriteJpeg( const char *filename, int quality_override) const +{ + return Image::WriteJpeg(filename, quality_override, (timeval){0,0}); +} +bool Image::WriteJpeg( const char *filename) const +{ + return Image::WriteJpeg(filename, 0, (timeval){0,0}); +} +bool Image::WriteJpeg( const char *filename, struct timeval timestamp ) const +{ + return Image::WriteJpeg(filename,0,timestamp); +} + +bool Image::WriteJpeg( const char *filename, int quality_override, struct timeval timestamp ) const { if ( config.colour_jpeg_files && colours == ZM_COLOUR_GRAY8 ) { Image temp_image( *this ); temp_image.Colourise( ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB ); - return( temp_image.WriteJpeg( filename, quality_override ) ); + return( temp_image.WriteJpeg( filename, quality_override, timestamp) ); } - int quality = quality_override?quality_override:config.jpeg_file_quality; struct jpeg_compress_struct *cinfo = jpg_ccinfo[quality]; @@ -886,6 +901,30 @@ bool Image::WriteJpeg( const char *filename, int quality_override ) const { jpeg_write_marker( cinfo, JPEG_COM, (const JOCTET *)text, strlen(text) ); } + // 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) + { + #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 + #define EXIFTIMES_LEN 0x13 // = 19 + #define EXIF_CODE 0xE1 + + char timebuf[64], msbuf[64]; + strftime(timebuf, sizeof timebuf, "%Y:%m:%d %H:%M:%S", localtime(&(timestamp.tv_sec))); + 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 + 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, + 0x02, 0x00, 0x03, 0x90, 0x02, 0x00, 0x14, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x91, 0x92, + 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00 }; + memcpy(&exiftimes[EXIFTIMES_OFFSET], timebuf,EXIFTIMES_LEN); + memcpy(&exiftimes[EXIFTIMES_MS_OFFSET], msbuf ,EXIFTIMES_MS_LEN); + jpeg_write_marker (cinfo, EXIF_CODE, (const JOCTET *)exiftimes, sizeof(exiftimes) ); + } JSAMPROW row_pointer; /* pointer to a single row */ int row_stride = cinfo->image_width * colours; /* physical row width in buffer */ diff --git a/src/zm_image.h b/src/zm_image.h index 46c7a031b..a48fafacc 100644 --- a/src/zm_image.h +++ b/src/zm_image.h @@ -204,7 +204,12 @@ public: bool WriteRaw( const char *filename ) const; bool ReadJpeg( const char *filename, unsigned int p_colours, unsigned int p_subpixelorder); - bool WriteJpeg( const char *filename, int quality_override=0 ) const; + + bool WriteJpeg ( const char *filename) 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 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; From 965425c395e3d59fb32246e0be30f3fa97819932 Mon Sep 17 00:00:00 2001 From: Linwood-F Date: Thu, 23 Jul 2015 19:37:23 -0400 Subject: [PATCH 2/9] Remove extraneous file --- src/zm_event.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/zm_event.h b/src/zm_event.h index 412baa0bb..f50a5bf06 100644 --- a/src/zm_event.h +++ b/src/zm_event.h @@ -59,12 +59,6 @@ protected: protected: static int sd; -private: - int UpldCount; - char UpldPath[PATH_MAX]; - int UpldWriteEvery; - int UpldWriteEveryAlarm; - public: typedef std::set StringSet; typedef std::map StringSetMap; From 59c1b83d4ff3ed17e61f9c6b87699ed05a3105eb Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Sun, 16 Aug 2015 17:42:12 -0500 Subject: [PATCH 3/9] create embed_exif flag in Monitor class --- src/zm_event.cpp | 3 +++ src/zm_monitor.cpp | 32 ++++++++++++++++++++++---------- src/zm_monitor.h | 7 ++++++- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/zm_event.cpp b/src/zm_event.cpp index f6c3f90da..805d8565a 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -352,6 +352,9 @@ bool Event::WriteFrameImage( Image *image, struct timeval timestamp, const char Image* ImgToWrite; Image ts_image( *image ); + // This will be noisy. Delete or modify when finished testing. + Info("Embed EXIF flag is set to: %d", monitor->Exif()); + if ( config.timestamp_on_capture ) // stash the image we plan to use in another pointer regardless if timestamped. { monitor->TimestampImage( &ts_image, ×tamp ); diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 906ca68f6..b4a618b89 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -288,6 +288,7 @@ Monitor::Monitor( int p_alarm_ref_blend_perc, bool p_track_motion, Rgb p_signal_check_colour, + bool p_embed_exif, Purpose p_purpose, int p_n_zones, Zone *p_zones[] @@ -315,6 +316,7 @@ Monitor::Monitor( alarm_ref_blend_perc( p_alarm_ref_blend_perc ), track_motion( p_track_motion ), signal_check_colour( p_signal_check_colour ), + embed_exif( p_embed_exif ), delta_image( width, height, ZM_COLOUR_GRAY8, ZM_SUBPIX_ORDER_NONE ), ref_image( width, height, p_camera->Colours(), p_camera->SubpixelOrder() ), purpose( p_purpose ), @@ -1863,11 +1865,11 @@ int Monitor::LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose static char sql[ZM_SQL_MED_BUFSIZ]; if ( !device[0] ) { - strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Method, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Function != 'None' and Type = 'Local' order by Device, Channel", sizeof(sql) ); + strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Method, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour, Exif from Monitors where Function != 'None' and Type = 'Local' order by Device, Channel", sizeof(sql) ); } else { - snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Method, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Function != 'None' and Type = 'Local' and Device = '%s' order by Channel", device ); + snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Method, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour, Exif from Monitors where Function != 'None' and Type = 'Local' and Device = '%s' order by Channel", device ); } if ( mysql_query( &dbconn, sql ) ) { @@ -1959,6 +1961,7 @@ Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); else signal_check_colour = strtol(dbrow[col],0,16); col++; + bool embed_exif = (*dbrow[col] != '0'); col++; int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); @@ -2013,6 +2016,7 @@ Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); alarm_ref_blend_perc, track_motion, signal_check_colour, + embed_exif, purpose, 0, 0 @@ -2039,11 +2043,11 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c static char sql[ZM_SQL_MED_BUFSIZ]; if ( !protocol ) { - strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Remote'", sizeof(sql) ); + strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'Remote'", sizeof(sql) ); } else { - snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Remote' and Protocol = '%s' and Host = '%s' and Port = '%s' and Path = '%s'", protocol, host, port, path ); + snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'Remote' and Protocol = '%s' and Host = '%s' and Port = '%s' and Path = '%s'", protocol, host, port, path ); } if ( mysql_query( &dbconn, sql ) ) { @@ -2109,7 +2113,7 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c int ref_blend_perc = atoi(dbrow[col]); col++; int alarm_ref_blend_perc = atoi(dbrow[col]); col++; int track_motion = atoi(dbrow[col]); col++; - + bool embed_exif = (*dbrow[col] != '0'); col++; int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); @@ -2186,6 +2190,7 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c alarm_ref_blend_perc, track_motion, RGB_WHITE, + embed_exif, purpose, 0, 0 @@ -2212,11 +2217,11 @@ int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose pu static char sql[ZM_SQL_MED_BUFSIZ]; if ( !file[0] ) { - strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'File'", sizeof(sql) ); + strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'File'", sizeof(sql) ); } else { - snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'File' and Path = '%s'", file ); + snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'File' and Path = '%s'", file ); } if ( mysql_query( &dbconn, sql ) ) { @@ -2278,6 +2283,7 @@ int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose pu int ref_blend_perc = atoi(dbrow[col]); col++; int alarm_ref_blend_perc = atoi(dbrow[col]); col++; int track_motion = atoi(dbrow[col]); col++; + bool embed_exif = (*dbrow[col] != '0'); col++; int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); @@ -2322,6 +2328,7 @@ int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose pu ref_blend_perc, alarm_ref_blend_perc, track_motion, + embed_exif, RGB_WHITE, purpose, 0, @@ -2349,11 +2356,11 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose static char sql[ZM_SQL_MED_BUFSIZ]; if ( !file[0] ) { - strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Method, Options, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Ffmpeg'", sizeof(sql) ); + strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Method, Options, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'Ffmpeg'", sizeof(sql) ); } else { - snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Method, Options, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Ffmpeg' and Path = '%s'", file ); + snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Method, Options, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'Ffmpeg' and Path = '%s'", file ); } if ( mysql_query( &dbconn, sql ) ) { @@ -2417,6 +2424,7 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose int ref_blend_perc = atoi(dbrow[col]); col++; int alarm_ref_blend_perc = atoi(dbrow[col]); col++; int track_motion = atoi(dbrow[col]); col++; + bool embed_exif = (*dbrow[col] != '0'); col++; int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); @@ -2463,6 +2471,7 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose ref_blend_perc, alarm_ref_blend_perc, track_motion, + embed_exif, RGB_WHITE, purpose, 0, @@ -2488,7 +2497,7 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose Monitor *Monitor::Load( int id, bool load_zones, Purpose purpose ) { static char sql[ZM_SQL_MED_BUFSIZ]; - snprintf( sql, sizeof(sql), "select Id, Name, Type, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Protocol, Method, Host, Port, Path, Options, User, Pass, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Id = %d", id ); + snprintf( sql, sizeof(sql), "select Id, Name, Type, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Protocol, Method, Host, Port, Path, Options, User, Pass, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour, Exif from Monitors where Id = %d", id ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); @@ -2587,6 +2596,8 @@ Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); signal_check_colour = strtol(dbrow[col]+1,0,16); else signal_check_colour = strtol(dbrow[col],0,16); + col++; + bool embed_exif = (*dbrow[col] != '0'); col++; int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); @@ -2777,6 +2788,7 @@ Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); alarm_ref_blend_perc, track_motion, signal_check_colour, + embed_exif, purpose, 0, 0 diff --git a/src/zm_monitor.h b/src/zm_monitor.h index b8326a1c4..cb4a78401 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -242,6 +242,7 @@ protected: int alarm_ref_blend_perc; // Percentage of new image going into reference image during alarm. bool track_motion; // Whether this monitor tries to track detected motion Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected + bool embed_exif; // Whether to embed Exif data into each image frame or not double fps; Image delta_image; @@ -298,7 +299,7 @@ protected: public: // OurCheckAlarms seems to be unused. Check it on zm_monitor.cpp for more info. //bool OurCheckAlarms( Zone *zone, const Image *pImage ); - Monitor( int p_id, const char *p_name, int p_function, bool p_enabled, const char *p_linked_monitors, Camera *p_camera, int p_orientation, unsigned int p_deinterlacing, const char *p_event_prefix, const char *p_label_format, const Coord &p_label_coord, int p_image_buffer_count, int p_warmup_count, int p_pre_event_count, int p_post_event_count, int p_stream_replay_buffer, int p_alarm_frame_count, int p_section_length, int p_frame_skip, int p_motion_frame_skip, int p_capture_delay, int p_alarm_capture_delay, int p_fps_report_interval, int p_ref_blend_perc, int p_alarm_ref_blend_perc, bool p_track_motion, Rgb p_signal_check_colour, Purpose p_purpose, int p_n_zones=0, Zone *p_zones[]=0 ); + Monitor( int p_id, const char *p_name, int p_function, bool p_enabled, const char *p_linked_monitors, Camera *p_camera, int p_orientation, unsigned int p_deinterlacing, const char *p_event_prefix, const char *p_label_format, const Coord &p_label_coord, int p_image_buffer_count, int p_warmup_count, int p_pre_event_count, int p_post_event_count, int p_stream_replay_buffer, int p_alarm_frame_count, int p_section_length, int p_frame_skip, int p_motion_frame_skip, int p_capture_delay, int p_alarm_capture_delay, int p_fps_report_interval, int p_ref_blend_perc, int p_alarm_ref_blend_perc, bool p_track_motion, Rgb p_signal_check_colour, bool p_embed_exif, Purpose p_purpose, int p_n_zones=0, Zone *p_zones[]=0 ); ~Monitor(); void AddZones( int p_n_zones, Zone *p_zones[] ); @@ -343,6 +344,10 @@ public: return( false ); return( enabled && shared_data->active ); } + inline bool Exif() + { + return( embed_exif ); + } unsigned int Width() const { return( width ); } unsigned int Height() const { return( height ); } From 3489d0d734b45270d77ea7cb1a0a09425c93de54 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Sun, 16 Aug 2015 17:43:45 -0500 Subject: [PATCH 4/9] add Exif column to sql database --- db/zm_create.sql.in | 1 + db/zm_update-1.28.102.sql | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 db/zm_update-1.28.102.sql diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index c52c5741e..db2e3b4f9 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -377,6 +377,7 @@ CREATE TABLE `Monitors` ( `DefaultScale` smallint(5) unsigned NOT NULL default '100', `SignalCheckColour` varchar(32) NOT NULL default '#0000BE', `WebColour` varchar(32) NOT NULL default 'red', + `Exif` tinyint(1) unsigned NOT NULL default '0', `Sequence` smallint(5) unsigned default NULL, PRIMARY KEY (`Id`) ) ENGINE=@ZM_MYSQL_ENGINE@; diff --git a/db/zm_update-1.28.102.sql b/db/zm_update-1.28.102.sql new file mode 100644 index 000000000..57b588bfb --- /dev/null +++ b/db/zm_update-1.28.102.sql @@ -0,0 +1,17 @@ +-- +-- Add Monitor Exif field +-- Used to enable or disable processing of the remote camera RTSP DESCRIBE response header +-- +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Monitors' + AND table_schema = DATABASE() + AND column_name = 'Exif' + ) > 0, +"SELECT 'Column Options already exists in Monitors'", +"ALTER TABLE `Monitors` ADD `Exif` tinyint(1) unsigned NOT NULL default '0' AFTER `WebColour`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; From c17de4a00fcb62e22c84eeb6533d601e983bc93f Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Sun, 16 Aug 2015 17:56:03 -0500 Subject: [PATCH 5/9] add Exif checkbox to monitor --- web/lang/en_gb.php | 5 +++++ web/skins/classic/views/monitor.php | 3 +++ 2 files changed, 8 insertions(+) diff --git a/web/lang/en_gb.php b/web/lang/en_gb.php index ceaabad02..303316d04 100644 --- a/web/lang/en_gb.php +++ b/web/lang/en_gb.php @@ -302,6 +302,7 @@ $SLANG = array( 'Exclude' => 'Exclude', 'Execute' => 'Execute', 'ExportDetails' => 'Export Event Details', + 'Exif' => 'Embed EXIF data into image', 'Export' => 'Export', 'ExportFailed' => 'Export Failed', 'ExportFormat' => 'Export File Format', @@ -894,6 +895,10 @@ $OLANG = array( "\"--rtp-client-port=nnn\" Set local port to use for rtp data~~~~". "\"--verbose=2\" Set verbosity of libVLC" ), + 'OPTIONS_EXIF' => array( + 'Help' => "Enable this option to embed EXIF data into each jpeg frame." + ), + // 'LANG_DEFAULT' => array( // 'Prompt' => "This is a new prompt for this option", diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 7140138ba..b65cd1b5f 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -110,6 +110,7 @@ if ( ! empty($_REQUEST['mid']) ) { 'DefaultScale' => '100', 'SignalCheckColour' => '#0000c0', 'WebColour' => 'red', + 'Exif' => '', 'Triggers' => "", 'V4LMultiBuffer' => '', 'V4LCapturesPerFrame' => 1, @@ -603,6 +604,7 @@ if ( $tab != 'misc' ) +      +  () checked="checked"/> Date: Sun, 16 Aug 2015 17:57:31 -0500 Subject: [PATCH 6/9] bump version to 1.28.102 --- CMakeLists.txt | 2 +- configure.ac | 2 +- version | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index aa5a3219e..befd4588b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ # cmake_minimum_required (VERSION 2.6) project (zoneminder) -set(zoneminder_VERSION "1.28.100") +set(zoneminder_VERSION "1.28.102") # make API version a minor of ZM version set(zoneminder_API_VERSION "${zoneminder_VERSION}.1") diff --git a/configure.ac b/configure.ac index 4924c5c9a..bb27bd949 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ # For instructions on building with cmake, please see INSTALL # AC_PREREQ(2.59) -AC_INIT(zm,1.28.100,[http://www.zoneminder.com/forums/ - Please check FAQ first],zoneminder,http://www.zoneminder.com/downloads.html) +AC_INIT(zm,1.28.102,[http://www.zoneminder.com/forums/ - Please check FAQ first],zoneminder,http://www.zoneminder.com/downloads.html) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR(src/zm.h) AC_CONFIG_HEADERS(config.h) diff --git a/version b/version index 1bffe5fba..69d32b636 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.28.100 +1.28.102 From 9351e1b9d9f97277a65a36c3d539e8ea13a3e758 Mon Sep 17 00:00:00 2001 From: Linwood-F Date: Mon, 17 Aug 2015 22:07:41 -0400 Subject: [PATCH 7/9] Incorporate exif switch --- src/zm_event.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 805d8565a..865dc4e58 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -352,9 +352,6 @@ bool Event::WriteFrameImage( Image *image, struct timeval timestamp, const char Image* ImgToWrite; Image ts_image( *image ); - // This will be noisy. Delete or modify when finished testing. - Info("Embed EXIF flag is set to: %d", monitor->Exif()); - if ( config.timestamp_on_capture ) // stash the image we plan to use in another pointer regardless if timestamped. { monitor->TimestampImage( &ts_image, ×tamp ); @@ -366,7 +363,7 @@ bool Event::WriteFrameImage( Image *image, struct timeval timestamp, const char if ( !config.opt_frame_server || !SendFrameImage(ImgToWrite, alarm_frame) ) { int thisquality = ( alarm_frame && (config.jpeg_alarm_file_quality > config.jpeg_file_quality) ) ? config.jpeg_alarm_file_quality : 0 ; // quality to use, zero is default - ImgToWrite->WriteJpeg( event_file, thisquality, timestamp ); + ImgToWrite->WriteJpeg( event_file, thisquality, (monitor->Exif() ? timestamp : (timeval){0,0}) ); } return( true ); } From 3b9bb6053cc075b84ffd6598a4c06fea7cb513b3 Mon Sep 17 00:00:00 2001 From: Linwood-F Date: Mon, 17 Aug 2015 22:10:39 -0400 Subject: [PATCH 8/9] Add clarifying comment --- src/zm_event.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 865dc4e58..41f76feef 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -363,7 +363,7 @@ bool Event::WriteFrameImage( Image *image, struct timeval timestamp, const char if ( !config.opt_frame_server || !SendFrameImage(ImgToWrite, alarm_frame) ) { int thisquality = ( alarm_frame && (config.jpeg_alarm_file_quality > config.jpeg_file_quality) ) ? config.jpeg_alarm_file_quality : 0 ; // quality to use, zero is default - ImgToWrite->WriteJpeg( event_file, thisquality, (monitor->Exif() ? timestamp : (timeval){0,0}) ); + ImgToWrite->WriteJpeg( event_file, thisquality, (monitor->Exif() ? timestamp : (timeval){0,0}) ); // exif is only timestamp at present this switches on or off for write } return( true ); } From 987db68452c80d54ca5450b99e75b149d6c5c544 Mon Sep 17 00:00:00 2001 From: Linwood-F Date: Tue, 18 Aug 2015 14:39:55 -0400 Subject: [PATCH 9/9] Fix extra image copy; fix options unset of exif toggle --- src/zm_event.cpp | 8 +++++--- web/includes/actions.php | 3 ++- web/skins/classic/views/monitor.php | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 29d2f6aee..5cf99de81 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -350,12 +350,13 @@ bool Event::SendFrameImage( const Image *image, bool alarm_frame ) bool Event::WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame ) { Image* ImgToWrite; - Image ts_image( *image ); + Image* ts_image = NULL; if ( config.timestamp_on_capture ) // stash the image we plan to use in another pointer regardless if timestamped. { - monitor->TimestampImage( &ts_image, ×tamp ); - ImgToWrite=&ts_image; + ts_image = new Image(*image); + monitor->TimestampImage( ts_image, ×tamp ); + ImgToWrite=ts_image; } else ImgToWrite=image; @@ -365,6 +366,7 @@ bool Event::WriteFrameImage( Image *image, struct timeval timestamp, const char int thisquality = ( alarm_frame && (config.jpeg_alarm_file_quality > config.jpeg_file_quality) ) ? config.jpeg_alarm_file_quality : 0 ; // quality to use, zero is default ImgToWrite->WriteJpeg( event_file, thisquality, (monitor->Exif() ? timestamp : (timeval){0,0}) ); // exif is only timestamp at present this switches on or off for write } + if(ts_image) delete(ts_image); // clean up if used. return( true ); } diff --git a/web/includes/actions.php b/web/includes/actions.php index 7868f0adb..5f5b56db8 100644 --- a/web/includes/actions.php +++ b/web/includes/actions.php @@ -421,7 +421,8 @@ if ( !empty($action) ) 'Controllable' => 'toggle', 'TrackMotion' => 'toggle', 'Enabled' => 'toggle', - 'DoNativeMotDet' => 'toggle' + 'DoNativeMotDet' => 'toggle', + 'Exif' => 'toggle' ); $columns = getTableColumns( 'Monitors' ); diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 07e0364f6..d9a4ce692 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -111,7 +111,7 @@ if ( ! empty($_REQUEST['mid']) ) { 'DefaultScale' => '100', 'SignalCheckColour' => '#0000c0', 'WebColour' => 'red', - 'Exif' => '', + 'Exif' => '0', 'Triggers' => "", 'V4LMultiBuffer' => '', 'V4LCapturesPerFrame' => 1,