2017-06-23 05:58:32 +08:00
//
// ZoneMinder Event Class Implementation, $Date$, $Revision$
// Copyright (C) 2001-2008 Philip Coombes
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
# include <fcntl.h>
# include <sys/socket.h>
# include <arpa/inet.h>
# include <sys/un.h>
# include <sys/uio.h>
# include <sys/ipc.h>
# include <sys/msg.h>
# include <getopt.h>
# include <arpa/inet.h>
# include <glob.h>
2018-04-25 01:16:19 +08:00
# include <cinttypes>
2017-06-23 05:58:32 +08:00
# include "zm.h"
# include "zm_db.h"
# include "zm_time.h"
# include "zm_mpeg.h"
# include "zm_signal.h"
# include "zm_event.h"
# include "zm_eventstream.h"
2017-06-27 04:55:49 +08:00
# include "zm_storage.h"
2017-06-22 22:12:04 +08:00
# include "zm_monitor.h"
2017-06-23 05:58:32 +08:00
# include "zm_sendfile.h"
bool EventStream : : loadInitialEventData ( int monitor_id , time_t event_time ) {
static char sql [ ZM_SQL_SML_BUFSIZ ] ;
2018-04-13 01:14:00 +08:00
snprintf ( sql , sizeof ( sql ) , " SELECT Id FROM Events WHERE MonitorId = %d AND unix_timestamp(EndTime) > %ld ORDER BY Id ASC LIMIT 1 " , monitor_id , event_time ) ;
2017-06-23 05:58:32 +08:00
2018-04-13 01:14:00 +08:00
if ( mysql_query ( & dbconn , sql ) ) {
Error ( " Can't run query: %s " , mysql_error ( & dbconn ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
2017-06-23 05:58:32 +08:00
}
2018-04-13 01:14:00 +08:00
MYSQL_RES * result = mysql_store_result ( & dbconn ) ;
2017-06-23 05:58:32 +08:00
if ( ! result ) {
2018-04-13 01:14:00 +08:00
Error ( " Can't use query result: %s " , mysql_error ( & dbconn ) ) ;
2019-01-17 00:20:10 +08:00
exit ( mysql_errno ( & dbconn ) ) ;
2017-06-23 05:58:32 +08:00
}
2018-04-13 01:14:00 +08:00
MYSQL_ROW dbrow = mysql_fetch_row ( result ) ;
2017-06-23 05:58:32 +08:00
2018-04-13 01:14:00 +08:00
if ( mysql_errno ( & dbconn ) ) {
Error ( " Can't fetch row: %s " , mysql_error ( & dbconn ) ) ;
2019-01-17 00:20:10 +08:00
exit ( mysql_errno ( & dbconn ) ) ;
2017-06-23 05:58:32 +08:00
}
2018-04-18 01:51:20 +08:00
uint64_t init_event_id = atoll ( dbrow [ 0 ] ) ;
2017-06-23 05:58:32 +08:00
2018-04-13 01:14:00 +08:00
mysql_free_result ( result ) ;
2017-06-23 05:58:32 +08:00
2018-04-13 01:14:00 +08:00
loadEventData ( init_event_id ) ;
2017-06-23 05:58:32 +08:00
if ( event_time ) {
curr_stream_time = event_time ;
2018-11-20 05:45:56 +08:00
curr_frame_id = 1 ; // curr_frame_id is 1-based
2017-06-23 05:58:32 +08:00
if ( event_time > = event_data - > start_time ) {
2018-11-20 05:45:56 +08:00
Debug ( 2 , " event time is after event start " ) ;
2017-06-23 05:58:32 +08:00
for ( unsigned int i = 0 ; i < event_data - > frame_count ; i + + ) {
//Info( "eft %d > et %d", event_data->frames[i].timestamp, event_time );
if ( event_data - > frames [ i ] . timestamp > = event_time ) {
curr_frame_id = i + 1 ;
2019-01-17 00:20:10 +08:00
Debug ( 3 , " Set curr_stream_time:%.2f, curr_frame_id:%d " , curr_stream_time , curr_frame_id ) ;
2017-06-23 05:58:32 +08:00
break ;
}
}
2019-01-17 00:20:10 +08:00
Debug ( 3 , " Skipping %ld frames " , event_data - > frame_count ) ;
2017-06-23 05:58:32 +08:00
}
}
2018-04-13 04:40:11 +08:00
return true ;
2017-06-23 05:58:32 +08:00
}
2018-04-18 01:51:20 +08:00
bool EventStream : : loadInitialEventData ( uint64_t init_event_id , unsigned int init_frame_id ) {
2018-03-03 10:24:39 +08:00
loadEventData ( init_event_id ) ;
2017-06-23 05:58:32 +08:00
if ( init_frame_id ) {
2018-03-03 10:24:39 +08:00
if ( init_frame_id > = event_data - > frame_count ) {
2019-01-17 00:20:10 +08:00
Error ( " Invalid frame id specified. %d > %d " , init_frame_id , event_data - > frame_count ) ;
2018-03-03 10:24:39 +08:00
curr_stream_time = event_data - > start_time ;
} else {
curr_stream_time = event_data - > frames [ init_frame_id - 1 ] . timestamp ;
curr_frame_id = init_frame_id ;
}
2017-06-23 05:58:32 +08:00
} else {
curr_stream_time = event_data - > start_time ;
}
2018-03-03 10:24:39 +08:00
return true ;
2017-06-23 05:58:32 +08:00
}
2018-04-18 01:51:20 +08:00
bool EventStream : : loadEventData ( uint64_t event_id ) {
2017-06-23 05:58:32 +08:00
static char sql [ ZM_SQL_MED_BUFSIZ ] ;
2018-11-07 00:45:34 +08:00
snprintf ( sql , sizeof ( sql ) ,
" SELECT MonitorId, StorageId, Frames, unix_timestamp( StartTime ) AS StartTimestamp, "
" (SELECT max(Delta)-min(Delta) FROM Frames WHERE EventId=Events.Id) AS Duration, "
" DefaultVideo, Scheme, SaveJPEGs FROM Events WHERE Id = % " PRIu64 , event_id ) ;
2017-06-23 05:58:32 +08:00
2018-03-03 10:24:39 +08:00
if ( mysql_query ( & dbconn , sql ) ) {
Error ( " Can't run query: %s " , mysql_error ( & dbconn ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
2017-06-23 05:58:32 +08:00
}
2018-03-03 10:24:39 +08:00
MYSQL_RES * result = mysql_store_result ( & dbconn ) ;
2017-06-23 05:58:32 +08:00
if ( ! result ) {
2018-03-03 10:24:39 +08:00
Error ( " Can't use query result: %s " , mysql_error ( & dbconn ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
2017-06-23 05:58:32 +08:00
}
2018-03-03 10:24:39 +08:00
if ( ! mysql_num_rows ( result ) ) {
Fatal ( " Unable to load event %d, not found in DB " , event_id ) ;
2017-06-23 05:58:32 +08:00
}
2018-03-03 10:24:39 +08:00
MYSQL_ROW dbrow = mysql_fetch_row ( result ) ;
2017-06-23 05:58:32 +08:00
2018-03-03 10:24:39 +08:00
if ( mysql_errno ( & dbconn ) ) {
Error ( " Can't fetch row: %s " , mysql_error ( & dbconn ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
2017-06-23 05:58:32 +08:00
}
delete event_data ;
event_data = new EventData ;
event_data - > event_id = event_id ;
2017-06-26 23:20:10 +08:00
2019-01-17 00:20:10 +08:00
event_data - > monitor_id = atoi ( dbrow [ 0 ] ) ;
event_data - > storage_id = dbrow [ 1 ] ? atoi ( dbrow [ 1 ] ) : 0 ;
2017-06-27 04:55:49 +08:00
event_data - > frame_count = dbrow [ 2 ] = = NULL ? 0 : atoi ( dbrow [ 2 ] ) ;
2017-06-23 05:58:32 +08:00
event_data - > start_time = atoi ( dbrow [ 3 ] ) ;
2019-01-16 00:25:22 +08:00
event_data - > duration = dbrow [ 4 ] ? atof ( dbrow [ 4 ] ) : 0.0 ;
2019-01-17 00:20:10 +08:00
strncpy ( event_data - > video_file , dbrow [ 5 ] , sizeof ( event_data - > video_file ) - 1 ) ;
2017-12-20 00:01:03 +08:00
std : : string scheme_str = std : : string ( dbrow [ 6 ] ) ;
if ( scheme_str = = " Deep " ) {
event_data - > scheme = Storage : : DEEP ;
} else if ( scheme_str = = " Medium " ) {
event_data - > scheme = Storage : : MEDIUM ;
} else {
event_data - > scheme = Storage : : SHALLOW ;
}
2018-11-07 00:45:34 +08:00
event_data - > SaveJPEGs = dbrow [ 7 ] = = NULL ? 0 : atoi ( dbrow [ 7 ] ) ;
2017-06-27 04:55:49 +08:00
mysql_free_result ( result ) ;
2017-06-23 05:58:32 +08:00
2018-04-04 03:23:38 +08:00
Storage * storage = new Storage ( event_data - > storage_id ) ;
2017-06-23 05:58:32 +08:00
const char * storage_path = storage - > Path ( ) ;
2017-12-20 00:01:03 +08:00
if ( event_data - > scheme = = Storage : : DEEP ) {
2018-04-04 03:23:38 +08:00
struct tm * event_time = localtime ( & event_data - > start_time ) ;
2017-06-26 23:20:10 +08:00
2017-06-23 05:58:32 +08:00
if ( storage_path [ 0 ] = = ' / ' )
2019-01-17 00:20:10 +08:00
snprintf ( event_data - > path , sizeof ( event_data - > path ) ,
" %s/%ld/%02d/%02d/%02d/%02d/%02d/%02d " ,
2018-09-12 01:20:41 +08:00
storage_path , event_data - > monitor_id ,
event_time - > tm_year - 100 , event_time - > tm_mon + 1 , event_time - > tm_mday ,
2019-01-17 00:20:10 +08:00
event_time - > tm_hour , event_time - > tm_min , event_time - > tm_sec ) ;
2017-06-23 05:58:32 +08:00
else
2019-01-17 00:20:10 +08:00
snprintf ( event_data - > path , sizeof ( event_data - > path ) ,
" %s/%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d " ,
2018-09-12 01:20:41 +08:00
staticConfig . PATH_WEB . c_str ( ) , storage_path , event_data - > monitor_id ,
event_time - > tm_year - 100 , event_time - > tm_mon + 1 , event_time - > tm_mday ,
2019-01-17 00:20:10 +08:00
event_time - > tm_hour , event_time - > tm_min , event_time - > tm_sec ) ;
2017-12-20 00:01:03 +08:00
} else if ( event_data - > scheme = = Storage : : MEDIUM ) {
2019-01-17 00:20:10 +08:00
struct tm * event_time = localtime ( & event_data - > start_time ) ;
2017-12-20 00:01:03 +08:00
if ( storage_path [ 0 ] = = ' / ' )
2019-01-17 00:20:10 +08:00
snprintf ( event_data - > path , sizeof ( event_data - > path ) ,
" %s/%ld/%04d-%02d-%02d/% " PRIu64 ,
2018-09-12 01:20:41 +08:00
storage_path , event_data - > monitor_id ,
event_time - > tm_year + 1900 , event_time - > tm_mon + 1 , event_time - > tm_mday ,
2019-01-17 00:20:10 +08:00
event_data - > event_id ) ;
2017-12-20 00:01:03 +08:00
else
2019-01-17 00:20:10 +08:00
snprintf ( event_data - > path , sizeof ( event_data - > path ) ,
" %s/%s/%ld/%04d-%02d-%02d/% " PRIu64 ,
2018-09-12 01:20:41 +08:00
staticConfig . PATH_WEB . c_str ( ) , storage_path , event_data - > monitor_id ,
event_time - > tm_year + 1900 , event_time - > tm_mon + 1 , event_time - > tm_mday ,
2019-01-17 00:20:10 +08:00
event_data - > event_id ) ;
2017-06-26 23:20:10 +08:00
2017-06-23 05:58:32 +08:00
} else {
if ( storage_path [ 0 ] = = ' / ' )
2019-01-17 00:20:10 +08:00
snprintf ( event_data - > path , sizeof ( event_data - > path ) , " %s/%ld/% " PRIu64 ,
storage_path , event_data - > monitor_id , event_data - > event_id ) ;
2017-06-23 05:58:32 +08:00
else
2019-01-17 00:20:10 +08:00
snprintf ( event_data - > path , sizeof ( event_data - > path ) , " %s/%s/%ld/% " PRIu64 ,
staticConfig . PATH_WEB . c_str ( ) , storage_path , event_data - > monitor_id ,
event_data - > event_id ) ;
2017-06-23 05:58:32 +08:00
}
2017-07-07 05:45:23 +08:00
delete storage ; storage = NULL ;
2017-06-23 05:58:32 +08:00
2019-01-17 00:20:10 +08:00
updateFrameRate ( ( double ) event_data - > frame_count / event_data - > duration ) ;
2018-09-12 01:20:41 +08:00
Debug ( 3 , " fps set by frame_count(%d)/duration(%f) " , event_data - > frame_count , event_data - > duration ) ;
2017-06-23 05:58:32 +08:00
2018-09-12 01:20:41 +08:00
snprintf ( sql , sizeof ( sql ) , " SELECT FrameId, unix_timestamp(`TimeStamp`), Delta FROM Frames WHERE EventId = % " PRIu64 " ORDER BY FrameId ASC " , event_id ) ;
2018-04-04 03:23:38 +08:00
if ( mysql_query ( & dbconn , sql ) ) {
Error ( " Can't run query: %s " , mysql_error ( & dbconn ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
2017-06-23 05:58:32 +08:00
}
2018-04-04 03:23:38 +08:00
result = mysql_store_result ( & dbconn ) ;
2017-06-23 05:58:32 +08:00
if ( ! result ) {
2018-04-04 03:23:38 +08:00
Error ( " Can't use query result: %s " , mysql_error ( & dbconn ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
2017-06-23 05:58:32 +08:00
}
2018-04-04 03:23:38 +08:00
event_data - > n_frames = mysql_num_rows ( result ) ;
2017-06-23 05:58:32 +08:00
event_data - > frames = new FrameData [ event_data - > frame_count ] ;
2017-11-17 20:52:26 +08:00
int last_id = 0 ;
2018-09-12 01:20:41 +08:00
double last_timestamp = event_data - > start_time ;
2017-11-17 20:52:26 +08:00
double last_delta = 0.0 ;
2018-09-12 01:20:41 +08:00
2017-06-23 05:58:32 +08:00
while ( ( dbrow = mysql_fetch_row ( result ) ) ) {
2017-11-17 20:52:26 +08:00
int id = atoi ( dbrow [ 0 ] ) ;
2018-09-12 01:20:41 +08:00
//timestamp = atof(dbrow[1]);
2017-11-17 20:52:26 +08:00
double delta = atof ( dbrow [ 2 ] ) ;
2017-06-23 05:58:32 +08:00
int id_diff = id - last_id ;
2019-03-07 03:50:36 +08:00
double frame_delta = id_diff ? ( delta - last_delta ) / id_diff : ( delta - last_delta ) ;
2018-09-12 01:20:41 +08:00
// Fill in data between bulk frames
2017-06-23 05:58:32 +08:00
if ( id_diff > 1 ) {
for ( int i = last_id + 1 ; i < id ; i + + ) {
2018-09-12 01:20:41 +08:00
// Delta is the time since last frame, no since beginning of Event
2017-06-23 05:58:32 +08:00
event_data - > frames [ i - 1 ] . delta = frame_delta ;
2018-09-12 01:20:41 +08:00
event_data - > frames [ i - 1 ] . timestamp = last_timestamp + ( ( i - last_id ) * frame_delta ) ;
event_data - > frames [ i - 1 ] . offset = event_data - > frames [ i - 1 ] . timestamp - event_data - > start_time ;
2017-06-23 05:58:32 +08:00
event_data - > frames [ i - 1 ] . in_db = false ;
2018-11-20 05:45:56 +08:00
Debug ( 3 , " Frame %d timestamp:(%f), offset(%f) delta(%f), in_db(%d) " ,
2018-09-12 01:20:41 +08:00
i ,
event_data - > frames [ i - 1 ] . timestamp ,
event_data - > frames [ i - 1 ] . offset ,
event_data - > frames [ i - 1 ] . delta ,
event_data - > frames [ i - 1 ] . in_db
) ;
2017-06-23 05:58:32 +08:00
}
}
2018-09-12 01:20:41 +08:00
event_data - > frames [ id - 1 ] . timestamp = event_data - > start_time + delta ;
event_data - > frames [ id - 1 ] . offset = delta ;
event_data - > frames [ id - 1 ] . delta = frame_delta ;
2017-06-23 05:58:32 +08:00
event_data - > frames [ id - 1 ] . in_db = true ;
last_id = id ;
last_delta = delta ;
2018-09-12 01:20:41 +08:00
last_timestamp = event_data - > frames [ id - 1 ] . timestamp ;
Debug ( 4 , " Frame %d timestamp:(%f), offset(%f) delta(%f), in_db(%d) " ,
id ,
event_data - > frames [ id - 1 ] . timestamp ,
event_data - > frames [ id - 1 ] . offset ,
event_data - > frames [ id - 1 ] . delta ,
event_data - > frames [ id - 1 ] . in_db
) ;
2017-06-23 05:58:32 +08:00
}
if ( mysql_errno ( & dbconn ) ) {
2019-03-07 03:50:36 +08:00
Error ( " Can't fetch row: %s " , mysql_error ( & dbconn ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
2017-06-23 05:58:32 +08:00
}
2018-04-13 04:40:11 +08:00
mysql_free_result ( result ) ;
2017-06-23 05:58:32 +08:00
//for ( int i = 0; i < 250; i++ )
//{
//Info( "%d -> %d @ %f (%d)", i+1, event_data->frames[i].timestamp, event_data->frames[i].delta, event_data->frames[i].in_db );
//}
if ( event_data - > video_file [ 0 ] ) {
2017-06-27 04:55:49 +08:00
char filepath [ PATH_MAX ] ;
2018-04-04 03:23:38 +08:00
snprintf ( filepath , sizeof ( filepath ) , " %s/%s " , event_data - > path , event_data - > video_file ) ;
2018-11-07 00:45:34 +08:00
Debug ( 1 , " Loading video file from %s " , filepath ) ;
2017-06-23 05:58:32 +08:00
ffmpeg_input = new FFmpeg_Input ( ) ;
2017-08-24 03:05:44 +08:00
if ( 0 > ffmpeg_input - > Open ( filepath ) ) {
2018-04-04 03:23:38 +08:00
Warning ( " Unable to open ffmpeg_input %s/%s " , event_data - > path , event_data - > video_file ) ;
2017-06-23 05:58:32 +08:00
delete ffmpeg_input ;
ffmpeg_input = NULL ;
}
}
if ( forceEventChange | | mode = = MODE_ALL_GAPLESS ) {
if ( replay_rate > 0 )
curr_stream_time = event_data - > frames [ 0 ] . timestamp ;
else
curr_stream_time = event_data - > frames [ event_data - > frame_count - 1 ] . timestamp ;
}
2018-04-18 01:51:20 +08:00
Debug ( 2 , " Event:% " PRIu64 " , Frames:%ld, Duration: %.2f " , event_data - > event_id , event_data - > frame_count , event_data - > duration ) ;
2017-06-23 05:58:32 +08:00
2018-04-04 03:23:38 +08:00
return true ;
2017-08-24 03:05:44 +08:00
} // bool EventStream::loadEventData( int event_id )
2017-06-23 05:58:32 +08:00
2018-04-04 03:23:38 +08:00
void EventStream : : processCommand ( const CmdMsg * msg ) {
Debug ( 2 , " Got message, type %d, msg %d " , msg - > msg_type , msg - > msg_data [ 0 ] ) ;
2017-06-23 05:58:32 +08:00
// Check for incoming command
switch ( ( MsgCommand ) msg - > msg_data [ 0 ] ) {
case CMD_PAUSE :
2019-01-17 00:20:10 +08:00
Debug ( 1 , " Got PAUSE command " ) ;
2017-06-23 05:58:32 +08:00
// Set paused flag
paused = true ;
replay_rate = ZM_RATE_BASE ;
2019-01-17 00:20:10 +08:00
last_frame_sent = TV_2_FLOAT ( now ) ;
2017-06-23 05:58:32 +08:00
break ;
case CMD_PLAY :
2019-01-17 00:20:10 +08:00
Debug ( 1 , " Got PLAY command " ) ;
2017-06-22 22:46:32 +08:00
if ( paused ) {
2017-06-23 05:58:32 +08:00
paused = false ;
}
// If we are in single event mode and at the last frame, replay the current event
2018-09-29 00:31:53 +08:00
if ( ( mode = = MODE_SINGLE | | mode = = MODE_NONE ) & & ( ( unsigned int ) curr_frame_id = = event_data - > frame_count ) ) {
Debug ( 1 , " Was in single or no replay mode, and at last frame, so jumping to 1st frame " ) ;
2017-06-23 05:58:32 +08:00
curr_frame_id = 1 ;
2017-06-22 22:46:32 +08:00
} else {
Debug ( 1 , " mode is %s, current frame is %d, frame count is %d " , ( mode = = MODE_SINGLE ? " single " : " not single " ) , curr_frame_id , event_data - > frame_count ) ;
}
2017-06-23 05:58:32 +08:00
replay_rate = ZM_RATE_BASE ;
break ;
case CMD_VARPLAY :
2019-01-17 00:20:10 +08:00
Debug ( 1 , " Got VARPLAY command " ) ;
2017-10-25 07:10:16 +08:00
if ( paused ) {
2017-06-23 05:58:32 +08:00
paused = false ;
}
replay_rate = ntohs ( ( ( unsigned char ) msg - > msg_data [ 2 ] < < 8 ) | ( unsigned char ) msg - > msg_data [ 1 ] ) - 32768 ;
break ;
case CMD_STOP :
2019-01-17 00:20:10 +08:00
Debug ( 1 , " Got STOP command " ) ;
2017-06-23 05:58:32 +08:00
paused = false ;
break ;
case CMD_FASTFWD :
2019-01-17 00:20:10 +08:00
Debug ( 1 , " Got FAST FWD command " ) ;
2017-06-23 05:58:32 +08:00
if ( paused ) {
paused = false ;
}
// Set play rate
switch ( replay_rate ) {
case 2 * ZM_RATE_BASE :
replay_rate = 5 * ZM_RATE_BASE ;
break ;
case 5 * ZM_RATE_BASE :
replay_rate = 10 * ZM_RATE_BASE ;
break ;
case 10 * ZM_RATE_BASE :
replay_rate = 25 * ZM_RATE_BASE ;
break ;
case 25 * ZM_RATE_BASE :
case 50 * ZM_RATE_BASE :
replay_rate = 50 * ZM_RATE_BASE ;
break ;
default :
2019-02-27 00:41:02 +08:00
Debug ( 1 , " Defaulting replay_rate to 2*ZM_RATE_BASE because it is %d " , replay_rate ) ;
2017-06-23 05:58:32 +08:00
replay_rate = 2 * ZM_RATE_BASE ;
break ;
}
break ;
case CMD_SLOWFWD :
2019-01-17 00:20:10 +08:00
Debug ( 1 , " Got SLOW FWD command " ) ;
2017-06-23 05:58:32 +08:00
paused = true ;
replay_rate = ZM_RATE_BASE ;
step = 1 ;
break ;
case CMD_SLOWREV :
2019-01-17 00:20:10 +08:00
Debug ( 1 , " Got SLOW REV command " ) ;
2017-06-23 05:58:32 +08:00
paused = true ;
replay_rate = ZM_RATE_BASE ;
step = - 1 ;
break ;
case CMD_FASTREV :
2019-01-17 00:20:10 +08:00
Debug ( 1 , " Got FAST REV command " ) ;
2018-04-13 04:40:11 +08:00
paused = false ;
2017-06-23 05:58:32 +08:00
// Set play rate
switch ( replay_rate ) {
case - 2 * ZM_RATE_BASE :
replay_rate = - 5 * ZM_RATE_BASE ;
break ;
case - 5 * ZM_RATE_BASE :
replay_rate = - 10 * ZM_RATE_BASE ;
break ;
case - 10 * ZM_RATE_BASE :
replay_rate = - 25 * ZM_RATE_BASE ;
break ;
case - 25 * ZM_RATE_BASE :
case - 50 * ZM_RATE_BASE :
replay_rate = - 50 * ZM_RATE_BASE ;
break ;
default :
replay_rate = - 2 * ZM_RATE_BASE ;
break ;
}
break ;
case CMD_ZOOMIN :
x = ( ( unsigned char ) msg - > msg_data [ 1 ] < < 8 ) | ( unsigned char ) msg - > msg_data [ 2 ] ;
y = ( ( unsigned char ) msg - > msg_data [ 3 ] < < 8 ) | ( unsigned char ) msg - > msg_data [ 4 ] ;
2019-01-17 00:20:10 +08:00
Debug ( 1 , " Got ZOOM IN command, to %d,%d " , x , y ) ;
2017-06-23 05:58:32 +08:00
switch ( zoom ) {
case 100 :
zoom = 150 ;
break ;
case 150 :
zoom = 200 ;
break ;
case 200 :
zoom = 300 ;
break ;
case 300 :
zoom = 400 ;
break ;
case 400 :
default :
zoom = 500 ;
break ;
}
2017-06-22 22:46:32 +08:00
send_frame = true ;
2017-06-23 05:58:32 +08:00
break ;
case CMD_ZOOMOUT :
2019-01-17 00:20:10 +08:00
Debug ( 1 , " Got ZOOM OUT command " ) ;
2017-06-23 05:58:32 +08:00
switch ( zoom ) {
case 500 :
zoom = 400 ;
break ;
case 400 :
zoom = 300 ;
break ;
case 300 :
zoom = 200 ;
break ;
case 200 :
zoom = 150 ;
break ;
case 150 :
default :
zoom = 100 ;
break ;
}
2017-06-22 22:46:32 +08:00
send_frame = true ;
2017-06-23 05:58:32 +08:00
break ;
case CMD_PAN :
x = ( ( unsigned char ) msg - > msg_data [ 1 ] < < 8 ) | ( unsigned char ) msg - > msg_data [ 2 ] ;
y = ( ( unsigned char ) msg - > msg_data [ 3 ] < < 8 ) | ( unsigned char ) msg - > msg_data [ 4 ] ;
2019-01-17 00:20:10 +08:00
Debug ( 1 , " Got PAN command, to %d,%d " , x , y ) ;
2017-06-23 05:58:32 +08:00
break ;
case CMD_SCALE :
scale = ( ( unsigned char ) msg - > msg_data [ 1 ] < < 8 ) | ( unsigned char ) msg - > msg_data [ 2 ] ;
2019-01-17 00:20:10 +08:00
Debug ( 1 , " Got SCALE command, to %d " , scale ) ;
2017-06-23 05:58:32 +08:00
break ;
case CMD_PREV :
2019-01-17 00:20:10 +08:00
Debug ( 1 , " Got PREV command " ) ;
2017-06-23 05:58:32 +08:00
if ( replay_rate > = 0 )
curr_frame_id = 0 ;
else
curr_frame_id = event_data - > frame_count + 1 ;
paused = false ;
forceEventChange = true ;
break ;
case CMD_NEXT :
2019-01-17 00:20:10 +08:00
Debug ( 1 , " Got NEXT command " ) ;
2017-06-23 05:58:32 +08:00
if ( replay_rate > = 0 )
curr_frame_id = event_data - > frame_count + 1 ;
else
curr_frame_id = 0 ;
paused = false ;
forceEventChange = true ;
break ;
case CMD_SEEK :
{
int offset = ( ( unsigned char ) msg - > msg_data [ 1 ] < < 24 ) | ( ( unsigned char ) msg - > msg_data [ 2 ] < < 16 ) | ( ( unsigned char ) msg - > msg_data [ 3 ] < < 8 ) | ( unsigned char ) msg - > msg_data [ 4 ] ;
curr_frame_id = ( int ) ( event_data - > frame_count * offset / event_data - > duration ) ;
2019-01-17 00:20:10 +08:00
Debug ( 1 , " Got SEEK command, to %d (new cfid: %d) " , offset , curr_frame_id ) ;
2017-06-22 22:46:32 +08:00
send_frame = true ;
2017-06-23 05:58:32 +08:00
break ;
}
case CMD_QUERY :
2019-01-17 00:20:10 +08:00
Debug ( 1 , " Got QUERY command, sending STATUS " ) ;
2017-06-23 05:58:32 +08:00
break ;
case CMD_QUIT :
2018-04-13 04:40:11 +08:00
Info ( " User initiated exit - CMD_QUIT " ) ;
2017-06-23 05:58:32 +08:00
break ;
default :
// Do nothing, for now
2018-04-13 04:40:11 +08:00
break ;
2017-06-23 05:58:32 +08:00
}
struct {
2018-04-18 01:51:20 +08:00
uint64_t event_id ;
2017-06-23 05:58:32 +08:00
int progress ;
int rate ;
int zoom ;
bool paused ;
} status_data ;
2018-04-13 04:40:11 +08:00
status_data . event_id = event_data - > event_id ;
2017-06-23 05:58:32 +08:00
status_data . progress = ( int ) event_data - > frames [ curr_frame_id - 1 ] . offset ;
status_data . rate = replay_rate ;
status_data . zoom = zoom ;
status_data . paused = paused ;
2019-01-17 00:20:10 +08:00
Debug ( 2 , " Event:% " PRIu64 " , Paused:%d, progress:%d Rate:%d, Zoom:%d " ,
2018-04-13 04:40:11 +08:00
status_data . event_id ,
2017-06-22 22:12:04 +08:00
status_data . paused ,
status_data . progress ,
status_data . rate ,
status_data . zoom
) ;
2017-06-23 05:58:32 +08:00
DataMsg status_msg ;
status_msg . msg_type = MSG_DATA_EVENT ;
2018-04-13 04:40:11 +08:00
memcpy ( & status_msg . msg_data , & status_data , sizeof ( status_data ) ) ;
2019-02-27 00:41:02 +08:00
Debug ( 1 , " Size of msg %d " , sizeof ( status_data ) ) ;
2018-04-13 04:40:11 +08:00
if ( sendto ( sd , & status_msg , sizeof ( status_msg ) , MSG_DONTWAIT , ( sockaddr * ) & rem_addr , sizeof ( rem_addr ) ) < 0 ) {
2017-06-23 05:58:32 +08:00
//if ( errno != EAGAIN )
{
2018-04-13 04:40:11 +08:00
Error ( " Can't sendto on sd %d: %s " , sd , strerror ( errno ) ) ;
2018-12-05 07:23:08 +08:00
//exit(-1);
2017-06-23 05:58:32 +08:00
}
}
// quit after sending a status, if this was a quit request
2018-04-04 03:23:38 +08:00
if ( ( MsgCommand ) msg - > msg_data [ 0 ] = = CMD_QUIT )
2017-06-23 05:58:32 +08:00
exit ( 0 ) ;
2018-04-13 04:40:11 +08:00
updateFrameRate ( ( double ) event_data - > frame_count / event_data - > duration ) ;
2019-02-27 00:41:02 +08:00
} // void EventStream::processCommand(const CmdMsg *msg)
2017-06-23 05:58:32 +08:00
void EventStream : : checkEventLoaded ( ) {
static char sql [ ZM_SQL_SML_BUFSIZ ] ;
if ( curr_frame_id < = 0 ) {
2019-01-17 00:20:10 +08:00
snprintf ( sql , sizeof ( sql ) , " SELECT Id FROM Events WHERE MonitorId = %ld AND Id < % " PRIu64 " ORDER BY Id DESC LIMIT 1 " , event_data - > monitor_id , event_data - > event_id ) ;
2017-06-23 05:58:32 +08:00
} else if ( ( unsigned int ) curr_frame_id > event_data - > frame_count ) {
2019-01-17 00:20:10 +08:00
snprintf ( sql , sizeof ( sql ) , " SELECT Id FROM Events WHERE MonitorId = %ld AND Id > % " PRIu64 " ORDER BY Id ASC LIMIT 1 " , event_data - > monitor_id , event_data - > event_id ) ;
} else {
// No event change required
2019-02-27 00:41:02 +08:00
//Debug(3, "No event change required");
2019-01-17 00:20:10 +08:00
return ;
2017-06-23 05:58:32 +08:00
}
2019-01-17 00:20:10 +08:00
// Event change required.
if ( forceEventChange | | ( mode ! = MODE_SINGLE & & mode ! = MODE_NONE ) ) {
if ( mysql_query ( & dbconn , sql ) ) {
Error ( " Can't run query: %s " , mysql_error ( & dbconn ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
}
2017-06-23 05:58:32 +08:00
2019-01-17 00:20:10 +08:00
MYSQL_RES * result = mysql_store_result ( & dbconn ) ;
if ( ! result ) {
Error ( " Can't use query result: %s " , mysql_error ( & dbconn ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
}
MYSQL_ROW dbrow = mysql_fetch_row ( result ) ;
2017-06-23 05:58:32 +08:00
2019-01-17 00:20:10 +08:00
if ( mysql_errno ( & dbconn ) ) {
Error ( " Can't fetch row: %s " , mysql_error ( & dbconn ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
}
2017-06-23 05:58:32 +08:00
2019-01-17 00:20:10 +08:00
if ( dbrow ) {
uint64_t event_id = atoll ( dbrow [ 0 ] ) ;
Debug ( 1 , " Loading new event % " PRIu64 , event_id ) ;
2017-06-23 05:58:32 +08:00
2019-01-17 00:20:10 +08:00
loadEventData ( event_id ) ;
2017-06-23 05:58:32 +08:00
2019-01-17 00:20:10 +08:00
Debug ( 2 , " Current frame id = %d " , curr_frame_id ) ;
if ( replay_rate < 0 ) //rewind
curr_frame_id = event_data - > frame_count ;
else
curr_frame_id = 1 ;
Debug ( 2 , " New frame id = %d " , curr_frame_id ) ;
2017-06-23 05:58:32 +08:00
} else {
if ( curr_frame_id < = 0 )
curr_frame_id = 1 ;
else
curr_frame_id = event_data - > frame_count ;
paused = true ;
2019-01-17 00:20:10 +08:00
} // end if found a new event or not
mysql_free_result ( result ) ;
forceEventChange = false ;
} else {
if ( curr_frame_id < = 0 )
curr_frame_id = 1 ;
else
curr_frame_id = event_data - > frame_count ;
paused = true ;
2017-06-23 05:58:32 +08:00
}
2019-01-17 00:20:10 +08:00
} // void EventStream::checkEventLoaded()
2017-06-23 05:58:32 +08:00
Image * EventStream : : getImage ( ) {
static char filepath [ PATH_MAX ] ;
2018-04-04 03:23:38 +08:00
Debug ( 2 , " EventStream::getImage path(%s) frame(%d) " , event_data - > path , curr_frame_id ) ;
2019-01-17 00:20:10 +08:00
snprintf ( filepath , sizeof ( filepath ) , staticConfig . capture_file_format , event_data - > path , curr_frame_id ) ;
2018-04-04 03:23:38 +08:00
Debug ( 2 , " EventStream::getImage path(%s) " , filepath , curr_frame_id ) ;
Image * image = new Image ( filepath ) ;
2017-06-23 05:58:32 +08:00
return image ;
}
bool EventStream : : sendFrame ( int delta_us ) {
2019-01-17 00:20:10 +08:00
Debug ( 2 , " Sending frame %d " , curr_frame_id ) ;
2017-06-23 05:58:32 +08:00
static char filepath [ PATH_MAX ] ;
static struct stat filestat ;
FILE * fdj = NULL ;
// This needs to be abstracted. If we are saving jpgs, then load the capture file. If we are only saving analysis frames, then send that.
2017-11-10 04:03:23 +08:00
// // This is also wrong, need to have this info stored in the event! FIXME
2018-11-07 00:45:34 +08:00
if ( event_data - > SaveJPEGs & 1 ) {
2019-01-17 00:20:10 +08:00
snprintf ( filepath , sizeof ( filepath ) , staticConfig . capture_file_format , event_data - > path , curr_frame_id ) ;
2018-11-07 00:45:34 +08:00
} else if ( event_data - > SaveJPEGs & 2 ) {
2019-01-17 00:20:10 +08:00
snprintf ( filepath , sizeof ( filepath ) , staticConfig . analyse_file_format , event_data - > path , curr_frame_id ) ;
2019-01-17 03:39:56 +08:00
if ( stat ( filepath , & filestat ) < 0 ) {
2017-08-24 03:05:44 +08:00
Debug ( 1 , " analyze file %s not found will try to stream from other " , filepath ) ;
2019-01-17 00:20:10 +08:00
snprintf ( filepath , sizeof ( filepath ) , staticConfig . capture_file_format , event_data - > path , curr_frame_id ) ;
2019-01-17 03:39:56 +08:00
if ( stat ( filepath , & filestat ) < 0 ) {
2018-11-07 00:45:34 +08:00
Debug ( 1 , " capture file %s not found either " , filepath ) ;
filepath [ 0 ] = 0 ;
}
2017-06-23 05:58:32 +08:00
}
2019-01-17 00:20:10 +08:00
} else if ( ! ffmpeg_input ) {
Fatal ( " JPEGS not saved. zms is not capable of streaming jpegs from mp4 yet " ) ;
2017-06-23 05:58:32 +08:00
return false ;
}
# if HAVE_LIBAVCODEC
if ( type = = STREAM_MPEG ) {
2018-04-04 03:23:38 +08:00
Debug ( 2 , " Streaming MPEG " ) ;
2019-01-17 00:20:10 +08:00
Image image ( filepath ) ;
2017-06-23 05:58:32 +08:00
2018-04-04 03:23:38 +08:00
Image * send_image = prepareImage ( & image ) ;
2017-06-23 05:58:32 +08:00
if ( ! vid_stream ) {
2018-04-04 03:23:38 +08:00
vid_stream = new VideoStream ( " pipe: " , format , bitrate , effective_fps , send_image - > Colours ( ) , send_image - > SubpixelOrder ( ) , send_image - > Width ( ) , send_image - > Height ( ) ) ;
fprintf ( stdout , " Content-type: %s \r \n \r \n " , vid_stream - > MimeType ( ) ) ;
2017-06-23 05:58:32 +08:00
vid_stream - > OpenStream ( ) ;
}
2019-01-17 00:20:10 +08:00
/* double pts = */ vid_stream - > EncodeFrame ( send_image - > Buffer ( ) , send_image - > Size ( ) , config . mpeg_timed_frames , delta_us * 1000 ) ;
2017-06-23 05:58:32 +08:00
} else
# endif // HAVE_LIBAVCODEC
{
static unsigned char temp_img_buffer [ ZM_MAX_IMAGE_SIZE ] ;
int img_buffer_size = 0 ;
uint8_t * img_buffer = temp_img_buffer ;
2017-08-24 03:05:44 +08:00
bool send_raw = ( ( scale > = ZM_SCALE_BASE ) & & ( zoom = = ZM_SCALE_BASE ) ) & & filepath [ 0 ] ;
2017-06-23 05:58:32 +08:00
2019-01-17 00:20:10 +08:00
fprintf ( stdout , " --ZoneMinderFrame \r \n " ) ;
2017-06-23 05:58:32 +08:00
2018-04-04 03:23:38 +08:00
if ( ( type ! = STREAM_JPEG ) | | ( ! filepath [ 0 ] ) )
2017-06-23 05:58:32 +08:00
send_raw = false ;
if ( send_raw ) {
2018-04-13 04:40:11 +08:00
fdj = fopen ( filepath , " rb " ) ;
2017-06-23 05:58:32 +08:00
if ( ! fdj ) {
2018-04-13 04:40:11 +08:00
Error ( " Can't open %s: %s " , filepath , strerror ( errno ) ) ;
return false ;
2017-06-23 05:58:32 +08:00
}
# if HAVE_SENDFILE
2019-01-17 00:20:10 +08:00
if ( fstat ( fileno ( fdj ) , & filestat ) < 0 ) {
Error ( " Failed getting information about file %s: %s " , filepath , strerror ( errno ) ) ;
2018-04-13 04:40:11 +08:00
return false ;
2017-06-23 05:58:32 +08:00
}
# else
2018-04-13 04:40:11 +08:00
img_buffer_size = fread ( img_buffer , 1 , sizeof ( temp_img_buffer ) , fdj ) ;
2017-06-23 05:58:32 +08:00
# endif
} else {
2017-08-24 03:05:44 +08:00
Image * image = NULL ;
if ( filepath [ 0 ] ) {
2018-04-04 03:23:38 +08:00
Debug ( 1 , " Loading image " ) ;
2018-04-13 04:40:11 +08:00
image = new Image ( filepath ) ;
2017-08-24 03:05:44 +08:00
} else if ( ffmpeg_input ) {
// Get the frame from the mp4 input
Debug ( 1 , " Getting frame from ffmpeg " ) ;
2018-11-20 05:45:56 +08:00
AVFrame * frame ;
2018-09-12 01:20:41 +08:00
FrameData * frame_data = & event_data - > frames [ curr_frame_id - 1 ] ;
2018-11-20 05:45:56 +08:00
frame = ffmpeg_input - > get_frame ( ffmpeg_input - > get_video_stream_id ( ) , frame_data - > offset ) ;
2017-08-24 03:05:44 +08:00
if ( frame ) {
2018-04-04 03:23:38 +08:00
image = new Image ( frame ) ;
2018-11-20 05:45:56 +08:00
//av_frame_free(&frame);
2017-08-24 03:05:44 +08:00
} else {
Error ( " Failed getting a frame. " ) ;
return false ;
}
} else {
Error ( " Unable to get a frame " ) ;
return false ;
}
2018-04-04 03:23:38 +08:00
Image * send_image = prepareImage ( image ) ;
2017-06-23 05:58:32 +08:00
2019-01-17 00:20:10 +08:00
switch ( type ) {
2017-06-23 05:58:32 +08:00
case STREAM_JPEG :
2018-04-04 03:23:38 +08:00
send_image - > EncodeJpeg ( img_buffer , & img_buffer_size ) ;
2017-06-23 05:58:32 +08:00
break ;
case STREAM_ZIP :
# if HAVE_ZLIB_H
unsigned long zip_buffer_size ;
2018-04-04 03:23:38 +08:00
send_image - > Zip ( img_buffer , & zip_buffer_size ) ;
2017-06-23 05:58:32 +08:00
img_buffer_size = zip_buffer_size ;
break ;
# else
Error ( " zlib is required for zipped images. Falling back to raw image " ) ;
type = STREAM_RAW ;
# endif // HAVE_ZLIB_H
case STREAM_RAW :
img_buffer = ( uint8_t * ) ( send_image - > Buffer ( ) ) ;
img_buffer_size = send_image - > Size ( ) ;
break ;
default :
2019-01-17 00:20:10 +08:00
Fatal ( " Unexpected frame type %d " , type ) ;
2017-06-23 05:58:32 +08:00
break ;
}
2017-08-24 03:05:44 +08:00
delete image ;
image = NULL ;
2019-01-17 00:20:10 +08:00
} // end if send_raw or not
2017-06-23 05:58:32 +08:00
2019-01-17 00:20:10 +08:00
switch ( type ) {
2017-06-23 05:58:32 +08:00
case STREAM_JPEG :
2019-01-17 00:20:10 +08:00
fputs ( " Content-Type: image/jpeg \r \n " , stdout ) ;
2017-06-23 05:58:32 +08:00
break ;
case STREAM_RAW :
2019-01-17 00:20:10 +08:00
fputs ( " Content-Type: image/x-rgb \r \n " , stdout ) ;
2017-06-23 05:58:32 +08:00
break ;
case STREAM_ZIP :
2019-01-17 00:20:10 +08:00
fputs ( " Content-Type: image/x-rgbz \r \n " , stdout ) ;
2017-06-23 05:58:32 +08:00
break ;
default :
2018-04-13 04:40:11 +08:00
Fatal ( " Unexpected frame type %d " , type ) ;
2017-06-23 05:58:32 +08:00
break ;
}
2017-08-24 03:05:44 +08:00
if ( send_raw ) {
2017-06-23 05:58:32 +08:00
# if HAVE_SENDFILE
2019-01-17 00:20:10 +08:00
fprintf ( stdout , " Content-Length: %d \r \n \r \n " , ( int ) filestat . st_size ) ;
2017-06-26 23:20:10 +08:00
if ( zm_sendfile ( fileno ( stdout ) , fileno ( fdj ) , 0 , ( int ) filestat . st_size ) ! = ( int ) filestat . st_size ) {
2017-06-23 05:58:32 +08:00
/* sendfile() failed, use standard way instead */
img_buffer_size = fread ( img_buffer , 1 , sizeof ( temp_img_buffer ) , fdj ) ;
2019-01-17 00:20:10 +08:00
if ( fwrite ( img_buffer , img_buffer_size , 1 , stdout ) ! = 1 ) {
2017-12-13 02:38:31 +08:00
fclose ( fdj ) ; /* Close the file handle */
2017-06-23 05:58:32 +08:00
Error ( " Unable to send raw frame %u: %s " , curr_frame_id , strerror ( errno ) ) ;
2019-01-17 00:20:10 +08:00
return false ;
2017-06-23 05:58:32 +08:00
}
}
# else
2019-01-17 00:20:10 +08:00
fprintf ( stdout , " Content-Length: %d \r \n \r \n " , img_buffer_size ) ;
if ( fwrite ( img_buffer , img_buffer_size , 1 , stdout ) ! = 1 ) {
2017-12-13 02:38:31 +08:00
fclose ( fdj ) ; /* Close the file handle */
2017-06-23 05:58:32 +08:00
Error ( " Unable to send raw frame %u: %s " , curr_frame_id , strerror ( errno ) ) ;
2019-01-17 00:20:10 +08:00
return false ;
2017-06-23 05:58:32 +08:00
}
2017-06-22 22:12:04 +08:00
# endif
2017-06-23 05:58:32 +08:00
fclose ( fdj ) ; /* Close the file handle */
} else {
2019-01-17 00:20:10 +08:00
Debug ( 3 , " Content length: %d " , img_buffer_size ) ;
fprintf ( stdout , " Content-Length: %d \r \n \r \n " , img_buffer_size ) ;
if ( fwrite ( img_buffer , img_buffer_size , 1 , stdout ) ! = 1 ) {
Error ( " Unable to send stream frame: %s " , strerror ( errno ) ) ;
return false ;
2017-06-23 05:58:32 +08:00
}
2019-01-17 00:20:10 +08:00
} // end if send_raw or not
2017-06-23 05:58:32 +08:00
2018-04-13 04:40:11 +08:00
fputs ( " \r \n \r \n " , stdout ) ;
fflush ( stdout ) ;
2019-01-17 00:20:10 +08:00
} // end if stream MPEG or other
2018-04-13 04:40:11 +08:00
last_frame_sent = TV_2_FLOAT ( now ) ;
return true ;
2019-01-17 00:20:10 +08:00
} // bool EventStream::sendFrame( int delta_us )
2017-06-23 05:58:32 +08:00
void EventStream : : runStream ( ) {
openComms ( ) ;
2017-08-24 03:05:44 +08:00
Debug ( 3 , " Comms open " ) ;
2017-06-23 05:58:32 +08:00
checkInitialised ( ) ;
if ( type = = STREAM_JPEG )
2018-04-13 01:14:00 +08:00
fputs ( " Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame \r \n \r \n " , stdout ) ;
2017-06-23 05:58:32 +08:00
if ( ! event_data ) {
2018-04-13 01:14:00 +08:00
sendTextFrame ( " No event data found " ) ;
exit ( 0 ) ;
2017-06-23 05:58:32 +08:00
}
2018-04-13 01:14:00 +08:00
Debug ( 3 , " frame rate is: (%f) " , ( double ) event_data - > frame_count / event_data - > duration ) ;
updateFrameRate ( ( double ) event_data - > frame_count / event_data - > duration ) ;
2019-03-07 03:50:36 +08:00
gettimeofday ( & start , NULL ) ;
uint64_t start_usec = start . tv_sec * 1000000 + start . tv_usec ;
uint64_t last_frame_offset = 0 ;
2017-12-13 02:30:43 +08:00
2019-01-17 00:20:10 +08:00
while ( ! zm_terminate ) {
2018-04-13 01:14:00 +08:00
gettimeofday ( & now , NULL ) ;
2017-06-23 05:58:32 +08:00
2019-01-16 00:25:22 +08:00
int delta_us = 0 ;
2017-06-22 22:46:32 +08:00
send_frame = false ;
2019-02-26 23:08:07 +08:00
if ( connkey ) {
// commands may set send_frame to true
while ( checkCommandQueue ( ) & & ! zm_terminate ) {
// The idea is to loop here processing all commands before proceeding.
}
2017-06-23 05:58:32 +08:00
2019-02-26 23:08:07 +08:00
// Update modified time of the socket .lock file so that we can tell which ones are stale.
if ( now . tv_sec - last_comm_update . tv_sec > 3600 ) {
touch ( sock_path_lock ) ;
last_comm_update = now ;
}
2018-09-22 03:58:14 +08:00
}
2017-06-23 05:58:32 +08:00
if ( step ! = 0 )
curr_frame_id + = step ;
2017-08-24 03:05:44 +08:00
// Detects when we hit end of event and will load the next event or previous event
2017-06-23 05:58:32 +08:00
checkEventLoaded ( ) ;
// Get current frame data
FrameData * frame_data = & event_data - > frames [ curr_frame_id - 1 ] ;
//Info( "cst:%.2f", curr_stream_time );
//Info( "cfid:%d", curr_frame_id );
//Info( "fdt:%d", frame_data->timestamp );
if ( ! paused ) {
2019-02-27 00:41:02 +08:00
Debug ( 3 , " Not paused " ) ;
2017-06-23 05:58:32 +08:00
bool in_event = true ;
double time_to_event = 0 ;
if ( replay_rate > 0 ) {
time_to_event = event_data - > frames [ 0 ] . timestamp - curr_stream_time ;
if ( time_to_event > 0 )
in_event = false ;
} else if ( replay_rate < 0 ) {
time_to_event = curr_stream_time - event_data - > frames [ event_data - > frame_count - 1 ] . timestamp ;
if ( time_to_event > 0 )
in_event = false ;
}
if ( ! in_event ) {
2018-04-13 01:14:00 +08:00
double actual_delta_time = TV_2_FLOAT ( now ) - last_frame_sent ;
2019-01-17 00:20:10 +08:00
// > 1 second
2017-06-23 05:58:32 +08:00
if ( actual_delta_time > 1 ) {
static char frame_text [ 64 ] ;
2018-04-13 01:14:00 +08:00
snprintf ( frame_text , sizeof ( frame_text ) , " Time to next event = %d seconds " , ( int ) time_to_event ) ;
if ( ! sendTextFrame ( frame_text ) )
2017-06-23 05:58:32 +08:00
zm_terminate = true ;
}
//else
//{
2019-02-27 00:41:02 +08:00
Debug ( 2 , " Sleeping because paused " ) ;
2019-01-17 00:20:10 +08:00
usleep ( STREAM_PAUSE_WAIT ) ;
2017-06-22 22:12:04 +08:00
//curr_stream_time += (replay_rate>0?1:-1) * ((1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000));
curr_stream_time + = ( 1.0 L * replay_rate * STREAM_PAUSE_WAIT ) / ( ZM_RATE_BASE * 1000000 ) ;
2017-06-23 05:58:32 +08:00
//}
continue ;
2019-01-17 00:20:10 +08:00
} // end if !in_event
2017-06-23 05:58:32 +08:00
2017-06-22 22:46:32 +08:00
// Figure out if we should send this frame
2018-09-12 01:20:41 +08:00
Debug ( 3 , " cur_frame_id (%d-1) mod frame_mod(%d) " , curr_frame_id , frame_mod ) ;
2017-06-23 05:58:32 +08:00
// If we are streaming and this frame is due to be sent
2018-09-12 01:20:41 +08:00
// frame mod defaults to 1 and if we are going faster than max_fps will get multiplied by 2
// so if it is 2, then we send every other frame, if is it 4 then every fourth frame, etc.
if ( ( frame_mod = = 1 ) | | ( ( ( curr_frame_id - 1 ) % frame_mod ) = = 0 ) ) {
2017-06-23 05:58:32 +08:00
delta_us = ( unsigned int ) ( frame_data - > delta * 1000000 ) ;
2018-12-05 07:23:08 +08:00
Debug ( 3 , " frame delta %uus " , delta_us ) ;
2017-06-23 05:58:32 +08:00
// if effective > base we should speed up frame delivery
delta_us = ( unsigned int ) ( ( delta_us * base_fps ) / effective_fps ) ;
2018-09-12 01:20:41 +08:00
Debug ( 3 , " delta %u = base_fps(%f)/effective fps(%f) " , delta_us , base_fps , effective_fps ) ;
2017-06-23 05:58:32 +08:00
// but must not exceed maxfps
delta_us = max ( delta_us , 1000000 / maxfps ) ;
2019-03-07 03:50:36 +08:00
Debug ( 3 , " delta %u = base_fps(%f)/effective fps(%f) from 30fps " , delta_us , base_fps , effective_fps ) ;
2017-06-23 05:58:32 +08:00
send_frame = true ;
}
} else if ( step ! = 0 ) {
2019-02-27 00:41:02 +08:00
Debug ( 2 , " Paused with step " ) ;
2017-06-23 05:58:32 +08:00
// We are paused and are just stepping forward or backward one frame
step = 0 ;
send_frame = true ;
2017-08-14 22:41:04 +08:00
} else if ( ! send_frame ) {
2018-09-29 00:31:53 +08:00
// We are paused, not stepping and doing nothing
2018-04-13 01:14:00 +08:00
double actual_delta_time = TV_2_FLOAT ( now ) - last_frame_sent ;
2017-06-23 05:58:32 +08:00
if ( actual_delta_time > MAX_STREAM_DELAY ) {
// Send keepalive
2018-04-13 01:14:00 +08:00
Debug ( 2 , " Sending keepalive frame " ) ;
2017-06-23 05:58:32 +08:00
send_frame = true ;
2019-02-27 00:41:02 +08:00
//} else {
//Debug(2, "Not Sending keepalive frame");
2017-06-23 05:58:32 +08:00
}
2019-01-17 00:20:10 +08:00
} // end if streaming stepping or doing nothing
2017-06-23 05:58:32 +08:00
2019-02-27 00:41:02 +08:00
if ( send_frame ) {
//Debug(3,"sending frame");
2018-04-13 01:14:00 +08:00
if ( ! sendFrame ( delta_us ) )
2017-06-23 05:58:32 +08:00
zm_terminate = true ;
2019-02-27 00:41:02 +08:00
//} else {
//Debug(3,"Not sending frame");
}
2017-06-23 05:58:32 +08:00
curr_stream_time = frame_data - > timestamp ;
if ( ! paused ) {
2019-03-07 03:50:36 +08:00
// +/- 1? What if we are skipping frames?
curr_frame_id + = ( replay_rate > 0 ) ? frame_mod : - 1 * frame_mod ;
2019-01-16 00:25:22 +08:00
2018-09-29 00:31:53 +08:00
if ( ( mode = = MODE_SINGLE ) & & ( ( unsigned int ) curr_frame_id = = event_data - > frame_count ) ) {
Debug ( 2 , " Have mode==MODE_SINGLE and at end of event, looping back to start " ) ;
2017-06-23 05:58:32 +08:00
curr_frame_id = 1 ;
2018-09-29 00:31:53 +08:00
}
2019-01-16 00:25:22 +08:00
frame_data = & event_data - > frames [ curr_frame_id - 1 ] ;
2019-03-07 03:50:36 +08:00
// sending the frame may have taken some time, so reload now
gettimeofday ( & now , NULL ) ;
2019-01-16 00:25:22 +08:00
uint64_t now_usec = ( now . tv_sec * 1000000 + now . tv_usec ) ;
2019-03-07 03:50:36 +08:00
// frame_data->delta is the time since last frame as a float in seconds
// but what if we are skipping frames? We need the distance from the last frame sent
// Also, what about reverse? needs to be absolute value
// There are two ways to go about this, not sure which is correct.
// you can calculate the relationship between now and the start
// or calc the relationship from the last frame. I think from the start is better as it self-corrects
if ( last_frame_offset ) {
// We assume that we are going forward and the next frame is in the future.
delta_us = frame_data - > offset * 1000000 - ( now_usec - start_usec ) ;
// - (now_usec - start_usec);
Debug ( 2 , " New delta_us now % " PRIu64 " - start % " PRIu64 " = %d offset % " PRId64 " - elapsed = %dusec " ,
now_usec , start_usec , now_usec - start_usec , frame_data - > offset * 1000000 , delta_us ) ;
} else {
Debug ( 2 , " No last frame_offset, no sleep " ) ;
delta_us = 0 ;
}
last_frame_offset = frame_data - > offset * 1000000 ;
2019-01-16 00:25:22 +08:00
2017-06-23 05:58:32 +08:00
if ( send_frame & & type ! = STREAM_MPEG ) {
2019-01-16 00:25:22 +08:00
if ( delta_us > 0 ) {
Debug ( 3 , " dUs: %d " , delta_us ) ;
2019-01-17 00:20:10 +08:00
usleep ( delta_us ) ;
2019-02-27 00:41:02 +08:00
Debug ( 3 , " Done sleeping: %d usec " , delta_us ) ;
2019-01-16 00:25:22 +08:00
}
2017-06-23 05:58:32 +08:00
}
} else {
2019-02-27 00:41:02 +08:00
delta_us = ( ( 1000000 * ZM_RATE_BASE ) / ( ( base_fps ? base_fps : 1 ) * ( replay_rate ? abs ( replay_rate * 2 ) : 2 ) ) ) ;
Debug ( 2 , " Sleeping %d because 1000000 * ZM_RATE_BASE(%d) / ( base_fps (%f), replay_rate(%d) " ,
( unsigned long ) ( ( 1000000 * ZM_RATE_BASE ) / ( ( base_fps ? base_fps : 1 ) * abs ( replay_rate * 2 ) ) ) ,
ZM_RATE_BASE ,
( base_fps ? base_fps : 1 ) ,
( replay_rate ? abs ( replay_rate * 2 ) : 200 )
) ;
if ( delta_us > 0 and delta_us < 100000 ) {
2019-01-17 00:20:10 +08:00
usleep ( ( unsigned long ) ( ( 1000000 * ZM_RATE_BASE ) / ( ( base_fps ? base_fps : 1 ) * abs ( replay_rate * 2 ) ) ) ) ;
2019-02-27 00:41:02 +08:00
} else {
//Error("Not sleeping!");
usleep ( 100000 ) ;
}
2017-06-23 05:58:32 +08:00
}
} // end while ! zm_terminate
# if HAVE_LIBAVCODEC
if ( type = = STREAM_MPEG )
delete vid_stream ;
# endif // HAVE_LIBAVCODEC
closeComms ( ) ;
2019-01-17 00:20:10 +08:00
} // void EventStream::runStream()
2018-04-18 01:51:20 +08:00
void EventStream : : setStreamStart ( uint64_t init_event_id , unsigned int init_frame_id = 0 ) {
2019-01-17 00:20:10 +08:00
loadInitialEventData ( init_event_id , init_frame_id ) ;
if ( ! ( monitor = Monitor : : Load ( event_data - > monitor_id , false , Monitor : : QUERY ) ) ) {
Fatal ( " Unable to load monitor id %d for streaming " , event_data - > monitor_id ) ;
2018-03-03 10:24:39 +08:00
return ;
}
}
2019-01-17 00:20:10 +08:00
void EventStream : : setStreamStart ( int monitor_id , time_t event_time ) {
2018-04-13 04:40:11 +08:00
loadInitialEventData ( monitor_id , event_time ) ;
if ( ! ( monitor = Monitor : : Load ( event_data - > monitor_id , false , Monitor : : QUERY ) ) ) {
Fatal ( " Unable to load monitor id %d for streaming " , monitor_id ) ;
2018-03-03 10:24:39 +08:00
return ;
}
}