Merge branch 'master' of github.com:/connortechnology/ZoneMinder

This commit is contained in:
Isaac Connor 2018-10-22 16:13:55 -04:00
commit 7b69bc4292
28 changed files with 286 additions and 165 deletions

5
db/zm_update-1.32.1.sql Normal file
View File

@ -0,0 +1,5 @@
--
-- This updates a 1.32.0 database to 1.32.1
--
-- No changes required
--

View File

@ -25,7 +25,7 @@
%global _hardened_build 1
Name: zoneminder
Version: 1.32.0
Version: 1.32.1
Release: 1%{?dist}
Summary: A camera monitoring and analysis tool
Group: System Environment/Daemons
@ -320,6 +320,10 @@ EOF
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/run/zoneminder
%changelog
* Tue Oct 2 2018 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.32.1-1
- 1.32.1 release
- Bug fix release
* Wed Sep 12 2018 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.32.0-1
- 1.32.0 release
- remove el6 (sys v init) support

View File

@ -28,7 +28,7 @@ override_dh_auto_configure:
-DZM_CACHEDIR="/var/cache/zoneminder/cache" \
-DZM_DIR_EVENTS="/var/cache/zoneminder/events" \
-DZM_DIR_IMAGES="/var/cache/zoneminder/images" \
-DZM_PATH_ZMS="/zm/cgi-bin/nph-zms" \
-DZM_PATH_ZMS="/zm/cgi-bin/nph-zms"
override_dh_clean:
dh_clean $(MANPAGES1)

View File

@ -35,7 +35,7 @@ guide you with a quick search.
`releases page <https://github.com/ZoneMinder/zoneminder/releases>`_ for
the latest release.
Alternatively, the ZoneMinder project team maintains a ppa, which is updated immediately
Alternatively, the ZoneMinder project team maintains a `PPA <https://askubuntu.com/questions/4983/what-are-ppas-and-how-do-i-use-them>`_, which is updated immediately
following a new release of ZoneMinder. To use this repository instead of the
official Ubuntu repository, enter the following from the command line:
@ -43,6 +43,15 @@ guide you with a quick search.
add-apt-repository ppa:iconnor/zoneminder
Please note that as of 1.32.0 We are creating a new PPA for each major version, as a means to prevent automatic upgrades from one major version to another. So instead of the above ppa line use the following:
::
add-apt-repository ppa:iconnor/zoneminder-1.32
If you are on trusty, you may want to add both, as there are some packages for dependencies included in the old ppa.
Update repo and upgrade.
::
@ -51,6 +60,7 @@ Update repo and upgrade.
apt-get upgrade
apt-get dist-upgrade
**Step 3:** Configure MySQL
.. sidebar :: Note
@ -62,8 +72,10 @@ Update repo and upgrade.
| /etc/alternatives/my.cnf -> /etc/mysql/mysql.cnf
| /etc/mysql/mysql.cnf is a basic file
Certain new defaults in MySQL 5.7 are currently causing some issues with ZoneMinder,
the workaround is to modify the sql_mode setting of MySQL.
Certain new defaults in MySQL 5.7 cause some issues with ZoneMinder < 1.32.0,
the workaround is to modify the sql_mode setting of MySQL. Please note that these
changes are NOT required for ZoneMinder 1.32.0 and some people have reported them
causing problems in 1.32.0.
To better manage the MySQL server it is recommended to copy the sample config file and
replace the default my.cnf symbolic link.
@ -104,10 +116,12 @@ Restart MySQL
**Step 5:** Configure the ZoneMinder Database
This step should not be required on ZoneMinder 1.32.0.
::
mysql -uroot -p < /usr/share/zoneminder/db/zm_create.sql
mysql -uroot -p -e "grant select,insert,update,delete,create,alter,index,lock tables on zm.* to 'zmuser'@localhost identified by 'zmpass';"
mysql -uroot -p -e "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on zm.* to 'zmuser'@localhost identified by 'zmpass';"
**Step 6:** Set permissions
@ -124,10 +138,17 @@ Set /etc/zm/zm.conf to root:www-data 740 and www-data access to content
::
a2enconf zoneminder
a2enmod cgi
a2enconf zoneminder
a2enmod cgi
a2enmod rewrite
You may also want to enable to following modules to improve caching performance
::
a2enmod expires
a2enmod headers
**Step 8:** Enable and start Zoneminder
::

View File

@ -1,10 +1,14 @@
# This configuration is only needed for compatibility with zmninja on iOS
# This configuration is only needed for compatibility with zmninja
# If not using VirtualHosts, copy or symlink this file into the Apache config folder
# If using VirtualHosts, then this config must be placed inside the appropriate
# <VirtualHost> directive.
#zmNinja header permissions. Tweak to your needs
# Make sure you have enabled/loaded header manipulation modules
# For example, in Debian based distros the command is "sudo a2enmod headers"
# zmNinja header permissions. Tweak to your needs
Header always set Access-Control-Allow-Credentials true
#zmNinja's WKWebView will set the origin header as localhost:8080
Header always set Access-Control-Allow-Origin "http://localhost:8080"

