From 5a73c38237ae545d8d640d0e0d129bee3ff0cb7e Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 2 Aug 2017 16:29:23 -0400 Subject: [PATCH] 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' ) { ?> - + + + + - - + + + + + + + +
+
'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"/>