diff --git a/scripts/zmaudit.pl.in b/scripts/zmaudit.pl.in index e0ba169ee..8d18eabc5 100644 --- a/scripts/zmaudit.pl.in +++ b/scripts/zmaudit.pl.in @@ -81,7 +81,7 @@ use constant EVENT_PATH => ($Config{ZM_DIR_EVENTS}=~m|/|) ? $Config{ZM_DIR_EVENTS} : ($Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_EVENTS}) ; -use constant ZM_AUDIT_PID => $Config{ZM_RUNDIR}.'/zm.pid' +use constant ZM_AUDIT_PID => '@ZM_RUNDIR@/zmaudit.pid'; $| = 1; diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp index 817c8753a..4344e7f75 100644 --- a/src/zm_eventstream.cpp +++ b/src/zm_eventstream.cpp @@ -579,7 +579,6 @@ Image * EventStream::getImage( ) { Debug( 2, "EventStream::getImage path(%s) frame(%d)", event_data->path, curr_frame_id ); snprintf( filepath, sizeof(filepath), staticConfig.capture_file_format, event_data->path, curr_frame_id ); - Debug( 2, "EventStream::getImage path(%s) ", filepath, curr_frame_id ); Image *image = new Image( filepath ); return image; } @@ -657,7 +656,7 @@ bool EventStream::sendFrame( int delta_us ) { } else if ( ffmpeg_input ) { // Get the frame from the mp4 input Debug(1,"Getting frame from ffmpeg"); - AVFrame *frame = ffmpeg_input->get_frame( ffmpeg_input->get_video_stream_id() ); + AVFrame *frame = ffmpeg_input->get_frame( ffmpeg_input->get_video_stream_id(), curr_frame_id ); if ( frame ) { image = new Image( frame ); av_frame_free(&frame); diff --git a/src/zm_ffmpeg.cpp b/src/zm_ffmpeg.cpp index ca07a0f81..fe710ab87 100644 --- a/src/zm_ffmpeg.cpp +++ b/src/zm_ffmpeg.cpp @@ -373,3 +373,48 @@ bool is_audio_stream( AVStream * stream ) { } return false; } + +int zm_receive_frame( AVCodecContext *context, AVFrame *frame, AVPacket &packet ) { + int ret; +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + if ( (ret = avcodec_send_packet(context, &packet)) < 0 ) { + Error( "Unable to send packet %s, continuing", + av_make_error_string(ret).c_str() ); + return 0; + } + +#if HAVE_AVUTIL_HWCONTEXT_H + if ( hwaccel ) { + if ( (ret = avcodec_receive_frame(context, hwFrame)) < 0 ) { + Error( "Unable to receive frame %d: %s, continuing", streams[packet.stream_index].frame_count, + av_make_error_string(ret).c_str() ); + return 0; + } + if ( (ret = av_hwframe_transfer_data(frame, hwFrame, 0)) < 0 ) { + Error( "Unable to transfer frame at frame %d: %s, continuing", streams[packet.stream_index].frame_count, + av_make_error_string(ret).c_str() ); + return 0; + } + } else { +#endif + if ( (ret = avcodec_receive_frame(context, frame)) < 0 ) { + Error( "Unable to send packet %s, continuing", av_make_error_string(ret).c_str() ); + return 0; + } +#if HAVE_AVUTIL_HWCONTEXT_H + } +#endif + +# else + int frameComplete; + while ( !frameComplete ) { + if ( (ret = zm_avcodec_decode_video( context, frame, &frameComplete, &packet )) < 0 ) { + Error( "Unable to decode frame at frame %d: %s, continuing", + streams[packet.stream_index].frame_count, + av_make_error_string(ret).c_str() ); + return 0; + } + } +#endif + return 1; +} // end int zm_receive_frame( AVCodecContext *context, AVFrame *frame, AVPacket &packet ) diff --git a/src/zm_ffmpeg.h b/src/zm_ffmpeg.h index 10ad5260c..ff896e0ae 100644 --- a/src/zm_ffmpeg.h +++ b/src/zm_ffmpeg.h @@ -237,9 +237,8 @@ enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subp */ #ifdef __cplusplus - inline static const std::string av_make_error_string(int errnum) - { - char errbuf[AV_ERROR_MAX_STRING_SIZE]; + inline static const std::string av_make_error_string(int errnum) { + static char errbuf[AV_ERROR_MAX_STRING_SIZE]; #if LIBAVUTIL_VERSION_CHECK(50, 13, 0, 13, 0) av_strerror(errnum, errbuf, AV_ERROR_MAX_STRING_SIZE); #else @@ -327,4 +326,5 @@ int check_sample_fmt(AVCodec *codec, enum AVSampleFormat sample_fmt); bool is_video_stream( AVStream * stream ); bool is_audio_stream( AVStream * stream ); +int zm_receive_frame( AVCodecContext *context, AVFrame *frame, AVPacket &packet ); #endif // ZM_FFMPEG_H diff --git a/src/zm_ffmpeg_input.cpp b/src/zm_ffmpeg_input.cpp index 8697d55b8..240dfff24 100644 --- a/src/zm_ffmpeg_input.cpp +++ b/src/zm_ffmpeg_input.cpp @@ -13,6 +13,9 @@ FFmpeg_Input::FFmpeg_Input() { } FFmpeg_Input::~FFmpeg_Input() { + if ( input_format_context ) { + Close(); + } if ( streams ) { delete streams; streams = NULL; @@ -25,7 +28,6 @@ int FFmpeg_Input::Open( const char *filepath ) { /** Open the input file to read from it. */ if ( (error = avformat_open_input( &input_format_context, filepath, NULL, NULL)) < 0 ) { - Error("Could not open input file '%s' (error '%s')\n", filepath, av_make_error_string(error).c_str() ); input_format_context = NULL; @@ -94,94 +96,73 @@ int FFmpeg_Input::Open( const char *filepath ) { return 0; } // end int FFmpeg_Input::Open( const char * filepath ) -AVFrame *FFmpeg_Input::get_frame( int stream_id ) { - Debug(1, "Getting frame from stream %d", stream_id ); +int FFmpeg_Input::Close( ) { + for ( unsigned int i = 0; i < input_format_context->nb_streams; i += 1 ) { + if ( streams[i].context ) { + avcodec_close( streams[i].context ); +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + avcodec_free_context(& streams[i].context ); +#endif + streams[i].context = NULL; + } + } + + if ( input_format_context ) { +#if !LIBAVFORMAT_VERSION_CHECK(53, 17, 0, 25, 0) + av_close_input_file( input_format_context ); +#else + avformat_close_input( &input_format_context ); +#endif + input_format_context = NULL; + } + return 1; +} // end int FFmpeg_Input::Close() + +AVFrame *FFmpeg_Input::get_frame( int stream_id, int frame_number ) { + Debug(1, "Getting frame from stream %d, frame_number(%d)", stream_id, frame_number ); - int frameComplete = false; AVPacket packet; av_init_packet( &packet ); AVFrame *frame = zm_av_frame_alloc(); - char errbuf[AV_ERROR_MAX_STRING_SIZE]; - while ( !frameComplete ) { - int ret = av_read_frame( input_format_context, &packet ); + while ( frame_number >= streams[stream_id].frame_count ) { + + int ret = av_read_frame(input_format_context, &packet); if ( ret < 0 ) { - av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE); if ( // Check if EOF. (ret == AVERROR_EOF || (input_format_context->pb && input_format_context->pb->eof_reached)) || // Check for Connection failure. (ret == -110) ) { - Info( "av_read_frame returned %s.", errbuf ); - return NULL; + Info( "av_read_frame returned %s.", av_make_error_string(ret).c_str() ); + } else { + Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret, + av_make_error_string(ret).c_str() ); } - Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret, errbuf ); return NULL; } - if ( (stream_id < 0 ) || ( packet.stream_index == stream_id ) ) { - Debug(1,"Packet is for our stream (%d)", packet.stream_index ); + if ( ( stream_id < 0 ) || ( packet.stream_index != stream_id ) ) { + Warning("Packet is not for our stream (%d)", packet.stream_index); + return NULL; + } -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - AVCodecContext *context = streams[packet.stream_index].context; - - ret = avcodec_send_packet( context, &packet ); - if ( ret < 0 ) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to send packet at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf ); + if ( ! zm_receive_frame( streams[packet.stream_index].context, frame, packet ) ) { + Error("Unable to get frame %d, continuing", streams[packet.stream_index].frame_count); zm_av_packet_unref( &packet ); continue; } else { - Debug(1, "Success getting a packet"); + Debug(1, "Success getting a packet at frame (%d)", streams[packet.stream_index].frame_count); + streams[packet.stream_index].frame_count += 1; } -#if HAVE_AVUTIL_HWCONTEXT_H - if ( hwaccel ) { - ret = avcodec_receive_frame( context, hwFrame ); - if ( ret < 0 ) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to receive frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf ); - zm_av_packet_unref( &packet ); - continue; - } - ret = av_hwframe_transfer_data(frame, hwFrame, 0); - if (ret < 0) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to transfer frame at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf ); - zm_av_packet_unref( &packet ); - continue; - } - } else { -#endif - Debug(1,"Getting a frame?"); - ret = avcodec_receive_frame( context, frame ); - if ( ret < 0 ) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to send packet at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf ); - zm_av_packet_unref( &packet ); - continue; - } + zm_av_packet_unref( &packet ); -#if HAVE_AVUTIL_HWCONTEXT_H - } -#endif - - frameComplete = 1; -# else - ret = zm_avcodec_decode_video( context, frame, &frameComplete, &packet ); - if ( ret < 0 ) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to decode frame at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf ); - zm_av_packet_unref( &packet ); - continue; - } -#endif - } // end if it's the right stream - - zm_av_packet_unref( &packet ); - - } // end while ! frameComplete + if ( frame_number == -1 ) + break; + } // end while frame_number > streams.frame_count return frame; - } // end AVFrame *FFmpeg_Input::get_frame + + diff --git a/src/zm_ffmpeg_input.h b/src/zm_ffmpeg_input.h index 727fc110a..0fd9801f3 100644 --- a/src/zm_ffmpeg_input.h +++ b/src/zm_ffmpeg_input.h @@ -21,7 +21,7 @@ class FFmpeg_Input { int Open( const char *filename ); int Close(); - AVFrame *get_frame( int stream_id=-1 ); + AVFrame *get_frame( int stream_id=-1, int frame_number=-1 ); int get_video_stream_id() { return video_stream_id; } diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 4187cf9e3..6e1b55126 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1216,7 +1216,7 @@ bool Monitor::Analyse() { // if have event, sent frames until we find a video packet, at which point do analysis. Adaptive skip should only affect which frames we do analysis on. - int skip_index = 0; + unsigned int skip_index = 0; if ( adaptive_skip ) { int read_margin = shared_data->last_read_index - shared_data->last_write_index; @@ -1295,6 +1295,7 @@ bool Monitor::Analyse() { Debug(3, "Motion detection is enabled signal(%d) signal_change(%d)", signal, signal_change); + // if we have been told to be OFF, then we are off and don't do any processing. if ( trigger_data->trigger_state != TRIGGER_OFF ) { Debug(4, "Trigger not oFF state is (%d)", trigger_data->trigger_state ); unsigned int score = 0; @@ -1304,8 +1305,8 @@ Debug(4, "Ready"); std::string cause; Event::StringSetMap noteSetMap; + // Specifically told to be on. Setting the score here will trigger the alarm. if ( trigger_data->trigger_state == TRIGGER_ON ) { - score += trigger_data->trigger_score; if ( !event ) { if ( cause.length() ) @@ -1372,7 +1373,6 @@ Debug(3,"before DetectMotion"); // If we aren't recording, check linked monitors to see if we should be. if ( (!(signal_change && signal)) && (n_linked_monitors > 0) ) { - bool first_link = true; Event::StringSet noteSet; for ( int i=0; i < n_linked_monitors; i++ ) { if ( ! linked_monitors[i]->isConnected() ) @@ -1434,6 +1434,7 @@ Debug(3,"before DetectMotion"); } } // end if ! event } + if ( score ) { Debug(9, "Score: (%d)", score ); if ( (state == IDLE || state == TAPE || state == PREALARM ) ) { @@ -1488,7 +1489,7 @@ Debug(3, "creating new event"); if ( pre_event_images ) { Debug(2,"Have pre_event_image"); if ( analysis_fps ) { - Debug(2,"Have analysis_fps"); + Debug(2,"Have analysis_fps"); for ( int i = 0; i < pre_event_images; i++ ) { timestamps[i] = &pre_event_buffer[pre_index].timestamp; images[i] = pre_event_buffer[pre_index].image; @@ -1520,7 +1521,7 @@ Debug(3, "creating new event"); shared_data->state = state = ALARM; } last_alarm_count = image_count; - } else { + } else { // no score? if ( state == ALARM ) { Info( "%s: %03d - Gone into alert state", name, image_count ); shared_data->state = state = ALERT; @@ -1536,17 +1537,14 @@ Debug(3, "creating new event"); shared_data->state = state = TAPE; } } - } - if ( state == PREALARM ) { - if ( function != MOCORD ) { - shared_data->state = state = IDLE; - } else { - shared_data->state = state = TAPE; - } + } else if ( state == PREALARM ) { + // Back to IDLE + shared_data->state = state = function != MOCORD ? IDLE : TAPE; } if ( Event::PreAlarmCount() ) Event::EmptyPreAlarmFrames(); - } + } // end if score or not + if ( state != IDLE ) { if ( state == PREALARM || state == ALARM ) { if ( config.create_analysis_images ) { @@ -1565,6 +1563,7 @@ Debug(3, "creating new event"); } if ( got_anal_image ) { if ( state == PREALARM ) + // AddPreAlarmFrame just copies/buffers these frames in the event. If we go back to idle, we will drop them. Event::AddPreAlarmFrame( snap_image, *timestamp, score, &alarm_image ); else //event->AddFrame( snap_image, *timestamp, score, &alarm_image ); @@ -1593,23 +1592,17 @@ Debug(3, "creating new event"); if ( event && noteSetMap.size() > 0 ) event->updateNotes( noteSetMap ); } else if ( state == ALERT ) { - event->AddFrame( snap_image, *timestamp ); + // Alert means this frame has no motion, but we were alarmed and are still recording. + event->AddPacket( snap ); if ( noteSetMap.size() > 0 ) event->updateNotes( noteSetMap ); } else if ( state == TAPE ) { - //Video Storage: activate only for supported cameras. Event::AddFrame knows whether or not we are recording video and saves frames accordingly - //if((GetOptVideoWriter() == 2) && camera->SupportsNativeVideo()) { - // I don't think this is required, and causes problems, as the event file hasn't been setup yet. - //Warning("In state TAPE, - //video_store_data->recording = event->StartTime(); - //} + if ( !(image_count%(frame_skip+1)) ) { if ( config.bulk_frame_interval > 1 ) { - event->AddPacket( snap, (event->Frames()AddFrame( snap_image, *timestamp, (event->Frames()AddPacket( snap, (event->Frames()AddFrame( snap_image, *timestamp ); - event->AddPacket( snap ); + event->AddPacket( snap ); } } } diff --git a/src/zm_monitor.h b/src/zm_monitor.h index 699ecdb4c..8ad87229b 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -285,7 +285,7 @@ protected: int ready_count; int first_alarm_count; int last_alarm_count; - static bool last_signal; + bool last_signal; int buffer_count; int prealarm_count; State state; @@ -294,7 +294,6 @@ protected: time_t last_analysis_fps_time; time_t auto_resume_time; unsigned int last_motion_score; - bool last_signal; EventCloseMode event_close_mode; diff --git a/src/zm_user.cpp b/src/zm_user.cpp index b6c9f9553..418a6fd8b 100644 --- a/src/zm_user.cpp +++ b/src/zm_user.cpp @@ -148,7 +148,7 @@ User *zmLoadAuthUser( const char *auth, bool use_remote_addr ) { } } - Debug( 1, "Attempting to authenticate user from auth string '%s'", auth ); + Debug( 1, "Attempting to authenticate user from auth string '%s', remote addr(%s)", auth, remote_addr ); char sql[ZM_SQL_SML_BUFSIZ] = ""; snprintf( sql, sizeof(sql), "SELECT Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds FROM Users WHERE Enabled = 1" ); @@ -170,6 +170,17 @@ User *zmLoadAuthUser( const char *auth, bool use_remote_addr ) { return( 0 ); } + // getting the time is expensive, so only do it once. + time_t now = time( 0 ); + unsigned int hours = config.auth_hash_ttl; + + if ( ! hours ) { + Warning("No value set for ZM_AUTH_HASH_TTL. Defaulting to 2."); + hours = 2; + } else { + Debug( 1, "AUTH_HASH_TTL is %d, time is %d", hours, now ); + } + while( MYSQL_ROW dbrow = mysql_fetch_row( result ) ) { const char *user = dbrow[0]; const char *pass = dbrow[1]; @@ -179,18 +190,9 @@ User *zmLoadAuthUser( const char *auth, bool use_remote_addr ) { size_t md5len = 16; unsigned char md5sum[md5len]; - time_t now = time( 0 ); - unsigned int hours = config.auth_hash_ttl; - - if ( ! hours ) { - Warning("No value set for ZM_AUTH_HASH_TTL. Defaulting to 2."); - hours = 2; - } else { - Debug( 1, "AUTH_HASH_TTL is %d", hours ); - } - - for ( unsigned int i = 0; i < hours; i++, now -= 3600 ) { - struct tm *now_tm = localtime( &now ); + time_t now_copy = now; + for ( unsigned int i = 0; i < hours; i++, now_copy -= 3600 ) { + struct tm *now_tm = localtime(&now_copy); snprintf( auth_key, sizeof(auth_key), "%s%s%s%s%d%d%d%d", config.auth_hash_secret, @@ -221,11 +223,10 @@ User *zmLoadAuthUser( const char *auth, bool use_remote_addr ) { Debug(1, "Authenticated user '%s'", user->getUsername() ); mysql_free_result( result ); return( user ); - } else { - Debug(1, "No match for %s", auth ); } - } - } + } // end foreach hours + } // end foreach user + Debug(1, "No match for %s", auth ); mysql_free_result( result ); #else // HAVE_DECL_MD5 Error( "You need to build with gnutls or openssl installed to use hash based authentication" ); diff --git a/web/includes/functions.php b/web/includes/functions.php index 2d0b9c681..128b565d6 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -28,13 +28,13 @@ if ( version_compare( phpversion(), '4.3.0', '<') ) { } # We are requiring these because this file is getting included from the api, which hasn't already included them. -require_once( 'logger.php' ); -require_once( 'database.php' ); +require_once('logger.php'); +require_once('database.php'); function userLogin( $username, $password='', $passwordHashed=false ) { global $user, $cookies; - $sql = 'SELECT * FROM Users WHERE Enabled = 1'; + $sql = 'SELECT * FROM Users WHERE Enabled=1'; $sql_values = NULL; if ( ZM_AUTH_TYPE == 'builtin' ) { if ( $passwordHashed ) { @@ -44,7 +44,7 @@ function userLogin( $username, $password='', $passwordHashed=false ) { } $sql_values = array( $username, $password ); } else { - $sql .= ' AND Username = ?'; + $sql .= ' AND Username=?'; $sql_values = array( $username ); } $_SESSION['username'] = $username; @@ -138,26 +138,27 @@ function getAuthUser( $auth ) { $authHash = md5( $authKey ); if ( $auth == $authHash ) { - return( $user ); + return $user; } } // end foreach hour } // end foreach user } // end if using auth hash Error( "Unable to authenticate user from auth hash '$auth'" ); return( false ); -} +} // end getAuthUser($auth) function generateAuthHash( $useRemoteAddr ) { if ( ZM_OPT_USE_AUTH and ZM_AUTH_RELAY == 'hashed' and isset($_SESSION['username']) and $_SESSION['passwordHash'] ) { # regenerate a hash at half the liftetime of a hash, an hour is 3600 so half is 1800 - if ( ( ! isset($_SESSION['AuthHash']) ) or ( $_SESSION['AuthHashGeneratedAt'] < time() - ( ZM_AUTH_HASH_TTL * 1800 ) ) ) { + $time = time(); + if ( ( ! isset($_SESSION['AuthHash']) ) or ( $_SESSION['AuthHashGeneratedAt'] < ( $time - ( ZM_AUTH_HASH_TTL * 1800 ) ) ) ) { # Don't both regenerating Auth Hash if an hour hasn't gone by yet - $time = localtime(); + $local_time = localtime(); $authKey = ''; if ( $useRemoteAddr ) { - $authKey = ZM_AUTH_HASH_SECRET.$_SESSION['username'].$_SESSION['passwordHash'].$_SESSION['remoteAddr'].$time[2].$time[3].$time[4].$time[5]; + $authKey = ZM_AUTH_HASH_SECRET.$_SESSION['username'].$_SESSION['passwordHash'].$_SESSION['remoteAddr'].$local_time[2].$local_time[3].$local_time[4].$local_time[5]; } else { - $authKey = ZM_AUTH_HASH_SECRET.$_SESSION['username'].$_SESSION['passwordHash'].$time[2].$time[3].$time[4].$time[5]; + $authKey = ZM_AUTH_HASH_SECRET.$_SESSION['username'].$_SESSION['passwordHash'].$local_time[2].$local_time[3].$local_time[4].$local_time[5]; } $auth = md5( $authKey ); if ( session_status() == PHP_SESSION_NONE ) { @@ -167,10 +168,10 @@ function generateAuthHash( $useRemoteAddr ) { Warning("Session is not active. AuthHash will not be cached. called from $file:$line"); } $_SESSION['AuthHash'] = $auth; - $_SESSION['AuthHashGeneratedAt'] = time(); + $_SESSION['AuthHashGeneratedAt'] = $time; Logger::Debug("Generated new auth $auth at " . $_SESSION['AuthHashGeneratedAt']. " using $authKey" ); } else { - Logger::Debug( "Using cached auth " . $_SESSION['AuthHash'] ); + Logger::Debug( "Using cached auth " . $_SESSION['AuthHash'] ." beacuse " . $_SESSION['AuthHashGeneratedAt'] . ' < '. $time . ' - ' . ZM_AUTH_HASH_TTL . ' * 1800 = '.( $time - (ZM_AUTH_HASH_TTL * 1800) )); } # end if AuthHash is not cached return $_SESSION['AuthHash']; } else {