View File

@ -549,7 +549,7 @@ sub jsonDecode {
$out =~ s/=>null/=>undef/g;
$out =~ s/`/'/g;
$out =~ s/qx/qq/g;
( $out ) = $out =~ m/^({.+})$/; # Detaint and check it's a valid object syntax
( $out ) = $out =~ m/^(\{.+\})$/; # Detaint and check it's a valid object syntax
my $result = eval $out;
Fatal( $@ ) if ( $@ );
return( $result );

View File

@ -570,7 +570,7 @@ sub logPrint {
$this->{databaseLevel} = $oldlevel;
}
my $sql = 'INSERT INTO Logs ( TimeKey, Component, Pid, Level, Code, Message, File, Line ) VALUES ( ?, ?, ?, ?, ?, ?, ?, NULL )';
my $sql = 'INSERT INTO Logs ( TimeKey, Component, ServerId, Pid, Level, Code, Message, File, Line ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, NULL )';
$this->{sth} = $this->{dbh}->prepare_cached($sql) if ! $this->{sth};
if ( !$this->{sth} ) {
$this->{databaseLevel} = NOLOG;
@ -578,13 +578,15 @@ sub logPrint {
return;
}
my $res = $this->{sth}->execute($seconds+($microseconds/1000000.0)
, $this->{id}
, $$
, $level
, $codes{$level}
, $string
, $this->{fileName}
my $res = $this->{sth}->execute(
$seconds+($microseconds/1000000.0),
$this->{id},
($Config{ZM_SERVER_ID} ? $Config{ZM_SERVER_ID} : undef),
$$,
$level,
$codes{$level},
$string,
$this->{fileName},
);
if ( !$res ) {
$this->{databaseLevel} = NOLOG;

View File

@ -482,7 +482,7 @@ sub start {
logTerm();
zmDbDisconnect();
my $fd = 0;
my $fd = 3; # leave stdin,stdout,stderr open. Closing them causes problems with libx264
while( $fd < POSIX::sysconf(&POSIX::_SC_OPEN_MAX) ) {
POSIX::close($fd++);
}

View File

@ -321,7 +321,7 @@ sub checkFilter {
}
if ( $Config{ZM_OPT_UPLOAD} && $filter->{AutoUpload} ) {
if ( !$event->{Uploaded} ) {
$delete_ok = undef if !uploadArchFile($filter, $event);
$delete_ok = undef if !uploadArchFile($filter, $Event);
}
}
if ( $filter->{AutoExecute} ) {

View File

@ -323,14 +323,10 @@ sub loadMonitors {
my $res = $sth->execute( $Config{ZM_SERVER_ID} ? $Config{ZM_SERVER_ID} : () )
or Fatal( "Can't execute: ".$sth->errstr() );
while( my $monitor = $sth->fetchrow_hashref() ) {
# Check shared memory ok
if ( !zmMemVerify( $monitor ) ) {
zmMemInvalidate( $monitor );
next;
if ( zmMemVerify( $monitor ) ) {
$monitor->{LastState} = zmGetMonitorState( $monitor );
$monitor->{LastEvent} = zmGetLastEvent( $monitor );
}
$monitor->{LastState} = zmGetMonitorState( $monitor );
$monitor->{LastEvent} = zmGetLastEvent( $monitor );
$new_monitors{$monitor->{Id}} = $monitor;
} # end while fetchrow
%monitors = %new_monitors;

View File

@ -279,8 +279,8 @@ void EventStream::processCommand(const CmdMsg *msg) {
}
// If we are in single event mode and at the last frame, replay the current event
if ( (mode == MODE_SINGLE) && ((unsigned int)curr_frame_id == event_data->frame_count) ) {
Debug(1, "Was in single_mode, and last frame, so jumping to 1st frame");
if ( (mode == MODE_SINGLE || mode == MODE_NONE) && ((unsigned int)curr_frame_id == event_data->frame_count) ) {
Debug(1, "Was in single or no replay mode, and at last frame, so jumping to 1st frame");
curr_frame_id = 1;
} else {
Debug(1, "mode is %s, current frame is %d, frame count is %d", (mode == MODE_SINGLE ? "single" : "not single" ), curr_frame_id, event_data->frame_count );
@ -501,7 +501,7 @@ void EventStream::checkEventLoaded() {
}
if ( reload_event ) {
if ( forceEventChange || mode != MODE_SINGLE ) {
if ( forceEventChange || ( mode != MODE_SINGLE && mode != MODE_NONE ) ) {
//Info( "SQL:%s", sql );
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't run query: %s", mysql_error( &dbconn ) );
@ -812,7 +812,7 @@ void EventStream::runStream() {
step = 0;
send_frame = true;
} else if ( !send_frame ) {
// We are paused, and doing nothing
// We are paused, not stepping and doing nothing
double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent;
if ( actual_delta_time > MAX_STREAM_DELAY ) {
// Send keepalive
@ -828,9 +828,11 @@ void EventStream::runStream() {
curr_stream_time = frame_data->timestamp;
if ( !paused ) {
curr_frame_id += replay_rate>0?1:-1;
if ( (mode == MODE_SINGLE) && ((unsigned int)curr_frame_id == event_data->frame_count) )
curr_frame_id += (replay_rate>0) ? 1 : -1;
if ( (mode == MODE_SINGLE) && ((unsigned int)curr_frame_id == event_data->frame_count) ) {
Debug(2, "Have mode==MODE_SINGLE and at end of event, looping back to start");
curr_frame_id = 1;
}
if ( send_frame && type != STREAM_MPEG ) {
Debug( 3, "dUs: %d", delta_us );
if ( delta_us )

View File

@ -42,7 +42,7 @@ extern "C" {
class EventStream : public StreamBase {
public:
typedef enum { MODE_SINGLE, MODE_ALL, MODE_ALL_GAPLESS } StreamMode;
typedef enum { MODE_NONE, MODE_SINGLE, MODE_ALL, MODE_ALL_GAPLESS } StreamMode;
protected:
struct FrameData {

View File

@ -225,8 +225,9 @@ static void zm_log_fps(double d, const char *postfix) {
Debug(1, "%3.2f %s", d, postfix);
} else if (v % (100 * 1000)) {
Debug(1, "%1.0f %s", d, postfix);
} else
} else {
Debug(1, "%1.0fk %s", d / 1000, postfix);
}
}
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
@ -355,9 +356,8 @@ int check_sample_fmt(AVCodec *codec, enum AVSampleFormat sample_fmt) {
#if LIBAVCODEC_VERSION_CHECK(56, 8, 0, 60, 100)
#else
unsigned int zm_av_packet_ref( AVPacket *dst, AVPacket *src ) {
dst->size = (src->size + FF_INPUT_BUFFER_PADDING_SIZE)/sizeof(uint64_t) + 1;
dst->data = reinterpret_cast<uint8_t*>(new uint64_t[dst->size]);
memcpy(dst->data, src->data, src->size );
av_new_packet(dst,src->size);
memcpy(dst->data, src->data, src->size);
dst->flags = src->flags;
return 0;
}

View File

@ -303,8 +303,8 @@ void zm_dump_codecpar ( const AVCodecParameters *par );
#define zm_av_packet_unref( packet ) av_packet_unref( packet )
#define zm_av_packet_ref( dst, src ) av_packet_ref( dst, src )
#else
unsigned int zm_av_packet_ref( AVPacket *dst, AVPacket *src );
#define zm_av_packet_unref( packet ) av_free_packet( packet )
unsigned int zm_av_packet_ref( AVPacket *dst, AVPacket *src );
#endif
#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0)
#define zm_avcodec_decode_video( context, rawFrame, frameComplete, packet ) avcodec_decode_video2( context, rawFrame, frameComplete, packet )

View File

@ -331,6 +331,8 @@ int FfmpegCamera::OpenFfmpeg() {
ret = av_dict_set(&opts, "rtsp_transport", "tcp", 0);
} else if ( method == "rtpRtspHttp" ) {
ret = av_dict_set(&opts, "rtsp_transport", "http", 0);
} else if ( method == "rtpUni" ) {
ret = av_dict_set(&opts, "rtsp_transport", "udp", 0);
} else {
Warning("Unknown method (%s)", method.c_str() );
}
@ -606,7 +608,8 @@ int FfmpegCamera::OpenFfmpeg() {
return -1;
}
mConvertContext = sws_getContext(mVideoCodecContext->width,
mConvertContext = sws_getContext(
mVideoCodecContext->width,
mVideoCodecContext->height,
mVideoCodecContext->pix_fmt,
width, height,
@ -722,7 +725,8 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
uint32_t video_writer_event_id = monitor->GetVideoWriterEventId();
if ( last_event_id != video_writer_event_id ) {
Debug(2, "Have change of event. last_event(%d), our current (%d)", last_event_id, video_writer_event_id );
Debug(2, "Have change of event. last_event(%d), our current (%d)",
last_event_id, video_writer_event_id);
if ( videoStore ) {
Info("Re-starting video storage module");
@ -731,7 +735,7 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
// Also don't know how much it matters for audio.
if ( packet.stream_index == mVideoStreamId ) {
//Write the packet to our video store
int ret = videoStore->writeVideoFramePacket( &packet );
int ret = videoStore->writeVideoFramePacket(&packet);
if ( ret < 0 ) { //Less than zero and we skipped a frame
Warning("Error writing last packet to videostore.");
}

View File

@ -64,7 +64,7 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
}
// Couldn't deduce format from filename, trying from format name
if (!oc) {
if ( !oc ) {
avformat_alloc_output_context2(&oc, NULL, format, filename);
if (!oc) {
Error(
@ -108,7 +108,7 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
Debug(2, "Success creating video out stream");
}
if (!video_out_ctx->codec_tag) {
if ( !video_out_ctx->codec_tag ) {
video_out_ctx->codec_tag =
av_codec_get_tag(oc->oformat->codec_tag, video_in_ctx->codec_id);
Debug(2, "No codec_tag, setting to %d", video_out_ctx->codec_tag);
@ -127,9 +127,10 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
#else
video_out_stream =
avformat_new_stream(oc,(AVCodec *)(video_in_ctx->codec));
avformat_new_stream(oc, NULL);
//(AVCodec *)(video_in_ctx->codec));
//avformat_new_stream(oc,(const AVCodec *)(video_in_ctx->codec));
if (!video_out_stream) {
if ( !video_out_stream ) {
Fatal("Unable to create video out stream\n");
} else {
Debug(2, "Success creating video out stream");
@ -158,6 +159,9 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
// Just copy them from the in, no reason to choose different
video_out_ctx->time_base = video_in_ctx->time_base;
if ( ! (video_out_ctx->time_base.num && video_out_ctx->time_base.den) ) {
video_out_ctx->time_base = AV_TIME_BASE_Q;
}
video_out_stream->time_base = video_in_stream->time_base;
Debug(3,
@ -339,6 +343,9 @@ bool VideoStore::open() {
if (ret < 0) {
Error("Error occurred when writing out file header to %s: %s\n",
filename, av_make_error_string(ret).c_str());
/* free the stream */
avio_closep(&oc->pb);
//avformat_free_context(oc);
return false;
}
return true;
@ -412,7 +419,7 @@ VideoStore::~VideoStore() {
if (int rc = av_write_trailer(oc)) {
Error("Error writing trailer %s", av_err2str(rc));
} else {
Debug(3, "Sucess Writing trailer");
Debug(3, "Success Writing trailer");
}
// When will we not be using a file ?
@ -426,7 +433,7 @@ VideoStore::~VideoStore() {
} else {
Debug(3, "Not closing avio because we are not writing to a file.");
}
}
} // end if ( oc->pb )
// I wonder if we should be closing the file first.
// I also wonder if we really need to be doing all the ctx
// allocation/de-allocation constantly, or whether we can just re-use it.

View File

@ -66,7 +66,7 @@ int main( int argc, const char *argv[] ) {
double maxfps = 10.0;
unsigned int bitrate = 100000;
unsigned int ttl = 0;
EventStream::StreamMode replay = EventStream::MODE_SINGLE;
EventStream::StreamMode replay = EventStream::MODE_NONE;
std::string username;
std::string password;
char auth[64] = "";
@ -137,8 +137,17 @@ int main( int argc, const char *argv[] ) {
} else if ( !strcmp( name, "ttl" ) ) {
ttl = atoi(value);
} else if ( !strcmp( name, "replay" ) ) {
replay = !strcmp( value, "gapless" )?EventStream::MODE_ALL_GAPLESS:EventStream::MODE_SINGLE;
replay = !strcmp( value, "all" )?EventStream::MODE_ALL:replay;
if ( !strcmp(value, "gapless") ) {
replay = EventStream::MODE_ALL_GAPLESS;
} else if ( !strcmp(value, "all") ) {
replay = EventStream::MODE_ALL;
} else if ( !strcmp(value, "none") ) {
replay = EventStream::MODE_NONE;
} else if ( !strcmp(value, "single") ) {
replay = EventStream::MODE_SINGLE;
} else {
Error("Unsupported value %s for replay, defaulting to none", value);
}
} else if ( !strcmp( name, "connkey" ) ) {
connkey = atoi(value);
} else if ( !strcmp( name, "buffer" ) ) {

View File

@ -163,8 +163,19 @@ if [ $? -ne 0 ]; then
fi;
cd "$DIRECTORY.orig";
# Init submodules
git submodule init
git submodule update --init --recursive
# Cleanup
rm -rf .git
rm .gitignore
cd ../
tar zcf $DIRECTORY.orig.tar.gz $DIRECTORY.orig
cd $DIRECTORY.orig
# Generate Changlog
if [ "$DISTRO" == "trusty" ] || [ "$DISTRO" == "precise" ]; then
mv distros/ubuntu1204 debian
else
@ -189,12 +200,6 @@ if [ "$URGENCY" = "" ]; then
URGENCY="medium"
fi;
rm -rf .git
rm .gitignore
cd ../
tar zcf $DIRECTORY.orig.tar.gz $DIRECTORY.orig
cd $DIRECTORY.orig
if [ "$SNAPSHOT" == "stable" ]; then
cat <<EOF > debian/changelog
zoneminder ($VERSION-$DISTRO${PACKAGE_VERSION}) $DISTRO; urgency=$URGENCY
@ -288,6 +293,13 @@ else
SC="zoneminder_${VERSION}-${DISTRO}${PACKAGE_VERSION}_source.changes";
PPA="";
if [ "$RELEASE" != "" ]; then
# We need to use our official tarball for the original source, so grab it and overwrite our generated one.
if [ ! -e "$RELEASE.tar.gz" ]; then
echo "Grabbing official source tarball from github."
wget "https://github.com/ZoneMinder/zoneminder/archive/$RELEASE.tar.gz"
fi;
echo "Overwriting generated zoneminder_${VERSION}.orig.tar.gz with source tarball from github";
cp "$RELEASE.tar.gz" "zoneminder_${VERSION}.orig.tar.gz"
IFS='.' read -r -a VERSION <<< "$RELEASE"
PPA="ppa:iconnor/zoneminder-${VERSION[0]}.${VERSION[1]}"
else

View File

@ -1 +1 @@
1.32.0
1.32.1

View File

@ -383,7 +383,7 @@ function getNearEvents() {
parseSort();
if ( $user['MonitorIds'] )
$midSql = ' and MonitorId in ('.join( ',', preg_split( '/["\'\s]*,["\'\s]*/', $user['MonitorIds'] ) ).')';
$midSql = ' AND MonitorId IN ('.join( ',', preg_split( '/["\'\s]*,["\'\s]*/', $user['MonitorIds'] ) ).')';
else
$midSql = '';
@ -392,32 +392,40 @@ function getNearEvents() {
$sortOrder = 'asc';
}
$sql = "SELECT E.Id AS Id, E.StartTime AS StartTime 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=='asc'?'desc':'asc') . ' LIMIT 2';
$result = dbQuery( $sql );
while ( $id = dbFetchNext( $result, 'Id' ) ) {
if ( $id == $eventId ) {
$prevEvent = dbFetchNext( $result );
break;
}
$sql = "SELECT E.Id AS Id, E.StartTime AS StartTime 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.' AND E.Id<'.$event['Id'] . " ORDER BY $sortColumn ".($sortOrder=='asc'?'desc':'asc');
if ( $sortColumn != 'E.Id' ) {
# When sorting by starttime, if we have two events with the same starttime (diffreent monitors) then we should sort secondly by Id
$sql .= ', E.Id DESC';
}
$sql .= ' LIMIT 1';
$result = dbQuery( $sql );
$prevEvent = dbFetchNext( $result );
$sql = "SELECT E.Id AS Id, E.StartTime AS StartTime 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 LIMIT 2";
$result = dbQuery( $sql );
while ( $id = dbFetchNext( $result, 'Id' ) ) {
if ( $id == $eventId ) {
$nextEvent = dbFetchNext( $result );
break;
}
$sql = "SELECT E.Id AS Id, E.StartTime AS StartTime 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.' AND E.Id>'.$event['Id'] . " ORDER BY $sortColumn $sortOrder";
if ( $sortColumn != 'E.Id' ) {
# When sorting by starttime, if we have two events with the same starttime (diffreent monitors) then we should sort secondly by Id
$sql .= ', E.Id ASC';
}
$sql .= ' LIMIT 1';
$result = dbQuery( $sql );
$nextEvent = dbFetchNext( $result );
$result = array( 'EventId'=>$eventId );
$result['PrevEventId'] = empty($prevEvent)?0:$prevEvent['Id'];
$result['NextEventId'] = empty($nextEvent)?0:$nextEvent['Id'];
$result['PrevEventStartTime'] = empty($prevEvent)?0:$prevEvent['StartTime'];
$result['NextEventStartTime'] = empty($nextEvent)?0:$nextEvent['StartTime'];
$result['PrevEventDefVideoPath'] = empty($prevEvent)?0:(getEventDefaultVideoPath($prevEvent['Id']));
$result['NextEventDefVideoPath'] = empty($nextEvent)?0:(getEventDefaultVideoPath($nextEvent['Id']));
return( $result );
if ( $prevEvent ) {
$result['PrevEventId'] = $prevEvent['Id'];
$result['PrevEventStartTime'] = $prevEvent['StartTime'];
$result['PrevEventDefVideoPath'] = getEventDefaultVideoPath($prevEvent['Id']);
} else {
$result['PrevEventId'] = $result['PrevEventStartTime'] = $result['PrevEventDefVideoPath'] = 0;
}
if ( $nextEvent ) {
$result['NextEventId'] = $nextEvent['Id'];
$result['NextEventStartTime'] = $nextEvent['StartTime'];
$result['NextEventDefVideoPath'] = getEventDefaultVideoPath($nextEvent['Id']);
} else {
$result['NextEventId'] = $result['NextEventStartTime'] = $result['NextEventDefVideoPath'] = 0;
}
return $result;
}
?>

31
web/fonts/license.md Normal file
View File

@ -0,0 +1,31 @@
ZoneMinder uses certain 3rd party media assets/libraries for UI display purposes. Their licenses are listed in this file
### Material Design icons
Origin: https://github.com/google/material-design-icons
License: Apache 2.0 (https://github.com/google/material-design-icons/blob/master/LICENSE)
### Glyphicon halflings font
Origin: http://www.glyphicons.com/
License: MIT, As clarified below (http://www.glyphicons.com/license/)
```
License for GLYPHICONS Halflings in Bootstrap
GLYPHICONS Halflings font is also released as an extension of a Bootstrap www.getbootstrap.com for free and
it is released under the same license as Bootstrap. While you are not required to include attribution on your
Bootstrap-based projects, I would certainly appreciate any form of support, even a nice Tweet is enough.
Of course if you want, you can say thank you and support me by buying more icons on GLYPHICONS.com.
Jan Kovařík
```
ZoneMinder uses Bootstrap for UI and qualifies as a Bootstrap-based project.
Bootstrap is MIT licensed (https://github.com/twbs/bootstrap/blob/v4.1.3/LICENSE)

View File

@ -189,6 +189,8 @@ if ( ZM_OPT_USE_AUTH ) {
userLogin($authUser['Username'], $authUser['Password'], true);
}
}
} else if ( isset($_REQUEST['username']) and isset($_REQUEST['password']) ) {
userLogin($_REQUEST['username'], $_REQUEST['password'], false);
}
if ( !empty($user) ) {
// generate it once here, while session is open. Value will be cached in session and return when called later on

View File

@ -199,7 +199,7 @@ if ( currentView != 'none' && currentView != 'login' ) {
console.log( "Request Failed: " + err );
// The idea is that this should only fail due to auth, so reload the page
// which should go to login if it can't stay logged in.
window.location.href = thisUrl+'?view='+currentView;
window.location.reload( true );
});
}

View File

@ -174,7 +174,6 @@ if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
outputVideoStream( "evtStream", $streamSrc, reScale( $Event->Width(), $scale ), reScale( $Event->Height(), $scale ), ZM_MPEG_LIVE_FORMAT );
} else {
$streamSrc = $Event->getStreamSrc( array( 'mode'=>'jpeg', 'frame'=>$fid, 'scale'=>$scale, 'rate'=>$rate, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'replay'=>$replayMode) );
Warning("Streamsrc: $streamSrc");
if ( canStreamNative() ) {
outputImageStream( 'evtStream', $streamSrc, reScale( $Event->Width(), $scale ), reScale( $Event->Height(), $scale ), validHtmlStr($Event->Name()) );
} else {

View File

@ -44,7 +44,7 @@ if ( $_REQUEST['filter']['sql'] ) {
$countSql .= $_REQUEST['filter']['sql'];
$eventsSql .= $_REQUEST['filter']['sql'];
}
$eventsSql .= " ORDER BY $sortColumn $sortOrder";
$eventsSql .= " ORDER BY $sortColumn $sortOrder,Id $sortOrder";
$page = isset($_REQUEST['page']) ? validInt($_REQUEST['page']) : 0;
$limit = isset($_REQUEST['limit']) ? validInt($_REQUEST['limit']) : 0;

View File

@ -42,7 +42,7 @@ function vjsReplay() {
$j.ajaxSetup ({timeout: AJAX_TIMEOUT }); //sets timeout for all getJSON.
var cueFrames = null; //make cueFrames availaible even if we don't send another ajax query
var cueFrames = null; //make cueFrames available even if we don't send another ajax query
function initialAlarmCues (eventId) {
$j.getJSON(thisUrl + '?view=request&request=status&entity=frames&id=' + eventId, setAlarmCues); //get frames data for alarmCues and inserts into html
@ -55,70 +55,78 @@ function setAlarmCues (data) {
}
function renderAlarmCues (containerEl) {
if (cueFrames) {
var cueRatio = containerEl.width() / (cueFrames[cueFrames.length - 1].Delta * 100);
var minAlarm = Math.ceil(1/cueRatio);
var spanTimeStart = 0;
var spanTimeEnd = 0;
var alarmed = 0;
var alarmHtml = "";
var pixSkew = 0;
var skip = 0;
for (let i = 0; i < cueFrames.length; i++) {
skip = 0;
frame = cueFrames[i];
if (frame.Type == "Alarm" && alarmed == 0) { //From nothing to alarm. End nothing and start alarm.
alarmed = 1;
if (frame.Delta == 0) continue; //If event starts with an alarm or too few for a nonespan
spanTimeEnd = frame.Delta * 100;
spanTime = spanTimeEnd - spanTimeStart;
let pix = cueRatio * spanTime;
pixSkew += pix - Math.round(pix);//average out the rounding errors.
pix = Math.round(pix);
if ((pixSkew > 1 || pixSkew < -1) && pix + Math.round(pixSkew) > 0) { //add skew if it's a pixel and won't zero out span.
pix += Math.round(pixSkew);
pixSkew = pixSkew - Math.round(pixSkew);
}
alarmHtml += '<span class="alarmCue noneCue" style="width: ' + pix + 'px;"></span>';
spanTimeStart = spanTimeEnd;
} else if (frame.Type !== "Alarm" && alarmed == 1) { //from alarm to nothing. End alarm and start nothing.
futNone = 0;
indexPlus = i+1;
if (((frame.Delta * 100) - spanTimeStart) < minAlarm && indexPlus < cueFrames.length) continue; //alarm is too short and there is more event
while (futNone < minAlarm) { //check ahead to see if there's enough for a nonespan
if (indexPlus >= cueFrames.length) break; //check if end of event.
futNone = (cueFrames[indexPlus].Delta *100) - (frame.Delta *100);
if (cueFrames[indexPlus].Type == "Alarm") {
i = --indexPlus;
skip = 1;
break;
}
indexPlus++;
}
if (skip == 1) continue; //javascript doesn't support continue 2;
spanTimeEnd = frame.Delta *100;
spanTime = spanTimeEnd - spanTimeStart;
alarmed = 0;
pix = cueRatio * spanTime;
pixSkew += pix - Math.round(pix);
pix = Math.round(pix);
if ((pixSkew > 1 || pixSkew < -1) && pix + Math.round(pixSkew) > 0) {
pix += Math.round(pixSkew);
pixSkew = pixSkew - Math.round(pixSkew);
}
alarmHtml += '<span class="alarmCue" style="width: ' + pix + 'px;"></span>';
spanTimeStart = spanTimeEnd;
} else if (frame.Type == "Alarm" && alarmed == 1 && i + 1 >= cueFrames.length) { //event ends on an alarm
spanTimeEnd = frame.Delta * 100;
spanTime = spanTimeEnd - spanTimeStart;
alarmed = 0;
pix = Math.round(cueRatio * spanTime);
if (pixSkew >= .5 || pixSkew <= -.5) pix += Math.round(pixSkew);
alarmHtml += '<span class="alarmCue" style="width: ' + pix + 'px;"></span>';
}
}
return alarmHtml;
if ( !( cueFrames && cueFrames.length ) ) {
console.log("No cue frames for event");
return;
}
// This uses the Delta of the last frame to get the length of the event. I can't help but wonder though
// if we shouldn't just use the event length endtime-starttime
var cueRatio = containerEl.width() / (cueFrames[cueFrames.length - 1].Delta * 100);
var minAlarm = Math.ceil(1/cueRatio);
var spanTimeStart = 0;
var spanTimeEnd = 0;
var alarmed = 0;
var alarmHtml = "";
var pixSkew = 0;
var skip = 0;
var num_cueFrames = cueFrames.length;
for ( let i = 0; i < num_cueFrames; i++ ) {
skip = 0;
frame = cueFrames[i];
if (frame.Type == "Alarm" && alarmed == 0) { //From nothing to alarm. End nothing and start alarm.
alarmed = 1;
if (frame.Delta == 0) continue; //If event starts with an alarm or too few for a nonespan
spanTimeEnd = frame.Delta * 100;
spanTime = spanTimeEnd - spanTimeStart;
let pix = cueRatio * spanTime;
pixSkew += pix - Math.round(pix);//average out the rounding errors.
pix = Math.round(pix);
if ((pixSkew > 1 || pixSkew < -1) && pix + Math.round(pixSkew) > 0) { //add skew if it's a pixel and won't zero out span.
pix += Math.round(pixSkew);
pixSkew = pixSkew - Math.round(pixSkew);
}
alarmHtml += '<span class="alarmCue noneCue" style="width: ' + pix + 'px;"></span>';
spanTimeStart = spanTimeEnd;
} else if (frame.Type !== "Alarm" && alarmed == 1) { //from alarm to nothing. End alarm and start nothing.
futNone = 0;
indexPlus = i+1;
if (((frame.Delta * 100) - spanTimeStart) < minAlarm && indexPlus < num_cueFrames) {
//alarm is too short and there is more event
continue;
}
while (futNone < minAlarm) { //check ahead to see if there's enough for a nonespan
if (indexPlus >= cueFrames.length) break; //check if end of event.
futNone = (cueFrames[indexPlus].Delta *100) - (frame.Delta *100);
if (cueFrames[indexPlus].Type == "Alarm") {
i = --indexPlus;
skip = 1;
break;
}
indexPlus++;
}
if (skip == 1) continue; //javascript doesn't support continue 2;
spanTimeEnd = frame.Delta *100;
spanTime = spanTimeEnd - spanTimeStart;
alarmed = 0;
pix = cueRatio * spanTime;
pixSkew += pix - Math.round(pix);
pix = Math.round(pix);
if ((pixSkew > 1 || pixSkew < -1) && pix + Math.round(pixSkew) > 0) {
pix += Math.round(pixSkew);
pixSkew = pixSkew - Math.round(pixSkew);
}
alarmHtml += '<span class="alarmCue" style="width: ' + pix + 'px;"></span>';
spanTimeStart = spanTimeEnd;
} else if (frame.Type == "Alarm" && alarmed == 1 && i + 1 >= cueFrames.length) { //event ends on an alarm
spanTimeEnd = frame.Delta * 100;
spanTime = spanTimeEnd - spanTimeStart;
alarmed = 0;
pix = Math.round(cueRatio * spanTime);
if (pixSkew >= .5 || pixSkew <= -.5) pix += Math.round(pixSkew);
alarmHtml += '<span class="alarmCue" style="width: ' + pix + 'px;"></span>';
}
}
return alarmHtml;
}
function setButtonState( element, butClass ) {
@ -147,7 +155,7 @@ function changeScale() {
} else {
eventViewer = $j(vid ? '#videoobj' : '#evtStream');
}
if (scale == "auto") {
if ( scale == "auto" ) {
let newSize = scaleToFit(eventData.Width, eventData.Height, eventViewer, bottomEl);
newWidth = newSize.width;
newHeight = newSize.height;
@ -157,21 +165,22 @@ function changeScale() {
newWidth = eventData.Width * scale / SCALE_BASE;
newHeight = eventData.Height * scale / SCALE_BASE;
}
if (!(streamMode == 'stills')) eventViewer.width(newWidth); //stills handles its own width
if ( !(streamMode == 'stills') )
eventViewer.width(newWidth); //stills handles its own width
eventViewer.height(newHeight);
if ( !vid ) { // zms needs extra sizing
streamScale(scale == "auto" ? autoScale : scale);
drawProgressBar();
}
if (streamMode == 'stills') {
if ( streamMode == 'stills' ) {
slider.autosize();
alarmCue.html(renderAlarmCues($j('#thumbsSliderPanel')));
} else {
alarmCue.html(renderAlarmCues(eventViewer));//just re-render alarmCues. skip ajax call
}
if (scale == "auto") {
if ( scale == "auto" ) {
Cookie.write('zmEventScaleAuto', 'auto', {duration: 10*365});
}else{
} else {
Cookie.write('zmEventScale'+eventData.MonitorId, scale, {duration: 10*365});
Cookie.dispose('zmEventScaleAuto');
}
@ -193,7 +202,7 @@ var lastEventId = 0;
var zmsBroke = false; //Use alternate navigation if zms has crashed
function getCmdResponse( respObj, respText ) {
if ( checkStreamForErrors( "getCmdResponse", respObj ) ) {
if ( checkStreamForErrors("getCmdResponse", respObj) ) {
console.log('Got an error from getCmdResponse');
console.log(respObj);
console.log(respText);

View File

@ -921,13 +921,19 @@ if ( $monitor->Type() == 'Local' ) {
<?php
$videowriteropts = array(
0 => 'Disabled',
1 => 'X264 Encode',
);
if ($monitor->Type() == 'Ffmpeg' )
$videowriteropts[2] = 'H264 Camera Passthrough';
if (stripos(php_uname('m'), 'arm') === false )
$videowriteropts[1] = 'X264 Encode';
else
$videowriteropts[1] = array('text'=>'X264 Encode - Not compatible on Arm','disabled'=>1);
if ($monitor->Type() == 'Ffmpeg' )
$videowriteropts[2] = 'H264 Camera Passthrough';
else
$videowriteropts[2] = array('text'=>'H264 Camera Passthrough - only for FFMPEG','disabled'=>1);
echo htmlselect( 'newMonitor[VideoWriter]', $videowriteropts, $monitor->VideoWriter() );
echo htmlselect( 'newMonitor[VideoWriter]', $videowriteropts, $monitor->VideoWriter() );
?>
</td></tr>
<tr><td><?php echo translate('OptionalEncoderParam') ?></td><td><textarea name="newMonitor[EncoderParameters]" rows="4" cols="36"><?php echo validHtmlStr($monitor->EncoderParameters()) ?></textarea></td></tr>

View File

@ -70,8 +70,8 @@ session_start();
$layout_id = '';
if ( isset($_COOKIE['zmMontageLayout']) ) {
$layout_id = $_SESSION['zmMontageLayout'] = $_COOKIE['zmMontageLayout'];
} elseif ( isset($_SESSION['zmMontageLayout']) ) {
$layout_id = $_SESSION['zmMontageLayout'];
#} elseif ( isset($_SESSION['zmMontageLayout']) ) {
#$layout_id = $_SESSION['zmMontageLayout'];
}
$options = array();
@ -87,15 +87,15 @@ if ( $Layout and ( $Layout->Name() != 'Freeform' ) ) {
if ( isset($_COOKIE['zmMontageWidth']) and $_COOKIE['zmMontageWidth'] ) {
$_SESSION['zmMontageWidth'] = $options['width'] = $_COOKIE['zmMontageWidth'];
} elseif ( isset($_SESSION['zmMontageWidth']) and $_SESSION['zmMontageWidth'] ) {
$options['width'] = $_SESSION['zmMontageWidth'];
#} elseif ( isset($_SESSION['zmMontageWidth']) and $_SESSION['zmMontageWidth'] ) {
#$options['width'] = $_SESSION['zmMontageWidth'];
} else
$options['width'] = '';
if ( isset($_COOKIE['zmMontageHeight']) and $_COOKIE['zmMontageHeight'] )
$_SESSION['zmMontageHeight'] = $options['height'] = $_COOKIE['zmMontageHeight'];
else if ( isset($_SESSION['zmMontageHeight']) and $_SESSION['zmMontageHeight'] )
$options['height'] = $_SESSION['zmMontageHeight'];
#else if ( isset($_SESSION['zmMontageHeight']) and $_SESSION['zmMontageHeight'] )
#$options['height'] = $_SESSION['zmMontageHeight'];
else
$options['height'] = '';
@ -134,7 +134,7 @@ xhtmlHeaders(__FILE__, translate('Montage'));
<body>
<div id="page">
<?php echo getNavBarHTML() ?>
<div id="header">&nbsp&nbsp
<div id="header">&nbsp;&nbsp;
<a href="#"><span id="hdrbutton" class="glyphicon glyphicon-menu-up pull-right"></span></a>
<div id="flipMontageHeader">
<div id="headerButtons">