diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index edd2538f1..300b44ed6 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -450,6 +450,7 @@ CREATE TABLE `Monitors` ( `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket','VNC') NOT NULL default 'Local', `Function` enum('None','Monitor','Modect','Record','Mocord','Nodect') NOT NULL default 'Monitor', `Enabled` tinyint(3) unsigned NOT NULL default '1', + `DecodingEnabled` tinyint(3) unsigned NOT NULL default '1', `LinkedMonitors` varchar(255), `Triggers` set('X10') NOT NULL default '', `ONVIF_URL` VARCHAR(255) NOT NULL DEFAULT '', diff --git a/db/zm_update-1.35.16.sql b/db/zm_update-1.35.16.sql new file mode 100644 index 000000000..50c952bca --- /dev/null +++ b/db/zm_update-1.35.16.sql @@ -0,0 +1,12 @@ + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitors' + AND column_name = 'DecodingEnabled' + ) > 0, + "SELECT 'Column DecodingEnabled already exists in Monitors'", + "ALTER TABLE Monitors ADD `DecodingEnabled` tinyint(3) unsigned NOT NULL default '1' AFTER `Enabled`" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index 6d2e2a88e..9fd777f23 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -28,7 +28,7 @@ %global _hardened_build 1 Name: zoneminder -Version: 1.35.15 +Version: 1.35.16 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons diff --git a/scripts/zmstats.pl.in b/scripts/zmstats.pl.in index 8aef4ddc6..81fdd3129 100644 --- a/scripts/zmstats.pl.in +++ b/scripts/zmstats.pl.in @@ -91,6 +91,9 @@ while( 1 ) { } } # end if ZM_LOG_DATABASE_LIMIT + # Delete any sessions that are more ethan a week old. Limiting to 100 because mysql sucks + zmDbDo('DELETE FROM Sessions WHERE access < ? LIMIT 100', time - (60*60*24*7)); + sleep($Config{ZM_STATS_UPDATE_INTERVAL}); } # end while (1) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index a19e31c9b..c9ca98a66 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -71,8 +71,8 @@ // This is the official SQL (and ordering of the fields) to load a Monitor. // It will be used whereever a Monitor dbrow is needed. WHERE conditions can be appended std::string load_monitor_sql = -"SELECT `Id`, `Name`, `ServerId`, `StorageId`, `Type`, `Function`+0, `Enabled`, `LinkedMonitors`, " -"`AnalysisFPSLimit`, `AnalysisUpdateDelay`, `MaxFPS`, `AlarmMaxFPS`," +"SELECT `Id`, `Name`, `ServerId`, `StorageId`, `Type`, `Function`+0, `Enabled`, `DecodingEnabled`, " +"`LinkedMonitors`, `AnalysisFPSLimit`, `AnalysisUpdateDelay`, `MaxFPS`, `AlarmMaxFPS`," "`Device`, `Channel`, `Format`, `V4LMultiBuffer`, `V4LCapturesPerFrame`, " // V4L Settings "`Protocol`, `Method`, `Options`, `User`, `Pass`, `Host`, `Port`, `Path`, `Width`, `Height`, `Colours`, `Palette`, `Orientation`+0, `Deinterlacing`, " "`DecoderHWAccelName`, `DecoderHWAccelDevice`, `RTSPDescribe`, " @@ -272,6 +272,7 @@ Monitor::Monitor() type(LOCAL), function(NONE), enabled(0), + decoding_enabled(0), //protocol //method //options @@ -362,7 +363,7 @@ Monitor::Monitor() /* std::string load_monitor_sql = - "SELECT Id, Name, ServerId, StorageId, Type, Function+0, Enabled, LinkedMonitors, " + "SELECT Id, Name, ServerId, StorageId, Type, Function+0, Enabled, DecodingEnabled, LinkedMonitors, " "AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS," "Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, " // V4L Settings "Protocol, Method, Options, User, Pass, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, RTSPDescribe, " @@ -409,6 +410,7 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) { col++; function = (Function)atoi(dbrow[col]); col++; enabled = dbrow[col] ? atoi(dbrow[col]) : 0; col++; + decoding_enabled = dbrow[col] ? atoi(dbrow[col]) : 0; col++; ReloadLinkedMonitors(dbrow[col]); col++; @@ -596,6 +598,18 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) { if ( mkdir(monitor_dir.c_str(), 0755) && ( errno != EEXIST ) ) { Error("Can't mkdir %s: %s", monitor_dir.c_str(), strerror(errno)); } + + // Do this here to save a few cycles with all the comparisons + decoding_enabled = !( + ( function == RECORD or function == NODECT ) + and + ( savejpegs == 0 ) + and + ( videowriter == H264PASSTHROUGH ) + and + !decoding_enabled + ); + Debug(1, "Decoding enabled: %d", decoding_enabled); } else if ( purpose == ANALYSIS ) { // FIXME Now that zma is a thread, this might not get called. Unless maybe we are redoing motion detection in a separate program. while ( @@ -2160,7 +2174,7 @@ void Monitor::ReloadZones() { 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 ) { - 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; @@ -2489,7 +2503,6 @@ int Monitor::Capture() { } shared_data->signal = signal_check_points ? CheckSignal(capture_image) : true; - Debug(1, "signal check points? %d", signal_check_points); shared_data->last_write_index = index; shared_data->last_write_time = image_buffer[index].timestamp->tv_sec; image_count++; @@ -2826,9 +2839,12 @@ int Monitor::PrimeCapture() { video_stream_id = camera->get_VideoStreamId(); audio_stream_id = camera->get_AudioStreamId(); packetqueue = new zm_packetqueue(image_buffer_count, video_stream_id, audio_stream_id); + Debug(2, "Video stream id is %d, audio is %d, minimum_packets to keep in buffer %d", + video_stream_id, audio_stream_id, pre_event_buffer_count); + } else { + Debug(2, "Not Video stream id is %d, audio is %d, minimum_packets to keep in buffer %d", + video_stream_id, audio_stream_id, pre_event_buffer_count); } - Debug(2, "Video stream id is %d, audio is %d, minimum_packets to keep in buffer %d", - video_stream_id, audio_stream_id, pre_event_buffer_count); return ret; } @@ -2839,6 +2855,7 @@ int Monitor::Close() { delete packetqueue; packetqueue = nullptr; } + Debug(1, "Closing camera"); return camera->Close(); }; Monitor::Orientation Monitor::getOrientation() const { return orientation; } diff --git a/src/zm_monitor.h b/src/zm_monitor.h index d79475a12..13a14a140 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -245,6 +245,7 @@ protected: CameraType type; Function function; // What the monitor is doing bool enabled; // Whether the monitor is enabled or asleep + bool decoding_enabled; // Whether the monitor will decode h264/h265 packets std::string protocol; std::string method; @@ -424,6 +425,9 @@ public: return false; return enabled; } + inline bool DecodingEnabled() const { + return decoding_enabled; + } inline const char *EventPrefix() const { return event_prefix; } inline bool Ready() const { if ( function <= MONITOR ) { diff --git a/src/zm_packetqueue.cpp b/src/zm_packetqueue.cpp index 66e50ba20..8aa5ea1fc 100644 --- a/src/zm_packetqueue.cpp +++ b/src/zm_packetqueue.cpp @@ -48,14 +48,21 @@ zm_packetqueue::~zm_packetqueue() { } while ( !pktQueue.empty() ) { + Debug(1, "Fronting packet %d", pktQueue.empty()); ZMPacket * packet = pktQueue.front(); + Debug(1, "poppng packet %d", packet->image_index); pktQueue.pop_front(); - delete packet; + if ( packet->image_index == -1 ) { + Debug(1, "Deletng packet"); + delete packet; + } } delete[] packet_counts; + Debug(1, "Done in destrcutor"); packet_counts = nullptr; mutex.unlock(); + condition.notify_all(); } /* Enqueues the given packet. Will maintain the analysis_it pointer and image packet counts. @@ -351,10 +358,13 @@ ZMPacket *zm_packetqueue::get_analysis_packet() { Debug(1, "Locking in get_analysis_packet"); std::unique_lock lck(mutex); - while ( ((! pktQueue.size()) || ( analysis_it == pktQueue.end() )) && !zm_terminate ) { + while ( ((! pktQueue.size()) or ( analysis_it == pktQueue.end() )) and !zm_terminate and !deleting ) { Debug(2, "waiting. Queue size %d analysis_it == end? %d", pktQueue.size(), ( analysis_it == pktQueue.end() ) ); condition.wait(lck); } + if ( deleting ) { + return nullptr; + } //Debug(2, "Distance from head: (%d)", std::distance( pktQueue.begin(), analysis_it ) ); //Debug(2, "Distance from end: (%d)", std::distance( analysis_it, pktQueue.end() ) ); @@ -364,6 +374,7 @@ ZMPacket *zm_packetqueue::get_analysis_packet() { Debug(2,"waiting. Queue size %d analysis_it == end? %d", pktQueue.size(), ( analysis_it == pktQueue.end() ) ); condition.wait(lck); if ( deleting ) { + Debug(1, "deleting"); // packetqueue is being deleted, do not assume we have a lock on the packet return nullptr; } diff --git a/src/zm_video.cpp b/src/zm_video.cpp index b24905ece..2d7033a84 100644 --- a/src/zm_video.cpp +++ b/src/zm_video.cpp @@ -553,8 +553,8 @@ int ParseEncoderParameters( } valueoffset = line.find('='); - if ( valueoffset == std::string::npos || valueoffset+1 >= line.length() || valueoffset == 0 ) { - Warning("Failed parsing encoder parameters line %d: Invalid pair", lineno); + if ( valueoffset == std::string::npos || (valueoffset+1 >= line.length()) || (valueoffset == 0) ) { + Warning("Failed parsing encoder parameters line %d %s: Invalid pair", lineno, line.c_str()); continue; } diff --git a/src/zmc.cpp b/src/zmc.cpp index 60512f83c..2dbd024ed 100644 --- a/src/zmc.cpp +++ b/src/zmc.cpp @@ -319,14 +319,10 @@ int main(int argc, char *argv[]) { if ( monitors[i]->PreCapture() < 0 ) { Error("Failed to pre-capture monitor %d %d (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors); - monitors[i]->Close(); result = -1; break; } if ( monitors[i]->Capture() < 0 ) { - Error("Failed to capture image from monitor %d %s (%d/%d)", - monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors); - monitors[i]->Close(); Error("Failed to capture image from monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors); result = -1; @@ -335,7 +331,6 @@ int main(int argc, char *argv[]) { if ( monitors[i]->PostCapture() < 0 ) { Error("Failed to post-capture monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors); - monitors[i]->Close(); result = -1; break; } @@ -383,6 +378,9 @@ int main(int argc, char *argv[]) { zm_reload = false; } // end if zm_reload } // end while ! zm_terminate and connected + for ( int i = 0; i < n_monitors; i++ ) { + monitors[i]->Close(); + } delete [] alarm_capture_delays; delete [] capture_delays; @@ -398,6 +396,7 @@ int main(int argc, char *argv[]) { } } // end foreach monitor delete [] analysis_threads; + } // end while ! zm_terminate outer connection loop Debug(1,"Updating Monitor status"); diff --git a/version b/version index aa76ce17f..efa14e52b 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.35.15 +1.35.16 diff --git a/web/ajax/events.php b/web/ajax/events.php index 37e225527..51949f84e 100644 --- a/web/ajax/events.php +++ b/web/ajax/events.php @@ -6,7 +6,7 @@ $data = array(); // INITIALIZE AND CHECK SANITY // -if ( !canEdit('Events') ) $message = 'Insufficient permissions for user '.$user['Username']; +if ( !canView('Events') ) $message = 'Insufficient permissions for user '.$user['Username']; if ( empty($_REQUEST['task']) ) { $message = 'Must specify a task'; @@ -74,10 +74,22 @@ if ( isset($_REQUEST['limit']) ) { switch ( $task ) { case 'archive' : + foreach ( $eids as $eid ) archiveRequest($task, $eid); + break; case 'unarchive' : + # The idea is that anyone can archive, but only people with Event Edit permission can unarchive.. + if ( !canEdit('Events') ) { + ajaxError('Insufficient permissions for user '.$user['Username']); + return; + } foreach ( $eids as $eid ) archiveRequest($task, $eid); break; case 'delete' : + if ( !canEdit('Events') ) { + ajaxError('Insufficient permissions for user '.$user['Username']); + return; + } + foreach ( $eids as $eid ) $data[] = deleteRequest($eid); break; case 'query' : @@ -217,12 +229,12 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim $event = new ZM\Event($row); $scale = intval(5*100*ZM_WEB_LIST_THUMB_WIDTH / $event->Width()); - $imgSrc = $event->getThumbnailSrc(array(),'&'); + $imgSrc = $event->getThumbnailSrc(array(), '&'); $streamSrc = $event->getStreamSrc(array( 'mode'=>'jpeg', 'scale'=>$scale, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'replay'=>'single', 'rate'=>'400'), '&'); // Modify the row data as needed - $row['imgHtml'] = '' .validHtmlStr('Event ' .$event->Id()). ''; + $row['imgHtml'] = 'Event '.$event->Id().''; $row['Name'] = validHtmlStr($row['Name']); $row['Archived'] = $row['Archived'] ? translate('Yes') : translate('No'); $row['Emailed'] = $row['Emailed'] ? translate('Yes') : translate('No'); diff --git a/web/ajax/frames.php b/web/ajax/frames.php index e85988dbc..e129200e7 100644 --- a/web/ajax/frames.php +++ b/web/ajax/frames.php @@ -161,15 +161,30 @@ function queryRequest($eid, $search, $advsearch, $sort, $offset, $order, $limit) $returned_rows = array(); foreach ( array_slice($filtered_rows, $offset, $limit) as $row ) { if ( ZM_WEB_LIST_THUMBS ) { + + # Build the path to the potential analysis image + $analImage = sprintf('%0'.ZM_EVENT_IMAGE_DIGITS.'d-analyse.jpg', $row['FrameId']); + $analPath = $Event->Path().'/'.$analImage; + $alarmFrame = $row['Type'] == 'Alarm'; + $hasAnalImage = $alarmFrame && file_exists($analPath) && filesize($analPath); + + # Our base img source component, which we will add on to $base_img_src = '?view=image&fid=' .$row['Id']; + + # if an analysis images exists, use it as the thumbnail + if ( $hasAnalImage ) $base_img_src .= '&show=analyse'; + + # Build the subcomponents needed for the image source $ratio_factor = $Monitor->ViewHeight() / $Monitor->ViewWidth(); $thmb_width = ZM_WEB_LIST_THUMB_WIDTH ? 'width='.ZM_WEB_LIST_THUMB_WIDTH : ''; $thmb_height = 'height="'.( ZM_WEB_LIST_THUMB_HEIGHT ? ZM_WEB_LIST_THUMB_HEIGHT : ZM_WEB_LIST_THUMB_WIDTH*$ratio_factor ) .'"'; $thmb_fn = 'filename=' .$Event->MonitorId(). '_' .$row['EventId']. '_' .$row['FrameId']. '.jpg'; + + # Assemble the scaled and unscaled image source image source components $img_src = join('&', array_filter(array($base_img_src, $thmb_width, $thmb_height, $thmb_fn))); $full_img_src = join('&', array_filter(array($base_img_src, $thmb_fn))); - $frame_src = '?view=frame&eid=' .$row['EventId']. '&fid=' .$row['FrameId']; + # finally, we assemble the the entire thumbnail img src structure, whew $row['Thumbnail'] = ''; } $returned_rows[] = $row; diff --git a/web/ajax/modals/function.php b/web/ajax/modals/function.php index e6bab2718..08b470840 100644 --- a/web/ajax/modals/function.php +++ b/web/ajax/modals/function.php @@ -37,11 +37,39 @@ if ( !canEdit('Monitors') ) return; +
+ + +'.$OLANG['FUNCTION_DECODING_ENABLED']['Help'].'
'; + } +?> + +