From 3b97fdadc45861f1c2069f2005786750ebd6777b Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 31 Jul 2017 15:57:07 -0400 Subject: [PATCH 01/20] Fix memleak by freeing input and output frames --- src/zm_videostore.cpp | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 0d6056e3e..2fd710dd8 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -189,6 +189,8 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in, audio_output_codec = NULL; audio_input_context = NULL; audio_output_stream = NULL; + input_frame = NULL; + output_frame = NULL; #ifdef HAVE_LIBAVRESAMPLE resample_context = NULL; #endif @@ -391,6 +393,15 @@ Debug(2, "writing flushed packet pts(%d) dts(%d) duration(%d)", pkt.pts, pkt.dts /* free the stream */ avformat_free_context(oc); + + if ( input_frame ) { + av_frame_free( &input_frame ); + input_frame = NULL; + } + if ( output_frame ) { + av_frame_free( &output_frame ); + output_frame = NULL; + } } bool VideoStore::setup_resampler() { @@ -495,13 +506,13 @@ bool VideoStore::setup_resampler() { } /** Create a new frame to store the audio samples. */ - if (!(input_frame = zm_av_frame_alloc())) { + if ( !(input_frame = zm_av_frame_alloc()) ) { Error("Could not allocate input frame"); return false; } /** Create a new frame to store the audio samples. */ - if (!(output_frame = zm_av_frame_alloc())) { + if ( !(output_frame = zm_av_frame_alloc()) ) { Error("Could not allocate output frame"); av_frame_free( &input_frame ); return false; @@ -618,7 +629,7 @@ int VideoStore::writeVideoFramePacket( AVPacket *ipkt ) { int duration; //Scale the PTS of the outgoing packet to be the correct time base - if (ipkt->pts != AV_NOPTS_VALUE) { + if ( ipkt->pts != AV_NOPTS_VALUE ) { if ( ! video_last_pts ) { // This is the first packet. @@ -696,9 +707,9 @@ int VideoStore::writeVideoFramePacket( AVPacket *ipkt ) { } AVPacket safepkt; - memcpy(&safepkt, &opkt, sizeof(AVPacket)); + memcpy( &safepkt, &opkt, sizeof(AVPacket) ); -Debug(1, "writing video packet pts(%d) dts(%d) duration(%d)", opkt.pts, opkt.dts, opkt.duration ); + Debug(1, "writing video packet pts(%d) dts(%d) duration(%d)", opkt.pts, opkt.dts, opkt.duration ); if ((opkt.data == NULL)||(opkt.size < 1)) { Warning("%s:%d: Mangled AVPacket: discarding frame", __FILE__, __LINE__ ); dumpPacket( ipkt); @@ -714,10 +725,14 @@ Debug(1, "writing video packet pts(%d) dts(%d) duration(%d)", opkt.pts, opkt.dts video_previous_dts = opkt.dts; // Unsure if av_interleaved_write_frame() clobbers opkt.dts when out of order, so storing in advance video_previous_pts = opkt.pts; ret = av_interleaved_write_frame(oc, &opkt); - if(ret<0){ + if ( ret < 0 ) { // There's nothing we can really do if the frame is rejected, just drop it and get on with the next Warning("%s:%d: Writing frame [av_interleaved_write_frame()] failed: %s(%d) ", __FILE__, __LINE__, av_make_error_string(ret).c_str(), (ret)); dumpPacket(&safepkt); +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + zm_dump_codecpar( video_input_stream->codecpar ); + zm_dump_codecpar( video_output_stream->codecpar ); +#endif } } From 7e5edf1623878dc6376e2dbb5c05128e384ec6fc Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 2 Aug 2017 16:23:37 -0400 Subject: [PATCH 02/20] Always set the packet stream_index to the id of the output stream. --- src/zm_videostore.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 2fd710dd8..4df2d902d 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -698,13 +698,7 @@ int VideoStore::writeVideoFramePacket( AVPacket *ipkt ) { opkt.data = ipkt->data; opkt.size = ipkt->size; - // Some camera have audio on stream 0 and video on stream 1. So when we remove the audio, video stream has to go on 0 - if ( ipkt->stream_index > 0 and ! audio_output_stream ) { - Debug(1,"Setting stream index to 0 instead of %d", ipkt->stream_index ); - opkt.stream_index = 0; - } else { - opkt.stream_index = ipkt->stream_index; - } + opkt.stream_index = video_output_stream->index; AVPacket safepkt; memcpy( &safepkt, &opkt, sizeof(AVPacket) ); @@ -929,8 +923,7 @@ int VideoStore::writeAudioFramePacket( AVPacket *ipkt ) { // pkt.pos: byte position in stream, -1 if unknown opkt.pos = -1; - opkt.stream_index = ipkt->stream_index; - Debug(2, "Stream index is %d", opkt.stream_index ); + opkt.stream_index = audio_output_stream->index;//ipkt->stream_index; AVPacket safepkt; memcpy(&safepkt, &opkt, sizeof(AVPacket)); From 5a73c38237ae545d8d640d0e0d129bee3ff0cb7e Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 2 Aug 2017 16:29:23 -0400 Subject: [PATCH 03/20] video storage fixes (#1958) * use a monitor object instead of just a db array. * fix braces, spacing, move pod docs to bottom * Fix memleak by freeing input and output frames * Always set the packet stream_index to the id of the output stream. --- src/zm_videostore.cpp | 38 +- web/skins/classic/views/monitor.php | 889 ++++++++++++++-------------- 2 files changed, 481 insertions(+), 446 deletions(-) diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 0d6056e3e..4df2d902d 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -189,6 +189,8 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in, audio_output_codec = NULL; audio_input_context = NULL; audio_output_stream = NULL; + input_frame = NULL; + output_frame = NULL; #ifdef HAVE_LIBAVRESAMPLE resample_context = NULL; #endif @@ -391,6 +393,15 @@ Debug(2, "writing flushed packet pts(%d) dts(%d) duration(%d)", pkt.pts, pkt.dts /* free the stream */ avformat_free_context(oc); + + if ( input_frame ) { + av_frame_free( &input_frame ); + input_frame = NULL; + } + if ( output_frame ) { + av_frame_free( &output_frame ); + output_frame = NULL; + } } bool VideoStore::setup_resampler() { @@ -495,13 +506,13 @@ bool VideoStore::setup_resampler() { } /** Create a new frame to store the audio samples. */ - if (!(input_frame = zm_av_frame_alloc())) { + if ( !(input_frame = zm_av_frame_alloc()) ) { Error("Could not allocate input frame"); return false; } /** Create a new frame to store the audio samples. */ - if (!(output_frame = zm_av_frame_alloc())) { + if ( !(output_frame = zm_av_frame_alloc()) ) { Error("Could not allocate output frame"); av_frame_free( &input_frame ); return false; @@ -618,7 +629,7 @@ int VideoStore::writeVideoFramePacket( AVPacket *ipkt ) { int duration; //Scale the PTS of the outgoing packet to be the correct time base - if (ipkt->pts != AV_NOPTS_VALUE) { + if ( ipkt->pts != AV_NOPTS_VALUE ) { if ( ! video_last_pts ) { // This is the first packet. @@ -687,18 +698,12 @@ int VideoStore::writeVideoFramePacket( AVPacket *ipkt ) { opkt.data = ipkt->data; opkt.size = ipkt->size; - // Some camera have audio on stream 0 and video on stream 1. So when we remove the audio, video stream has to go on 0 - if ( ipkt->stream_index > 0 and ! audio_output_stream ) { - Debug(1,"Setting stream index to 0 instead of %d", ipkt->stream_index ); - opkt.stream_index = 0; - } else { - opkt.stream_index = ipkt->stream_index; - } + opkt.stream_index = video_output_stream->index; AVPacket safepkt; - memcpy(&safepkt, &opkt, sizeof(AVPacket)); + memcpy( &safepkt, &opkt, sizeof(AVPacket) ); -Debug(1, "writing video packet pts(%d) dts(%d) duration(%d)", opkt.pts, opkt.dts, opkt.duration ); + Debug(1, "writing video packet pts(%d) dts(%d) duration(%d)", opkt.pts, opkt.dts, opkt.duration ); if ((opkt.data == NULL)||(opkt.size < 1)) { Warning("%s:%d: Mangled AVPacket: discarding frame", __FILE__, __LINE__ ); dumpPacket( ipkt); @@ -714,10 +719,14 @@ Debug(1, "writing video packet pts(%d) dts(%d) duration(%d)", opkt.pts, opkt.dts video_previous_dts = opkt.dts; // Unsure if av_interleaved_write_frame() clobbers opkt.dts when out of order, so storing in advance video_previous_pts = opkt.pts; ret = av_interleaved_write_frame(oc, &opkt); - if(ret<0){ + if ( ret < 0 ) { // There's nothing we can really do if the frame is rejected, just drop it and get on with the next Warning("%s:%d: Writing frame [av_interleaved_write_frame()] failed: %s(%d) ", __FILE__, __LINE__, av_make_error_string(ret).c_str(), (ret)); dumpPacket(&safepkt); +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + zm_dump_codecpar( video_input_stream->codecpar ); + zm_dump_codecpar( video_output_stream->codecpar ); +#endif } } @@ -914,8 +923,7 @@ int VideoStore::writeAudioFramePacket( AVPacket *ipkt ) { // pkt.pos: byte position in stream, -1 if unknown opkt.pos = -1; - opkt.stream_index = ipkt->stream_index; - Debug(2, "Stream index is %d", opkt.stream_index ); + opkt.stream_index = audio_output_stream->index;//ipkt->stream_index; AVPacket safepkt; memcpy(&safepkt, &opkt, sizeof(AVPacket)); diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 4bc808122..dcc76e662 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -51,17 +51,94 @@ if ( ! $Server ) { } if ( ! empty($_REQUEST['mid']) ) { - $monitor = dbFetchMonitor( $_REQUEST['mid'] ); + $monitor = new Monitor( $_REQUEST['mid'] ); if ( ZM_OPT_X10 ) $x10Monitor = dbFetchOne( 'SELECT * FROM TriggersX10 WHERE MonitorId = ?', NULL, array($_REQUEST['mid']) ); } else { $nextId = getTableAutoInc( 'Monitors' ); - $monitor = getMonitorObject($_REQUEST['dupId']); - $clonedName = $monitor['Name']; - $monitor['Name'] = translate('Monitor').'-'.$nextId; - $monitor['Id']='0'; -} + if ( ! empty( $_REQUEST['dupId'] ) ) { + $monitor = new Monitor( $_REQUEST['dupId'] ); + if ( ZM_OPT_X10 ) + $x10Monitor = dbFetchOne( 'SELECT * FROM TriggersX10 WHERE MonitorId = ?', NULL, array($_REQUEST['dupId']) ); + $clonedName = $monitor->Name(); + $monitor->Name( translate('Monitor').'-'.$nextId ); + $monitor->Id( $nextId ); + } else { + $monitor = new Monitor(); + $monitor->set( array( + 'Id' => 0, + 'Name' => translate('Monitor').'-'.$nextId, + 'Function' => 'Monitor', + 'Enabled' => true, + 'LinkedMonitors' => '', + 'Type' => '', + 'Device' => "/dev/video0", + 'Channel' => '0', + 'Format' => 0x000000ff, + 'Protocol' => '', + 'Method' => '', + 'Host' => '', + 'Path' => '', + 'Options' => '', + 'Port' => '80', + 'User' => '', + 'Pass' => '', + 'Colours' => 3, + 'Palette' => 0, + 'Width' => '320', + 'Height' => '240', + 'Orientation' => '0', + 'Deinterlacing' => 0, + 'RTSPDescribe' => 0, + 'SaveJPEGs' => '3', + 'VideoWriter' => '0', + 'EncoderParameters' => "# Lines beginning with # are a comment \n# For changing quality, use the crf option\n# 1 is best, 51 is worst quality\n#crf=23\n", + 'RecordAudio' => '0', + 'LabelFormat' => '%N - %d/%m/%y %H:%M:%S', + 'LabelX' => 0, + 'LabelY' => 0, + 'LabelSize' => 1, + 'ImageBufferCount' => 50, + 'WarmupCount' => 25, + 'PreEventCount' => 25, + 'PostEventCount' => 25, + 'StreamReplayBuffer' => 1000, + 'AlarmFrameCount' => 1, + 'Controllable' => 0, + 'ControlId' => '', + 'ControlType' => 0, + 'ControlDevice' => '', + 'ControlAddress' => '', + 'AutoStopTimeout' => '', + 'TrackMotion' => 0, + 'TrackDelay' => '', + 'ReturnLocation' => -1, + 'ReturnDelay' => '', + 'SectionLength' => 600, + 'FrameSkip' => 0, + 'MotionFrameSkip' => 0, + 'EventPrefix' => 'Event-', + 'AnalysisFPS' => '', + 'AnalysisUpdateDelay' => 0, + 'MaxFPS' => '', + 'AlarmMaxFPS' => '', + 'FPSReportInterval' => 1000, + 'RefBlendPerc' => 6, + 'AlarmRefBlendPerc' => 6, + 'DefaultView' => 'Events', + 'DefaultRate' => '100', + 'DefaultScale' => '100', + 'SignalCheckColour' => '#0000c0', + 'WebColour' => 'red', + 'Exif' => '0', + 'Triggers' => '', + 'V4LMultiBuffer' => '', + 'V4LCapturesPerFrame' => 1, + 'ServerId' => $Server['Id'], + ) ); + } # end if $_REQUEST['dupID'] +} # end if $_REQUEST['mid'] if ( ZM_OPT_X10 && empty($x10Monitor) ) { $x10Monitor = array( @@ -71,114 +148,37 @@ if ( ZM_OPT_X10 && empty($x10Monitor) ) { ); } -function getMonitorObject( $mid = null ) { - if ( $mid !== null ) { - $monitor = dbFetchMonitor($mid); - } else { - $monitor = array( - 'Id' => 0, - 'Name' => 'willbereplaced', - 'Function' => 'Monitor', - 'Enabled' => true, - 'LinkedMonitors' => '', - 'Type' => '', - 'Device' => '/dev/video0', - 'Channel' => '0', - 'Format' => 0x000000ff, - 'Protocol' => '', - 'Method' => '', - 'Host' => '', - 'Path' => '', - 'Options' => '', - 'Port' => "80", - 'User' => '', - 'Pass' => '', - 'Colours' => 3, - 'Palette' => 0, - 'Width' => '320', - 'Height' => '240', - 'Orientation' => '0', - 'Deinterlacing' => 0, - 'RTSPDescribe' => 0, - 'SaveJPEGs' => '3', - 'VideoWriter' => '0', - 'EncoderParameters' => "# Lines beginning with # are a comment \n# For changing quality, use the crf option\n# 1 is best, 51 is worst quality\n#crf=23\n", - 'RecordAudio' => '0', - 'LabelFormat' => '%N - %d/%m/%y %H:%M:%S', - 'LabelX' => 0, - 'LabelY' => 0, - 'LabelSize' => 1, - 'ImageBufferCount' => 50, - 'WarmupCount' => 25, - 'PreEventCount' => 25, - 'PostEventCount' => 25, - 'StreamReplayBuffer' => 1000, - 'AlarmFrameCount' => 1, - 'Controllable' => 0, - 'ControlId' => '', - 'ControlType' => 0, - 'ControlDevice' => '', - 'ControlAddress' => '', - 'AutoStopTimeout' => '', - 'TrackMotion' => 0, - 'TrackDelay' => '', - 'ReturnLocation' => -1, - 'ReturnDelay' => '', - 'SectionLength' => 600, - 'FrameSkip' => 0, - 'MotionFrameSkip' => 0, - 'EventPrefix' => 'Event-', - 'AnalysisFPS' => '', - 'AnalysisUpdateDelay' => 0, - 'MaxFPS' => '', - 'AlarmMaxFPS' => '', - 'FPSReportInterval' => 1000, - 'RefBlendPerc' => 6, - 'AlarmRefBlendPerc' => 6, - 'DefaultView' => 'Events', - 'DefaultRate' => '100', - 'DefaultScale' => '100', - 'SignalCheckColour' => '#0000c0', - 'WebColour' => 'red', - 'Exif' => '0', - 'Triggers' => '', - 'V4LMultiBuffer' => '', - 'V4LCapturesPerFrame' => 1, - 'ServerId' => $Server['Id'], - ); - } - return ($monitor); -} - function fourcc( $a, $b, $c, $d ) { return( ord($a) | (ord($b) << 8) | (ord($c) << 16) | (ord($d) << 24) ); } if ( isset( $_REQUEST['newMonitor'] ) ) { - $newMonitor = $_REQUEST['newMonitor']; + # Update the monitor object with whatever has been set so far. + $monitor->set( $_REQUEST['newMonitor'] ); + if ( ZM_OPT_X10 ) $newX10Monitor = $_REQUEST['newX10Monitor']; } else { - $newMonitor = $monitor; - $newMonitor['Triggers'] = explode( ',', isset($monitor['Triggers'])?$monitor['Triggers']:'' ); + # FIXME: Triggers in the db is a comma separated string. Needs to be an array. + #$monitor->Triggers()= explode( ',', isset($monitor->Triggers())?$monitor->Triggers:"" ); if ( ZM_OPT_X10 ) $newX10Monitor = $x10Monitor; } -$newMonitor['Name'] = trim($newMonitor['Name']); - -if ( $newMonitor['AnalysisFPS'] == '0.00' ) - $newMonitor['AnalysisFPS'] = ''; -if ( $newMonitor['MaxFPS'] == '0.00' ) - $newMonitor['MaxFPS'] = ''; -if ( $newMonitor['AlarmMaxFPS'] == '0.00' ) - $newMonitor['AlarmMaxFPS'] = ''; +# What if it has less zeros? This is not robust code. +if ( $monitor->AnalysisFPS() == '0.00' ) + $monitor->AnalysisFPS( '' ); +if ( $monitor->MaxFPS() == '0.00' ) + $monitor->MaxFPS( '' ); +if ( $monitor->AlarmMaxFPS() == '0.00' ) + $monitor->AlarmMaxFPS( '' ); if ( !empty($_REQUEST['preset']) ) { $preset = dbFetchOne( 'SELECT Type, Device, Channel, Format, Protocol, Method, Host, Port, Path, Width, Height, Palette, MaxFPS, Controllable, ControlId, ControlDevice, ControlAddress, DefaultRate, DefaultScale FROM MonitorPresets WHERE Id = ?', NULL, array($_REQUEST['preset']) ); foreach ( $preset as $name=>$value ) { + # Does isset handle NULL's? I don't think this code is correct. if ( isset($value) ) { - $newMonitor[$name] = $value; + $monitor->$name = $value; } } } @@ -186,16 +186,17 @@ if ( !empty($_REQUEST['probe']) ) { $probe = unserialize(base64_decode($_REQUEST['probe'])); foreach ( $probe as $name=>$value ) { if ( isset($value) ) { - $newMonitor[$name] = $value; + # Does isset handle NULL's? I don't think this code is correct. + $monitor->$name = $value; } } - if ( ZM_HAS_V4L && $newMonitor['Type'] == 'Local' ) { - $newMonitor['Palette'] = fourCC( substr($newMonitor['Palette'],0,1), substr($newMonitor['Palette'],1,1), substr($newMonitor['Palette'],2,1), substr($newMonitor['Palette'],3,1) ); - if ( $newMonitor['Format'] == 'PAL' ) - $newMonitor['Format'] = 0x000000ff; - elseif ( $newMonitor['Format'] == 'NTSC' ) - $newMonitor['Format'] = 0x0000b000; - } + if ( ZM_HAS_V4L && $monitor->Type() == 'Local' ) { + $monitor->Palette( fourCC( substr($monitor->Palette,0,1), substr($monitor->Palette,1,1), substr($monitor->Palette,2,1), substr($monitor->Palette,3,1) ) ); + if ( $monitor->Format() == 'PAL' ) + $monitor->Format( 0x000000ff ); + elseif ( $monitor->Format() == 'NTSC' ) + $monitor->Format( 0x0000b000 ); + } } $sourceTypes = array( @@ -246,7 +247,7 @@ $httpMethods = array( if ( !ZM_PCRE ) unset($httpMethods['regexp']); -// Currently unsupported + // Currently unsupported unset($httpMethods['jpegTags']); if ( ZM_HAS_V4L1 ) { @@ -267,9 +268,9 @@ if ( ZM_HAS_V4L1 ) { $v4l1DeviceChannels[$i] = $i; $v4l1LocalPalettes = array( - translate('Grey') => 1, - 'BGR32' => 5, - 'BGR24' => 4, + translate('Grey') => 1, + 'BGR32' => 5, + 'BGR24' => 4, '*YUYV' => 8, '*RGB565' => 3, '*RGB555' => 6, @@ -466,7 +467,7 @@ $videowriteropts = array( 'H264 Camera Passthrough' => 2 ); -xhtmlHeaders(__FILE__, translate('Monitor')." - ".validHtmlStr($monitor['Name']) ); +xhtmlHeaders(__FILE__, translate('Monitor')." - ".validHtmlStr($monitor->Name()) ); ?>
@@ -474,270 +475,279 @@ xhtmlHeaders(__FILE__, translate('Monitor')." - ".validHtmlStr($monitor['Name']) -
- Configuration cloned from Monitor: -
- -
- - - - - -
- -

- ()

+
+ Configuration cloned from Monitor:
-
-
    $value ) { - if ( $tab == $name ) { + } ?> -
  • +
    + -
  • + + + +
    + +

    - Name()) ?>Id()) ) { ?> (Id()?>)

    +
+
+
    +$value ) { + if ( $tab == $name ) { +?> +
  • + +
  • + +
+
+
+ + + + + + + + + + + + + + + + + +Triggers() ) { + foreach( explode( ',', $monitor->Triggers() ) as $newTrigger ) { +?> + Type()!= 'Local') ) { +?> + + + + + + +Type()!= 'Remote' ) { +?> + + + +Type()!= 'Local' && $monitor->Type()!= 'Remote' && $monitor->Type()!= 'Ffmpeg' && $monitor->Type()!= 'Libvlc') ) { +?> + +Type()!= 'Ffmpeg' && $monitor->Type()!= 'Libvlc' )) { +?> + +Type()!= 'Remote' && $monitor->Type()!= 'File' && $monitor->Type()!= 'Ffmpeg' && $monitor->Type()!= 'Libvlc' && $monitor->Type()!= 'cURL') ) { +?> + + + + + + + + + + + + + + +Type()!= 'Remote' && $monitor->Protocol()!= 'rtsp') ) { +?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Type()!= 'Local') ) { +?> + + - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
+ - - + - - + + - - - - + + + + - - + + + + Type() != 'Local' && $monitor->Type() != 'File' ) { ?> - + - + - - + + - - - + + + + + + + - - + + @@ -753,148 +763,150 @@ switch ( $tab ) { if ( $optCount && ($optCount%$breakCount == 0) ) echo '
'; ?> - checked="checked"/>  + Triggers() ) && in_array( $optTrigger, $monitor->Triggers() ) ) { ?> checked="checked"/>  - - - - + + + + Type() == 'Local' ) { ?> - - + + Method() == 'v4l1' ) { ?> - - - + + + - - - + + + - - + + Type() == 'Remote' ) { ?> - + Protocol()) || $monitor->Protocol() == 'http' ) { ?> - + - + - - - + + + Type() == 'File' ) { ?> - + Type() == 'cURL' ) { ?> - - - + + + Type() == 'Ffmpeg' || $monitor->Type() == 'Libvlc' ) { ?> - - - + + + - - - - - + + + + + Type() == 'Local' ) { ?> - + + + - - - > + } + ?> + Type() == 'Remote' ) { + ?> + Protocol()!= 'rtsp' ) { echo ' style="display:none;"'; } ?>> - - - - + + + + - - - - + + + + - - - - - - + + + + + + - + - - - - + + + + translate('None'), @@ -902,9 +914,9 @@ switch ( $tab ) { '1' => translate('Preset')." 1", ); ?> - + - + - - - - - - - + + + + + + - - + + + Type() == 'Local' ) { ?> - + + + + - - + + + + + + + + From 928c4651a6060e1f63452fa83e1bc164dea94332 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 9 Aug 2017 09:54:27 -0400 Subject: [PATCH 04/20] fix braces, spacing --- web/skins/classic/views/video.php | 188 +++++++++++++----------------- 1 file changed, 81 insertions(+), 107 deletions(-) diff --git a/web/skins/classic/views/video.php b/web/skins/classic/views/video.php index cfbb6f929..75cc8c8ed 100644 --- a/web/skins/classic/views/video.php +++ b/web/skins/classic/views/video.php @@ -18,91 +18,82 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // -if ( !canView( 'Events' ) ) -{ - $view = "error"; - return; +if ( !canView( 'Events' ) ) { + $view = "error"; + return; } +require_once('includes/Event.php'); + $eid = validInt($_REQUEST['eid']); $sql = 'SELECT E.*,M.Name AS MonitorName,M.DefaultRate,M.DefaultScale FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE E.Id = ?'; $sql_values = array( $eid ); if ( $user['MonitorIds'] ) { - $monitor_ids = explode( ',', $user['MonitorIds'] ); - $sql .= ' AND MonitorId IN (' .implode( ',', array_fill(0,count($monitor_ids),'?') ) . ')'; - $sql_values = array_merge( $sql_values, $monitor_ids ); + $monitor_ids = explode( ',', $user['MonitorIds'] ); + $sql .= ' AND MonitorId IN (' .implode( ',', array_fill(0,count($monitor_ids),'?') ) . ')'; + $sql_values = array_merge( $sql_values, $monitor_ids ); } $event = dbFetchOne( $sql, NULL, $sql_values ); if ( isset( $_REQUEST['rate'] ) ) - $rate = validInt($_REQUEST['rate']); + $rate = validInt($_REQUEST['rate']); else - $rate = reScale( RATE_BASE, $event['DefaultRate'], ZM_WEB_DEFAULT_RATE ); + $rate = reScale( RATE_BASE, $event['DefaultRate'], ZM_WEB_DEFAULT_RATE ); if ( isset( $_REQUEST['scale'] ) ) - $scale = validInt($_REQUEST['scale']); + $scale = validInt($_REQUEST['scale']); else - $scale = reScale( SCALE_BASE, $event['DefaultScale'], ZM_WEB_DEFAULT_SCALE ); + $scale = reScale( SCALE_BASE, $event['DefaultScale'], ZM_WEB_DEFAULT_SCALE ); -$eventPath = ZM_DIR_EVENTS.'/'.getEventPath( $event ); +$Event = new Event( $event['Id'] ); +$eventPath = $Event->Path(); $videoFormats = array(); $ffmpegFormats = preg_split( '/\s+/', ZM_FFMPEG_FORMATS ); -foreach ( $ffmpegFormats as $ffmpegFormat ) -{ - if ( preg_match( '/^([^*]+)(\*\*?)$/', $ffmpegFormat, $matches ) ) - { - $videoFormats[$matches[1]] = $matches[1]; - if ( !isset($videoFormat) && $matches[2] == "*" ) - { - $videoFormat = $matches[1]; - } - } - else - { - $videoFormats[$ffmpegFormat] = $ffmpegFormat; +foreach ( $ffmpegFormats as $ffmpegFormat ) { + if ( preg_match( '/^([^*]+)(\*\*?)$/', $ffmpegFormat, $matches ) ) { + $videoFormats[$matches[1]] = $matches[1]; + if ( !isset($videoFormat) && $matches[2] == '*' ) { + $videoFormat = $matches[1]; } + } else { + $videoFormats[$ffmpegFormat] = $ffmpegFormat; + } } $videoFiles = array(); -if ( $dir = opendir( $eventPath ) ) -{ - while ( ($file = readdir( $dir )) !== false ) - { - $file = $eventPath.'/'.$file; - if ( is_file( $file ) ) - { - if ( preg_match( '/\.(?:'.join( '|', $videoFormats ).')$/', $file ) ) - { - $videoFiles[] = $file; - } - } +if ( $dir = opendir( $eventPath ) ) { + while ( ($file = readdir( $dir )) !== false ) { + $file = $eventPath.'/'.$file; + if ( is_file( $file ) ) { + if ( preg_match( '/\.(?:'.join( '|', $videoFormats ).')$/', $file ) ) { + $videoFiles[] = $file; + } } - closedir( $dir ); + } + closedir( $dir ); } -if ( isset($_REQUEST['deleteIndex']) ) -{ - $deleteIndex = validInt($_REQUEST['deleteIndex']); - unlink( $videoFiles[$deleteIndex] ); - unset( $videoFiles[$deleteIndex] ); +if ( isset($_REQUEST['deleteIndex']) ) { + $deleteIndex = validInt($_REQUEST['deleteIndex']); + unlink( $videoFiles[$deleteIndex] ); + unset( $videoFiles[$deleteIndex] ); } -if ( isset($_REQUEST['downloadIndex']) ) -{ - $downloadIndex = validInt($_REQUEST['downloadIndex']); - header( "Pragma: public" ); - header( "Expires: 0" ); - header( "Cache-Control: must-revalidate, post-check=0, pre-check=0" ); - header( "Cache-Control: private", false ); // required by certain browsers - header( "Content-Description: File Transfer" ); - header( 'Content-disposition: attachment; filename="'.basename($videoFiles[$downloadIndex]).'"' ); // basename is required because the video index contains the path and firefox doesn't strip the path but simply replaces the slashes with an underscore. - header( "Content-Transfer-Encoding: binary" ); - header( "Content-Type: application/force-download" ); - header( "Content-Length: ".filesize($videoFiles[$downloadIndex]) ); - readfile( $videoFiles[$downloadIndex] ); - exit; +if ( isset($_REQUEST['downloadIndex']) ) { + $downloadIndex = validInt($_REQUEST['downloadIndex']); + header( "Pragma: public" ); + header( "Expires: 0" ); + header( "Cache-Control: must-revalidate, post-check=0, pre-check=0" ); + header( "Cache-Control: private", false ); // required by certain browsers + header( "Content-Description: File Transfer" ); + header( 'Content-disposition: attachment; filename="'.basename($videoFiles[$downloadIndex]).'"' ); // basename is required because the video index contains the path and firefox doesn't strip the path but simply replaces the slashes with an underscore. + header( "Content-Transfer-Encoding: binary" ); + header( "Content-Type: application/force-download" ); + header( "Content-Length: ".filesize($videoFiles[$downloadIndex]) ); + readfile( $videoFiles[$downloadIndex] ); + exit; } $focusWindow = true; @@ -119,19 +110,16 @@ xhtmlHeaders(__FILE__, translate('Video') );

@@ -158,29 +146,23 @@ else disabled="disabled"/>

+
'None'); $result = dbQuery( 'SELECT * FROM Servers ORDER BY Name'); $results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Server' ); foreach ( $results as $row => $server_obj ) { - $servers[$server_obj->Id] = $server_obj->Name(); + $servers[$server_obj->Id()] = $server_obj->Name(); } + echo htmlSelect( 'newMonitor[ServerId]', $servers, $monitor->ServerId() ); ?> - -
Type() ); ?>
checked="checked"/>
-
Enabled()) ) { ?> checked="checked"/>
+ -
 ()
 ()
+
Method(), "submitTab( '$tab' );" ); ?>
- /> - - /> - - /> - -
+ V4LMultiBuffer() == 1 ? 'checked="checked"' : '' ) ?>/> + + V4LMultiBuffer() == 0 ? 'checked="checked"' : '' ) ?>/> + + V4LMultiBuffer()) ? 'checked="checked"' : '' ) ?>/> + +
Protocol(), "updateMethods( this );if(this.value=='rtsp'){\$('RTSPDescribe').setStyle('display','table-row');}else{\$('RTSPDescribe').hide();}" ); ?>
Method() ); ?>
Method() ); ?>
 ()
 ()
 ()Method() ); ?>
 (Type()), 'zmOptionHelp', 'optionhelp', '?' ) ?>)
