diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 16f9a0c41..6c1dd9c9b 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -59,501 +59,459 @@ int Event::pre_alarm_count = 0; Event::PreAlarmData Event::pre_alarm_data[MAX_PRE_ALARM_FRAMES] = { { 0 } }; Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap ) : - monitor( p_monitor ), - start_time( p_start_time ), - cause( p_cause ), - noteSetMap( p_noteSetMap ) + monitor( p_monitor ), + start_time( p_start_time ), + cause( p_cause ), + noteSetMap( p_noteSetMap ) { - if ( !initialised ) - Initialise(); + if ( !initialised ) + Initialise(); - std::string notes; - createNotes( notes ); + std::string notes; + createNotes( notes ); - bool untimedEvent = false; - if ( !start_time.tv_sec ) + bool untimedEvent = false; + if ( !start_time.tv_sec ) + { + untimedEvent = true; + gettimeofday( &start_time, 0 ); + } + + static char sql[ZM_SQL_MED_BUFSIZ]; + + struct tm *stime = localtime( &start_time.tv_sec ); + snprintf( sql, sizeof(sql), "insert into Events ( MonitorId, Name, StartTime, Width, Height, Cause, Notes ) values ( %d, 'New Event', from_unixtime( %ld ), %d, %d, '%s', '%s' )", monitor->Id(), start_time.tv_sec, monitor->Width(), monitor->Height(), cause.c_str(), notes.c_str() ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't insert event: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + id = mysql_insert_id( &dbconn ); + if ( untimedEvent ) + { + Warning( "Event %d has zero time, setting to current", id ); + } + end_time.tv_sec = 0; + frames = 0; + alarm_frames = 0; + tot_score = 0; + max_score = 0; + + if ( config.use_deep_storage ) + { + char *path_ptr = path; + path_ptr += snprintf( path_ptr, sizeof(path), "%s/%d", config.dir_events, monitor->Id() ); + + int dt_parts[6]; + dt_parts[0] = stime->tm_year-100; + dt_parts[1] = stime->tm_mon+1; + dt_parts[2] = stime->tm_mday; + dt_parts[3] = stime->tm_hour; + dt_parts[4] = stime->tm_min; + dt_parts[5] = stime->tm_sec; + + char date_path[PATH_MAX] = ""; + char time_path[PATH_MAX] = ""; + char *time_path_ptr = time_path; + for ( unsigned int i = 0; i < sizeof(dt_parts)/sizeof(*dt_parts); i++ ) { - untimedEvent = true; - gettimeofday( &start_time, 0 ); - } + path_ptr += snprintf( path_ptr, sizeof(path)-(path_ptr-path), "/%02d", dt_parts[i] ); - static char sql[ZM_SQL_MED_BUFSIZ]; - - struct tm *stime = localtime( &start_time.tv_sec ); - snprintf( sql, sizeof(sql), "insert into Events ( MonitorId, Name, StartTime, Width, Height, Cause, Notes ) values ( %d, 'New Event', from_unixtime( %ld ), %d, %d, '%s', '%s' )", monitor->Id(), start_time.tv_sec, monitor->Width(), monitor->Height(), cause.c_str(), notes.c_str() ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't insert event: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - id = mysql_insert_id( &dbconn ); - if ( untimedEvent ) - { - Warning( "Event %d has zero time, setting to current", id ); - } - end_time.tv_sec = 0; - frames = 0; - alarm_frames = 0; - tot_score = 0; - max_score = 0; - - if ( config.use_deep_storage ) - { - char *path_ptr = path; - path_ptr += snprintf( path_ptr, sizeof(path), "%s/%d", config.dir_events, monitor->Id() ); - - int dt_parts[6]; - dt_parts[0] = stime->tm_year-100; - dt_parts[1] = stime->tm_mon+1; - dt_parts[2] = stime->tm_mday; - dt_parts[3] = stime->tm_hour; - dt_parts[4] = stime->tm_min; - dt_parts[5] = stime->tm_sec; - - char date_path[PATH_MAX] = ""; - char time_path[PATH_MAX] = ""; - char *time_path_ptr = time_path; - for ( unsigned int i = 0; i < sizeof(dt_parts)/sizeof(*dt_parts); i++ ) + struct stat statbuf; + errno = 0; + stat( path, &statbuf ); + if ( errno == ENOENT || errno == ENOTDIR ) + { + if ( mkdir( path, 0755 ) ) { - path_ptr += snprintf( path_ptr, sizeof(path)-(path_ptr-path), "/%02d", dt_parts[i] ); - - struct stat statbuf; - errno = 0; - stat( path, &statbuf ); - if ( errno == ENOENT || errno == ENOTDIR ) - { - if ( mkdir( path, 0755 ) ) - { - Fatal( "Can't mkdir %s: %s", path, strerror(errno)); - } - } - if ( i == 2 ) - strncpy( date_path, path, sizeof(date_path) ); - else if ( i >= 3 ) - time_path_ptr += snprintf( time_path_ptr, sizeof(time_path)-(time_path_ptr-time_path), "%s%02d", i>3?"/":"", dt_parts[i] ); + Fatal( "Can't mkdir %s: %s", path, strerror(errno)); } - char id_file[PATH_MAX]; - // Create event id symlink - snprintf( id_file, sizeof(id_file), "%s/.%d", date_path, id ); - if ( symlink( time_path, id_file ) < 0 ) - Fatal( "Can't symlink %s -> %s: %s", id_file, path, strerror(errno)); - // Create empty id tag file - snprintf( id_file, sizeof(id_file), "%s/.%d", path, id ); - if ( FILE *id_fp = fopen( id_file, "w" ) ) - fclose( id_fp ); - else - Fatal( "Can't fopen %s: %s", id_file, strerror(errno)); + } + if ( i == 2 ) + strncpy( date_path, path, sizeof(date_path) ); + else if ( i >= 3 ) + time_path_ptr += snprintf( time_path_ptr, sizeof(time_path)-(time_path_ptr-time_path), "%s%02d", i>3?"/":"", dt_parts[i] ); } + char id_file[PATH_MAX]; + // Create event id symlink + snprintf( id_file, sizeof(id_file), "%s/.%d", date_path, id ); + if ( symlink( time_path, id_file ) < 0 ) + Fatal( "Can't symlink %s -> %s: %s", id_file, path, strerror(errno)); + // Create empty id tag file + snprintf( id_file, sizeof(id_file), "%s/.%d", path, id ); + if ( FILE *id_fp = fopen( id_file, "w" ) ) + fclose( id_fp ); else + Fatal( "Can't fopen %s: %s", id_file, strerror(errno)); + } + else + { + snprintf( path, sizeof(path), "%s/%d/%d", config.dir_events, monitor->Id(), id ); + + struct stat statbuf; + errno = 0; + stat( path, &statbuf ); + if ( errno == ENOENT || errno == ENOTDIR ) { - snprintf( path, sizeof(path), "%s/%d/%d", config.dir_events, monitor->Id(), id ); - - struct stat statbuf; - errno = 0; - stat( path, &statbuf ); - if ( errno == ENOENT || errno == ENOTDIR ) - { - if ( mkdir( path, 0755 ) ) - { - Error( "Can't mkdir %s: %s", path, strerror(errno)); - } - } - char id_file[PATH_MAX]; - // Create empty id tag file - snprintf( id_file, sizeof(id_file), "%s/.%d", path, id ); - if ( FILE *id_fp = fopen( id_file, "w" ) ) - fclose( id_fp ); - else - Fatal( "Can't fopen %s: %s", id_file, strerror(errno)); + if ( mkdir( path, 0755 ) ) + { + Error( "Can't mkdir %s: %s", path, strerror(errno)); + } } - last_db_frame = 0; + char id_file[PATH_MAX]; + // Create empty id tag file + snprintf( id_file, sizeof(id_file), "%s/.%d", path, id ); + if ( FILE *id_fp = fopen( id_file, "w" ) ) + fclose( id_fp ); + else + Fatal( "Can't fopen %s: %s", id_file, strerror(errno)); + } + last_db_frame = 0; } Event::~Event() { - if ( frames > last_db_frame ) - { - struct DeltaTimeval delta_time; - DELTA_TIMEVAL( delta_time, end_time, start_time, DT_PREC_2 ); - - Debug( 1, "Adding closing frame %d to DB", frames ); - static char sql[ZM_SQL_SML_BUFSIZ]; - snprintf( sql, sizeof(sql), "insert into Frames ( EventId, FrameId, TimeStamp, Delta ) values ( %d, %d, from_unixtime( %ld ), %s%ld.%02ld )", id, frames, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't insert frame: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - } - - static char sql[ZM_SQL_MED_BUFSIZ]; - + if ( frames > last_db_frame ) + { struct DeltaTimeval delta_time; DELTA_TIMEVAL( delta_time, end_time, start_time, DT_PREC_2 ); - snprintf( sql, sizeof(sql), "update Events set Name='%s%d', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d where Id = %d", monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, id ); + Debug( 1, "Adding closing frame %d to DB", frames ); + static char sql[ZM_SQL_SML_BUFSIZ]; + snprintf( sql, sizeof(sql), "insert into Frames ( EventId, FrameId, TimeStamp, Delta ) values ( %d, %d, from_unixtime( %ld ), %s%ld.%02ld )", id, frames, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec ); if ( mysql_query( &dbconn, sql ) ) { - Error( "Can't update event: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); + Error( "Can't insert frame: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); } + } + + static char sql[ZM_SQL_MED_BUFSIZ]; + + struct DeltaTimeval delta_time; + DELTA_TIMEVAL( delta_time, end_time, start_time, DT_PREC_2 ); + + snprintf( sql, sizeof(sql), "update Events set Name='%s%d', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d where Id = %d", monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, id ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't update event: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } } void Event::createNotes( std::string ¬es ) { - notes.clear(); - for ( StringSetMap::const_iterator mapIter = noteSetMap.begin(); mapIter != noteSetMap.end(); mapIter++ ) + notes.clear(); + for ( StringSetMap::const_iterator mapIter = noteSetMap.begin(); mapIter != noteSetMap.end(); mapIter++ ) + { + notes += mapIter->first; + notes += ": "; + const StringSet &stringSet = mapIter->second; + for ( StringSet::const_iterator setIter = stringSet.begin(); setIter != stringSet.end(); setIter++ ) { - notes += mapIter->first; - notes += ": "; - const StringSet &stringSet = mapIter->second; - for ( StringSet::const_iterator setIter = stringSet.begin(); setIter != stringSet.end(); setIter++ ) - { - if ( setIter != stringSet.begin() ) - notes += ", "; - notes += *setIter; - } + if ( setIter != stringSet.begin() ) + notes += ", "; + notes += *setIter; } + } } int Event::sd = -1; bool Event::OpenFrameSocket( int monitor_id ) { - if ( sd > 0 ) + if ( sd > 0 ) + { + close( sd ); + } + + sd = socket( AF_UNIX, SOCK_STREAM, 0); + if ( sd < 0 ) + { + Error( "Can't create socket: %s", strerror(errno) ); + return( false ); + } + + int socket_buffer_size = config.frame_socket_size; + if ( socket_buffer_size > 0 ) + { + if ( setsockopt( sd, SOL_SOCKET, SO_SNDBUF, &socket_buffer_size, sizeof(socket_buffer_size) ) < 0 ) { - close( sd ); + Error( "Can't get socket buffer size to %d, error = %s", socket_buffer_size, strerror(errno) ); + close( sd ); + sd = -1; + return( false ); } + } - sd = socket( AF_UNIX, SOCK_STREAM, 0); - if ( sd < 0 ) - { - Error( "Can't create socket: %s", strerror(errno) ); - return( false ); - } + int flags; + if ( (flags = fcntl( sd, F_GETFL )) < 0 ) + { + Error( "Can't get socket flags, error = %s", strerror(errno) ); + close( sd ); + sd = -1; + return( false ); + } + flags |= O_NONBLOCK; + if ( fcntl( sd, F_SETFL, flags ) < 0 ) + { + Error( "Can't set socket flags, error = %s", strerror(errno) ); + close( sd ); + sd = -1; + return( false ); + } - int socket_buffer_size = config.frame_socket_size; - if ( socket_buffer_size > 0 ) - { - if ( setsockopt( sd, SOL_SOCKET, SO_SNDBUF, &socket_buffer_size, sizeof(socket_buffer_size) ) < 0 ) - { - Error( "Can't get socket buffer size to %d, error = %s", socket_buffer_size, strerror(errno) ); - close( sd ); - sd = -1; - return( false ); - } - } + char sock_path[PATH_MAX] = ""; + snprintf( sock_path, sizeof(sock_path), "%s/zmf-%d.sock", config.path_socks, monitor_id ); - int flags; - if ( (flags = fcntl( sd, F_GETFL )) < 0 ) - { - Error( "Can't get socket flags, error = %s", strerror(errno) ); - close( sd ); - sd = -1; - return( false ); - } - flags |= O_NONBLOCK; - if ( fcntl( sd, F_SETFL, flags ) < 0 ) - { - Error( "Can't set socket flags, error = %s", strerror(errno) ); - close( sd ); - sd = -1; - return( false ); - } + struct sockaddr_un addr; - char sock_path[PATH_MAX] = ""; - snprintf( sock_path, sizeof(sock_path), "%s/zmf-%d.sock", config.path_socks, monitor_id ); + strncpy( addr.sun_path, sock_path, sizeof(addr.sun_path) ); + addr.sun_family = AF_UNIX; - struct sockaddr_un addr; + if ( connect( sd, (struct sockaddr *)&addr, strlen(addr.sun_path)+sizeof(addr.sun_family)) < 0 ) + { + Warning( "Can't connect to frame server: %s", strerror(errno) ); + close( sd ); + sd = -1; + return( false ); + } - strncpy( addr.sun_path, sock_path, sizeof(addr.sun_path) ); - addr.sun_family = AF_UNIX; - - if ( connect( sd, (struct sockaddr *)&addr, strlen(addr.sun_path)+sizeof(addr.sun_family)) < 0 ) - { - Warning( "Can't connect to frame server: %s", strerror(errno) ); - close( sd ); - sd = -1; - return( false ); - } - - Debug( 1, "Opened connection to frame server" ); - return( true ); + Debug( 1, "Opened connection to frame server" ); + return( true ); } bool Event::ValidateFrameSocket( int monitor_id ) { - if ( sd < 0 ) - { - return( OpenFrameSocket( monitor_id ) ); - } - return( true ); + if ( sd < 0 ) + { + return( OpenFrameSocket( monitor_id ) ); + } + return( true ); } bool Event::SendFrameImage( const Image *image, bool alarm_frame ) { - if ( !ValidateFrameSocket( monitor->Id() ) ) + if ( !ValidateFrameSocket( monitor->Id() ) ) + { + return( false ); + } + + static int jpg_buffer_size = 0; + static unsigned char jpg_buffer[ZM_MAX_IMAGE_SIZE]; + + image->EncodeJpeg( jpg_buffer, &jpg_buffer_size, (alarm_frame&&(config.jpeg_alarm_file_quality>config.jpeg_file_quality))?config.jpeg_alarm_file_quality:config.jpeg_file_quality ); + + static FrameHeader frame_header; + + frame_header.event_id = id; + if ( config.use_deep_storage ) + frame_header.event_time = start_time.tv_sec; + frame_header.frame_id = frames; + frame_header.alarm_frame = alarm_frame; + frame_header.image_length = jpg_buffer_size; + + struct iovec iovecs[2]; + iovecs[0].iov_base = &frame_header; + iovecs[0].iov_len = sizeof(frame_header); + iovecs[1].iov_base = jpg_buffer; + iovecs[1].iov_len = jpg_buffer_size; + + ssize_t writev_size = sizeof(frame_header)+jpg_buffer_size; + ssize_t writev_result = writev( sd, iovecs, sizeof(iovecs)/sizeof(*iovecs)); + if ( writev_result != writev_size ) + { + if ( writev_result < 0 ) { - return( false ); + if ( errno == EAGAIN ) + { + Warning( "Blocking write detected" ); + } + else + { + Error( "Can't write frame: %s", strerror(errno) ); + close( sd ); + sd = -1; + } } - - static int jpg_buffer_size = 0; - static unsigned char jpg_buffer[ZM_MAX_IMAGE_SIZE]; - - image->EncodeJpeg( jpg_buffer, &jpg_buffer_size, (alarm_frame&&(config.jpeg_alarm_file_quality>config.jpeg_file_quality))?config.jpeg_alarm_file_quality:config.jpeg_file_quality ); - - static FrameHeader frame_header; - - frame_header.event_id = id; - if ( config.use_deep_storage ) - frame_header.event_time = start_time.tv_sec; - frame_header.frame_id = frames; - frame_header.alarm_frame = alarm_frame; - frame_header.image_length = jpg_buffer_size; - - struct iovec iovecs[2]; - iovecs[0].iov_base = &frame_header; - iovecs[0].iov_len = sizeof(frame_header); - iovecs[1].iov_base = jpg_buffer; - iovecs[1].iov_len = jpg_buffer_size; - - ssize_t writev_size = sizeof(frame_header)+jpg_buffer_size; - ssize_t writev_result = writev( sd, iovecs, sizeof(iovecs)/sizeof(*iovecs)); - if ( writev_result != writev_size ) + else { - if ( writev_result < 0 ) - { - if ( errno == EAGAIN ) - { - Warning( "Blocking write detected" ); - } - else - { - Error( "Can't write frame: %s", strerror(errno) ); - close( sd ); - sd = -1; - } - } - else - { - Error( "Incomplete frame write: %zd of %zd bytes written", writev_result, writev_size ); - close( sd ); - sd = -1; - } - return( false ); + Error( "Incomplete frame write: %zd of %zd bytes written", writev_result, writev_size ); + close( sd ); + sd = -1; } - Debug( 1, "Wrote frame image, %d bytes", jpg_buffer_size ); + return( false ); + } + Debug( 1, "Wrote frame image, %d bytes", jpg_buffer_size ); - return( true ); + return( true ); } bool Event::WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame ) { - if ( config.timestamp_on_capture ) + if ( config.timestamp_on_capture ) + { + if ( !config.opt_frame_server || !SendFrameImage( image, alarm_frame) ) { - 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 ); - } + 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 ); } - else + } + else + { + Image ts_image( *image ); + monitor->TimestampImage( &ts_image, ×tamp ); + if ( !config.opt_frame_server || !SendFrameImage( &ts_image, 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 ); - } + 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 ); } - return( true ); + } + return( true ); } void Event::updateNotes( const StringSetMap &newNoteSetMap ) { - bool update = false; + bool update = false; - //Info( "Checking notes, %d <> %d", noteSetMap.size(), newNoteSetMap.size() ); - if ( newNoteSetMap.size() > 0 ) + //Info( "Checking notes, %d <> %d", noteSetMap.size(), newNoteSetMap.size() ); + if ( newNoteSetMap.size() > 0 ) + { + if ( noteSetMap.size() == 0 ) { - if ( noteSetMap.size() == 0 ) + noteSetMap = newNoteSetMap; + update = true; + } + else + { + for ( StringSetMap::const_iterator newNoteSetMapIter = newNoteSetMap.begin(); newNoteSetMapIter != newNoteSetMap.end(); newNoteSetMapIter++ ) + { + const std::string &newNoteGroup = newNoteSetMapIter->first; + const StringSet &newNoteSet = newNoteSetMapIter->second; + //Info( "Got %d new strings", newNoteSet.size() ); + if ( newNoteSet.size() > 0 ) { - noteSetMap = newNoteSetMap; + StringSetMap::iterator noteSetMapIter = noteSetMap.find( newNoteGroup ); + if ( noteSetMapIter == noteSetMap.end() ) + { + //Info( "Can't find note group %s, copying %d strings", newNoteGroup.c_str(), newNoteSet.size() ); + noteSetMap.insert( StringSetMap::value_type( newNoteGroup, newNoteSet ) ); update = true; - } - else - { - for ( StringSetMap::const_iterator newNoteSetMapIter = newNoteSetMap.begin(); newNoteSetMapIter != newNoteSetMap.end(); newNoteSetMapIter++ ) + } + else + { + StringSet ¬eSet = noteSetMapIter->second; + //Info( "Found note group %s, got %d strings", newNoteGroup.c_str(), newNoteSet.size() ); + for ( StringSet::const_iterator newNoteSetIter = newNoteSet.begin(); newNoteSetIter != newNoteSet.end(); newNoteSetIter++ ) { - const std::string &newNoteGroup = newNoteSetMapIter->first; - const StringSet &newNoteSet = newNoteSetMapIter->second; - //Info( "Got %d new strings", newNoteSet.size() ); - if ( newNoteSet.size() > 0 ) - { - StringSetMap::iterator noteSetMapIter = noteSetMap.find( newNoteGroup ); - if ( noteSetMapIter == noteSetMap.end() ) - { - //Info( "Can't find note group %s, copying %d strings", newNoteGroup.c_str(), newNoteSet.size() ); - noteSetMap.insert( StringSetMap::value_type( newNoteGroup, newNoteSet ) ); - update = true; - } - else - { - StringSet ¬eSet = noteSetMapIter->second; - //Info( "Found note group %s, got %d strings", newNoteGroup.c_str(), newNoteSet.size() ); - for ( StringSet::const_iterator newNoteSetIter = newNoteSet.begin(); newNoteSetIter != newNoteSet.end(); newNoteSetIter++ ) - { - const std::string &newNote = *newNoteSetIter; - StringSet::iterator noteSetIter = noteSet.find( newNote ); - if ( noteSetIter == noteSet.end() ) - { - noteSet.insert( newNote ); - update = true; - } - } - } - } + const std::string &newNote = *newNoteSetIter; + StringSet::iterator noteSetIter = noteSet.find( newNote ); + if ( noteSetIter == noteSet.end() ) + { + noteSet.insert( newNote ); + update = true; + } } + } } + } } + } - if ( update ) - { - std::string notes; - createNotes( notes ); + if ( update ) + { + std::string notes; + createNotes( notes ); - Debug( 2, "Updating notes for event %d, '%s'", id, notes.c_str() ); - static char sql[ZM_SQL_MED_BUFSIZ]; + Debug( 2, "Updating notes for event %d, '%s'", id, notes.c_str() ); + static char sql[ZM_SQL_MED_BUFSIZ]; #if USE_PREPARED_SQL - static MYSQL_STMT *stmt = 0; + static MYSQL_STMT *stmt = 0; - char notesStr[ZM_SQL_MED_BUFSIZ] = ""; - unsigned long notesLen = 0; + char notesStr[ZM_SQL_MED_BUFSIZ] = ""; + unsigned long notesLen = 0; - if ( !stmt ) - { - const char *sql = "update Events set Notes = ? where Id = ?"; + if ( !stmt ) + { + const char *sql = "update Events set Notes = ? where Id = ?"; - stmt = mysql_stmt_init( &dbconn ); - if ( mysql_stmt_prepare( stmt, sql, strlen(sql) ) ) - { - Fatal( "Unable to prepare sql '%s': %s", sql, mysql_stmt_error(stmt) ); - } + stmt = mysql_stmt_init( &dbconn ); + if ( mysql_stmt_prepare( stmt, sql, strlen(sql) ) ) + { + Fatal( "Unable to prepare sql '%s': %s", sql, mysql_stmt_error(stmt) ); + } - /* Get the parameter count from the statement */ - if ( mysql_stmt_param_count( stmt ) != 2 ) - { - Fatal( "Unexpected parameter count %ld in sql '%s'", mysql_stmt_param_count( stmt ), sql ); - } + /* Get the parameter count from the statement */ + if ( mysql_stmt_param_count( stmt ) != 2 ) + { + Fatal( "Unexpected parameter count %ld in sql '%s'", mysql_stmt_param_count( stmt ), sql ); + } - MYSQL_BIND bind[2]; - memset(bind, 0, sizeof(bind)); + MYSQL_BIND bind[2]; + memset(bind, 0, sizeof(bind)); - /* STRING PARAM */ - bind[0].buffer_type = MYSQL_TYPE_STRING; - bind[0].buffer = (char *)notesStr; - bind[0].buffer_length = sizeof(notesStr); - bind[0].is_null = 0; - bind[0].length = ¬esLen; + /* STRING PARAM */ + bind[0].buffer_type = MYSQL_TYPE_STRING; + bind[0].buffer = (char *)notesStr; + bind[0].buffer_length = sizeof(notesStr); + bind[0].is_null = 0; + bind[0].length = ¬esLen; - bind[1].buffer_type= MYSQL_TYPE_LONG; - bind[1].buffer= (char *)&id; - bind[1].is_null= 0; - bind[1].length= 0; + bind[1].buffer_type= MYSQL_TYPE_LONG; + bind[1].buffer= (char *)&id; + bind[1].is_null= 0; + bind[1].length= 0; - /* Bind the buffers */ - if ( mysql_stmt_bind_param( stmt, bind ) ) - { - Fatal( "Unable to bind sql '%s': %s", sql, mysql_stmt_error(stmt) ); - } - } - - strncpy( notesStr, notes.c_str(), sizeof(notesStr) ); - notesLen = notes.length(); - - if ( mysql_stmt_execute( stmt ) ) - { - Fatal( "Unable to execute sql '%s': %s", sql, mysql_stmt_error(stmt) ); - } -#else - static char escapedNotes[ZM_SQL_MED_BUFSIZ]; - - mysql_real_escape_string( &dbconn, escapedNotes, notes.c_str(), notes.length() ); - - snprintf( sql, sizeof(sql), "update Events set Notes = '%s' where Id = %d", escapedNotes, id ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't insert event: %s", mysql_error( &dbconn ) ); - } -#endif + /* Bind the buffers */ + if ( mysql_stmt_bind_param( stmt, bind ) ) + { + Fatal( "Unable to bind sql '%s': %s", sql, mysql_stmt_error(stmt) ); + } } + + strncpy( notesStr, notes.c_str(), sizeof(notesStr) ); + notesLen = notes.length(); + + if ( mysql_stmt_execute( stmt ) ) + { + Fatal( "Unable to execute sql '%s': %s", sql, mysql_stmt_error(stmt) ); + } +#else + static char escapedNotes[ZM_SQL_MED_BUFSIZ]; + + mysql_real_escape_string( &dbconn, escapedNotes, notes.c_str(), notes.length() ); + + snprintf( sql, sizeof(sql), "update Events set Notes = '%s' where Id = %d", escapedNotes, id ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't insert event: %s", mysql_error( &dbconn ) ); + } +#endif + } } void Event::AddFrames( int n_frames, Image **images, struct timeval **timestamps ) { - for (int i = 0; i < n_frames; i += ZM_SQL_BATCH_SIZE) { - AddFramesInternal(n_frames, i, images, timestamps); - } + for (int i = 0; i < n_frames; i += ZM_SQL_BATCH_SIZE) { + AddFramesInternal(n_frames, i, images, timestamps); + } } void Event::AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps ) { - static char sql[ZM_SQL_LGE_BUFSIZ]; - strncpy( sql, "insert into Frames ( EventId, FrameId, TimeStamp, Delta ) values ", sizeof(sql) ); - int frameCount = 0; - for ( int i = start_frame; i < n_frames && i - start_frame < ZM_SQL_BATCH_SIZE; i++ ) + static char sql[ZM_SQL_LGE_BUFSIZ]; + strncpy( sql, "insert into Frames ( EventId, FrameId, TimeStamp, Delta ) values ", sizeof(sql) ); + int frameCount = 0; + for ( int i = start_frame; i < n_frames && i - start_frame < ZM_SQL_BATCH_SIZE; i++ ) + { + if ( !timestamps[i]->tv_sec ) { - if ( !timestamps[i]->tv_sec ) - { - Debug( 1, "Not adding pre-capture frame %d, zero timestamp", i ); - continue; - } - - frames++; - - static char event_file[PATH_MAX]; - snprintf( event_file, sizeof(event_file), capture_file_format, path, frames ); - - Debug( 1, "Writing pre-capture frame %d", frames ); - WriteFrameImage( images[i], *(timestamps[i]), event_file ); - - struct DeltaTimeval delta_time; - DELTA_TIMEVAL( delta_time, *(timestamps[i]), start_time, DT_PREC_2 ); - - int sql_len = strlen(sql); - snprintf( sql+sql_len, sizeof(sql)-sql_len, "( %d, %d, from_unixtime(%ld), %s%ld.%02ld ), ", id, frames, timestamps[i]->tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec ); - - frameCount++; - } - - if ( frameCount ) - { - Debug( 1, "Adding %d/%d frames to DB", frameCount, n_frames ); - *(sql+strlen(sql)-2) = '\0'; - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't insert frames: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - last_db_frame = frames; - } - else - { - Debug( 1, "No valid pre-capture frames to add" ); - } -} - -void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image *alarm_image ) -{ - if ( !timestamp.tv_sec ) - { - Debug( 1, "Not adding new frame, zero timestamp" ); - return; + Debug( 1, "Not adding pre-capture frame %d, zero timestamp", i ); + continue; } frames++; @@ -561,922 +519,964 @@ void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image * static char event_file[PATH_MAX]; snprintf( event_file, sizeof(event_file), capture_file_format, path, frames ); - Debug( 1, "Writing capture frame %d", frames ); - WriteFrameImage( image, timestamp, event_file ); + Debug( 1, "Writing pre-capture frame %d", frames ); + WriteFrameImage( images[i], *(timestamps[i]), event_file ); struct DeltaTimeval delta_time; - DELTA_TIMEVAL( delta_time, timestamp, start_time, DT_PREC_2 ); + DELTA_TIMEVAL( delta_time, *(timestamps[i]), start_time, DT_PREC_2 ); - bool db_frame = (score>=0) || ((frames%config.bulk_frame_interval)==0) || !frames; + int sql_len = strlen(sql); + snprintf( sql+sql_len, sizeof(sql)-sql_len, "( %d, %d, from_unixtime(%ld), %s%ld.%02ld ), ", id, frames, timestamps[i]->tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec ); - if ( db_frame ) + frameCount++; + } + + if ( frameCount ) + { + Debug( 1, "Adding %d/%d frames to DB", frameCount, n_frames ); + *(sql+strlen(sql)-2) = '\0'; + if ( mysql_query( &dbconn, sql ) ) { - const char *frame_type = score>0?"Alarm":(score<0?"Bulk":"Normal"); - - Debug( 1, "Adding frame %d to DB", frames ); - static char sql[ZM_SQL_MED_BUFSIZ]; - snprintf( sql, sizeof(sql), "insert into Frames ( EventId, FrameId, Type, TimeStamp, Delta, Score ) values ( %d, %d, '%s', from_unixtime( %ld ), %s%ld.%02ld, %d )", id, frames, frame_type, timestamp.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, score ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't insert frame: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - last_db_frame = frames; - - // We are writing a bulk frame - if ( score < 0 ) - { - snprintf( sql, sizeof(sql), "update Events set Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d where Id = %d", delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, id ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't update event: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - } + Error( "Can't insert frames: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); } + last_db_frame = frames; + } + else + { + Debug( 1, "No valid pre-capture frames to add" ); + } +} - end_time = timestamp; +void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image *alarm_image ) +{ + if ( !timestamp.tv_sec ) + { + Debug( 1, "Not adding new frame, zero timestamp" ); + return; + } - if ( score > 0 ) + frames++; + + static char event_file[PATH_MAX]; + snprintf( event_file, sizeof(event_file), capture_file_format, path, frames ); + + Debug( 1, "Writing capture frame %d", frames ); + WriteFrameImage( image, timestamp, event_file ); + + struct DeltaTimeval delta_time; + DELTA_TIMEVAL( delta_time, timestamp, start_time, DT_PREC_2 ); + + bool db_frame = (score>=0) || ((frames%config.bulk_frame_interval)==0) || !frames; + + if ( db_frame ) + { + const char *frame_type = score>0?"Alarm":(score<0?"Bulk":"Normal"); + + Debug( 1, "Adding frame %d to DB", frames ); + static char sql[ZM_SQL_MED_BUFSIZ]; + snprintf( sql, sizeof(sql), "insert into Frames ( EventId, FrameId, Type, TimeStamp, Delta, Score ) values ( %d, %d, '%s', from_unixtime( %ld ), %s%ld.%02ld, %d )", id, frames, frame_type, timestamp.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, score ); + if ( mysql_query( &dbconn, sql ) ) { - alarm_frames++; - - tot_score += score; - if ( score > (int)max_score ) - max_score = score; - - if ( alarm_image ) - { - snprintf( event_file, sizeof(event_file), analyse_file_format, path, frames ); - - Debug( 1, "Writing analysis frame %d", frames ); - WriteFrameImage( alarm_image, timestamp, event_file, true ); - } + Error( "Can't insert frame: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); } - - /* This makes viewing the diagnostic images impossible because it keeps deleting them - if ( config.record_diag_images ) + last_db_frame = frames; + + // We are writing a bulk frame + if ( score < 0 ) { - char diag_glob[PATH_MAX] = ""; - - snprintf( diag_glob, sizeof(diag_glob), "%s/%d/diag-*.jpg", config.dir_events, monitor->Id() ); - glob_t pglob; - int glob_status = glob( diag_glob, 0, 0, &pglob ); - if ( glob_status != 0 ) - { - if ( glob_status < 0 ) - { - Error( "Can't glob '%s': %s", diag_glob, strerror(errno) ); - } - else - { - Debug( 1, "Can't glob '%s': %d", diag_glob, glob_status ); - } - } - else - { - char new_diag_path[PATH_MAX] = ""; - for ( int i = 0; i < pglob.gl_pathc; i++ ) - { - char *diag_path = pglob.gl_pathv[i]; - - char *diag_file = strstr( diag_path, "diag-" ); - - if ( diag_file ) - { - snprintf( new_diag_path, sizeof(new_diag_path), general_file_format, path, frames, diag_file ); - - if ( rename( diag_path, new_diag_path ) < 0 ) - { - Error( "Can't rename '%s' to '%s': %s", diag_path, new_diag_path, strerror(errno) ); - } - } - } - } - globfree( &pglob ); + snprintf( sql, sizeof(sql), "update Events set Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d where Id = %d", delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, id ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't update event: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } } - */ + } + + end_time = timestamp; + + if ( score > 0 ) + { + alarm_frames++; + + tot_score += score; + if ( score > (int)max_score ) + max_score = score; + + if ( alarm_image ) + { + snprintf( event_file, sizeof(event_file), analyse_file_format, path, frames ); + + Debug( 1, "Writing analysis frame %d", frames ); + WriteFrameImage( alarm_image, timestamp, event_file, true ); + } + } + + /* This makes viewing the diagnostic images impossible because it keeps deleting them + if ( config.record_diag_images ) + { + char diag_glob[PATH_MAX] = ""; + + snprintf( diag_glob, sizeof(diag_glob), "%s/%d/diag-*.jpg", config.dir_events, monitor->Id() ); + glob_t pglob; + int glob_status = glob( diag_glob, 0, 0, &pglob ); + if ( glob_status != 0 ) + { + if ( glob_status < 0 ) + { + Error( "Can't glob '%s': %s", diag_glob, strerror(errno) ); + } + else + { + Debug( 1, "Can't glob '%s': %d", diag_glob, glob_status ); + } + } + else + { + char new_diag_path[PATH_MAX] = ""; + for ( int i = 0; i < pglob.gl_pathc; i++ ) + { + char *diag_path = pglob.gl_pathv[i]; + + char *diag_file = strstr( diag_path, "diag-" ); + + if ( diag_file ) + { + snprintf( new_diag_path, sizeof(new_diag_path), general_file_format, path, frames, diag_file ); + + if ( rename( diag_path, new_diag_path ) < 0 ) + { + Error( "Can't rename '%s' to '%s': %s", diag_path, new_diag_path, strerror(errno) ); + } + } + } + } + globfree( &pglob ); + } + */ } bool EventStream::loadInitialEventData( int monitor_id, time_t event_time ) { - static char sql[ZM_SQL_SML_BUFSIZ]; + static char sql[ZM_SQL_SML_BUFSIZ]; - snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %d and unix_timestamp( EndTime ) > %ld order by Id asc limit 1", monitor_id, event_time ); + snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %d and unix_timestamp( EndTime ) > %ld order by Id asc limit 1", monitor_id, event_time ); - if ( mysql_query( &dbconn, sql ) ) + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + MYSQL_ROW dbrow = mysql_fetch_row( result ); + + if ( mysql_errno( &dbconn ) ) + { + Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + + int init_event_id = atoi( dbrow[0] ); + + mysql_free_result( result ); + + loadEventData( init_event_id ); + + if ( event_time ) + { + curr_stream_time = event_time; + curr_frame_id = 1; + if ( event_time >= event_data->start_time ) { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - MYSQL_ROW dbrow = mysql_fetch_row( result ); - - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - - int init_event_id = atoi( dbrow[0] ); - - mysql_free_result( result ); - - loadEventData( init_event_id ); - - if ( event_time ) - { - curr_stream_time = event_time; - curr_frame_id = 1; - if ( event_time >= event_data->start_time ) + for (unsigned int i = 0; i < event_data->frame_count; i++ ) + { + //Info( "eft %d > et %d", event_data->frames[i].timestamp, event_time ); + if ( event_data->frames[i].timestamp >= event_time ) { - for (unsigned int i = 0; i < event_data->frame_count; i++ ) - { - //Info( "eft %d > et %d", event_data->frames[i].timestamp, event_time ); - if ( event_data->frames[i].timestamp >= event_time ) - { - curr_frame_id = i+1; - Debug( 3, "Set cst:%.2f", curr_stream_time ); - Debug( 3, "Set cfid:%d", curr_frame_id ); - break; - } - } - Debug( 3, "Skipping %ld frames", event_data->frame_count ); + curr_frame_id = i+1; + Debug( 3, "Set cst:%.2f", curr_stream_time ); + Debug( 3, "Set cfid:%d", curr_frame_id ); + break; } + } + Debug( 3, "Skipping %ld frames", event_data->frame_count ); } - return( true ); + } + return( true ); } bool EventStream::loadInitialEventData( int init_event_id, int init_frame_id ) { - loadEventData( init_event_id ); + loadEventData( init_event_id ); - if ( init_frame_id ) - { - curr_stream_time = event_data->frames[init_frame_id-1].timestamp; - curr_frame_id = init_frame_id; - } - else - { - curr_stream_time = event_data->start_time; - } + if ( init_frame_id ) + { + curr_stream_time = event_data->frames[init_frame_id-1].timestamp; + curr_frame_id = init_frame_id; + } + else + { + curr_stream_time = event_data->start_time; + } - return( true ); + return( true ); } bool EventStream::loadEventData( int event_id ) { - static char sql[ZM_SQL_MED_BUFSIZ]; + static char sql[ZM_SQL_MED_BUFSIZ]; - snprintf( sql, sizeof(sql), "select M.Id, M.Name, E.Frames, unix_timestamp( StartTime ) as StartTimestamp, max(F.Delta)-min(F.Delta) as Duration from Events as E inner join Monitors as M on E.MonitorId = M.Id inner join Frames as F on E.Id = F.EventId where E.Id = %d group by E.Id", event_id ); + snprintf( sql, sizeof(sql), "select M.Id, M.Name, E.Frames, unix_timestamp( StartTime ) as StartTimestamp, max(F.Delta)-min(F.Delta) as Duration from Events as E inner join Monitors as M on E.MonitorId = M.Id inner join Frames as F on E.Id = F.EventId where E.Id = %d group by E.Id", event_id ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - if ( !mysql_num_rows( result ) ) - { - Fatal( "Unable to load event %d, not found in DB", event_id ); - } + if ( !mysql_num_rows( result ) ) + { + Fatal( "Unable to load event %d, not found in DB", event_id ); + } - MYSQL_ROW dbrow = mysql_fetch_row( result ); + MYSQL_ROW dbrow = mysql_fetch_row( result ); - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + if ( mysql_errno( &dbconn ) ) + { + Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - delete event_data; - event_data = new EventData; - event_data->event_id = event_id; - event_data->monitor_id = atoi( dbrow[0] ); - event_data->start_time = atoi(dbrow[3]); - if ( config.use_deep_storage ) - { - struct tm *event_time = localtime( &event_data->start_time ); - if ( config.dir_events[0] == '/' ) - snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", config.dir_events, event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec ); - else - snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", staticConfig.PATH_WEB.c_str(), config.dir_events, event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec ); - } + delete event_data; + event_data = new EventData; + event_data->event_id = event_id; + event_data->monitor_id = atoi( dbrow[0] ); + event_data->start_time = atoi(dbrow[3]); + if ( config.use_deep_storage ) + { + struct tm *event_time = localtime( &event_data->start_time ); + if ( config.dir_events[0] == '/' ) + snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", config.dir_events, event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec ); else + snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", staticConfig.PATH_WEB.c_str(), config.dir_events, event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec ); + } + else + { + if ( config.dir_events[0] == '/' ) + snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%ld", config.dir_events, event_data->monitor_id, event_data->event_id ); + else + snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%ld", staticConfig.PATH_WEB.c_str(), config.dir_events, event_data->monitor_id, event_data->event_id ); + } + event_data->frame_count = dbrow[2] == NULL ? 0 : atoi(dbrow[2]); + event_data->duration = atof(dbrow[4]); + + updateFrameRate( (double)event_data->frame_count/event_data->duration ); + + mysql_free_result( result ); + + snprintf( sql, sizeof(sql), "select FrameId, unix_timestamp( `TimeStamp` ), Delta from Frames where EventId = %d order by FrameId asc", event_id ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + + result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + + event_data->n_frames = mysql_num_rows( result ); + + event_data->frames = new FrameData[event_data->frame_count]; + int id, last_id = 0; + time_t timestamp, last_timestamp = event_data->start_time; + double delta, last_delta = 0.0; + while ( ( dbrow = mysql_fetch_row( result ) ) ) + { + id = atoi(dbrow[0]); + timestamp = atoi(dbrow[1]); + delta = atof(dbrow[2]); + int id_diff = id - last_id; + double frame_delta = (delta-last_delta)/id_diff; + if ( id_diff > 1 ) { - if ( config.dir_events[0] == '/' ) - snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%ld", config.dir_events, event_data->monitor_id, event_data->event_id ); - else - snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%ld", staticConfig.PATH_WEB.c_str(), config.dir_events, event_data->monitor_id, event_data->event_id ); + for ( int i = last_id+1; i < id; i++ ) + { + event_data->frames[i-1].timestamp = (time_t)(last_timestamp + ((i-last_id)*frame_delta)); + event_data->frames[i-1].offset = (time_t)(event_data->frames[i-1].timestamp-event_data->start_time); + event_data->frames[i-1].delta = frame_delta; + event_data->frames[i-1].in_db = false; + } } - event_data->frame_count = dbrow[2] == NULL ? 0 : atoi(dbrow[2]); - event_data->duration = atof(dbrow[4]); + event_data->frames[id-1].timestamp = timestamp; + event_data->frames[id-1].offset = (time_t)(event_data->frames[id-1].timestamp-event_data->start_time); + event_data->frames[id-1].delta = id>1?frame_delta:0.0; + event_data->frames[id-1].in_db = true; + last_id = id; + last_delta = delta; + last_timestamp = timestamp; + } + if ( mysql_errno( &dbconn ) ) + { + Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - updateFrameRate( (double)event_data->frame_count/event_data->duration ); + //for ( int i = 0; i < 250; i++ ) + //{ + //Info( "%d -> %d @ %f (%d)", i+1, event_data->frames[i].timestamp, event_data->frames[i].delta, event_data->frames[i].in_db ); + //} - mysql_free_result( result ); + mysql_free_result( result ); - snprintf( sql, sizeof(sql), "select FrameId, unix_timestamp( `TimeStamp` ), Delta from Frames where EventId = %d order by FrameId asc", event_id ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + if ( forceEventChange || mode == ALL_GAPLESS ) + { + if ( replay_rate > 0 ) + curr_stream_time = event_data->frames[0].timestamp; + else + curr_stream_time = event_data->frames[event_data->frame_count-1].timestamp; + } + Debug( 2, "Event:%ld, Frames:%ld, Duration: %.2f", event_data->event_id, event_data->frame_count, event_data->duration ); - result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - - event_data->n_frames = mysql_num_rows( result ); - - event_data->frames = new FrameData[event_data->frame_count]; - int id, last_id = 0; - time_t timestamp, last_timestamp = event_data->start_time; - double delta, last_delta = 0.0; - while ( ( dbrow = mysql_fetch_row( result ) ) ) - { - id = atoi(dbrow[0]); - timestamp = atoi(dbrow[1]); - delta = atof(dbrow[2]); - int id_diff = id - last_id; - double frame_delta = (delta-last_delta)/id_diff; - if ( id_diff > 1 ) - { - for ( int i = last_id+1; i < id; i++ ) - { - event_data->frames[i-1].timestamp = (time_t)(last_timestamp + ((i-last_id)*frame_delta)); - event_data->frames[i-1].offset = (time_t)(event_data->frames[i-1].timestamp-event_data->start_time); - event_data->frames[i-1].delta = frame_delta; - event_data->frames[i-1].in_db = false; - } - } - event_data->frames[id-1].timestamp = timestamp; - event_data->frames[id-1].offset = (time_t)(event_data->frames[id-1].timestamp-event_data->start_time); - event_data->frames[id-1].delta = id>1?frame_delta:0.0; - event_data->frames[id-1].in_db = true; - last_id = id; - last_delta = delta; - last_timestamp = timestamp; - } - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - - //for ( int i = 0; i < 250; i++ ) - //{ - //Info( "%d -> %d @ %f (%d)", i+1, event_data->frames[i].timestamp, event_data->frames[i].delta, event_data->frames[i].in_db ); - //} - - mysql_free_result( result ); - - if ( forceEventChange || mode == ALL_GAPLESS ) - { - if ( replay_rate > 0 ) - curr_stream_time = event_data->frames[0].timestamp; - else - curr_stream_time = event_data->frames[event_data->frame_count-1].timestamp; - } - Debug( 2, "Event:%ld, Frames:%ld, Duration: %.2f", event_data->event_id, event_data->frame_count, event_data->duration ); - - return( true ); + return( true ); } void EventStream::processCommand( const CmdMsg *msg ) { - Debug( 2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0] ) + Debug( 2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0] ) // Check for incoming command switch( (MsgCommand)msg->msg_data[0] ) { - case CMD_PAUSE : + case CMD_PAUSE : { - Debug( 1, "Got PAUSE command" ); + Debug( 1, "Got PAUSE command" ); - // Set paused flag - paused = true; - replay_rate = ZM_RATE_BASE; - last_frame_sent = TV_2_FLOAT( now ); - break; + // Set paused flag + paused = true; + replay_rate = ZM_RATE_BASE; + last_frame_sent = TV_2_FLOAT( now ); + break; } - case CMD_PLAY : + case CMD_PLAY : { - Debug( 1, "Got PLAY command" ); - if ( paused ) - { - // Clear paused flag - paused = false; - } - - // If we are in single event mode and at the last frame, replay the current event - if ( (mode == SINGLE) && (curr_frame_id == event_data->frame_count) ) - curr_frame_id = 1; - - replay_rate = ZM_RATE_BASE; - break; - } - case CMD_VARPLAY : - { - Debug( 1, "Got VARPLAY command" ); - if ( paused ) - { - // Clear paused flag - paused = false; - } - replay_rate = ntohs(((unsigned char)msg->msg_data[2]<<8)|(unsigned char)msg->msg_data[1])-32768; - break; - } - case CMD_STOP : - { - Debug( 1, "Got STOP command" ); - + Debug( 1, "Got PLAY command" ); + if ( paused ) + { // Clear paused flag paused = false; - break; + } + + // If we are in single event mode and at the last frame, replay the current event + if ( (mode == SINGLE) && (curr_frame_id == event_data->frame_count) ) + curr_frame_id = 1; + + replay_rate = ZM_RATE_BASE; + break; } - case CMD_FASTFWD : + case CMD_VARPLAY : { - Debug( 1, "Got FAST FWD command" ); - if ( paused ) - { - // Clear paused flag - paused = false; - } - // Set play rate - switch ( replay_rate ) - { - case 2 * ZM_RATE_BASE : - replay_rate = 5 * ZM_RATE_BASE; - break; - case 5 * ZM_RATE_BASE : - replay_rate = 10 * ZM_RATE_BASE; - break; - case 10 * ZM_RATE_BASE : - replay_rate = 25 * ZM_RATE_BASE; - break; - case 25 * ZM_RATE_BASE : - case 50 * ZM_RATE_BASE : - replay_rate = 50 * ZM_RATE_BASE; - break; - default : - replay_rate = 2 * ZM_RATE_BASE; - break; - } - break; - } - case CMD_SLOWFWD : - { - Debug( 1, "Got SLOW FWD command" ); - // Set paused flag - paused = true; - // Set play rate - replay_rate = ZM_RATE_BASE; - // Set step - step = 1; - break; - } - case CMD_SLOWREV : - { - Debug( 1, "Got SLOW REV command" ); - // Set paused flag - paused = true; - // Set play rate - replay_rate = ZM_RATE_BASE; - // Set step - step = -1; - break; - } - case CMD_FASTREV : - { - Debug( 1, "Got FAST REV command" ); - if ( paused ) - { - // Clear paused flag - paused = false; - } - // Set play rate - switch ( replay_rate ) - { - case -2 * ZM_RATE_BASE : - replay_rate = -5 * ZM_RATE_BASE; - break; - case -5 * ZM_RATE_BASE : - replay_rate = -10 * ZM_RATE_BASE; - break; - case -10 * ZM_RATE_BASE : - replay_rate = -25 * ZM_RATE_BASE; - break; - case -25 * ZM_RATE_BASE : - case -50 * ZM_RATE_BASE : - replay_rate = -50 * ZM_RATE_BASE; - break; - default : - replay_rate = -2 * ZM_RATE_BASE; - break; - } - break; - } - case CMD_ZOOMIN : - { - x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; - y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; - Debug( 1, "Got ZOOM IN command, to %d,%d", x, y ); - switch ( zoom ) - { - case 100: - zoom = 150; - break; - case 150: - zoom = 200; - break; - case 200: - zoom = 300; - break; - case 300: - zoom = 400; - break; - case 400: - default : - zoom = 500; - break; - } - break; - } - case CMD_ZOOMOUT : - { - Debug( 1, "Got ZOOM OUT command" ); - switch ( zoom ) - { - case 500: - zoom = 400; - break; - case 400: - zoom = 300; - break; - case 300: - zoom = 200; - break; - case 200: - zoom = 150; - break; - case 150: - default : - zoom = 100; - break; - } - break; - } - case CMD_PAN : - { - x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; - y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; - Debug( 1, "Got PAN command, to %d,%d", x, y ); - break; - } - case CMD_SCALE : - { - scale = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; - Debug( 1, "Got SCALE command, to %d", scale ); - break; - } - case CMD_PREV : - { - Debug( 1, "Got PREV command" ); - if ( replay_rate >= 0 ) - curr_frame_id = 0; - else - curr_frame_id = event_data->frame_count+1; + Debug( 1, "Got VARPLAY command" ); + if ( paused ) + { + // Clear paused flag paused = false; - forceEventChange = true; - break; + } + replay_rate = ntohs(((unsigned char)msg->msg_data[2]<<8)|(unsigned char)msg->msg_data[1])-32768; + break; } - case CMD_NEXT : + case CMD_STOP : { - Debug( 1, "Got NEXT command" ); - if ( replay_rate >= 0 ) - curr_frame_id = event_data->frame_count+1; - else - curr_frame_id = 0; + Debug( 1, "Got STOP command" ); + + // Clear paused flag + paused = false; + break; + } + case CMD_FASTFWD : + { + Debug( 1, "Got FAST FWD command" ); + if ( paused ) + { + // Clear paused flag paused = false; - forceEventChange = true; - break; + } + // Set play rate + switch ( replay_rate ) + { + case 2 * ZM_RATE_BASE : + replay_rate = 5 * ZM_RATE_BASE; + break; + case 5 * ZM_RATE_BASE : + replay_rate = 10 * ZM_RATE_BASE; + break; + case 10 * ZM_RATE_BASE : + replay_rate = 25 * ZM_RATE_BASE; + break; + case 25 * ZM_RATE_BASE : + case 50 * ZM_RATE_BASE : + replay_rate = 50 * ZM_RATE_BASE; + break; + default : + replay_rate = 2 * ZM_RATE_BASE; + break; + } + break; } - case CMD_SEEK : + case CMD_SLOWFWD : { - int offset = ((unsigned char)msg->msg_data[1]<<24)|((unsigned char)msg->msg_data[2]<<16)|((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; - curr_frame_id = (int)(event_data->frame_count*offset/event_data->duration); - Debug( 1, "Got SEEK command, to %d (new cfid: %d)", offset, curr_frame_id ); - break; + Debug( 1, "Got SLOW FWD command" ); + // Set paused flag + paused = true; + // Set play rate + replay_rate = ZM_RATE_BASE; + // Set step + step = 1; + break; } - case CMD_QUERY : + case CMD_SLOWREV : { - Debug( 1, "Got QUERY command, sending STATUS" ); - break; + Debug( 1, "Got SLOW REV command" ); + // Set paused flag + paused = true; + // Set play rate + replay_rate = ZM_RATE_BASE; + // Set step + step = -1; + break; } - default : + case CMD_FASTREV : { - // Do nothing, for now + Debug( 1, "Got FAST REV command" ); + if ( paused ) + { + // Clear paused flag + paused = false; + } + // Set play rate + switch ( replay_rate ) + { + case -2 * ZM_RATE_BASE : + replay_rate = -5 * ZM_RATE_BASE; + break; + case -5 * ZM_RATE_BASE : + replay_rate = -10 * ZM_RATE_BASE; + break; + case -10 * ZM_RATE_BASE : + replay_rate = -25 * ZM_RATE_BASE; + break; + case -25 * ZM_RATE_BASE : + case -50 * ZM_RATE_BASE : + replay_rate = -50 * ZM_RATE_BASE; + break; + default : + replay_rate = -2 * ZM_RATE_BASE; + break; + } + break; + } + case CMD_ZOOMIN : + { + x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; + y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; + Debug( 1, "Got ZOOM IN command, to %d,%d", x, y ); + switch ( zoom ) + { + case 100: + zoom = 150; + break; + case 150: + zoom = 200; + break; + case 200: + zoom = 300; + break; + case 300: + zoom = 400; + break; + case 400: + default : + zoom = 500; + break; + } + break; + } + case CMD_ZOOMOUT : + { + Debug( 1, "Got ZOOM OUT command" ); + switch ( zoom ) + { + case 500: + zoom = 400; + break; + case 400: + zoom = 300; + break; + case 300: + zoom = 200; + break; + case 200: + zoom = 150; + break; + case 150: + default : + zoom = 100; + break; + } + break; + } + case CMD_PAN : + { + x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; + y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; + Debug( 1, "Got PAN command, to %d,%d", x, y ); + break; + } + case CMD_SCALE : + { + scale = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; + Debug( 1, "Got SCALE command, to %d", scale ); + break; + } + case CMD_PREV : + { + Debug( 1, "Got PREV command" ); + if ( replay_rate >= 0 ) + curr_frame_id = 0; + else + curr_frame_id = event_data->frame_count+1; + paused = false; + forceEventChange = true; + break; + } + case CMD_NEXT : + { + Debug( 1, "Got NEXT command" ); + if ( replay_rate >= 0 ) + curr_frame_id = event_data->frame_count+1; + else + curr_frame_id = 0; + paused = false; + forceEventChange = true; + break; + } + case CMD_SEEK : + { + int offset = ((unsigned char)msg->msg_data[1]<<24)|((unsigned char)msg->msg_data[2]<<16)|((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; + curr_frame_id = (int)(event_data->frame_count*offset/event_data->duration); + Debug( 1, "Got SEEK command, to %d (new cfid: %d)", offset, curr_frame_id ); + break; + } + case CMD_QUERY : + { + Debug( 1, "Got QUERY command, sending STATUS" ); + break; + } + default : + { + // Do nothing, for now } } - struct { - int event; - int progress; - int rate; - int zoom; - bool paused; - } status_data; + struct { + int event; + int progress; + int rate; + int zoom; + bool paused; + } status_data; - status_data.event = event_data->event_id; - status_data.progress = (int)event_data->frames[curr_frame_id-1].offset; - status_data.rate = replay_rate; - status_data.zoom = zoom; - status_data.paused = paused; - Debug( 2, "E:%d, P:%d, p:%d R:%d, Z:%d", - status_data.event, - status_data.paused, - status_data.progress, - status_data.rate, - status_data.zoom - ); + status_data.event = event_data->event_id; + status_data.progress = (int)event_data->frames[curr_frame_id-1].offset; + status_data.rate = replay_rate; + status_data.zoom = zoom; + status_data.paused = paused; + Debug( 2, "E:%d, P:%d, p:%d R:%d, Z:%d", + status_data.event, + status_data.paused, + status_data.progress, + status_data.rate, + status_data.zoom + ); - DataMsg status_msg; - status_msg.msg_type = MSG_DATA_EVENT; - memcpy( &status_msg.msg_data, &status_data, sizeof(status_msg.msg_data) ); - if ( sendto( sd, &status_msg, sizeof(status_msg), MSG_DONTWAIT, (sockaddr *)&rem_addr, sizeof(rem_addr) ) < 0 ) + DataMsg status_msg; + status_msg.msg_type = MSG_DATA_EVENT; + memcpy( &status_msg.msg_data, &status_data, sizeof(status_msg.msg_data) ); + if ( sendto( sd, &status_msg, sizeof(status_msg), MSG_DONTWAIT, (sockaddr *)&rem_addr, sizeof(rem_addr) ) < 0 ) + { + //if ( errno != EAGAIN ) { - //if ( errno != EAGAIN ) - { - Error( "Can't sendto on sd %d: %s", sd, strerror(errno) ); - exit( -1 ); - } + Error( "Can't sendto on sd %d: %s", sd, strerror(errno) ); + exit( -1 ); } + } - updateFrameRate( (double)event_data->frame_count/event_data->duration ); + updateFrameRate( (double)event_data->frame_count/event_data->duration ); } void EventStream::checkEventLoaded() { - bool reload_event = false; - static char sql[ZM_SQL_SML_BUFSIZ]; + bool reload_event = false; + static char sql[ZM_SQL_SML_BUFSIZ]; - if ( curr_frame_id <= 0 ) + if ( curr_frame_id <= 0 ) + { + snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %ld and Id < %ld order by Id desc limit 1", event_data->monitor_id, event_data->event_id ); + reload_event = true; + } + else if ( (unsigned int)curr_frame_id > event_data->frame_count ) + { + snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %ld and Id > %ld order by Id asc limit 1", event_data->monitor_id, event_data->event_id ); + reload_event = true; + } + + if ( reload_event ) + { + if ( forceEventChange || mode != SINGLE ) { - snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %ld and Id < %ld order by Id desc limit 1", event_data->monitor_id, event_data->event_id ); - reload_event = true; - } - else if ( (unsigned int)curr_frame_id > event_data->frame_count ) - { - snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %ld and Id > %ld order by Id asc limit 1", event_data->monitor_id, event_data->event_id ); - reload_event = true; - } + //Info( "SQL:%s", sql ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - if ( reload_event ) - { - if ( forceEventChange || mode != SINGLE ) - { - //Info( "SQL:%s", sql ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + MYSQL_ROW dbrow = mysql_fetch_row( result ); - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - MYSQL_ROW dbrow = mysql_fetch_row( result ); + if ( mysql_errno( &dbconn ) ) + { + Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + if ( dbrow ) + { + int event_id = atoi(dbrow[0]); + Debug( 1, "Loading new event %d", event_id ); - if ( dbrow ) - { - int event_id = atoi(dbrow[0]); - Debug( 1, "Loading new event %d", event_id ); + loadEventData( event_id ); - loadEventData( event_id ); - - Debug( 2, "Current frame id = %d", curr_frame_id ); - if ( replay_rate < 0 ) - curr_frame_id = event_data->frame_count; - else - curr_frame_id = 1; - Debug( 2, "New frame id = %d", curr_frame_id ); - } - else - { - if ( curr_frame_id <= 0 ) - curr_frame_id = 1; - else - curr_frame_id = event_data->frame_count; - paused = true; - } - mysql_free_result( result ); - forceEventChange = false; - } + Debug( 2, "Current frame id = %d", curr_frame_id ); + if ( replay_rate < 0 ) + curr_frame_id = event_data->frame_count; else - { - if ( curr_frame_id <= 0 ) - curr_frame_id = 1; - else - curr_frame_id = event_data->frame_count; - paused = true; - } + curr_frame_id = 1; + Debug( 2, "New frame id = %d", curr_frame_id ); + } + else + { + if ( curr_frame_id <= 0 ) + curr_frame_id = 1; + else + curr_frame_id = event_data->frame_count; + paused = true; + } + mysql_free_result( result ); + forceEventChange = false; } + else + { + if ( curr_frame_id <= 0 ) + curr_frame_id = 1; + else + curr_frame_id = event_data->frame_count; + paused = true; + } + } } bool EventStream::sendFrame( int delta_us ) { - Debug( 2, "Sending frame %d", curr_frame_id ); + Debug( 2, "Sending frame %d", curr_frame_id ); - static char filepath[PATH_MAX]; - static struct stat filestat; - FILE *fdj = NULL; - - snprintf( filepath, sizeof(filepath), Event::capture_file_format, event_data->path, curr_frame_id ); + static char filepath[PATH_MAX]; + static struct stat filestat; + FILE *fdj = NULL; + + snprintf( filepath, sizeof(filepath), Event::capture_file_format, event_data->path, curr_frame_id ); #if HAVE_LIBAVCODEC - if ( type == MPEG ) + if ( type == MPEG ) + { + Image image( filepath ); + + Image *send_image = prepareImage( &image ); + + if ( !vid_stream ) { - Image image( filepath ); + vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height() ); + fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() ); + vid_stream->OpenStream(); + } + /* double pts = */ vid_stream->EncodeFrame( send_image->Buffer(), send_image->Size(), config.mpeg_timed_frames, delta_us*1000 ); + } + else +#endif // HAVE_LIBAVCODEC + { + static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE]; - Image *send_image = prepareImage( &image ); + int img_buffer_size = 0; + uint8_t *img_buffer = temp_img_buffer; - if ( !vid_stream ) - { - vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height() ); - fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() ); - vid_stream->OpenStream(); - } - /* double pts = */ vid_stream->EncodeFrame( send_image->Buffer(), send_image->Size(), config.mpeg_timed_frames, delta_us*1000 ); + bool send_raw = ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE)); + + fprintf( stdout, "--ZoneMinderFrame\r\n" ); + + if ( type != JPEG ) + send_raw = false; + + if ( send_raw ) + { + fdj = fopen( filepath, "rb" ); + if ( !fdj ) + { + Error( "Can't open %s: %s", filepath, strerror(errno) ); + return( false ); + } +#if HAVE_SENDFILE + if( fstat(fileno(fdj),&filestat) < 0 ) { + Error( "Failed getting information about file %s: %s", filepath, strerror(errno) ); + return( false ); + } +#else + img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj ); +#endif } else -#endif // HAVE_LIBAVCODEC { - static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE]; + Image image( filepath ); - int img_buffer_size = 0; - uint8_t *img_buffer = temp_img_buffer; + Image *send_image = prepareImage( &image ); - bool send_raw = ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE)); - - fprintf( stdout, "--ZoneMinderFrame\r\n" ); - - if ( type != JPEG ) - send_raw = false; - - if ( send_raw ) - { - fdj = fopen( filepath, "rb" ); - if ( !fdj ) - { - Error( "Can't open %s: %s", filepath, strerror(errno) ); - return( false ); - } -#if HAVE_SENDFILE - if( fstat(fileno(fdj),&filestat) < 0 ) { - Error( "Failed getting information about file %s: %s", filepath, strerror(errno) ); - return( false ); - } -#else - img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj ); -#endif - } - else - { - Image image( filepath ); - - Image *send_image = prepareImage( &image ); - - switch( type ) - { - case JPEG : - send_image->EncodeJpeg( img_buffer, &img_buffer_size ); - break; - case ZIP : + switch( type ) + { + case JPEG : + send_image->EncodeJpeg( img_buffer, &img_buffer_size ); + break; + case ZIP : #if HAVE_ZLIB_H - unsigned long zip_buffer_size; - send_image->Zip( img_buffer, &zip_buffer_size ); - img_buffer_size = zip_buffer_size; - break; + unsigned long zip_buffer_size; + send_image->Zip( img_buffer, &zip_buffer_size ); + img_buffer_size = zip_buffer_size; + break; #else - Error("zlib is required for zipped images. Falling back to raw image"); - type = RAW; + Error("zlib is required for zipped images. Falling back to raw image"); + type = RAW; #endif // HAVE_ZLIB_H - case RAW : - img_buffer = (uint8_t*)(send_image->Buffer()); - img_buffer_size = send_image->Size(); - break; - default: - Fatal( "Unexpected frame type %d", type ); - break; - } - } - - switch( type ) - { - case JPEG : - fprintf( stdout, "Content-Type: image/jpeg\r\n" ); - break; - case RAW : - fprintf( stdout, "Content-Type: image/x-rgb\r\n" ); - break; - case ZIP : - fprintf( stdout, "Content-Type: image/x-rgbz\r\n" ); - break; - default : - Fatal( "Unexpected frame type %d", type ); - break; - } - - - if(send_raw) { -#if HAVE_SENDFILE - fprintf( stdout, "Content-Length: %d\r\n\r\n", (int)filestat.st_size ); - if(zm_sendfile(fileno(stdout), fileno(fdj), 0, (int)filestat.st_size) != (int)filestat.st_size) { - /* sendfile() failed, use standard way instead */ - img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj ); - if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) { - Error("Unable to send raw frame %u: %s",curr_frame_id,strerror(errno)); - return( false ); - } - } -#else - fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size ); - if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) { - Error("Unable to send raw frame %u: %s",curr_frame_id,strerror(errno)); - return( false ); - } -#endif - fclose(fdj); /* Close the file handle */ - } else { - fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size ); - if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) - { - Error( "Unable to send stream frame: %s", strerror(errno) ); - return( false ); - } - } - - fprintf( stdout, "\r\n\r\n" ); - fflush( stdout ); + case RAW : + img_buffer = (uint8_t*)(send_image->Buffer()); + img_buffer_size = send_image->Size(); + break; + default: + Fatal( "Unexpected frame type %d", type ); + break; + } } - last_frame_sent = TV_2_FLOAT( now ); - return( true ); + + switch( type ) + { + case JPEG : + fprintf( stdout, "Content-Type: image/jpeg\r\n" ); + break; + case RAW : + fprintf( stdout, "Content-Type: image/x-rgb\r\n" ); + break; + case ZIP : + fprintf( stdout, "Content-Type: image/x-rgbz\r\n" ); + break; + default : + Fatal( "Unexpected frame type %d", type ); + break; + } + + + if(send_raw) { +#if HAVE_SENDFILE + fprintf( stdout, "Content-Length: %d\r\n\r\n", (int)filestat.st_size ); + if(zm_sendfile(fileno(stdout), fileno(fdj), 0, (int)filestat.st_size) != (int)filestat.st_size) { + /* sendfile() failed, use standard way instead */ + img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj ); + if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) { + Error("Unable to send raw frame %u: %s",curr_frame_id,strerror(errno)); + return( false ); + } + } +#else + fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size ); + if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) { + Error("Unable to send raw frame %u: %s",curr_frame_id,strerror(errno)); + return( false ); + } +#endif + fclose(fdj); /* Close the file handle */ + } else { + fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size ); + if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) + { + Error( "Unable to send stream frame: %s", strerror(errno) ); + return( false ); + } + } + + fprintf( stdout, "\r\n\r\n" ); + fflush( stdout ); + } + last_frame_sent = TV_2_FLOAT( now ); + return( true ); } void EventStream::runStream() { - Event::Initialise(); + Event::Initialise(); - openComms(); + openComms(); - checkInitialised(); + checkInitialised(); - updateFrameRate( (double)event_data->frame_count/event_data->duration ); + updateFrameRate( (double)event_data->frame_count/event_data->duration ); - if ( mode == STREAM ) - fprintf( stdout, "Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n" ); + if ( mode == STREAM ) + fprintf( stdout, "Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n" ); - if ( !event_data ) + if ( !event_data ) + { + sendTextFrame( "No event data found" ); + exit( 0 ); + } + + unsigned int delta_us = 0; + while( !zm_terminate ) + { + gettimeofday( &now, NULL ); + + while(checkCommandQueue()); + + if ( step != 0 ) + curr_frame_id += step; + + checkEventLoaded(); + + // Get current frame data + FrameData *frame_data = &event_data->frames[curr_frame_id-1]; + + //Info( "cst:%.2f", curr_stream_time ); + //Info( "cfid:%d", curr_frame_id ); + //Info( "fdt:%d", frame_data->timestamp ); + if ( !paused ) { - sendTextFrame( "No event data found" ); - exit( 0 ); + bool in_event = true; + double time_to_event = 0; + if ( replay_rate > 0 ) + { + time_to_event = event_data->frames[0].timestamp - curr_stream_time; + if ( time_to_event > 0 ) + in_event = false; + } + else if ( replay_rate < 0 ) + { + time_to_event = curr_stream_time - event_data->frames[event_data->frame_count-1].timestamp; + if ( time_to_event > 0 ) + in_event = false; + } + if ( !in_event ) + { + double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; + if ( actual_delta_time > 1 ) + { + static char frame_text[64]; + snprintf( frame_text, sizeof(frame_text), "Time to next event = %d seconds", (int)time_to_event ); + if ( !sendTextFrame( frame_text ) ) + zm_terminate = true; + } + //else + //{ + usleep( STREAM_PAUSE_WAIT ); + //curr_stream_time += (replay_rate>0?1:-1) * ((1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000)); + curr_stream_time += (1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000); + //} + continue; + } } - unsigned int delta_us = 0; - while( !zm_terminate ) + // Figure out if we should send this frame + bool send_frame = false; + if ( !paused ) { - gettimeofday( &now, NULL ); + // If we are streaming and this frame is due to be sent + if ( ((curr_frame_id-1)%frame_mod) == 0 ) + { + delta_us = (unsigned int)(frame_data->delta * 1000000); + // if effective > base we should speed up frame delivery + delta_us = (unsigned int)((delta_us * base_fps)/effective_fps); + // but must not exceed maxfps + delta_us = max(delta_us, 1000000 / maxfps); + send_frame = true; + } + } + else if ( step != 0 ) + { + // We are paused and are just stepping forward or backward one frame + step = 0; + send_frame = true; + } + else + { + // We are paused, and doing nothing + double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; + if ( actual_delta_time > MAX_STREAM_DELAY ) + { + // Send keepalive + Debug( 2, "Sending keepalive frame" ); + send_frame = true; + } + } - while(checkCommandQueue()); + if ( send_frame ) + if ( !sendFrame( delta_us ) ) + zm_terminate = true; - if ( step != 0 ) - curr_frame_id += step; + curr_stream_time = frame_data->timestamp; - checkEventLoaded(); - - // Get current frame data - FrameData *frame_data = &event_data->frames[curr_frame_id-1]; - - //Info( "cst:%.2f", curr_stream_time ); - //Info( "cfid:%d", curr_frame_id ); - //Info( "fdt:%d", frame_data->timestamp ); - if ( !paused ) - { - bool in_event = true; - double time_to_event = 0; - if ( replay_rate > 0 ) - { - time_to_event = event_data->frames[0].timestamp - curr_stream_time; - if ( time_to_event > 0 ) - in_event = false; - } - else if ( replay_rate < 0 ) - { - time_to_event = curr_stream_time - event_data->frames[event_data->frame_count-1].timestamp; - if ( time_to_event > 0 ) - in_event = false; - } - if ( !in_event ) - { - double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; - if ( actual_delta_time > 1 ) - { - static char frame_text[64]; - snprintf( frame_text, sizeof(frame_text), "Time to next event = %d seconds", (int)time_to_event ); - if ( !sendTextFrame( frame_text ) ) - zm_terminate = true; - } - //else - //{ - usleep( STREAM_PAUSE_WAIT ); - //curr_stream_time += (replay_rate>0?1:-1) * ((1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000)); - curr_stream_time += (1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000); - //} - continue; - } - } - - // Figure out if we should send this frame - bool send_frame = false; - if ( !paused ) - { - // If we are streaming and this frame is due to be sent - if ( ((curr_frame_id-1)%frame_mod) == 0 ) - { - delta_us = (unsigned int)(frame_data->delta * 1000000); - // if effective > base we should speed up frame delivery - delta_us = (unsigned int)((delta_us * base_fps)/effective_fps); - // but must not exceed maxfps - delta_us = max(delta_us, 1000000 / maxfps); - send_frame = true; - } - } - else if ( step != 0 ) - { - // We are paused and are just stepping forward or backward one frame - step = 0; - send_frame = true; - } - else - { - // We are paused, and doing nothing - double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; - if ( actual_delta_time > MAX_STREAM_DELAY ) - { - // Send keepalive - Debug( 2, "Sending keepalive frame" ); - send_frame = true; - } - } - - if ( send_frame ) - if ( !sendFrame( delta_us ) ) - zm_terminate = true; - - curr_stream_time = frame_data->timestamp; - - if ( !paused ) - { - curr_frame_id += replay_rate>0?1:-1; - if ( send_frame && type != MPEG ) - { - Debug( 3, "dUs: %d", delta_us ); - usleep( delta_us ); - } - } - else - { - usleep( (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2))) ); - } - } // end while ! terminate + if ( !paused ) + { + curr_frame_id += replay_rate>0?1:-1; + if ( send_frame && type != MPEG ) + { + Debug( 3, "dUs: %d", delta_us ); + usleep( delta_us ); + } + } + else + { + usleep( (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2))) ); + } + } // end while ! terminate #if HAVE_LIBAVCODEC - if ( type == MPEG ) - delete vid_stream; + if ( type == MPEG ) + delete vid_stream; #endif // HAVE_LIBAVCODEC - closeComms(); + closeComms(); } diff --git a/src/zm_event.h b/src/zm_event.h index 43e68c329..b1bd54535 100644 --- a/src/zm_event.h +++ b/src/zm_event.h @@ -41,193 +41,193 @@ class Zone; class Monitor; -#define MAX_PRE_ALARM_FRAMES 16 // Maximum number of prealarm frames that can be stored +#define MAX_PRE_ALARM_FRAMES 16 // Maximum number of prealarm frames that can be stored // // Class describing events, i.e. captured periods of activity. // class Event { -friend class EventStream; + friend class EventStream; -protected: - static bool initialised; - static char capture_file_format[PATH_MAX]; - static char analyse_file_format[PATH_MAX]; - static char general_file_format[PATH_MAX]; + protected: + static bool initialised; + static char capture_file_format[PATH_MAX]; + static char analyse_file_format[PATH_MAX]; + static char general_file_format[PATH_MAX]; -protected: - static int sd; + protected: + static int sd; -public: - typedef std::set StringSet; - typedef std::map StringSetMap; + public: + typedef std::set StringSet; + typedef std::map StringSetMap; -protected: - typedef enum { NORMAL, BULK, ALARM } FrameType; + protected: + typedef enum { NORMAL, BULK, ALARM } FrameType; - struct PreAlarmData - { - Image *image; - struct timeval timestamp; - unsigned int score; - Image *alarm_frame; - }; + struct PreAlarmData + { + Image *image; + struct timeval timestamp; + unsigned int score; + Image *alarm_frame; + }; - static int pre_alarm_count; - static PreAlarmData pre_alarm_data[MAX_PRE_ALARM_FRAMES]; + static int pre_alarm_count; + static PreAlarmData pre_alarm_data[MAX_PRE_ALARM_FRAMES]; -protected: - unsigned int id; - Monitor *monitor; - struct timeval start_time; - struct timeval end_time; - std::string cause; - StringSetMap noteSetMap; - int frames; - int alarm_frames; - unsigned int tot_score; - unsigned int max_score; - char path[PATH_MAX]; + protected: + unsigned int id; + Monitor *monitor; + struct timeval start_time; + struct timeval end_time; + std::string cause; + StringSetMap noteSetMap; + int frames; + int alarm_frames; + unsigned int tot_score; + unsigned int max_score; + char path[PATH_MAX]; -protected: - int last_db_frame; + protected: + int last_db_frame; -protected: - static void Initialise() - { - if ( initialised ) - return; + protected: + static void Initialise() + { + if ( initialised ) + return; - snprintf( capture_file_format, sizeof(capture_file_format), "%%s/%%0%dd-capture.jpg", config.event_image_digits ); - snprintf( analyse_file_format, sizeof(analyse_file_format), "%%s/%%0%dd-analyse.jpg", config.event_image_digits ); - snprintf( general_file_format, sizeof(general_file_format), "%%s/%%0%dd-%%s", config.event_image_digits ); + snprintf( capture_file_format, sizeof(capture_file_format), "%%s/%%0%dd-capture.jpg", config.event_image_digits ); + snprintf( analyse_file_format, sizeof(analyse_file_format), "%%s/%%0%dd-analyse.jpg", config.event_image_digits ); + snprintf( general_file_format, sizeof(general_file_format), "%%s/%%0%dd-%%s", config.event_image_digits ); - initialised = true; - } + initialised = true; + } - void createNotes( std::string ¬es ); + void createNotes( std::string ¬es ); -public: - static bool OpenFrameSocket( int ); - static bool ValidateFrameSocket( int ); + public: + static bool OpenFrameSocket( int ); + static bool ValidateFrameSocket( int ); -public: - Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap ); - ~Event(); + public: + Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap ); + ~Event(); - int Id() const { return( id ); } - const std::string &Cause() { return( cause ); } - int Frames() const { return( frames ); } - int AlarmFrames() const { return( alarm_frames ); } + int Id() const { return( id ); } + const std::string &Cause() { return( cause ); } + int Frames() const { return( frames ); } + int AlarmFrames() const { return( alarm_frames ); } - const struct timeval &StartTime() const { return( start_time ); } - const struct timeval &EndTime() const { return( end_time ); } - struct timeval &EndTime() { return( end_time ); } + const struct timeval &StartTime() const { return( start_time ); } + const struct timeval &EndTime() const { return( end_time ); } + struct timeval &EndTime() { return( end_time ); } - bool SendFrameImage( const Image *image, bool alarm_frame=false ); - bool WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame=false ); + bool SendFrameImage( const Image *image, bool alarm_frame=false ); + bool WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame=false ); - void updateNotes( const StringSetMap &stringSetMap ); + void updateNotes( const StringSetMap &stringSetMap ); - void AddFrames( int n_frames, Image **images, struct timeval **timestamps ); - void AddFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL ); + void AddFrames( int n_frames, Image **images, struct timeval **timestamps ); + void AddFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL ); -private: - void AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps ); + private: + void AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps ); -public: - static const char *getSubPath( struct tm *time ) + public: + static const char *getSubPath( struct tm *time ) + { + static char subpath[PATH_MAX] = ""; + snprintf( subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec ); + return( subpath ); + } + static const char *getSubPath( time_t *time ) + { + return( Event::getSubPath( localtime( time ) ) ); + } + + public: + static int PreAlarmCount() + { + return( pre_alarm_count ); + } + static void EmptyPreAlarmFrames() + { + if ( pre_alarm_count > 0 ) { - static char subpath[PATH_MAX] = ""; - snprintf( subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec ); - return( subpath ); + for ( int i = 0; i < MAX_PRE_ALARM_FRAMES; i++ ) + { + delete pre_alarm_data[i].image; + delete pre_alarm_data[i].alarm_frame; + } + memset( pre_alarm_data, 0, sizeof(pre_alarm_data) ); } - static const char *getSubPath( time_t *time ) + pre_alarm_count = 0; + } + static void AddPreAlarmFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL ) + { + pre_alarm_data[pre_alarm_count].image = new Image( *image ); + pre_alarm_data[pre_alarm_count].timestamp = timestamp; + pre_alarm_data[pre_alarm_count].score = score; + if ( alarm_frame ) { - return( Event::getSubPath( localtime( time ) ) ); + pre_alarm_data[pre_alarm_count].alarm_frame = new Image( *alarm_frame ); } - -public: - static int PreAlarmCount() - { - return( pre_alarm_count ); - } - static void EmptyPreAlarmFrames() - { - if ( pre_alarm_count > 0 ) - { - for ( int i = 0; i < MAX_PRE_ALARM_FRAMES; i++ ) - { - delete pre_alarm_data[i].image; - delete pre_alarm_data[i].alarm_frame; - } - memset( pre_alarm_data, 0, sizeof(pre_alarm_data) ); - } - pre_alarm_count = 0; - } - static void AddPreAlarmFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL ) - { - pre_alarm_data[pre_alarm_count].image = new Image( *image ); - pre_alarm_data[pre_alarm_count].timestamp = timestamp; - pre_alarm_data[pre_alarm_count].score = score; - if ( alarm_frame ) - { - pre_alarm_data[pre_alarm_count].alarm_frame = new Image( *alarm_frame ); - } - pre_alarm_count++; - } - void SavePreAlarmFrames() - { - for ( int i = 0; i < pre_alarm_count; i++ ) - { - AddFrame( pre_alarm_data[i].image, pre_alarm_data[i].timestamp, pre_alarm_data[i].score, pre_alarm_data[i].alarm_frame ); - } - EmptyPreAlarmFrames(); - } + pre_alarm_count++; + } + void SavePreAlarmFrames() + { + for ( int i = 0; i < pre_alarm_count; i++ ) + { + AddFrame( pre_alarm_data[i].image, pre_alarm_data[i].timestamp, pre_alarm_data[i].score, pre_alarm_data[i].alarm_frame ); + } + EmptyPreAlarmFrames(); + } }; class EventStream : public StreamBase { -public: + public: -protected: + protected: struct FrameData { - //unsigned long id; - time_t timestamp; - time_t offset; - double delta; - bool in_db; + //unsigned long id; + time_t timestamp; + time_t offset; + double delta; + bool in_db; }; struct EventData { - unsigned long event_id; - unsigned long monitor_id; - unsigned long frame_count; - time_t start_time; - double duration; - char path[PATH_MAX]; - int n_frames; - FrameData *frames; + unsigned long event_id; + unsigned long monitor_id; + unsigned long frame_count; + time_t start_time; + double duration; + char path[PATH_MAX]; + int n_frames; + FrameData *frames; }; -protected: + protected: static const int STREAM_PAUSE_WAIT = 250000; // Microseconds static const StreamMode DEFAULT_MODE = SINGLE; -protected: + protected: StreamMode mode; bool forceEventChange; -protected: + protected: int curr_frame_id; double curr_stream_time; EventData *event_data; -protected: + protected: bool loadEventData( int event_id ); bool loadInitialEventData( int init_event_id, int init_frame_id ); bool loadInitialEventData( int monitor_id, time_t event_time ); @@ -236,31 +236,31 @@ protected: void processCommand( const CmdMsg *msg ); bool sendFrame( int delta_us ); -public: + public: EventStream() { - mode = DEFAULT_MODE; + mode = DEFAULT_MODE; - forceEventChange = false; + forceEventChange = false; - curr_frame_id = 0; - curr_stream_time = 0.0; + curr_frame_id = 0; + curr_stream_time = 0.0; - event_data = 0; + event_data = 0; } - void setStreamStart( int init_event_id, int init_frame_id=0 ) + void setStreamStart( int init_event_id, int init_frame_id=0 ) { - loadInitialEventData( init_event_id, init_frame_id ); - loadMonitor( event_data->monitor_id ); + loadInitialEventData( init_event_id, init_frame_id ); + loadMonitor( event_data->monitor_id ); } - void setStreamStart( int monitor_id, time_t event_time ) + void setStreamStart( int monitor_id, time_t event_time ) { - loadInitialEventData( monitor_id, event_time ); - loadMonitor( monitor_id ); + loadInitialEventData( monitor_id, event_time ); + loadMonitor( monitor_id ); } void setStreamMode( StreamMode p_mode ) { - mode = p_mode; + mode = p_mode; } void runStream(); }; diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 97407fbaa..1a6a30f34 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -62,13 +62,13 @@ //============================================================================= std::vector split(const std::string &s, char delim) { - std::vector elems; - std::stringstream ss(s); - std::string item; - while(std::getline(ss, item, delim)) { - elems.push_back(trimSpaces(item)); - } - return elems; + std::vector elems; + std::stringstream ss(s); + std::string item; + while(std::getline(ss, item, delim)) { + elems.push_back(trimSpaces(item)); + } + return elems; } //============================================================================= @@ -76,188 +76,188 @@ std::vector split(const std::string &s, char delim) { Monitor::MonitorLink::MonitorLink( int p_id, const char *p_name ) : id( p_id ) { - strncpy( name, p_name, sizeof(name) ); + strncpy( name, p_name, sizeof(name) ); #if ZM_MEM_MAPPED - map_fd = -1; - snprintf( mem_file, sizeof(mem_file), "%s/zm.mmap.%d", config.path_map, id ); + map_fd = -1; + snprintf( mem_file, sizeof(mem_file), "%s/zm.mmap.%d", config.path_map, id ); #else // ZM_MEM_MAPPED - shm_id = 0; + shm_id = 0; #endif // ZM_MEM_MAPPED - mem_size = 0; - mem_ptr = 0; + mem_size = 0; + mem_ptr = 0; - last_event = 0; - last_state = IDLE; + last_event = 0; + last_state = IDLE; - last_connect_time = 0; - connected = false; + last_connect_time = 0; + connected = false; } Monitor::MonitorLink::~MonitorLink() { - disconnect(); + disconnect(); } bool Monitor::MonitorLink::connect() { - if ( !last_connect_time || (time( 0 ) - last_connect_time) > 60 ) - { - last_connect_time = time( 0 ); + if ( !last_connect_time || (time( 0 ) - last_connect_time) > 60 ) + { + last_connect_time = time( 0 ); - mem_size = sizeof(SharedData) + sizeof(TriggerData); + mem_size = sizeof(SharedData) + sizeof(TriggerData); - Debug( 1, "link.mem.size=%d", mem_size ); + Debug( 1, "link.mem.size=%d", mem_size ); #if ZM_MEM_MAPPED - map_fd = open( mem_file, O_RDWR, (mode_t)0600 ); - if ( map_fd < 0 ) - { - Debug( 3, "Can't open linked memory map file %s: %s", mem_file, strerror(errno) ); - disconnect(); - return( false ); - } + map_fd = open( mem_file, O_RDWR, (mode_t)0600 ); + if ( map_fd < 0 ) + { + Debug( 3, "Can't open linked memory map file %s: %s", mem_file, strerror(errno) ); + disconnect(); + return( false ); + } - struct stat map_stat; - if ( fstat( map_fd, &map_stat ) < 0 ) - { - Error( "Can't stat linked memory map file %s: %s", mem_file, strerror(errno) ); - disconnect(); - return( false ); - } + struct stat map_stat; + if ( fstat( map_fd, &map_stat ) < 0 ) + { + Error( "Can't stat linked memory map file %s: %s", mem_file, strerror(errno) ); + disconnect(); + return( false ); + } - if ( map_stat.st_size == 0 ) - { - Error( "Linked memory map file %s is empty: %s", mem_file, strerror(errno) ); - disconnect(); - return( false ); - } - else if ( map_stat.st_size < mem_size ) - { - Error( "Got unexpected memory map file size %ld, expected %d", map_stat.st_size, mem_size ); - disconnect(); - return( false ); - } + if ( map_stat.st_size == 0 ) + { + Error( "Linked memory map file %s is empty: %s", mem_file, strerror(errno) ); + disconnect(); + return( false ); + } + else if ( map_stat.st_size < mem_size ) + { + Error( "Got unexpected memory map file size %ld, expected %d", map_stat.st_size, mem_size ); + disconnect(); + return( false ); + } - mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0 ); - if ( mem_ptr == MAP_FAILED ) - { - Error( "Can't map file %s (%d bytes) to memory: %s", mem_file, mem_size, strerror(errno) ); - disconnect(); - return( false ); - } + mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0 ); + if ( mem_ptr == MAP_FAILED ) + { + Error( "Can't map file %s (%d bytes) to memory: %s", mem_file, mem_size, strerror(errno) ); + disconnect(); + return( false ); + } #else // ZM_MEM_MAPPED - shm_id = shmget( (config.shm_key&0xffff0000)|id, mem_size, 0700 ); - if ( shm_id < 0 ) - { - Debug( 3, "Can't shmget link memory: %s", strerror(errno) ); - connected = false; - return( false ); - } - mem_ptr = (unsigned char *)shmat( shm_id, 0, 0 ); - if ( mem_ptr < 0 ) - { - Debug( 3, "Can't shmat link memory: %s", strerror(errno) ); - connected = false; - return( false ); - } + shm_id = shmget( (config.shm_key&0xffff0000)|id, mem_size, 0700 ); + if ( shm_id < 0 ) + { + Debug( 3, "Can't shmget link memory: %s", strerror(errno) ); + connected = false; + return( false ); + } + mem_ptr = (unsigned char *)shmat( shm_id, 0, 0 ); + if ( mem_ptr < 0 ) + { + Debug( 3, "Can't shmat link memory: %s", strerror(errno) ); + connected = false; + return( false ); + } #endif // ZM_MEM_MAPPED - shared_data = (SharedData *)mem_ptr; - trigger_data = (TriggerData *)((char *)shared_data + sizeof(SharedData)); + shared_data = (SharedData *)mem_ptr; + trigger_data = (TriggerData *)((char *)shared_data + sizeof(SharedData)); - if ( !shared_data->valid ) - { - Debug( 3, "Linked memory not initialised by capture daemon" ); - disconnect(); - return( false ); - } - - last_state = shared_data->state; - last_event = shared_data->last_event; - connected = true; - - return( true ); + if ( !shared_data->valid ) + { + Debug( 3, "Linked memory not initialised by capture daemon" ); + disconnect(); + return( false ); } - return( false ); + + last_state = shared_data->state; + last_event = shared_data->last_event; + connected = true; + + return( true ); + } + return( false ); } bool Monitor::MonitorLink::disconnect() { - if ( connected ) - { - connected = false; + if ( connected ) + { + connected = false; #if ZM_MEM_MAPPED - if ( mem_ptr > 0 ) - { - msync( mem_ptr, mem_size, MS_ASYNC ); - munmap( mem_ptr, mem_size ); - } - if ( map_fd >= 0 ) - close( map_fd ); + if ( mem_ptr > 0 ) + { + msync( mem_ptr, mem_size, MS_ASYNC ); + munmap( mem_ptr, mem_size ); + } + if ( map_fd >= 0 ) + close( map_fd ); - map_fd = -1; + map_fd = -1; #else // ZM_MEM_MAPPED - struct shmid_ds shm_data; - if ( shmctl( shm_id, IPC_STAT, &shm_data ) < 0 ) - { - Debug( 3, "Can't shmctl: %s", strerror(errno) ); - return( false ); - } + struct shmid_ds shm_data; + if ( shmctl( shm_id, IPC_STAT, &shm_data ) < 0 ) + { + Debug( 3, "Can't shmctl: %s", strerror(errno) ); + return( false ); + } - shm_id = 0; + shm_id = 0; - if ( shm_data.shm_nattch <= 1 ) - { - if ( shmctl( shm_id, IPC_RMID, 0 ) < 0 ) - { - Debug( 3, "Can't shmctl: %s", strerror(errno) ); - return( false ); - } - } + if ( shm_data.shm_nattch <= 1 ) + { + if ( shmctl( shm_id, IPC_RMID, 0 ) < 0 ) + { + Debug( 3, "Can't shmctl: %s", strerror(errno) ); + return( false ); + } + } - if ( shmdt( mem_ptr ) < 0 ) - { - Debug( 3, "Can't shmdt: %s", strerror(errno) ); - return( false ); - } + if ( shmdt( mem_ptr ) < 0 ) + { + Debug( 3, "Can't shmdt: %s", strerror(errno) ); + return( false ); + } #endif // ZM_MEM_MAPPED - mem_size = 0; - mem_ptr = 0; - } - return( true ); + mem_size = 0; + mem_ptr = 0; + } + return( true ); } bool Monitor::MonitorLink::isAlarmed() { - if ( !connected ) - { - return( false ); - } - return( shared_data->state == ALARM ); + if ( !connected ) + { + return( false ); + } + return( shared_data->state == ALARM ); } bool Monitor::MonitorLink::inAlarm() { - if ( !connected ) - { - return( false ); - } - return( shared_data->state == ALARM || shared_data->state == ALERT ); + if ( !connected ) + { + return( false ); + } + return( shared_data->state == ALARM || shared_data->state == ALERT ); } bool Monitor::MonitorLink::hasAlarmed() { - if ( shared_data->state == ALARM ) - { - return( true ); - } - else if( shared_data->last_event != (unsigned int)last_event ) - { - last_event = shared_data->last_event; - } - return( false ); + if ( shared_data->state == ALARM ) + { + return( true ); + } + else if( shared_data->last_event != (unsigned int)last_event ) + { + last_event = shared_data->last_event; + } + return( false ); } Monitor::Monitor( @@ -291,7 +291,7 @@ Monitor::Monitor( Purpose p_purpose, int p_n_zones, Zone *p_zones[] -) : id( p_id ), + ) : id( p_id ), function( (Function)p_function ), enabled( p_enabled ), width( (p_orientation==ROTATE_90||p_orientation==ROTATE_270)?p_camera->Height():p_camera->Width() ), @@ -325,2107 +325,2311 @@ Monitor::Monitor( timestamps( 0 ), images( 0 ) { - strncpy( name, p_name, sizeof(name) ); + strncpy( name, p_name, sizeof(name) ); - strncpy( event_prefix, p_event_prefix, sizeof(event_prefix) ); - strncpy( label_format, p_label_format, sizeof(label_format) ); + strncpy( event_prefix, p_event_prefix, sizeof(event_prefix) ); + strncpy( label_format, p_label_format, sizeof(label_format) ); - // Change \n to actual line feeds - char *token_ptr = label_format; - const char *token_string = "\n"; - while( ( token_ptr = strstr( token_ptr, token_string ) ) ) + // Change \n to actual line feeds + char *token_ptr = label_format; + const char *token_string = "\n"; + while( ( token_ptr = strstr( token_ptr, token_string ) ) ) + { + if ( *(token_ptr+1) ) { - if ( *(token_ptr+1) ) - { - *token_ptr = '\n'; - token_ptr++; - strcpy( token_ptr, token_ptr+1 ); - } - else - { - *token_ptr = '\0'; - break; - } + *token_ptr = '\n'; + token_ptr++; + strcpy( token_ptr, token_ptr+1 ); } - - fps = 0.0; - event_count = 0; - image_count = 0; - ready_count = warmup_count; - first_alarm_count = 0; - last_alarm_count = 0; - state = IDLE; - - if ( alarm_frame_count < 1 ) - alarm_frame_count = 1; - else if ( alarm_frame_count > MAX_PRE_ALARM_FRAMES ) - alarm_frame_count = MAX_PRE_ALARM_FRAMES; - - auto_resume_time = 0; - - if ( strcmp( config.event_close_mode, "time" ) == 0 ) - event_close_mode = CLOSE_TIME; - else if ( strcmp( config.event_close_mode, "alarm" ) == 0 ) - event_close_mode = CLOSE_ALARM; else - event_close_mode = CLOSE_IDLE; - - Debug( 1, "monitor purpose=%d", purpose ); - - mem_size = sizeof(SharedData) - + sizeof(TriggerData) - + (image_buffer_count*sizeof(struct timeval)) - + (image_buffer_count*camera->ImageSize()) - + 64; /* Padding used to permit aligning the images buffer to 16 byte boundary */ - - Debug( 1, "mem.size=%d", mem_size ); - mem_ptr = NULL; - - if ( purpose == CAPTURE ) { - this->connect(); - if ( ! mem_ptr ) exit(-1); - memset( mem_ptr, 0, mem_size ); - shared_data->size = sizeof(SharedData); - shared_data->active = enabled; - shared_data->signal = false; - shared_data->state = IDLE; - shared_data->last_write_index = image_buffer_count; - shared_data->last_read_index = image_buffer_count; - shared_data->last_write_time = 0; - shared_data->last_event = 0; - shared_data->action = (Action)0; - shared_data->brightness = -1; - shared_data->hue = -1; - shared_data->colour = -1; - shared_data->contrast = -1; - shared_data->alarm_x = -1; - shared_data->alarm_y = -1; - shared_data->format = camera->SubpixelOrder(); - shared_data->imagesize = camera->ImageSize(); - trigger_data->size = sizeof(TriggerData); - trigger_data->trigger_state = TRIGGER_CANCEL; - trigger_data->trigger_score = 0; - trigger_data->trigger_cause[0] = 0; - trigger_data->trigger_text[0] = 0; - trigger_data->trigger_showtext[0] = 0; - shared_data->valid = true; - } else if ( purpose == ANALYSIS ) { - this->connect(); - if ( ! mem_ptr ) exit(-1); - shared_data->state = IDLE; - shared_data->last_read_time = 0; - shared_data->alarm_x = -1; - shared_data->alarm_y = -1; - } - - if ( ( ! mem_ptr ) || ! shared_data->valid ) { - if ( purpose != QUERY ) - { - Error( "Shared data not initialised by capture daemon for monitor %s", name ); - exit( -1 ); - } - else - { - Warning( "Shared data not initialised by capture daemon, some query functions may not be available or produce invalid results for monitor %s", name ); - } + *token_ptr = '\0'; + break; } + } - // Will this not happen every time a monitor is instantiated? Seems like all the calls to the Monitor constructor pass a zero for n_zones, then load zones after.. - if ( !n_zones ) { - Debug( 1, "Monitor %s has no zones, adding one.", name ); - n_zones = 1; - zones = new Zone *[1]; - Coord coords[4] = { Coord( 0, 0 ), Coord( width-1, 0 ), Coord( width-1, height-1 ), Coord( 0, height-1 ) }; - zones[0] = new Zone( this, 0, "All", Zone::ACTIVE, Polygon( sizeof(coords)/sizeof(*coords), coords ), RGB_RED, Zone::BLOBS ); - } - start_time = last_fps_time = time( 0 ); + fps = 0.0; + event_count = 0; + image_count = 0; + ready_count = warmup_count; + first_alarm_count = 0; + last_alarm_count = 0; + state = IDLE; - event = 0; + if ( alarm_frame_count < 1 ) + alarm_frame_count = 1; + else if ( alarm_frame_count > MAX_PRE_ALARM_FRAMES ) + alarm_frame_count = MAX_PRE_ALARM_FRAMES; - Debug( 1, "Monitor %s has function %d", name, function ); - Debug( 1, "Monitor %s LBF = '%s', LBX = %d, LBY = %d", name, label_format, label_coord.X(), label_coord.Y() ); - Debug( 1, "Monitor %s IBC = %d, WUC = %d, pEC = %d, PEC = %d, EAF = %d, FRI = %d, RBP = %d, ARBP = %d, FM = %d", name, image_buffer_count, warmup_count, pre_event_count, post_event_count, alarm_frame_count, fps_report_interval, ref_blend_perc, alarm_ref_blend_perc, track_motion ); + auto_resume_time = 0; - if ( purpose == ANALYSIS ) + if ( strcmp( config.event_close_mode, "time" ) == 0 ) + event_close_mode = CLOSE_TIME; + else if ( strcmp( config.event_close_mode, "alarm" ) == 0 ) + event_close_mode = CLOSE_ALARM; + else + event_close_mode = CLOSE_IDLE; + + Debug( 1, "monitor purpose=%d", purpose ); + + mem_size = sizeof(SharedData) + + sizeof(TriggerData) + + (image_buffer_count*sizeof(struct timeval)) + + (image_buffer_count*camera->ImageSize()) + + 64; /* Padding used to permit aligning the images buffer to 16 byte boundary */ + + Debug( 1, "mem.size=%d", mem_size ); + mem_ptr = NULL; + + if ( purpose == CAPTURE ) { + this->connect(); + if ( ! mem_ptr ) exit(-1); + memset( mem_ptr, 0, mem_size ); + shared_data->size = sizeof(SharedData); + shared_data->active = enabled; + shared_data->signal = false; + shared_data->state = IDLE; + shared_data->last_write_index = image_buffer_count; + shared_data->last_read_index = image_buffer_count; + shared_data->last_write_time = 0; + shared_data->last_event = 0; + shared_data->action = (Action)0; + shared_data->brightness = -1; + shared_data->hue = -1; + shared_data->colour = -1; + shared_data->contrast = -1; + shared_data->alarm_x = -1; + shared_data->alarm_y = -1; + shared_data->format = camera->SubpixelOrder(); + shared_data->imagesize = camera->ImageSize(); + trigger_data->size = sizeof(TriggerData); + trigger_data->trigger_state = TRIGGER_CANCEL; + trigger_data->trigger_score = 0; + trigger_data->trigger_cause[0] = 0; + trigger_data->trigger_text[0] = 0; + trigger_data->trigger_showtext[0] = 0; + shared_data->valid = true; + } else if ( purpose == ANALYSIS ) { + this->connect(); + if ( ! mem_ptr ) exit(-1); + shared_data->state = IDLE; + shared_data->last_read_time = 0; + shared_data->alarm_x = -1; + shared_data->alarm_y = -1; + } + + if ( ( ! mem_ptr ) || ! shared_data->valid ) + { + if ( purpose != QUERY ) { - static char path[PATH_MAX]; - - strncpy( path, config.dir_events, sizeof(path) ); - - struct stat statbuf; - errno = 0; - stat( path, &statbuf ); - if ( errno == ENOENT || errno == ENOTDIR ) - { - if ( mkdir( path, 0755 ) ) - { - Error( "Can't make %s: %s", path, strerror(errno)); - } - } - - snprintf( path, sizeof(path), "%s/%d", config.dir_events, id ); - - errno = 0; - stat( path, &statbuf ); - if ( errno == ENOENT || errno == ENOTDIR ) - { - if ( mkdir( path, 0755 ) ) - { - Error( "Can't make %s: %s", path, strerror(errno)); - } - char temp_path[PATH_MAX]; - snprintf( temp_path, sizeof(temp_path), "%d", id ); - if ( chdir( config.dir_events ) < 0 ) - Fatal( "Can't change directory to '%s': %s", config.dir_events, strerror(errno) ); - if ( symlink( temp_path, name ) < 0 ) - Fatal( "Can't symlink '%s' to '%s': %s", temp_path, name, strerror(errno) ); - if ( chdir( ".." ) < 0 ) - Fatal( "Can't change to parent directory: %s", strerror(errno) ); - } - - while( shared_data->last_write_index == (unsigned int)image_buffer_count - && shared_data->last_write_time == 0) - { - Warning( "Waiting for capture daemon" ); - sleep( 1 ); - } - ref_image.Assign( width, height, camera->Colours(), camera->SubpixelOrder(), image_buffer[shared_data->last_write_index].image->Buffer(), camera->ImageSize()); - - n_linked_monitors = 0; - linked_monitors = 0; - ReloadLinkedMonitors( p_linked_monitors ); + Error( "Shared data not initialised by capture daemon for monitor %s", name ); + exit( -1 ); } + else + { + Warning( "Shared data not initialised by capture daemon, some query functions may not be available or produce invalid results for monitor %s", name ); + } + } + + // Will this not happen every time a monitor is instantiated? Seems like all the calls to the Monitor constructor pass a zero for n_zones, then load zones after.. + if ( !n_zones ) { + Debug( 1, "Monitor %s has no zones, adding one.", name ); + n_zones = 1; + zones = new Zone *[1]; + Coord coords[4] = { Coord( 0, 0 ), Coord( width-1, 0 ), Coord( width-1, height-1 ), Coord( 0, height-1 ) }; + zones[0] = new Zone( this, 0, "All", Zone::ACTIVE, Polygon( sizeof(coords)/sizeof(*coords), coords ), RGB_RED, Zone::BLOBS ); + } + start_time = last_fps_time = time( 0 ); + + event = 0; + + Debug( 1, "Monitor %s has function %d", name, function ); + Debug( 1, "Monitor %s LBF = '%s', LBX = %d, LBY = %d", name, label_format, label_coord.X(), label_coord.Y() ); + Debug( 1, "Monitor %s IBC = %d, WUC = %d, pEC = %d, PEC = %d, EAF = %d, FRI = %d, RBP = %d, ARBP = %d, FM = %d", name, image_buffer_count, warmup_count, pre_event_count, post_event_count, alarm_frame_count, fps_report_interval, ref_blend_perc, alarm_ref_blend_perc, track_motion ); + + if ( purpose == ANALYSIS ) + { + static char path[PATH_MAX]; + + strncpy( path, config.dir_events, sizeof(path) ); + + struct stat statbuf; + errno = 0; + stat( path, &statbuf ); + if ( errno == ENOENT || errno == ENOTDIR ) + { + if ( mkdir( path, 0755 ) ) + { + Error( "Can't make %s: %s", path, strerror(errno)); + } + } + + snprintf( path, sizeof(path), "%s/%d", config.dir_events, id ); + + errno = 0; + stat( path, &statbuf ); + if ( errno == ENOENT || errno == ENOTDIR ) + { + if ( mkdir( path, 0755 ) ) + { + Error( "Can't make %s: %s", path, strerror(errno)); + } + char temp_path[PATH_MAX]; + snprintf( temp_path, sizeof(temp_path), "%d", id ); + if ( chdir( config.dir_events ) < 0 ) + Fatal( "Can't change directory to '%s': %s", config.dir_events, strerror(errno) ); + if ( symlink( temp_path, name ) < 0 ) + Fatal( "Can't symlink '%s' to '%s': %s", temp_path, name, strerror(errno) ); + if ( chdir( ".." ) < 0 ) + Fatal( "Can't change to parent directory: %s", strerror(errno) ); + } + + while( shared_data->last_write_index == (unsigned int)image_buffer_count + && shared_data->last_write_time == 0) + { + Warning( "Waiting for capture daemon" ); + sleep( 1 ); + } + ref_image.Assign( width, height, camera->Colours(), camera->SubpixelOrder(), image_buffer[shared_data->last_write_index].image->Buffer(), camera->ImageSize()); + + n_linked_monitors = 0; + linked_monitors = 0; + ReloadLinkedMonitors( p_linked_monitors ); + } } bool Monitor::connect() { #if ZM_MEM_MAPPED - snprintf( mem_file, sizeof(mem_file), "%s/zm.mmap.%d", config.path_map, id ); - map_fd = open( mem_file, O_RDWR|O_CREAT, (mode_t)0600 ); - if ( map_fd < 0 ) - Fatal( "Can't open memory map file %s, probably not enough space free: %s", mem_file, strerror(errno) ); + snprintf( mem_file, sizeof(mem_file), "%s/zm.mmap.%d", config.path_map, id ); + map_fd = open( mem_file, O_RDWR|O_CREAT, (mode_t)0600 ); + if ( map_fd < 0 ) + Fatal( "Can't open memory map file %s, probably not enough space free: %s", mem_file, strerror(errno) ); - struct stat map_stat; - if ( fstat( map_fd, &map_stat ) < 0 ) - Fatal( "Can't stat memory map file %s: %s, is the zmc process for this monitor running?", mem_file, strerror(errno) ); - if ( map_stat.st_size != mem_size && purpose == CAPTURE ) { - // Allocate the size - if ( ftruncate( map_fd, mem_size ) < 0 ) { - Fatal( "Can't extend memory map file %s to %d bytes: %s", mem_file, mem_size, strerror(errno) ); - } - } else if ( map_stat.st_size == 0 ) { - Error( "Got empty memory map file size %ld, is the zmc process for this monitor running?", map_stat.st_size, mem_size ); - return false; - } else if ( map_stat.st_size != mem_size ) { - Error( "Got unexpected memory map file size %ld, expected %d", map_stat.st_size, mem_size ); - return false; - } else { -#ifdef MAP_LOCKED - mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, map_fd, 0 ); - if ( mem_ptr == MAP_FAILED ) { - if ( errno == EAGAIN ) { - Debug( 1, "Unable to map file %s (%d bytes) to locked memory, trying unlocked", mem_file, mem_size ); -#endif - mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0 ); - Debug( 1, "Mapped file %s (%d bytes) to locked memory, unlocked", mem_file, mem_size ); -#ifdef MAP_LOCKED - } - } -#endif - if ( mem_ptr == MAP_FAILED ) - Fatal( "Can't map file %s (%d bytes) to memory: %s(%d)", mem_file, mem_size, strerror(errno), errno ); + struct stat map_stat; + if ( fstat( map_fd, &map_stat ) < 0 ) + Fatal( "Can't stat memory map file %s: %s, is the zmc process for this monitor running?", mem_file, strerror(errno) ); + if ( map_stat.st_size != mem_size && purpose == CAPTURE ) { + // Allocate the size + if ( ftruncate( map_fd, mem_size ) < 0 ) { + Fatal( "Can't extend memory map file %s to %d bytes: %s", mem_file, mem_size, strerror(errno) ); } + } else if ( map_stat.st_size == 0 ) { + Error( "Got empty memory map file size %ld, is the zmc process for this monitor running?", map_stat.st_size, mem_size ); + return false; + } else if ( map_stat.st_size != mem_size ) { + Error( "Got unexpected memory map file size %ld, expected %d", map_stat.st_size, mem_size ); + return false; + } else { +#ifdef MAP_LOCKED + mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, map_fd, 0 ); + if ( mem_ptr == MAP_FAILED ) { + if ( errno == EAGAIN ) { + Debug( 1, "Unable to map file %s (%d bytes) to locked memory, trying unlocked", mem_file, mem_size ); +#endif + mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0 ); + Debug( 1, "Mapped file %s (%d bytes) to locked memory, unlocked", mem_file, mem_size ); +#ifdef MAP_LOCKED + } + } +#endif + if ( mem_ptr == MAP_FAILED ) + Fatal( "Can't map file %s (%d bytes) to memory: %s(%d)", mem_file, mem_size, strerror(errno), errno ); + } #else // ZM_MEM_MAPPED - shm_id = shmget( (config.shm_key&0xffff0000)|id, mem_size, IPC_CREAT|0700 ); - if ( shm_id < 0 ) { - Error( "Can't shmget, probably not enough shared memory space free: %s", strerror(errno)); - exit( -1 ); - } - mem_ptr = (unsigned char *)shmat( shm_id, 0, 0 ); - if ( mem_ptr < 0 ) - { - Error( "Can't shmat: %s", strerror(errno)); - exit( -1 ); - } + shm_id = shmget( (config.shm_key&0xffff0000)|id, mem_size, IPC_CREAT|0700 ); + if ( shm_id < 0 ) { + Error( "Can't shmget, probably not enough shared memory space free: %s", strerror(errno)); + exit( -1 ); + } + mem_ptr = (unsigned char *)shmat( shm_id, 0, 0 ); + if ( mem_ptr < 0 ) + { + Error( "Can't shmat: %s", strerror(errno)); + exit( -1 ); + } #endif // ZM_MEM_MAPPED - shared_data = (SharedData *)mem_ptr; - trigger_data = (TriggerData *)((char *)shared_data + sizeof(SharedData)); - struct timeval *shared_timestamps = (struct timeval *)((char *)trigger_data + sizeof(TriggerData)); - unsigned char *shared_images = (unsigned char *)((char *)shared_timestamps + (image_buffer_count*sizeof(struct timeval))); - - if(((unsigned long)shared_images % 16) != 0) { - /* Align images buffer to nearest 16 byte boundary */ - Debug(3,"Aligning shared memory images to the next 16 byte boundary"); - shared_images = (uint8_t*)((unsigned long)shared_images + (16 - ((unsigned long)shared_images % 16))); - } - image_buffer = new Snapshot[image_buffer_count]; - for ( int i = 0; i < image_buffer_count; i++ ) - { - image_buffer[i].timestamp = &(shared_timestamps[i]); - image_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder(), &(shared_images[i*camera->ImageSize()]) ); - image_buffer[i].image->HoldBuffer(true); /* Don't release the internal buffer or replace it with another */ - } - if ( (deinterlacing & 0xff) == 4) - { - /* Four field motion adaptive deinterlacing in use */ - /* Allocate a buffer for the next image */ - next_buffer.image = new Image( width, height, camera->Colours(), camera->SubpixelOrder()); - next_buffer.timestamp = new struct timeval; - } - return true; + shared_data = (SharedData *)mem_ptr; + trigger_data = (TriggerData *)((char *)shared_data + sizeof(SharedData)); + struct timeval *shared_timestamps = (struct timeval *)((char *)trigger_data + sizeof(TriggerData)); + unsigned char *shared_images = (unsigned char *)((char *)shared_timestamps + (image_buffer_count*sizeof(struct timeval))); + + if(((unsigned long)shared_images % 16) != 0) { + /* Align images buffer to nearest 16 byte boundary */ + Debug(3,"Aligning shared memory images to the next 16 byte boundary"); + shared_images = (uint8_t*)((unsigned long)shared_images + (16 - ((unsigned long)shared_images % 16))); + } + image_buffer = new Snapshot[image_buffer_count]; + for ( int i = 0; i < image_buffer_count; i++ ) + { + image_buffer[i].timestamp = &(shared_timestamps[i]); + image_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder(), &(shared_images[i*camera->ImageSize()]) ); + image_buffer[i].image->HoldBuffer(true); /* Don't release the internal buffer or replace it with another */ + } + if ( (deinterlacing & 0xff) == 4) + { + /* Four field motion adaptive deinterlacing in use */ + /* Allocate a buffer for the next image */ + next_buffer.image = new Image( width, height, camera->Colours(), camera->SubpixelOrder()); + next_buffer.timestamp = new struct timeval; + } + return true; } Monitor::~Monitor() { - if ( timestamps ) { - delete[] timestamps; - timestamps = 0; - } - if ( images ) { - delete[] images; - images = 0; - } - if ( mem_ptr ) { - if ( event ) - Info( "%s: %03d - Closing event %d, shutting down", name, image_count, event->Id() ); - closeEvent(); + if ( timestamps ) { + delete[] timestamps; + timestamps = 0; + } + if ( images ) { + delete[] images; + images = 0; + } + if ( mem_ptr ) { + if ( event ) + Info( "%s: %03d - Closing event %d, shutting down", name, image_count, event->Id() ); + closeEvent(); - if ( (deinterlacing & 0xff) == 4) - { - delete next_buffer.image; - delete next_buffer.timestamp; - } - for ( int i = 0; i < image_buffer_count; i++ ) - { - delete image_buffer[i].image; - } - delete[] image_buffer; - - } // end if mem_ptr - - for ( int i = 0; i < n_zones; i++ ) + if ( (deinterlacing & 0xff) == 4) { - delete zones[i]; + delete next_buffer.image; + delete next_buffer.timestamp; } - delete[] zones; + for ( int i = 0; i < image_buffer_count; i++ ) + { + delete image_buffer[i].image; + } + delete[] image_buffer; - delete camera; + } // end if mem_ptr - if ( mem_ptr ) { - if ( purpose == ANALYSIS ) - { - shared_data->state = state = IDLE; - shared_data->last_read_index = image_buffer_count; - shared_data->last_read_time = 0; - } - else if ( purpose == CAPTURE ) - { - shared_data->valid = false; - memset( mem_ptr, 0, mem_size ); - } + for ( int i = 0; i < n_zones; i++ ) + { + delete zones[i]; + } + delete[] zones; + + delete camera; + + if ( mem_ptr ) { + if ( purpose == ANALYSIS ) + { + shared_data->state = state = IDLE; + shared_data->last_read_index = image_buffer_count; + shared_data->last_read_time = 0; + } + else if ( purpose == CAPTURE ) + { + shared_data->valid = false; + memset( mem_ptr, 0, mem_size ); + } #if ZM_MEM_MAPPED - if ( msync( mem_ptr, mem_size, MS_SYNC ) < 0 ) - Error( "Can't msync: %s", strerror(errno) ); - if ( munmap( mem_ptr, mem_size ) < 0 ) - Fatal( "Can't munmap: %s", strerror(errno) ); - close( map_fd ); + if ( msync( mem_ptr, mem_size, MS_SYNC ) < 0 ) + Error( "Can't msync: %s", strerror(errno) ); + if ( munmap( mem_ptr, mem_size ) < 0 ) + Fatal( "Can't munmap: %s", strerror(errno) ); + close( map_fd ); #else // ZM_MEM_MAPPED - struct shmid_ds shm_data; - if ( shmctl( shm_id, IPC_STAT, &shm_data ) < 0 ) { - Error( "Can't shmctl: %s", strerror(errno) ); - exit( -1 ); - } - if ( shm_data.shm_nattch <= 1 ) { - if ( shmctl( shm_id, IPC_RMID, 0 ) < 0 ) { - Error( "Can't shmctl: %s", strerror(errno) ); - exit( -1 ); - } - } + struct shmid_ds shm_data; + if ( shmctl( shm_id, IPC_STAT, &shm_data ) < 0 ) { + Error( "Can't shmctl: %s", strerror(errno) ); + exit( -1 ); + } + if ( shm_data.shm_nattch <= 1 ) { + if ( shmctl( shm_id, IPC_RMID, 0 ) < 0 ) { + Error( "Can't shmctl: %s", strerror(errno) ); + exit( -1 ); + } + } #endif // ZM_MEM_MAPPED - } // end if mem_ptr + } // end if mem_ptr } void Monitor::AddZones( int p_n_zones, Zone *p_zones[] ) { - for ( int i = 0; i < n_zones; i++ ) - delete zones[i]; - delete[] zones; - n_zones = p_n_zones; - zones = p_zones; + for ( int i = 0; i < n_zones; i++ ) + delete zones[i]; + delete[] zones; + n_zones = p_n_zones; + zones = p_zones; } Monitor::State Monitor::GetState() const { - return( (State)shared_data->state ); + return( (State)shared_data->state ); } int Monitor::GetImage( int index, int scale ) { - if ( index < 0 || index > image_buffer_count ) - { - index = shared_data->last_write_index; + if ( index < 0 || index > image_buffer_count ) + { + index = shared_data->last_write_index; + } + + if ( index != image_buffer_count ) + { + Image *image; + // If we are going to be modifying the snapshot before writing, then we need to copy it + if ( ( scale != ZM_SCALE_BASE ) || ( !config.timestamp_on_capture ) ) { + Snapshot *snap = &image_buffer[index]; + Image *snap_image = snap->image; + + alarm_image.Assign( *snap_image ); + + + //write_image.Assign( *snap_image ); + + if ( scale != ZM_SCALE_BASE ) { + alarm_image.Scale( scale ); + } + + if ( !config.timestamp_on_capture ) { + TimestampImage( &alarm_image, snap->timestamp ); + } + image = &alarm_image; + } else { + image = image_buffer[index].image; } - if ( index != image_buffer_count ) - { - Image *image; - // If we are going to be modifying the snapshot before writing, then we need to copy it - if ( ( scale != ZM_SCALE_BASE ) || ( !config.timestamp_on_capture ) ) { - Snapshot *snap = &image_buffer[index]; - Image *snap_image = snap->image; - - alarm_image.Assign( *snap_image ); - - - //write_image.Assign( *snap_image ); - - if ( scale != ZM_SCALE_BASE ) { - alarm_image.Scale( scale ); - } - - if ( !config.timestamp_on_capture ) { - TimestampImage( &alarm_image, snap->timestamp ); - } - image = &alarm_image; - } else { - image = image_buffer[index].image; - } - - static char filename[PATH_MAX]; - snprintf( filename, sizeof(filename), "Monitor%d.jpg", id ); - image->WriteJpeg( filename ); - } - else - { - Error( "Unable to generate image, no images in buffer" ); - } - return( 0 ); + static char filename[PATH_MAX]; + snprintf( filename, sizeof(filename), "Monitor%d.jpg", id ); + image->WriteJpeg( filename ); + } + else + { + Error( "Unable to generate image, no images in buffer" ); + } + return( 0 ); } struct timeval Monitor::GetTimestamp( int index ) const { - if ( index < 0 || index > image_buffer_count ) - { - index = shared_data->last_write_index; - } + if ( index < 0 || index > image_buffer_count ) + { + index = shared_data->last_write_index; + } - if ( index != image_buffer_count ) - { - Snapshot *snap = &image_buffer[index]; + if ( index != image_buffer_count ) + { + Snapshot *snap = &image_buffer[index]; - return( *(snap->timestamp) ); - } - else - { - static struct timeval null_tv = { 0, 0 }; + return( *(snap->timestamp) ); + } + else + { + static struct timeval null_tv = { 0, 0 }; - return( null_tv ); - } + return( null_tv ); + } } unsigned int Monitor::GetLastReadIndex() const { - return( shared_data->last_read_index!=(unsigned int)image_buffer_count?shared_data->last_read_index:-1 ); + return( shared_data->last_read_index!=(unsigned int)image_buffer_count?shared_data->last_read_index:-1 ); } unsigned int Monitor::GetLastWriteIndex() const { - return( shared_data->last_write_index!=(unsigned int)image_buffer_count?shared_data->last_write_index:-1 ); + return( shared_data->last_write_index!=(unsigned int)image_buffer_count?shared_data->last_write_index:-1 ); } unsigned int Monitor::GetLastEvent() const { - return( shared_data->last_event ); + return( shared_data->last_event ); } double Monitor::GetFPS() const { - int index1 = shared_data->last_write_index; - if ( index1 == image_buffer_count ) - { - return( 0.0 ); - } - Snapshot *snap1 = &image_buffer[index1]; - if ( !snap1->timestamp || !snap1->timestamp->tv_sec ) - { - return( 0.0 ); - } - struct timeval time1 = *snap1->timestamp; + int index1 = shared_data->last_write_index; + if ( index1 == image_buffer_count ) + { + return( 0.0 ); + } + Snapshot *snap1 = &image_buffer[index1]; + if ( !snap1->timestamp || !snap1->timestamp->tv_sec ) + { + return( 0.0 ); + } + struct timeval time1 = *snap1->timestamp; - int image_count = image_buffer_count; - int index2 = (index1+1)%image_buffer_count; - if ( index2 == image_buffer_count ) + int image_count = image_buffer_count; + int index2 = (index1+1)%image_buffer_count; + if ( index2 == image_buffer_count ) + { + return( 0.0 ); + } + Snapshot *snap2 = &image_buffer[index2]; + while ( !snap2->timestamp || !snap2->timestamp->tv_sec ) + { + if ( index1 == index2 ) { - return( 0.0 ); + return( 0.0 ); } - Snapshot *snap2 = &image_buffer[index2]; - while ( !snap2->timestamp || !snap2->timestamp->tv_sec ) - { - if ( index1 == index2 ) - { - return( 0.0 ); - } - index2 = (index2+1)%image_buffer_count; - snap2 = &image_buffer[index2]; - image_count--; - } - struct timeval time2 = *snap2->timestamp; + index2 = (index2+1)%image_buffer_count; + snap2 = &image_buffer[index2]; + image_count--; + } + struct timeval time2 = *snap2->timestamp; - double time_diff = tvDiffSec( time2, time1 ); + double time_diff = tvDiffSec( time2, time1 ); - double curr_fps = image_count/time_diff; + double curr_fps = image_count/time_diff; - if ( curr_fps < 0.0 ) - { - //Error( "Negative FPS %f, time_diff = %lf (%d:%ld.%ld - %d:%ld.%ld), ibc: %d", curr_fps, time_diff, index2, time2.tv_sec, time2.tv_usec, index1, time1.tv_sec, time1.tv_usec, image_buffer_count ); - return( 0.0 ); - } - return( curr_fps ); + if ( curr_fps < 0.0 ) + { + //Error( "Negative FPS %f, time_diff = %lf (%d:%ld.%ld - %d:%ld.%ld), ibc: %d", curr_fps, time_diff, index2, time2.tv_sec, time2.tv_usec, index1, time1.tv_sec, time1.tv_usec, image_buffer_count ); + return( 0.0 ); + } + return( curr_fps ); } void Monitor::ForceAlarmOn( int force_score, const char *force_cause, const char *force_text ) { - trigger_data->trigger_state = TRIGGER_ON; - trigger_data->trigger_score = force_score; - strncpy( trigger_data->trigger_cause, force_cause, sizeof(trigger_data->trigger_cause) ); - strncpy( trigger_data->trigger_text, force_text, sizeof(trigger_data->trigger_text) ); + trigger_data->trigger_state = TRIGGER_ON; + trigger_data->trigger_score = force_score; + strncpy( trigger_data->trigger_cause, force_cause, sizeof(trigger_data->trigger_cause) ); + strncpy( trigger_data->trigger_text, force_text, sizeof(trigger_data->trigger_text) ); } void Monitor::ForceAlarmOff() { - trigger_data->trigger_state = TRIGGER_OFF; + trigger_data->trigger_state = TRIGGER_OFF; } void Monitor::CancelForced() { - trigger_data->trigger_state = TRIGGER_CANCEL; + trigger_data->trigger_state = TRIGGER_CANCEL; } void Monitor::actionReload() { - shared_data->action |= RELOAD; + shared_data->action |= RELOAD; } void Monitor::actionEnable() { - shared_data->action |= RELOAD; + shared_data->action |= RELOAD; - static char sql[ZM_SQL_SML_BUFSIZ]; - snprintf( sql, sizeof(sql), "update Monitors set Enabled = 1 where Id = '%d'", id ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + static char sql[ZM_SQL_SML_BUFSIZ]; + snprintf( sql, sizeof(sql), "update Monitors set Enabled = 1 where Id = '%d'", id ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } } void Monitor::actionDisable() { - shared_data->action |= RELOAD; + shared_data->action |= RELOAD; - static char sql[ZM_SQL_SML_BUFSIZ]; - snprintf( sql, sizeof(sql), "update Monitors set Enabled = 0 where Id = '%d'", id ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + static char sql[ZM_SQL_SML_BUFSIZ]; + snprintf( sql, sizeof(sql), "update Monitors set Enabled = 0 where Id = '%d'", id ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } } void Monitor::actionSuspend() { - shared_data->action |= SUSPEND; + shared_data->action |= SUSPEND; } void Monitor::actionResume() { - shared_data->action |= RESUME; + shared_data->action |= RESUME; } int Monitor::actionBrightness( int p_brightness ) { - if ( purpose != CAPTURE ) + if ( purpose != CAPTURE ) + { + if ( p_brightness >= 0 ) { - if ( p_brightness >= 0 ) - { - shared_data->brightness = p_brightness; - shared_data->action |= SET_SETTINGS; - int wait_loops = 10; - while ( shared_data->action & SET_SETTINGS ) - { - if ( wait_loops-- ) - usleep( 100000 ); - else - { - Warning( "Timed out waiting to set brightness" ); - return( -1 ); - } - } - } + shared_data->brightness = p_brightness; + shared_data->action |= SET_SETTINGS; + int wait_loops = 10; + while ( shared_data->action & SET_SETTINGS ) + { + if ( wait_loops-- ) + usleep( 100000 ); else { - shared_data->action |= GET_SETTINGS; - int wait_loops = 10; - while ( shared_data->action & GET_SETTINGS ) - { - if ( wait_loops-- ) - usleep( 100000 ); - else - { - Warning( "Timed out waiting to get brightness" ); - return( -1 ); - } - } + Warning( "Timed out waiting to set brightness" ); + return( -1 ); } - return( shared_data->brightness ); + } } - return( camera->Brightness( p_brightness ) ); + else + { + shared_data->action |= GET_SETTINGS; + int wait_loops = 10; + while ( shared_data->action & GET_SETTINGS ) + { + if ( wait_loops-- ) + usleep( 100000 ); + else + { + Warning( "Timed out waiting to get brightness" ); + return( -1 ); + } + } + } + return( shared_data->brightness ); + } + return( camera->Brightness( p_brightness ) ); } int Monitor::actionContrast( int p_contrast ) { - if ( purpose != CAPTURE ) + if ( purpose != CAPTURE ) + { + if ( p_contrast >= 0 ) { - if ( p_contrast >= 0 ) - { - shared_data->contrast = p_contrast; - shared_data->action |= SET_SETTINGS; - int wait_loops = 10; - while ( shared_data->action & SET_SETTINGS ) - { - if ( wait_loops-- ) - usleep( 100000 ); - else - { - Warning( "Timed out waiting to set contrast" ); - return( -1 ); - } - } - } + shared_data->contrast = p_contrast; + shared_data->action |= SET_SETTINGS; + int wait_loops = 10; + while ( shared_data->action & SET_SETTINGS ) + { + if ( wait_loops-- ) + usleep( 100000 ); else { - shared_data->action |= GET_SETTINGS; - int wait_loops = 10; - while ( shared_data->action & GET_SETTINGS ) - { - if ( wait_loops-- ) - usleep( 100000 ); - else - { - Warning( "Timed out waiting to get contrast" ); - return( -1 ); - } - } + Warning( "Timed out waiting to set contrast" ); + return( -1 ); } - return( shared_data->contrast ); + } } - return( camera->Contrast( p_contrast ) ); + else + { + shared_data->action |= GET_SETTINGS; + int wait_loops = 10; + while ( shared_data->action & GET_SETTINGS ) + { + if ( wait_loops-- ) + usleep( 100000 ); + else + { + Warning( "Timed out waiting to get contrast" ); + return( -1 ); + } + } + } + return( shared_data->contrast ); + } + return( camera->Contrast( p_contrast ) ); } int Monitor::actionHue( int p_hue ) { - if ( purpose != CAPTURE ) + if ( purpose != CAPTURE ) + { + if ( p_hue >= 0 ) { - if ( p_hue >= 0 ) - { - shared_data->hue = p_hue; - shared_data->action |= SET_SETTINGS; - int wait_loops = 10; - while ( shared_data->action & SET_SETTINGS ) - { - if ( wait_loops-- ) - usleep( 100000 ); - else - { - Warning( "Timed out waiting to set hue" ); - return( -1 ); - } - } - } + shared_data->hue = p_hue; + shared_data->action |= SET_SETTINGS; + int wait_loops = 10; + while ( shared_data->action & SET_SETTINGS ) + { + if ( wait_loops-- ) + usleep( 100000 ); else { - shared_data->action |= GET_SETTINGS; - int wait_loops = 10; - while ( shared_data->action & GET_SETTINGS ) - { - if ( wait_loops-- ) - usleep( 100000 ); - else - { - Warning( "Timed out waiting to get hue" ); - return( -1 ); - } - } + Warning( "Timed out waiting to set hue" ); + return( -1 ); } - return( shared_data->hue ); + } } - return( camera->Hue( p_hue ) ); + else + { + shared_data->action |= GET_SETTINGS; + int wait_loops = 10; + while ( shared_data->action & GET_SETTINGS ) + { + if ( wait_loops-- ) + usleep( 100000 ); + else + { + Warning( "Timed out waiting to get hue" ); + return( -1 ); + } + } + } + return( shared_data->hue ); + } + return( camera->Hue( p_hue ) ); } int Monitor::actionColour( int p_colour ) { - if ( purpose != CAPTURE ) + if ( purpose != CAPTURE ) + { + if ( p_colour >= 0 ) { - if ( p_colour >= 0 ) - { - shared_data->colour = p_colour; - shared_data->action |= SET_SETTINGS; - int wait_loops = 10; - while ( shared_data->action & SET_SETTINGS ) - { - if ( wait_loops-- ) - usleep( 100000 ); - else - { - Warning( "Timed out waiting to set colour" ); - return( -1 ); - } - } - } + shared_data->colour = p_colour; + shared_data->action |= SET_SETTINGS; + int wait_loops = 10; + while ( shared_data->action & SET_SETTINGS ) + { + if ( wait_loops-- ) + usleep( 100000 ); else { - shared_data->action |= GET_SETTINGS; - int wait_loops = 10; - while ( shared_data->action & GET_SETTINGS ) - { - if ( wait_loops-- ) - usleep( 100000 ); - else - { - Warning( "Timed out waiting to get colour" ); - return( -1 ); - } - } + Warning( "Timed out waiting to set colour" ); + return( -1 ); } - return( shared_data->colour ); + } } - return( camera->Colour( p_colour ) ); + else + { + shared_data->action |= GET_SETTINGS; + int wait_loops = 10; + while ( shared_data->action & GET_SETTINGS ) + { + if ( wait_loops-- ) + usleep( 100000 ); + else + { + Warning( "Timed out waiting to get colour" ); + return( -1 ); + } + } + } + return( shared_data->colour ); + } + return( camera->Colour( p_colour ) ); } void Monitor::DumpZoneImage( const char *zone_string ) { - int exclude_id = 0; - int extra_colour = 0; - Polygon extra_zone; + int exclude_id = 0; + int extra_colour = 0; + Polygon extra_zone; - if ( zone_string ) + if ( zone_string ) + { + if ( !Zone::ParseZoneString( zone_string, exclude_id, extra_colour, extra_zone ) ) { - if ( !Zone::ParseZoneString( zone_string, exclude_id, extra_colour, extra_zone ) ) - { - Error( "Failed to parse zone string, ignoring" ); - } + Error( "Failed to parse zone string, ignoring" ); } + } - int index = shared_data->last_write_index; - Snapshot *snap = &image_buffer[index]; - Image *snap_image = snap->image; + int index = shared_data->last_write_index; + Snapshot *snap = &image_buffer[index]; + Image *snap_image = snap->image; - Image zone_image( *snap_image ); - if(zone_image.Colours() == ZM_COLOUR_GRAY8) { - zone_image.Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB ); - } - - for( int i = 0; i < n_zones; i++ ) + Image zone_image( *snap_image ); + if(zone_image.Colours() == ZM_COLOUR_GRAY8) { + zone_image.Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB ); + } + + for( int i = 0; i < n_zones; i++ ) + { + if ( exclude_id && (!extra_colour || extra_zone.getNumCoords()) && zones[i]->Id() == exclude_id ) + continue; + + Rgb colour; + if ( exclude_id && !extra_zone.getNumCoords() && zones[i]->Id() == exclude_id ) { - if ( exclude_id && (!extra_colour || extra_zone.getNumCoords()) && zones[i]->Id() == exclude_id ) - continue; - - Rgb colour; - if ( exclude_id && !extra_zone.getNumCoords() && zones[i]->Id() == exclude_id ) - { - colour = extra_colour; - } - else - { - if ( zones[i]->IsActive() ) - { - colour = RGB_RED; - } - else if ( zones[i]->IsInclusive() ) - { - colour = RGB_ORANGE; - } - else if ( zones[i]->IsExclusive() ) - { - colour = RGB_PURPLE; - } - else if ( zones[i]->IsPreclusive() ) - { - colour = RGB_BLUE; - } - else - { - colour = RGB_WHITE; - } - } - zone_image.Fill( colour, 2, zones[i]->GetPolygon() ); - zone_image.Outline( colour, zones[i]->GetPolygon() ); + colour = extra_colour; } - - if ( extra_zone.getNumCoords() ) + else { - zone_image.Fill( extra_colour, 2, extra_zone ); - zone_image.Outline( extra_colour, extra_zone ); + if ( zones[i]->IsActive() ) + { + colour = RGB_RED; + } + else if ( zones[i]->IsInclusive() ) + { + colour = RGB_ORANGE; + } + else if ( zones[i]->IsExclusive() ) + { + colour = RGB_PURPLE; + } + else if ( zones[i]->IsPreclusive() ) + { + colour = RGB_BLUE; + } + else + { + colour = RGB_WHITE; + } } + zone_image.Fill( colour, 2, zones[i]->GetPolygon() ); + zone_image.Outline( colour, zones[i]->GetPolygon() ); + } - static char filename[PATH_MAX]; - snprintf( filename, sizeof(filename), "Zones%d.jpg", id ); - zone_image.WriteJpeg( filename ); + if ( extra_zone.getNumCoords() ) + { + zone_image.Fill( extra_colour, 2, extra_zone ); + zone_image.Outline( extra_colour, extra_zone ); + } + + static char filename[PATH_MAX]; + snprintf( filename, sizeof(filename), "Zones%d.jpg", id ); + zone_image.WriteJpeg( filename ); } void Monitor::DumpImage( Image *dump_image ) const { - if ( image_count && !(image_count%10) ) - { - static char filename[PATH_MAX]; - static char new_filename[PATH_MAX]; - snprintf( filename, sizeof(filename), "Monitor%d.jpg", id ); - snprintf( new_filename, sizeof(new_filename), "Monitor%d-new.jpg", id ); - dump_image->WriteJpeg( new_filename ); - rename( new_filename, filename ); - } + if ( image_count && !(image_count%10) ) + { + static char filename[PATH_MAX]; + static char new_filename[PATH_MAX]; + snprintf( filename, sizeof(filename), "Monitor%d.jpg", id ); + snprintf( new_filename, sizeof(new_filename), "Monitor%d-new.jpg", id ); + dump_image->WriteJpeg( new_filename ); + rename( new_filename, filename ); + } } bool Monitor::CheckSignal( const Image *image ) { - static bool static_undef = true; - /* RGB24 colors */ - static uint8_t red_val; - static uint8_t green_val; - static uint8_t blue_val; - static uint8_t grayscale_val; /* 8bit grayscale color */ - static Rgb colour_val; /* RGB32 color */ - static int usedsubpixorder; + static bool static_undef = true; + /* RGB24 colors */ + static uint8_t red_val; + static uint8_t green_val; + static uint8_t blue_val; + static uint8_t grayscale_val; /* 8bit grayscale color */ + static Rgb colour_val; /* RGB32 color */ + static int usedsubpixorder; - if ( config.signal_check_points > 0 ) + if ( config.signal_check_points > 0 ) + { + if ( static_undef ) { - if ( static_undef ) - { - static_undef = false; - usedsubpixorder = camera->SubpixelOrder(); - colour_val = rgb_convert(signal_check_colour, ZM_SUBPIX_ORDER_BGR); /* HTML colour code is actually BGR in memory, we want RGB */ - colour_val = rgb_convert(colour_val, usedsubpixorder); - red_val = RED_VAL_BGRA(signal_check_colour); - green_val = GREEN_VAL_BGRA(signal_check_colour); - blue_val = BLUE_VAL_BGRA(signal_check_colour); - grayscale_val = signal_check_colour & 0xff; /* Clear all bytes but lowest byte */ - } - - const uint8_t *buffer = image->Buffer(); - int pixels = image->Pixels(); - int width = image->Width(); - int colours = image->Colours(); - - int index = 0; - for ( int i = 0; i < config.signal_check_points; i++ ) - { - while( true ) - { - index = (int)(((long long)rand()*(long long)(pixels-1))/RAND_MAX); - 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 ) - break; - } - - if(colours == ZM_COLOUR_GRAY8) { - if ( *(buffer+index) != grayscale_val ) - return true; - - } else if(colours == ZM_COLOUR_RGB24) { - const uint8_t *ptr = buffer+(index*colours); - - if ( usedsubpixorder == ZM_SUBPIX_ORDER_BGR) { - if ( (RED_PTR_BGRA(ptr) != red_val) || (GREEN_PTR_BGRA(ptr) != green_val) || (BLUE_PTR_BGRA(ptr) != blue_val) ) - return true; - } else { - /* Assume RGB */ - if ( (RED_PTR_RGBA(ptr) != red_val) || (GREEN_PTR_RGBA(ptr) != green_val) || (BLUE_PTR_RGBA(ptr) != blue_val) ) - return true; - } - - } else if(colours == ZM_COLOUR_RGB32) { - if ( usedsubpixorder == ZM_SUBPIX_ORDER_ARGB || usedsubpixorder == ZM_SUBPIX_ORDER_ABGR) { - if ( ARGB_ABGR_ZEROALPHA(*(((const Rgb*)buffer)+index)) != ARGB_ABGR_ZEROALPHA(colour_val) ) - return true; - } else { - /* Assume RGBA or BGRA */ - if ( RGBA_BGRA_ZEROALPHA(*(((const Rgb*)buffer)+index)) != RGBA_BGRA_ZEROALPHA(colour_val) ) - return true; - } - } - - } - return( false ); + static_undef = false; + usedsubpixorder = camera->SubpixelOrder(); + colour_val = rgb_convert(signal_check_colour, ZM_SUBPIX_ORDER_BGR); /* HTML colour code is actually BGR in memory, we want RGB */ + colour_val = rgb_convert(colour_val, usedsubpixorder); + red_val = RED_VAL_BGRA(signal_check_colour); + green_val = GREEN_VAL_BGRA(signal_check_colour); + blue_val = BLUE_VAL_BGRA(signal_check_colour); + grayscale_val = signal_check_colour & 0xff; /* Clear all bytes but lowest byte */ } - return( true ); + + const uint8_t *buffer = image->Buffer(); + int pixels = image->Pixels(); + int width = image->Width(); + int colours = image->Colours(); + + int index = 0; + for ( int i = 0; i < config.signal_check_points; i++ ) + { + while( true ) + { + index = (int)(((long long)rand()*(long long)(pixels-1))/RAND_MAX); + 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 ) + break; + } + + if(colours == ZM_COLOUR_GRAY8) { + if ( *(buffer+index) != grayscale_val ) + return true; + + } else if(colours == ZM_COLOUR_RGB24) { + const uint8_t *ptr = buffer+(index*colours); + + if ( usedsubpixorder == ZM_SUBPIX_ORDER_BGR) { + if ( (RED_PTR_BGRA(ptr) != red_val) || (GREEN_PTR_BGRA(ptr) != green_val) || (BLUE_PTR_BGRA(ptr) != blue_val) ) + return true; + } else { + /* Assume RGB */ + if ( (RED_PTR_RGBA(ptr) != red_val) || (GREEN_PTR_RGBA(ptr) != green_val) || (BLUE_PTR_RGBA(ptr) != blue_val) ) + return true; + } + + } else if(colours == ZM_COLOUR_RGB32) { + if ( usedsubpixorder == ZM_SUBPIX_ORDER_ARGB || usedsubpixorder == ZM_SUBPIX_ORDER_ABGR) { + if ( ARGB_ABGR_ZEROALPHA(*(((const Rgb*)buffer)+index)) != ARGB_ABGR_ZEROALPHA(colour_val) ) + return true; + } else { + /* Assume RGBA or BGRA */ + if ( RGBA_BGRA_ZEROALPHA(*(((const Rgb*)buffer)+index)) != RGBA_BGRA_ZEROALPHA(colour_val) ) + return true; + } + } + + } + return( false ); + } + return( true ); } bool Monitor::Analyse() { - if ( shared_data->last_read_index == shared_data->last_write_index ) + if ( shared_data->last_read_index == shared_data->last_write_index ) + { + return( false ); + } + + struct timeval now; + gettimeofday( &now, NULL ); + + if ( image_count && fps_report_interval && !(image_count%fps_report_interval) ) + { + fps = double(fps_report_interval)/(now.tv_sec-last_fps_time); + Info( "%s: %d - Processing at %.2f fps", name, image_count, fps ); + last_fps_time = now.tv_sec; + } + + int index; + if ( config.opt_adaptive_skip ) + { + int read_margin = shared_data->last_read_index - shared_data->last_write_index; + if ( read_margin < 0 ) read_margin += image_buffer_count; + + int step = 1; + if ( read_margin > 0 ) { - return( false ); + step = (9*image_buffer_count)/(5*read_margin); } - struct timeval now; - gettimeofday( &now, NULL ); + int pending_frames = shared_data->last_write_index - shared_data->last_read_index; + if ( pending_frames < 0 ) pending_frames += image_buffer_count; - if ( image_count && fps_report_interval && !(image_count%fps_report_interval) ) + Debug( 4, "RI:%d, WI: %d, PF = %d, RM = %d, Step = %d", shared_data->last_read_index, shared_data->last_write_index, pending_frames, read_margin, step ); + if ( step <= pending_frames ) { - fps = double(fps_report_interval)/(now.tv_sec-last_fps_time); - Info( "%s: %d - Processing at %.2f fps", name, image_count, fps ); - last_fps_time = now.tv_sec; - } - - int index; - if ( config.opt_adaptive_skip ) - { - int read_margin = shared_data->last_read_index - shared_data->last_write_index; - if ( read_margin < 0 ) read_margin += image_buffer_count; - - int step = 1; - if ( read_margin > 0 ) - { - step = (9*image_buffer_count)/(5*read_margin); - } - - int pending_frames = shared_data->last_write_index - shared_data->last_read_index; - if ( pending_frames < 0 ) pending_frames += image_buffer_count; - - Debug( 4, "RI:%d, WI: %d, PF = %d, RM = %d, Step = %d", shared_data->last_read_index, shared_data->last_write_index, pending_frames, read_margin, step ); - if ( step <= pending_frames ) - { - index = (shared_data->last_read_index+step)%image_buffer_count; - } - else - { - if ( pending_frames ) - { - Warning( "Approaching buffer overrun, consider slowing capture, simplifying analysis or increasing ring buffer size" ); - } - index = shared_data->last_write_index%image_buffer_count; - } + index = (shared_data->last_read_index+step)%image_buffer_count; } else { - index = shared_data->last_write_index%image_buffer_count; + if ( pending_frames ) + { + Warning( "Approaching buffer overrun, consider slowing capture, simplifying analysis or increasing ring buffer size" ); + } + index = shared_data->last_write_index%image_buffer_count; } + } + else + { + index = shared_data->last_write_index%image_buffer_count; + } - Snapshot *snap = &image_buffer[index]; - struct timeval *timestamp = snap->timestamp; - Image *snap_image = snap->image; + Snapshot *snap = &image_buffer[index]; + struct timeval *timestamp = snap->timestamp; + Image *snap_image = snap->image; - if ( shared_data->action ) + if ( shared_data->action ) + { + if ( shared_data->action & RELOAD ) { - if ( shared_data->action & RELOAD ) - { - Info( "Received reload indication at count %d", image_count ); - shared_data->action &= ~RELOAD; - Reload(); - } - if ( shared_data->action & SUSPEND ) - { - if ( Active() ) - { - Info( "Received suspend indication at count %d", image_count ); - shared_data->active = false; - //closeEvent(); - } - if ( config.max_suspend_time ) - { - auto_resume_time = now.tv_sec + config.max_suspend_time; - } - shared_data->action &= ~SUSPEND; - } - if ( shared_data->action & RESUME ) - { - if ( Enabled() && !Active() ) - { - Info( "Received resume indication at count %d", image_count ); - shared_data->active = true; - ref_image = *snap_image; - ready_count = image_count+(warmup_count/2); - shared_data->alarm_x = shared_data->alarm_y = -1; - } - shared_data->action &= ~RESUME; - } + Info( "Received reload indication at count %d", image_count ); + shared_data->action &= ~RELOAD; + Reload(); } - if ( auto_resume_time && (now.tv_sec >= auto_resume_time) ) + if ( shared_data->action & SUSPEND ) { - Info( "Auto resuming at count %d", image_count ); + if ( Active() ) + { + Info( "Received suspend indication at count %d", image_count ); + shared_data->active = false; + //closeEvent(); + } + if ( config.max_suspend_time ) + { + auto_resume_time = now.tv_sec + config.max_suspend_time; + } + shared_data->action &= ~SUSPEND; + } + if ( shared_data->action & RESUME ) + { + if ( Enabled() && !Active() ) + { + Info( "Received resume indication at count %d", image_count ); shared_data->active = true; ref_image = *snap_image; ready_count = image_count+(warmup_count/2); - auto_resume_time = 0; + shared_data->alarm_x = shared_data->alarm_y = -1; + } + shared_data->action &= ~RESUME; } + } + if ( auto_resume_time && (now.tv_sec >= auto_resume_time) ) + { + Info( "Auto resuming at count %d", image_count ); + shared_data->active = true; + ref_image = *snap_image; + ready_count = image_count+(warmup_count/2); + auto_resume_time = 0; + } - static bool static_undef = true; - static int last_section_mod = 0; - static bool last_signal; + static bool static_undef = true; + static int last_section_mod = 0; + static bool last_signal; - if ( static_undef ) + if ( static_undef ) + { + static_undef = false; + timestamps = new struct timeval *[pre_event_count]; + images = new Image *[pre_event_count]; + last_signal = shared_data->signal; + } + + if ( Enabled() ) + { + bool signal = shared_data->signal; + bool signal_change = (signal != last_signal); + if ( trigger_data->trigger_state != TRIGGER_OFF ) { - static_undef = false; - timestamps = new struct timeval *[pre_event_count]; - images = new Image *[pre_event_count]; - last_signal = shared_data->signal; - } + unsigned int score = 0; + if ( Ready() ) + { + std::string cause; + Event::StringSetMap noteSetMap; - if ( Enabled() ) - { - bool signal = shared_data->signal; - bool signal_change = (signal != last_signal); - if ( trigger_data->trigger_state != TRIGGER_OFF ) + if ( trigger_data->trigger_state == TRIGGER_ON ) { - unsigned int score = 0; - if ( Ready() ) + score += trigger_data->trigger_score; + if ( !event ) + { + if ( cause.length() ) + cause += ", "; + cause += trigger_data->trigger_cause; + } + Event::StringSet noteSet; + noteSet.insert( trigger_data->trigger_text ); + noteSetMap[trigger_data->trigger_cause] = noteSet; + } + if ( signal_change ) + { + const char *signalText; + if ( !signal ) + signalText = "Lost"; + else + { + signalText = "Reacquired"; + score += 100; + } + Warning( "%s: %s", SIGNAL_CAUSE, signalText ); + if ( event && !signal ) + { + Info( "%s: %03d - Closing event %d, signal loss", name, image_count, event->Id() ); + closeEvent(); + last_section_mod = 0; + } + if ( !event ) + { + if ( cause.length() ) + cause += ", "; + cause += SIGNAL_CAUSE; + } + Event::StringSet noteSet; + noteSet.insert( signalText ); + noteSetMap[SIGNAL_CAUSE] = noteSet; + shared_data->state = state = IDLE; + shared_data->active = signal; + ref_image = *snap_image; + } + else if ( signal && Active() && (function == MODECT || function == MOCORD) ) + { + Event::StringSet zoneSet; + int motion_score = last_motion_score; + if ( !(image_count % (motion_frame_skip+1) ) ) + { + // Get new score. + motion_score = last_motion_score = DetectMotion( *snap_image, zoneSet ); + } + //int motion_score = DetectBlack( *snap_image, zoneSet ); + if ( motion_score ) + { + if ( !event ) { - std::string cause; - Event::StringSetMap noteSetMap; + score += motion_score; + if ( cause.length() ) + cause += ", "; + cause += MOTION_CAUSE; + } + else + { + score += motion_score; + } + noteSetMap[MOTION_CAUSE] = zoneSet; - if ( trigger_data->trigger_state == TRIGGER_ON ) + } + shared_data->active = signal; + } + if ( (!signal_change && signal) && n_linked_monitors > 0 ) + { + bool first_link = true; + Event::StringSet noteSet; + for ( int i = 0; i < n_linked_monitors; i++ ) + { + if ( linked_monitors[i]->isConnected() ) + { + if ( linked_monitors[i]->hasAlarmed() ) + { + if ( !event ) { - score += trigger_data->trigger_score; - if ( !event ) - { - if ( cause.length() ) - cause += ", "; - cause += trigger_data->trigger_cause; - } - Event::StringSet noteSet; - noteSet.insert( trigger_data->trigger_text ); - noteSetMap[trigger_data->trigger_cause] = noteSet; + if ( first_link ) + { + if ( cause.length() ) + cause += ", "; + cause += LINKED_CAUSE; + first_link = false; + } } - if ( signal_change ) + noteSet.insert( linked_monitors[i]->Name() ); + score += 50; + } + } + else + { + linked_monitors[i]->connect(); + } + } + if ( noteSet.size() > 0 ) + noteSetMap[LINKED_CAUSE] = noteSet; + } + if ( (!signal_change && signal) && (function == RECORD || function == MOCORD) ) + { + if ( event ) + { + int section_mod = timestamp->tv_sec%section_length; + if ( section_mod < last_section_mod ) + { + if ( state == IDLE || state == TAPE || event_close_mode == CLOSE_TIME ) + { + if ( state == TAPE ) { - const char *signalText; - if ( !signal ) - signalText = "Lost"; - else - { - signalText = "Reacquired"; - score += 100; - } - Warning( "%s: %s", SIGNAL_CAUSE, signalText ); - if ( event && !signal ) - { - Info( "%s: %03d - Closing event %d, signal loss", name, image_count, event->Id() ); - closeEvent(); - last_section_mod = 0; - } - if ( !event ) - { - if ( cause.length() ) - cause += ", "; - cause += SIGNAL_CAUSE; - } - Event::StringSet noteSet; - noteSet.insert( signalText ); - noteSetMap[SIGNAL_CAUSE] = noteSet; - shared_data->state = state = IDLE; - shared_data->active = signal; - ref_image = *snap_image; - } - else if ( signal && Active() && (function == MODECT || function == MOCORD) ) - { - Event::StringSet zoneSet; - int motion_score = last_motion_score; - if ( !(image_count % (motion_frame_skip+1) ) ) - { - // Get new score. - motion_score = last_motion_score = DetectMotion( *snap_image, zoneSet ); - } - //int motion_score = DetectBlack( *snap_image, zoneSet ); - if ( motion_score ) - { - if ( !event ) - { - score += motion_score; - if ( cause.length() ) - cause += ", "; - cause += MOTION_CAUSE; - } - else - { - score += motion_score; - } - noteSetMap[MOTION_CAUSE] = zoneSet; - - } - shared_data->active = signal; - } - if ( (!signal_change && signal) && n_linked_monitors > 0 ) - { - bool first_link = true; - Event::StringSet noteSet; - for ( int i = 0; i < n_linked_monitors; i++ ) - { - if ( linked_monitors[i]->isConnected() ) - { - if ( linked_monitors[i]->hasAlarmed() ) - { - if ( !event ) - { - if ( first_link ) - { - if ( cause.length() ) - cause += ", "; - cause += LINKED_CAUSE; - first_link = false; - } - } - noteSet.insert( linked_monitors[i]->Name() ); - score += 50; - } - } - else - { - linked_monitors[i]->connect(); - } - } - if ( noteSet.size() > 0 ) - noteSetMap[LINKED_CAUSE] = noteSet; - } - if ( (!signal_change && signal) && (function == RECORD || function == MOCORD) ) - { - if ( event ) - { - int section_mod = timestamp->tv_sec%section_length; - if ( section_mod < last_section_mod ) - { - if ( state == IDLE || state == TAPE || event_close_mode == CLOSE_TIME ) - { - if ( state == TAPE ) - { - shared_data->state = state = IDLE; - Info( "%s: %03d - Closing event %d, section end", name, image_count, event->Id() ) - } - else - Info( "%s: %03d - Closing event %d, section end forced ", name, image_count, event->Id() ); - closeEvent(); - last_section_mod = 0; - } - } - else - { - last_section_mod = section_mod; - } - } - if ( !event ) - { - - // Create event - event = new Event( this, *timestamp, "Continuous", noteSetMap ); - shared_data->last_event = event->Id(); - - Info( "%s: %03d - Opening new event %d, section start", name, image_count, event->Id() ); - - /* To prevent cancelling out an existing alert\prealarm\alarm state */ - if ( state == IDLE ) - { - shared_data->state = state = TAPE; - } - - //if ( config.overlap_timed_events ) - if ( false ) - { - int pre_index = ((index+image_buffer_count)-pre_event_count)%image_buffer_count; - int pre_event_images = pre_event_count; - while ( pre_event_images && !image_buffer[pre_index].timestamp->tv_sec ) - { - pre_index = (pre_index+1)%image_buffer_count; - pre_event_images--; - } - - if ( pre_event_images ) - { - for ( int i = 0; i < pre_event_images; i++ ) - { - timestamps[i] = image_buffer[pre_index].timestamp; - images[i] = image_buffer[pre_index].image; - - pre_index = (pre_index+1)%image_buffer_count; - } - event->AddFrames( pre_event_images, images, timestamps ); - } - } - } - } - if ( score ) - { - if ( (state == IDLE || state == TAPE || state == PREALARM ) ) - { - if ( Event::PreAlarmCount() >= (alarm_frame_count-1) ) - { - Info( "%s: %03d - Gone into alarm state", name, image_count ); - shared_data->state = state = ALARM; - if ( signal_change || (function != MOCORD && state != ALERT) ) - { - int pre_index; - if ( alarm_frame_count > 1 ) - pre_index = ((index+image_buffer_count)-((alarm_frame_count-1)+pre_event_count))%image_buffer_count; - else - pre_index = ((index+image_buffer_count)-pre_event_count)%image_buffer_count; - - int pre_event_images = pre_event_count; - while ( pre_event_images && !image_buffer[pre_index].timestamp->tv_sec ) - { - pre_index = (pre_index+1)%image_buffer_count; - pre_event_images--; - } - - event = new Event( this, *(image_buffer[pre_index].timestamp), cause, noteSetMap ); - shared_data->last_event = event->Id(); - - Info( "%s: %03d - Opening new event %d, alarm start", name, image_count, event->Id() ); - - if ( pre_event_images ) - { - for ( int i = 0; i < pre_event_images; i++ ) - { - timestamps[i] = image_buffer[pre_index].timestamp; - images[i] = image_buffer[pre_index].image; - - pre_index = (pre_index+1)%image_buffer_count; - } - event->AddFrames( pre_event_images, images, timestamps ); - } - if ( alarm_frame_count ) - { - event->SavePreAlarmFrames(); - } - } - } - else if ( state != PREALARM ) - { - Info( "%s: %03d - Gone into prealarm state", name, image_count ); - shared_data->state = state = PREALARM; - } - } - else if ( state == ALERT ) - { - Info( "%s: %03d - Gone back into alarm state", name, image_count ); - shared_data->state = state = ALARM; - } - last_alarm_count = image_count; + shared_data->state = state = IDLE; + Info( "%s: %03d - Closing event %d, section end", name, image_count, event->Id() ) } else - { - if ( state == ALARM ) - { - Info( "%s: %03d - Gone into alert state", name, image_count ); - shared_data->state = state = ALERT; - } - else if ( state == ALERT ) - { - if ( image_count-last_alarm_count > post_event_count ) - { - Info( "%s: %03d - Left alarm state (%d) - %d(%d) images", name, image_count, event->Id(), event->Frames(), event->AlarmFrames() ); - //if ( function != MOCORD || event_close_mode == CLOSE_ALARM || event->Cause() == SIGNAL_CAUSE ) - if ( function != MOCORD || event_close_mode == CLOSE_ALARM ) - { - shared_data->state = state = IDLE; - Info( "%s: %03d - Closing event %d, alarm end%s", name, image_count, event->Id(), (function==MOCORD)?", section truncated":"" ); - closeEvent(); - } - else - { - shared_data->state = state = TAPE; - } - } - } - if ( state == PREALARM ) - { - if ( function != MOCORD ) - { - shared_data->state = state = IDLE; - } - else - { - shared_data->state = state = TAPE; - } - } - if ( Event::PreAlarmCount() ) - Event::EmptyPreAlarmFrames(); - } - if ( state != IDLE ) - { - if ( state == PREALARM || state == ALARM ) - { - if ( config.create_analysis_images ) - { - bool got_anal_image = false; - alarm_image.Assign( *snap_image ); - for( int i = 0; i < n_zones; i++ ) - { - if ( zones[i]->Alarmed() ) - { - if ( zones[i]->AlarmImage() ) - { - alarm_image.Overlay( *(zones[i]->AlarmImage()) ); - got_anal_image = true; - } - if ( config.record_event_stats && state == ALARM ) - { - zones[i]->RecordStats( event ); - } - } - } - if ( got_anal_image ) - { - if ( state == PREALARM ) - Event::AddPreAlarmFrame( snap_image, *timestamp, score, &alarm_image ); - else - event->AddFrame( snap_image, *timestamp, score, &alarm_image ); - } - else - { - if ( state == PREALARM ) - Event::AddPreAlarmFrame( snap_image, *timestamp, score ); - else - event->AddFrame( snap_image, *timestamp, score ); - } - } - else - { - for( int i = 0; i < n_zones; i++ ) - { - if ( zones[i]->Alarmed() ) - { - if ( config.record_event_stats && state == ALARM ) - { - zones[i]->RecordStats( event ); - } - } - } - if ( state == PREALARM ) - Event::AddPreAlarmFrame( snap_image, *timestamp, score ); - else - event->AddFrame( snap_image, *timestamp, score ); - } - if ( event && noteSetMap.size() > 0 ) - event->updateNotes( noteSetMap ); - } - else if ( state == ALERT ) - { - event->AddFrame( snap_image, *timestamp ); - if ( noteSetMap.size() > 0 ) - event->updateNotes( noteSetMap ); - } - else if ( state == TAPE ) - { - if ( !(image_count%(frame_skip+1)) ) - { - if ( config.bulk_frame_interval > 1 ) - { - event->AddFrame( snap_image, *timestamp, (event->Frames()AddFrame( snap_image, *timestamp ); - } - } - } - } + Info( "%s: %03d - Closing event %d, section end forced ", name, image_count, event->Id() ); + closeEvent(); + last_section_mod = 0; + } } + else + { + last_section_mod = section_mod; + } + } + if ( !event ) + { + + // Create event + event = new Event( this, *timestamp, "Continuous", noteSetMap ); + shared_data->last_event = event->Id(); + + Info( "%s: %03d - Opening new event %d, section start", name, image_count, event->Id() ); + + /* To prevent cancelling out an existing alert\prealarm\alarm state */ + if ( state == IDLE ) + { + shared_data->state = state = TAPE; + } + + //if ( config.overlap_timed_events ) + if ( false ) + { + int pre_index = ((index+image_buffer_count)-pre_event_count)%image_buffer_count; + int pre_event_images = pre_event_count; + while ( pre_event_images && !image_buffer[pre_index].timestamp->tv_sec ) + { + pre_index = (pre_index+1)%image_buffer_count; + pre_event_images--; + } + + if ( pre_event_images ) + { + for ( int i = 0; i < pre_event_images; i++ ) + { + timestamps[i] = image_buffer[pre_index].timestamp; + images[i] = image_buffer[pre_index].image; + + pre_index = (pre_index+1)%image_buffer_count; + } + event->AddFrames( pre_event_images, images, timestamps ); + } + } + } + } + if ( score ) + { + if ( (state == IDLE || state == TAPE || state == PREALARM ) ) + { + if ( Event::PreAlarmCount() >= (alarm_frame_count-1) ) + { + Info( "%s: %03d - Gone into alarm state", name, image_count ); + shared_data->state = state = ALARM; + if ( signal_change || (function != MOCORD && state != ALERT) ) + { + int pre_index; + if ( alarm_frame_count > 1 ) + pre_index = ((index+image_buffer_count)-((alarm_frame_count-1)+pre_event_count))%image_buffer_count; + else + pre_index = ((index+image_buffer_count)-pre_event_count)%image_buffer_count; + + int pre_event_images = pre_event_count; + while ( pre_event_images && !image_buffer[pre_index].timestamp->tv_sec ) + { + pre_index = (pre_index+1)%image_buffer_count; + pre_event_images--; + } + + event = new Event( this, *(image_buffer[pre_index].timestamp), cause, noteSetMap ); + shared_data->last_event = event->Id(); + + Info( "%s: %03d - Opening new event %d, alarm start", name, image_count, event->Id() ); + + if ( pre_event_images ) + { + for ( int i = 0; i < pre_event_images; i++ ) + { + timestamps[i] = image_buffer[pre_index].timestamp; + images[i] = image_buffer[pre_index].image; + + pre_index = (pre_index+1)%image_buffer_count; + } + event->AddFrames( pre_event_images, images, timestamps ); + } + if ( alarm_frame_count ) + { + event->SavePreAlarmFrames(); + } + } + } + else if ( state != PREALARM ) + { + Info( "%s: %03d - Gone into prealarm state", name, image_count ); + shared_data->state = state = PREALARM; + } + } + else if ( state == ALERT ) + { + Info( "%s: %03d - Gone back into alarm state", name, image_count ); + shared_data->state = state = ALARM; + } + last_alarm_count = image_count; } else { - if ( event ) + if ( state == ALARM ) + { + Info( "%s: %03d - Gone into alert state", name, image_count ); + shared_data->state = state = ALERT; + } + else if ( state == ALERT ) + { + if ( image_count-last_alarm_count > post_event_count ) { - Info( "%s: %03d - Closing event %d, trigger off", name, image_count, event->Id() ); + Info( "%s: %03d - Left alarm state (%d) - %d(%d) images", name, image_count, event->Id(), event->Frames(), event->AlarmFrames() ); + //if ( function != MOCORD || event_close_mode == CLOSE_ALARM || event->Cause() == SIGNAL_CAUSE ) + if ( function != MOCORD || event_close_mode == CLOSE_ALARM ) + { + shared_data->state = state = IDLE; + Info( "%s: %03d - Closing event %d, alarm end%s", name, image_count, event->Id(), (function==MOCORD)?", section truncated":"" ); closeEvent(); + } + else + { + shared_data->state = state = TAPE; + } } - shared_data->state = state = IDLE; - last_section_mod = 0; + } + if ( state == PREALARM ) + { + if ( function != MOCORD ) + { + shared_data->state = state = IDLE; + } + else + { + shared_data->state = state = TAPE; + } + } + if ( Event::PreAlarmCount() ) + Event::EmptyPreAlarmFrames(); } - if ( (!signal_change && signal) && (function == MODECT || function == MOCORD) ) + if ( state != IDLE ) { - if ( state == ALARM ) { - ref_image.Blend( *snap_image, alarm_ref_blend_perc ); - } else { - ref_image.Blend( *snap_image, ref_blend_perc ); + if ( state == PREALARM || state == ALARM ) + { + if ( config.create_analysis_images ) + { + bool got_anal_image = false; + alarm_image.Assign( *snap_image ); + for( int i = 0; i < n_zones; i++ ) + { + if ( zones[i]->Alarmed() ) + { + if ( zones[i]->AlarmImage() ) + { + alarm_image.Overlay( *(zones[i]->AlarmImage()) ); + got_anal_image = true; + } + if ( config.record_event_stats && state == ALARM ) + { + zones[i]->RecordStats( event ); + } + } + } + if ( got_anal_image ) + { + if ( state == PREALARM ) + Event::AddPreAlarmFrame( snap_image, *timestamp, score, &alarm_image ); + else + event->AddFrame( snap_image, *timestamp, score, &alarm_image ); + } + else + { + if ( state == PREALARM ) + Event::AddPreAlarmFrame( snap_image, *timestamp, score ); + else + event->AddFrame( snap_image, *timestamp, score ); + } } + else + { + for( int i = 0; i < n_zones; i++ ) + { + if ( zones[i]->Alarmed() ) + { + if ( config.record_event_stats && state == ALARM ) + { + zones[i]->RecordStats( event ); + } + } + } + if ( state == PREALARM ) + Event::AddPreAlarmFrame( snap_image, *timestamp, score ); + else + event->AddFrame( snap_image, *timestamp, score ); + } + if ( event && noteSetMap.size() > 0 ) + event->updateNotes( noteSetMap ); + } + else if ( state == ALERT ) + { + event->AddFrame( snap_image, *timestamp ); + if ( noteSetMap.size() > 0 ) + event->updateNotes( noteSetMap ); + } + else if ( state == TAPE ) + { + if ( !(image_count%(frame_skip+1)) ) + { + if ( config.bulk_frame_interval > 1 ) + { + event->AddFrame( snap_image, *timestamp, (event->Frames()AddFrame( snap_image, *timestamp ); + } + } + } } - last_signal = signal; + } } + else + { + if ( event ) + { + Info( "%s: %03d - Closing event %d, trigger off", name, image_count, event->Id() ); + closeEvent(); + } + shared_data->state = state = IDLE; + last_section_mod = 0; + } + if ( (!signal_change && signal) && (function == MODECT || function == MOCORD) ) + { + if ( state == ALARM ) { + ref_image.Blend( *snap_image, alarm_ref_blend_perc ); + } else { + ref_image.Blend( *snap_image, ref_blend_perc ); + } + } + last_signal = signal; + } - shared_data->last_read_index = index%image_buffer_count; - //shared_data->last_read_time = image_buffer[index].timestamp->tv_sec; - shared_data->last_read_time = now.tv_sec; - image_count++; + shared_data->last_read_index = index%image_buffer_count; + //shared_data->last_read_time = image_buffer[index].timestamp->tv_sec; + shared_data->last_read_time = now.tv_sec; + image_count++; - return( true ); + return( true ); } void Monitor::Reload() { - Debug( 1, "Reloading monitor %s", name ); + Debug( 1, "Reloading monitor %s", name ); - if ( event ) - Info( "%s: %03d - Closing event %d, reloading", name, image_count, event->Id() ); + if ( event ) + Info( "%s: %03d - Closing event %d, reloading", name, image_count, event->Id() ); - closeEvent(); + closeEvent(); - static char sql[ZM_SQL_MED_BUFSIZ]; - snprintf( sql, sizeof(sql), "select Function+0, Enabled, LinkedMonitors, EventPrefix, LabelFormat, LabelX, LabelY, WarmupCount, PreEventCount, PostEventCount, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Id = '%d'", id ); + static char sql[ZM_SQL_MED_BUFSIZ]; + snprintf( sql, sizeof(sql), "select Function+0, Enabled, LinkedMonitors, EventPrefix, LabelFormat, LabelX, LabelY, WarmupCount, PreEventCount, PostEventCount, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Id = '%d'", id ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - int n_monitors = mysql_num_rows( result ); - if ( n_monitors != 1 ) - { - Error( "Bogus number of monitors, %d, returned. Can't reload", n_monitors ); - return; - } + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + int n_monitors = mysql_num_rows( result ); + if ( n_monitors != 1 ) + { + Error( "Bogus number of monitors, %d, returned. Can't reload", n_monitors ); + return; + } - if ( MYSQL_ROW dbrow = mysql_fetch_row( result ) ) - { - int index = 0; - function = (Function)atoi(dbrow[index++]); - enabled = atoi(dbrow[index++]); - const char *p_linked_monitors = dbrow[index++]; - strncpy( event_prefix, dbrow[index++], sizeof(event_prefix) ); - strncpy( label_format, dbrow[index++], sizeof(label_format) ); - label_coord = Coord( atoi(dbrow[index]), atoi(dbrow[index+1]) ); index += 2; - warmup_count = atoi(dbrow[index++]); - pre_event_count = atoi(dbrow[index++]); - post_event_count = atoi(dbrow[index++]); - alarm_frame_count = atoi(dbrow[index++]); - section_length = atoi(dbrow[index++]); - frame_skip = atoi(dbrow[index++]); - motion_frame_skip = atoi(dbrow[index++]); - capture_delay = (dbrow[index]&&atof(dbrow[index])>0.0)?int(DT_PREC_3/atof(dbrow[index])):0; index++; - alarm_capture_delay = (dbrow[index]&&atof(dbrow[index])>0.0)?int(DT_PREC_3/atof(dbrow[index])):0; index++; - fps_report_interval = atoi(dbrow[index++]); - ref_blend_perc = atoi(dbrow[index++]); - alarm_ref_blend_perc = atoi(dbrow[index++]); - track_motion = atoi(dbrow[index++]); - + if ( MYSQL_ROW dbrow = mysql_fetch_row( result ) ) + { + int index = 0; + function = (Function)atoi(dbrow[index++]); + enabled = atoi(dbrow[index++]); + const char *p_linked_monitors = dbrow[index++]; + strncpy( event_prefix, dbrow[index++], sizeof(event_prefix) ); + strncpy( label_format, dbrow[index++], sizeof(label_format) ); + label_coord = Coord( atoi(dbrow[index]), atoi(dbrow[index+1]) ); index += 2; + warmup_count = atoi(dbrow[index++]); + pre_event_count = atoi(dbrow[index++]); + post_event_count = atoi(dbrow[index++]); + alarm_frame_count = atoi(dbrow[index++]); + section_length = atoi(dbrow[index++]); + frame_skip = atoi(dbrow[index++]); + motion_frame_skip = atoi(dbrow[index++]); + capture_delay = (dbrow[index]&&atof(dbrow[index])>0.0)?int(DT_PREC_3/atof(dbrow[index])):0; index++; + alarm_capture_delay = (dbrow[index]&&atof(dbrow[index])>0.0)?int(DT_PREC_3/atof(dbrow[index])):0; index++; + fps_report_interval = atoi(dbrow[index++]); + ref_blend_perc = atoi(dbrow[index++]); + alarm_ref_blend_perc = atoi(dbrow[index++]); + track_motion = atoi(dbrow[index++]); - if ( dbrow[index][0] == '#' ) - signal_check_colour = strtol(dbrow[index]+1,0,16); - else - signal_check_colour = strtol(dbrow[index],0,16); - index++; - shared_data->state = state = IDLE; - shared_data->alarm_x = shared_data->alarm_y = -1; - if ( enabled ) - shared_data->active = true; - ready_count = image_count+warmup_count; + if ( dbrow[index][0] == '#' ) + signal_check_colour = strtol(dbrow[index]+1,0,16); + else + signal_check_colour = strtol(dbrow[index],0,16); + index++; - ReloadLinkedMonitors( p_linked_monitors ); - } - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - mysql_free_result( result ); + shared_data->state = state = IDLE; + shared_data->alarm_x = shared_data->alarm_y = -1; + if ( enabled ) + shared_data->active = true; + ready_count = image_count+warmup_count; - ReloadZones(); + ReloadLinkedMonitors( p_linked_monitors ); + } + if ( mysql_errno( &dbconn ) ) + { + Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + mysql_free_result( result ); + + ReloadZones(); } void Monitor::ReloadZones() { - Debug( 1, "Reloading zones for monitor %s", name ); - for( int i = 0; i < n_zones; i++ ) - { - delete zones[i]; - } - delete[] zones; - zones = 0; - n_zones = Zone::Load( this, zones ); - //DumpZoneImage(); + Debug( 1, "Reloading zones for monitor %s", name ); + for( int i = 0; i < n_zones; i++ ) + { + delete zones[i]; + } + delete[] zones; + zones = 0; + n_zones = Zone::Load( this, zones ); + //DumpZoneImage(); } void Monitor::ReloadLinkedMonitors( const char *p_linked_monitors ) { - Debug( 1, "Reloading linked monitors for monitor %s, '%s'", name, p_linked_monitors ); - if ( n_linked_monitors ) + Debug( 1, "Reloading linked monitors for monitor %s, '%s'", name, p_linked_monitors ); + if ( n_linked_monitors ) + { + for( int i = 0; i < n_linked_monitors; i++ ) { - for( int i = 0; i < n_linked_monitors; i++ ) - { - delete linked_monitors[i]; - } - delete[] linked_monitors; - linked_monitors = 0; + delete linked_monitors[i]; } + delete[] linked_monitors; + linked_monitors = 0; + } - n_linked_monitors = 0; - if ( p_linked_monitors ) + n_linked_monitors = 0; + if ( p_linked_monitors ) + { + int n_link_ids = 0; + unsigned int link_ids[256]; + + char link_id_str[8]; + char *dest_ptr = link_id_str; + const char *src_ptr = p_linked_monitors; + while( 1 ) { - int n_link_ids = 0; - unsigned int link_ids[256]; - - char link_id_str[8]; - char *dest_ptr = link_id_str; - const char *src_ptr = p_linked_monitors; - while( 1 ) + dest_ptr = link_id_str; + while( *src_ptr >= '0' && *src_ptr <= '9' ) + { + if ( (dest_ptr-link_id_str) < (unsigned int)(sizeof(link_id_str)-1) ) { - dest_ptr = link_id_str; - while( *src_ptr >= '0' && *src_ptr <= '9' ) - { - if ( (dest_ptr-link_id_str) < (unsigned int)(sizeof(link_id_str)-1) ) - { - *dest_ptr++ = *src_ptr++; - } - else - { - break; - } - } - // Add the link monitor - if ( dest_ptr != link_id_str ) - { - *dest_ptr = '\0'; - unsigned int link_id = atoi(link_id_str); - if ( link_id > 0 && link_id != id) - { - Debug( 3, "Found linked monitor id %d", link_id ); - int j; - for ( j = 0; j < n_link_ids; j++ ) - { - if ( link_ids[j] == link_id ) - break; - } - if ( j == n_link_ids ) // Not already found - { - link_ids[n_link_ids++] = link_id; - } - } - } - if ( !*src_ptr ) - break; - while( *src_ptr && (*src_ptr < '0' || *src_ptr > '9') ) - src_ptr++; - if ( !*src_ptr ) - break; + *dest_ptr++ = *src_ptr++; } - if ( n_link_ids > 0 ) + else { - Debug( 1, "Linking to %d monitors", n_link_ids ); - linked_monitors = new MonitorLink *[n_link_ids]; - int count = 0; - for ( int i = 0; i < n_link_ids; i++ ) - { - Debug( 1, "Checking linked monitor %d", link_ids[i] ); - - static char sql[ZM_SQL_SML_BUFSIZ]; - snprintf( sql, sizeof(sql), "select Id, Name from Monitors where Id = %d and Function != 'None' and Function != 'Monitor' and Enabled = 1", link_ids[i] ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - int n_monitors = mysql_num_rows( result ); - if ( n_monitors == 1 ) - { - MYSQL_ROW dbrow = mysql_fetch_row( result ); - Debug( 1, "Linking to monitor %d", link_ids[i] ); - linked_monitors[count++] = new MonitorLink( link_ids[i], dbrow[1] ); - } - else - { - Warning( "Can't link to monitor %d, invalid id, function or not enabled", link_ids[i] ); - } - mysql_free_result( result ); - } - n_linked_monitors = count; + break; } + } + // Add the link monitor + if ( dest_ptr != link_id_str ) + { + *dest_ptr = '\0'; + unsigned int link_id = atoi(link_id_str); + if ( link_id > 0 && link_id != id) + { + Debug( 3, "Found linked monitor id %d", link_id ); + int j; + for ( j = 0; j < n_link_ids; j++ ) + { + if ( link_ids[j] == link_id ) + break; + } + if ( j == n_link_ids ) // Not already found + { + link_ids[n_link_ids++] = link_id; + } + } + } + if ( !*src_ptr ) + break; + while( *src_ptr && (*src_ptr < '0' || *src_ptr > '9') ) + src_ptr++; + if ( !*src_ptr ) + break; } + if ( n_link_ids > 0 ) + { + Debug( 1, "Linking to %d monitors", n_link_ids ); + linked_monitors = new MonitorLink *[n_link_ids]; + int count = 0; + for ( int i = 0; i < n_link_ids; i++ ) + { + Debug( 1, "Checking linked monitor %d", link_ids[i] ); + + static char sql[ZM_SQL_SML_BUFSIZ]; + snprintf( sql, sizeof(sql), "select Id, Name from Monitors where Id = %d and Function != 'None' and Function != 'Monitor' and Enabled = 1", link_ids[i] ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + int n_monitors = mysql_num_rows( result ); + if ( n_monitors == 1 ) + { + MYSQL_ROW dbrow = mysql_fetch_row( result ); + Debug( 1, "Linking to monitor %d", link_ids[i] ); + linked_monitors[count++] = new MonitorLink( link_ids[i], dbrow[1] ); + } + else + { + Warning( "Can't link to monitor %d, invalid id, function or not enabled", link_ids[i] ); + } + mysql_free_result( result ); + } + n_linked_monitors = count; + } + } } #if ZM_HAS_V4L int Monitor::LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose 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) ); + 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) ); + } + 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 ); + } + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + int n_monitors = mysql_num_rows( result ); + Debug( 1, "Got %d monitors", n_monitors ); + delete[] monitors; + monitors = new Monitor *[n_monitors]; + for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) + { + int col = 0; + + int id = atoi(dbrow[col]); col++; + const char *name = dbrow[col]; col++; + int function = atoi(dbrow[col]); col++; + int enabled = atoi(dbrow[col]); col++; + const char *linked_monitors = dbrow[col]; col++; + + const char *device = dbrow[col]; col++; + int channel = atoi(dbrow[col]); col++; + int format = atoi(dbrow[col]); col++; + bool v4l_multi_buffer; + if ( dbrow[col] ) { + if (*dbrow[col] == '0' ) { + v4l_multi_buffer = false; + } else if ( *dbrow[col] == '1' ) { + v4l_multi_buffer = true; + } + } else { + v4l_multi_buffer = config.v4l_multi_buffer; } + col++; + + int v4l_captures_per_frame = 0; + if ( dbrow[col] ) { + v4l_captures_per_frame = atoi(dbrow[col]); + } else { + v4l_captures_per_frame = config.captures_per_frame; + } + Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); + col++; + const char *method = dbrow[col]; col++; + + int width = atoi(dbrow[col]); col++; + int height = atoi(dbrow[col]); col++; + int colours = atoi(dbrow[col]); col++; + int palette = atoi(dbrow[col]); col++; + Orientation orientation = (Orientation)atoi(dbrow[col]); col++; + unsigned int deinterlacing = atoi(dbrow[col]); col++; + int brightness = atoi(dbrow[col]); col++; + int contrast = atoi(dbrow[col]); col++; + int hue = atoi(dbrow[col]); col++; + int colour = atoi(dbrow[col]); col++; + + const char *event_prefix = dbrow[col]; col++; + const char *label_format = dbrow[col]; col++; + + int label_x = atoi(dbrow[col]); col++; + int label_y = atoi(dbrow[col]); col++; + + int image_buffer_count = atoi(dbrow[col]); col++; + int warmup_count = atoi(dbrow[col]); col++; + int pre_event_count = atoi(dbrow[col]); col++; + int post_event_count = atoi(dbrow[col]); col++; + int stream_replay_buffer = atoi(dbrow[col]); col++; + int alarm_frame_count = atoi(dbrow[col]); col++; + int section_length = atoi(dbrow[col]); col++; + int frame_skip = atoi(dbrow[col]); col++; + int motion_frame_skip = atoi(dbrow[col]); col++; + int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; + int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; + int fps_report_interval = atoi(dbrow[col]); col++; + int ref_blend_perc = atoi(dbrow[col]); col++; + int alarm_ref_blend_perc = atoi(dbrow[col]); col++; + int track_motion = atoi(dbrow[col]); col++; + + int signal_check_colour; + if ( dbrow[col][0] == '#' ) + signal_check_colour = strtol(dbrow[col]+1,0,16); 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 ); - } - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + signal_check_colour = strtol(dbrow[col],0,16); + col++; - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - int n_monitors = mysql_num_rows( result ); - Debug( 1, "Got %d monitors", n_monitors ); - delete[] monitors; - monitors = new Monitor *[n_monitors]; - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) - { - int col = 0; + int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); + int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); - int id = atoi(dbrow[col]); col++; - const char *name = dbrow[col]; col++; - int function = atoi(dbrow[col]); col++; - int enabled = atoi(dbrow[col]); col++; - const char *linked_monitors = dbrow[col]; col++; + int extras = (deinterlacing>>24)&0xff; - const char *device = dbrow[col]; col++; - int channel = atoi(dbrow[col]); col++; - int format = atoi(dbrow[col]); col++; - bool v4l_multi_buffer; - if ( dbrow[col] ) { - if (*dbrow[col] == '0' ) { - v4l_multi_buffer = false; - } else if ( *dbrow[col] == '1' ) { - v4l_multi_buffer = true; - } - } else { - v4l_multi_buffer = config.v4l_multi_buffer; - } - col++; - - int v4l_captures_per_frame = 0; - if ( dbrow[col] ) { - v4l_captures_per_frame = atoi(dbrow[col]); - } else { - v4l_captures_per_frame = config.captures_per_frame; - } -Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); - col++; - const char *method = dbrow[col]; col++; - - int width = atoi(dbrow[col]); col++; - int height = atoi(dbrow[col]); col++; - int colours = atoi(dbrow[col]); col++; - int palette = atoi(dbrow[col]); col++; - Orientation orientation = (Orientation)atoi(dbrow[col]); col++; - unsigned int deinterlacing = atoi(dbrow[col]); col++; - int brightness = atoi(dbrow[col]); col++; - int contrast = atoi(dbrow[col]); col++; - int hue = atoi(dbrow[col]); col++; - int colour = atoi(dbrow[col]); col++; - - const char *event_prefix = dbrow[col]; col++; - const char *label_format = dbrow[col]; col++; - - int label_x = atoi(dbrow[col]); col++; - int label_y = atoi(dbrow[col]); col++; - - int image_buffer_count = atoi(dbrow[col]); col++; - int warmup_count = atoi(dbrow[col]); col++; - int pre_event_count = atoi(dbrow[col]); col++; - int post_event_count = atoi(dbrow[col]); col++; - int stream_replay_buffer = atoi(dbrow[col]); col++; - int alarm_frame_count = atoi(dbrow[col]); col++; - int section_length = atoi(dbrow[col]); col++; - int frame_skip = atoi(dbrow[col]); col++; - int motion_frame_skip = atoi(dbrow[col]); col++; - int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int fps_report_interval = atoi(dbrow[col]); col++; - int ref_blend_perc = atoi(dbrow[col]); col++; - int alarm_ref_blend_perc = atoi(dbrow[col]); col++; - int track_motion = atoi(dbrow[col]); col++; - - int signal_check_colour; - if ( dbrow[col][0] == '#' ) - signal_check_colour = strtol(dbrow[col]+1,0,16); - else - signal_check_colour = strtol(dbrow[col],0,16); - col++; - - int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); - int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); - - int extras = (deinterlacing>>24)&0xff; - - Camera *camera = new LocalCamera( - id, - device, - channel, - format, - v4l_multi_buffer, - v4l_captures_per_frame, - method, - cam_width, - cam_height, - colours, - palette, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE, - extras + Camera *camera = new LocalCamera( + id, + device, + channel, + format, + v4l_multi_buffer, + v4l_captures_per_frame, + method, + cam_width, + cam_height, + colours, + palette, + brightness, + contrast, + hue, + colour, + purpose==CAPTURE, + extras ); - monitors[i] = new Monitor( - id, - name, - function, - enabled, - linked_monitors, - camera, - orientation, - deinterlacing, - event_prefix, - label_format, - Coord( label_x, label_y ), - image_buffer_count, - warmup_count, - pre_event_count, - post_event_count, - stream_replay_buffer, - alarm_frame_count, - section_length, - frame_skip, - motion_frame_skip, - capture_delay, - alarm_capture_delay, - fps_report_interval, - ref_blend_perc, - alarm_ref_blend_perc, - track_motion, - signal_check_colour, - purpose, - 0, - 0 - ); - Zone **zones = 0; - int n_zones = Zone::Load( monitors[i], zones ); - monitors[i]->AddZones( n_zones, zones ); - Debug( 1, "Loaded monitor %d(%s), %d zones", id, name, n_zones ); - } - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - // Yadda yadda - mysql_free_result( result ); + monitors[i] = new Monitor( + id, + name, + function, + enabled, + linked_monitors, + camera, + orientation, + deinterlacing, + event_prefix, + label_format, + Coord( label_x, label_y ), + image_buffer_count, + warmup_count, + pre_event_count, + post_event_count, + stream_replay_buffer, + alarm_frame_count, + section_length, + frame_skip, + motion_frame_skip, + capture_delay, + alarm_capture_delay, + fps_report_interval, + ref_blend_perc, + alarm_ref_blend_perc, + track_motion, + signal_check_colour, + purpose, + 0, + 0 + ); + Zone **zones = 0; + int n_zones = Zone::Load( monitors[i], zones ); + monitors[i]->AddZones( n_zones, zones ); + Debug( 1, "Loaded monitor %d(%s), %d zones", id, name, n_zones ); + } + if ( mysql_errno( &dbconn ) ) + { + Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + // Yadda yadda + mysql_free_result( result ); - return( n_monitors ); + return( n_monitors ); } #endif // ZM_HAS_V4L int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const char *port, const char *path, Monitor **&monitors, Purpose purpose ) { - static char sql[ZM_SQL_MED_BUFSIZ]; - if ( !protocol ) + 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) ); + } + 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 ); + } + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + int n_monitors = mysql_num_rows( result ); + Debug( 1, "Got %d monitors", n_monitors ); + delete[] monitors; + monitors = new Monitor *[n_monitors]; + for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) + { + int col = 0; + + int id = atoi(dbrow[col]); col++; + std::string name = dbrow[col]; col++; + int function = atoi(dbrow[col]); col++; + int enabled = atoi(dbrow[col]); col++; + const char *linked_monitors = dbrow[col]; col++; + + std::string protocol = dbrow[col]; col++; + std::string method = dbrow[col]; col++; + std::string host = dbrow[col]; col++; + std::string port = dbrow[col]; col++; + std::string path = dbrow[col]; col++; + + int width = atoi(dbrow[col]); col++; + int height = atoi(dbrow[col]); col++; + int colours = atoi(dbrow[col]); col++; + /* int palette = atoi(dbrow[col]); */ col++; + Orientation orientation = (Orientation)atoi(dbrow[col]); col++; + unsigned int deinterlacing = atoi(dbrow[col]); col++; + int brightness = atoi(dbrow[col]); col++; + int contrast = atoi(dbrow[col]); col++; + int hue = atoi(dbrow[col]); col++; + int colour = atoi(dbrow[col]); col++; + + std::string event_prefix = dbrow[col]; col++; + std::string label_format = dbrow[col]; col++; + + int label_x = atoi(dbrow[col]); col++; + int label_y = atoi(dbrow[col]); col++; + + int image_buffer_count = atoi(dbrow[col]); col++; + int warmup_count = atoi(dbrow[col]); col++; + int pre_event_count = atoi(dbrow[col]); col++; + int post_event_count = atoi(dbrow[col]); col++; + int stream_replay_buffer = atoi(dbrow[col]); col++; + int alarm_frame_count = atoi(dbrow[col]); col++; + int section_length = atoi(dbrow[col]); col++; + int frame_skip = atoi(dbrow[col]); col++; + int motion_frame_skip = atoi(dbrow[col]); col++; + int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; + int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; + int fps_report_interval = atoi(dbrow[col]); col++; + int ref_blend_perc = atoi(dbrow[col]); col++; + int alarm_ref_blend_perc = atoi(dbrow[col]); col++; + int track_motion = atoi(dbrow[col]); col++; + + + int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); + int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); + + Camera *camera = 0; + if ( protocol == "http" ) { - 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) ); + camera = new RemoteCameraHttp( + id, + method, + host, // Host + port, // Port + path, // Path + cam_width, + cam_height, + colours, + brightness, + contrast, + hue, + colour, + purpose==CAPTURE + ); } +#if HAVE_LIBAVFORMAT + else if ( protocol == "rtsp" ) + { + camera = new RemoteCameraRtsp( + id, + method, + host, // Host + port, // Port + path, // Path + cam_width, + cam_height, + colours, + brightness, + contrast, + hue, + colour, + purpose==CAPTURE + ); + } +#endif // HAVE_LIBAVFORMAT 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 ); - } - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); + Fatal( "Unexpected remote camera protocol '%s'", protocol.c_str() ); } - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - int n_monitors = mysql_num_rows( result ); - Debug( 1, "Got %d monitors", n_monitors ); - delete[] monitors; - monitors = new Monitor *[n_monitors]; - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) - { - int col = 0; + monitors[i] = new Monitor( + id, + name.c_str(), + function, + enabled, + linked_monitors, + camera, + orientation, + deinterlacing, + event_prefix.c_str(), + label_format.c_str(), + Coord( label_x, label_y ), + image_buffer_count, + warmup_count, + pre_event_count, + post_event_count, + stream_replay_buffer, + alarm_frame_count, + section_length, + frame_skip, + motion_frame_skip, + capture_delay, + alarm_capture_delay, + fps_report_interval, + ref_blend_perc, + alarm_ref_blend_perc, + track_motion, + RGB_WHITE, + purpose, + 0, + 0 - int id = atoi(dbrow[col]); col++; - std::string name = dbrow[col]; col++; - int function = atoi(dbrow[col]); col++; - int enabled = atoi(dbrow[col]); col++; - const char *linked_monitors = dbrow[col]; col++; + ); + Zone **zones = 0; + int n_zones = Zone::Load( monitors[i], zones ); + monitors[i]->AddZones( n_zones, zones ); + Debug( 1, "Loaded monitor %d(%s), %d zones", id, name.c_str(), n_zones ); + } + if ( mysql_errno( &dbconn ) ) + { + Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + // Yadda yadda + mysql_free_result( result ); - std::string protocol = dbrow[col]; col++; - std::string method = dbrow[col]; col++; - std::string host = dbrow[col]; col++; - std::string port = dbrow[col]; col++; - std::string path = dbrow[col]; col++; - - int width = atoi(dbrow[col]); col++; - int height = atoi(dbrow[col]); col++; - int colours = atoi(dbrow[col]); col++; - /* int palette = atoi(dbrow[col]); */ col++; - Orientation orientation = (Orientation)atoi(dbrow[col]); col++; - unsigned int deinterlacing = atoi(dbrow[col]); col++; - int brightness = atoi(dbrow[col]); col++; - int contrast = atoi(dbrow[col]); col++; - int hue = atoi(dbrow[col]); col++; - int colour = atoi(dbrow[col]); col++; - - std::string event_prefix = dbrow[col]; col++; - std::string label_format = dbrow[col]; col++; - - int label_x = atoi(dbrow[col]); col++; - int label_y = atoi(dbrow[col]); col++; - - int image_buffer_count = atoi(dbrow[col]); col++; - int warmup_count = atoi(dbrow[col]); col++; - int pre_event_count = atoi(dbrow[col]); col++; - int post_event_count = atoi(dbrow[col]); col++; - int stream_replay_buffer = atoi(dbrow[col]); col++; - int alarm_frame_count = atoi(dbrow[col]); col++; - int section_length = atoi(dbrow[col]); col++; - int frame_skip = atoi(dbrow[col]); col++; - int motion_frame_skip = atoi(dbrow[col]); col++; - int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int fps_report_interval = atoi(dbrow[col]); col++; - int ref_blend_perc = atoi(dbrow[col]); col++; - int alarm_ref_blend_perc = atoi(dbrow[col]); col++; - int track_motion = atoi(dbrow[col]); col++; - - - int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); - int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); - - Camera *camera = 0; - if ( protocol == "http" ) - { - camera = new RemoteCameraHttp( - id, - method, - host, // Host - port, // Port - path, // Path - cam_width, - cam_height, - colours, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE - ); - } -#if HAVE_LIBAVFORMAT - else if ( protocol == "rtsp" ) - { - camera = new RemoteCameraRtsp( - id, - method, - host, // Host - port, // Port - path, // Path - cam_width, - cam_height, - colours, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE - ); - } -#endif // HAVE_LIBAVFORMAT - else - { - Fatal( "Unexpected remote camera protocol '%s'", protocol.c_str() ); - } - - monitors[i] = new Monitor( - id, - name.c_str(), - function, - enabled, - linked_monitors, - camera, - orientation, - deinterlacing, - event_prefix.c_str(), - label_format.c_str(), - Coord( label_x, label_y ), - image_buffer_count, - warmup_count, - pre_event_count, - post_event_count, - stream_replay_buffer, - alarm_frame_count, - section_length, - frame_skip, - motion_frame_skip, - capture_delay, - alarm_capture_delay, - fps_report_interval, - ref_blend_perc, - alarm_ref_blend_perc, - track_motion, - RGB_WHITE, - purpose, - 0, - 0 - - ); - Zone **zones = 0; - int n_zones = Zone::Load( monitors[i], zones ); - monitors[i]->AddZones( n_zones, zones ); - Debug( 1, "Loaded monitor %d(%s), %d zones", id, name.c_str(), n_zones ); - } - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - // Yadda yadda - mysql_free_result( result ); - - return( n_monitors ); + return( n_monitors ); } int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose purpose ) { - 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) ); - } - 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 ); - } - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + 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) ); + } + 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 ); + } + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - int n_monitors = mysql_num_rows( result ); - Debug( 1, "Got %d monitors", n_monitors ); - delete[] monitors; - monitors = new Monitor *[n_monitors]; - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) - { - int col = 0; + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + int n_monitors = mysql_num_rows( result ); + Debug( 1, "Got %d monitors", n_monitors ); + delete[] monitors; + monitors = new Monitor *[n_monitors]; + for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) + { + int col = 0; - int id = atoi(dbrow[col]); col++; - const char *name = dbrow[col]; col++; - int function = atoi(dbrow[col]); col++; - int enabled = atoi(dbrow[col]); col++; - const char *linked_monitors = dbrow[col]; col++; + int id = atoi(dbrow[col]); col++; + const char *name = dbrow[col]; col++; + int function = atoi(dbrow[col]); col++; + int enabled = atoi(dbrow[col]); col++; + const char *linked_monitors = dbrow[col]; col++; - const char *path = dbrow[col]; col++; + const char *path = dbrow[col]; col++; - int width = atoi(dbrow[col]); col++; - int height = atoi(dbrow[col]); col++; - int colours = atoi(dbrow[col]); col++; - /* int palette = atoi(dbrow[col]); */ col++; - Orientation orientation = (Orientation)atoi(dbrow[col]); col++; - unsigned int deinterlacing = atoi(dbrow[col]); col++; - int brightness = atoi(dbrow[col]); col++; - int contrast = atoi(dbrow[col]); col++; - int hue = atoi(dbrow[col]); col++; - int colour = atoi(dbrow[col]); col++; + int width = atoi(dbrow[col]); col++; + int height = atoi(dbrow[col]); col++; + int colours = atoi(dbrow[col]); col++; + /* int palette = atoi(dbrow[col]); */ col++; + Orientation orientation = (Orientation)atoi(dbrow[col]); col++; + unsigned int deinterlacing = atoi(dbrow[col]); col++; + int brightness = atoi(dbrow[col]); col++; + int contrast = atoi(dbrow[col]); col++; + int hue = atoi(dbrow[col]); col++; + int colour = atoi(dbrow[col]); col++; - const char *event_prefix = dbrow[col]; col++; - const char *label_format = dbrow[col]; col++; + const char *event_prefix = dbrow[col]; col++; + const char *label_format = dbrow[col]; col++; - int label_x = atoi(dbrow[col]); col++; - int label_y = atoi(dbrow[col]); col++; + int label_x = atoi(dbrow[col]); col++; + int label_y = atoi(dbrow[col]); col++; - int image_buffer_count = atoi(dbrow[col]); col++; - int warmup_count = atoi(dbrow[col]); col++; - int pre_event_count = atoi(dbrow[col]); col++; - int post_event_count = atoi(dbrow[col]); col++; - int stream_replay_buffer = atoi(dbrow[col]); col++; - int alarm_frame_count = atoi(dbrow[col]); col++; - int section_length = atoi(dbrow[col]); col++; - int frame_skip = atoi(dbrow[col]); col++; - int motion_frame_skip = atoi(dbrow[col]); col++; - int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int fps_report_interval = atoi(dbrow[col]); col++; - int ref_blend_perc = atoi(dbrow[col]); col++; - int alarm_ref_blend_perc = atoi(dbrow[col]); col++; - int track_motion = atoi(dbrow[col]); col++; + int image_buffer_count = atoi(dbrow[col]); col++; + int warmup_count = atoi(dbrow[col]); col++; + int pre_event_count = atoi(dbrow[col]); col++; + int post_event_count = atoi(dbrow[col]); col++; + int stream_replay_buffer = atoi(dbrow[col]); col++; + int alarm_frame_count = atoi(dbrow[col]); col++; + int section_length = atoi(dbrow[col]); col++; + int frame_skip = atoi(dbrow[col]); col++; + int motion_frame_skip = atoi(dbrow[col]); col++; + int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; + int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; + int fps_report_interval = atoi(dbrow[col]); col++; + int ref_blend_perc = atoi(dbrow[col]); col++; + int alarm_ref_blend_perc = atoi(dbrow[col]); col++; + int track_motion = atoi(dbrow[col]); col++; - int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); - int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); + int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); + int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); - Camera *camera = new FileCamera( - id, - path, // File - cam_width, - cam_height, - colours, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE + Camera *camera = new FileCamera( + id, + path, // File + cam_width, + cam_height, + colours, + brightness, + contrast, + hue, + colour, + purpose==CAPTURE ); - monitors[i] = new Monitor( - id, - name, - function, - enabled, - linked_monitors, - camera, - orientation, - deinterlacing, - event_prefix, - label_format, - Coord( label_x, label_y ), - image_buffer_count, - warmup_count, - pre_event_count, - post_event_count, - stream_replay_buffer, - alarm_frame_count, - section_length, - frame_skip, - motion_frame_skip, - capture_delay, - alarm_capture_delay, - fps_report_interval, - ref_blend_perc, - alarm_ref_blend_perc, - track_motion, - RGB_WHITE, - purpose, - 0, - 0 - ); - Zone **zones = 0; - int n_zones = Zone::Load( monitors[i], zones ); - monitors[i]->AddZones( n_zones, zones ); - Debug( 1, "Loaded monitor %d(%s), %d zones", id, name, n_zones ); - } - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - // Yadda yadda - mysql_free_result( result ); + monitors[i] = new Monitor( + id, + name, + function, + enabled, + linked_monitors, + camera, + orientation, + deinterlacing, + event_prefix, + label_format, + Coord( label_x, label_y ), + image_buffer_count, + warmup_count, + pre_event_count, + post_event_count, + stream_replay_buffer, + alarm_frame_count, + section_length, + frame_skip, + motion_frame_skip, + capture_delay, + alarm_capture_delay, + fps_report_interval, + ref_blend_perc, + alarm_ref_blend_perc, + track_motion, + RGB_WHITE, + purpose, + 0, + 0 + ); + Zone **zones = 0; + int n_zones = Zone::Load( monitors[i], zones ); + monitors[i]->AddZones( n_zones, zones ); + Debug( 1, "Loaded monitor %d(%s), %d zones", id, name, n_zones ); + } + if ( mysql_errno( &dbconn ) ) + { + Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + // Yadda yadda + mysql_free_result( result ); - return( n_monitors ); + return( n_monitors ); } #if HAVE_LIBAVFORMAT int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose 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) ); + 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) ); + } + 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 ); + } + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + int n_monitors = mysql_num_rows( result ); + Debug( 1, "Got %d monitors", n_monitors ); + delete[] monitors; + monitors = new Monitor *[n_monitors]; + for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) + { + int col = 0; + + int id = atoi(dbrow[col]); col++; + const char *name = dbrow[col]; col++; + int function = atoi(dbrow[col]); col++; + int enabled = atoi(dbrow[col]); col++; + const char *linked_monitors = dbrow[col]; col++; + + const char *path = dbrow[col]; col++; + const char *method = dbrow[col]; col++; + const char *options = dbrow[col]; col++; + + int width = atoi(dbrow[col]); col++; + int height = atoi(dbrow[col]); col++; + int colours = atoi(dbrow[col]); col++; + /* int palette = atoi(dbrow[col]); */ col++; + Orientation orientation = (Orientation)atoi(dbrow[col]); col++; + unsigned int deinterlacing = atoi(dbrow[col]); col++; + int brightness = atoi(dbrow[col]); col++; + int contrast = atoi(dbrow[col]); col++; + int hue = atoi(dbrow[col]); col++; + int colour = atoi(dbrow[col]); col++; + + const char *event_prefix = dbrow[col]; col++; + const char *label_format = dbrow[col]; col++; + + int label_x = atoi(dbrow[col]); col++; + int label_y = atoi(dbrow[col]); col++; + + int image_buffer_count = atoi(dbrow[col]); col++; + int warmup_count = atoi(dbrow[col]); col++; + int pre_event_count = atoi(dbrow[col]); col++; + int post_event_count = atoi(dbrow[col]); col++; + int stream_replay_buffer = atoi(dbrow[col]); col++; + int alarm_frame_count = atoi(dbrow[col]); col++; + int section_length = atoi(dbrow[col]); col++; + int frame_skip = atoi(dbrow[col]); col++; + int motion_frame_skip = atoi(dbrow[col]); col++; + int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; + int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; + int fps_report_interval = atoi(dbrow[col]); col++; + int ref_blend_perc = atoi(dbrow[col]); col++; + int alarm_ref_blend_perc = atoi(dbrow[col]); col++; + int track_motion = atoi(dbrow[col]); col++; + + int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); + int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); + + Camera *camera = new FfmpegCamera( + id, + path, // File + method, + options, + cam_width, + cam_height, + colours, + brightness, + contrast, + hue, + colour, + purpose==CAPTURE + ); + + monitors[i] = new Monitor( + id, + name, + function, + enabled, + linked_monitors, + camera, + orientation, + deinterlacing, + event_prefix, + label_format, + Coord( label_x, label_y ), + image_buffer_count, + warmup_count, + pre_event_count, + post_event_count, + stream_replay_buffer, + alarm_frame_count, + section_length, + frame_skip, + motion_frame_skip, + capture_delay, + alarm_capture_delay, + fps_report_interval, + ref_blend_perc, + alarm_ref_blend_perc, + track_motion, + RGB_WHITE, + purpose, + 0, + 0 + ); + Zone **zones = 0; + int n_zones = Zone::Load( monitors[i], zones ); + monitors[i]->AddZones( n_zones, zones ); + Debug( 1, "Loaded monitor %d(%s), %d zones", id, name, n_zones ); + } + if ( mysql_errno( &dbconn ) ) + { + Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + // Yadda yadda + mysql_free_result( result ); + + return( n_monitors ); +} +#endif // HAVE_LIBAVFORMAT + +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 ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + int n_monitors = mysql_num_rows( result ); + Debug( 1, "Got %d monitors", n_monitors ); + Monitor *monitor = 0; + for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) + { + int col = 0; + + int id = atoi(dbrow[col]); col++; + std::string name = dbrow[col]; col++; + std::string type = dbrow[col]; col++; + int function = atoi(dbrow[col]); col++; + int enabled = atoi(dbrow[col]); col++; + std::string linked_monitors = dbrow[col]; col++; + + std::string device = dbrow[col]; col++; + int channel = atoi(dbrow[col]); col++; + int format = atoi(dbrow[col]); col++; + + bool v4l_multi_buffer; + if ( dbrow[col] ) { + if (*dbrow[col] == '0' ) { + v4l_multi_buffer = false; + } else if ( *dbrow[col] == '1' ) { + v4l_multi_buffer = true; + } + } else { + v4l_multi_buffer = config.v4l_multi_buffer; } + col++; + + int v4l_captures_per_frame = 0; + if ( dbrow[col] ) { + v4l_captures_per_frame = atoi(dbrow[col]); + } else { + v4l_captures_per_frame = config.captures_per_frame; + } + Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); + col++; + + std::string protocol = dbrow[col]; col++; + std::string method = dbrow[col]; col++; + std::string host = dbrow[col]; col++; + std::string port = dbrow[col]; col++; + std::string path = dbrow[col]; col++; + std::string options = dbrow[col]; col++; + std::string user = dbrow[col]; col++; + std::string pass = dbrow[col]; col++; + + int width = atoi(dbrow[col]); col++; + int height = atoi(dbrow[col]); col++; + int colours = atoi(dbrow[col]); col++; + int palette = atoi(dbrow[col]); col++; + Orientation orientation = (Orientation)atoi(dbrow[col]); col++; + unsigned int deinterlacing = atoi(dbrow[col]); col++; + int brightness = atoi(dbrow[col]); col++; + int contrast = atoi(dbrow[col]); col++; + int hue = atoi(dbrow[col]); col++; + int colour = atoi(dbrow[col]); col++; + + std::string event_prefix = dbrow[col]; col++; + std::string label_format = dbrow[col]; col++; + + int label_x = atoi(dbrow[col]); col++; + int label_y = atoi(dbrow[col]); col++; + + int image_buffer_count = atoi(dbrow[col]); col++; + int warmup_count = atoi(dbrow[col]); col++; + int pre_event_count = atoi(dbrow[col]); col++; + int post_event_count = atoi(dbrow[col]); col++; + int stream_replay_buffer = atoi(dbrow[col]); col++; + int alarm_frame_count = atoi(dbrow[col]); col++; + int section_length = atoi(dbrow[col]); col++; + int frame_skip = atoi(dbrow[col]); col++; + int motion_frame_skip = atoi(dbrow[col]); col++; + int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; + int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; + int fps_report_interval = atoi(dbrow[col]); col++; + int ref_blend_perc = atoi(dbrow[col]); col++; + int alarm_ref_blend_perc = atoi(dbrow[col]); col++; + int track_motion = atoi(dbrow[col]); col++; + + int signal_check_colour; + if ( dbrow[col][0] == '#' ) + signal_check_colour = strtol(dbrow[col]+1,0,16); else + signal_check_colour = strtol(dbrow[col],0,16); + + int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); + int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); + + int extras = (deinterlacing>>24)&0xff; + + Camera *camera = 0; + if ( type == "Local" ) { - 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 ); +#if ZM_HAS_V4L + camera = new LocalCamera( + id, + device.c_str(), + channel, + format, + v4l_multi_buffer, + v4l_captures_per_frame, + method, + cam_width, + cam_height, + colours, + palette, + brightness, + contrast, + hue, + colour, + purpose==CAPTURE, + extras + ); +#else // ZM_HAS_V4L + Fatal( "You must have video4linux libraries and headers installed to use local analog or USB cameras for monitor %d", id ); +#endif // ZM_HAS_V4L } - if ( mysql_query( &dbconn, sql ) ) + else if ( type == "Remote" ) { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - int n_monitors = mysql_num_rows( result ); - Debug( 1, "Got %d monitors", n_monitors ); - delete[] monitors; - monitors = new Monitor *[n_monitors]; - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) - { - int col = 0; - - int id = atoi(dbrow[col]); col++; - const char *name = dbrow[col]; col++; - int function = atoi(dbrow[col]); col++; - int enabled = atoi(dbrow[col]); col++; - const char *linked_monitors = dbrow[col]; col++; - - const char *path = dbrow[col]; col++; - const char *method = dbrow[col]; col++; - const char *options = dbrow[col]; col++; - - int width = atoi(dbrow[col]); col++; - int height = atoi(dbrow[col]); col++; - int colours = atoi(dbrow[col]); col++; - /* int palette = atoi(dbrow[col]); */ col++; - Orientation orientation = (Orientation)atoi(dbrow[col]); col++; - unsigned int deinterlacing = atoi(dbrow[col]); col++; - int brightness = atoi(dbrow[col]); col++; - int contrast = atoi(dbrow[col]); col++; - int hue = atoi(dbrow[col]); col++; - int colour = atoi(dbrow[col]); col++; - - const char *event_prefix = dbrow[col]; col++; - const char *label_format = dbrow[col]; col++; - - int label_x = atoi(dbrow[col]); col++; - int label_y = atoi(dbrow[col]); col++; - - int image_buffer_count = atoi(dbrow[col]); col++; - int warmup_count = atoi(dbrow[col]); col++; - int pre_event_count = atoi(dbrow[col]); col++; - int post_event_count = atoi(dbrow[col]); col++; - int stream_replay_buffer = atoi(dbrow[col]); col++; - int alarm_frame_count = atoi(dbrow[col]); col++; - int section_length = atoi(dbrow[col]); col++; - int frame_skip = atoi(dbrow[col]); col++; - int motion_frame_skip = atoi(dbrow[col]); col++; - int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int fps_report_interval = atoi(dbrow[col]); col++; - int ref_blend_perc = atoi(dbrow[col]); col++; - int alarm_ref_blend_perc = atoi(dbrow[col]); col++; - int track_motion = atoi(dbrow[col]); col++; - - int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); - int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); - - Camera *camera = new FfmpegCamera( + if ( protocol == "http" ) + { + camera = new RemoteCameraHttp( id, - path, // File - method, - options, + method.c_str(), + host.c_str(), + port.c_str(), + path.c_str(), cam_width, cam_height, colours, @@ -2434,578 +2638,374 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose hue, colour, purpose==CAPTURE - ); - - monitors[i] = new Monitor( + ); + } + else if ( protocol == "rtsp" ) + { +#if HAVE_LIBAVFORMAT + camera = new RemoteCameraRtsp( id, - name, - function, - enabled, - linked_monitors, - camera, - orientation, - deinterlacing, - event_prefix, - label_format, - Coord( label_x, label_y ), - image_buffer_count, - warmup_count, - pre_event_count, - post_event_count, - stream_replay_buffer, - alarm_frame_count, - section_length, - frame_skip, - motion_frame_skip, - capture_delay, - alarm_capture_delay, - fps_report_interval, - ref_blend_perc, - alarm_ref_blend_perc, - track_motion, - RGB_WHITE, - purpose, - 0, - 0 - ); - Zone **zones = 0; - int n_zones = Zone::Load( monitors[i], zones ); - monitors[i]->AddZones( n_zones, zones ); - Debug( 1, "Loaded monitor %d(%s), %d zones", id, name, n_zones ); - } - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - // Yadda yadda - mysql_free_result( result ); - - return( n_monitors ); -} -#endif // HAVE_LIBAVFORMAT - -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 ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - int n_monitors = mysql_num_rows( result ); - Debug( 1, "Got %d monitors", n_monitors ); - Monitor *monitor = 0; - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) - { - int col = 0; - - int id = atoi(dbrow[col]); col++; - std::string name = dbrow[col]; col++; - std::string type = dbrow[col]; col++; - int function = atoi(dbrow[col]); col++; - int enabled = atoi(dbrow[col]); col++; - std::string linked_monitors = dbrow[col]; col++; - - std::string device = dbrow[col]; col++; - int channel = atoi(dbrow[col]); col++; - int format = atoi(dbrow[col]); col++; - - bool v4l_multi_buffer; - if ( dbrow[col] ) { - if (*dbrow[col] == '0' ) { - v4l_multi_buffer = false; - } else if ( *dbrow[col] == '1' ) { - v4l_multi_buffer = true; - } - } else { - v4l_multi_buffer = config.v4l_multi_buffer; - } - col++; - - int v4l_captures_per_frame = 0; - if ( dbrow[col] ) { - v4l_captures_per_frame = atoi(dbrow[col]); - } else { - v4l_captures_per_frame = config.captures_per_frame; - } -Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); - col++; - - std::string protocol = dbrow[col]; col++; - std::string method = dbrow[col]; col++; - std::string host = dbrow[col]; col++; - std::string port = dbrow[col]; col++; - std::string path = dbrow[col]; col++; - std::string options = dbrow[col]; col++; - std::string user = dbrow[col]; col++; - std::string pass = dbrow[col]; col++; - - int width = atoi(dbrow[col]); col++; - int height = atoi(dbrow[col]); col++; - int colours = atoi(dbrow[col]); col++; - int palette = atoi(dbrow[col]); col++; - Orientation orientation = (Orientation)atoi(dbrow[col]); col++; - unsigned int deinterlacing = atoi(dbrow[col]); col++; - int brightness = atoi(dbrow[col]); col++; - int contrast = atoi(dbrow[col]); col++; - int hue = atoi(dbrow[col]); col++; - int colour = atoi(dbrow[col]); col++; - - std::string event_prefix = dbrow[col]; col++; - std::string label_format = dbrow[col]; col++; - - int label_x = atoi(dbrow[col]); col++; - int label_y = atoi(dbrow[col]); col++; - - int image_buffer_count = atoi(dbrow[col]); col++; - int warmup_count = atoi(dbrow[col]); col++; - int pre_event_count = atoi(dbrow[col]); col++; - int post_event_count = atoi(dbrow[col]); col++; - int stream_replay_buffer = atoi(dbrow[col]); col++; - int alarm_frame_count = atoi(dbrow[col]); col++; - int section_length = atoi(dbrow[col]); col++; - int frame_skip = atoi(dbrow[col]); col++; - int motion_frame_skip = atoi(dbrow[col]); col++; - int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int fps_report_interval = atoi(dbrow[col]); col++; - int ref_blend_perc = atoi(dbrow[col]); col++; - int alarm_ref_blend_perc = atoi(dbrow[col]); col++; - int track_motion = atoi(dbrow[col]); col++; - - int signal_check_colour; - if ( dbrow[col][0] == '#' ) - signal_check_colour = strtol(dbrow[col]+1,0,16); - else - signal_check_colour = strtol(dbrow[col],0,16); - - int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); - int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); - - int extras = (deinterlacing>>24)&0xff; - - Camera *camera = 0; - if ( type == "Local" ) - { -#if ZM_HAS_V4L - camera = new LocalCamera( - id, - device.c_str(), - channel, - format, - v4l_multi_buffer, - v4l_captures_per_frame, - method, - cam_width, - cam_height, - colours, - palette, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE, - extras - ); -#else // ZM_HAS_V4L - Fatal( "You must have video4linux libraries and headers installed to use local analog or USB cameras for monitor %d", id ); -#endif // ZM_HAS_V4L - } - else if ( type == "Remote" ) - { - if ( protocol == "http" ) - { - camera = new RemoteCameraHttp( - id, - method.c_str(), - host.c_str(), - port.c_str(), - path.c_str(), - cam_width, - cam_height, - colours, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE - ); - } - else if ( protocol == "rtsp" ) - { -#if HAVE_LIBAVFORMAT - camera = new RemoteCameraRtsp( - id, - method.c_str(), - host.c_str(), - port.c_str(), - path.c_str(), - cam_width, - cam_height, - colours, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE - ); -#else // HAVE_LIBAVFORMAT - Fatal( "You must have ffmpeg libraries installed to use remote camera protocol '%s' for monitor %d", protocol.c_str(), id ); -#endif // HAVE_LIBAVFORMAT - } - else - { - Fatal( "Unexpected remote camera protocol '%s' for monitor %d", protocol.c_str(), id ); - } - } - else if ( type == "File" ) - { - camera = new FileCamera( - id, - path.c_str(), - cam_width, - cam_height, - colours, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE - ); - } - else if ( type == "Ffmpeg" ) - { -#if HAVE_LIBAVFORMAT - camera = new FfmpegCamera( - id, - path.c_str(), - method, - options, - cam_width, - cam_height, - colours, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE + method.c_str(), + host.c_str(), + port.c_str(), + path.c_str(), + cam_width, + cam_height, + colours, + brightness, + contrast, + hue, + colour, + purpose==CAPTURE ); #else // HAVE_LIBAVFORMAT - Fatal( "You must have ffmpeg libraries installed to use ffmpeg cameras for monitor %d", id ); + Fatal( "You must have ffmpeg libraries installed to use remote camera protocol '%s' for monitor %d", protocol.c_str(), id ); #endif // HAVE_LIBAVFORMAT - } - else if (type == "Libvlc") - { + } + else + { + Fatal( "Unexpected remote camera protocol '%s' for monitor %d", protocol.c_str(), id ); + } + } + else if ( type == "File" ) + { + camera = new FileCamera( + id, + path.c_str(), + cam_width, + cam_height, + colours, + brightness, + contrast, + hue, + colour, + purpose==CAPTURE + ); + } + else if ( type == "Ffmpeg" ) + { +#if HAVE_LIBAVFORMAT + camera = new FfmpegCamera( + id, + path.c_str(), + method, + options, + cam_width, + cam_height, + colours, + brightness, + contrast, + hue, + colour, + purpose==CAPTURE + ); +#else // HAVE_LIBAVFORMAT + Fatal( "You must have ffmpeg libraries installed to use ffmpeg cameras for monitor %d", id ); +#endif // HAVE_LIBAVFORMAT + } + else if (type == "Libvlc") + { #if HAVE_LIBVLC - camera = new LibvlcCamera( - id, - path.c_str(), - method, - options, - cam_width, - cam_height, - colours, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE - ); + camera = new LibvlcCamera( + id, + path.c_str(), + method, + options, + cam_width, + cam_height, + colours, + brightness, + contrast, + hue, + colour, + purpose==CAPTURE + ); #else // HAVE_LIBVLC - Fatal( "You must have vlc libraries installed to use vlc cameras for monitor %d", id ); + Fatal( "You must have vlc libraries installed to use vlc cameras for monitor %d", id ); #endif // HAVE_LIBVLC - } - else if ( type == "cURL" ) - { -#if HAVE_LIBCURL - camera = new cURLCamera( - id, - path.c_str(), - user.c_str(), - pass.c_str(), - cam_width, - cam_height, - colours, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE - ); -#else // HAVE_LIBCURL - Fatal( "You must have libcurl installed to use ffmpeg cameras for monitor %d", id ); -#endif // HAVE_LIBCURL - } - else - { - Fatal( "Bogus monitor type '%s' for monitor %d", type.c_str(), id ); - } - monitor = new Monitor( - id, - name.c_str(), - function, - enabled, - linked_monitors.c_str(), - camera, - orientation, - deinterlacing, - event_prefix.c_str(), - label_format.c_str(), - Coord( label_x, label_y ), - image_buffer_count, - warmup_count, - pre_event_count, - post_event_count, - stream_replay_buffer, - alarm_frame_count, - section_length, - frame_skip, - motion_frame_skip, - capture_delay, - alarm_capture_delay, - fps_report_interval, - ref_blend_perc, - alarm_ref_blend_perc, - track_motion, - signal_check_colour, - purpose, - 0, - 0 - - ); - - int n_zones = 0; - if ( load_zones ) - { - Zone **zones = 0; - n_zones = Zone::Load( monitor, zones ); - monitor->AddZones( n_zones, zones ); - } - Debug( 1, "Loaded monitor %d(%s), %d zones", id, name.c_str(), n_zones ); } - if ( mysql_errno( &dbconn ) ) + else if ( type == "cURL" ) { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); +#if HAVE_LIBCURL + camera = new cURLCamera( + id, + path.c_str(), + user.c_str(), + pass.c_str(), + cam_width, + cam_height, + colours, + brightness, + contrast, + hue, + colour, + purpose==CAPTURE + ); +#else // HAVE_LIBCURL + Fatal( "You must have libcurl installed to use ffmpeg cameras for monitor %d", id ); +#endif // HAVE_LIBCURL } - // Yadda yadda - mysql_free_result( result ); + else + { + Fatal( "Bogus monitor type '%s' for monitor %d", type.c_str(), id ); + } + monitor = new Monitor( + id, + name.c_str(), + function, + enabled, + linked_monitors.c_str(), + camera, + orientation, + deinterlacing, + event_prefix.c_str(), + label_format.c_str(), + Coord( label_x, label_y ), + image_buffer_count, + warmup_count, + pre_event_count, + post_event_count, + stream_replay_buffer, + alarm_frame_count, + section_length, + frame_skip, + motion_frame_skip, + capture_delay, + alarm_capture_delay, + fps_report_interval, + ref_blend_perc, + alarm_ref_blend_perc, + track_motion, + signal_check_colour, + purpose, + 0, + 0 - return( monitor ); + ); + + int n_zones = 0; + if ( load_zones ) + { + Zone **zones = 0; + n_zones = Zone::Load( monitor, zones ); + monitor->AddZones( n_zones, zones ); + } + Debug( 1, "Loaded monitor %d(%s), %d zones", id, name.c_str(), n_zones ); + } + if ( mysql_errno( &dbconn ) ) + { + Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + // Yadda yadda + mysql_free_result( result ); + + return( monitor ); } int Monitor::Capture() { - static int FirstCapture = 1; - int captureResult; - - int index = image_count%image_buffer_count; - Image* capture_image = image_buffer[index].image; - - if ( (deinterlacing & 0xff) == 4) { - if ( FirstCapture != 1 ) { - /* Copy the next image into the shared memory */ - capture_image->CopyBuffer(*(next_buffer.image)); - } - - /* Capture a new next image */ - captureResult = camera->Capture(*(next_buffer.image)); - - if ( FirstCapture ) { - FirstCapture = 0; - return 0; - } - - } else { - /* Capture directly into image buffer, avoiding the need to memcpy() */ - captureResult = camera->Capture(*capture_image); - } - - if ( captureResult != 0 ) + static int FirstCapture = 1; + int captureResult; + + int index = image_count%image_buffer_count; + Image* capture_image = image_buffer[index].image; + + if ( (deinterlacing & 0xff) == 4) { + if ( FirstCapture != 1 ) { + /* Copy the next image into the shared memory */ + capture_image->CopyBuffer(*(next_buffer.image)); + } + + /* Capture a new next image */ + captureResult = camera->Capture(*(next_buffer.image)); + + if ( FirstCapture ) { + FirstCapture = 0; + return 0; + } + + } else { + /* Capture directly into image buffer, avoiding the need to memcpy() */ + captureResult = camera->Capture(*capture_image); + } + + if ( captureResult != 0 ) + { + // Unable to capture image for temporary reason + // Fake a signal loss image + Rgb signalcolor; + signalcolor = rgb_convert(signal_check_colour, ZM_SUBPIX_ORDER_BGR); /* HTML colour code is actually BGR in memory, we want RGB */ + capture_image->Fill(signalcolor); + captureResult = 0; + } else { + captureResult = 1; + } + + if ( captureResult == 1 ) + { + + /* Deinterlacing */ + if ( (deinterlacing & 0xff) == 1 ) { + capture_image->Deinterlace_Discard(); + } else if ( (deinterlacing & 0xff) == 2 ) { + capture_image->Deinterlace_Linear(); + } else if ( (deinterlacing & 0xff) == 3 ) { + capture_image->Deinterlace_Blend(); + } else if ( (deinterlacing & 0xff) == 4 ) { + capture_image->Deinterlace_4Field( next_buffer.image, (deinterlacing>>8)&0xff ); + } else if ( (deinterlacing & 0xff) == 5 ) { + capture_image->Deinterlace_Blend_CustomRatio( (deinterlacing>>8)&0xff ); + } + + + if ( orientation != ROTATE_0 ) { - // Unable to capture image for temporary reason - // Fake a signal loss image - Rgb signalcolor; - signalcolor = rgb_convert(signal_check_colour, ZM_SUBPIX_ORDER_BGR); /* HTML colour code is actually BGR in memory, we want RGB */ - capture_image->Fill(signalcolor); - captureResult = 0; - } else { - captureResult = 1; + switch ( orientation ) + { + case ROTATE_0 : + { + // No action required + break; + } + case ROTATE_90 : + case ROTATE_180 : + case ROTATE_270 : + { + capture_image->Rotate( (orientation-1)*90 ); + break; + } + case FLIP_HORI : + case FLIP_VERT : + { + capture_image->Flip( orientation==FLIP_HORI ); + break; + } + } } - - if ( captureResult == 1 ) + + } + if ( true ) { + + if ( capture_image->Size() != camera->ImageSize() ) { - - /* Deinterlacing */ - if ( (deinterlacing & 0xff) == 1 ) { - capture_image->Deinterlace_Discard(); - } else if ( (deinterlacing & 0xff) == 2 ) { - capture_image->Deinterlace_Linear(); - } else if ( (deinterlacing & 0xff) == 3 ) { - capture_image->Deinterlace_Blend(); - } else if ( (deinterlacing & 0xff) == 4 ) { - capture_image->Deinterlace_4Field( next_buffer.image, (deinterlacing>>8)&0xff ); - } else if ( (deinterlacing & 0xff) == 5 ) { - capture_image->Deinterlace_Blend_CustomRatio( (deinterlacing>>8)&0xff ); - } - - - if ( orientation != ROTATE_0 ) - { - switch ( orientation ) - { - case ROTATE_0 : - { - // No action required - break; - } - case ROTATE_90 : - case ROTATE_180 : - case ROTATE_270 : - { - capture_image->Rotate( (orientation-1)*90 ); - break; - } - case FLIP_HORI : - case FLIP_VERT : - { - capture_image->Flip( orientation==FLIP_HORI ); - break; - } - } - } - + Error( "Captured image %d does not match expected size %d check width, height and colour depth",capture_image->Size(),camera->ImageSize() ); + return( -1 ); } - if ( true ) { - if ( capture_image->Size() != camera->ImageSize() ) - { - Error( "Captured image %d does not match expected size %d check width, height and colour depth",capture_image->Size(),camera->ImageSize() ); - return( -1 ); - } - - if ( ((unsigned int)index == shared_data->last_read_index) && (function > MONITOR) ) - { - Warning( "Buffer overrun at index %d, image %d, slow down capture, speed up analysis or increase ring buffer size", index, image_count ); - time_t now = time(0); - double approxFps = double(image_buffer_count)/double(now-image_buffer[index].timestamp->tv_sec); - time_t last_read_delta = now - shared_data->last_read_time; - if ( last_read_delta > (image_buffer_count/approxFps) ) - { - Warning( "Last image read from shared memory %ld seconds ago, zma may have gone away", last_read_delta ) - shared_data->last_read_index = image_buffer_count; - } - } - - gettimeofday( image_buffer[index].timestamp, NULL ); - if ( config.timestamp_on_capture ) - { - TimestampImage( capture_image, image_buffer[index].timestamp ); - } - shared_data->signal = CheckSignal(capture_image); - shared_data->last_write_index = index; - shared_data->last_write_time = image_buffer[index].timestamp->tv_sec; - - image_count++; - - if ( image_count && fps_report_interval && !(image_count%fps_report_interval) ) - { - time_t now = image_buffer[index].timestamp->tv_sec; - fps = double(fps_report_interval)/(now-last_fps_time); - //Info( "%d -> %d -> %d", fps_report_interval, now, last_fps_time ); - //Info( "%d -> %d -> %lf -> %lf", now-last_fps_time, fps_report_interval/(now-last_fps_time), double(fps_report_interval)/(now-last_fps_time), fps ); - Info( "%s: %d - Capturing at %.2lf fps", name, image_count, fps ); - last_fps_time = now; - } - - if ( shared_data->action & GET_SETTINGS ) - { - shared_data->brightness = camera->Brightness(); - shared_data->hue = camera->Hue(); - shared_data->colour = camera->Colour(); - shared_data->contrast = camera->Contrast(); - shared_data->action &= ~GET_SETTINGS; - } - if ( shared_data->action & SET_SETTINGS ) - { - camera->Brightness( shared_data->brightness ); - camera->Hue( shared_data->hue ); - camera->Colour( shared_data->colour ); - camera->Contrast( shared_data->contrast ); - shared_data->action &= ~SET_SETTINGS; - } - return( 0 ); + if ( ((unsigned int)index == shared_data->last_read_index) && (function > MONITOR) ) + { + Warning( "Buffer overrun at index %d, image %d, slow down capture, speed up analysis or increase ring buffer size", index, image_count ); + time_t now = time(0); + double approxFps = double(image_buffer_count)/double(now-image_buffer[index].timestamp->tv_sec); + time_t last_read_delta = now - shared_data->last_read_time; + if ( last_read_delta > (image_buffer_count/approxFps) ) + { + Warning( "Last image read from shared memory %ld seconds ago, zma may have gone away", last_read_delta ) + shared_data->last_read_index = image_buffer_count; + } } - shared_data->signal = false; - return( -1 ); + + gettimeofday( image_buffer[index].timestamp, NULL ); + if ( config.timestamp_on_capture ) + { + TimestampImage( capture_image, image_buffer[index].timestamp ); + } + shared_data->signal = CheckSignal(capture_image); + shared_data->last_write_index = index; + shared_data->last_write_time = image_buffer[index].timestamp->tv_sec; + + image_count++; + + if ( image_count && fps_report_interval && !(image_count%fps_report_interval) ) + { + time_t now = image_buffer[index].timestamp->tv_sec; + fps = double(fps_report_interval)/(now-last_fps_time); + //Info( "%d -> %d -> %d", fps_report_interval, now, last_fps_time ); + //Info( "%d -> %d -> %lf -> %lf", now-last_fps_time, fps_report_interval/(now-last_fps_time), double(fps_report_interval)/(now-last_fps_time), fps ); + Info( "%s: %d - Capturing at %.2lf fps", name, image_count, fps ); + last_fps_time = now; + } + + if ( shared_data->action & GET_SETTINGS ) + { + shared_data->brightness = camera->Brightness(); + shared_data->hue = camera->Hue(); + shared_data->colour = camera->Colour(); + shared_data->contrast = camera->Contrast(); + shared_data->action &= ~GET_SETTINGS; + } + if ( shared_data->action & SET_SETTINGS ) + { + camera->Brightness( shared_data->brightness ); + camera->Hue( shared_data->hue ); + camera->Colour( shared_data->colour ); + camera->Contrast( shared_data->contrast ); + shared_data->action &= ~SET_SETTINGS; + } + return( 0 ); + } + shared_data->signal = false; + return( -1 ); } void Monitor::TimestampImage( Image *ts_image, const struct timeval *ts_time ) const { - if ( label_format[0] ) - { - // Expand the strftime macros first - char label_time_text[256]; - strftime( label_time_text, sizeof(label_time_text), label_format, localtime( &ts_time->tv_sec ) ); + if ( label_format[0] ) + { + // Expand the strftime macros first + char label_time_text[256]; + strftime( label_time_text, sizeof(label_time_text), label_format, localtime( &ts_time->tv_sec ) ); - char label_text[1024]; - const char *s_ptr = label_time_text; - char *d_ptr = label_text; - while ( *s_ptr && ((d_ptr-label_text) < (unsigned int)sizeof(label_text)) ) + char label_text[1024]; + const char *s_ptr = label_time_text; + char *d_ptr = label_text; + while ( *s_ptr && ((d_ptr-label_text) < (unsigned int)sizeof(label_text)) ) + { + if ( *s_ptr == '%' ) + { + bool found_macro = false; + switch ( *(s_ptr+1) ) { - if ( *s_ptr == '%' ) - { - bool found_macro = false; - switch ( *(s_ptr+1) ) - { - case 'N' : - d_ptr += snprintf( d_ptr, sizeof(label_text)-(d_ptr-label_text), "%s", name ); - found_macro = true; - break; - case 'Q' : - d_ptr += snprintf( d_ptr, sizeof(label_text)-(d_ptr-label_text), "%s", trigger_data->trigger_showtext ); - found_macro = true; - break; - case 'f' : - d_ptr += snprintf( d_ptr, sizeof(label_text)-(d_ptr-label_text), "%02ld", ts_time->tv_usec/10000 ); - found_macro = true; - break; - } - if ( found_macro ) - { - s_ptr += 2; - continue; - } - } - *d_ptr++ = *s_ptr++; + case 'N' : + d_ptr += snprintf( d_ptr, sizeof(label_text)-(d_ptr-label_text), "%s", name ); + found_macro = true; + break; + case 'Q' : + d_ptr += snprintf( d_ptr, sizeof(label_text)-(d_ptr-label_text), "%s", trigger_data->trigger_showtext ); + found_macro = true; + break; + case 'f' : + d_ptr += snprintf( d_ptr, sizeof(label_text)-(d_ptr-label_text), "%02ld", ts_time->tv_usec/10000 ); + found_macro = true; + break; } - *d_ptr = '\0'; - ts_image->Annotate( label_text, label_coord ); + if ( found_macro ) + { + s_ptr += 2; + continue; + } + } + *d_ptr++ = *s_ptr++; } + *d_ptr = '\0'; + ts_image->Annotate( label_text, label_coord ); + } } bool Monitor::closeEvent() { - if ( event ) + if ( event ) + { + if ( function == RECORD || function == MOCORD ) { - if ( function == RECORD || function == MOCORD ) - { - gettimeofday( &(event->EndTime()), NULL ); - } - delete event; - event = 0; - return( true ); + gettimeofday( &(event->EndTime()), NULL ); } - return( false ); + delete event; + event = 0; + return( true ); + } + return( false ); } //----------------------------------------- @@ -3039,153 +3039,153 @@ bool Monitor::closeEvent() */ /* -bool Monitor::OurCheckAlarms( Zone *zone, const Image *pImage ) + bool Monitor::OurCheckAlarms( Zone *zone, const Image *pImage ) + { + Info("Entering OurCheckAlarms >>>>>>>>>>>>>>>>>>>>>>>>>>>>"); + unsigned char black_thr = 23; + int min_alarm_score = 10; + int max_alarm_score = 99; +//bool alarm = false; +unsigned int score; +Polygon zone_polygon = zone->GetPolygon(); +Info("Got polygon of a zone. It has %d vertices.", zone_polygon.getNumCoords()); + +zone->ResetStats(); +Info("ResetStats done."); + +if ( !zone->CheckOverloadCount() ) { - Info("Entering OurCheckAlarms >>>>>>>>>>>>>>>>>>>>>>>>>>>>"); - unsigned char black_thr = 23; - int min_alarm_score = 10; - int max_alarm_score = 99; - //bool alarm = false; - unsigned int score; - Polygon zone_polygon = zone->GetPolygon(); - Info("Got polygon of a zone. It has %d vertices.", zone_polygon.getNumCoords()); +Info("CheckOverloadCount() return false, we'll return false."); +return( false ); +} - zone->ResetStats(); - Info("ResetStats done."); +Image *pMaskImage = new Image(pImage->Width(), pImage->Height(), ZM_COLOUR_GRAY8, pImage->SubpixelOrder()); +Info("Mask image created."); - if ( !zone->CheckOverloadCount() ) - { - Info("CheckOverloadCount() return false, we'll return false."); - return( false ); - } +pMaskImage->Fill(BLACK); +Info("Mask image filled with BLACK."); +if (pImage->Colours() == ZM_COLOUR_GRAY8) +{ +Info("Analysed image is not colored! Set score = 0."); +score = 0; +} +else +{ +Info("Start processing image."); +//Process image +unsigned char *buffer = (unsigned char*)pImage->Buffer(); +unsigned char *mask_buffer = (unsigned char*)pMaskImage->Buffer(); - Image *pMaskImage = new Image(pImage->Width(), pImage->Height(), ZM_COLOUR_GRAY8, pImage->SubpixelOrder()); - Info("Mask image created."); - - pMaskImage->Fill(BLACK); - Info("Mask image filled with BLACK."); - if (pImage->Colours() == ZM_COLOUR_GRAY8) - { - Info("Analysed image is not colored! Set score = 0."); - score = 0; - } - else - { - Info("Start processing image."); - //Process image - unsigned char *buffer = (unsigned char*)pImage->Buffer(); - unsigned char *mask_buffer = (unsigned char*)pMaskImage->Buffer(); - - int black_pixels_count = 0; - Info("Loop for black pixels counting and mask filling."); - while (buffer < (pImage->Buffer() + pImage->Size())) - { - if ( (RED(buffer) < black_thr) && (GREEN(buffer) < black_thr) && (BLUE(buffer) < black_thr) ) - { - *mask_buffer = WHITE; - black_pixels_count++; - } - buffer += pImage->Colours(); - mask_buffer++; - } +int black_pixels_count = 0; +Info("Loop for black pixels counting and mask filling."); +while (buffer < (pImage->Buffer() + pImage->Size())) +{ +if ( (RED(buffer) < black_thr) && (GREEN(buffer) < black_thr) && (BLUE(buffer) < black_thr) ) +{ + *mask_buffer = WHITE; + black_pixels_count++; + } + buffer += pImage->Colours(); + mask_buffer++; + } - if ( !black_pixels_count ) - { - delete pMaskImage; - return( false ); - } - score = (100*black_pixels_count)/zone_polygon.Area(); - Info("Number of black pixels is %d, zone polygon area is %d, score is %d", black_pixels_count, zone_polygon.Area(), score); + if ( !black_pixels_count ) + { + delete pMaskImage; + return( false ); + } + score = (100*black_pixels_count)/zone_polygon.Area(); + Info("Number of black pixels is %d, zone polygon area is %d, score is %d", black_pixels_count, zone_polygon.Area(), score); - if ( min_alarm_score && ( score < min_alarm_score) ) - { - delete pMaskImage; - return( false ); - } - if ( max_alarm_score && (score > max_alarm_score) ) - { - zone->SetOverloadCount(zone->GetOverloadFrames()); - delete pMaskImage; - return( false ); - } - } + if ( min_alarm_score && ( score < min_alarm_score) ) + { + delete pMaskImage; + return( false ); + } + if ( max_alarm_score && (score > max_alarm_score) ) + { + zone->SetOverloadCount(zone->GetOverloadFrames()); + delete pMaskImage; + return( false ); + } + } - zone->SetScore(score); - Info("Score have been set in zone."); - //Get mask - Rgb alarm_colour = RGB_RED; - Image *tempImage = pMaskImage->HighlightEdges(alarm_colour, &zone_polygon.Extent() ); - Info("After HighlightEdges"); +zone->SetScore(score); +Info("Score have been set in zone."); +//Get mask +Rgb alarm_colour = RGB_RED; +Image *tempImage = pMaskImage->HighlightEdges(alarm_colour, &zone_polygon.Extent() ); +Info("After HighlightEdges"); - zone->SetAlarmImage(tempImage); - Info("After SetAlarmImage"); - delete pMaskImage; - Info("After Delete pMaskImage"); - delete tempImage; +zone->SetAlarmImage(tempImage); +Info("After SetAlarmImage"); +delete pMaskImage; +Info("After Delete pMaskImage"); +delete tempImage; - Info("Leaving OurCheckAlarms >>>>>>>>>>>>>>>>>>>>>>>>>>>>"); - return true; +Info("Leaving OurCheckAlarms >>>>>>>>>>>>>>>>>>>>>>>>>>>>"); +return true; } unsigned int Monitor::DetectBlack(const Image &comp_image, Event::StringSet &zoneSet ) { - Info("Entering DetectBlack >>>>>>>>>>>>>>>>>>>>>>>>>>"); - bool alarm = false; - unsigned int score = 0; + Info("Entering DetectBlack >>>>>>>>>>>>>>>>>>>>>>>>>>"); + bool alarm = false; + unsigned int score = 0; - if ( n_zones <= 0 ) return( alarm ); + if ( n_zones <= 0 ) return( alarm ); -// Coord alarm_centre; -// int top_score = -1; + // Coord alarm_centre; + // int top_score = -1; - // Find all alarm pixels in active zones - Info("Number of zones to process %d", n_zones); - for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) + // Find all alarm pixels in active zones + Info("Number of zones to process %d", n_zones); + for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) + { + Zone *zone = zones[n_zone]; + if ( !zone->IsActive() ) { - Zone *zone = zones[n_zone]; - if ( !zone->IsActive() ) - { - continue; - } - Debug( 3, "Checking active zone %s", zone->Label() ); - Info( "Checking active zone %s", zone->Label() ); - if ( OurCheckAlarms( zone, &comp_image ) ) - { - Info("OurCheckAlarm is TRUE!!!!!!"); - alarm = true; - score += zone->Score(); - zone->SetAlarm(); - Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); - Info( "Zone is alarmed, zone score = %d", zone->Score() ); - zoneSet.insert( zone->Label() ); -// if ( config.opt_control && track_motion ) -// { -// if ( (int)zone->Score() > top_score ) -// { -// top_score = zone->Score(); -// alarm_centre = zone->GetAlarmCentre(); -// } -// } - } - Info( "Finish checking active zone %s", zone->Label() ); + continue; } + Debug( 3, "Checking active zone %s", zone->Label() ); + Info( "Checking active zone %s", zone->Label() ); + if ( OurCheckAlarms( zone, &comp_image ) ) + { + Info("OurCheckAlarm is TRUE!!!!!!"); + alarm = true; + score += zone->Score(); + zone->SetAlarm(); + Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); + Info( "Zone is alarmed, zone score = %d", zone->Score() ); + zoneSet.insert( zone->Label() ); + // if ( config.opt_control && track_motion ) + // { + // if ( (int)zone->Score() > top_score ) + // { + // top_score = zone->Score(); + // alarm_centre = zone->GetAlarmCentre(); + // } + // } + } + Info( "Finish checking active zone %s", zone->Label() ); + } -// if ( top_score > 0 ) -// { -// 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, image_count ); -// } -// else -// { -// shared_data->alarm_x = shared_data->alarm_y = -1; -// } + // if ( top_score > 0 ) + // { + // 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, image_count ); + // } + // else + // { + // shared_data->alarm_x = shared_data->alarm_y = -1; + // } - // This is a small and innocent hack to prevent scores of 0 being returned in alarm state - Info("Leaving DetectBlack <<<<<<<<<<<<<<<<<<<<<<<<<<<"); - return( score?score:alarm ); + // This is a small and innocent hack to prevent scores of 0 being returned in alarm state + Info("Leaving DetectBlack <<<<<<<<<<<<<<<<<<<<<<<<<<<"); + return( score?score:alarm ); } */ @@ -3195,1114 +3195,1114 @@ unsigned int Monitor::DetectBlack(const Image &comp_image, Event::StringSet &zon unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &zoneSet ) { - bool alarm = false; - unsigned int score = 0; + bool alarm = false; + unsigned int score = 0; - if ( n_zones <= 0 ) return( alarm ); + if ( n_zones <= 0 ) return( alarm ); - if ( config.record_diag_images ) + if ( config.record_diag_images ) + { + static char diag_path[PATH_MAX] = ""; + if ( !diag_path[0] ) { - static char diag_path[PATH_MAX] = ""; - if ( !diag_path[0] ) - { - snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-r.jpg", config.dir_events, id ); - } - ref_image.WriteJpeg( diag_path ); + snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-r.jpg", config.dir_events, id ); } + ref_image.WriteJpeg( diag_path ); + } - ref_image.Delta( comp_image, &delta_image); + ref_image.Delta( comp_image, &delta_image); - if ( config.record_diag_images ) + if ( config.record_diag_images ) + { + static char diag_path[PATH_MAX] = ""; + if ( !diag_path[0] ) { - static char diag_path[PATH_MAX] = ""; - if ( !diag_path[0] ) - { - snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-d.jpg", config.dir_events, id ); - } - delta_image.WriteJpeg( diag_path ); + snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-d.jpg", config.dir_events, id ); } + delta_image.WriteJpeg( diag_path ); + } - // Blank out all exclusion zones - for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) + // Blank out all exclusion zones + for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) + { + Zone *zone = zones[n_zone]; + // need previous alarmed state for preclusive zone, so don't clear just yet + if (!zone->IsPreclusive()) + zone->ClearAlarm(); + if ( !zone->IsInactive() ) { - Zone *zone = zones[n_zone]; - // need previous alarmed state for preclusive zone, so don't clear just yet - if (!zone->IsPreclusive()) - zone->ClearAlarm(); - if ( !zone->IsInactive() ) - { - continue; - } - Debug( 3, "Blanking inactive zone %s", zone->Label() ); - delta_image.Fill( RGB_BLACK, zone->GetPolygon() ); + continue; } + Debug( 3, "Blanking inactive zone %s", zone->Label() ); + delta_image.Fill( RGB_BLACK, zone->GetPolygon() ); + } - // Check preclusive zones first - for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) + // Check preclusive zones first + for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) + { + Zone *zone = zones[n_zone]; + if ( !zone->IsPreclusive() ) { - Zone *zone = zones[n_zone]; - if ( !zone->IsPreclusive() ) - { - continue; + continue; + } + int old_zone_score = zone->Score(); + bool old_zone_alarmed = zone->Alarmed(); + Debug( 3, "Checking preclusive zone %s - old score: %d, state: %s", zone->Label(),old_zone_score, zone->Alarmed()?"alarmed":"quiet" ); + if ( zone->CheckAlarms( &delta_image ) ) + { + alarm = true; + score += zone->Score(); + zone->SetAlarm(); + Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); + zoneSet.insert( zone->Label() ); + //zone->ResetStats(); + } else { + // check if end of alarm + if (old_zone_alarmed) { + Debug(3, "Preclusive Zone %s alarm Ends. Prevíous score: %d", zone->Label(), old_zone_score); + if (old_zone_score > 0) { + zone->SetExtendAlarmCount(zone->GetExtendAlarmFrames()); } - int old_zone_score = zone->Score(); - bool old_zone_alarmed = zone->Alarmed(); - Debug( 3, "Checking preclusive zone %s - old score: %d, state: %s", zone->Label(),old_zone_score, zone->Alarmed()?"alarmed":"quiet" ); - if ( zone->CheckAlarms( &delta_image ) ) - { - alarm = true; - score += zone->Score(); - zone->SetAlarm(); - Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); - zoneSet.insert( zone->Label() ); - //zone->ResetStats(); + if (zone->CheckExtendAlarmCount()) { + alarm=true; + zone->SetAlarm(); } else { - // check if end of alarm - if (old_zone_alarmed) { - Debug(3, "Preclusive Zone %s alarm Ends. Prevíous score: %d", zone->Label(), old_zone_score); - if (old_zone_score > 0) { - zone->SetExtendAlarmCount(zone->GetExtendAlarmFrames()); - } - if (zone->CheckExtendAlarmCount()) { - alarm=true; - zone->SetAlarm(); - } else { - zone->ClearAlarm(); - } - } + zone->ClearAlarm(); } + } } + } - Coord alarm_centre; - int top_score = -1; + Coord alarm_centre; + int top_score = -1; + + if ( alarm ) + { + alarm = false; + score = 0; + } + else + { + // Find all alarm pixels in active zones + for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) + { + Zone *zone = zones[n_zone]; + if ( !zone->IsActive() || zone->IsPreclusive()) + { + continue; + } + Debug( 3, "Checking active zone %s", zone->Label() ); + if ( zone->CheckAlarms( &delta_image ) ) + { + alarm = true; + score += zone->Score(); + zone->SetAlarm(); + Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); + zoneSet.insert( zone->Label() ); + if ( config.opt_control && track_motion ) + { + if ( (int)zone->Score() > top_score ) + { + top_score = zone->Score(); + alarm_centre = zone->GetAlarmCentre(); + } + } + } + } if ( alarm ) { - alarm = false; - score = 0; + for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) + { + Zone *zone = zones[n_zone]; + if ( !zone->IsInclusive() ) + { + continue; + } + Debug( 3, "Checking inclusive zone %s", zone->Label() ); + if ( zone->CheckAlarms( &delta_image ) ) + { + alarm = true; + score += zone->Score(); + zone->SetAlarm(); + Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); + zoneSet.insert( zone->Label() ); + if ( config.opt_control && track_motion ) + { + if ( zone->Score() > (unsigned int)top_score ) + { + top_score = zone->Score(); + alarm_centre = zone->GetAlarmCentre(); + } + } + } + } } else { - // Find all alarm pixels in active zones - for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) + // Find all alarm pixels in exclusive zones + for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) + { + Zone *zone = zones[n_zone]; + if ( !zone->IsExclusive() ) { - Zone *zone = zones[n_zone]; - if ( !zone->IsActive() || zone->IsPreclusive()) - { - continue; - } - Debug( 3, "Checking active zone %s", zone->Label() ); - if ( zone->CheckAlarms( &delta_image ) ) - { - alarm = true; - score += zone->Score(); - zone->SetAlarm(); - Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); - zoneSet.insert( zone->Label() ); - if ( config.opt_control && track_motion ) - { - if ( (int)zone->Score() > top_score ) - { - top_score = zone->Score(); - alarm_centre = zone->GetAlarmCentre(); - } - } - } + continue; } - - if ( alarm ) + Debug( 3, "Checking exclusive zone %s", zone->Label() ); + if ( zone->CheckAlarms( &delta_image ) ) { - for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) - { - Zone *zone = zones[n_zone]; - if ( !zone->IsInclusive() ) - { - continue; - } - Debug( 3, "Checking inclusive zone %s", zone->Label() ); - if ( zone->CheckAlarms( &delta_image ) ) - { - alarm = true; - score += zone->Score(); - zone->SetAlarm(); - Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); - zoneSet.insert( zone->Label() ); - if ( config.opt_control && track_motion ) - { - if ( zone->Score() > (unsigned int)top_score ) - { - top_score = zone->Score(); - alarm_centre = zone->GetAlarmCentre(); - } - } - } - } - } - else - { - // Find all alarm pixels in exclusive zones - for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) - { - Zone *zone = zones[n_zone]; - if ( !zone->IsExclusive() ) - { - continue; - } - Debug( 3, "Checking exclusive zone %s", zone->Label() ); - if ( zone->CheckAlarms( &delta_image ) ) - { - alarm = true; - score += zone->Score(); - zone->SetAlarm(); - Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); - zoneSet.insert( zone->Label() ); - } - } + alarm = true; + score += zone->Score(); + zone->SetAlarm(); + Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); + zoneSet.insert( zone->Label() ); } + } } + } - if ( top_score > 0 ) - { - shared_data->alarm_x = alarm_centre.X(); - shared_data->alarm_y = alarm_centre.Y(); + if ( top_score > 0 ) + { + 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, image_count ); - } - else - { - shared_data->alarm_x = shared_data->alarm_y = -1; - } + Info( "Got alarm centre at %d,%d, at count %d", shared_data->alarm_x, shared_data->alarm_y, image_count ); + } + else + { + shared_data->alarm_x = shared_data->alarm_y = -1; + } - // This is a small and innocent hack to prevent scores of 0 being returned in alarm state - return( score?score:alarm ); + // This is a small and innocent hack to prevent scores of 0 being returned in alarm state + return( score?score:alarm ); } bool Monitor::DumpSettings( char *output, bool verbose ) { - output[0] = 0; + output[0] = 0; - sprintf( output+strlen(output), "Id : %d\n", id ); - sprintf( output+strlen(output), "Name : %s\n", name ); - sprintf( output+strlen(output), "Type : %s\n", camera->IsLocal()?"Local":(camera->IsRemote()?"Remote":"File") ); + sprintf( output+strlen(output), "Id : %d\n", id ); + sprintf( output+strlen(output), "Name : %s\n", name ); + sprintf( output+strlen(output), "Type : %s\n", camera->IsLocal()?"Local":(camera->IsRemote()?"Remote":"File") ); #if ZM_HAS_V4L - if ( camera->IsLocal() ) - { - sprintf( output+strlen(output), "Device : %s\n", ((LocalCamera *)camera)->Device().c_str() ); - sprintf( output+strlen(output), "Channel : %d\n", ((LocalCamera *)camera)->Channel() ); - sprintf( output+strlen(output), "Standard : %d\n", ((LocalCamera *)camera)->Standard() ); - } - else + if ( camera->IsLocal() ) + { + sprintf( output+strlen(output), "Device : %s\n", ((LocalCamera *)camera)->Device().c_str() ); + sprintf( output+strlen(output), "Channel : %d\n", ((LocalCamera *)camera)->Channel() ); + sprintf( output+strlen(output), "Standard : %d\n", ((LocalCamera *)camera)->Standard() ); + } + else #endif // ZM_HAS_V4L if ( camera->IsRemote() ) { - sprintf( output+strlen(output), "Protocol : %s\n", ((RemoteCamera *)camera)->Protocol().c_str() ); - sprintf( output+strlen(output), "Host : %s\n", ((RemoteCamera *)camera)->Host().c_str() ); - sprintf( output+strlen(output), "Port : %s\n", ((RemoteCamera *)camera)->Port().c_str() ); - sprintf( output+strlen(output), "Path : %s\n", ((RemoteCamera *)camera)->Path().c_str() ); + sprintf( output+strlen(output), "Protocol : %s\n", ((RemoteCamera *)camera)->Protocol().c_str() ); + sprintf( output+strlen(output), "Host : %s\n", ((RemoteCamera *)camera)->Host().c_str() ); + sprintf( output+strlen(output), "Port : %s\n", ((RemoteCamera *)camera)->Port().c_str() ); + sprintf( output+strlen(output), "Path : %s\n", ((RemoteCamera *)camera)->Path().c_str() ); } else if ( camera->IsFile() ) { - sprintf( output+strlen(output), "Path : %s\n", ((FileCamera *)camera)->Path() ); + sprintf( output+strlen(output), "Path : %s\n", ((FileCamera *)camera)->Path() ); } #if HAVE_LIBAVFORMAT else if ( camera->IsFfmpeg() ) { - sprintf( output+strlen(output), "Path : %s\n", ((FfmpegCamera *)camera)->Path().c_str() ); + sprintf( output+strlen(output), "Path : %s\n", ((FfmpegCamera *)camera)->Path().c_str() ); } #endif // HAVE_LIBAVFORMAT - sprintf( output+strlen(output), "Width : %d\n", camera->Width() ); - sprintf( output+strlen(output), "Height : %d\n", camera->Height() ); + sprintf( output+strlen(output), "Width : %d\n", camera->Width() ); + sprintf( output+strlen(output), "Height : %d\n", camera->Height() ); #if ZM_HAS_V4L - if ( camera->IsLocal() ) - { - sprintf( output+strlen(output), "Palette : %d\n", ((LocalCamera *)camera)->Palette() ); - } + if ( camera->IsLocal() ) + { + sprintf( output+strlen(output), "Palette : %d\n", ((LocalCamera *)camera)->Palette() ); + } #endif // ZM_HAS_V4L - sprintf( output+strlen(output), "Colours : %d\n", camera->Colours() ); - sprintf( output+strlen(output), "Subpixel Order : %d\n", camera->SubpixelOrder() ); - sprintf( output+strlen(output), "Event Prefix : %s\n", event_prefix ); - sprintf( output+strlen(output), "Label Format : %s\n", label_format ); - sprintf( output+strlen(output), "Label Coord : %d,%d\n", label_coord.X(), label_coord.Y() ); - sprintf( output+strlen(output), "Image Buffer Count : %d\n", image_buffer_count ); - sprintf( output+strlen(output), "Warmup Count : %d\n", warmup_count ); - sprintf( output+strlen(output), "Pre Event Count : %d\n", pre_event_count ); - sprintf( output+strlen(output), "Post Event Count : %d\n", post_event_count ); - sprintf( output+strlen(output), "Stream Replay Buffer : %d\n", stream_replay_buffer ); - sprintf( output+strlen(output), "Alarm Frame Count : %d\n", alarm_frame_count ); - sprintf( output+strlen(output), "Section Length : %d\n", section_length ); - sprintf( output+strlen(output), "Maximum FPS : %.2f\n", capture_delay?DT_PREC_3/capture_delay:0.0 ); - sprintf( output+strlen(output), "Alarm Maximum FPS : %.2f\n", alarm_capture_delay?DT_PREC_3/alarm_capture_delay:0.0 ); - sprintf( output+strlen(output), "Reference Blend %%ge : %d\n", ref_blend_perc ); - sprintf( output+strlen(output), "Alarm Reference Blend %%ge : %d\n", alarm_ref_blend_perc ); - sprintf( output+strlen(output), "Track Motion : %d\n", track_motion ); - sprintf( output+strlen(output), "Function: %d - %s\n", function, - function==NONE?"None":( + sprintf( output+strlen(output), "Colours : %d\n", camera->Colours() ); + sprintf( output+strlen(output), "Subpixel Order : %d\n", camera->SubpixelOrder() ); + sprintf( output+strlen(output), "Event Prefix : %s\n", event_prefix ); + sprintf( output+strlen(output), "Label Format : %s\n", label_format ); + sprintf( output+strlen(output), "Label Coord : %d,%d\n", label_coord.X(), label_coord.Y() ); + sprintf( output+strlen(output), "Image Buffer Count : %d\n", image_buffer_count ); + sprintf( output+strlen(output), "Warmup Count : %d\n", warmup_count ); + sprintf( output+strlen(output), "Pre Event Count : %d\n", pre_event_count ); + sprintf( output+strlen(output), "Post Event Count : %d\n", post_event_count ); + sprintf( output+strlen(output), "Stream Replay Buffer : %d\n", stream_replay_buffer ); + sprintf( output+strlen(output), "Alarm Frame Count : %d\n", alarm_frame_count ); + sprintf( output+strlen(output), "Section Length : %d\n", section_length ); + sprintf( output+strlen(output), "Maximum FPS : %.2f\n", capture_delay?DT_PREC_3/capture_delay:0.0 ); + sprintf( output+strlen(output), "Alarm Maximum FPS : %.2f\n", alarm_capture_delay?DT_PREC_3/alarm_capture_delay:0.0 ); + sprintf( output+strlen(output), "Reference Blend %%ge : %d\n", ref_blend_perc ); + sprintf( output+strlen(output), "Alarm Reference Blend %%ge : %d\n", alarm_ref_blend_perc ); + sprintf( output+strlen(output), "Track Motion : %d\n", track_motion ); + sprintf( output+strlen(output), "Function: %d - %s\n", function, + function==NONE?"None":( function==MONITOR?"Monitor Only":( - function==MODECT?"Motion Detection":( - function==RECORD?"Continuous Record":( - function==MOCORD?"Continuous Record with Motion Detection":( - function==NODECT?"Externally Triggered only, no Motion Detection":"Unknown" - )))))); - sprintf( output+strlen(output), "Zones : %d\n", n_zones ); - for ( int i = 0; i < n_zones; i++ ) - { - zones[i]->DumpSettings( output+strlen(output), verbose ); - } - return( true ); + function==MODECT?"Motion Detection":( + function==RECORD?"Continuous Record":( + function==MOCORD?"Continuous Record with Motion Detection":( + function==NODECT?"Externally Triggered only, no Motion Detection":"Unknown" + )))))); + sprintf( output+strlen(output), "Zones : %d\n", n_zones ); + for ( int i = 0; i < n_zones; i++ ) + { + zones[i]->DumpSettings( output+strlen(output), verbose ); + } + return( true ); } bool MonitorStream::checkSwapPath( const char *path, bool create_path ) { - uid_t uid = getuid(); - gid_t gid = getgid(); + uid_t uid = getuid(); + gid_t gid = getgid(); - struct stat stat_buf; - if ( stat( path, &stat_buf ) < 0 ) + struct stat stat_buf; + if ( stat( path, &stat_buf ) < 0 ) + { + if ( create_path && errno == ENOENT ) { - if ( create_path && errno == ENOENT ) - { - Debug( 3, "Swap path '%s' missing, creating", path ); - if ( mkdir( path, 0755 ) ) - { - Error( "Can't mkdir %s: %s", path, strerror(errno)); - return( false ); - } - if ( stat( path, &stat_buf ) < 0 ) - { - Error( "Can't stat '%s': %s", path, strerror(errno) ); - return( false ); - } - } - else - { - Error( "Can't stat '%s': %s", path, strerror(errno) ); - return( false ); - } - } - if ( !S_ISDIR(stat_buf.st_mode) ) - { - Error( "Swap image path '%s' is not a directory", path ); + Debug( 3, "Swap path '%s' missing, creating", path ); + if ( mkdir( path, 0755 ) ) + { + Error( "Can't mkdir %s: %s", path, strerror(errno)); return( false ); - } - - mode_t mask = 0; - if ( uid == stat_buf.st_uid ) - { - // If we are the owner - mask = 00700; - } - else if ( gid == stat_buf.st_gid ) - { - // If we are in the owner group - mask = 00070; + } + if ( stat( path, &stat_buf ) < 0 ) + { + Error( "Can't stat '%s': %s", path, strerror(errno) ); + return( false ); + } } else { - // We are neither the owner nor in the group - mask = 00007; + Error( "Can't stat '%s': %s", path, strerror(errno) ); + return( false ); } + } + if ( !S_ISDIR(stat_buf.st_mode) ) + { + Error( "Swap image path '%s' is not a directory", path ); + return( false ); + } - if ( (stat_buf.st_mode & mask) != mask ) - { - Error( "Insufficient permissions on swap image path '%s'", path ); - return( false ); - } - return( true ); + mode_t mask = 0; + if ( uid == stat_buf.st_uid ) + { + // If we are the owner + mask = 00700; + } + else if ( gid == stat_buf.st_gid ) + { + // If we are in the owner group + mask = 00070; + } + else + { + // We are neither the owner nor in the group + mask = 00007; + } + + if ( (stat_buf.st_mode & mask) != mask ) + { + Error( "Insufficient permissions on swap image path '%s'", path ); + return( false ); + } + return( true ); } void MonitorStream::processCommand( const CmdMsg *msg ) { - Debug( 2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0] ); - // Check for incoming command - switch( (MsgCommand)msg->msg_data[0] ) + Debug( 2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0] ); + // Check for incoming command + switch( (MsgCommand)msg->msg_data[0] ) + { + case CMD_PAUSE : + { + Debug( 1, "Got PAUSE command" ); + + // Set paused flag + paused = true; + // Set delayed flag + delayed = true; + last_frame_sent = TV_2_FLOAT( now ); + break; + } + case CMD_PLAY : + { + Debug( 1, "Got PLAY command" ); + if ( paused ) + { + // Clear paused flag + paused = false; + // Set delayed_play flag + delayed = true; + } + replay_rate = ZM_RATE_BASE; + break; + } + case CMD_VARPLAY : + { + Debug( 1, "Got VARPLAY command" ); + if ( paused ) + { + // Clear paused flag + paused = false; + // Set delayed_play flag + delayed = true; + } + replay_rate = ntohs(((unsigned char)msg->msg_data[2]<<8)|(unsigned char)msg->msg_data[1])-32768; + break; + } + case CMD_STOP : + { + Debug( 1, "Got STOP command" ); + + // Clear paused flag + paused = false; + // Clear delayed_play flag + delayed = false; + break; + } + case CMD_FASTFWD : + { + Debug( 1, "Got FAST FWD command" ); + if ( paused ) + { + // Clear paused flag + paused = false; + // Set delayed_play flag + delayed = true; + } + // Set play rate + switch ( replay_rate ) + { + case 2 * ZM_RATE_BASE : + replay_rate = 5 * ZM_RATE_BASE; + break; + case 5 * ZM_RATE_BASE : + replay_rate = 10 * ZM_RATE_BASE; + break; + case 10 * ZM_RATE_BASE : + replay_rate = 25 * ZM_RATE_BASE; + break; + case 25 * ZM_RATE_BASE : + case 50 * ZM_RATE_BASE : + replay_rate = 50 * ZM_RATE_BASE; + break; + default : + replay_rate = 2 * ZM_RATE_BASE; + break; + } + break; + } + case CMD_SLOWFWD : + { + Debug( 1, "Got SLOW FWD command" ); + // Set paused flag + paused = true; + // Set delayed flag + delayed = true; + // Set play rate + replay_rate = ZM_RATE_BASE; + // Set step + step = 1; + break; + } + case CMD_SLOWREV : + { + Debug( 1, "Got SLOW REV command" ); + // Set paused flag + paused = true; + // Set delayed flag + delayed = true; + // Set play rate + replay_rate = ZM_RATE_BASE; + // Set step + step = -1; + break; + } + case CMD_FASTREV : + { + Debug( 1, "Got FAST REV command" ); + if ( paused ) + { + // Clear paused flag + paused = false; + // Set delayed_play flag + delayed = true; + } + // Set play rate + switch ( replay_rate ) + { + case -2 * ZM_RATE_BASE : + replay_rate = -5 * ZM_RATE_BASE; + break; + case -5 * ZM_RATE_BASE : + replay_rate = -10 * ZM_RATE_BASE; + break; + case -10 * ZM_RATE_BASE : + replay_rate = -25 * ZM_RATE_BASE; + break; + case -25 * ZM_RATE_BASE : + case -50 * ZM_RATE_BASE : + replay_rate = -50 * ZM_RATE_BASE; + break; + default : + replay_rate = -2 * ZM_RATE_BASE; + break; + } + break; + } + case CMD_ZOOMIN : + { + x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; + y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; + Debug( 1, "Got ZOOM IN command, to %d,%d", x, y ); + switch ( zoom ) + { + case 100: + zoom = 150; + break; + case 150: + zoom = 200; + break; + case 200: + zoom = 300; + break; + case 300: + zoom = 400; + break; + case 400: + default : + zoom = 500; + break; + } + break; + } + case CMD_ZOOMOUT : + { + Debug( 1, "Got ZOOM OUT command" ); + switch ( zoom ) + { + case 500: + zoom = 400; + break; + case 400: + zoom = 300; + break; + case 300: + zoom = 200; + break; + case 200: + zoom = 150; + break; + case 150: + default : + zoom = 100; + break; + } + break; + } + case CMD_PAN : + { + x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; + y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; + Debug( 1, "Got PAN command, to %d,%d", x, y ); + break; + } + case CMD_SCALE : + { + scale = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; + Debug( 1, "Got SCALE command, to %d", scale ); + break; + } + case CMD_QUERY : + { + Debug( 1, "Got QUERY command, sending STATUS" ); + break; + } + default : + { + Error( "Got unexpected command %d", msg->msg_data[0] ); + break; + } + } + + struct { + int id; + int state; + double fps; + int buffer_level; + int rate; + double delay; + int zoom; + bool delayed; + bool paused; + bool enabled; + bool forced; + } status_data; + + status_data.id = monitor->Id(); + status_data.fps = monitor->GetFPS(); + status_data.state = monitor->shared_data->state; + if ( playback_buffer > 0 ) + status_data.buffer_level = (MOD_ADD( (temp_write_index-temp_read_index), 0, temp_image_buffer_count )*100)/temp_image_buffer_count; + else + status_data.buffer_level = 0; + status_data.delayed = delayed; + status_data.paused = paused; + status_data.rate = replay_rate; + status_data.delay = TV_2_FLOAT( now ) - TV_2_FLOAT( last_frame_timestamp ); + status_data.zoom = zoom; + //status_data.enabled = monitor->shared_data->active; + status_data.enabled = monitor->trigger_data->trigger_state!=Monitor::TRIGGER_OFF; + status_data.forced = monitor->trigger_data->trigger_state==Monitor::TRIGGER_ON; + Debug( 2, "L:%d, D:%d, P:%d, R:%d, d:%.3f, Z:%d, E:%d F:%d", + status_data.buffer_level, + status_data.delayed, + status_data.paused, + status_data.rate, + status_data.delay, + status_data.zoom, + status_data.enabled, + status_data.forced + ); + + DataMsg status_msg; + status_msg.msg_type = MSG_DATA_WATCH; + memcpy( &status_msg.msg_data, &status_data, sizeof(status_msg.msg_data) ); + int nbytes = 0; + if ( (nbytes = sendto( sd, &status_msg, sizeof(status_msg), MSG_DONTWAIT, (sockaddr *)&rem_addr, sizeof(rem_addr) )) < 0 ) + { + //if ( errno != EAGAIN ) { - case CMD_PAUSE : - { - Debug( 1, "Got PAUSE command" ); - - // Set paused flag - paused = true; - // Set delayed flag - delayed = true; - last_frame_sent = TV_2_FLOAT( now ); - break; - } - case CMD_PLAY : - { - Debug( 1, "Got PLAY command" ); - if ( paused ) - { - // Clear paused flag - paused = false; - // Set delayed_play flag - delayed = true; - } - replay_rate = ZM_RATE_BASE; - break; - } - case CMD_VARPLAY : - { - Debug( 1, "Got VARPLAY command" ); - if ( paused ) - { - // Clear paused flag - paused = false; - // Set delayed_play flag - delayed = true; - } - replay_rate = ntohs(((unsigned char)msg->msg_data[2]<<8)|(unsigned char)msg->msg_data[1])-32768; - break; - } - case CMD_STOP : - { - Debug( 1, "Got STOP command" ); - - // Clear paused flag - paused = false; - // Clear delayed_play flag - delayed = false; - break; - } - case CMD_FASTFWD : - { - Debug( 1, "Got FAST FWD command" ); - if ( paused ) - { - // Clear paused flag - paused = false; - // Set delayed_play flag - delayed = true; - } - // Set play rate - switch ( replay_rate ) - { - case 2 * ZM_RATE_BASE : - replay_rate = 5 * ZM_RATE_BASE; - break; - case 5 * ZM_RATE_BASE : - replay_rate = 10 * ZM_RATE_BASE; - break; - case 10 * ZM_RATE_BASE : - replay_rate = 25 * ZM_RATE_BASE; - break; - case 25 * ZM_RATE_BASE : - case 50 * ZM_RATE_BASE : - replay_rate = 50 * ZM_RATE_BASE; - break; - default : - replay_rate = 2 * ZM_RATE_BASE; - break; - } - break; - } - case CMD_SLOWFWD : - { - Debug( 1, "Got SLOW FWD command" ); - // Set paused flag - paused = true; - // Set delayed flag - delayed = true; - // Set play rate - replay_rate = ZM_RATE_BASE; - // Set step - step = 1; - break; - } - case CMD_SLOWREV : - { - Debug( 1, "Got SLOW REV command" ); - // Set paused flag - paused = true; - // Set delayed flag - delayed = true; - // Set play rate - replay_rate = ZM_RATE_BASE; - // Set step - step = -1; - break; - } - case CMD_FASTREV : - { - Debug( 1, "Got FAST REV command" ); - if ( paused ) - { - // Clear paused flag - paused = false; - // Set delayed_play flag - delayed = true; - } - // Set play rate - switch ( replay_rate ) - { - case -2 * ZM_RATE_BASE : - replay_rate = -5 * ZM_RATE_BASE; - break; - case -5 * ZM_RATE_BASE : - replay_rate = -10 * ZM_RATE_BASE; - break; - case -10 * ZM_RATE_BASE : - replay_rate = -25 * ZM_RATE_BASE; - break; - case -25 * ZM_RATE_BASE : - case -50 * ZM_RATE_BASE : - replay_rate = -50 * ZM_RATE_BASE; - break; - default : - replay_rate = -2 * ZM_RATE_BASE; - break; - } - break; - } - case CMD_ZOOMIN : - { - x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; - y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; - Debug( 1, "Got ZOOM IN command, to %d,%d", x, y ); - switch ( zoom ) - { - case 100: - zoom = 150; - break; - case 150: - zoom = 200; - break; - case 200: - zoom = 300; - break; - case 300: - zoom = 400; - break; - case 400: - default : - zoom = 500; - break; - } - break; - } - case CMD_ZOOMOUT : - { - Debug( 1, "Got ZOOM OUT command" ); - switch ( zoom ) - { - case 500: - zoom = 400; - break; - case 400: - zoom = 300; - break; - case 300: - zoom = 200; - break; - case 200: - zoom = 150; - break; - case 150: - default : - zoom = 100; - break; - } - break; - } - case CMD_PAN : - { - x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; - y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; - Debug( 1, "Got PAN command, to %d,%d", x, y ); - break; - } - case CMD_SCALE : - { - scale = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; - Debug( 1, "Got SCALE command, to %d", scale ); - break; - } - case CMD_QUERY : - { - Debug( 1, "Got QUERY command, sending STATUS" ); - break; - } - default : - { - Error( "Got unexpected command %d", msg->msg_data[0] ); - break; - } + Error( "Can't sendto on sd %d: %s", sd, strerror(errno) ); + //exit( -1 ); } + } - struct { - int id; - int state; - double fps; - int buffer_level; - int rate; - double delay; - int zoom; - bool delayed; - bool paused; - bool enabled; - bool forced; - } status_data; - - status_data.id = monitor->Id(); - status_data.fps = monitor->GetFPS(); - status_data.state = monitor->shared_data->state; - if ( playback_buffer > 0 ) - status_data.buffer_level = (MOD_ADD( (temp_write_index-temp_read_index), 0, temp_image_buffer_count )*100)/temp_image_buffer_count; - else - status_data.buffer_level = 0; - status_data.delayed = delayed; - status_data.paused = paused; - status_data.rate = replay_rate; - status_data.delay = TV_2_FLOAT( now ) - TV_2_FLOAT( last_frame_timestamp ); - status_data.zoom = zoom; - //status_data.enabled = monitor->shared_data->active; - status_data.enabled = monitor->trigger_data->trigger_state!=Monitor::TRIGGER_OFF; - status_data.forced = monitor->trigger_data->trigger_state==Monitor::TRIGGER_ON; - Debug( 2, "L:%d, D:%d, P:%d, R:%d, d:%.3f, Z:%d, E:%d F:%d", - status_data.buffer_level, - status_data.delayed, - status_data.paused, - status_data.rate, - status_data.delay, - status_data.zoom, - status_data.enabled, - status_data.forced - ); - - DataMsg status_msg; - status_msg.msg_type = MSG_DATA_WATCH; - memcpy( &status_msg.msg_data, &status_data, sizeof(status_msg.msg_data) ); - int nbytes = 0; - if ( (nbytes = sendto( sd, &status_msg, sizeof(status_msg), MSG_DONTWAIT, (sockaddr *)&rem_addr, sizeof(rem_addr) )) < 0 ) - { - //if ( errno != EAGAIN ) - { - Error( "Can't sendto on sd %d: %s", sd, strerror(errno) ); - //exit( -1 ); - } - } - - updateFrameRate( monitor->GetFPS() ); + updateFrameRate( monitor->GetFPS() ); } bool MonitorStream::sendFrame( const char *filepath, struct timeval *timestamp ) { - bool send_raw = ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE)); + bool send_raw = ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE)); - if ( type != JPEG ) - send_raw = false; - if ( !config.timestamp_on_capture && timestamp ) - send_raw = false; + if ( type != JPEG ) + send_raw = false; + if ( !config.timestamp_on_capture && timestamp ) + send_raw = false; - if ( !send_raw ) + if ( !send_raw ) + { + Image temp_image( filepath ); + + return( sendFrame( &temp_image, timestamp ) ); + } + else + { + int img_buffer_size = 0; + static unsigned char img_buffer[ZM_MAX_IMAGE_SIZE]; + + FILE *fdj = NULL; + if ( (fdj = fopen( filepath, "r" )) ) { - Image temp_image( filepath ); - - return( sendFrame( &temp_image, timestamp ) ); + img_buffer_size = fread( img_buffer, 1, sizeof(img_buffer), fdj ); + fclose( fdj ); } else { - int img_buffer_size = 0; - static unsigned char img_buffer[ZM_MAX_IMAGE_SIZE]; - - FILE *fdj = NULL; - if ( (fdj = fopen( filepath, "r" )) ) - { - img_buffer_size = fread( img_buffer, 1, sizeof(img_buffer), fdj ); - fclose( fdj ); - } - else - { - Error( "Can't open %s: %s", filepath, strerror(errno) ); - return( false ); - } - - // Calculate how long it takes to actually send the frame - struct timeval frameStartTime; - gettimeofday( &frameStartTime, NULL ); - - fprintf( stdout, "--ZoneMinderFrame\r\n" ); - fprintf( stdout, "Content-Length: %d\r\n", img_buffer_size ); - fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" ); - if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) - { - if ( !zm_terminate ) - Error( "Unable to send stream frame: %s", strerror(errno) ); - return( false ); - } - fprintf( stdout, "\r\n\r\n" ); - fflush( stdout ); - - struct timeval frameEndTime; - gettimeofday( &frameEndTime, NULL ); - - int frameSendTime = tvDiffMsec( frameStartTime, frameEndTime ); - if ( frameSendTime > 1000/maxfps ) - { - maxfps /= 2; - Error( "Frame send time %d msec too slow, throttling maxfps to %.2f", frameSendTime, maxfps ); - } - - last_frame_sent = TV_2_FLOAT( now ); - - return( true ); + Error( "Can't open %s: %s", filepath, strerror(errno) ); + return( false ); } - return( false ); + + // Calculate how long it takes to actually send the frame + struct timeval frameStartTime; + gettimeofday( &frameStartTime, NULL ); + + fprintf( stdout, "--ZoneMinderFrame\r\n" ); + fprintf( stdout, "Content-Length: %d\r\n", img_buffer_size ); + fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" ); + if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) + { + if ( !zm_terminate ) + Error( "Unable to send stream frame: %s", strerror(errno) ); + return( false ); + } + fprintf( stdout, "\r\n\r\n" ); + fflush( stdout ); + + struct timeval frameEndTime; + gettimeofday( &frameEndTime, NULL ); + + int frameSendTime = tvDiffMsec( frameStartTime, frameEndTime ); + if ( frameSendTime > 1000/maxfps ) + { + maxfps /= 2; + Error( "Frame send time %d msec too slow, throttling maxfps to %.2f", frameSendTime, maxfps ); + } + + last_frame_sent = TV_2_FLOAT( now ); + + return( true ); + } + return( false ); } bool MonitorStream::sendFrame( Image *image, struct timeval *timestamp ) { - Image *send_image = prepareImage( image ); - if ( !config.timestamp_on_capture && timestamp ) - monitor->TimestampImage( send_image, timestamp ); + Image *send_image = prepareImage( image ); + if ( !config.timestamp_on_capture && timestamp ) + monitor->TimestampImage( send_image, timestamp ); #if HAVE_LIBAVCODEC - if ( type == MPEG ) + if ( type == MPEG ) + { + if ( !vid_stream ) { - if ( !vid_stream ) - { - vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height() ); - fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() ); - vid_stream->OpenStream(); - } - static struct timeval base_time; - struct DeltaTimeval delta_time; - if ( !frame_count ) - base_time = *timestamp; - DELTA_TIMEVAL( delta_time, *timestamp, base_time, DT_PREC_3 ); - /* double pts = */ vid_stream->EncodeFrame( send_image->Buffer(), send_image->Size(), config.mpeg_timed_frames, delta_time.delta ); + vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height() ); + fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() ); + vid_stream->OpenStream(); } - else + static struct timeval base_time; + struct DeltaTimeval delta_time; + if ( !frame_count ) + base_time = *timestamp; + DELTA_TIMEVAL( delta_time, *timestamp, base_time, DT_PREC_3 ); + /* double pts = */ vid_stream->EncodeFrame( send_image->Buffer(), send_image->Size(), config.mpeg_timed_frames, delta_time.delta ); + } + else #endif // HAVE_LIBAVCODEC + { + static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE]; + + int img_buffer_size = 0; + unsigned char *img_buffer = temp_img_buffer; + + // Calculate how long it takes to actually send the frame + struct timeval frameStartTime; + gettimeofday( &frameStartTime, NULL ); + + fprintf( stdout, "--ZoneMinderFrame\r\n" ); + switch( type ) { - static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE]; + case JPEG : + send_image->EncodeJpeg( img_buffer, &img_buffer_size ); + fprintf( stdout, "Content-Type: image/jpeg\r\n" ); + break; + case RAW : + fprintf( stdout, "Content-Type: image/x-rgb\r\n" ); + img_buffer = (uint8_t*)send_image->Buffer(); + img_buffer_size = send_image->Size(); + break; + case ZIP : + fprintf( stdout, "Content-Type: image/x-rgbz\r\n" ); + unsigned long zip_buffer_size; + send_image->Zip( img_buffer, &zip_buffer_size ); + img_buffer_size = zip_buffer_size; + break; + default : + Fatal( "Unexpected frame type %d", type ); + break; + } + fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size ); + if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) + { + if ( !zm_terminate ) + Error( "Unable to send stream frame: %s", strerror(errno) ); + return( false ); + } + fprintf( stdout, "\r\n\r\n" ); + fflush( stdout ); - int img_buffer_size = 0; - unsigned char *img_buffer = temp_img_buffer; + struct timeval frameEndTime; + gettimeofday( &frameEndTime, NULL ); - // Calculate how long it takes to actually send the frame - struct timeval frameStartTime; - gettimeofday( &frameStartTime, NULL ); - - fprintf( stdout, "--ZoneMinderFrame\r\n" ); - switch( type ) - { - case JPEG : - send_image->EncodeJpeg( img_buffer, &img_buffer_size ); - fprintf( stdout, "Content-Type: image/jpeg\r\n" ); - break; - case RAW : - fprintf( stdout, "Content-Type: image/x-rgb\r\n" ); - img_buffer = (uint8_t*)send_image->Buffer(); - img_buffer_size = send_image->Size(); - break; - case ZIP : - fprintf( stdout, "Content-Type: image/x-rgbz\r\n" ); - unsigned long zip_buffer_size; - send_image->Zip( img_buffer, &zip_buffer_size ); - img_buffer_size = zip_buffer_size; - break; - default : - Fatal( "Unexpected frame type %d", type ); - break; - } - fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size ); - if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) - { - if ( !zm_terminate ) - Error( "Unable to send stream frame: %s", strerror(errno) ); - return( false ); - } - fprintf( stdout, "\r\n\r\n" ); - fflush( stdout ); - - struct timeval frameEndTime; - gettimeofday( &frameEndTime, NULL ); - - int frameSendTime = tvDiffMsec( frameStartTime, frameEndTime ); - if ( frameSendTime > 1000/maxfps ) - { - maxfps /= 1.5; - Error( "Frame send time %d msec too slow, throttling maxfps to %.2f", frameSendTime, maxfps ); - } - } // endif HAVE_LIBAV and type==MPEG - last_frame_sent = TV_2_FLOAT( now ); - return( true ); + int frameSendTime = tvDiffMsec( frameStartTime, frameEndTime ); + if ( frameSendTime > 1000/maxfps ) + { + maxfps /= 1.5; + Error( "Frame send time %d msec too slow, throttling maxfps to %.2f", frameSendTime, maxfps ); + } + } // endif HAVE_LIBAV and type==MPEG + last_frame_sent = TV_2_FLOAT( now ); + return( true ); } void MonitorStream::runStream() { - if ( mode == SINGLE ) + if ( mode == SINGLE ) + { + // Not yet migrated over to stream class + monitor->SingleImage( scale ); + return; + } + + openComms(); + + checkInitialised(); + + updateFrameRate( monitor->GetFPS() ); + + if ( type == JPEG ) + fprintf( stdout, "Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n" ); + + int last_read_index = monitor->image_buffer_count; + + time_t stream_start_time; + time( &stream_start_time ); + + frame_count = 0; + + temp_image_buffer = 0; + temp_image_buffer_count = playback_buffer; + temp_read_index = temp_image_buffer_count; + temp_write_index = temp_image_buffer_count; + + char *swap_path = 0; + bool buffered_playback = false; + int swap_path_length = strlen(config.path_swap)+1; // +1 for NULL terminator + + if ( connkey && playback_buffer > 0 ) { + + if ( swap_path_length + 15 > PATH_MAX ) { + // 15 is for /zmswap-whatever, assuming max 6 digits for monitor id + Error( "Swap Path is too long. %d > %d ", swap_path_length+15, PATH_MAX ); + } else { + swap_path = (char *)malloc( swap_path_length+15 ); + Debug( 3, "Checking swap image path %s", config.path_swap ); + strncpy( swap_path, config.path_swap, swap_path_length ); + if ( checkSwapPath( swap_path, false ) ) { + snprintf( &(swap_path[swap_path_length]), sizeof(swap_path)-swap_path_length, "/zmswap-m%d", monitor->Id() ); + if ( checkSwapPath( swap_path, true ) ) { + snprintf( &(swap_path[swap_path_length]), sizeof(swap_path)-swap_path_length, "/zmswap-q%06d", connkey ); + if ( checkSwapPath( swap_path, true ) ) { + buffered_playback = true; + } + } + } + + if ( !buffered_playback ) { + Error( "Unable to validate swap image path, disabling buffered playback" ); + } else { + Debug( 2, "Assigning temporary buffer" ); + temp_image_buffer = new SwapImage[temp_image_buffer_count]; + memset( temp_image_buffer, 0, sizeof(*temp_image_buffer)*temp_image_buffer_count ); + Debug( 2, "Assigned temporary buffer" ); + } + } + } + + float max_secs_since_last_sent_frame = 10.0; //should be > keep alive amount (5 secs) + while ( !zm_terminate ) + { + bool got_command = false; + if ( feof( stdout ) || ferror( stdout ) || !monitor->ShmValid() ) { - // Not yet migrated over to stream class - monitor->SingleImage( scale ); - return; + break; } - openComms(); + gettimeofday( &now, NULL ); - checkInitialised(); - - updateFrameRate( monitor->GetFPS() ); - - if ( type == JPEG ) - fprintf( stdout, "Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n" ); - - int last_read_index = monitor->image_buffer_count; - - time_t stream_start_time; - time( &stream_start_time ); - - frame_count = 0; - - temp_image_buffer = 0; - temp_image_buffer_count = playback_buffer; - temp_read_index = temp_image_buffer_count; - temp_write_index = temp_image_buffer_count; - - char *swap_path = 0; - bool buffered_playback = false; - int swap_path_length = strlen(config.path_swap)+1; // +1 for NULL terminator - - if ( connkey && playback_buffer > 0 ) { - - if ( swap_path_length + 15 > PATH_MAX ) { - // 15 is for /zmswap-whatever, assuming max 6 digits for monitor id - Error( "Swap Path is too long. %d > %d ", swap_path_length+15, PATH_MAX ); - } else { - swap_path = (char *)malloc( swap_path_length+15 ); - Debug( 3, "Checking swap image path %s", config.path_swap ); - strncpy( swap_path, config.path_swap, swap_path_length ); - if ( checkSwapPath( swap_path, false ) ) { - snprintf( &(swap_path[swap_path_length]), sizeof(swap_path)-swap_path_length, "/zmswap-m%d", monitor->Id() ); - if ( checkSwapPath( swap_path, true ) ) { - snprintf( &(swap_path[swap_path_length]), sizeof(swap_path)-swap_path_length, "/zmswap-q%06d", connkey ); - if ( checkSwapPath( swap_path, true ) ) { - buffered_playback = true; - } - } - } - - if ( !buffered_playback ) { - Error( "Unable to validate swap image path, disabling buffered playback" ); - } else { - Debug( 2, "Assigning temporary buffer" ); - temp_image_buffer = new SwapImage[temp_image_buffer_count]; - memset( temp_image_buffer, 0, sizeof(*temp_image_buffer)*temp_image_buffer_count ); - Debug( 2, "Assigned temporary buffer" ); - } - } + if ( connkey ) + { + while(checkCommandQueue()) { + got_command = true; + } } - float max_secs_since_last_sent_frame = 10.0; //should be > keep alive amount (5 secs) - while ( !zm_terminate ) + //bool frame_sent = false; + if ( buffered_playback && delayed ) { - bool got_command = false; - if ( feof( stdout ) || ferror( stdout ) || !monitor->ShmValid() ) + if ( temp_read_index == temp_write_index ) + { + // Go back to live viewing + Debug( 1, "Exceeded temporary streaming buffer" ); + // Clear paused flag + paused = false; + // Clear delayed_play flag + delayed = false; + replay_rate = ZM_RATE_BASE; + } + else + { + if ( !paused ) { - break; - } + int temp_index = MOD_ADD( temp_read_index, 0, temp_image_buffer_count ); + //Debug( 3, "tri: %d, ti: %d", temp_read_index, temp_index ); + SwapImage *swap_image = &temp_image_buffer[temp_index]; - gettimeofday( &now, NULL ); + if ( !swap_image->valid ) + { + paused = true; + delayed = true; + temp_read_index = MOD_ADD( temp_read_index, (replay_rate>=0?-1:1), temp_image_buffer_count ); + } + else + { + //Debug( 3, "siT: %f, lfT: %f", TV_2_FLOAT( swap_image->timestamp ), TV_2_FLOAT( last_frame_timestamp ) ); + double expected_delta_time = ((TV_2_FLOAT( swap_image->timestamp ) - TV_2_FLOAT( last_frame_timestamp )) * ZM_RATE_BASE)/replay_rate; + double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; - if ( connkey ) - { - while(checkCommandQueue()) { - got_command = true; - } - } - - //bool frame_sent = false; - if ( buffered_playback && delayed ) - { - if ( temp_read_index == temp_write_index ) + //Debug( 3, "eDT: %.3lf, aDT: %.3f, lFS:%.3f, NOW:%.3f", expected_delta_time, actual_delta_time, last_frame_sent, TV_2_FLOAT( now ) ); + // If the next frame is due + if ( actual_delta_time > expected_delta_time ) { - // Go back to live viewing - Debug( 1, "Exceeded temporary streaming buffer" ); - // Clear paused flag - paused = false; - // Clear delayed_play flag - delayed = false; - replay_rate = ZM_RATE_BASE; - } - else - { - if ( !paused ) - { - int temp_index = MOD_ADD( temp_read_index, 0, temp_image_buffer_count ); - //Debug( 3, "tri: %d, ti: %d", temp_read_index, temp_index ); - SwapImage *swap_image = &temp_image_buffer[temp_index]; - - if ( !swap_image->valid ) - { - paused = true; - delayed = true; - temp_read_index = MOD_ADD( temp_read_index, (replay_rate>=0?-1:1), temp_image_buffer_count ); - } - else - { - //Debug( 3, "siT: %f, lfT: %f", TV_2_FLOAT( swap_image->timestamp ), TV_2_FLOAT( last_frame_timestamp ) ); - double expected_delta_time = ((TV_2_FLOAT( swap_image->timestamp ) - TV_2_FLOAT( last_frame_timestamp )) * ZM_RATE_BASE)/replay_rate; - double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; - - //Debug( 3, "eDT: %.3lf, aDT: %.3f, lFS:%.3f, NOW:%.3f", expected_delta_time, actual_delta_time, last_frame_sent, TV_2_FLOAT( now ) ); - // If the next frame is due - if ( actual_delta_time > expected_delta_time ) - { - //Debug( 2, "eDT: %.3lf, aDT: %.3f", expected_delta_time, actual_delta_time ); - if ( temp_index%frame_mod == 0 ) - { - Debug( 2, "Sending delayed frame %d", temp_index ); - // Send the next frame - if ( !sendFrame( temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp ) ) - zm_terminate = true; - memcpy( &last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp) ); - //frame_sent = true; - } - temp_read_index = MOD_ADD( temp_read_index, (replay_rate>0?1:-1), temp_image_buffer_count ); - } - } - } - else if ( step != 0 ) - { - temp_read_index = MOD_ADD( temp_read_index, (step>0?1:-1), temp_image_buffer_count ); - - SwapImage *swap_image = &temp_image_buffer[temp_read_index]; - - // Send the next frame - if ( !sendFrame( temp_image_buffer[temp_read_index].file_name, &temp_image_buffer[temp_read_index].timestamp ) ) - zm_terminate = true; - memcpy( &last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp) ); - //frame_sent = true; - step = 0; - } - else - { - int temp_index = MOD_ADD( temp_read_index, 0, temp_image_buffer_count ); - - double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; - if ( got_command || actual_delta_time > 5 ) - { - // Send keepalive - Debug( 2, "Sending keepalive frame %d", temp_index ); - // Send the next frame - if ( !sendFrame( temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp ) ) - zm_terminate = true; - //frame_sent = true; - } - } - } - if ( temp_read_index == temp_write_index ) - { - // Go back to live viewing - Warning( "Rewound over write index, resuming live play" ); - // Clear paused flag - paused = false; - // Clear delayed_play flag - delayed = false; - replay_rate = ZM_RATE_BASE; + //Debug( 2, "eDT: %.3lf, aDT: %.3f", expected_delta_time, actual_delta_time ); + if ( temp_index%frame_mod == 0 ) + { + Debug( 2, "Sending delayed frame %d", temp_index ); + // Send the next frame + if ( !sendFrame( temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp ) ) + zm_terminate = true; + memcpy( &last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp) ); + //frame_sent = true; + } + temp_read_index = MOD_ADD( temp_read_index, (replay_rate>0?1:-1), temp_image_buffer_count ); } + } } - if ( (unsigned int)last_read_index != monitor->shared_data->last_write_index ) + else if ( step != 0 ) { - int index = monitor->shared_data->last_write_index%monitor->image_buffer_count; - last_read_index = monitor->shared_data->last_write_index; - //Debug( 1, "%d: %x - %x", index, image_buffer[index].image, image_buffer[index].image->buffer ); - if ( (frame_mod == 1) || ((frame_count%frame_mod) == 0) ) - { - if ( !paused && !delayed ) - { - // Send the next frame - Monitor::Snapshot *snap = &monitor->image_buffer[index]; + temp_read_index = MOD_ADD( temp_read_index, (step>0?1:-1), temp_image_buffer_count ); - if ( !sendFrame( snap->image, snap->timestamp ) ) - zm_terminate = true; - memcpy( &last_frame_timestamp, snap->timestamp, sizeof(last_frame_timestamp) ); - //frame_sent = true; + SwapImage *swap_image = &temp_image_buffer[temp_read_index]; - temp_read_index = temp_write_index; - } - } - if ( buffered_playback ) - { - if ( monitor->shared_data->valid ) - { - if ( monitor->image_buffer[index].timestamp->tv_sec ) - { - int temp_index = temp_write_index%temp_image_buffer_count; - Debug( 2, "Storing frame %d", temp_index ); - if ( !temp_image_buffer[temp_index].valid ) - { - snprintf( temp_image_buffer[temp_index].file_name, sizeof(temp_image_buffer[0].file_name), "%s/zmswap-i%05d.jpg", swap_path, temp_index ); - temp_image_buffer[temp_index].valid = true; - } - memcpy( &(temp_image_buffer[temp_index].timestamp), monitor->image_buffer[index].timestamp, sizeof(temp_image_buffer[0].timestamp) ); - monitor->image_buffer[index].image->WriteJpeg( temp_image_buffer[temp_index].file_name, config.jpeg_file_quality ); - temp_write_index = MOD_ADD( temp_write_index, 1, temp_image_buffer_count ); - if ( temp_write_index == temp_read_index ) - { - // Go back to live viewing - Warning( "Exceeded temporary buffer, resuming live play" ); - // Clear paused flag - paused = false; - // Clear delayed_play flag - delayed = false; - replay_rate = ZM_RATE_BASE; - } - } - else - { - Warning( "Unable to store frame as timestamp invalid" ); - } - } - else - { - Warning( "Unable to store frame as shared memory invalid" ); - } - } - frame_count++; - } - usleep( (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2))) ); - if ( ttl ) - { - if ( (now.tv_sec - stream_start_time) > ttl ) - { - break; - } - } - if ( (TV_2_FLOAT( now ) - last_frame_sent) > max_secs_since_last_sent_frame ) - { - Error( "Terminating, last frame sent time %f secs more than maximum of %f", TV_2_FLOAT( now ) - last_frame_sent, max_secs_since_last_sent_frame ); - break; - } - } - if ( buffered_playback ) - { - char swap_path[PATH_MAX] = ""; - - snprintf( swap_path, sizeof(swap_path), "%s/zmswap-m%d/zmswap-q%06d", config.path_swap, monitor->Id(), connkey ); - Debug( 1, "Cleaning swap files from %s", swap_path ); - struct stat stat_buf; - if ( stat( swap_path, &stat_buf ) < 0 ) - { - if ( errno != ENOENT ) - { - Error( "Can't stat '%s': %s", swap_path, strerror(errno) ); - } - } - else if ( !S_ISDIR(stat_buf.st_mode) ) - { - Error( "Swap image path '%s' is not a directory", swap_path ); + // Send the next frame + if ( !sendFrame( temp_image_buffer[temp_read_index].file_name, &temp_image_buffer[temp_read_index].timestamp ) ) + zm_terminate = true; + memcpy( &last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp) ); + //frame_sent = true; + step = 0; } else { - char glob_pattern[PATH_MAX] = ""; + int temp_index = MOD_ADD( temp_read_index, 0, temp_image_buffer_count ); - snprintf( glob_pattern, sizeof(glob_pattern), "%s/*.*", swap_path ); - glob_t pglob; - int glob_status = glob( glob_pattern, 0, 0, &pglob ); - if ( glob_status != 0 ) - { - if ( glob_status < 0 ) - { - Error( "Can't glob '%s': %s", glob_pattern, strerror(errno) ); - } - else - { - Debug( 1, "Can't glob '%s': %d", glob_pattern, glob_status ); - } - } - else - { - for ( unsigned int i = 0; i < pglob.gl_pathc; i++ ) - { - if ( unlink( pglob.gl_pathv[i] ) < 0 ) - { - Error( "Can't unlink '%s': %s", pglob.gl_pathv[i], strerror(errno) ); - } - } - } - globfree( &pglob ); - if ( rmdir( swap_path ) < 0 ) - { - Error( "Can't rmdir '%s': %s", swap_path, strerror(errno) ); - } + double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; + if ( got_command || actual_delta_time > 5 ) + { + // Send keepalive + Debug( 2, "Sending keepalive frame %d", temp_index ); + // Send the next frame + if ( !sendFrame( temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp ) ) + zm_terminate = true; + //frame_sent = true; + } } + } + if ( temp_read_index == temp_write_index ) + { + // Go back to live viewing + Warning( "Rewound over write index, resuming live play" ); + // Clear paused flag + paused = false; + // Clear delayed_play flag + delayed = false; + replay_rate = ZM_RATE_BASE; + } } - if ( swap_path ) free( swap_path ); - closeComms(); + if ( (unsigned int)last_read_index != monitor->shared_data->last_write_index ) + { + int index = monitor->shared_data->last_write_index%monitor->image_buffer_count; + last_read_index = monitor->shared_data->last_write_index; + //Debug( 1, "%d: %x - %x", index, image_buffer[index].image, image_buffer[index].image->buffer ); + if ( (frame_mod == 1) || ((frame_count%frame_mod) == 0) ) + { + if ( !paused && !delayed ) + { + // Send the next frame + Monitor::Snapshot *snap = &monitor->image_buffer[index]; + + if ( !sendFrame( snap->image, snap->timestamp ) ) + zm_terminate = true; + memcpy( &last_frame_timestamp, snap->timestamp, sizeof(last_frame_timestamp) ); + //frame_sent = true; + + temp_read_index = temp_write_index; + } + } + if ( buffered_playback ) + { + if ( monitor->shared_data->valid ) + { + if ( monitor->image_buffer[index].timestamp->tv_sec ) + { + int temp_index = temp_write_index%temp_image_buffer_count; + Debug( 2, "Storing frame %d", temp_index ); + if ( !temp_image_buffer[temp_index].valid ) + { + snprintf( temp_image_buffer[temp_index].file_name, sizeof(temp_image_buffer[0].file_name), "%s/zmswap-i%05d.jpg", swap_path, temp_index ); + temp_image_buffer[temp_index].valid = true; + } + memcpy( &(temp_image_buffer[temp_index].timestamp), monitor->image_buffer[index].timestamp, sizeof(temp_image_buffer[0].timestamp) ); + monitor->image_buffer[index].image->WriteJpeg( temp_image_buffer[temp_index].file_name, config.jpeg_file_quality ); + temp_write_index = MOD_ADD( temp_write_index, 1, temp_image_buffer_count ); + if ( temp_write_index == temp_read_index ) + { + // Go back to live viewing + Warning( "Exceeded temporary buffer, resuming live play" ); + // Clear paused flag + paused = false; + // Clear delayed_play flag + delayed = false; + replay_rate = ZM_RATE_BASE; + } + } + else + { + Warning( "Unable to store frame as timestamp invalid" ); + } + } + else + { + Warning( "Unable to store frame as shared memory invalid" ); + } + } + frame_count++; + } + usleep( (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2))) ); + if ( ttl ) + { + if ( (now.tv_sec - stream_start_time) > ttl ) + { + break; + } + } + if ( (TV_2_FLOAT( now ) - last_frame_sent) > max_secs_since_last_sent_frame ) + { + Error( "Terminating, last frame sent time %f secs more than maximum of %f", TV_2_FLOAT( now ) - last_frame_sent, max_secs_since_last_sent_frame ); + break; + } + } + if ( buffered_playback ) + { + char swap_path[PATH_MAX] = ""; + + snprintf( swap_path, sizeof(swap_path), "%s/zmswap-m%d/zmswap-q%06d", config.path_swap, monitor->Id(), connkey ); + Debug( 1, "Cleaning swap files from %s", swap_path ); + struct stat stat_buf; + if ( stat( swap_path, &stat_buf ) < 0 ) + { + if ( errno != ENOENT ) + { + Error( "Can't stat '%s': %s", swap_path, strerror(errno) ); + } + } + else if ( !S_ISDIR(stat_buf.st_mode) ) + { + Error( "Swap image path '%s' is not a directory", swap_path ); + } + else + { + char glob_pattern[PATH_MAX] = ""; + + snprintf( glob_pattern, sizeof(glob_pattern), "%s/*.*", swap_path ); + glob_t pglob; + int glob_status = glob( glob_pattern, 0, 0, &pglob ); + if ( glob_status != 0 ) + { + if ( glob_status < 0 ) + { + Error( "Can't glob '%s': %s", glob_pattern, strerror(errno) ); + } + else + { + Debug( 1, "Can't glob '%s': %d", glob_pattern, glob_status ); + } + } + else + { + for ( unsigned int i = 0; i < pglob.gl_pathc; i++ ) + { + if ( unlink( pglob.gl_pathv[i] ) < 0 ) + { + Error( "Can't unlink '%s': %s", pglob.gl_pathv[i], strerror(errno) ); + } + } + } + globfree( &pglob ); + if ( rmdir( swap_path ) < 0 ) + { + Error( "Can't rmdir '%s': %s", swap_path, strerror(errno) ); + } + } + } + if ( swap_path ) free( swap_path ); + closeComms(); } void Monitor::SingleImage( int scale) { - int img_buffer_size = 0; - static JOCTET img_buffer[ZM_MAX_IMAGE_SIZE]; - Image scaled_image; - int index = shared_data->last_write_index%image_buffer_count; - Snapshot *snap = &image_buffer[index]; - Image *snap_image = snap->image; + int img_buffer_size = 0; + static JOCTET img_buffer[ZM_MAX_IMAGE_SIZE]; + Image scaled_image; + int index = shared_data->last_write_index%image_buffer_count; + Snapshot *snap = &image_buffer[index]; + Image *snap_image = snap->image; - if ( scale != ZM_SCALE_BASE ) - { - scaled_image.Assign( *snap_image ); - scaled_image.Scale( scale ); - snap_image = &scaled_image; - } - if ( !config.timestamp_on_capture ) - { - TimestampImage( snap_image, snap->timestamp ); - } - snap_image->EncodeJpeg( img_buffer, &img_buffer_size ); - - fprintf( stdout, "Content-Length: %d\r\n", img_buffer_size ); - fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" ); - fwrite( img_buffer, img_buffer_size, 1, stdout ); + if ( scale != ZM_SCALE_BASE ) + { + scaled_image.Assign( *snap_image ); + scaled_image.Scale( scale ); + snap_image = &scaled_image; + } + if ( !config.timestamp_on_capture ) + { + TimestampImage( snap_image, snap->timestamp ); + } + snap_image->EncodeJpeg( img_buffer, &img_buffer_size ); + + fprintf( stdout, "Content-Length: %d\r\n", img_buffer_size ); + fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" ); + fwrite( img_buffer, img_buffer_size, 1, stdout ); } void Monitor::SingleImageRaw( int scale) { - Image scaled_image; - int index = shared_data->last_write_index%image_buffer_count; - Snapshot *snap = &image_buffer[index]; - Image *snap_image = snap->image; + Image scaled_image; + int index = shared_data->last_write_index%image_buffer_count; + Snapshot *snap = &image_buffer[index]; + Image *snap_image = snap->image; - if ( scale != ZM_SCALE_BASE ) - { - scaled_image.Assign( *snap_image ); - scaled_image.Scale( scale ); - snap_image = &scaled_image; - } - if ( !config.timestamp_on_capture ) - { - TimestampImage( snap_image, snap->timestamp ); - } - - fprintf( stdout, "Content-Length: %d\r\n", snap_image->Size() ); - fprintf( stdout, "Content-Type: image/x-rgb\r\n\r\n" ); - fwrite( snap_image->Buffer(), snap_image->Size(), 1, stdout ); + if ( scale != ZM_SCALE_BASE ) + { + scaled_image.Assign( *snap_image ); + scaled_image.Scale( scale ); + snap_image = &scaled_image; + } + if ( !config.timestamp_on_capture ) + { + TimestampImage( snap_image, snap->timestamp ); + } + + fprintf( stdout, "Content-Length: %d\r\n", snap_image->Size() ); + fprintf( stdout, "Content-Type: image/x-rgb\r\n\r\n" ); + fwrite( snap_image->Buffer(), snap_image->Size(), 1, stdout ); } void Monitor::SingleImageZip( int scale) { - unsigned long img_buffer_size = 0; - static Bytef img_buffer[ZM_MAX_IMAGE_SIZE]; - Image scaled_image; - int index = shared_data->last_write_index%image_buffer_count; - Snapshot *snap = &image_buffer[index]; - Image *snap_image = snap->image; + unsigned long img_buffer_size = 0; + static Bytef img_buffer[ZM_MAX_IMAGE_SIZE]; + Image scaled_image; + int index = shared_data->last_write_index%image_buffer_count; + Snapshot *snap = &image_buffer[index]; + Image *snap_image = snap->image; - if ( scale != ZM_SCALE_BASE ) - { - scaled_image.Assign( *snap_image ); - scaled_image.Scale( scale ); - snap_image = &scaled_image; - } - if ( !config.timestamp_on_capture ) - { - TimestampImage( snap_image, snap->timestamp ); - } - snap_image->Zip( img_buffer, &img_buffer_size ); - - fprintf( stdout, "Content-Length: %ld\r\n", img_buffer_size ); - fprintf( stdout, "Content-Type: image/x-rgbz\r\n\r\n" ); - fwrite( img_buffer, img_buffer_size, 1, stdout ); + if ( scale != ZM_SCALE_BASE ) + { + scaled_image.Assign( *snap_image ); + scaled_image.Scale( scale ); + snap_image = &scaled_image; + } + if ( !config.timestamp_on_capture ) + { + TimestampImage( snap_image, snap->timestamp ); + } + snap_image->Zip( img_buffer, &img_buffer_size ); + + fprintf( stdout, "Content-Length: %ld\r\n", img_buffer_size ); + fprintf( stdout, "Content-Type: image/x-rgbz\r\n\r\n" ); + fwrite( img_buffer, img_buffer_size, 1, stdout ); } diff --git a/src/zm_stream.cpp b/src/zm_stream.cpp index 4ee38aeb0..2180474e7 100644 --- a/src/zm_stream.cpp +++ b/src/zm_stream.cpp @@ -28,299 +28,299 @@ StreamBase::~StreamBase() { #if HAVE_LIBAVCODEC - if ( vid_stream ) - { - delete vid_stream; - vid_stream = NULL; - } + if ( vid_stream ) + { + delete vid_stream; + vid_stream = NULL; + } #endif - closeComms(); + closeComms(); } bool StreamBase::loadMonitor( int monitor_id ) { - if ( !(monitor = Monitor::Load( monitor_id, false, Monitor::QUERY )) ) - { - Fatal( "Unable to load monitor id %d for streaming", monitor_id ); - return( false ); - } - monitor->connect(); - return( true ); + if ( !(monitor = Monitor::Load( monitor_id, false, Monitor::QUERY )) ) + { + Fatal( "Unable to load monitor id %d for streaming", monitor_id ); + return( false ); + } + monitor->connect(); + return( true ); } bool StreamBase::checkInitialised() { - if ( !monitor ) - { - Fatal( "Cannot stream, not initialised" ); - return( false ); - } - return( true ); + if ( !monitor ) + { + Fatal( "Cannot stream, not initialised" ); + return( false ); + } + return( true ); } void StreamBase::updateFrameRate( double fps ) { - base_fps = fps; - effective_fps = (base_fps*abs(replay_rate))/ZM_RATE_BASE; - frame_mod = 1; - Debug( 3, "FPS:%.2f, MXFPS:%.2f, BFPS:%.2f, EFPS:%.2f, FM:%d", fps, maxfps, base_fps, effective_fps, frame_mod ); - // Min frame repeat? - while( effective_fps > maxfps ) - { - effective_fps /= 2.0; - frame_mod *= 2; - } - Debug( 3, "aEFPS:%.2f, aFM:%d", effective_fps, frame_mod ); + base_fps = fps; + effective_fps = (base_fps*abs(replay_rate))/ZM_RATE_BASE; + frame_mod = 1; + Debug( 3, "FPS:%.2f, MXFPS:%.2f, BFPS:%.2f, EFPS:%.2f, FM:%d", fps, maxfps, base_fps, effective_fps, frame_mod ); + // Min frame repeat? + while( effective_fps > maxfps ) + { + effective_fps /= 2.0; + frame_mod *= 2; + } + Debug( 3, "aEFPS:%.2f, aFM:%d", effective_fps, frame_mod ); } bool StreamBase::checkCommandQueue() { - if ( sd >= 0 ) + if ( sd >= 0 ) + { + CmdMsg msg; + memset( &msg, 0, sizeof(msg) ); + int nbytes = recvfrom( sd, &msg, sizeof(msg), MSG_DONTWAIT, 0, 0 ); + if ( nbytes < 0 ) { - CmdMsg msg; - memset( &msg, 0, sizeof(msg) ); - int nbytes = recvfrom( sd, &msg, sizeof(msg), MSG_DONTWAIT, 0, 0 ); - if ( nbytes < 0 ) - { - if ( errno != EAGAIN ) - { - Fatal( "recvfrom(), errno = %d, error = %s", errno, strerror(errno) ); - } - } - //else if ( (nbytes != sizeof(msg)) ) - //{ - //Error( "Partial message received, expected %d bytes, got %d", sizeof(msg), nbytes ); - //} - else - { - processCommand( &msg ); - return( true ); - } + if ( errno != EAGAIN ) + { + Fatal( "recvfrom(), errno = %d, error = %s", errno, strerror(errno) ); + } } - return( false ); + //else if ( (nbytes != sizeof(msg)) ) + //{ + //Error( "Partial message received, expected %d bytes, got %d", sizeof(msg), nbytes ); + //} + else + { + processCommand( &msg ); + return( true ); + } + } + return( false ); } Image *StreamBase::prepareImage( Image *image ) { - static int last_scale = 0; - static int last_zoom = 0; - static int last_x = 0; - static int last_y = 0; + static int last_scale = 0; + static int last_zoom = 0; + static int last_x = 0; + static int last_y = 0; - if ( !last_scale ) - last_scale = scale; - if ( !last_zoom ) - last_zoom = zoom; - - // Do not bother to scale zoomed in images, just crop them and let the browser scale - // Works in FF2 but breaks FF3 which doesn't like image sizes changing in mid stream. - bool optimisedScaling = false; - - bool image_copied = false; - - int mag = (scale * zoom) / ZM_SCALE_BASE; - int act_mag = optimisedScaling?(mag > ZM_SCALE_BASE?ZM_SCALE_BASE:mag):mag; - Debug( 3, "Scaling by %d, zooming by %d = magnifying by %d(%d)", scale, zoom, mag, act_mag ); - - int last_mag = (last_scale * last_zoom) / ZM_SCALE_BASE; - int last_act_mag = last_mag > ZM_SCALE_BASE?ZM_SCALE_BASE:last_mag; - Debug( 3, "Last scaling by %d, zooming by %d = magnifying by %d(%d)", last_scale, last_zoom, last_mag, last_act_mag ); - - int base_image_width = image->Width(), base_image_height = image->Height(); - Debug( 3, "Base image width = %d, height = %d", base_image_width, base_image_height ); - - int virt_image_width = (base_image_width * mag) / ZM_SCALE_BASE, virt_image_height = (base_image_height * mag) / ZM_SCALE_BASE; - Debug( 3, "Virtual image width = %d, height = %d", virt_image_width, virt_image_height ); - - int last_virt_image_width = (base_image_width * last_mag) / ZM_SCALE_BASE, last_virt_image_height = (base_image_height * last_mag) / ZM_SCALE_BASE; - Debug( 3, "Last virtual image width = %d, height = %d", last_virt_image_width, last_virt_image_height ); - - int act_image_width = (base_image_width * act_mag ) / ZM_SCALE_BASE, act_image_height = (base_image_height * act_mag ) / ZM_SCALE_BASE; - Debug( 3, "Actual image width = %d, height = %d", act_image_width, act_image_height ); - - int last_act_image_width = (base_image_width * last_act_mag ) / ZM_SCALE_BASE, last_act_image_height = (base_image_height * last_act_mag ) / ZM_SCALE_BASE; - Debug( 3, "Last actual image width = %d, height = %d", last_act_image_width, last_act_image_height ); - - int disp_image_width = (image->Width() * scale) / ZM_SCALE_BASE, disp_image_height = (image->Height() * scale) / ZM_SCALE_BASE; - Debug( 3, "Display image width = %d, height = %d", disp_image_width, disp_image_height ); - - int last_disp_image_width = (image->Width() * last_scale) / ZM_SCALE_BASE, last_disp_image_height = (image->Height() * last_scale) / ZM_SCALE_BASE; - Debug( 3, "Last display image width = %d, height = %d", last_disp_image_width, last_disp_image_height ); - - int send_image_width = (disp_image_width * act_mag ) / mag, send_image_height = (disp_image_height * act_mag ) / mag; - Debug( 3, "Send image width = %d, height = %d", send_image_width, send_image_height ); - - int last_send_image_width = (last_disp_image_width * last_act_mag ) / last_mag, last_send_image_height = (last_disp_image_height * last_act_mag ) / last_mag; - Debug( 3, "Last send image width = %d, height = %d", last_send_image_width, last_send_image_height ); - - if ( mag != ZM_SCALE_BASE ) - { - if ( act_mag != ZM_SCALE_BASE ) - { - Debug( 3, "Magnifying by %d", mag ); - if ( !image_copied ) - { - static Image copy_image; - copy_image.Assign( *image ); - image = ©_image; - image_copied = true; - } - image->Scale( mag ); - } - } - - Debug( 3, "Real image width = %d, height = %d", image->Width(), image->Height() ); - - if ( disp_image_width < virt_image_width || disp_image_height < virt_image_height ) - { - static Box last_crop; - - if ( mag != last_mag || x != last_x || y != last_y ) - { - Debug( 3, "Got click at %d,%d x %d", x, y, mag ); - - //if ( !last_mag ) - //last_mag = mag; - - if ( !(last_disp_image_width < last_virt_image_width || last_disp_image_height < last_virt_image_height) ) - last_crop = Box(); - - Debug( 3, "Recalculating crop" ); - // 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; - Debug( 3, "Got adjusted click at %d%%,%d%%", click_x, click_y ); - - // Convert the click locations to the current image pixels - click_x = ( click_x * act_image_width ) / 100; - click_y = ( click_y * act_image_height ) / 100; - Debug( 3, "Got readjusted click at %d,%d", click_x, click_y ); - - int lo_x = click_x - (send_image_width/2); - if ( lo_x < 0 ) - lo_x = 0; - int hi_x = lo_x + (send_image_width-1); - if ( hi_x >= act_image_width ) - { - hi_x = act_image_width - 1; - lo_x = hi_x - (send_image_width - 1); - } - - int lo_y = click_y - (send_image_height/2); - if ( lo_y < 0 ) - lo_y = 0; - int hi_y = lo_y + (send_image_height-1); - if ( hi_y >= act_image_height ) - { - 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 ); - } - Debug( 3, "Cropping to %d,%d -> %d,%d", last_crop.LoX(), last_crop.LoY(), last_crop.HiX(), last_crop.HiY() ); - if ( !image_copied ) - { - static Image copy_image; - copy_image.Assign( *image ); - image = ©_image; - image_copied = true; - } - image->Crop( last_crop ); - } + if ( !last_scale ) last_scale = scale; + if ( !last_zoom ) last_zoom = zoom; - last_x = x; - last_y = y; - return( image ); + // Do not bother to scale zoomed in images, just crop them and let the browser scale + // Works in FF2 but breaks FF3 which doesn't like image sizes changing in mid stream. + bool optimisedScaling = false; + + bool image_copied = false; + + int mag = (scale * zoom) / ZM_SCALE_BASE; + int act_mag = optimisedScaling?(mag > ZM_SCALE_BASE?ZM_SCALE_BASE:mag):mag; + Debug( 3, "Scaling by %d, zooming by %d = magnifying by %d(%d)", scale, zoom, mag, act_mag ); + + int last_mag = (last_scale * last_zoom) / ZM_SCALE_BASE; + int last_act_mag = last_mag > ZM_SCALE_BASE?ZM_SCALE_BASE:last_mag; + Debug( 3, "Last scaling by %d, zooming by %d = magnifying by %d(%d)", last_scale, last_zoom, last_mag, last_act_mag ); + + int base_image_width = image->Width(), base_image_height = image->Height(); + Debug( 3, "Base image width = %d, height = %d", base_image_width, base_image_height ); + + int virt_image_width = (base_image_width * mag) / ZM_SCALE_BASE, virt_image_height = (base_image_height * mag) / ZM_SCALE_BASE; + Debug( 3, "Virtual image width = %d, height = %d", virt_image_width, virt_image_height ); + + int last_virt_image_width = (base_image_width * last_mag) / ZM_SCALE_BASE, last_virt_image_height = (base_image_height * last_mag) / ZM_SCALE_BASE; + Debug( 3, "Last virtual image width = %d, height = %d", last_virt_image_width, last_virt_image_height ); + + int act_image_width = (base_image_width * act_mag ) / ZM_SCALE_BASE, act_image_height = (base_image_height * act_mag ) / ZM_SCALE_BASE; + Debug( 3, "Actual image width = %d, height = %d", act_image_width, act_image_height ); + + int last_act_image_width = (base_image_width * last_act_mag ) / ZM_SCALE_BASE, last_act_image_height = (base_image_height * last_act_mag ) / ZM_SCALE_BASE; + Debug( 3, "Last actual image width = %d, height = %d", last_act_image_width, last_act_image_height ); + + int disp_image_width = (image->Width() * scale) / ZM_SCALE_BASE, disp_image_height = (image->Height() * scale) / ZM_SCALE_BASE; + Debug( 3, "Display image width = %d, height = %d", disp_image_width, disp_image_height ); + + int last_disp_image_width = (image->Width() * last_scale) / ZM_SCALE_BASE, last_disp_image_height = (image->Height() * last_scale) / ZM_SCALE_BASE; + Debug( 3, "Last display image width = %d, height = %d", last_disp_image_width, last_disp_image_height ); + + int send_image_width = (disp_image_width * act_mag ) / mag, send_image_height = (disp_image_height * act_mag ) / mag; + Debug( 3, "Send image width = %d, height = %d", send_image_width, send_image_height ); + + int last_send_image_width = (last_disp_image_width * last_act_mag ) / last_mag, last_send_image_height = (last_disp_image_height * last_act_mag ) / last_mag; + Debug( 3, "Last send image width = %d, height = %d", last_send_image_width, last_send_image_height ); + + if ( mag != ZM_SCALE_BASE ) + { + if ( act_mag != ZM_SCALE_BASE ) + { + Debug( 3, "Magnifying by %d", mag ); + if ( !image_copied ) + { + static Image copy_image; + copy_image.Assign( *image ); + image = ©_image; + image_copied = true; + } + image->Scale( mag ); + } + } + + Debug( 3, "Real image width = %d, height = %d", image->Width(), image->Height() ); + + if ( disp_image_width < virt_image_width || disp_image_height < virt_image_height ) + { + static Box last_crop; + + if ( mag != last_mag || x != last_x || y != last_y ) + { + Debug( 3, "Got click at %d,%d x %d", x, y, mag ); + + //if ( !last_mag ) + //last_mag = mag; + + if ( !(last_disp_image_width < last_virt_image_width || last_disp_image_height < last_virt_image_height) ) + last_crop = Box(); + + Debug( 3, "Recalculating crop" ); + // 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; + Debug( 3, "Got adjusted click at %d%%,%d%%", click_x, click_y ); + + // Convert the click locations to the current image pixels + click_x = ( click_x * act_image_width ) / 100; + click_y = ( click_y * act_image_height ) / 100; + Debug( 3, "Got readjusted click at %d,%d", click_x, click_y ); + + int lo_x = click_x - (send_image_width/2); + if ( lo_x < 0 ) + lo_x = 0; + int hi_x = lo_x + (send_image_width-1); + if ( hi_x >= act_image_width ) + { + hi_x = act_image_width - 1; + lo_x = hi_x - (send_image_width - 1); + } + + int lo_y = click_y - (send_image_height/2); + if ( lo_y < 0 ) + lo_y = 0; + int hi_y = lo_y + (send_image_height-1); + if ( hi_y >= act_image_height ) + { + 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 ); + } + Debug( 3, "Cropping to %d,%d -> %d,%d", last_crop.LoX(), last_crop.LoY(), last_crop.HiX(), last_crop.HiY() ); + if ( !image_copied ) + { + static Image copy_image; + copy_image.Assign( *image ); + image = ©_image; + image_copied = true; + } + image->Crop( last_crop ); + } + last_scale = scale; + last_zoom = zoom; + last_x = x; + last_y = y; + + return( image ); } bool StreamBase::sendTextFrame( const char *frame_text ) { - Debug( 2, "Sending text frame '%s'", frame_text ); + Debug( 2, "Sending text frame '%s'", frame_text ); - Image image( monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder() ); - image.Annotate( frame_text, image.centreCoord( frame_text ) ); + Image image( monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder() ); + image.Annotate( frame_text, image.centreCoord( frame_text ) ); - if ( scale != 100 ) - { - image.Scale( scale ); - } + if ( scale != 100 ) + { + image.Scale( scale ); + } #if HAVE_LIBAVCODEC - if ( type == MPEG ) + if ( type == MPEG ) + { + if ( !vid_stream ) { - if ( !vid_stream ) - { - vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, image.Colours(), image.SubpixelOrder(), image.Width(), image.Height() ); - fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() ); - vid_stream->OpenStream(); - } - /* double pts = */ vid_stream->EncodeFrame( image.Buffer(), image.Size() ); + vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, image.Colours(), image.SubpixelOrder(), image.Width(), image.Height() ); + fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() ); + vid_stream->OpenStream(); } - else + /* double pts = */ vid_stream->EncodeFrame( image.Buffer(), image.Size() ); + } + else #endif // HAVE_LIBAVCODEC + { + static unsigned char buffer[ZM_MAX_IMAGE_SIZE]; + int n_bytes = 0; + + image.EncodeJpeg( buffer, &n_bytes ); + + fprintf( stdout, "--ZoneMinderFrame\r\n" ); + fprintf( stdout, "Content-Length: %d\r\n", n_bytes ); + fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" ); + if ( fwrite( buffer, n_bytes, 1, stdout ) != 1 ) { - static unsigned char buffer[ZM_MAX_IMAGE_SIZE]; - int n_bytes = 0; - - image.EncodeJpeg( buffer, &n_bytes ); - - fprintf( stdout, "--ZoneMinderFrame\r\n" ); - fprintf( stdout, "Content-Length: %d\r\n", n_bytes ); - fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" ); - if ( fwrite( buffer, n_bytes, 1, stdout ) != 1 ) - { - Error( "Unable to send stream text frame: %s", strerror(errno) ); - return( false ); - } - fprintf( stdout, "\r\n\r\n" ); - fflush( stdout ); + Error( "Unable to send stream text frame: %s", strerror(errno) ); + return( false ); } - last_frame_sent = TV_2_FLOAT( now ); - return( true ); + fprintf( stdout, "\r\n\r\n" ); + fflush( stdout ); + } + last_frame_sent = TV_2_FLOAT( now ); + return( true ); } void StreamBase::openComms() { - if ( connkey > 0 ) + if ( connkey > 0 ) + { + sd = socket( AF_UNIX, SOCK_DGRAM, 0 ); + if ( sd < 0 ) { - sd = socket( AF_UNIX, SOCK_DGRAM, 0 ); - if ( sd < 0 ) - { - Fatal( "Can't create socket: %s", strerror(errno) ); - } - - snprintf( loc_sock_path, sizeof(loc_sock_path), "%s/zms-%06ds.sock", config.path_socks, connkey ); - unlink( loc_sock_path ); - - strncpy( loc_addr.sun_path, loc_sock_path, sizeof(loc_addr.sun_path) ); - loc_addr.sun_family = AF_UNIX; - if ( bind( sd, (struct sockaddr *)&loc_addr, strlen(loc_addr.sun_path)+sizeof(loc_addr.sun_family)) < 0 ) - { - Fatal( "Can't bind: %s", strerror(errno) ); - } - - snprintf( rem_sock_path, sizeof(rem_sock_path), "%s/zms-%06dw.sock", config.path_socks, connkey ); - strncpy( rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path) ); - rem_addr.sun_family = AF_UNIX; + Fatal( "Can't create socket: %s", strerror(errno) ); } + + snprintf( loc_sock_path, sizeof(loc_sock_path), "%s/zms-%06ds.sock", config.path_socks, connkey ); + unlink( loc_sock_path ); + + strncpy( loc_addr.sun_path, loc_sock_path, sizeof(loc_addr.sun_path) ); + loc_addr.sun_family = AF_UNIX; + if ( bind( sd, (struct sockaddr *)&loc_addr, strlen(loc_addr.sun_path)+sizeof(loc_addr.sun_family)) < 0 ) + { + Fatal( "Can't bind: %s", strerror(errno) ); + } + + snprintf( rem_sock_path, sizeof(rem_sock_path), "%s/zms-%06dw.sock", config.path_socks, connkey ); + strncpy( rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path) ); + rem_addr.sun_family = AF_UNIX; + } } void StreamBase::closeComms() { - if ( connkey > 0 ) + if ( connkey > 0 ) + { + if ( sd >= 0 ) { - if ( sd >= 0 ) - { - close( sd ); - sd = -1; - } - if ( loc_sock_path[0] ) - { - unlink( loc_sock_path ); - } + close( sd ); + sd = -1; } + if ( loc_sock_path[0] ) + { + unlink( loc_sock_path ); + } + } } diff --git a/src/zm_stream.h b/src/zm_stream.h index 5f8f0f9c1..13f5e5131 100644 --- a/src/zm_stream.h +++ b/src/zm_stream.h @@ -32,11 +32,11 @@ class Monitor; class StreamBase { -public: + public: typedef enum { JPEG, RAW, ZIP, MPEG } StreamType; - typedef enum { SINGLE, STREAM, ALL, ALL_GAPLESS } StreamMode; + typedef enum { SINGLE, STREAM, ALL, ALL_GAPLESS } StreamMode; -protected: + protected: static const int MAX_STREAM_DELAY = 5; // Seconds static const StreamType DEFAULT_TYPE = JPEG; @@ -46,25 +46,25 @@ protected: enum { DEFAULT_MAXFPS=10 }; enum { DEFAULT_BITRATE=100000 }; -protected: + protected: typedef struct { - int msg_type; - char msg_data[16]; + int msg_type; + char msg_data[16]; } CmdMsg; typedef struct { - int msg_type; - char msg_data[256]; + int msg_type; + char msg_data[256]; } DataMsg; typedef enum { MSG_CMD=1, MSG_DATA_WATCH, MSG_DATA_EVENT } MsgType; typedef enum { CMD_NONE=0, CMD_PAUSE, CMD_PLAY, CMD_STOP, CMD_FASTFWD, CMD_SLOWFWD, CMD_SLOWREV, CMD_FASTREV, CMD_ZOOMIN, CMD_ZOOMOUT, CMD_PAN, CMD_SCALE, CMD_PREV, CMD_NEXT, CMD_SEEK, CMD_VARPLAY, CMD_GET_IMAGE, CMD_QUERY=99 } MsgCommand; -protected: + protected: Monitor *monitor; StreamType type; - StreamMode mode; + StreamMode mode; const char *format; // used to pass to ffmpeg libs int replay_rate; int scale; @@ -73,15 +73,15 @@ protected: int bitrate; unsigned short x, y; -protected: + protected: int connkey; int sd; - char loc_sock_path[PATH_MAX]; + char loc_sock_path[PATH_MAX]; struct sockaddr_un loc_addr; - char rem_sock_path[PATH_MAX]; + char rem_sock_path[PATH_MAX]; struct sockaddr_un rem_addr; -protected: + protected: bool paused; int step; @@ -100,7 +100,7 @@ protected: CmdMsg msg; -protected: + protected: bool loadMonitor( int monitor_id ); bool checkInitialised(); void updateFrameRate( double fps ); @@ -109,73 +109,73 @@ protected: bool checkCommandQueue(); virtual void processCommand( const CmdMsg *msg )=0; -public: + public: StreamBase() { - monitor = 0; + monitor = 0; - type = DEFAULT_TYPE; - mode = STREAM; - format = ""; - replay_rate = DEFAULT_RATE; - scale = DEFAULT_SCALE; - zoom = DEFAULT_ZOOM; - maxfps = DEFAULT_MAXFPS; - bitrate = DEFAULT_BITRATE; + type = DEFAULT_TYPE; + mode = STREAM; + format = ""; + replay_rate = DEFAULT_RATE; + scale = DEFAULT_SCALE; + zoom = DEFAULT_ZOOM; + maxfps = DEFAULT_MAXFPS; + bitrate = DEFAULT_BITRATE; - paused = false; - step = 0; - x = 0; - y = 0; + paused = false; + step = 0; + x = 0; + y = 0; - connkey = 0; - sd = -1; - memset( &loc_sock_path, 0, sizeof(loc_sock_path) ); - memset( &loc_addr, 0, sizeof(loc_addr) ); - memset( &rem_sock_path, 0, sizeof(rem_sock_path) ); - memset( &rem_addr, 0, sizeof(rem_addr) ); + connkey = 0; + sd = -1; + memset( &loc_sock_path, 0, sizeof(loc_sock_path) ); + memset( &loc_addr, 0, sizeof(loc_addr) ); + memset( &rem_sock_path, 0, sizeof(rem_sock_path) ); + memset( &rem_addr, 0, sizeof(rem_addr) ); - base_fps = 0.0; - effective_fps = 0.0; - frame_mod = 1; + base_fps = 0.0; + effective_fps = 0.0; + frame_mod = 1; #if HAVE_LIBAVCODEC - vid_stream = 0; + vid_stream = 0; #endif // HAVE_LIBAVCODEC } virtual ~StreamBase(); - void setStreamType( StreamType p_type ) + void setStreamType( StreamType p_type ) { - type = p_type; + type = p_type; } - void setStreamMode( StreamMode p_mode ) + void setStreamMode( StreamMode p_mode ) { - mode = p_mode; + mode = p_mode; } - void setStreamFormat( const char *p_format ) + void setStreamFormat( const char *p_format ) { - format = p_format; + format = p_format; } - void setStreamScale( int p_scale ) + void setStreamScale( int p_scale ) { - scale = p_scale; + scale = p_scale; } - void setStreamReplayRate( int p_rate ) + void setStreamReplayRate( int p_rate ) { - replay_rate = p_rate; + replay_rate = p_rate; } - void setStreamMaxFPS( double p_maxfps ) + void setStreamMaxFPS( double p_maxfps ) { - maxfps = p_maxfps; + maxfps = p_maxfps; } - void setStreamBitrate( int p_bitrate ) + void setStreamBitrate( int p_bitrate ) { - bitrate = p_bitrate; + bitrate = p_bitrate; } - void setStreamQueue( int p_connkey ) + void setStreamQueue( int p_connkey ) { - connkey = p_connkey; + connkey = p_connkey; } virtual void openComms(); virtual void closeComms(); diff --git a/web/api/app/Plugin/Crud b/web/api/app/Plugin/Crud index e22c1563a..c3976f147 160000 --- a/web/api/app/Plugin/Crud +++ b/web/api/app/Plugin/Crud @@ -1 +1 @@ -Subproject commit e22c1563a51d86aac0d5054beee28b4afb60c802 +Subproject commit c3976f1478c681b0bbc132ec3a3e82c3984eeed5