Merge branch 'video' into feature-h264-videostorage
Conflicts: src/CMakeLists.txt src/Makefile.am src/zm_event.cpp web/skins/classic/views/js/event.jspull/618/head
commit
bf33eab17c
|
@ -264,6 +264,59 @@ else(MYSQLCLIENT_LIBRARIES)
|
|||
message(FATAL_ERROR "zm requires mysqlclient but it was not found on your system")
|
||||
endif(MYSQLCLIENT_LIBRARIES)
|
||||
|
||||
# x264 (using find_library and find_path)
|
||||
find_library(X264_LIBRARIES x264)
|
||||
if(X264_LIBRARIES)
|
||||
set(HAVE_LIBX264 1)
|
||||
list(APPEND ZM_BIN_LIBS "${X264_LIBRARIES}")
|
||||
find_path(X264_INCLUDE_DIR x264.h)
|
||||
if(X264_INCLUDE_DIR)
|
||||
include_directories("${X264_INCLUDE_DIR}")
|
||||
set(CMAKE_REQUIRED_INCLUDES "${X264_INCLUDE_DIR}")
|
||||
endif(X264_INCLUDE_DIR)
|
||||
mark_as_advanced(FORCE X264_LIBRARIES X264_INCLUDE_DIR)
|
||||
check_include_files("stdint.h;x264.h" HAVE_X264_H)
|
||||
set(optlibsfound "${optlibsfound} x264")
|
||||
else(X264_LIBRARIES)
|
||||
set(optlibsnotfound "${optlibsnotfound} x264")
|
||||
endif(X264_LIBRARIES)
|
||||
|
||||
# mp4v2 (using find_library and find_path)
|
||||
find_library(MP4V2_LIBRARIES mp4v2)
|
||||
if(MP4V2_LIBRARIES)
|
||||
set(HAVE_LIBMP4V2 1)
|
||||
list(APPEND ZM_BIN_LIBS "${MP4V2_LIBRARIES}")
|
||||
|
||||
# mp4v2/mp4v2.h
|
||||
find_path(MP4V2_INCLUDE_DIR mp4v2/mp4v2.h)
|
||||
if(MP4V2_INCLUDE_DIR)
|
||||
include_directories("${MP4V2_INCLUDE_DIR}")
|
||||
set(CMAKE_REQUIRED_INCLUDES "${MP4V2_INCLUDE_DIR}")
|
||||
endif(MP4V2_INCLUDE_DIR)
|
||||
check_include_file("mp4v2/mp4v2.h" HAVE_MP4V2_MP4V2_H)
|
||||
|
||||
# mp4v2.h
|
||||
find_path(MP4V2_INCLUDE_DIR mp4v2.h)
|
||||
if(MP4V2_INCLUDE_DIR)
|
||||
include_directories("${MP4V2_INCLUDE_DIR}")
|
||||
set(CMAKE_REQUIRED_INCLUDES "${MP4V2_INCLUDE_DIR}")
|
||||
endif(MP4V2_INCLUDE_DIR)
|
||||
check_include_file("mp4v2.h" HAVE_MP4V2_H)
|
||||
|
||||
# mp4.h
|
||||
find_path(MP4V2_INCLUDE_DIR mp4.h)
|
||||
if(MP4V2_INCLUDE_DIR)
|
||||
include_directories("${MP4V2_INCLUDE_DIR}")
|
||||
set(CMAKE_REQUIRED_INCLUDES "${MP4V2_INCLUDE_DIR}")
|
||||
endif(MP4V2_INCLUDE_DIR)
|
||||
check_include_file("mp4.h" HAVE_MP4_H)
|
||||
|
||||
mark_as_advanced(FORCE MP4V2_LIBRARIES MP4V2_INCLUDE_DIR)
|
||||
set(optlibsfound "${optlibsfound} mp4v2")
|
||||
else(MP4V2_LIBRARIES)
|
||||
set(optlibsnotfound "${optlibsnotfound} mp4v2")
|
||||
endif(MP4V2_LIBRARIES)
|
||||
|
||||
set(PATH_FFMPEG "")
|
||||
set(OPT_FFMPEG "no")
|
||||
# Do not check for ffmpeg if ZM_NO_FFMPEG is on
|
||||
|
|
|
@ -326,6 +326,7 @@ fi
|
|||
AC_CHECK_LIB(pcre,pcre_compile,,AC_MSG_WARN(libpcre.a may be required for remote/network camera support))
|
||||
AC_CHECK_LIB(z,zlibVersion)
|
||||
AC_CHECK_LIB(x264,x264_predict_16x16_init)
|
||||
AC_CHECK_LIB(mp4v2,MP4AddH264VideoTrack)
|
||||
AC_CHECK_LIB(avutil,av_malloc,,AC_MSG_WARN(libavutil.a may be required for MPEG streaming))
|
||||
# Don't bother to warn about this one
|
||||
AC_CHECK_LIB(avcore,av_image_copy,,)
|
||||
|
@ -375,6 +376,8 @@ AC_CHECK_HEADERS(sys/ipc.h,,,)
|
|||
AC_CHECK_HEADERS(sys/shm.h,,,)
|
||||
fi
|
||||
AC_CHECK_HEADERS(zlib.h,,,)
|
||||
AC_CHECK_HEADERS(x264.h,,,)
|
||||
AC_CHECK_HEADERS([mp4v2/mp4v2.h mp4v2.h mp4.h],,,)
|
||||
AC_CHECK_HEADERS(vlc/vlc.h,,,)
|
||||
AC_CHECK_HEADERS(curl/curl.h,,,)
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
configure_file(zm_config.h.in "${CMAKE_CURRENT_BINARY_DIR}/zm_config.h" @ONLY)
|
||||
|
||||
# Group together all the source files that are used by all the binaries (zmc, zma, zmu, zms etc)
|
||||
set(ZM_BIN_SRC_FILES zm_box.cpp zm_buffer.cpp zm_camera.cpp zm_comms.cpp zm_config.cpp zm_coord.cpp zm_curl_camera.cpp zm.cpp zm_db.cpp zm_logger.cpp zm_event.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_camera.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_ffmpeg.cpp zm_mpeg.cpp zm_poly.cpp zm_regexp.cpp zm_remote_camera.cpp zm_remote_camera_http.cpp zm_remote_camera_rtsp.cpp zm_rtp.cpp zm_rtp_ctrl.cpp zm_rtp_data.cpp zm_rtp_source.cpp zm_rtsp.cpp zm_rtsp_auth.cpp zm_sdp.cpp zm_signal.cpp zm_stream.cpp zm_thread.cpp zm_time.cpp zm_timer.cpp zm_user.cpp zm_utils.cpp zm_videostore.cpp zm_zone.cpp)
|
||||
set(ZM_BIN_SRC_FILES zm_box.cpp zm_buffer.cpp zm_camera.cpp zm_comms.cpp zm_config.cpp zm_coord.cpp zm_curl_camera.cpp zm.cpp zm_db.cpp zm_logger.cpp zm_event.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_camera.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_ffmpeg.cpp zm_mpeg.cpp zm_poly.cpp zm_regexp.cpp zm_remote_camera.cpp zm_remote_camera_http.cpp zm_remote_camera_rtsp.cpp zm_rtp.cpp zm_rtp_ctrl.cpp zm_rtp_data.cpp zm_rtp_source.cpp zm_rtsp.cpp zm_rtsp_auth.cpp zm_sdp.cpp zm_signal.cpp zm_stream.cpp zm_thread.cpp zm_time.cpp zm_timer.cpp zm_user.cpp zm_utils.cpp zm_video.cpp zm_videostore.cpp zm_zone.cpp)
|
||||
|
||||
# A fix for cmake recompiling the source files for every target.
|
||||
add_library(zm STATIC ${ZM_BIN_SRC_FILES})
|
||||
|
|
|
@ -60,6 +60,7 @@ zm_SOURCES = \
|
|||
zm_timer.cpp \
|
||||
zm_user.cpp \
|
||||
zm_utils.cpp \
|
||||
zm_video.cpp \
|
||||
zm_videostore.cpp \
|
||||
zm_zone.cpp
|
||||
|
||||
|
@ -117,6 +118,7 @@ noinst_HEADERS = \
|
|||
zm_timer.h \
|
||||
zm_user.h \
|
||||
zm_utils.h \
|
||||
zm_video.h \
|
||||
zm_videostore.h \
|
||||
zm_zone.h
|
||||
|
||||
|
|
126
src/zm_event.cpp
126
src/zm_event.cpp
|
@ -48,6 +48,7 @@ bool Event::initialised = false;
|
|||
char Event::capture_file_format[PATH_MAX];
|
||||
char Event::analyse_file_format[PATH_MAX];
|
||||
char Event::general_file_format[PATH_MAX];
|
||||
char Event::video_file_format[PATH_MAX];
|
||||
|
||||
int Event::pre_alarm_count = 0;
|
||||
Event::PreAlarmData Event::pre_alarm_data[MAX_PRE_ALARM_FRAMES] = { { 0 } };
|
||||
|
@ -162,6 +163,48 @@ Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string
|
|||
Fatal( "Can't fopen %s: %s", id_file, strerror(errno));
|
||||
}
|
||||
last_db_frame = 0;
|
||||
|
||||
video_name[0] = 0;
|
||||
|
||||
/* Save as video */
|
||||
if ( monitor->GetOptVideoWriter() != 0 ) {
|
||||
int nRet;
|
||||
snprintf( video_name, sizeof(video_name), "%d-%s", id, "video.mp4" );
|
||||
snprintf( video_file, sizeof(video_file), video_file_format, path, video_name );
|
||||
snprintf( timecodes_name, sizeof(timecodes_name), "%d-%s", id, "video.timecodes" );
|
||||
snprintf( timecodes_file, sizeof(timecodes_file), video_file_format, path, timecodes_name );
|
||||
|
||||
|
||||
/* X264 MP4 video writer */
|
||||
if(monitor->GetOptVideoWriter() == 1) {
|
||||
#if ZM_HAVE_VIDEOWRITER_X264MP4
|
||||
videowriter = new X264MP4Writer(video_file, monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder(), monitor->GetOptEncoderParams());
|
||||
#else
|
||||
videowriter = NULL;
|
||||
Error("ZoneMinder was not compiled with the X264 MP4 video writer, check dependencies (x264 and mp4v2)");
|
||||
#endif
|
||||
}
|
||||
|
||||
if(videowriter != NULL) {
|
||||
/* Open the video stream */
|
||||
nRet = videowriter->Open();
|
||||
if(nRet != 0) {
|
||||
Error("Failed opening video stream");
|
||||
delete videowriter;
|
||||
videowriter = NULL;
|
||||
}
|
||||
|
||||
/* Create timecodes file */
|
||||
timecodes_fd = fopen(timecodes_file, "wb");
|
||||
if(timecodes_fd == NULL) {
|
||||
Error("Failed creating timecodes file");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* No video object */
|
||||
videowriter = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Event::~Event()
|
||||
|
@ -181,12 +224,28 @@ Event::~Event()
|
|||
}
|
||||
}
|
||||
|
||||
/* Close the video file */
|
||||
if ( videowriter != NULL ) {
|
||||
int nRet;
|
||||
|
||||
nRet = videowriter->Close();
|
||||
if(nRet != 0) {
|
||||
Error("Failed closing video stream");
|
||||
}
|
||||
delete videowriter;
|
||||
videowriter = NULL;
|
||||
|
||||
/* Close the timecodes file */
|
||||
fclose(timecodes_fd);
|
||||
timecodes_fd = NULL;
|
||||
}
|
||||
|
||||
static char sql[ZM_SQL_MED_BUFSIZ];
|
||||
|
||||
struct DeltaTimeval delta_time;
|
||||
DELTA_TIMEVAL( delta_time, end_time, start_time, DT_PREC_2 );
|
||||
|
||||
snprintf( sql, sizeof(sql), "update Events set Name='%s%d', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d where Id = %d", monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, id );
|
||||
snprintf( sql, sizeof(sql), "update Events set Name='%s%d', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d, DefaultVideo = '%s' where Id = %d", monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, video_name, id );
|
||||
if ( mysql_query( &dbconn, sql ) )
|
||||
{
|
||||
Error( "Can't update event: %s", mysql_error( &dbconn ) );
|
||||
|
@ -369,6 +428,40 @@ bool Event::WriteFrameImage( Image *image, struct timeval timestamp, const char
|
|||
return( true );
|
||||
}
|
||||
|
||||
bool Event::WriteFrameVideo( const Image *image, const struct timeval timestamp, VideoWriter* videow )
|
||||
{
|
||||
const Image* frameimg = image;
|
||||
Image ts_image;
|
||||
|
||||
/* Checking for invalid parameters */
|
||||
if ( videow == NULL ) {
|
||||
Error("NULL Video object");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If the image does not contain a timestamp, add the timestamp */
|
||||
if (!config.timestamp_on_capture) {
|
||||
ts_image = *image;
|
||||
monitor->TimestampImage( &ts_image, ×tamp );
|
||||
frameimg = &ts_image;
|
||||
}
|
||||
|
||||
/* Calculate delta time */
|
||||
struct DeltaTimeval delta_time3;
|
||||
DELTA_TIMEVAL( delta_time3, timestamp, start_time, DT_PREC_3 );
|
||||
unsigned int timeMS = (delta_time3.sec * delta_time3.prec) + delta_time3.fsec;
|
||||
|
||||
/* Encode and write the frame */
|
||||
if(videowriter->Encode(image, timeMS) != 0) {
|
||||
Error("Failed encoding video frame");
|
||||
}
|
||||
|
||||
/* Add the frame to the timecodes file */
|
||||
fprintf(timecodes_fd, "%u\n", timeMS);
|
||||
|
||||
return( true );
|
||||
}
|
||||
|
||||
void Event::updateNotes( const StringSetMap &newNoteSetMap )
|
||||
{
|
||||
bool update = false;
|
||||
|
@ -510,23 +603,27 @@ void Event::AddFramesInternal( int n_frames, int start_frame, Image **images, st
|
|||
}
|
||||
|
||||
frames++;
|
||||
|
||||
|
||||
static char event_file[PATH_MAX];
|
||||
snprintf( event_file, sizeof(event_file), capture_file_format, path, frames );
|
||||
|
||||
if(videoEvent){
|
||||
/* FIXME Comeback and fix either videoEvent or videowriter not both */
|
||||
if(videoEvent){
|
||||
//If this is the first frame, we should add a thumbnail to the event directory
|
||||
if(frames == 10){
|
||||
char snapshot_file[PATH_MAX];
|
||||
snprintf( snapshot_file, sizeof(snapshot_file), "%s/snapshot.jpg", path );
|
||||
WriteFrameImage( images[i], *(timestamps[i]), snapshot_file );
|
||||
}
|
||||
|
||||
}else{
|
||||
Debug( 1, "Writing pre-capture frame %d", frames );
|
||||
WriteFrameImage( images[i], *(timestamps[i]), event_file );
|
||||
Debug( 1, "Writing pre-capture frame %d", frames );
|
||||
if ( monitor->GetOptSaveJPEGs() & 1) {
|
||||
WriteFrameImage( images[i], *(timestamps[i]), event_file );
|
||||
}
|
||||
|
||||
|
||||
if ( videowriter != NULL ) {
|
||||
WriteFrameVideo( images[i], *(timestamps[i]), videowriter );
|
||||
}
|
||||
|
||||
struct DeltaTimeval delta_time;
|
||||
DELTA_TIMEVAL( delta_time, *(timestamps[i]), start_time, DT_PREC_2 );
|
||||
|
||||
|
@ -562,8 +659,7 @@ void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image *
|
|||
}
|
||||
|
||||
frames++;
|
||||
|
||||
|
||||
|
||||
static char event_file[PATH_MAX];
|
||||
snprintf( event_file, sizeof(event_file), capture_file_format, path, frames );
|
||||
|
||||
|
@ -577,9 +673,14 @@ void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image *
|
|||
|
||||
}else{
|
||||
Debug( 1, "Writing capture frame %d", frames );
|
||||
if( monitor->GetOptSaveJPEGs() & 1) {
|
||||
WriteFrameImage( image, timestamp, event_file );
|
||||
}
|
||||
|
||||
if ( videowriter != NULL ) {
|
||||
WriteFrameVideo( image, timestamp, videowriter );
|
||||
}
|
||||
|
||||
struct DeltaTimeval delta_time;
|
||||
DELTA_TIMEVAL( delta_time, timestamp, start_time, DT_PREC_2 );
|
||||
|
||||
|
@ -626,7 +727,9 @@ void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image *
|
|||
snprintf( event_file, sizeof(event_file), analyse_file_format, path, frames );
|
||||
|
||||
Debug( 1, "Writing analysis frame %d", frames );
|
||||
WriteFrameImage( alarm_image, timestamp, event_file, true );
|
||||
if ( monitor->GetOptSaveJPEGs() & 2) {
|
||||
WriteFrameImage( alarm_image, timestamp, event_file, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -674,7 +777,6 @@ void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image *
|
|||
*/
|
||||
}
|
||||
|
||||
|
||||
bool EventStream::loadInitialEventData( int monitor_id, time_t event_time )
|
||||
{
|
||||
static char sql[ZM_SQL_SML_BUFSIZ];
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "zm.h"
|
||||
#include "zm_image.h"
|
||||
#include "zm_stream.h"
|
||||
#include "zm_video.h"
|
||||
|
||||
class Zone;
|
||||
class Monitor;
|
||||
|
@ -55,6 +56,7 @@ protected:
|
|||
static char capture_file_format[PATH_MAX];
|
||||
static char analyse_file_format[PATH_MAX];
|
||||
static char general_file_format[PATH_MAX];
|
||||
static char video_file_format[PATH_MAX];
|
||||
|
||||
protected:
|
||||
static int sd;
|
||||
|
@ -90,6 +92,12 @@ protected:
|
|||
unsigned int tot_score;
|
||||
unsigned int max_score;
|
||||
char path[PATH_MAX];
|
||||
VideoWriter* videowriter;
|
||||
FILE* timecodes_fd;
|
||||
char video_name[PATH_MAX];
|
||||
char video_file[PATH_MAX];
|
||||
char timecodes_name[PATH_MAX];
|
||||
char timecodes_file[PATH_MAX];
|
||||
|
||||
protected:
|
||||
int last_db_frame;
|
||||
|
@ -103,6 +111,7 @@ protected:
|
|||
snprintf( capture_file_format, sizeof(capture_file_format), "%%s/%%0%dd-capture.jpg", config.event_image_digits );
|
||||
snprintf( analyse_file_format, sizeof(analyse_file_format), "%%s/%%0%dd-analyse.jpg", config.event_image_digits );
|
||||
snprintf( general_file_format, sizeof(general_file_format), "%%s/%%0%dd-%%s", config.event_image_digits );
|
||||
snprintf( video_file_format, sizeof(video_file_format), "%%s/%%s");
|
||||
|
||||
initialised = true;
|
||||
}
|
||||
|
@ -128,6 +137,7 @@ public:
|
|||
|
||||
bool SendFrameImage( const Image *image, bool alarm_frame=false );
|
||||
bool WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame=false );
|
||||
bool WriteFrameVideo( const Image *image, const struct timeval timestamp, VideoWriter* videow );
|
||||
|
||||
void updateNotes( const StringSetMap &stringSetMap );
|
||||
|
||||
|
|
|
@ -23,6 +23,16 @@
|
|||
|
||||
#if HAVE_LIBAVCODEC || HAVE_LIBAVUTIL || HAVE_LIBSWSCALE
|
||||
|
||||
void FFMPEGInit() {
|
||||
static bool bInit = false;
|
||||
|
||||
if(!bInit) {
|
||||
av_register_all();
|
||||
av_log_set_level(AV_LOG_DEBUG);
|
||||
bInit = true;
|
||||
}
|
||||
}
|
||||
|
||||
#if HAVE_LIBAVUTIL
|
||||
enum PixelFormat GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixelorder) {
|
||||
enum PixelFormat pf;
|
||||
|
@ -124,10 +134,7 @@ int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint
|
|||
Error("NULL Input or output buffer");
|
||||
return -1;
|
||||
}
|
||||
if(in_pf == 0 || out_pf == 0) {
|
||||
Error("Invalid input or output pixel formats");
|
||||
return -2;
|
||||
}
|
||||
|
||||
if(!width || !height) {
|
||||
Error("Invalid width or height");
|
||||
return -3;
|
||||
|
@ -156,7 +163,7 @@ int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint
|
|||
}
|
||||
|
||||
/* Get the context */
|
||||
swscale_ctx = sws_getCachedContext( NULL, width, height, in_pf, width, height, out_pf, 0, NULL, NULL, NULL );
|
||||
swscale_ctx = sws_getCachedContext( swscale_ctx, width, height, in_pf, width, height, out_pf, SWS_FAST_BILINEAR, NULL, NULL, NULL );
|
||||
if(swscale_ctx == NULL) {
|
||||
Error("Failed getting swscale context");
|
||||
return -6;
|
||||
|
|
|
@ -99,6 +99,8 @@ extern "C" {
|
|||
#define SWS_CPU_CAPS_SSE2 0x02000000
|
||||
#endif
|
||||
|
||||
/* A single function to initialize ffmpeg, to avoid multiple initializations */
|
||||
void FFMPEGInit();
|
||||
|
||||
#if HAVE_LIBAVUTIL
|
||||
enum PixelFormat GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixelorder);
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "zm_mpeg.h"
|
||||
#include "zm_signal.h"
|
||||
#include "zm_monitor.h"
|
||||
#include "zm_video.h"
|
||||
#if ZM_HAS_V4L
|
||||
#include "zm_local_camera.h"
|
||||
#endif // ZM_HAS_V4L
|
||||
|
@ -265,6 +266,9 @@ Monitor::Monitor(
|
|||
Camera *p_camera,
|
||||
int p_orientation,
|
||||
unsigned int p_deinterlacing,
|
||||
int p_savejpegs,
|
||||
int p_videowriter,
|
||||
std::string p_encoderparams,
|
||||
const char *p_event_prefix,
|
||||
const char *p_label_format,
|
||||
const Coord &p_label_coord,
|
||||
|
@ -294,6 +298,9 @@ Monitor::Monitor(
|
|||
height( (p_orientation==ROTATE_90||p_orientation==ROTATE_270)?p_camera->Width():p_camera->Height() ),
|
||||
orientation( (Orientation)p_orientation ),
|
||||
deinterlacing( p_deinterlacing ),
|
||||
savejpegspref( p_savejpegs ),
|
||||
videowriterpref( p_videowriter ),
|
||||
encoderparams( p_encoderparams ),
|
||||
label_coord( p_label_coord ),
|
||||
image_buffer_count( p_image_buffer_count ),
|
||||
warmup_count( p_warmup_count ),
|
||||
|
@ -344,6 +351,9 @@ Monitor::Monitor(
|
|||
}
|
||||
}
|
||||
|
||||
/* Parse encoder parameters */
|
||||
ParseEncoderParameters(encoderparams.c_str(), &encoderparamsvec);
|
||||
|
||||
fps = 0.0;
|
||||
event_count = 0;
|
||||
image_count = 0;
|
||||
|
@ -1870,11 +1880,11 @@ int Monitor::LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose
|
|||
static char sql[ZM_SQL_MED_BUFSIZ];
|
||||
if ( !device[0] )
|
||||
{
|
||||
strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Method, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Function != 'None' and Type = 'Local' order by Device, Channel", sizeof(sql) );
|
||||
strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Method, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Function != 'None' and Type = 'Local' order by Device, Channel", sizeof(sql) );
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Method, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Function != 'None' and Type = 'Local' and Device = '%s' order by Channel", device );
|
||||
snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Method, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Function != 'None' and Type = 'Local' and Device = '%s' order by Channel", device );
|
||||
}
|
||||
if ( mysql_query( &dbconn, sql ) )
|
||||
{
|
||||
|
@ -1933,6 +1943,11 @@ Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame );
|
|||
int palette = atoi(dbrow[col]); col++;
|
||||
Orientation orientation = (Orientation)atoi(dbrow[col]); col++;
|
||||
unsigned int deinterlacing = atoi(dbrow[col]); col++;
|
||||
|
||||
int savejpegs = atoi(dbrow[col]); col++;
|
||||
int videowriter = atoi(dbrow[col]); col++;
|
||||
std::string encoderparams = dbrow[col]; col++;
|
||||
|
||||
int brightness = atoi(dbrow[col]); col++;
|
||||
int contrast = atoi(dbrow[col]); col++;
|
||||
int hue = atoi(dbrow[col]); col++;
|
||||
|
@ -2001,6 +2016,9 @@ Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame );
|
|||
camera,
|
||||
orientation,
|
||||
deinterlacing,
|
||||
savejpegs,
|
||||
videowriter,
|
||||
encoderparams,
|
||||
event_prefix,
|
||||
label_format,
|
||||
Coord( label_x, label_y ),
|
||||
|
@ -2046,11 +2064,11 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c
|
|||
static char sql[ZM_SQL_MED_BUFSIZ];
|
||||
if ( !protocol )
|
||||
{
|
||||
strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Remote'", sizeof(sql) );
|
||||
strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Remote'", sizeof(sql) );
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Remote' and Protocol = '%s' and Host = '%s' and Port = '%s' and Path = '%s'", protocol, host, port, path );
|
||||
snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Remote' and Protocol = '%s' and Host = '%s' and Port = '%s' and Path = '%s'", protocol, host, port, path );
|
||||
}
|
||||
if ( mysql_query( &dbconn, sql ) )
|
||||
{
|
||||
|
@ -2089,7 +2107,12 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c
|
|||
int colours = atoi(dbrow[col]); col++;
|
||||
/* int palette = atoi(dbrow[col]); */ col++;
|
||||
Orientation orientation = (Orientation)atoi(dbrow[col]); col++;
|
||||
unsigned int deinterlacing = atoi(dbrow[col]); col++;
|
||||
unsigned int deinterlacing = atoi(dbrow[col]); col++;
|
||||
|
||||
int savejpegs = atoi(dbrow[col]); col++;
|
||||
int videowriter = atoi(dbrow[col]); col++;
|
||||
std::string encoderparams = dbrow[col]; col++;
|
||||
|
||||
int brightness = atoi(dbrow[col]); col++;
|
||||
int contrast = atoi(dbrow[col]); col++;
|
||||
int hue = atoi(dbrow[col]); col++;
|
||||
|
@ -2174,6 +2197,9 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c
|
|||
camera,
|
||||
orientation,
|
||||
deinterlacing,
|
||||
savejpegs,
|
||||
videowriter,
|
||||
encoderparams,
|
||||
event_prefix.c_str(),
|
||||
label_format.c_str(),
|
||||
Coord( label_x, label_y ),
|
||||
|
@ -2219,11 +2245,11 @@ int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose pu
|
|||
static char sql[ZM_SQL_MED_BUFSIZ];
|
||||
if ( !file[0] )
|
||||
{
|
||||
strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'File'", sizeof(sql) );
|
||||
strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'File'", sizeof(sql) );
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'File' and Path = '%s'", file );
|
||||
snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'File' and Path = '%s'", file );
|
||||
}
|
||||
if ( mysql_query( &dbconn, sql ) )
|
||||
{
|
||||
|
@ -2259,6 +2285,11 @@ int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose pu
|
|||
/* int palette = atoi(dbrow[col]); */ col++;
|
||||
Orientation orientation = (Orientation)atoi(dbrow[col]); col++;
|
||||
unsigned int deinterlacing = atoi(dbrow[col]); col++;
|
||||
|
||||
int savejpegs = atoi(dbrow[col]); col++;
|
||||
int videowriter = atoi(dbrow[col]); col++;
|
||||
std::string encoderparams = dbrow[col]; col++;
|
||||
|
||||
int brightness = atoi(dbrow[col]); col++;
|
||||
int contrast = atoi(dbrow[col]); col++;
|
||||
int hue = atoi(dbrow[col]); col++;
|
||||
|
@ -2311,6 +2342,9 @@ int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose pu
|
|||
camera,
|
||||
orientation,
|
||||
deinterlacing,
|
||||
savejpegs,
|
||||
videowriter,
|
||||
encoderparams,
|
||||
event_prefix,
|
||||
label_format,
|
||||
Coord( label_x, label_y ),
|
||||
|
@ -2356,11 +2390,11 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose
|
|||
static char sql[ZM_SQL_MED_BUFSIZ];
|
||||
if ( !file[0] )
|
||||
{
|
||||
strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Method, Options, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Ffmpeg'", sizeof(sql) );
|
||||
strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Method, Options, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Ffmpeg'", sizeof(sql) );
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Method, Options, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Ffmpeg' and Path = '%s'", file );
|
||||
snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Method, Options, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Ffmpeg' and Path = '%s'", file );
|
||||
}
|
||||
if ( mysql_query( &dbconn, sql ) )
|
||||
{
|
||||
|
@ -2398,6 +2432,11 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose
|
|||
/* int palette = atoi(dbrow[col]); */ col++;
|
||||
Orientation orientation = (Orientation)atoi(dbrow[col]); col++;
|
||||
unsigned int deinterlacing = atoi(dbrow[col]); col++;
|
||||
|
||||
int savejpegs = atoi(dbrow[col]); col++;
|
||||
int videowriter = atoi(dbrow[col]); col++;
|
||||
std::string encoderparams = dbrow[col]; col++;
|
||||
|
||||
int brightness = atoi(dbrow[col]); col++;
|
||||
int contrast = atoi(dbrow[col]); col++;
|
||||
int hue = atoi(dbrow[col]); col++;
|
||||
|
@ -2452,6 +2491,9 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose
|
|||
camera,
|
||||
orientation,
|
||||
deinterlacing,
|
||||
savejpegs,
|
||||
videowriter,
|
||||
encoderparams,
|
||||
event_prefix,
|
||||
label_format,
|
||||
Coord( label_x, label_y ),
|
||||
|
@ -2495,7 +2537,7 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose
|
|||
Monitor *Monitor::Load( int id, bool load_zones, Purpose purpose )
|
||||
{
|
||||
static char sql[ZM_SQL_MED_BUFSIZ];
|
||||
snprintf( sql, sizeof(sql), "select Id, Name, Type, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Protocol, Method, Host, Port, Path, Options, User, Pass, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Id = %d", id );
|
||||
snprintf( sql, sizeof(sql), "select Id, Name, Type, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Protocol, Method, Host, Port, Path, Options, User, Pass, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Id = %d", id );
|
||||
if ( mysql_query( &dbconn, sql ) )
|
||||
{
|
||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||
|
@ -2562,6 +2604,11 @@ Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame );
|
|||
int palette = atoi(dbrow[col]); col++;
|
||||
Orientation orientation = (Orientation)atoi(dbrow[col]); col++;
|
||||
unsigned int deinterlacing = atoi(dbrow[col]); col++;
|
||||
|
||||
int savejpegs = atoi(dbrow[col]); col++;
|
||||
int videowriter = atoi(dbrow[col]); col++;
|
||||
std::string encoderparams = dbrow[col]; col++;
|
||||
|
||||
int brightness = atoi(dbrow[col]); col++;
|
||||
int contrast = atoi(dbrow[col]); col++;
|
||||
int hue = atoi(dbrow[col]); col++;
|
||||
|
@ -2765,6 +2812,9 @@ Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame );
|
|||
camera,
|
||||
orientation,
|
||||
deinterlacing,
|
||||
savejpegs,
|
||||
videowriter,
|
||||
encoderparams,
|
||||
event_prefix.c_str(),
|
||||
label_format.c_str(),
|
||||
Coord( label_x, label_y ),
|
||||
|
|
|
@ -234,6 +234,12 @@ protected:
|
|||
unsigned int v4l_captures_per_frame;
|
||||
Orientation orientation; // Whether the image has to be rotated at all
|
||||
unsigned int deinterlacing;
|
||||
|
||||
int savejpegspref;
|
||||
int videowriterpref;
|
||||
std::string encoderparams;
|
||||
std::vector<EncoderParameter_t> encoderparamsvec;
|
||||
|
||||
int brightness; // The statically saved brightness of the camera
|
||||
int contrast; // The statically saved contrast of the camera
|
||||
int hue; // The statically saved hue of the camera
|
||||
|
@ -312,7 +318,7 @@ protected:
|
|||
public:
|
||||
// OurCheckAlarms seems to be unused. Check it on zm_monitor.cpp for more info.
|
||||
//bool OurCheckAlarms( Zone *zone, const Image *pImage );
|
||||
Monitor( int p_id, const char *p_name, int p_function, bool p_enabled, const char *p_linked_monitors, Camera *p_camera, int p_orientation, unsigned int p_deinterlacing, const char *p_event_prefix, const char *p_label_format, const Coord &p_label_coord, int p_image_buffer_count, int p_warmup_count, int p_pre_event_count, int p_post_event_count, int p_stream_replay_buffer, int p_alarm_frame_count, int p_section_length, int p_frame_skip, int p_motion_frame_skip, int p_capture_delay, int p_alarm_capture_delay, int p_fps_report_interval, int p_ref_blend_perc, int p_alarm_ref_blend_perc, bool p_track_motion, Rgb p_signal_check_colour, Purpose p_purpose, int p_n_zones=0, Zone *p_zones[]=0 );
|
||||
Monitor( int p_id, const char *p_name, int p_function, bool p_enabled, const char *p_linked_monitors, Camera *p_camera, int p_orientation, unsigned int p_deinterlacing, int p_savejpegs, int p_videowriter, std::string p_encoderparams, const char *p_event_prefix, const char *p_label_format, const Coord &p_label_coord, int p_image_buffer_count, int p_warmup_count, int p_pre_event_count, int p_post_event_count, int p_stream_replay_buffer, int p_alarm_frame_count, int p_section_length, int p_frame_skip, int p_motion_frame_skip, int p_capture_delay, int p_alarm_capture_delay, int p_fps_report_interval, int p_ref_blend_perc, int p_alarm_ref_blend_perc, bool p_track_motion, Rgb p_signal_check_colour, Purpose p_purpose, int p_n_zones=0, Zone *p_zones[]=0 );
|
||||
~Monitor();
|
||||
|
||||
void AddZones( int p_n_zones, Zone *p_zones[] );
|
||||
|
@ -362,7 +368,10 @@ public:
|
|||
unsigned int Height() const { return( height ); }
|
||||
unsigned int Colours() const { return( camera->Colours() ); }
|
||||
unsigned int SubpixelOrder() const { return( camera->SubpixelOrder() ); }
|
||||
|
||||
|
||||
int GetOptSaveJPEGs() const { return( savejpegspref ); }
|
||||
int GetOptVideoWriter() const { return( videowriterpref ); }
|
||||
const std::vector<EncoderParameter_t>* GetOptEncoderParams() const { return( &encoderparamsvec ); }
|
||||
|
||||
State GetState() const;
|
||||
int GetImage( int index=-1, int scale=100 ) const;
|
||||
|
|
|
@ -0,0 +1,518 @@
|
|||
// 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
//
|
||||
#include "zm.h"
|
||||
#include "zm_video.h"
|
||||
#include "zm_image.h"
|
||||
#include "zm_utils.h"
|
||||
#include "zm_rgb.h"
|
||||
#include <sstream>
|
||||
|
||||
VideoWriter::VideoWriter(const char* p_container, const char* p_codec, const char* p_path, const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder) :
|
||||
container(p_container), codec(p_codec), path(p_path), width(p_width), height(p_height), colours(p_colours), subpixelorder(p_subpixelorder), frame_count(0) {
|
||||
Debug(7,"Video object created");
|
||||
|
||||
/* Parameter checking */
|
||||
if(path.empty()) {
|
||||
Error("Invalid file path");
|
||||
}
|
||||
if(!width || !height) {
|
||||
Error("Invalid width or height");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
VideoWriter::~VideoWriter() {
|
||||
Debug(7,"Video object destroyed");
|
||||
|
||||
}
|
||||
|
||||
int VideoWriter::Reset(const char* new_path) {
|
||||
/* Common variables reset */
|
||||
|
||||
/* If there is a new path, use it */
|
||||
if(new_path != NULL) {
|
||||
path = new_path;
|
||||
}
|
||||
|
||||
/* Reset frame counter */
|
||||
frame_count = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#if ZM_HAVE_VIDEOWRITER_X264MP4
|
||||
X264MP4Writer::X264MP4Writer(const char* p_path, const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder, const std::vector<EncoderParameter_t>* p_user_params) : VideoWriter("mp4", "h264", p_path, p_width, p_height, p_colours, p_subpixelorder), bOpen(false), bGotH264AVCInfo(false), bFirstFrame(true) {
|
||||
|
||||
/* Initialize ffmpeg if it hasn't been initialized yet */
|
||||
FFMPEGInit();
|
||||
|
||||
/* Initialize swscale */
|
||||
zm_pf = GetFFMPEGPixelFormat(colours,subpixelorder);
|
||||
if(zm_pf == 0) {
|
||||
Error("Unable to match ffmpeg pixelformat");
|
||||
}
|
||||
codec_pf = PIX_FMT_YUV420P;
|
||||
|
||||
swscaleobj.SetDefaults(zm_pf, codec_pf, width, height);
|
||||
|
||||
/* Calculate the image sizes. We will need this for parameter checking */
|
||||
zm_imgsize = colours * width * height;
|
||||
codec_imgsize = avpicture_get_size( codec_pf, width, height);
|
||||
if(!codec_imgsize) {
|
||||
Error("Failed calculating codec pixel format image size");
|
||||
}
|
||||
|
||||
/* If supplied with user parameters to the encoder, copy them */
|
||||
if(p_user_params != NULL) {
|
||||
user_params = *p_user_params;
|
||||
}
|
||||
|
||||
/* Setup x264 parameters */
|
||||
if(x264config() < 0) {
|
||||
Error("Failed setting x264 parameters");
|
||||
}
|
||||
|
||||
/* Allocate x264 input picture */
|
||||
x264_picture_alloc(&x264picin, X264_CSP_I420, x264params.i_width, x264params.i_height);
|
||||
|
||||
}
|
||||
|
||||
X264MP4Writer::~X264MP4Writer() {
|
||||
|
||||
/* Free x264 input picture */
|
||||
x264_picture_clean(&x264picin);
|
||||
|
||||
if(bOpen)
|
||||
Close();
|
||||
|
||||
}
|
||||
|
||||
int X264MP4Writer::Open() {
|
||||
|
||||
/* Open the encoder */
|
||||
x264enc = x264_encoder_open(&x264params);
|
||||
if(x264enc == NULL) {
|
||||
Error("Failed opening x264 encoder");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Debug(4,"x264 maximum delayed frames: %d",x264_encoder_maximum_delayed_frames(x264enc));
|
||||
|
||||
x264_nal_t* nals;
|
||||
int i_nals;
|
||||
if(!x264_encoder_headers(x264enc,&nals,&i_nals)) {
|
||||
Error("Failed getting encoder headers");
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Search SPS NAL for AVC information */
|
||||
for(int i=0;i<i_nals;i++) {
|
||||
if(nals[i].i_type == NAL_SPS) {
|
||||
x264_profleindication = nals[i].p_payload[5];
|
||||
x264_profilecompat = nals[i].p_payload[6];
|
||||
x264_levelindication = nals[i].p_payload[7];
|
||||
bGotH264AVCInfo = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!bGotH264AVCInfo) {
|
||||
Warning("Missing AVC information");
|
||||
}
|
||||
|
||||
/* Create the file */
|
||||
mp4h = MP4Create((path + ".incomplete").c_str());
|
||||
if(mp4h == MP4_INVALID_FILE_HANDLE) {
|
||||
Error("Failed creating mp4 file: %s",path.c_str());
|
||||
return -10;
|
||||
}
|
||||
|
||||
/* Set the global timescale */
|
||||
if(!MP4SetTimeScale(mp4h, 1000)) {
|
||||
Error("Failed setting timescale");
|
||||
return -11;
|
||||
}
|
||||
|
||||
/* Set the global video profile */
|
||||
/* I am a bit confused about this one.
|
||||
I couldn't find what the value should be
|
||||
Some use 0x15 while others use 0x7f. */
|
||||
MP4SetVideoProfileLevel(mp4h, 0x7f);
|
||||
|
||||
/* Add H264 video track */
|
||||
mp4vtid = MP4AddH264VideoTrack(mp4h,1000,MP4_INVALID_DURATION,width,height,x264_profleindication,x264_profilecompat,x264_levelindication,3);
|
||||
if(mp4vtid == MP4_INVALID_TRACK_ID) {
|
||||
Error("Failed adding H264 video track");
|
||||
return -12;
|
||||
}
|
||||
|
||||
bOpen = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int X264MP4Writer::Close() {
|
||||
|
||||
/* Flush all pending frames */
|
||||
for(int i = (x264_encoder_delayed_frames(x264enc) + 1); i > 0; i-- ) {
|
||||
x264encodeloop(true);
|
||||
}
|
||||
|
||||
/* Close the encoder */
|
||||
x264_encoder_close(x264enc);
|
||||
|
||||
/* Close MP4 handle */
|
||||
MP4Close(mp4h);
|
||||
|
||||
/* Required for proper HTTP streaming */
|
||||
MP4Optimize((path + ".incomplete").c_str(), path.c_str());
|
||||
|
||||
/* Delete the temporary file */
|
||||
unlink((path + ".incomplete").c_str());
|
||||
|
||||
bOpen = false;
|
||||
|
||||
Debug(7, "Video closed. Total frames: %d", frame_count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int X264MP4Writer::Reset(const char* new_path) {
|
||||
|
||||
/* Close the encoder and file */
|
||||
if(bOpen)
|
||||
Close();
|
||||
|
||||
/* Reset common variables */
|
||||
VideoWriter::Reset(new_path);
|
||||
|
||||
/* Reset local variables */
|
||||
bFirstFrame = true;
|
||||
bGotH264AVCInfo = false;
|
||||
prevnals.clear();
|
||||
prevpayload.clear();
|
||||
|
||||
/* Reset x264 parameters */
|
||||
x264config();
|
||||
|
||||
/* Open the encoder */
|
||||
Open();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int X264MP4Writer::Encode(const uint8_t* data, const size_t data_size, const unsigned int frame_time) {
|
||||
|
||||
/* Parameter checking */
|
||||
if(data == NULL) {
|
||||
Error("NULL buffer");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(data_size != zm_imgsize) {
|
||||
Error("The data buffer size does not match the expected size. Expected: %d Current: %d", zm_imgsize, data_size);
|
||||
return -2;
|
||||
}
|
||||
|
||||
if(!bOpen) {
|
||||
Warning("The encoder was not initialized, initializing now");
|
||||
Open();
|
||||
}
|
||||
|
||||
/* Convert the image into the x264 input picture */
|
||||
if(swscaleobj.ConvertDefaults(data, data_size, x264picin.img.plane[0], codec_imgsize) < 0) {
|
||||
Error("Image conversion failed");
|
||||
return -3;
|
||||
}
|
||||
|
||||
/* Set PTS */
|
||||
x264picin.i_pts = frame_time;
|
||||
|
||||
/* Do the encoding */
|
||||
x264encodeloop();
|
||||
|
||||
/* Increment frame counter */
|
||||
frame_count++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int X264MP4Writer::Encode(const Image* img, const unsigned int frame_time) {
|
||||
|
||||
if(img->Width() != width) {
|
||||
Error("Source image width differs. Source: %d Output: %d",img->Width(), width);
|
||||
return -12;
|
||||
}
|
||||
|
||||
if(img->Height() != height) {
|
||||
Error("Source image height differs. Source: %d Output: %d",img->Height(), height);
|
||||
return -13;
|
||||
}
|
||||
|
||||
return Encode(img->Buffer(),img->Size(),frame_time);
|
||||
}
|
||||
|
||||
int X264MP4Writer::x264config() {
|
||||
/* Sets up the encoder configuration */
|
||||
|
||||
int x264ret;
|
||||
|
||||
/* Defaults */
|
||||
const char* preset = "veryfast";
|
||||
const char* tune = "stillimage";
|
||||
const char* profile = "main";
|
||||
|
||||
/* Search the user parameters for preset, tune and profile */
|
||||
for(unsigned int i=0; i < user_params.size(); i++) {
|
||||
if(strcmp(user_params[i].pname, "preset") == 0) {
|
||||
/* Got preset */
|
||||
preset = user_params[i].pvalue;
|
||||
} else if(strcmp(user_params[i].pname, "tune") == 0) {
|
||||
/* Got tune */
|
||||
tune = user_params[i].pvalue;
|
||||
} else if(strcmp(user_params[i].pname, "profile") == 0) {
|
||||
/* Got profile */
|
||||
profile = user_params[i].pvalue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the defaults and preset and tune */
|
||||
x264ret = x264_param_default_preset(&x264params, preset, tune);
|
||||
if(x264ret != 0) {
|
||||
Error("Failed setting x264 preset %s and tune %s : %d",preset,tune,x264ret);
|
||||
}
|
||||
|
||||
/* Set the profile */
|
||||
x264ret = x264_param_apply_profile(&x264params, profile);
|
||||
if(x264ret != 0) {
|
||||
Error("Failed setting x264 profile %s : %d",profile,x264ret);
|
||||
}
|
||||
|
||||
/* Input format */
|
||||
x264params.i_width = width;
|
||||
x264params.i_height = height;
|
||||
x264params.i_csp = X264_CSP_I420;
|
||||
|
||||
/* Quality control */
|
||||
x264params.rc.i_rc_method = X264_RC_CRF;
|
||||
x264params.rc.f_rf_constant = 23.0;
|
||||
|
||||
/* Enable b-frames */
|
||||
x264params.i_bframe = 16;
|
||||
x264params.i_bframe_adaptive = 1;
|
||||
|
||||
/* Timebase */
|
||||
x264params.i_timebase_num = 1;
|
||||
x264params.i_timebase_den = 1000;
|
||||
|
||||
/* Enable variable frame rate */
|
||||
x264params.b_vfr_input = 1;
|
||||
|
||||
/* Disable annex-b (start codes) */
|
||||
x264params.b_annexb = 0;
|
||||
|
||||
/* TODO: Setup error handler */
|
||||
//x264params.i_log_level = X264_LOG_DEBUG;
|
||||
|
||||
/* Process user parameters (excluding preset, tune and profile) */
|
||||
for(unsigned int i=0; i < user_params.size(); i++) {
|
||||
/* Skip preset, tune and profile */
|
||||
if( (strcmp(user_params[i].pname, "preset") == 0) || (strcmp(user_params[i].pname, "tune") == 0) || (strcmp(user_params[i].pname, "profile") == 0) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Pass the name and value to x264 */
|
||||
x264ret = x264_param_parse(&x264params, user_params[i].pname, user_params[i].pvalue);
|
||||
|
||||
/* Error checking */
|
||||
if(x264ret != 0) {
|
||||
if(x264ret == X264_PARAM_BAD_NAME) {
|
||||
Error("Failed processing x264 user parameter %s=%s : Bad name", user_params[i].pname, user_params[i].pvalue);
|
||||
} else if(x264ret == X264_PARAM_BAD_VALUE) {
|
||||
Error("Failed processing x264 user parameter %s=%s : Bad value", user_params[i].pname, user_params[i].pvalue);
|
||||
} else {
|
||||
Error("Failed processing x264 user parameter %s=%s : Unknown error (%d)", user_params[i].pname, user_params[i].pvalue, x264ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void X264MP4Writer::x264encodeloop(bool bFlush) {
|
||||
|
||||
x264_nal_t* nals;
|
||||
int i_nals;
|
||||
int frame_size;
|
||||
|
||||
if(bFlush) {
|
||||
frame_size = x264_encoder_encode(x264enc, &nals, &i_nals, NULL, &x264picout);
|
||||
} else {
|
||||
frame_size = x264_encoder_encode(x264enc, &nals, &i_nals, &x264picin, &x264picout);
|
||||
}
|
||||
|
||||
if (frame_size > 0 || bFlush) {
|
||||
Debug(8, "x264 Frame: %d PTS: %d DTS: %d Size: %d\n",frame_count, x264picout.i_pts, x264picout.i_dts, frame_size);
|
||||
|
||||
/* Handle the previous frame */
|
||||
if(!bFirstFrame) {
|
||||
|
||||
buffer.clear();
|
||||
|
||||
/* Process the NALs for the previous frame */
|
||||
for(unsigned int i=0; i < prevnals.size(); i++) {
|
||||
Debug(9,"Processing NAL: Type %d Size %d",prevnals[i].i_type,prevnals[i].i_payload);
|
||||
|
||||
switch(prevnals[i].i_type) {
|
||||
case NAL_PPS:
|
||||
/* PPS NAL */
|
||||
MP4AddH264PictureParameterSet(mp4h, mp4vtid, prevnals[i].p_payload+4, prevnals[i].i_payload-4);
|
||||
break;
|
||||
case NAL_SPS:
|
||||
/* SPS NAL */
|
||||
MP4AddH264SequenceParameterSet(mp4h, mp4vtid, prevnals[i].p_payload+4, prevnals[i].i_payload-4);
|
||||
break;
|
||||
default:
|
||||
/* Anything else, hopefully frames, so copy it into the sample */
|
||||
buffer.append(prevnals[i].p_payload, prevnals[i].i_payload);
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate frame duration and offset */
|
||||
int duration = x264picout.i_dts - prevDTS;
|
||||
int offset = prevPTS - prevDTS;
|
||||
|
||||
/* Write the sample */
|
||||
if(!buffer.empty()) {
|
||||
if(!MP4WriteSample(mp4h, mp4vtid, buffer.extract(buffer.size()), buffer.size(), duration, offset, prevKeyframe)) {
|
||||
Error("Failed writing sample");
|
||||
}
|
||||
}
|
||||
|
||||
/* Cleanup */
|
||||
prevnals.clear();
|
||||
prevpayload.clear();
|
||||
|
||||
}
|
||||
|
||||
/* Got a frame. Copy this new frame into the previous frame */
|
||||
if(frame_size > 0) {
|
||||
/* Copy the NALs and the payloads */
|
||||
for(int i=0;i<i_nals;i++) {
|
||||
|
||||
prevnals.push_back(nals[i]);
|
||||
prevpayload.append(nals[i].p_payload, nals[i].i_payload);
|
||||
}
|
||||
|
||||
/* Update the payload pointers */
|
||||
/* This is done in a separate loop because the previous loop might reallocate memory when appending,
|
||||
making the pointers invalid */
|
||||
unsigned int payload_offset = 0;
|
||||
for(unsigned int i=0;i<prevnals.size();i++) {
|
||||
prevnals[i].p_payload = prevpayload.head() + payload_offset;
|
||||
payload_offset += nals[i].i_payload;
|
||||
}
|
||||
|
||||
/* We need this for the next frame */
|
||||
prevPTS = x264picout.i_pts;
|
||||
prevDTS = x264picout.i_dts;
|
||||
prevKeyframe = x264picout.b_keyframe;
|
||||
|
||||
bFirstFrame = false;
|
||||
}
|
||||
|
||||
} else if(frame_size == 0) {
|
||||
Debug(7,"x264 encode returned zero. Delayed frames: %d",x264_encoder_delayed_frames(x264enc));
|
||||
} else {
|
||||
Error("x264 encode failed: %d",frame_size);
|
||||
}
|
||||
}
|
||||
#endif // ZM_VIDEOWRITER_X264MP4
|
||||
|
||||
int ParseEncoderParameters(const char* str, std::vector<EncoderParameter_t>* vec) {
|
||||
if(vec == NULL) {
|
||||
Error("NULL Encoder parameters vector pointer");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(str == NULL) {
|
||||
Error("NULL Encoder parameters string");
|
||||
return -2;
|
||||
}
|
||||
|
||||
vec->clear();
|
||||
|
||||
if(str[0] == 0) {
|
||||
/* Empty */
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
std::stringstream ss(str);
|
||||
size_t valueoffset;
|
||||
size_t valuelen;
|
||||
unsigned int lineno = 0;
|
||||
EncoderParameter_t param;
|
||||
|
||||
while(std::getline(ss, line) ) {
|
||||
lineno++;
|
||||
|
||||
/* Remove CR if exists */
|
||||
if(line.length() >= 1 && line[line.length()-1] == '\r') {
|
||||
line.erase(line.length()-1);
|
||||
}
|
||||
|
||||
/* Skip comments and empty lines */
|
||||
if(line.empty() || line[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
valueoffset = line.find('=');
|
||||
if(valueoffset == std::string::npos || valueoffset+1 >= line.length() || valueoffset == 0) {
|
||||
Warning("Failed parsing encoder parameters line %d: Invalid pair", lineno);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if(valueoffset > (sizeof(param.pname)-1) ) {
|
||||
Warning("Failed parsing encoder parameters line %d: Name too long", lineno);
|
||||
continue;
|
||||
}
|
||||
|
||||
valuelen = line.length() - (valueoffset+1);
|
||||
|
||||
if( valuelen > (sizeof(param.pvalue)-1) ) {
|
||||
Warning("Failed parsing encoder parameters line %d: Value too long", lineno);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Copy and NULL terminate */
|
||||
line.copy(param.pname, valueoffset, 0);
|
||||
line.copy(param.pvalue, valuelen, valueoffset+1);
|
||||
param.pname[valueoffset] = 0;
|
||||
param.pvalue[valuelen] = 0;
|
||||
|
||||
/* Push to the vector */
|
||||
vec->push_back(param);
|
||||
|
||||
Debug(7, "Parsed encoder parameter: %s = %s", param.pname, param.pvalue);
|
||||
}
|
||||
|
||||
Debug(7, "Parsed %d lines", lineno);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
// 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
//
|
||||
|
||||
#ifndef ZM_VIDEO_H
|
||||
#define ZM_VIDEO_H
|
||||
|
||||
#include "zm.h"
|
||||
#include "zm_rgb.h"
|
||||
#include "zm_utils.h"
|
||||
#include "zm_ffmpeg.h"
|
||||
#include "zm_buffer.h"
|
||||
|
||||
/*
|
||||
#define HAVE_LIBX264 1
|
||||
#define HAVE_LIBMP4V2 1
|
||||
#define HAVE_X264_H 1
|
||||
#define HAVE_MP4_H 1
|
||||
*/
|
||||
|
||||
#if HAVE_MP4V2_MP4V2_H
|
||||
#include <mp4v2/mp4v2.h>
|
||||
#endif
|
||||
#if HAVE_MP4V2_H
|
||||
#include <mp4v2.h>
|
||||
#endif
|
||||
#if HAVE_MP4_H
|
||||
#include <mp4.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_X264_H
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <x264.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Structure for user parameters to the encoder */
|
||||
struct EncoderParameter_t {
|
||||
char pname[48];
|
||||
char pvalue[48];
|
||||
|
||||
};
|
||||
int ParseEncoderParameters(const char* str, std::vector<EncoderParameter_t>* vec);
|
||||
|
||||
/* VideoWriter is a generic interface that ZM uses to save events as videos */
|
||||
/* It is relatively simple and the functions are pure virtual, so they must be implemented by the deriving class */
|
||||
|
||||
class VideoWriter {
|
||||
|
||||
protected:
|
||||
std::string container;
|
||||
std::string codec;
|
||||
std::string path;
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
unsigned int colours;
|
||||
unsigned int subpixelorder;
|
||||
|
||||
unsigned int frame_count;
|
||||
|
||||
public:
|
||||
VideoWriter(const char* p_container, const char* p_codec, const char* p_path, const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder);
|
||||
virtual ~VideoWriter();
|
||||
virtual int Encode(const uint8_t* data, const size_t data_size, const unsigned int frame_time) = 0;
|
||||
virtual int Encode(const Image* img, const unsigned int frame_time) = 0;
|
||||
virtual int Open() = 0;
|
||||
virtual int Close() = 0;
|
||||
virtual int Reset(const char* new_path = NULL);
|
||||
|
||||
const char* GetContainer() const {
|
||||
return container.c_str();
|
||||
}
|
||||
const char* GetCodec() const {
|
||||
return codec.c_str();
|
||||
}
|
||||
const char* GetPath() const {
|
||||
return path.c_str();
|
||||
}
|
||||
unsigned int GetWidth() const {
|
||||
return width;
|
||||
}
|
||||
unsigned int GetHeight() const {
|
||||
return height;
|
||||
}
|
||||
unsigned int GetColours() const {
|
||||
return colours;
|
||||
}
|
||||
unsigned int GetSubpixelorder () const {
|
||||
return subpixelorder;
|
||||
}
|
||||
unsigned int GetFrameCount() const {
|
||||
return frame_count;
|
||||
}
|
||||
};
|
||||
|
||||
#if HAVE_LIBX264 && HAVE_LIBMP4V2 && HAVE_LIBAVUTIL && HAVE_LIBSWSCALE
|
||||
#define ZM_HAVE_VIDEOWRITER_X264MP4 1
|
||||
class X264MP4Writer : public VideoWriter {
|
||||
|
||||
protected:
|
||||
|
||||
bool bOpen;
|
||||
bool bGotH264AVCInfo;
|
||||
bool bFirstFrame;
|
||||
|
||||
/* SWScale */
|
||||
SWScale swscaleobj;
|
||||
enum PixelFormat zm_pf;
|
||||
enum PixelFormat codec_pf;
|
||||
size_t codec_imgsize;
|
||||
size_t zm_imgsize;
|
||||
|
||||
/* User parameters */
|
||||
std::vector<EncoderParameter_t> user_params;
|
||||
|
||||
/* AVC Information */
|
||||
uint8_t x264_profleindication;
|
||||
uint8_t x264_profilecompat;
|
||||
uint8_t x264_levelindication;
|
||||
|
||||
/* NALs */
|
||||
Buffer buffer;
|
||||
|
||||
/* Previous frame */
|
||||
int prevPTS;
|
||||
int prevDTS;
|
||||
bool prevKeyframe;
|
||||
Buffer prevpayload;
|
||||
std::vector<x264_nal_t> prevnals;
|
||||
|
||||
/* Internal functions */
|
||||
int x264config();
|
||||
void x264encodeloop(bool bFlush = false);
|
||||
|
||||
/* x264 objects */
|
||||
x264_t* x264enc;
|
||||
x264_param_t x264params;
|
||||
x264_picture_t x264picin;
|
||||
x264_picture_t x264picout;
|
||||
|
||||
/* MP4v2 objects */
|
||||
MP4FileHandle mp4h;
|
||||
MP4TrackId mp4vtid;
|
||||
|
||||
|
||||
public:
|
||||
X264MP4Writer(const char* p_path, const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder, const std::vector<EncoderParameter_t>* p_user_params = NULL);
|
||||
~X264MP4Writer();
|
||||
int Encode(const uint8_t* data, const size_t data_size, const unsigned int frame_time);
|
||||
int Encode(const Image* img, const unsigned int frame_time);
|
||||
int Open();
|
||||
int Close();
|
||||
int Reset(const char* new_path = NULL);
|
||||
|
||||
};
|
||||
#endif // HAVE_LIBX264 && HAVE_LIBMP4V2 && HAVE_LIBAVUTIL && HAVE_LIBSWSCALE
|
||||
|
||||
#endif // ZM_VIDEO_H
|
|
@ -384,31 +384,33 @@ function getNearEvents()
|
|||
else
|
||||
$midSql = '';
|
||||
|
||||
$sql = "select E.Id as Id from Events as E inner join Monitors as M on E.MonitorId = M.Id where ".dbEscape($sortColumn)." ".($sortOrder=='asc'?'<=':'>=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." order by $sortColumn ".($sortOrder=='asc'?'desc':'asc');
|
||||
$sql = "select E.* as Id from Events as E inner join Monitors as M on E.MonitorId = M.Id where ".dbEscape($sortColumn)." ".($sortOrder=='asc'?'<=':'>=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." order by $sortColumn ".($sortOrder=='asc'?'desc':'asc');
|
||||
$result = dbQuery( $sql );
|
||||
while ( $id = dbFetchNext( $result, 'Id' ) )
|
||||
{
|
||||
if ( $id == $eventId )
|
||||
{
|
||||
$prevId = dbFetchNext( $result, 'Id' );
|
||||
$prevEvent = dbFetchNext( $result );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$sql = "select E.Id as Id from Events as E inner join Monitors as M on E.MonitorId = M.Id where $sortColumn ".($sortOrder=='asc'?'>=':'<=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." order by $sortColumn $sortOrder";
|
||||
$sql = "select E.* as Id from Events as E inner join Monitors as M on E.MonitorId = M.Id where $sortColumn ".($sortOrder=='asc'?'>=':'<=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." order by $sortColumn $sortOrder";
|
||||
$result = dbQuery( $sql );
|
||||
while ( $id = dbFetchNext( $result, 'Id' ) )
|
||||
{
|
||||
if ( $id == $eventId )
|
||||
{
|
||||
$nextId = dbFetchNext( $result, 'Id' );
|
||||
$nextEvent = dbFetchNext( $result );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$result = array( 'EventId'=>$eventId );
|
||||
$result['PrevEventId'] = empty($prevId)?0:$prevId;
|
||||
$result['NextEventId'] = empty($nextId)?0:$nextId;
|
||||
$result['PrevEventId'] = empty($prevEvent)?0:$prevEvent['Id'];
|
||||
$result['NextEventId'] = empty($nextEvent)?0:$nextEvent['Id'];
|
||||
$result['PrevEventDefVideoPath'] = empty($prevEvent)?0:(getEventDefaultVideoPath($prevEvent));
|
||||
$result['NextEventDefVideoPath'] = empty($nextEvent)?0:(getEventDefaultVideoPath($nextEvent));
|
||||
return( $result );
|
||||
}
|
||||
|
||||
|
|
|
@ -494,6 +494,9 @@ function getEventPath( $event )
|
|||
return( $eventPath );
|
||||
}
|
||||
|
||||
function getEventDefaultVideoPath( $event ) {
|
||||
return ZM_DIR_EVENTS . "/" . getEventPath($event) . "/" . $event['DefaultVideo'];
|
||||
}
|
||||
|
||||
function deletePath( $path )
|
||||
{
|
||||
|
|
|
@ -57,6 +57,23 @@
|
|||
text-align: right;
|
||||
}
|
||||
|
||||
#videoBar1 div {
|
||||
text-align: center;
|
||||
float: center;
|
||||
}
|
||||
|
||||
#videoBar1 #prevEvent {
|
||||
float: left;
|
||||
}
|
||||
|
||||
#videoBar1 #dlEvent {
|
||||
float: center;
|
||||
}
|
||||
|
||||
#videoBar1 #nextEvent {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#imageFeed {
|
||||
text-align: center;
|
||||
}
|
||||
|
|
|
@ -10,55 +10,55 @@
|
|||
//
|
||||
// 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
|
||||
// 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
//
|
||||
|
||||
if ( !canView( 'Events' ) )
|
||||
{
|
||||
$view = "error";
|
||||
return;
|
||||
$view = "error";
|
||||
return;
|
||||
}
|
||||
|
||||
$eid = validInt( $_REQUEST['eid'] );
|
||||
$fid = !empty($_REQUEST['fid'])?validInt($_REQUEST['fid']):1;
|
||||
|
||||
if ( $user['MonitorIds'] )
|
||||
$midSql = " and MonitorId in (".join( ",", preg_split( '/["\'\s]*,["\'\s]*/', dbEscape($user['MonitorIds']) ) ).")";
|
||||
$midSql = " and MonitorId in (".join( ",", preg_split( '/["\'\s]*,["\'\s]*/', dbEscape($user['MonitorIds']) ) ).")";
|
||||
else
|
||||
$midSql = '';
|
||||
$midSql = '';
|
||||
|
||||
$sql = 'SELECT E.*,M.Name AS MonitorName,M.DefaultRate,M.DefaultScale FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE E.Id = ?'.$midSql;
|
||||
$event = dbFetchOne( $sql, NULL, array($eid) );
|
||||
|
||||
if ( isset( $_REQUEST['rate'] ) )
|
||||
$rate = validInt($_REQUEST['rate']);
|
||||
$rate = validInt($_REQUEST['rate']);
|
||||
else
|
||||
$rate = reScale( RATE_BASE, $event['DefaultRate'], ZM_WEB_DEFAULT_RATE );
|
||||
$rate = reScale( RATE_BASE, $event['DefaultRate'], ZM_WEB_DEFAULT_RATE );
|
||||
if ( isset( $_REQUEST['scale'] ) )
|
||||
$scale = validInt($_REQUEST['scale']);
|
||||
$scale = validInt($_REQUEST['scale']);
|
||||
else
|
||||
$scale = reScale( SCALE_BASE, $event['DefaultScale'], ZM_WEB_DEFAULT_SCALE );
|
||||
$scale = reScale( SCALE_BASE, $event['DefaultScale'], ZM_WEB_DEFAULT_SCALE );
|
||||
|
||||
$replayModes = array(
|
||||
'single' => $SLANG['ReplaySingle'],
|
||||
'all' => $SLANG['ReplayAll'],
|
||||
'gapless' => $SLANG['ReplayGapless'],
|
||||
'single' => $SLANG['ReplaySingle'],
|
||||
'all' => $SLANG['ReplayAll'],
|
||||
'gapless' => $SLANG['ReplayGapless'],
|
||||
);
|
||||
|
||||
if ( isset( $_REQUEST['streamMode'] ) )
|
||||
$streamMode = validHtmlStr($_REQUEST['streamMode']);
|
||||
$streamMode = validHtmlStr($_REQUEST['streamMode']);
|
||||
else
|
||||
$streamMode = canStream()?'stream':'stills';
|
||||
$streamMode = canStream()?'stream':'stills';
|
||||
|
||||
if ( isset( $_REQUEST['replayMode'] ) )
|
||||
$replayMode = validHtmlStr($_REQUEST['replayMode']);
|
||||
$replayMode = validHtmlStr($_REQUEST['replayMode']);
|
||||
if ( isset( $_COOKIE['replayMode']) && preg_match('#^[a-z]+$#', $_COOKIE['replayMode']) )
|
||||
$replayMode = validHtmlStr($_COOKIE['replayMode']);
|
||||
$replayMode = validHtmlStr($_COOKIE['replayMode']);
|
||||
else {
|
||||
$keys = array_keys( $replayModes );
|
||||
$replayMode = array_shift( $keys );
|
||||
|
@ -79,63 +79,64 @@ $focusWindow = true;
|
|||
xhtmlHeaders(__FILE__, $SLANG['Event'] );
|
||||
?>
|
||||
<body>
|
||||
<div id="page">
|
||||
<div id="content">
|
||||
<div id="dataBar">
|
||||
<table id="dataTable" class="major" cellspacing="0">
|
||||
<tr>
|
||||
<td><span id="dataId" title="<?= $SLANG['Id'] ?>"><?= $event['Id'] ?></span></td>
|
||||
<td><span id="dataCause" title="<?= $event['Notes']?validHtmlStr($event['Notes']):$SLANG['AttrCause'] ?>"><?= validHtmlStr($event['Cause']) ?></span></td>
|
||||
<td><span id="dataTime" title="<?= $SLANG['Time'] ?>"><?= strftime( STRF_FMT_DATETIME_SHORT, strtotime($event['StartTime'] ) ) ?></span></td>
|
||||
<td><span id="dataDuration" title="<?= $SLANG['Duration'] ?>"><?= $event['Length'] ?></span>s</td>
|
||||
<td><span id="dataFrames" title="<?= $SLANG['AttrFrames']."/".$SLANG['AttrAlarmFrames'] ?>"><?= $event['Frames'] ?>/<?= $event['AlarmFrames'] ?></span></td>
|
||||
<td><span id="dataScore" title="<?= $SLANG['AttrTotalScore']."/".$SLANG['AttrAvgScore']."/".$SLANG['AttrMaxScore'] ?>"><?= $event['TotScore'] ?>/<?= $event['AvgScore'] ?>/<?= $event['MaxScore'] ?></span></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div id="menuBar1">
|
||||
<div id="scaleControl"><label for="scale"><?= $SLANG['Scale'] ?></label><?= buildSelect( "scale", $scales, "changeScale();" ); ?></div>
|
||||
<div id="replayControl"><label for="replayMode"><?= $SLANG['Replay'] ?></label><?= buildSelect( "replayMode", $replayModes, "changeReplayMode();" ); ?></div>
|
||||
<div id="nameControl"><input type="text" id="eventName" name="eventName" value="<?= validHtmlStr($event['Name']) ?>" size="16"/><input type="button" value="<?= $SLANG['Rename'] ?>" onclick="renameEvent()"<?php if ( !canEdit( 'Events' ) ) { ?> disabled="disabled"<?php } ?>/></div>
|
||||
</div>
|
||||
<div id="menuBar2">
|
||||
<div id="closeWindow"><a href="#" onclick="closeWindow();"><?= $SLANG['Close'] ?></a></div>
|
||||
<div id="page">
|
||||
<div id="content">
|
||||
<div id="dataBar">
|
||||
<table id="dataTable" class="major" cellspacing="0">
|
||||
<tr>
|
||||
<td><span id="dataId" title="<?= $SLANG['Id'] ?>"><?= $event['Id'] ?></span></td>
|
||||
<td><span id="dataCause" title="<?= $event['Notes']?validHtmlStr($event['Notes']):$SLANG['AttrCause'] ?>"><?= validHtmlStr($event['Cause']) ?></span></td>
|
||||
<td><span id="dataTime" title="<?= $SLANG['Time'] ?>"><?= strftime( STRF_FMT_DATETIME_SHORT, strtotime($event['StartTime'] ) ) ?></span></td>
|
||||
<td><span id="dataDuration" title="<?= $SLANG['Duration'] ?>"><?= $event['Length'] ?></span>s</td>
|
||||
<td><span id="dataFrames" title="<?= $SLANG['AttrFrames']."/".$SLANG['AttrAlarmFrames'] ?>"><?= $event['Frames'] ?>/<?= $event['AlarmFrames'] ?></span></td>
|
||||
<td><span id="dataScore" title="<?= $SLANG['AttrTotalScore']."/".$SLANG['AttrAvgScore']."/".$SLANG['AttrMaxScore'] ?>"><?= $event['TotScore'] ?>/<?= $event['AvgScore'] ?>/<?= $event['MaxScore'] ?></span></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div id="menuBar1">
|
||||
<div id="scaleControl"><label for="scale"><?= $SLANG['Scale'] ?></label><?= buildSelect( "scale", $scales, "changeScale();" ); ?></div>
|
||||
<div id="replayControl"><label for="replayMode"><?= $SLANG['Replay'] ?></label><?= buildSelect( "replayMode", $replayModes, "changeReplayMode();" ); ?></div>
|
||||
<div id="nameControl"><input type="text" id="eventName" name="eventName" value="<?= validHtmlStr($event['Name']) ?>" size="16"/><input type="button" value="<?= $SLANG['Rename'] ?>" onclick="renameEvent()"<?php if ( !canEdit( 'Events' ) ) { ?> disabled="disabled"<?php } ?>/></div>
|
||||
</div>
|
||||
<div id="menuBar2">
|
||||
<div id="closeWindow"><a href="#" onclick="closeWindow();"><?= $SLANG['Close'] ?></a></div>
|
||||
<?php
|
||||
if ( canEdit( 'Events' ) )
|
||||
{
|
||||
?>
|
||||
<div id="deleteEvent"><a href="#" onclick="deleteEvent()"><?= $SLANG['Delete'] ?></a></div>
|
||||
<div id="editEvent"><a href="#" onclick="editEvent()"><?= $SLANG['Edit'] ?></a></div>
|
||||
<div id="deleteEvent"><a href="#" onclick="deleteEvent()"><?= $SLANG['Delete'] ?></a></div>
|
||||
<div id="editEvent"><a href="#" onclick="editEvent()"><?= $SLANG['Edit'] ?></a></div>
|
||||
<?php
|
||||
}
|
||||
if ( canView( 'Events' ) )
|
||||
{
|
||||
?>
|
||||
<div id="exportEvent"><a href="#" onclick="exportEvent()"><?= $SLANG['Export'] ?></a></div>
|
||||
<div id="exportEvent"><a href="#" onclick="exportEvent()"><?= $SLANG['Export'] ?></a></div>
|
||||
<?php
|
||||
}
|
||||
if ( canEdit( 'Events' ) )
|
||||
{
|
||||
?>
|
||||
<div id="archiveEvent" class="hidden"><a href="#" onclick="archiveEvent()"><?= $SLANG['Archive'] ?></a></div>
|
||||
<div id="unarchiveEvent" class="hidden"><a href="#" onclick="unarchiveEvent()"><?= $SLANG['Unarchive'] ?></a></div>
|
||||
<div id="archiveEvent" class="hidden"><a href="#" onclick="archiveEvent()"><?= $SLANG['Archive'] ?></a></div>
|
||||
<div id="unarchiveEvent" class="hidden"><a href="#" onclick="unarchiveEvent()"><?= $SLANG['Unarchive'] ?></a></div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<div id="framesEvent"><a href="#" onclick="showEventFrames()"><?= $SLANG['Frames'] ?></a></div>
|
||||
<div id="streamEvent"<?php if ( $streamMode == 'stream' ) { ?> class="hidden"<?php } ?>><a href="#" onclick="showStream()"><?= $SLANG['Stream'] ?></a></div>
|
||||
<div id="stillsEvent"<?php if ( $streamMode == 'still' ) { ?> class="hidden"<?php } ?>><a href="#" onclick="showStills()"><?= $SLANG['Stills'] ?></a></div>
|
||||
<div id="framesEvent"><a href="#" onclick="showEventFrames()"><?= $SLANG['Frames'] ?></a></div>
|
||||
<div id="streamEvent"<?php if ( $streamMode == 'stream' ) { ?> class="hidden"<?php } ?>><a href="#" onclick="showStream()"><?= $SLANG['Stream'] ?></a></div>
|
||||
<div id="stillsEvent"<?php if ( $streamMode == 'still' ) { ?> class="hidden"<?php } ?>><a href="#" onclick="showStills()"><?= $SLANG['Stills'] ?></a></div>
|
||||
<div id="videoEvent"<?php if ( $streamMode == 'video' ) { ?> class="hidden"<?php } ?>><a href="#" onclick="showVideo()">HTML5Video</a></div>
|
||||
<?php
|
||||
if ( ZM_OPT_FFMPEG )
|
||||
{
|
||||
?>
|
||||
<div id="videoEvent"><a href="#" onclick="videoEvent()"><?= $SLANG['Video'] ?></a></div>
|
||||
<div id="videoEvent"><a href="#" onclick="videoEvent()"><?= $SLANG['Video'] ?></a></div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<div id="eventStream">
|
||||
<div id="imageFeed">
|
||||
</div>
|
||||
<div id="eventStream">
|
||||
<div id="imageFeed">
|
||||
<?php
|
||||
if(file_exists(ZM_PATH_WEB."/events/".getEventPath($event)."/event.mp4")){
|
||||
?>
|
||||
|
@ -153,87 +154,102 @@ Your browser does not support the video tag.
|
|||
}else{
|
||||
if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT )
|
||||
{
|
||||
$streamSrc = getStreamSrc( array( "source=event", "mode=mpeg", "event=".$eid, "frame=".$fid, "scale=".$scale, "rate=".$rate, "bitrate=".ZM_WEB_VIDEO_BITRATE, "maxfps=".ZM_WEB_VIDEO_MAXFPS, "format=".ZM_MPEG_REPLAY_FORMAT, "replay=".$replayMode ) );
|
||||
outputVideoStream( "evtStream", $streamSrc, reScale( $event['Width'], $scale ), reScale( $event['Height'], $scale ), ZM_MPEG_LIVE_FORMAT );
|
||||
$streamSrc = getStreamSrc( array( "source=event", "mode=mpeg", "event=".$eid, "frame=".$fid, "scale=".$scale, "rate=".$rate, "bitrate=".ZM_WEB_VIDEO_BITRATE, "maxfps=".ZM_WEB_VIDEO_MAXFPS, "format=".ZM_MPEG_REPLAY_FORMAT, "replay=".$replayMode ) );
|
||||
outputVideoStream( "evtStream", $streamSrc, reScale( $event['Width'], $scale ), reScale( $event['Height'], $scale ), ZM_MPEG_LIVE_FORMAT );
|
||||
}
|
||||
else
|
||||
{
|
||||
$streamSrc = getStreamSrc( array( "source=event", "mode=jpeg", "event=".$eid, "frame=".$fid, "scale=".$scale, "rate=".$rate, "maxfps=".ZM_WEB_VIDEO_MAXFPS, "replay=".$replayMode) );
|
||||
if ( canStreamNative() )
|
||||
{
|
||||
outputImageStream( "evtStream", $streamSrc, reScale( $event['Width'], $scale ), reScale( $event['Height'], $scale ), validHtmlStr($event['Name']) );
|
||||
}
|
||||
else
|
||||
{
|
||||
outputHelperStream( "evtStream", $streamSrc, reScale( $event['Width'], $scale ), reScale( $event['Height'], $scale ) );
|
||||
}
|
||||
$streamSrc = getStreamSrc( array( "source=event", "mode=jpeg", "event=".$eid, "frame=".$fid, "scale=".$scale, "rate=".$rate, "maxfps=".ZM_WEB_VIDEO_MAXFPS, "replay=".$replayMode) );
|
||||
if ( canStreamNative() )
|
||||
{
|
||||
outputImageStream( "evtStream", $streamSrc, reScale( $event['Width'], $scale ), reScale( $event['Height'], $scale ), validHtmlStr($event['Name']) );
|
||||
}
|
||||
else
|
||||
{
|
||||
outputHelperStream( "evtStream", $streamSrc, reScale( $event['Width'], $scale ), reScale( $event['Height'], $scale ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<p id="dvrControls">
|
||||
<input type="button" value="<+" id="prevBtn" title="<?= $SLANG['Prev'] ?>" class="inactive" onclick="streamPrev( true )"/>
|
||||
<input type="button" value="<<" id="fastRevBtn" title="<?= $SLANG['Rewind'] ?>" class="inactive" disabled="disabled" onclick="streamFastRev( true )"/>
|
||||
<input type="button" value="<" id="slowRevBtn" title="<?= $SLANG['StepBack'] ?>" class="unavail" disabled="disabled" onclick="streamSlowRev( true )"/>
|
||||
<input type="button" value="||" id="pauseBtn" title="<?= $SLANG['Pause'] ?>" class="inactive" onclick="streamPause( true )"/>
|
||||
<input type="button" value="|>" id="playBtn" title="<?= $SLANG['Play'] ?>" class="active" disabled="disabled" onclick="streamPlay( true )"/>
|
||||
<input type="button" value=">" id="slowFwdBtn" title="<?= $SLANG['StepForward'] ?>" class="unavail" disabled="disabled" onclick="streamSlowFwd( true )"/>
|
||||
<input type="button" value=">>" id="fastFwdBtn" title="<?= $SLANG['FastForward'] ?>" class="inactive" disabled="disabled" onclick="streamFastFwd( true )"/>
|
||||
<input type="button" value="–" id="zoomOutBtn" title="<?= $SLANG['ZoomOut'] ?>" class="avail" onclick="streamZoomOut()"/>
|
||||
<input type="button" value="+>" id="nextBtn" title="<?= $SLANG['Next'] ?>" class="inactive" onclick="streamNext( true )"/>
|
||||
</p>
|
||||
<div id="replayStatus">
|
||||
<span id="mode">Mode: <span id="modeValue"> </span></span>
|
||||
<span id="rate">Rate: <span id="rateValue"></span>x</span>
|
||||
<span id="progress">Progress: <span id="progressValue"></span>s</span>
|
||||
<span id="zoom">Zoom: <span id="zoomValue"></span>x</span>
|
||||
</div>
|
||||
<div id="progressBar" class="invisible">
|
||||
</div>
|
||||
<p id="dvrControls">
|
||||
<input type="button" value="<+" id="prevBtn" title="<?= $SLANG['Prev'] ?>" class="inactive" onclick="streamPrev( true )"/>
|
||||
<input type="button" value="<<" id="fastRevBtn" title="<?= $SLANG['Rewind'] ?>" class="inactive" disabled="disabled" onclick="streamFastRev( true )"/>
|
||||
<input type="button" value="<" id="slowRevBtn" title="<?= $SLANG['StepBack'] ?>" class="unavail" disabled="disabled" onclick="streamSlowRev( true )"/>
|
||||
<input type="button" value="||" id="pauseBtn" title="<?= $SLANG['Pause'] ?>" class="inactive" onclick="streamPause( true )"/>
|
||||
<input type="button" value="|>" id="playBtn" title="<?= $SLANG['Play'] ?>" class="active" disabled="disabled" onclick="streamPlay( true )"/>
|
||||
<input type="button" value=">" id="slowFwdBtn" title="<?= $SLANG['StepForward'] ?>" class="unavail" disabled="disabled" onclick="streamSlowFwd( true )"/>
|
||||
<input type="button" value=">>" id="fastFwdBtn" title="<?= $SLANG['FastForward'] ?>" class="inactive" disabled="disabled" onclick="streamFastFwd( true )"/>
|
||||
<input type="button" value="–" id="zoomOutBtn" title="<?= $SLANG['ZoomOut'] ?>" class="avail" onclick="streamZoomOut()"/>
|
||||
<input type="button" value="+>" id="nextBtn" title="<?= $SLANG['Next'] ?>" class="inactive" onclick="streamNext( true )"/>
|
||||
</p>
|
||||
<div id="replayStatus">
|
||||
<span id="mode">Mode: <span id="modeValue"> </span></span>
|
||||
<span id="rate">Rate: <span id="rateValue"></span>x</span>
|
||||
<span id="progress">Progress: <span id="progressValue"></span>s</span>
|
||||
<span id="zoom">Zoom: <span id="zoomValue"></span>x</span>
|
||||
</div>
|
||||
<div id="progressBar" class="invisible">
|
||||
<?php
|
||||
for ( $i = 0; $i < $panelSections; $i++ )
|
||||
{
|
||||
for ( $i = 0; $i < $panelSections; $i++ )
|
||||
{
|
||||
?>
|
||||
<div class="progressBox" id="progressBox<?= $i ?>" title=""></div>
|
||||
<div class="progressBox" id="progressBox<?= $i ?>" title=""></div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
<div id="eventStills" class="hidden">
|
||||
<div id="eventThumbsPanel">
|
||||
<div id="eventThumbs">
|
||||
</div>
|
||||
</div>
|
||||
<div id="eventImagePanel" class="hidden">
|
||||
<div id="eventImageFrame">
|
||||
<img id="eventImage" src="graphics/transparent.gif" alt=""/>
|
||||
<div id="eventImageBar">
|
||||
<div id="eventImageClose"><input type="button" value="<?= $SLANG['Close'] ?>" onclick="hideEventImage()"/></div>
|
||||
<div id="eventImageStats" class="hidden"><input type="button" value="<?= $SLANG['Stats'] ?>" onclick="showFrameStats()"/></div>
|
||||
<div id="eventImageData">Frame <span id="eventImageNo"></span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="eventImageNav">
|
||||
<div id="eventImageButtons">
|
||||
<div id="prevButtonsPanel">
|
||||
<input id="prevEventBtn" type="button" value="<E" onclick="prevEvent()" disabled="disabled"/>
|
||||
<input id="prevThumbsBtn" type="button" value="<<" onclick="prevThumbs()" disabled="disabled"/>
|
||||
<input id="prevImageBtn" type="button" value="<" onclick="prevImage()" disabled="disabled"/>
|
||||
<input id="nextImageBtn" type="button" value=">" onclick="nextImage()" disabled="disabled"/>
|
||||
<input id="nextThumbsBtn" type="button" value=">>" onclick="nextThumbs()" disabled="disabled"/>
|
||||
<input id="nextEventBtn" type="button" value="E>" onclick="nextEvent()" disabled="disabled"/>
|
||||
</div>
|
||||
</div>
|
||||
<div id="thumbsSliderPanel">
|
||||
<div id="thumbsSlider">
|
||||
<div id="thumbsKnob">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php if ( $Monitor[VideoWriter} ) { ?>
|
||||
<div id="eventVideo" class="hidden">
|
||||
<div id="videoFeed">
|
||||
<video id="videoobj" width="<?= $event['Width'] ?>" height="<?= $event['Height'] ?>" controls autoplay>
|
||||
<source src="<?= getEventDefaultVideoPath($event) ?>" type="video/mp4">
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</div>
|
||||
<div id="videoBar1">
|
||||
<div id="prevEvent"><a href="#" onclick="prevEvent()">Previous Event</a></div>
|
||||
<div id="dlEvent"><a id="downloadlink" href="<?= getEventDefaultVideoPath($event) ?>" download>Download Video</a></div>
|
||||
<div id="nextEvent"><a href="#" onclick="nextEvent()">Next Event</a></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="eventStills" class="hidden">
|
||||
<div id="eventThumbsPanel">
|
||||
<div id="eventThumbs">
|
||||
</div>
|
||||
</div>
|
||||
<div id="eventImagePanel" class="hidden">
|
||||
<div id="eventImageFrame">
|
||||
<img id="eventImage" src="graphics/transparent.gif" alt=""/>
|
||||
<div id="eventImageBar">
|
||||
<div id="eventImageClose"><input type="button" value="<?= $SLANG['Close'] ?>" onclick="hideEventImage()"/></div>
|
||||
<div id="eventImageStats" class="hidden"><input type="button" value="<?= $SLANG['Stats'] ?>" onclick="showFrameStats()"/></div>
|
||||
<div id="eventImageData">Frame <span id="eventImageNo"></span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="eventImageNav">
|
||||
<div id="eventImageButtons">
|
||||
<div id="prevButtonsPanel">
|
||||
<input id="prevEventBtn" type="button" value="<E" onclick="prevEvent()" disabled="disabled"/>
|
||||
<input id="prevThumbsBtn" type="button" value="<<" onclick="prevThumbs()" disabled="disabled"/>
|
||||
<input id="prevImageBtn" type="button" value="<" onclick="prevImage()" disabled="disabled"/>
|
||||
<input id="nextImageBtn" type="button" value=">" onclick="nextImage()" disabled="disabled"/>
|
||||
<input id="nextThumbsBtn" type="button" value=">>" onclick="nextThumbs()" disabled="disabled"/>
|
||||
<input id="nextEventBtn" type="button" value="E>" onclick="nextEvent()" disabled="disabled"/>
|
||||
</div>
|
||||
</div>
|
||||
<div id="thumbsSliderPanel">
|
||||
<div id="thumbsSlider">
|
||||
<div id="thumbsKnob">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ) ?>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -12,7 +12,13 @@ function changeScale()
|
|||
var newWidth = ( baseWidth * scale ) / SCALE_BASE;
|
||||
var newHeight = ( baseHeight * scale ) / SCALE_BASE;
|
||||
|
||||
streamScale( scale );
|
||||
if(streamMode == 'video') {
|
||||
var videoid = document.getElementById('videoobj');
|
||||
|
||||
videoid.style.width = newWidth + "px";
|
||||
videoid.style.height = newHeight + "px";
|
||||
} else {
|
||||
streamScale( scale );
|
||||
|
||||
/*Stream could be an applet so can't use moo tools*/
|
||||
|
||||
|
@ -217,15 +223,35 @@ function streamFastRev( action )
|
|||
function streamPrev( action )
|
||||
{
|
||||
streamPlay( false );
|
||||
if ( action )
|
||||
streamReq.send( streamParms+"&command="+CMD_PREV );
|
||||
if(streamMode == 'video') {
|
||||
var videoid = document.getElementById('videoobj');
|
||||
videoobj.src = PrevEventDefVideoPath;
|
||||
videoobj.load();
|
||||
updatedownloadlink();
|
||||
} else {
|
||||
if ( action )
|
||||
streamReq.send( streamParms+"&command="+CMD_PREV );
|
||||
}
|
||||
}
|
||||
|
||||
function streamNext( action )
|
||||
{
|
||||
streamPlay( false );
|
||||
if ( action )
|
||||
streamReq.send( streamParms+"&command="+CMD_NEXT );
|
||||
if(streamMode == 'video') {
|
||||
var videoid = document.getElementById('videoobj');
|
||||
videoobj.src = NextEventDefVideoPath;
|
||||
videoobj.load();
|
||||
updatedownloadlink();
|
||||
} else {
|
||||
if ( action )
|
||||
streamReq.send( streamParms+"&command="+CMD_NEXT );
|
||||
}
|
||||
}
|
||||
|
||||
function updatedownloadlink() {
|
||||
var videoid = document.getElementById('videoobj');
|
||||
var link = document.getElementById('downloadlink');
|
||||
link.href = videoid.currentSrc;
|
||||
}
|
||||
|
||||
function streamZoomIn( x, y )
|
||||
|
@ -316,6 +342,8 @@ function eventQuery( eventId )
|
|||
|
||||
var prevEventId = 0;
|
||||
var nextEventId = 0;
|
||||
var PrevEventDefVideoPath = "";
|
||||
var NextEventDefVideoPath = "";
|
||||
|
||||
function getNearEventsResponse( respObj, respText )
|
||||
{
|
||||
|
@ -323,6 +351,8 @@ function getNearEventsResponse( respObj, respText )
|
|||
return;
|
||||
prevEventId = respObj.nearevents.PrevEventId;
|
||||
nextEventId = respObj.nearevents.NextEventId;
|
||||
PrevEventDefVideoPath = respObj.nearevents.PrevEventDefVideoPath;
|
||||
NextEventDefVideoPath = respObj.nearevents.NextEventDefVideoPath;
|
||||
|
||||
$('prevEventBtn').disabled = !prevEventId;
|
||||
$('nextEventBtn').disabled = !nextEventId;
|
||||
|
@ -691,18 +721,42 @@ function showStream()
|
|||
{
|
||||
$('eventStills').addClass( 'hidden' );
|
||||
$('eventStream').removeClass( 'hidden' );
|
||||
$('eventVideo').addClass( 'hidden' );
|
||||
|
||||
$('streamEvent').addClass( 'hidden' );
|
||||
$('stillsEvent').removeClass( 'hidden' );
|
||||
$('videoEvent').removeClass( 'hidden' );
|
||||
|
||||
streamMode = 'stream';
|
||||
}
|
||||
|
||||
//$(window).removeEvent( 'resize', updateStillsSizes );
|
||||
function showVideo()
|
||||
{
|
||||
$('eventStills').addClass( 'hidden' );
|
||||
$('eventStream').addClass( 'hidden' );
|
||||
$('eventVideo').removeClass( 'hidden' );
|
||||
|
||||
$('streamEvent').removeClass( 'hidden' );
|
||||
$('stillsEvent').removeClass( 'hidden' );
|
||||
$('videoEvent').addClass( 'hidden' );
|
||||
|
||||
streamMode = 'video';
|
||||
|
||||
var videoid = document.getElementById('videoobj');
|
||||
}
|
||||
|
||||
function showStills()
|
||||
{
|
||||
$('eventStream').addClass( 'hidden' );
|
||||
$('eventStills').removeClass( 'hidden' );
|
||||
$('stillsEvent').addClass( 'hidden' );
|
||||
$('eventStream').addClass( 'hidden' );
|
||||
$('eventVideo').addClass( 'hidden' );
|
||||
|
||||
$('streamEvent').removeClass( 'hidden' );
|
||||
$('stillsEvent').addClass( 'hidden' );
|
||||
$('videoEvent').removeClass( 'hidden' );
|
||||
|
||||
streamMode = 'stills';
|
||||
|
||||
streamPause( true );
|
||||
if ( !scroll )
|
||||
{
|
||||
|
@ -819,13 +873,12 @@ function initPage()
|
|||
streamCmdTimer = streamQuery.delay( 250 );
|
||||
eventQuery.pass( eventData.Id ).delay( 500 );
|
||||
|
||||
if ( canStreamNative )
|
||||
{
|
||||
var streamImg = $('imageFeed').getElement('img');
|
||||
if ( !streamImg )
|
||||
streamImg = $('imageFeed').getElement('object');
|
||||
$(streamImg).addEvent( 'click', function( event ) { handleClick( event ); } );
|
||||
}
|
||||
if ( canStreamNative )
|
||||
{
|
||||
var streamImg = $('imageFeed').getElement('img');
|
||||
if ( !streamImg )
|
||||
streamImg = $('imageFeed').getElement('object');
|
||||
$(streamImg).addEvent( 'click', function( event ) { handleClick( event ); } );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ if ( !canView( 'Monitors' ) )
|
|||
$tabs = array();
|
||||
$tabs["general"] = $SLANG['General'];
|
||||
$tabs["source"] = $SLANG['Source'];
|
||||
$tabs["storage"] = "Storage";
|
||||
$tabs["timestamp"] = $SLANG['Timestamp'];
|
||||
$tabs["buffers"] = $SLANG['Buffers'];
|
||||
if ( ZM_OPT_CONTROL && canView( 'Control' ) )
|
||||
|
@ -70,6 +71,9 @@ if ( ! empty($_REQUEST['mid']) ) {
|
|||
'Height' => "240",
|
||||
'Orientation' => "0",
|
||||
'Deinterlacing' => 0,
|
||||
'SaveJPEGs' => "3",
|
||||
'VideoWriter' => "0",
|
||||
'EncoderParameters' => "# Lines beginning with # are a comment \n# For changing quality, use the crf option\n# 1 is best, 51 is worst quality\n#crf=23\n",
|
||||
'LabelFormat' => '%N - %d/%m/%y %H:%M:%S',
|
||||
'LabelX' => 0,
|
||||
'LabelY' => 0,
|
||||
|
@ -415,6 +419,19 @@ $fastblendopts_alarm = array(
|
|||
"50% (Alarm lasts a moment)" => 50
|
||||
);
|
||||
|
||||
$savejpegopts = array(
|
||||
"Disabled" => 0,
|
||||
"Frames only" => 1,
|
||||
"Analysis images only (if available)" => 2,
|
||||
"Frames + Analysis images (if available)" => 3
|
||||
);
|
||||
|
||||
$videowriteropts = array(
|
||||
"Disabled" => 0,
|
||||
"X264 : MP4V2" => 1
|
||||
// "H264 Passthrough : MP4V2 (not implemented)" => 2
|
||||
);
|
||||
|
||||
xhtmlHeaders(__FILE__, $SLANG['Monitor']." - ".validHtmlStr($monitor['Name']) );
|
||||
?>
|
||||
<body>
|
||||
|
@ -533,6 +550,14 @@ if ( $tab != 'source' )
|
|||
<input type="hidden" name="newMonitor[Deinterlacing]" value="<?= validHtmlStr($newMonitor['Deinterlacing']) ?>"/>
|
||||
<?php
|
||||
}
|
||||
if ( $tab != 'storage' )
|
||||
{
|
||||
?>
|
||||
<input type="hidden" name="newMonitor[SaveJPEGs]" value="<?= validHtmlStr($newMonitor['SaveJPEGs']) ?>"/>
|
||||
<input type="hidden" name="newMonitor[VideoWriter]" value="<?= validHtmlStr($newMonitor['VideoWriter']) ?>"/>
|
||||
<input type="hidden" name="newMonitor[EncoderParameters]" value="<?= validHtmlStr($newMonitor['EncoderParameters']) ?>"/>
|
||||
<?php
|
||||
}
|
||||
if ( $tab != 'timestamp' )
|
||||
{
|
||||
?>
|
||||
|
@ -785,6 +810,13 @@ switch ( $tab )
|
|||
<?php
|
||||
break;
|
||||
}
|
||||
case 'storage' :
|
||||
?>
|
||||
<tr><td>Save as JPEGs</td><td><select name="newMonitor[SaveJPEGs]"><?php foreach ( $savejpegopts as $name => $value ) { ?><option value="<?= $value ?>"<?php if ( $value == $newMonitor['SaveJPEGs'] ) { ?> selected="selected"<?php } ?>><?= $name ?></option><?php } ?></select></td></tr>
|
||||
<tr><td>Video writer</td><td><select name="newMonitor[VideoWriter]"><?php foreach ( $videowriteropts as $name => $value ) { ?><option value="<?= $value ?>"<?php if ( $value == $newMonitor['VideoWriter'] ) { ?> selected="selected"<?php } ?>><?= $name ?></option><?php } ?></select></td></tr>
|
||||
<tr><td>Optional encoder parameters</td><td><textarea name="newMonitor[EncoderParameters]" rows="4" cols="36"><?= validHtmlStr($newMonitor['EncoderParameters']) ?></textarea></td></tr>
|
||||
<?php
|
||||
break;
|
||||
case 'timestamp' :
|
||||
{
|
||||
?>
|
||||
|
|
|
@ -39,6 +39,12 @@
|
|||
#cmakedefine HAVE_GNUTLS_GNUTLS_H 1
|
||||
#cmakedefine HAVE_LIBMYSQLCLIENT 1
|
||||
#cmakedefine HAVE_MYSQL_H 1
|
||||
#cmakedefine HAVE_LIBX264 1
|
||||
#cmakedefine HAVE_X264_H 1
|
||||
#cmakedefine HAVE_LIBMP4V2 1
|
||||
#cmakedefine HAVE_MP4V2_MP4V2_H 1
|
||||
#cmakedefine HAVE_MP4V2_H 1
|
||||
#cmakedefine HAVE_MP4_H 1
|
||||
#cmakedefine HAVE_LIBAVFORMAT 1
|
||||
#cmakedefine HAVE_LIBAVFORMAT_AVFORMAT_H 1
|
||||
#cmakedefine HAVE_LIBAVCODEC 1
|
||||
|
|
Loading…
Reference in New Issue