()
()
()
()
 () RTSPDescribe()) ) { ?> checked="checked"/>
checked="checked"/>
RecordAudio()) ) { ?> checked="checked"/>
checked="checked"/>
Controllable()) ) { ?> checked="checked"/>
 
checked="checked"/>
TrackMotion()) ) { ?> checked="checked"/>
DefaultRate() ); ?>
DefaultScale() ); ?>
    
+ +      +
    
 () checked="checked"/>
+ +      +
 () Exif()) ) { ?> checked="checked"/>
@@ -194,32 +176,24 @@ else 0 ) - { - preg_match( '/^(.+)-((?:r[_\d]+)|(?:F[_\d]+))-((?:s[_\d]+)|(?:S[0-9a-z]+))\.([^.]+)$/', $file, $matches ); - if ( preg_match( '/^r(.+)$/', $matches[2], $temp_matches ) ) - { - $rate = (int)(100 * preg_replace( '/_/', '.', $temp_matches[1] ) ); - $rateText = isset($rates[$rate])?$rates[$rate]:($rate."x"); - } - elseif ( preg_match( '/^F(.+)$/', $matches[2], $temp_matches ) ) - { - $rateText = $temp_matches[1]."fps"; - } - if ( preg_match( '/^s(.+)$/', $matches[3], $temp_matches ) ) - { - $scale = (int)(100 * preg_replace( '/_/', '.', $temp_matches[1] ) ); - $scaleText = isset($scales[$scale])?$scales[$scale]:($scale."x"); - } - elseif ( preg_match( '/^S(.+)$/', $matches[3], $temp_matches ) ) - { - $scaleText = $temp_matches[1]; - } - $width = $scale?reScale( $event['Width'], $scale ):$event['Width']; - $height = $scale?reScale( $event['Height'], $scale ):$event['Height']; + $index = 0; + foreach ( $videoFiles as $file ) { + if ( filesize( $file ) > 0 ) { + preg_match( '/^(.+)-((?:r[_\d]+)|(?:F[_\d]+))-((?:s[_\d]+)|(?:S[0-9a-z]+))\.([^.]+)$/', $file, $matches ); + if ( preg_match( '/^r(.+)$/', $matches[2], $temp_matches ) ) { + $rate = (int)(100 * preg_replace( '/_/', '.', $temp_matches[1] ) ); + $rateText = isset($rates[$rate])?$rates[$rate]:($rate."x"); + } elseif ( preg_match( '/^F(.+)$/', $matches[2], $temp_matches ) ) { + $rateText = $temp_matches[1]."fps"; + } + if ( preg_match( '/^s(.+)$/', $matches[3], $temp_matches ) ) { + $scale = (int)(100 * preg_replace( '/_/', '.', $temp_matches[1] ) ); + $scaleText = isset($scales[$scale])?$scales[$scale]:($scale."x"); + } elseif ( preg_match( '/^S(.+)$/', $matches[3], $temp_matches ) ) { + $scaleText = $temp_matches[1]; + } + $width = $scale?reScale( $event['Width'], $scale ):$event['Width']; + $height = $scale?reScale( $event['Height'], $scale ):$event['Height']; ?> @@ -229,14 +203,14 @@ else
 /  / 
From fd7ea84e81b8d1afc7a2878025a86dbfdd06b2dd Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Thu, 10 Aug 2017 20:34:05 -0500 Subject: [PATCH 05/20] Update README.md Point users to rpmfusion for zoneminder rpm releases --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 460fe62ff..60b8451d5 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,8 @@ This is the recommended method to install ZoneMinder onto your system. ZoneMinde - Ubuntu via [Iconnor's PPA](https://launchpad.net/~iconnor/+archive/ubuntu/zoneminder) - Debian from their [default repository](https://packages.debian.org/search?searchon=names&keywords=zoneminder) -- RHEL/CentOS and clones via [zmrepo](http://zmrepo.zoneminder.com/) -- Fedora via [zmrepo](http://zmrepo.zoneminder.com/) +- RHEL/CentOS and clones via [RPMFusion](http://rpmfusion.org) +- Fedora via [RPMFusion](http://rpmfusion.org) - OpenSuse via [third party repository](http://www.zoneminder.com/wiki/index.php/Installing_using_ZoneMinder_RPMs_for_SuSE) - Mageia from their default repository From a64810fcfcc94ffdf9d757d21bc10e9f5241e715 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Thu, 10 Aug 2017 20:34:48 -0500 Subject: [PATCH 06/20] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 60b8451d5..4c318b659 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,8 @@ This is the recommended method to install ZoneMinder onto your system. ZoneMinde - Ubuntu via [Iconnor's PPA](https://launchpad.net/~iconnor/+archive/ubuntu/zoneminder) - Debian from their [default repository](https://packages.debian.org/search?searchon=names&keywords=zoneminder) -- RHEL/CentOS and clones via [RPMFusion](http://rpmfusion.org) -- Fedora via [RPMFusion](http://rpmfusion.org) +- RHEL/CentOS and clones via [RPM Fusion](http://rpmfusion.org) +- Fedora via [RPM Fusion](http://rpmfusion.org) - OpenSuse via [third party repository](http://www.zoneminder.com/wiki/index.php/Installing_using_ZoneMinder_RPMs_for_SuSE) - Mageia from their default repository From 0a5188d79f47f3c7366fa27a499fecbf2d97cc21 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Thu, 10 Aug 2017 20:55:22 -0500 Subject: [PATCH 07/20] Update redhat.rst --- docs/installationguide/redhat.rst | 40 +++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/docs/installationguide/redhat.rst b/docs/installationguide/redhat.rst index d662d1812..6f4240d6b 100644 --- a/docs/installationguide/redhat.rst +++ b/docs/installationguide/redhat.rst @@ -23,30 +23,46 @@ Fedora has a short life-cycle of just 6 months. However, Fedora, and consequentl If you desire newer packages than what is available in RHEL or CentOS, you should consider using Fedora. -Zmrepo – A ZoneMinder RPM Repository ------------------------------------- +How To Avoid Known Installation Problems +---------------------------------------- -Zmrepo is a turn key solution. It will install all of ZoneMinder's dependencies for you. This is the easiest and the recommended way to install ZoneMinder on any system running a Redhat based distribution. +The following notes are based on real problems which have occurred by those who came before you: -Zmrepo supports the two most recent, major releases of each Redhat based distro. - -The following notes are based on real problems which have occurred: - -- Zmrepo assumes you have installed the underlying distribution **using the official installation media for that distro**. Third party "Spins" are not supported and may not work correctly. +- Zmrepo assumes you have installed the underlying distribution **using the official installation media for that distro**. Third party "Spins" may not work correctly. - ZoneMinder is intended to be installed in an environment dedicated to ZoneMinder. While ZoneMinder will play well with many applications, some invariably will not. Asterisk is one such example. -- Be advised that you need to start with a clean system before using zmrepo. +- Be advised that you need to start with a clean system before installing ZoneMinder. - If you have previously installed ZoneMinder from-source, then your system is **NOT** clean. You must manually search for and delete all ZoneMinder related files before using zmrepo (look under /usr/local). Make uninstall helps, but it will not do this for you correctly. You **WILL** have problems if you ignore this step. -- It is not necessary, and not recommended, to install a LAMP stack ahead of time. +- Unlike the Debian/Ubuntu packages, it is not necessary, and not recommended, to install a LAMP stack ahead of time. -- Disable other third party repos and uninstall any of ZoneMinder's third party dependencies, which might already be on the system, especially ffmpeg and vlc. Attempting to install dependencies yourself often causes problems. +- Disable any other third party repos and uninstall any of ZoneMinder's third party dependencies, which might already be on the system, especially ffmpeg and vlc. Attempting to install dependencies yourself often causes problems. - Each ZoneMinder rpm includes a README file under /usr/share/doc. You must follow the all steps in this README file, precisely, each and every time ZoneMinder is installed or upgraded. **Failure to do so is guaranteed to result in a non-functional system.** -To begin the installation of ZoneMinder on your Redhat based distro, please navigate to: http://zmrepo.zoneminder.com +How to Install ZoneMinder +------------------------- + +These instructions apply to all redhat user, except for RHEL/CEntOS 6. + +ZoneMinder releases are now being hosted at RPM Fusion. New users should navigate the `RPM Fusion site `_ then follow the instructions to enable that repo. Note the RHEL/CentOS must also navaigate to the `EPEL Site `_ and enable that repo as well. Once both of these repositories are enabled, install ZoneMinder from the commandline: + +:: + + sudo dnf install zoneminder + +Note that RHEL/CentOS 7 users should substitute yum for dnf. + +Once ZoneMinder has been installed, it is critically important that you read the README file under /usr/share/doc/zoneminder. ZoneMinder will not run without completing the steps outlined in the README. + +How to Install ZoneMinder on RHEL/CentOS 6 +------------------------------------------ + +We continue to encounter build problems, caused by the age of this distro. However, we can see the writing on the wall. The end of the line for this distros is near. + +Please be advised that we do not recommend any new ZoneMinder installations using CentOS 6. However, for the time being, ZoneMinder rpms will continue to be hosted at `zmrepo `_. How to Build a (Custom) ZoneMinder Package ------------------------------------------ From 27796ab720e1e3a645533739d6eac793009f2e08 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Thu, 10 Aug 2017 20:57:26 -0500 Subject: [PATCH 08/20] Update redhat.rst --- docs/installationguide/redhat.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/installationguide/redhat.rst b/docs/installationguide/redhat.rst index 6f4240d6b..f9403ba49 100644 --- a/docs/installationguide/redhat.rst +++ b/docs/installationguide/redhat.rst @@ -45,7 +45,7 @@ The following notes are based on real problems which have occurred by those who How to Install ZoneMinder ------------------------- -These instructions apply to all redhat user, except for RHEL/CEntOS 6. +These instructions apply to all redhat distros and compatible clones, except for RHEL/CEntOS 6. ZoneMinder releases are now being hosted at RPM Fusion. New users should navigate the `RPM Fusion site `_ then follow the instructions to enable that repo. Note the RHEL/CentOS must also navaigate to the `EPEL Site `_ and enable that repo as well. Once both of these repositories are enabled, install ZoneMinder from the commandline: @@ -60,7 +60,7 @@ Once ZoneMinder has been installed, it is critically important that you read the How to Install ZoneMinder on RHEL/CentOS 6 ------------------------------------------ -We continue to encounter build problems, caused by the age of this distro. However, we can see the writing on the wall. The end of the line for this distros is near. +We continue to encounter build problems, caused by the age of this distro. It is unforuntate, but we can see the writing on the wall. We do not have a date set, but the end of the line for this distros is near. Please be advised that we do not recommend any new ZoneMinder installations using CentOS 6. However, for the time being, ZoneMinder rpms will continue to be hosted at `zmrepo `_. From 4941e6715cd30443e7a2be87033094a0a67af51a Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Fri, 11 Aug 2017 07:49:59 -0500 Subject: [PATCH 09/20] Update redhat.rst --- docs/installationguide/redhat.rst | 32 ++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/docs/installationguide/redhat.rst b/docs/installationguide/redhat.rst index f9403ba49..8724436a4 100644 --- a/docs/installationguide/redhat.rst +++ b/docs/installationguide/redhat.rst @@ -34,9 +34,9 @@ The following notes are based on real problems which have occurred by those who - Be advised that you need to start with a clean system before installing ZoneMinder. -- If you have previously installed ZoneMinder from-source, then your system is **NOT** clean. You must manually search for and delete all ZoneMinder related files before using zmrepo (look under /usr/local). Make uninstall helps, but it will not do this for you correctly. You **WILL** have problems if you ignore this step. +- If you have previously installed ZoneMinder from-source, then your system is **NOT** clean. You must manually search for and delete all ZoneMinder related files first (look under /usr/local). Issuing a "make uninstall" helps, but it will not do this for you correctly. You **WILL** have problems if you ignore this step. -- Unlike the Debian/Ubuntu packages, it is not necessary, and not recommended, to install a LAMP stack ahead of time. +- Unlike Debian/Ubuntu distros, it is not necessary, and not recommended, to install a LAMP stack ahead of time. - Disable any other third party repos and uninstall any of ZoneMinder's third party dependencies, which might already be on the system, especially ffmpeg and vlc. Attempting to install dependencies yourself often causes problems. @@ -45,15 +45,15 @@ The following notes are based on real problems which have occurred by those who How to Install ZoneMinder ------------------------- -These instructions apply to all redhat distros and compatible clones, except for RHEL/CEntOS 6. +These instructions apply to all redhat distros and compatible clones, except for RHEL/CentOS 6. -ZoneMinder releases are now being hosted at RPM Fusion. New users should navigate the `RPM Fusion site `_ then follow the instructions to enable that repo. Note the RHEL/CentOS must also navaigate to the `EPEL Site `_ and enable that repo as well. Once both of these repositories are enabled, install ZoneMinder from the commandline: +ZoneMinder releases are now being hosted at RPM Fusion. New users should navigate the `RPM Fusion site `_ then follow the instructions to enable that repo. RHEL/CentOS users must also navaigate to the `EPEL Site `_ and enable that repo as well. Once enabled, install ZoneMinder from the commandline: :: sudo dnf install zoneminder -Note that RHEL/CentOS 7 users should substitute yum for dnf. +Note that RHEL/CentOS 7 users should use yum instead of dnf. Once ZoneMinder has been installed, it is critically important that you read the README file under /usr/share/doc/zoneminder. ZoneMinder will not run without completing the steps outlined in the README. @@ -64,6 +64,28 @@ We continue to encounter build problems, caused by the age of this distro. It is Please be advised that we do not recommend any new ZoneMinder installations using CentOS 6. However, for the time being, ZoneMinder rpms will continue to be hosted at `zmrepo `_. +How to Install Nightly Development Builds +----------------------------------------- + +ZoneMinder development packages, which represent the most recent build from our master branch, are available from `zmrepo `_. + +The feedback we get from those who use these development packages is extremely helpful. However, please understand these packages are intended for testing the latest master branch only. They are not intended to be used on any production system. There will be new bugs, and new features may not be documented. This is bleeding edge, and there might be breakage. Please keep that in mind when using this repo. We know from our user forum that this can't be stated enough. + +How to Change from Zmrepo to RPM Fusion +--------------------------------------- + +As mentioned above, the place to get the latest ZoneMinder release is now `RPM Fusion `_. If you are currently using ZoneMinder release packages from Zmrepo, then the following steps will change you over to RPM Fusion: + +- Navigate to the `RPM Fusion site `_ and enable RPM Fusion on your system +- Now issue the following from the command line: + +:: + + sudo dnf remove zmrepo + sudo dnf update + +Note that RHEL/CentOS 7 users should use yum instead of dnf. + How to Build a (Custom) ZoneMinder Package ------------------------------------------ From 64cf80ccf48e3f68a44f4074a0654211a5fce69e Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Fri, 11 Aug 2017 07:52:25 -0500 Subject: [PATCH 10/20] Update redhat.rst --- docs/installationguide/redhat.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installationguide/redhat.rst b/docs/installationguide/redhat.rst index 8724436a4..00a39c9c9 100644 --- a/docs/installationguide/redhat.rst +++ b/docs/installationguide/redhat.rst @@ -10,7 +10,7 @@ Background: RHEL, CentOS, and Clones These distributions are classified as enterprise operating systems and have a long operating lifetime of many years. By design, they will not have the latest and greatest versions of any package. Instead, stable packages are the emphasis. -Replacing any core package in these distributions with a newer package from a third party is expressly verboten. The ZoneMinder development team will not do this, and neither should you. If you have the perception that you've got to have a newer version of mysql, gnome, apache, etc. then, rather than upgrade these packages, you should instead consider using a different distribution such as Fedora. +Replacing any core package in these distributions with a newer package from a third party is expressly verboten. The ZoneMinder development team will not do this, and neither should you. If you have the perception that you've got to have a newer version of php, mysql, gnome, apache, etc. then, rather than upgrade these packages, you should instead consider using a different distribution such as Fedora. The ZoneMinder team will not provide support for systems which have had any core package replaced with a package from a third party. From 565612b86b229302c4d3277ec9f2f04f9148318c Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Fri, 11 Aug 2017 07:55:29 -0500 Subject: [PATCH 11/20] Update redhat.rst --- docs/installationguide/redhat.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installationguide/redhat.rst b/docs/installationguide/redhat.rst index 00a39c9c9..adb6b4503 100644 --- a/docs/installationguide/redhat.rst +++ b/docs/installationguide/redhat.rst @@ -40,7 +40,7 @@ The following notes are based on real problems which have occurred by those who - Disable any other third party repos and uninstall any of ZoneMinder's third party dependencies, which might already be on the system, especially ffmpeg and vlc. Attempting to install dependencies yourself often causes problems. -- Each ZoneMinder rpm includes a README file under /usr/share/doc. You must follow the all steps in this README file, precisely, each and every time ZoneMinder is installed or upgraded. **Failure to do so is guaranteed to result in a non-functional system.** +- Each ZoneMinder rpm includes a README file under /usr/share/doc. You must follow all the steps in this README file, precisely, each and every time ZoneMinder is installed or upgraded. **Failure to do so is guaranteed to result in a non-functional system.** How to Install ZoneMinder ------------------------- From ae8873c2ccb0ca53baf025429ae1a21fe4e9de83 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Fri, 11 Aug 2017 08:44:02 -0500 Subject: [PATCH 12/20] Update redhat.rst --- docs/installationguide/redhat.rst | 35 ++++++++++++++----------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/docs/installationguide/redhat.rst b/docs/installationguide/redhat.rst index adb6b4503..b91b12185 100644 --- a/docs/installationguide/redhat.rst +++ b/docs/installationguide/redhat.rst @@ -86,14 +86,14 @@ As mentioned above, the place to get the latest ZoneMinder release is now `RPM F Note that RHEL/CentOS 7 users should use yum instead of dnf. -How to Build a (Custom) ZoneMinder Package +How to Build a Your Own ZoneMinder Package ------------------------------------------ -If you are looking to do development or the packages in zmrepo just don't suit you, then you should follow these steps to learn how to build your own ZoneMinder RPM. +If you are looking to do development or the available packages just don't suit you, then you can follow these steps to build your own ZoneMinder RPM. Background ********** -The following method documents how to build ZoneMinder into an RPM package, compatible with Fedora, Redhat, CentOS, and other compatible clones. This is exactly how the RPMS in zmrepo are built. +The following method documents how to build ZoneMinder into an RPM package, for Fedora, Redhat, CentOS, and other compatible clones. This is exactly how the RPMS in zmrepo are built. The method documented below was chosen because: @@ -105,22 +105,20 @@ The method documented below was chosen because: - Troubleshooting becomes easier if we are all building ZoneMinder the same way. -The build instructions below make use of a custom script called "buildzm.sh". Advanced users are encouraged to view the contents of this script. Notice that the script doesn't really do a whole lot. The goal of the script is to simply make the process a little easier for the first time user. Once you become familar with the build process, you can issue the mock commands found in the buildzm.sh script yourself if you so desire. - ***IMPORTANT*** -Certain commands in these instructions require root privileges while other commands do not. Pay close attention to this. If the instructions below state to issue a command without a “sudo” prefix, then you should *not* be root while issuing the command. Getting this incorrect will result in a failed build. +Certain commands in these instructions require root privileges while other commands do not. Pay close attention to this. If the instructions below state to issue a command without a “sudo” prefix, then you should *not* be root while issuing the command. Getting this incorrect will result in a failed build, or worse a broken system. Set Up Your Environment *********************** Before you begin, set up an rpmbuild environment by following `this guide `_ by the CentOS developers. -Next, navigate to `Zmrepo `_, and follow the instructions to enable zmrepo on your system. +In addition, make sure RPM Fusion is enabled as described in the previous section `How to Install ZoneMinder`_. -With zmrepo enabled, issue the following command: +With RPM Fusion enabled, issue the following command: :: - sudo yum install zmrepo-mock-configs mock + sudo yum install mock-rpmfusion-free mock Add your user account to the group mock: @@ -134,9 +132,9 @@ Your build environment is now set up. Build from SRPM *************** -To continue, you need a ZoneMinder SRPM. For starters, let's use one of the SRPMS from zmrepo. Go browse the `Zmrepo `_ site and choose an appropriate SRPM and place it into the ~/rpmbuild/SRPMS folder. +To continue, you need a ZoneMinder SRPM. If you wish to rebuild a ZoneMinder release, then browse the `RPM Fusion site `_. If instead you wish to rebuild the latest package from our master branch then browse the `Zmrepo site`_. -For CentOS 7, I have chosen the following SRPM: +For this example, I'll use one of the SRPMS from zmrepo: :: @@ -147,36 +145,35 @@ Now comes the fun part. To build ZoneMinder, issue the following command: :: - buildzm.sh zmrepo-el7-x86_64 ~/rpmbuild/SRPMS/zoneminder-1.28.1-2.el7.centos.src.rpm + mock -r epel-7-x86_64-rpmfusion_free ~/rpmbuild/SRPMS/zoneminder-1.28.1-2.el7.centos.src.rpm Want to build ZoneMinder for Fedora, instead of CentOS, from the same host? Once you download the Fedora SRPM, issue the following: :: - buildzm.sh zmrepo-f21-x86_64 ~/rpmbuild/SRPMS/zoneminder-1.28.1-1.fc21.src.rpm + mock -r fedora-26-x86_64-rpmfusion_free ~/rpmbuild/SRPMS/zoneminder-1.28.1-1.fc21.src.rpm -Notice that the buildzm.sh tool requires the following parameters: +Notice that the mock tool requires the following parameters: :: - buildzm.sh MOCKCONFIG ZONEMINDER_SRPM + mock -r MOCKCONFIG ZONEMINDER_SRPM The list of available Mock config files are available here: :: - ls /etc/mock/zmrepo*.cfg + ls /etc/mock/*rpmfusion_free.cfg You choose the config file based on the desired distro (e.g. el6, el7, f20, f21) and basearch (e.g. x86, x86_64, arhmhfp). Notice that, when specifying the Mock config as a commandline parameter, you should leave off the ".cfg" filename extension. Installation ************ -Once the build completes, you will be presented with a folder containing the RPM's that were built. Copy the newly built ZoneMinder RPM to the desired system, enable zmrepo per the instruction on the `Zmrepo `_ -website, and then install the rpm by issuing the appropriate yum install command. Finish the installation by following the zoneminder setup instructions in the distro specific readme file, named README.{distroname}, which will be installed into the /usr/share/doc/zoneminder* folder. +Once the build completes, you will be presented with a folder containing the RPMs that were built. Copy the newly built ZoneMinder RPMs to the desired system, enable RPM Fusion as described in `How to Install ZoneMinder`_, and then install the rpm by issuing the appropriate yum/dnf install command. Finish the installation by following the zoneminder setup instructions in the distro specific readme file, named README.{distroname}, which will be installed into the /usr/share/doc/zoneminder* folder. -Finally, you may want to consider editing the zmrepo repo file under /etc/yum.repos.d and placing an “exclude=zoneminder*” line into the config file. This will prevent your system from overwriting your manually built RPM with the ZoneMinder RPM found in the repo. +Finally, you may want to consider editing the rpmfusion repo file under /etc/yum.repos.d and placing an “exclude=zoneminder*” line into the config file. This will prevent your system from overwriting your manually built RPM with the ZoneMinder RPM found in the repo. How to Modify the Source Prior to Build *************************************** From 4187da2d5a02a62b509f78a7deced5f35d84d5a5 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Fri, 11 Aug 2017 08:45:34 -0500 Subject: [PATCH 13/20] Update redhat.rst --- docs/installationguide/redhat.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installationguide/redhat.rst b/docs/installationguide/redhat.rst index b91b12185..b0fce8cf6 100644 --- a/docs/installationguide/redhat.rst +++ b/docs/installationguide/redhat.rst @@ -132,7 +132,7 @@ Your build environment is now set up. Build from SRPM *************** -To continue, you need a ZoneMinder SRPM. If you wish to rebuild a ZoneMinder release, then browse the `RPM Fusion site `_. If instead you wish to rebuild the latest package from our master branch then browse the `Zmrepo site`_. +To continue, you need a ZoneMinder SRPM. If you wish to rebuild a ZoneMinder release, then browse the `RPM Fusion site `_. If instead you wish to rebuild the latest package from our master branch then browse the `Zmrepo site `_. For this example, I'll use one of the SRPMS from zmrepo: From 3a7ad58c255dbb639e92ce22521ca36ef022c9e3 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Fri, 11 Aug 2017 08:55:57 -0500 Subject: [PATCH 14/20] Update redhat.rst --- docs/installationguide/redhat.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/installationguide/redhat.rst b/docs/installationguide/redhat.rst index b0fce8cf6..587dd3532 100644 --- a/docs/installationguide/redhat.rst +++ b/docs/installationguide/redhat.rst @@ -171,7 +171,13 @@ You choose the config file based on the desired distro (e.g. el6, el7, f20, f21) Installation ************ -Once the build completes, you will be presented with a folder containing the RPMs that were built. Copy the newly built ZoneMinder RPMs to the desired system, enable RPM Fusion as described in `How to Install ZoneMinder`_, and then install the rpm by issuing the appropriate yum/dnf install command. Finish the installation by following the zoneminder setup instructions in the distro specific readme file, named README.{distroname}, which will be installed into the /usr/share/doc/zoneminder* folder. +Once the build completes, you will be presented with a message stating where the newly built rpms can be found. It will look similar to this: + +:: + + INFO: Results and/or logs in: /var/lib/mock/fedora-26-x86_64/result + +Copy the newly built ZoneMinder RPMs to the desired system, enable RPM Fusion as described in `How to Install ZoneMinder`_, and then install the rpm by issuing the appropriate yum/dnf install command. Finish the installation by following the zoneminder setup instructions in the distro specific readme file, named README.{distroname}, which will be installed into the /usr/share/doc/zoneminder* folder. Finally, you may want to consider editing the rpmfusion repo file under /etc/yum.repos.d and placing an “exclude=zoneminder*” line into the config file. This will prevent your system from overwriting your manually built RPM with the ZoneMinder RPM found in the repo. From 4aa15702e793592c80fad33336faa6e456488c4b Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Fri, 11 Aug 2017 12:33:39 -0500 Subject: [PATCH 15/20] Update redhat.rst --- docs/installationguide/redhat.rst | 73 +++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 24 deletions(-) diff --git a/docs/installationguide/redhat.rst b/docs/installationguide/redhat.rst index 587dd3532..d3a8cf49e 100644 --- a/docs/installationguide/redhat.rst +++ b/docs/installationguide/redhat.rst @@ -183,27 +183,18 @@ Finally, you may want to consider editing the rpmfusion repo file under /etc/yum How to Modify the Source Prior to Build *************************************** -Before attempting this part of the instructions, make sure and follow the previous instructions for building one of the unmodified SRPMS from zmrepo. Knowing this part works will assist in troubleshooting should something go wrong. +In the previous section we described how to rebuild an existing ZoneMinder SRPM. The instructions which follow show how to build the ZoneMinder git source tree into an rpm. -These instructions may vary depending on what exactly you want to do. The following example assumes you want to build a development snapshot from the master branch. +Before attempting this part of the instructions, make sure and follow the previous instructions `Build from SRPM`_. Knowing this part works will assist in troubleshooting later should something go wrong. -From the previous instructions, we downloaded a CentOS 7 ZoneMinder SRPM and placed it into ~/rpmbuild/SRPMS. For this example, install it onto your system: +Make sure git and rpmdevtools are installed: :: - rpm -ivh ~/rpmbuild/SRPMS/zoneminder-1.28.1-2.el7.centos.src.rpm + sudo yum install git rpmdevtools -IMPORTANT: This operation must be done with your normal user account. Do *not* perform this command as root. - -Make sure you have git installed: - -:: - - sudo yum install git - - -Now clone the ZoneMinder git repository: +Now clone the ZoneMinder git repository from your home folder: :: @@ -211,32 +202,66 @@ Now clone the ZoneMinder git repository: git clone https://github.com/ZoneMinder/ZoneMinder cd ZoneMinder -This will create a sub-folder called ZoneMinder, which will contain the latest development. +This will create a sub-folder called ZoneMinder, which will contain the latest development source code. -We want to turn this into a tarball, but first we need to figure out what to name it. Look here: +If you have previsouly cloned the ZoneMinder git repo and wish to update it to the most recent, then issue these commands instead: :: - ls ~/rpmbuild/SOURCES - -The tarball from the previsouly installed SRPM should be there. This is the name we will use. For this example, the name is ZoneMinder-1.28.1.tar.gz. From the root folder of the local ZoneMinder git repository, execute the following: + cd ~\ZoneMinder + git pull origin master + +Get the crud submodule tarball: :: - git archive --prefix=ZoneMinder-1.28.1/ -o ~/rpmbuild/SOURCES/zoneminder-1.28.1.tar.gz HEAD + spectool -f -g -R -s 1 ~/ZoneMinder/distros/redhat/zoneminder.spec -Note that we are overwriting the original tarball. If you wish to keep the original tarball then create a copy prior to creating the new tarball. +At this point, you can make changes to the source code. Depending on what you want to do with those changes, you might want to create a new branch first: + +:: + + cd ~\ZoneMinder + git checkout -b mynewbranch + +When using git, you usually want to work out of a branch, rather than master. + +Again, depending on what you want to do with those changes, you may want to commit your changes: + +:: + + cd ~\ZoneMinder + git add . + git commit + +Once you have made your changes, it is time to turn your work into a new tarball, but first we need to look in the rpm specfile: + +:: + + less ~/ZoneMinder/distros/redhat/zoneminder.spec + +Scroll down until you see the Version field. Note the value which will be in the format x.xx.x. Now create the tarball with the following command: + +:: + + cd ~\ZoneMinder + git archive --prefix=ZoneMinder-1.31.1/ -o ~/rpmbuild/SOURCES/zoneminder-1.31.1.tar.gz HEAD + +Replace "1.31.1" with the Version specified in the rpm specfile. From the root of the local ZoneMinder git repo, execute the following: :: + cd ~\ZoneMinder rpmbuild -bs --nodeps distros/redhat/zoneminder.spec -Notice we used the rpm specfile that is part of the latest master branch you just downloaded, rather than the one that may be in your ~/rpmbbuild/SOURCES folder. +This step will create a source rpm and it will tell you where it was saved. For example: -This step will overwrite the SRPM you originally downloaded, so you may want to back it up prior to completing this step. Note that the name of the specfile will vary slightly depending on the target distro. +:: -You should now have a new SRPM under ~/rpmbuild/SRPMS. In our example, the SRPM is called zoneminder-1.28.1-2.el7.centos.src.rpm. Now follow the previous instructions that describe how to use the buildzm script, using ~/rpmbuild/SRPMS/zoneminder-1.28.1-2.el7.centos.src.rpm as the path to your SRPM. + Wrote: /home/abauer/rpmbuild/SRPMS/zoneminder-1.31.1-1.fc26.src.rpm + +Now follow the previous instructions `Build from SRPM`_ which describe how to build that source rpm into an rpm. From 0936dc79bc7e199245f34d61580f860a0d1cdff4 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Fri, 11 Aug 2017 12:37:44 -0500 Subject: [PATCH 16/20] Update redhat.rst --- docs/installationguide/redhat.rst | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/docs/installationguide/redhat.rst b/docs/installationguide/redhat.rst index d3a8cf49e..75158ffa6 100644 --- a/docs/installationguide/redhat.rst +++ b/docs/installationguide/redhat.rst @@ -181,11 +181,9 @@ Copy the newly built ZoneMinder RPMs to the desired system, enable RPM Fusion as Finally, you may want to consider editing the rpmfusion repo file under /etc/yum.repos.d and placing an “exclude=zoneminder*” line into the config file. This will prevent your system from overwriting your manually built RPM with the ZoneMinder RPM found in the repo. -How to Modify the Source Prior to Build -*************************************** -In the previous section we described how to rebuild an existing ZoneMinder SRPM. The instructions which follow show how to build the ZoneMinder git source tree into an rpm. - -Before attempting this part of the instructions, make sure and follow the previous instructions `Build from SRPM`_. Knowing this part works will assist in troubleshooting later should something go wrong. +How to Create Your Own Source RPM +********************************* +In the previous section we described how to rebuild an existing ZoneMinder SRPM. The instructions which follow show how to build the ZoneMinder git source tree into a source rpm, which can be used in the previous section to build an rpm. Make sure git and rpmdevtools are installed: @@ -217,14 +215,12 @@ Get the crud submodule tarball: spectool -f -g -R -s 1 ~/ZoneMinder/distros/redhat/zoneminder.spec -At this point, you can make changes to the source code. Depending on what you want to do with those changes, you might want to create a new branch first: +At this point, you can make changes to the source code. Depending on what you want to do with those changes, you generally want to create a new branch first: :: cd ~\ZoneMinder git checkout -b mynewbranch - -When using git, you usually want to work out of a branch, rather than master. Again, depending on what you want to do with those changes, you may want to commit your changes: @@ -240,14 +236,14 @@ Once you have made your changes, it is time to turn your work into a new tarball less ~/ZoneMinder/distros/redhat/zoneminder.spec -Scroll down until you see the Version field. Note the value which will be in the format x.xx.x. Now create the tarball with the following command: +Scroll down until you see the Version field. Note the value, which will be in the format x.xx.x. Now create the tarball with the following command: :: cd ~\ZoneMinder git archive --prefix=ZoneMinder-1.31.1/ -o ~/rpmbuild/SOURCES/zoneminder-1.31.1.tar.gz HEAD -Replace "1.31.1" with the Version specified in the rpm specfile. +Replace "1.31.1" with the Version shown in the rpm specfile. From the root of the local ZoneMinder git repo, execute the following: @@ -263,5 +259,3 @@ This step will create a source rpm and it will tell you where it was saved. For Wrote: /home/abauer/rpmbuild/SRPMS/zoneminder-1.31.1-1.fc26.src.rpm Now follow the previous instructions `Build from SRPM`_ which describe how to build that source rpm into an rpm. - - From edd66dd0cf277a80bf221b867703707d87cd56d6 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Fri, 11 Aug 2017 12:41:12 -0500 Subject: [PATCH 17/20] Update redhat.rst --- docs/installationguide/redhat.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/installationguide/redhat.rst b/docs/installationguide/redhat.rst index 75158ffa6..93513fe08 100644 --- a/docs/installationguide/redhat.rst +++ b/docs/installationguide/redhat.rst @@ -132,27 +132,27 @@ Your build environment is now set up. Build from SRPM *************** -To continue, you need a ZoneMinder SRPM. If you wish to rebuild a ZoneMinder release, then browse the `RPM Fusion site `_. If instead you wish to rebuild the latest package from our master branch then browse the `Zmrepo site `_. +To continue, you need a ZoneMinder SRPM. If you wish to rebuild a ZoneMinder release, then browse the `RPM Fusion site `_. If instead you wish to rebuild the latest source rpm from our master branch then browse the `Zmrepo site `_. -For this example, I'll use one of the SRPMS from zmrepo: +For this example, I'll use one of the source rpms from zmrepo: :: - wget -P ~/rpmbuild/SRPMS http://zmrepo.zoneminder.com/el/7/SRPMS/zoneminder-1.28.1-2.el7.centos.src.rpm + wget -P ~/rpmbuild/SRPMS http://zmrepo.zoneminder.com/el/7/SRPMS/zoneminder-1.31.1-1.el7.centos.src.rpm Now comes the fun part. To build ZoneMinder, issue the following command: :: - mock -r epel-7-x86_64-rpmfusion_free ~/rpmbuild/SRPMS/zoneminder-1.28.1-2.el7.centos.src.rpm + mock -r epel-7-x86_64-rpmfusion_free ~/rpmbuild/SRPMS/zoneminder-1.31.1-1.el7.centos.src.rpm Want to build ZoneMinder for Fedora, instead of CentOS, from the same host? Once you download the Fedora SRPM, issue the following: :: - mock -r fedora-26-x86_64-rpmfusion_free ~/rpmbuild/SRPMS/zoneminder-1.28.1-1.fc21.src.rpm + mock -r fedora-26-x86_64-rpmfusion_free ~/rpmbuild/SRPMS/zoneminder-1.31.1-1.el7.centos.src.rpm Notice that the mock tool requires the following parameters: From c353d5d17b9fe7d713364883a2f8598c81c95014 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Mon, 14 Aug 2017 08:45:03 -0500 Subject: [PATCH 18/20] Update CMakeLists.txt fix erroneous creation of /etc/zm/conf.d/conf.d during an out-of-source build --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c104998a..834c2a1af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -801,7 +801,7 @@ endif(ZM_PERL_SEARCH_PATH) # If this is an out-of-source build, copy the files we need to the binary directory if(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) - file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/conf.d" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/conf.d" PATTERN "*.in" EXCLUDE) + file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/conf.d" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}" PATTERN "*.in" EXCLUDE) endif(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) # Generate files from the .in files From ecb7df0e8b5df7a2448090cf2d8275066653f53c Mon Sep 17 00:00:00 2001 From: ralimi Date: Mon, 14 Aug 2017 07:30:42 -0700 Subject: [PATCH 19/20] Support SSL for mysql connections (#1965) * Fix install location for config files when building to alternate directory. With the previous code, we ended up with a directory structure like the following: $ find /etc/zm/conf.d/ /etc/zm/conf.d/ /etc/zm/conf.d/01-system-paths.conf /etc/zm/conf.d/conf.d /etc/zm/conf.d/conf.d/README /etc/zm/conf.d/conf.d/02-multiserver.conf * Omitted README file that should have appeared in /etc/zm/conf.d * Fix location for configs when building to alternate directory. * Fix works, but this should go on a branch instead. * Fix works, but this should go on a branch instead. * Fix location for configs when building to alternate directory. With the previous code, we ended up with a directory structure like the following: $ find /etc/zm/conf.d/ /etc/zm/conf.d/ /etc/zm/conf.d/01-system-paths.conf /etc/zm/conf.d/conf.d /etc/zm/conf.d/conf.d/README /etc/zm/conf.d/conf.d/02-multiserver.conf * Remove double quotes. This is a list of paths. * Allow SSL database connection to be secured with SSL. * Fix incorrect variable name * Fix PHP syntax errors * SSL connection parameters must also be passed in API. * Revert fixes to build files; they should not be in this branch. --- INSTALL | 3 +++ cmakecacheimport.sh | 6 ++++++ scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in | 11 ++++++++++- scripts/ZoneMinder/lib/ZoneMinder/Database.pm | 13 ++++++++++++- scripts/ZoneMinder/lib/ZoneMinder/Logger.pm | 11 ++++++++++- src/zm_config.cpp | 6 ++++++ src/zm_config.h.in | 3 +++ src/zm_db.cpp | 2 ++ src/zm_logger.cpp | 2 ++ web/api/app/Config/database.php.default | 3 +++ web/includes/database.php | 7 ++++++- zm.conf.in | 9 +++++++++ 12 files changed, 72 insertions(+), 4 deletions(-) diff --git a/INSTALL b/INSTALL index 3b50f5b62..666105c40 100644 --- a/INSTALL +++ b/INSTALL @@ -45,6 +45,9 @@ Possible configuration options: ZM_DB_NAME Name of ZoneMinder database, default: zm ZM_DB_USER Name of ZoneMinder database user, default: zmuser ZM_DB_PASS Password of ZoneMinder database user, default: zmpass + ZM_DB_SSL_CA_CERT Path to SSL CA certificate, default: empty; SSL not enabled + ZM_DB_SSL_CLIENT_KEY Path to SSL client key, default: empty; SSL not enabled + ZM_DB_SSL_CLIENT_CERT Path to SSL client certificate, default: empty; SSL not enabled ZM_WEB_USER The user apache or the local web server runs on. Leave empty for automatic detection. If that fails, you can use this variable to force ZM_WEB_GROUP The group apache or the local web server runs on, Leave empty to be the same as the web user ZM_DIR_EVENTS Location where events are recorded to, default: ZM_CONTENTDIR/events diff --git a/cmakecacheimport.sh b/cmakecacheimport.sh index 79253f6ad..7bd44f311 100755 --- a/cmakecacheimport.sh +++ b/cmakecacheimport.sh @@ -55,6 +55,9 @@ echo "Database host : $ZM_DB_HOST" echo "Database name : $ZM_DB_NAME" echo "Database user : $ZM_DB_USER" echo "Database password : Not shown" +echo "Database SSL CA Cert : $ZM_DB_SSL_CA_CERT" +echo "Database SSL Client Key : $ZM_DB_SSL_CLIENT_KEY" +echo "Database SSL Client Cert : $ZM_DB_SSL_CLIENT_CERT" CMPATH="CACHE PATH \"Imported by cmakecacheimport.sh\" FORCE" @@ -72,6 +75,9 @@ echo "set(ZM_DB_HOST \"$ZM_DB_HOST\" $CMSTRING)">>zm_conf.cmake echo "set(ZM_DB_NAME \"$ZM_DB_NAME\" $CMSTRING)">>zm_conf.cmake echo "set(ZM_DB_USER \"$ZM_DB_USER\" $CMSTRING)">>zm_conf.cmake echo "set(ZM_DB_PASS \"$ZM_DB_PASS\" $CMSTRING)">>zm_conf.cmake +echo "set(ZM_DB_SSL_CA_CERT \"$ZM_DB_SSL_CA_CERT\" $CMSTRING)">>zm_conf.cmake +echo "set(ZM_DB_SSL_CLIENT_KEY \"$ZM_DB_SSL_CLIENT_KEY\" $CMSTRING)">>zm_conf.cmake +echo "set(ZM_DB_SSL_CLIENT_CERT \"$ZM_DB_SSL_CLIENT_CERT\" $CMSTRING)">>zm_conf.cmake echo "" echo "Wrote zm_conf.cmake" diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in index 60cdce658..d2b444ef4 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in @@ -101,8 +101,17 @@ BEGIN { } else { $socket = ";host=".$Config{ZM_DB_HOST}; } + my $sslOptions = ""; + if ( $Config{ZM_DB_SSL_CA_CERT} ) { + $sslOptions = ';'.join(';', + "mysql_ssl=1", + "mysql_ssl_ca_file=".$Config{ZM_DB_SSL_CA_CERT}, + "mysql_ssl_client_key=".$Config{ZM_DB_SSL_CLIENT_KEY}, + "mysql_ssl_client_cert=".$Config{ZM_DB_SSL_CLIENT_CERT} + ); + } my $dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME} - .$socket + .$socket.$sslOptions , $Config{ZM_DB_USER} , $Config{ZM_DB_PASS} ) or croak( "Can't connect to db" ); diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Database.pm b/scripts/ZoneMinder/lib/ZoneMinder/Database.pm index 19374543e..cf0f488e2 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Database.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Database.pm @@ -90,8 +90,19 @@ sub zmDbConnect { } else { $socket = ";host=".$Config{ZM_DB_HOST}; } + + my $sslOptions = ""; + if ( $Config{ZM_DB_SSL_CA_CERT} ) { + $sslOptions = ';'.join(';', + "mysql_ssl=1", + "mysql_ssl_ca_file=".$Config{ZM_DB_SSL_CA_CERT}, + "mysql_ssl_client_key=".$Config{ZM_DB_SSL_CLIENT_KEY}, + "mysql_ssl_client_cert=".$Config{ZM_DB_SSL_CLIENT_CERT} + ); + } + $dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME} - .$socket . ($options?';'.join(';', map { $_.'='.$$options{$_} } keys %{$options} ) : '' ) + .$socket . $sslOptions . ($options?';'.join(';', map { $_.'='.$$options{$_} } keys %{$options} ) : '' ) , $Config{ZM_DB_USER} , $Config{ZM_DB_PASS} ); diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm b/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm index 123033105..27a9fc1a8 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm @@ -434,8 +434,17 @@ sub databaseLevel { } else { $socket = ";host=".$Config{ZM_DB_HOST}; } + my $sslOptions = ""; + if ( $Config{ZM_DB_SSL_CA_CERT} ) { + $sslOptions = ';'.join(';', + "mysql_ssl=1", + "mysql_ssl_ca_file=".$Config{ZM_DB_SSL_CA_CERT}, + "mysql_ssl_client_key=".$Config{ZM_DB_SSL_CLIENT_KEY}, + "mysql_ssl_client_cert=".$Config{ZM_DB_SSL_CLIENT_CERT} + ); + } $this->{dbh} = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME} - .$socket + .$socket.$sslOptions , $Config{ZM_DB_USER} , $Config{ZM_DB_PASS} ); diff --git a/src/zm_config.cpp b/src/zm_config.cpp index 601a4a950..fdbcffe52 100644 --- a/src/zm_config.cpp +++ b/src/zm_config.cpp @@ -150,6 +150,12 @@ void process_configfile( char* configFile) { staticConfig.DB_USER = std::string(val_ptr); else if ( strcasecmp( name_ptr, "ZM_DB_PASS" ) == 0 ) staticConfig.DB_PASS = std::string(val_ptr); + else if ( strcasecmp( name_ptr, "ZM_DB_SSL_CA_CERT" ) == 0 ) + staticConfig.DB_SSL_CA_CERT = std::string(val_ptr); + else if ( strcasecmp( name_ptr, "ZM_DB_SSL_CLIENT_KEY" ) == 0 ) + staticConfig.DB_SSL_CLIENT_KEY = std::string(val_ptr); + else if ( strcasecmp( name_ptr, "ZM_DB_SSL_CLIENT_CERT" ) == 0 ) + staticConfig.DB_SSL_CLIENT_CERT = std::string(val_ptr); else if ( strcasecmp( name_ptr, "ZM_PATH_WEB" ) == 0 ) staticConfig.PATH_WEB = std::string(val_ptr); else if ( strcasecmp( name_ptr, "ZM_SERVER_HOST" ) == 0 ) diff --git a/src/zm_config.h.in b/src/zm_config.h.in index 461e4bb5e..3ecc70d7c 100644 --- a/src/zm_config.h.in +++ b/src/zm_config.h.in @@ -67,6 +67,9 @@ struct StaticConfig std::string DB_NAME; std::string DB_USER; std::string DB_PASS; + std::string DB_SSL_CA_CERT; + std::string DB_SSL_CLIENT_KEY; + std::string DB_SSL_CLIENT_CERT; std::string PATH_WEB; std::string SERVER_NAME; unsigned int SERVER_ID; diff --git a/src/zm_db.cpp b/src/zm_db.cpp index 8eed90569..568f9cd72 100644 --- a/src/zm_db.cpp +++ b/src/zm_db.cpp @@ -37,6 +37,8 @@ void zmDbConnect() my_bool reconnect = 1; if ( mysql_options( &dbconn, MYSQL_OPT_RECONNECT, &reconnect ) ) Fatal( "Can't set database auto reconnect option: %s", mysql_error( &dbconn ) ); + if ( !staticConfig.DB_SSL_CA_CERT.empty() ) + mysql_ssl_set( &dbconn, staticConfig.DB_SSL_CLIENT_KEY.c_str(), staticConfig.DB_SSL_CLIENT_CERT.c_str(), staticConfig.DB_SSL_CA_CERT.c_str(), NULL, NULL ); std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":" ); if ( colonIndex == std::string::npos ) { diff --git a/src/zm_logger.cpp b/src/zm_logger.cpp index ee3a5a8b5..bc0d59c23 100644 --- a/src/zm_logger.cpp +++ b/src/zm_logger.cpp @@ -341,6 +341,8 @@ Logger::Level Logger::databaseLevel( Logger::Level databaseLevel ) { my_bool reconnect = 1; if ( mysql_options( &mDbConnection, MYSQL_OPT_RECONNECT, &reconnect ) ) Fatal( "Can't set database auto reconnect option: %s", mysql_error( &mDbConnection ) ); + if ( !staticConfig.DB_SSL_CA_CERT.empty() ) + mysql_ssl_set( &mDbConnection, staticConfig.DB_SSL_CLIENT_KEY.c_str(), staticConfig.DB_SSL_CLIENT_CERT.c_str(), staticConfig.DB_SSL_CA_CERT.c_str(), NULL, NULL ); std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":" ); if ( colonIndex == std::string::npos ) { if ( !mysql_real_connect( &mDbConnection, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, 0, NULL, 0 ) ) { diff --git a/web/api/app/Config/database.php.default b/web/api/app/Config/database.php.default index 55f2bc958..c06953ec7 100644 --- a/web/api/app/Config/database.php.default +++ b/web/api/app/Config/database.php.default @@ -70,6 +70,9 @@ class DATABASE_CONFIG { 'login' => ZM_DB_USER, 'password' => ZM_DB_PASS, 'database' => ZM_DB_NAME, + 'ssl_ca' => ZM_DB_SSL_CA_CERT, + 'ssl_key' => ZM_DB_SSL_CLIENT_KEY, + 'ssl_cert' => ZM_DB_SSL_CLIENT_CERT, 'prefix' => '', 'encoding' => 'utf8', ); diff --git a/web/includes/database.php b/web/includes/database.php index dea9e4b8c..bc1242029 100644 --- a/web/includes/database.php +++ b/web/includes/database.php @@ -42,7 +42,12 @@ function dbConnect() { } try { - $dbConn = new PDO( ZM_DB_TYPE . $socket . ';dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS ); + $dbOptions = array( + PDO::MYSQL_ATTR_SSL_CA => ZM_DB_SSL_CA_CERT, + PDO::MYSQL_ATTR_SSL_KEY => ZM_DB_SSL_CLIENT_KEY, + PDO::MYSQL_ATTR_SSL_CERT => ZM_DB_SSL_CLIENT_CERT, + ); + $dbConn = new PDO( ZM_DB_TYPE . $socket . ';dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS, $dbOptions ); $dbConn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $dbConn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch(PDOException $ex ) { diff --git a/zm.conf.in b/zm.conf.in index bde068104..312c9aeae 100644 --- a/zm.conf.in +++ b/zm.conf.in @@ -49,6 +49,15 @@ ZM_DB_USER=@ZM_DB_USER@ # ZoneMinder database password ZM_DB_PASS=@ZM_DB_PASS@ +# SSL CA certificate for ZoneMinder database +ZM_DB_SSL_CA_CERT=@ZM_DB_SSL_CA_CERT@ + +# SSL client key for ZoneMinder database +ZM_DB_SSL_CLIENT_KEY=@ZM_DB_SSL_CLIENT_KEY@ + +# SSL client cert for ZoneMinder database +ZM_DB_SSL_CLIENT_CERT=@ZM_DB_SSL_CLIENT_CERT@ + # Do NOT set ZM_SERVER_HOST if you are not using Multi-Server # You have been warned # From 0f0ab6170b4bb06d5dd258a11993e9900247e8e8 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 14 Aug 2017 11:15:09 -0400 Subject: [PATCH 20/20] Don't decode video until we have received a keyframe. --- src/zm_ffmpeg_camera.cpp | 137 +++++++++++++++++++++------------------ 1 file changed, 74 insertions(+), 63 deletions(-) diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index 12d3f4d19..f410ba9de 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -165,9 +165,14 @@ int FfmpegCamera::Capture( Image &image ) { Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, avResult, errbuf ); return( -1 ); } + + int keyframe = packet.flags & AV_PKT_FLAG_KEY; + if ( keyframe ) + have_video_keyframe = true; + Debug( 5, "Got packet from stream %d dts (%d) pts(%d)", packet.stream_index, packet.pts, packet.dts ); // What about audio stream? Maybe someday we could do sound detection... - if ( packet.stream_index == mVideoStreamId ) { + if ( ( packet.stream_index == mVideoStreamId ) && ( keyframe || have_video_keyframe ) ) { #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) ret = avcodec_send_packet( mVideoCodecContext, &packet ); if ( ret < 0 ) { @@ -257,6 +262,7 @@ int FfmpegCamera::OpenFfmpeg() { mOpenStart = time(NULL); mIsOpening = true; + have_video_keyframe = false; // Open the input, not necessarily a file #if !LIBAVFORMAT_VERSION_CHECK(53, 2, 0, 4, 0) @@ -614,11 +620,11 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event return( -1 ); } - int key_frame = packet.flags & AV_PKT_FLAG_KEY; + int keyframe = packet.flags & AV_PKT_FLAG_KEY; Debug( 4, "Got packet from stream %d packet pts (%d) dts(%d), key?(%d)", packet.stream_index, packet.pts, packet.dts, - key_frame + keyframe ); //Video recording @@ -725,7 +731,7 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event // Buffer video packets, since we are not recording. // All audio packets are keyframes, so only if it's a video keyframe if ( packet.stream_index == mVideoStreamId ) { - if ( key_frame ) { + if ( keyframe ) { Debug(3, "Clearing queue"); packetqueue.clearQueue( monitor->GetPreEventCount(), mVideoStreamId ); } @@ -748,81 +754,86 @@ else if ( packet.pts && video_last_pts > packet.pts ) { packetqueue.queuePacket( &packet ); } } else if ( packet.stream_index == mVideoStreamId ) { - if ( key_frame || packetqueue.size() ) // it's a keyframe or we already have something in the queue + if ( keyframe || packetqueue.size() ) // it's a keyframe or we already have something in the queue packetqueue.queuePacket( &packet ); } } // end if recording or not if ( packet.stream_index == mVideoStreamId ) { - if ( videoStore && ( have_video_keyframe || key_frame ) ) { - - //Write the packet to our video store - int ret = videoStore->writeVideoFramePacket( &packet ); - if ( ret < 0 ) { //Less than zero and we skipped a frame - zm_av_packet_unref( &packet ); - return 0; + // only do decode if we have had a keyframe, should save a few cycles. + if ( have_video_keyframe || keyframe ) { + + if ( videoStore ) { + + //Write the packet to our video store + int ret = videoStore->writeVideoFramePacket( &packet ); + if ( ret < 0 ) { //Less than zero and we skipped a frame + zm_av_packet_unref( &packet ); + return 0; + } + have_video_keyframe = true; } - have_video_keyframe = true; - } - Debug(4, "about to decode video" ); - + + Debug(4, "about to decode video" ); + #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - ret = avcodec_send_packet( mVideoCodecContext, &packet ); - if ( ret < 0 ) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to send packet at frame %d: %s, continuing", frameCount, errbuf ); - zm_av_packet_unref( &packet ); - continue; - } - ret = avcodec_receive_frame( mVideoCodecContext, mRawFrame ); - if ( ret < 0 ) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Debug( 1, "Unable to send packet at frame %d: %s, continuing", frameCount, errbuf ); - zm_av_packet_unref( &packet ); - continue; - } - frameComplete = 1; -# else - ret = zm_avcodec_decode_video( mVideoCodecContext, mRawFrame, &frameComplete, &packet ); - if ( ret < 0 ) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to decode frame at frame %d: %s, continuing", frameCount, errbuf ); - zm_av_packet_unref( &packet ); - continue; - } -#endif - - Debug( 4, "Decoded video packet at frame %d", frameCount ); - - if ( frameComplete ) { - Debug( 4, "Got frame %d", frameCount ); - - uint8_t* directbuffer; - - /* Request a writeable buffer of the target image */ - directbuffer = image.WriteBuffer(width, height, colours, subpixelorder); - if ( directbuffer == NULL ) { - Error("Failed requesting writeable buffer for the captured image."); + ret = avcodec_send_packet( mVideoCodecContext, &packet ); + if ( ret < 0 ) { + av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); + Error( "Unable to send packet at frame %d: %s, continuing", frameCount, errbuf ); zm_av_packet_unref( &packet ); - return (-1); + continue; } + ret = avcodec_receive_frame( mVideoCodecContext, mRawFrame ); + if ( ret < 0 ) { + av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); + Debug( 1, "Unable to send packet at frame %d: %s, continuing", frameCount, errbuf ); + zm_av_packet_unref( &packet ); + continue; + } + frameComplete = 1; +# else + ret = zm_avcodec_decode_video( mVideoCodecContext, mRawFrame, &frameComplete, &packet ); + if ( ret < 0 ) { + av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); + Error( "Unable to decode frame at frame %d: %s, continuing", frameCount, errbuf ); + zm_av_packet_unref( &packet ); + continue; + } +#endif + + Debug( 4, "Decoded video packet at frame %d", frameCount ); + + if ( frameComplete ) { + Debug( 4, "Got frame %d", frameCount ); + + uint8_t* directbuffer; + + /* Request a writeable buffer of the target image */ + directbuffer = image.WriteBuffer(width, height, colours, subpixelorder); + if ( directbuffer == NULL ) { + Error("Failed requesting writeable buffer for the captured image."); + zm_av_packet_unref( &packet ); + return (-1); + } #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) - av_image_fill_arrays(mFrame->data, mFrame->linesize, directbuffer, imagePixFormat, width, height, 1); + av_image_fill_arrays(mFrame->data, mFrame->linesize, directbuffer, imagePixFormat, width, height, 1); #else - avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height); + avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height); #endif - if (sws_scale(mConvertContext, mRawFrame->data, mRawFrame->linesize, - 0, mVideoCodecContext->height, mFrame->data, mFrame->linesize) < 0) { - Fatal("Unable to convert raw format %u to target format %u at frame %d", + if (sws_scale(mConvertContext, mRawFrame->data, mRawFrame->linesize, + 0, mVideoCodecContext->height, mFrame->data, mFrame->linesize) < 0) { + Fatal("Unable to convert raw format %u to target format %u at frame %d", mVideoCodecContext->pix_fmt, imagePixFormat, frameCount); - } + } - frameCount++; - } else { - Debug( 3, "Not framecomplete after av_read_frame" ); - } // end if frameComplete + frameCount++; + } else { + Debug( 3, "Not framecomplete after av_read_frame" ); + } // end if frameComplete + } // end if keyframe or have_video_keyframe } else if ( packet.stream_index == mAudioStreamId ) { //FIXME best way to copy all other streams if ( videoStore ) { if ( record_audio ) {