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"
2019-07-26 22:53:48 +08:00
bool EventStream : : loadInitialEventData ( int monitor_id , time_t event_time ) {
2017-06-23 05:58:32 +08:00
static char sql [ ZM_SQL_SML_BUFSIZ ] ;
2019-08-15 04:18:21 +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 " ) ;
2020-03-27 00:06:07 +08:00
for ( unsigned int i = 0 ; i < event_data - > frame_count ; i + + ) {
2017-06-23 05:58:32 +08:00
//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-05-28 05:15:46 +08:00
} // end foreach frame
2019-01-17 00:20:10 +08:00
Debug ( 3 , " Skipping %ld frames " , event_data - > frame_count ) ;
2019-05-28 05:15:46 +08:00
} else {
Warning ( " Requested an event time less than the start of the event. event_time %.2f < start_time %.2f " ,
event_time , event_data - > start_time ) ;
2017-06-23 05:58:32 +08:00
}
2019-05-28 05:15:46 +08:00
} // end if have a start time
2018-04-13 04:40:11 +08:00
return true ;
2019-05-28 05:15:46 +08:00
} // bool EventStream::loadInitialEventData( int monitor_id, time_t event_time )
2017-06-23 05:58:32 +08:00
2019-07-26 22:53:48 +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 ;
2020-01-08 03:49:58 +08:00
curr_frame_id = 1 ;
2018-03-03 10:24:39 +08:00
} 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 ) ,
2019-08-15 04:18:21 +08:00
" SELECT `MonitorId`, `StorageId`, `Frames`, unix_timestamp( `StartTime` ) AS StartTimestamp, "
2019-08-16 22:24:51 +08:00
" (SELECT max(`Delta`)-min(`Delta`) FROM `Frames` WHERE `EventId`=`Events`.`Id`) AS Duration, "
2020-02-11 05:22:01 +08:00
" `DefaultVideo`, `Scheme`, `SaveJPEGs`, `Orientation`+0 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 ] ) ;
2020-02-11 05:22:01 +08:00
event_data - > Orientation = ( Monitor : : Orientation ) ( dbrow [ 8 ] = = NULL ? 0 : atoi ( dbrow [ 8 ] ) ) ;
2019-07-26 22:53:48 +08:00
mysql_free_result ( result ) ;
2017-06-23 05:58:32 +08:00
2020-05-02 06:44:35 +08:00
if ( ! monitor ) {
monitor = Monitor : : Load ( event_data - > monitor_id , false , Monitor : : QUERY ) ;
} else if ( monitor - > Id ( ) ! = event_data - > monitor_id ) {
delete monitor ;
monitor = Monitor : : Load ( event_data - > monitor_id , false , Monitor : : QUERY ) ;
}
if ( ! monitor ) {
Fatal ( " Unable to load monitor id %d for streaming " , event_data - > monitor_id ) ;
}
if ( ! storage ) {
storage = new Storage ( event_data - > storage_id ) ;
} else if ( storage - > Id ( ) ! = event_data - > storage_id ) {
delete 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
}
2019-01-17 00:20:10 +08:00
updateFrameRate ( ( double ) event_data - > frame_count / event_data - > duration ) ;
2019-07-26 22:53:48 +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
2019-08-15 04:18:21 +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
2019-07-26 22:53:48 +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 ;
2020-05-02 06:44:35 +08:00
Debug ( 3 , " Frame %d timestamp:(%f), offset(%f) delta(%f), in_db(%d) " ,
2019-05-28 05:15:46 +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 ;
2019-07-26 22:53:48 +08:00
Debug ( 4 , " Frame %d timestamp:(%f), offset(%f) delta(%f), in_db(%d) " ,
2018-09-12 01:20:41 +08:00
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
}
2019-07-26 22:53:48 +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
2020-05-02 06:14:58 +08:00
if ( event_data - > video_file [ 0 ] | | ( monitor - > GetOptVideoWriter ( ) > 0 ) ) {
if ( ! event_data - > video_file [ 0 ] ) {
snprintf ( event_data - > video_file , sizeof ( event_data - > video_file ) , " % " PRIu64 " -%s " , event_data - > event_id , " video.mp4 " ) ;
}
2019-07-26 22:53:48 +08:00
std : : string filepath = std : : string ( event_data - > path ) + " / " + std : : string ( event_data - > video_file ) ;
//char filepath[PATH_MAX];
//snprintf(filepath, sizeof(filepath), "%s/%s", event_data->path, event_data->video_file);
Debug ( 1 , " Loading video file from %s " , filepath . c_str ( ) ) ;
2017-06-23 05:58:32 +08:00
ffmpeg_input = new FFmpeg_Input ( ) ;
2019-07-26 22:53:48 +08:00
if ( 0 > ffmpeg_input - > Open ( filepath . c_str ( ) ) ) {
Warning ( " Unable to open ffmpeg_input %s " , filepath . c_str ( ) ) ;
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 ;
}
2019-05-28 05:15:46 +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
2020-05-02 06:44:35 +08:00
switch ( ( MsgCommand ) msg - > msg_data [ 0 ] ) {
2017-06-23 05:58:32 +08:00
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
2019-07-26 22:53:48 +08:00
if (
( mode = = MODE_SINGLE | | mode = = MODE_NONE )
& &
( ( unsigned int ) curr_frame_id = = event_data - > frame_count )
) {
2018-09-29 00:31:53 +08:00
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 {
2019-07-26 22:53:48 +08:00
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-22 22:46:32 +08:00
}
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 ;
2020-03-27 00:06:07 +08:00
if ( ( unsigned int ) curr_frame_id < event_data - > frame_count )
curr_frame_id + = 1 ;
2017-06-23 05:58:32 +08:00
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 ;
2020-01-08 06:07:35 +08:00
curr_frame_id - = 1 ;
if ( curr_frame_id < 1 ) curr_frame_id = 1 ;
2017-06-23 05:58:32 +08:00
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 :
{
2019-05-28 05:15:46 +08:00
// offset is in seconds
2020-10-07 04:35:46 +08:00
int int_part = ( ( 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 ] ;
int dec_part = ( ( unsigned char ) msg - > msg_data [ 5 ] < < 24 ) | ( ( unsigned char ) msg - > msg_data [ 6 ] < < 16 ) | ( ( unsigned char ) msg - > msg_data [ 7 ] < < 8 ) | ( unsigned char ) msg - > msg_data [ 8 ] ;
double offset = ( double ) int_part + ( double ) ( dec_part / ( double ) 1000000 ) ;
if ( offset < 0.0 ) {
Warning ( " Invalid offset, not seeking " ) ;
break ;
}
curr_frame_id = ( int ) ( event_data - > frame_count * offset / event_data - > duration ) + 1 ;
curr_stream_time = event_data - > frames [ curr_frame_id - 1 ] . timestamp ;
Debug ( 1 , " Got SEEK command, to %f (new current frame id: %d offset %f) " ,
2020-10-06 03:55:42 +08:00
offset , curr_frame_id , event_data - > frames [ curr_frame_id - 1 ] . offset ) ;
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 ;
2020-10-05 22:29:42 +08:00
double progress ;
2017-06-23 05:58:32 +08:00
int rate ;
int zoom ;
bool paused ;
} status_data ;
2018-04-13 04:40:11 +08:00
status_data . event_id = event_data - > event_id ;
2020-10-05 22:29:42 +08:00
status_data . progress = event_data - > frames [ curr_frame_id - 1 ] . offset ;
2017-06-23 05:58:32 +08:00
status_data . rate = replay_rate ;
status_data . zoom = zoom ;
status_data . paused = paused ;
2020-10-05 22:29:42 +08:00
Debug ( 2 , " Event:% " PRIu64 " , Paused:%d, progress:%f 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-07-26 22:53:48 +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
2019-07-26 22:53:48 +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
2020-04-27 03:22:29 +08:00
bool EventStream : : checkEventLoaded ( ) {
2017-06-23 05:58:32 +08:00
static char sql [ ZM_SQL_SML_BUFSIZ ] ;
if ( curr_frame_id < = 0 ) {
2019-07-26 22:53:48 +08:00
snprintf ( sql , sizeof ( sql ) ,
2019-08-15 04:18:21 +08:00
" SELECT `Id` FROM `Events` WHERE `MonitorId` = %ld AND `Id` < % " PRIu64 " ORDER BY `Id` DESC LIMIT 1 " ,
2019-07-26 22:53:48 +08:00
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-07-26 22:53:48 +08:00
snprintf ( sql , sizeof ( sql ) ,
2019-08-15 04:18:21 +08:00
" SELECT `Id` FROM `Events` WHERE `MonitorId` = %ld AND `Id` > % " PRIu64 " ORDER BY `Id` ASC LIMIT 1 " ,
2019-07-26 22:53:48 +08:00
event_data - > monitor_id , event_data - > event_id ) ;
2019-01-17 00:20:10 +08:00
} else {
// No event change required
2019-07-26 22:53:48 +08:00
Debug ( 3 , " No event change required, as curr frame %d <=> event frames %d " ,
curr_frame_id , event_data - > frame_count ) ;
2020-04-27 03:22:29 +08:00
return false ;
2017-06-23 05:58:32 +08:00
}
2019-01-17 00:20:10 +08:00
// Event change required.
2019-07-26 22:53:48 +08:00
if ( forceEventChange | | ( ( mode ! = MODE_SINGLE ) & & ( mode ! = MODE_NONE ) ) ) {
2019-01-17 00:20:10 +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
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-07-26 22:53:48 +08:00
if ( replay_rate < 0 ) // rewind
2019-01-17 00:20:10 +08:00
curr_frame_id = event_data - > frame_count ;
else
curr_frame_id = 1 ;
Debug ( 2 , " New frame id = %d " , curr_frame_id ) ;
2020-04-27 03:22:29 +08:00
return true ;
2017-06-23 05:58:32 +08:00
} else {
2019-07-26 22:53:48 +08:00
Debug ( 2 , " No next event loaded using %s. Pausing " , sql ) ;
2017-06-23 05:58:32 +08:00
if ( curr_frame_id < = 0 )
curr_frame_id = 1 ;
else
curr_frame_id = event_data - > frame_count ;
paused = true ;
2019-07-27 00:21:15 +08:00
sendTextFrame ( " No more event data found " ) ;
2019-01-17 00:20:10 +08:00
} // end if found a new event or not
mysql_free_result ( result ) ;
forceEventChange = false ;
} else {
2019-07-26 22:53:48 +08:00
Debug ( 2 , " Pausing because mode is %d " , mode ) ;
2019-01-17 00:20:10 +08:00
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
}
2020-04-27 03:22:29 +08:00
return false ;
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 ] ;
2019-01-17 00:20:10 +08:00
snprintf ( filepath , sizeof ( filepath ) , staticConfig . capture_file_format , event_data - > path , curr_frame_id ) ;
2019-05-28 05:15:46 +08:00
Debug ( 2 , " EventStream::getImage path(%s) from %s frame(%d) " , filepath , event_data - > path , curr_frame_id ) ;
2018-04-04 03:23:38 +08:00
Image * image = new Image ( filepath ) ;
2017-06-23 05:58:32 +08:00
return image ;
}
2019-05-28 05:15:46 +08:00
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 ;
2019-07-26 22:53:48 +08:00
// 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.
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-07-26 22:53:48 +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-07-26 22:53:48 +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 ) {
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 ) {
2020-05-02 06:44:35 +08:00
vid_stream = new VideoStream ( " pipe: " , format , bitrate , effective_fps ,
send_image - > Colours ( ) , send_image - > SubpixelOrder ( ) , send_image - > Width ( ) , send_image - > Height ( ) ) ;
2018-04-04 03:23:38 +08:00
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 ) ) ;
2019-08-16 22:24:51 +08:00
return true ; // returning false will cause us to terminate.
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-09-12 01:20:41 +08:00
FrameData * frame_data = & event_data - > frames [ curr_frame_id - 1 ] ;
2020-05-02 06:44:35 +08:00
AVFrame * 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 ;
}
2020-02-11 05:22:01 +08:00
// when stored as an mp4, we just have the rotation as a flag in the headers
// so we need to rotate it before outputting
2020-02-25 03:12:10 +08:00
if (
( monitor - > GetOptVideoWriter ( ) = = Monitor : : H264PASSTHROUGH )
and
( event_data - > Orientation ! = Monitor : : ROTATE_0 )
) {
2020-02-11 05:22:01 +08:00
Debug ( 2 , " Rotating image %d " , event_data - > Orientation ) ;
switch ( event_data - > Orientation ) {
case Monitor : : ROTATE_0 :
// No action required
break ;
case Monitor : : ROTATE_90 :
case Monitor : : ROTATE_180 :
case Monitor : : ROTATE_270 :
image - > Rotate ( ( event_data - > Orientation - 1 ) * 90 ) ;
break ;
case Monitor : : FLIP_HORI :
case Monitor : : FLIP_VERT :
image - > Flip ( event_data - > Orientation = = Monitor : : FLIP_HORI ) ;
break ;
default :
Error ( " Invalid Orientation: %d " , event_data - > Orientation ) ;
}
} else {
Debug ( 2 , " Not Rotating image %d " , event_data - > Orientation ) ;
} // end if have rotation
2017-08-24 03:05:44 +08:00
} 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
2020-07-30 01:39:00 +08:00
if ( 0 > fprintf ( stdout , " Content-Length: %d \r \n \r \n " , ( int ) filestat . st_size ) ) {
fclose ( fdj ) ; /* Close the file handle */
Info ( " Unable to send raw frame %u: %s " , curr_frame_id , strerror ( errno ) ) ;
return false ;
}
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 */
2020-07-30 01:39:00 +08:00
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 */
2020-07-30 01:39:00 +08:00
Info ( " 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
2020-07-30 01:39:00 +08:00
if (
( 0 > fprintf ( stdout , " Content-Length: %d \r \n \r \n " , img_buffer_size ) )
| |
( fwrite ( img_buffer , img_buffer_size , 1 , stdout ) ! = 1 )
) {
2017-12-13 02:38:31 +08:00
fclose ( fdj ) ; /* Close the file handle */
2020-07-30 01:39:00 +08:00
Info ( " 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 ) ;
2020-07-30 01:39:00 +08:00
if (
( 0 > fprintf ( stdout , " Content-Length: %d \r \n \r \n " , img_buffer_size ) )
| |
( fwrite ( img_buffer , img_buffer_size , 1 , stdout ) ! = 1 ) ) {
2019-01-17 00:20:10 +08:00
Error ( " Unable to send stream frame: %s " , strerror ( errno ) ) ;
return false ;
2017-06-23 05:58:32 +08:00
}
2019-07-26 22:53:48 +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-07-26 22:53:48 +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-07-26 22:53:48 +08:00
} // bool EventStream::sendFrame( int delta_us )
2017-06-23 05:58:32 +08:00
void EventStream : : runStream ( ) {
openComms ( ) ;
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 ;
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
// 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-07-26 22:53:48 +08:00
Debug ( 3 , " Not paused at frame %d " , curr_frame_id ) ;
2019-07-27 00:21:15 +08:00
// This next bit is to determine if we are in the current event time wise
// and whether to show an image saying how long until the next event.
2017-06-23 05:58:32 +08:00
bool in_event = true ;
double time_to_event = 0 ;
if ( replay_rate > 0 ) {
2020-10-07 04:35:46 +08:00
// As we are playing, curr_stream_time starts at first frame timestamp and increases so it should only be greater if event data has been loaded for the next event.
2017-06-23 05:58:32 +08:00
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 ;
}
2019-07-27 00:21:15 +08:00
Debug ( 1 , " replay rate(%d) in_event(%d) time_to_event(%f)=curr_stream_time(%f)-frame timestamp:%f " ,
replay_rate , in_event , time_to_event , curr_stream_time , event_data - > frames [ event_data - > frame_count - 1 ] . timestamp ) ;
2017-06-23 05:58:32 +08:00
if ( ! in_event ) {
2018-04-13 01:14:00 +08:00
double actual_delta_time = TV_2_FLOAT ( now ) - last_frame_sent ;
2019-07-27 00:21:15 +08:00
Debug ( 1 , " Ctual delta time = %f = %f - %f " , 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 ) {
2019-07-27 00:21:15 +08:00
Debug ( 1 , " Sending time to next event frame " ) ;
2017-06-23 05:58:32 +08:00
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 ;
2019-07-27 00:21:15 +08:00
} else {
Debug ( 1 , " Not Sending time to next event frame because actual delta time is %f " , actual_delta_time ) ;
2017-06-23 05:58:32 +08:00
}
//else
//{
2019-07-26 22:53:48 +08:00
// FIXME ICON But we are not paused. We are somehow still in the event?
2019-07-27 00:21:15 +08:00
double sleep_time = ( replay_rate > 0 ? 1 : - 1 ) * ( ( 1.0 L * replay_rate * STREAM_PAUSE_WAIT ) / ( ZM_RATE_BASE * 1000000 ) ) ;
//double sleep_time = (replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000);
//// ZM_RATE_BASE == 100, and 1x replay_rate is 100
//double sleep_time = ((replay_rate/ZM_RATE_BASE) * STREAM_PAUSE_WAIT)/1000000;
if ( ! sleep_time ) {
sleep_time + = STREAM_PAUSE_WAIT / 1000000 ;
}
curr_stream_time + = sleep_time ;
Debug ( 2 , " Sleeping (%dus) because we are not at the next event yet, adding %f " , STREAM_PAUSE_WAIT , sleep_time ) ;
2019-01-17 00:20:10 +08:00
usleep ( STREAM_PAUSE_WAIT ) ;
2019-07-27 00:21:15 +08:00
//curr_stream_time += (1.0L * 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
2019-07-26 22:53:48 +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 ) ;
2019-07-26 22:53:48 +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 ) ;
2019-07-26 22:53:48 +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-07-26 22:53:48 +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 ) {
2020-01-08 06:07:35 +08:00
Debug ( 2 , " Paused with step %d " , 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 ) {
2019-08-20 23:13:38 +08:00
if ( ! sendFrame ( delta_us ) ) {
2017-06-23 05:58:32 +08:00
zm_terminate = true ;
2019-08-20 23:13:38 +08:00
break ;
}
2019-02-27 00:41:02 +08:00
}
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-05-25 01:45:48 +08:00
// sending the frame may have taken some time, so reload now
gettimeofday ( & now , NULL ) ;
uint64_t now_usec = ( now . tv_sec * 1000000 + now . tv_usec ) ;
2019-01-16 00:25:22 +08:00
2019-07-26 22:53:48 +08:00
// we incremented by replay_rate, so might have jumped past frame_count
if ( ( mode = = MODE_SINGLE ) & & ( ( unsigned int ) curr_frame_id > = event_data - > frame_count ) ) {
2018-09-29 00:31:53 +08:00
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 ;
2019-05-25 01:45:48 +08:00
// Have to reset start_usec to now when replaying
start_usec = now_usec ;
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
// 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
2020-05-02 06:44:35 +08:00
if ( send_frame & & ( type ! = STREAM_MPEG ) ) {
if ( delta_us > 0 ) {
2019-08-16 22:24:51 +08:00
if ( delta_us > MAX_SLEEP_USEC ) {
Debug ( 1 , " Limiting sleep to %d because calculated sleep is too long %d " , MAX_SLEEP_USEC , delta_us ) ;
delta_us = MAX_SLEEP_USEC ;
}
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 ) ) ) ;
2019-07-26 22:53:48 +08:00
Debug ( 2 , " Sleeping %d because 1000000 * ZM_RATE_BASE(%d) / ( base_fps (%f), replay_rate(%d) " ,
2019-02-27 00:41:02 +08:00
( unsigned long ) ( ( 1000000 * ZM_RATE_BASE ) / ( ( base_fps ? base_fps : 1 ) * abs ( replay_rate * 2 ) ) ) ,
ZM_RATE_BASE ,
( base_fps ? base_fps : 1 ) ,
2019-08-16 22:24:51 +08:00
( replay_rate ? abs ( replay_rate * 2 ) : 0 )
2019-02-27 00:41:02 +08:00
) ;
2019-08-16 22:24:51 +08:00
if ( delta_us > 0 ) {
if ( delta_us > MAX_SLEEP_USEC ) {
Debug ( 1 , " Limiting sleep to %d because calculated sleep is too long %d " , MAX_SLEEP_USEC , delta_us ) ;
delta_us = MAX_SLEEP_USEC ;
}
2019-07-26 22:53:48 +08:00
usleep ( delta_us ) ;
2019-02-27 00:41:02 +08:00
}
2019-07-26 22:53:48 +08:00
} // end if !paused
2019-08-16 22:24:51 +08:00
//if ( step != 0 )// Adding 0 is cheaper than an if 0
// curr_frame_id starts at 1 though, so we might skip the first frame?
curr_frame_id + = step ;
// Detects when we hit end of event and will load the next event or previous event
2020-03-27 00:06:07 +08:00
if ( ! paused )
checkEventLoaded ( ) ;
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()
2020-05-02 06:44:35 +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 ) ;
2020-05-02 06:44:35 +08:00
} // end void EventStream::setStreamStart(init_event_id,init_frame_id=0)
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 ) ;
2018-03-03 10:24:39 +08:00
}