diff --git a/distros/debian_cmake/links b/distros/debian_cmake/links index 9f24ba6d1..5560a100a 100644 --- a/distros/debian_cmake/links +++ b/distros/debian_cmake/links @@ -1,4 +1,3 @@ var/cache/zoneminder/events usr/share/zoneminder/events var/cache/zoneminder/images usr/share/zoneminder/images var/cache/zoneminder/temp usr/share/zoneminder/temp -usr/lib/zoneminder/cgi-bin usr/share/zoneminder/cgi-bin diff --git a/distros/ubuntu1204/apache.conf b/distros/ubuntu1204/apache.conf index 13b8b9c44..262db423f 100644 --- a/distros/ubuntu1204/apache.conf +++ b/distros/ubuntu1204/apache.conf @@ -12,10 +12,10 @@ Alias /zm /usr/share/zoneminder/www - php_flag register_globals off - Options Indexes FollowSymLinks - - DirectoryIndex index.php - + php_flag register_globals off + Options Indexes FollowSymLinks + + DirectoryIndex index.php + diff --git a/distros/ubuntu1504_cmake/zoneminder.postinst b/distros/ubuntu1504_cmake/zoneminder.postinst index f465bd7ea..82b256e6b 100644 --- a/distros/ubuntu1504_cmake/zoneminder.postinst +++ b/distros/ubuntu1504_cmake/zoneminder.postinst @@ -23,7 +23,7 @@ if [ "$1" = "configure" ]; then zmupdate.pl --nointeractive else echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)" - fi; + fi; fi diff --git a/docs/userguide/definemonitor.rst b/docs/userguide/definemonitor.rst index c2006eed9..d69b44425 100644 --- a/docs/userguide/definemonitor.rst +++ b/docs/userguide/definemonitor.rst @@ -21,6 +21,9 @@ Monitor Tab Name The name for your monitor. This should be made up of alphanumeric characters (a-z,A-Z,0-9) and hyphen (-) and underscore(_) only. Whitespace is not allowed. +Server + Multi-Server implementation allows the ability to define multiple ZoneMinder servers sharing a single database. When servers are configured this setting allows you nominate the server for each monitor. + Source Type This determines whether the camera is a local one attached to a physical video or USB port on your machine, a remote network camera or an image source that is represented by a file (for instance periodically downloaded from a alternate location). Choosing one or the other affects which set of options are shown in the Source tab. diff --git a/docs/userguide/options/images/Options_Servers.png b/docs/userguide/options/images/Options_Servers.png new file mode 100644 index 000000000..2ca47edba Binary files /dev/null and b/docs/userguide/options/images/Options_Servers.png differ diff --git a/docs/userguide/options/options_servers.rst b/docs/userguide/options/options_servers.rst new file mode 100644 index 000000000..4736c305a --- /dev/null +++ b/docs/userguide/options/options_servers.rst @@ -0,0 +1,12 @@ +Options - Servers +--------------- + +.. image:: images/Options_Servers.png + +Servers tab is used for setting up multiple ZoneMinder servers sharing the same database and using a shared file share for all event data. To add a new server use the Add Server button. All that is required is a Name for the Server and Hostname. + +To delete a server mark that server and click the Delete button. + +Please note that all servers must have a functional web UI as the live view must come from the monitor's host server. + +On each server, you will have to edit /etc/zm/zm.conf and set either ZM_SERVER_NAME= diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 1ed9fd621..501a71027 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -398,11 +398,11 @@ Monitor::Monitor( + 64; /* Padding used to permit aligning the images buffer to 16 byte boundary */ Debug( 1, "mem.size=%d", mem_size ); - mem_ptr = NULL; + mem_ptr = NULL; if ( purpose == CAPTURE ) { - this->connect(); - if ( ! mem_ptr ) exit(-1); + this->connect(); + if ( ! mem_ptr ) exit(-1); memset( mem_ptr, 0, mem_size ); shared_data->size = sizeof(SharedData); shared_data->active = enabled; @@ -429,8 +429,8 @@ Monitor::Monitor( trigger_data->trigger_showtext[0] = 0; shared_data->valid = true; } else if ( purpose == ANALYSIS ) { - this->connect(); - if ( ! mem_ptr ) exit(-1); + this->connect(); + if ( ! mem_ptr ) exit(-1); shared_data->state = IDLE; shared_data->last_read_time = 0; shared_data->alarm_x = -1; @@ -446,9 +446,9 @@ Monitor::Monitor( } } - // 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.. + // 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 ); + 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 ) }; @@ -533,28 +533,28 @@ bool Monitor::connect() { // 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; + 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 { + 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 ); + 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 ); + 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 ); + 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 ); @@ -575,9 +575,9 @@ bool Monitor::connect() { 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))); + /* 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++ ) @@ -608,39 +608,39 @@ bool Monitor::connect() { } } - return true; + return true; } Monitor::~Monitor() { - if ( timestamps ) { - delete[] timestamps; - timestamps = 0; - } - if ( images ) { - delete[] images; - images = 0; - } + if ( timestamps ) { + delete[] timestamps; + timestamps = 0; + } + if ( images ) { + delete[] images; + images = 0; + } if ( privacy_bitmask ) { delete[] privacy_bitmask; privacy_bitmask = NULL; } - if ( mem_ptr ) { - if ( event ) - Info( "%s: %03d - Closing event %d, shutting down", name, image_count, event->Id() ); - closeEvent(); + 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 + 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++ ) { @@ -651,49 +651,49 @@ Monitor::~Monitor() delete camera; delete storage; - 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; + 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; - if ( analysis_fps ) - { - for ( int i = 0; i < pre_event_buffer_count; i++ ) - { - delete pre_event_buffer[i].image; - delete pre_event_buffer[i].timestamp; - } - delete[] pre_event_buffer; - } - } - else if ( purpose == CAPTURE ) - { - shared_data->valid = false; - memset( mem_ptr, 0, mem_size ); - } + if ( analysis_fps ) + { + for ( int i = 0; i < pre_event_buffer_count; i++ ) + { + delete pre_event_buffer[i].image; + delete pre_event_buffer[i].timestamp; + } + delete[] pre_event_buffer; + } + } + 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[] ) @@ -744,7 +744,7 @@ int Monitor::GetImage( int index, int scale ) 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 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; @@ -1244,32 +1244,32 @@ bool Monitor::CheckSignal( const Image *image ) 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; - } - } + 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 ); @@ -1740,7 +1740,7 @@ bool Monitor::Analyse() if ( config.create_analysis_images ) { bool got_anal_image = false; - alarm_image.Assign( *snap_image ); + alarm_image.Assign( *snap_image ); for( int i = 0; i < n_zones; i++ ) { if ( zones[i]->Alarmed() ) @@ -2090,24 +2090,24 @@ Debug( 1, "Server ID %d", staticConfig.SERVER_ID ); const char *device = dbrow[col]; col++; int channel = atoi(dbrow[col]); col++; int format = atoi(dbrow[col]); col++; - bool v4l_multi_buffer = config.v4l_multi_buffer; - if ( dbrow[col] ) { - if (*dbrow[col] == '0' ) { - v4l_multi_buffer = false; - } else if ( *dbrow[col] == '1' ) { - v4l_multi_buffer = true; - } - } - 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; - } + bool v4l_multi_buffer = config.v4l_multi_buffer; + if ( dbrow[col] ) { + if (*dbrow[col] == '0' ) { + v4l_multi_buffer = false; + } else if ( *dbrow[col] == '1' ) { + v4l_multi_buffer = true; + } + } + 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++; + col++; const char *method = dbrow[col]; col++; int width = atoi(dbrow[col]); col++; @@ -2164,8 +2164,8 @@ Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); device, channel, format, - v4l_multi_buffer, - v4l_captures_per_frame, + v4l_multi_buffer, + v4l_captures_per_frame, method, cam_width, cam_height, @@ -3019,30 +3019,30 @@ Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); 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); - } + 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 ) { @@ -3059,18 +3059,18 @@ int Monitor::Capture() 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 ); - } + /* 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 ) 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 diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index 29f73d117..8aa7f4dc9 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -5,16 +5,19 @@ require_once( 'Server.php' ); class Monitor { public function __construct( $IdOrRow ) { $row = NULL; - if ( is_int( $IdOrRow ) ) { - $row = dbFetchOne( 'SELECT * FROM Monitors WHERE Id=?', NULL, array( $IdOrRow ) ); - } else if ( is_numeric( $IdOrRow ) ) { - $row = dbFetchOne( 'SELECT * FROM Monitors WHERE Id=?', NULL, array( $IdOrRow ) ); - } else if ( is_array($IdOrRow) ) { - $row = $IdOrRow; - } else { - Error("Unknown argument passed to Monitor Constructor ($IdOrRow)"); - return; - } + if ( $IdOrRow ) { + if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) { + $row = dbFetchOne( 'SELECT * FROM Monitors WHERE Id=?', NULL, array( $IdOrRow ) ); + if ( ! $row ) { + Error("Unable to load Server record for Id=" . $IdOrRow ); + } + } elseif ( is_array( $IdOrRow ) ) { + $row = $IdOrRow; + } else { + Error("Unknown argument passed to Monitor Constructor ($IdOrRow)"); + return; + } + } # end if isset($IdOrRow) if ( $row ) { foreach ($row as $k => $v) {