Merge branch 'master' into master

This commit is contained in:
Isaac Connor 2022-02-02 12:28:17 -05:00 committed by GitHub
commit 3feb4fcc51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 147 additions and 108 deletions

View File

@ -21,6 +21,7 @@ jobs:
steps:
- uses: actions/checkout@v2
with:
fetch-depth: '0'
submodules: recursive
- name: Run packpack
env:
@ -29,3 +30,13 @@ jobs:
DIST: ${{ matrix.os_dist.dist }}
DOCKER_REPO: iconzm/packpack
run: utils/packpack/startpackpack.sh
- name: Publish
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.ZMREPO_SSH_KEY }}
ARGS: "-rltgoDzvO"
SOURCE: build/
REMOTE_HOST: ${{ secrets.ZMREPO_HOST }}
REMOTE_USER: ${{ secrets.ZMREPO_SSH_USER }}
TARGET: debian/master/mini-dinstall/incoming/

View File

@ -1,7 +1,6 @@
ZoneMinder
==========
[![Build Status](https://travis-ci.org/ZoneMinder/zoneminder.png)](https://travis-ci.org/ZoneMinder/zoneminder)
[![Bounty Source](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received)
[![Join Slack](https://github.com/ozonesecurity/ozonebase/blob/master/img/slacksm.png?raw=true)](https://join.slack.com/t/zoneminder-chat/shared_invite/enQtNTU0NDkxMDM5NDQwLTdhZmQ5Y2M2NWQyN2JkYTBiN2ZkMzIzZGQ0MDliMTRmM2FjZWRlYzUwYTQ2MjMwMTVjMzQ1NjYxOTdmMjE2MTE)
@ -25,7 +24,7 @@ https://github.com/ZoneMinder/zmdockerfiles
This is the recommended method to install ZoneMinder onto your system. ZoneMinder packages are maintained for the following distros:
- Ubuntu via [Iconnor's PPA](https://launchpad.net/~iconnor)
- Ubuntu via [Isaac Connor's PPA](https://launchpad.net/~iconnor)
- Debian from their [default repository](https://packages.debian.org/search?searchon=names&keywords=zoneminder)
- RHEL/CentOS and clones via [RPM Fusion](http://rpmfusion.org)
- Fedora via [RPM Fusion](http://rpmfusion.org)

@ -1 +1 @@
Subproject commit cd7fd49becad6010a1b8466bfebbd93999a39878
Subproject commit eab32851421ffe54fec0229c3efc44c642bc8d46

View File

@ -42,7 +42,8 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,libswscale5|libswscale4
,libswresample3|libswresample2
,ffmpeg
,libdate-manip-perl, libmime-lite-perl, libmime-tools-perl
,libcurl4
,libdatetime-perl, libdate-manip-perl, libmime-lite-perl, libmime-tools-perl
,libdbd-mysql-perl
,libphp-serialization-perl
,libmodule-load-conditional-perl

View File

@ -35,7 +35,7 @@ Run the following commands.
sudo apt install mariadb-server
sudo apt install zoneminder
When mariadb is installed for the first time, it doesn't add a password to the root user. Therefore, for security, it is recommended to run ``mysql secure installation``.
By default MariaDB uses `unix socket authentication`_, so no root user password is required (root MariaDB user access only available to local root Linux user). If you wish, you can set a root MariaDB password (and apply other security tweaks) by running `mariadb-secure-installation`_.
**Step 3:** Setup permissions for zm.conf
@ -337,3 +337,6 @@ Reload Apache to enable your changes and then start ZoneMinder.
sudo systemctl start zoneminder
You are now ready to go with ZoneMinder. Open a browser and type either ``localhost/zm`` one the local machine or ``{IP-OF-ZM-SERVER}/zm`` if you connect from a remote computer.
.. _unix socket authentication: https://mariadb.com/kb/en/authentication-plugin-unix-socket/
.. _mariadb-secure-installation: https://mariadb.com/kb/en/mysql_secure_installation/

View File

@ -444,11 +444,6 @@ if ( $version ) {
print( "\nUpgrading database to version ".ZM_VERSION."\n" );
# Update config first of all
migratePaths();
ZoneMinder::Config::loadConfigFromDB();
ZoneMinder::Config::saveConfigToDB();
my $cascade = undef;
if ( $cascade || $version eq "1.19.0" ) {
# Patch the database

View File

@ -245,6 +245,7 @@ class Socket : public CommsBase {
}
virtual ssize_t recv(std::string &msg) const {
msg.reserve(ZM_NETWORK_BUFSIZ);
std::vector<char> buffer(msg.capacity());
ssize_t nBytes;
if ((nBytes = ::recv(mSd, buffer.data(), buffer.size(), 0)) < 0) {

View File

@ -251,7 +251,7 @@ void zmDbQueue::process() {
mCondition.wait(lock);
}
while (!mQueue.empty()) {
if (mQueue.size() > 20) {
if (mQueue.size() > 30) {
Logger *log = Logger::fetch();
Logger::Level db_level = log->databaseLevel();
log->databaseLevel(Logger::NOLOG);

View File

@ -301,9 +301,9 @@ void Event::updateNotes(const StringSetMap &newNoteSetMap) {
} // end if update
} // void Event::updateNotes(const StringSetMap &newNoteSetMap)
void Event::AddPacket(const std::shared_ptr<ZMPacket>&packet) {
void Event::AddPacket(ZMLockedPacket *packetlock) {
std::unique_lock<std::mutex> lck(packet_queue_mutex);
packet_queue.push(packet);
packet_queue.push(packetlock);
packet_queue_condition.notify_one();
}
@ -423,7 +423,7 @@ void Event::AddFrame(Image *image,
// If this is the first frame, we should add a thumbnail to the event directory
if ((frames == 1) || (score > max_score)) {
write_to_db = true; // web ui might show this as thumbnail, so db needs to know about it.
Debug(1, "Writing snapshot");
Debug(1, "Writing snapshot to %s", snapshot_file.c_str());
WriteFrameImage(image, timestamp, snapshot_file.c_str());
} else {
Debug(1, "Not Writing snapshot because frames %d score %d > max %d", frames, score, max_score);
@ -435,17 +435,19 @@ void Event::AddFrame(Image *image,
if (!alarm_frame_written) {
write_to_db = true; // OD processing will need it, so the db needs to know about it
alarm_frame_written = true;
Debug(1, "Writing alarm image");
WriteFrameImage(image, timestamp, alarm_file.c_str());
Debug(1, "Writing alarm image to %s", alarm_file.c_str());
if (!WriteFrameImage(image, timestamp, alarm_file.c_str())) {
Error("Failed to write alarm frame image to %s", alarm_file.c_str());
}
} else {
Debug(3, "Not Writing alarm image because alarm frame already written");
}
if (alarm_image and (save_jpegs & 2)) {
std::string event_file = stringtf(staticConfig.analyse_file_format.c_str(), path.c_str(), frames);
Debug(1, "Writing analysis frame %d", frames);
Debug(1, "Writing analysis frame %d to %s", frames, event_file.c_str());
if (!WriteFrameImage(alarm_image, timestamp, event_file.c_str(), true)) {
Error("Failed to write analysis frame image");
Error("Failed to write analysis frame image to %s", event_file.c_str());
}
}
} // end if is an alarm frame
@ -682,7 +684,9 @@ void Event::Run() {
while (true) {
if (!packet_queue.empty()) {
Debug(1, "adding packet");
this->AddPacket_(packet_queue.front());
const ZMLockedPacket * packet_lock = packet_queue.front();
this->AddPacket_(packet_lock->packet_);
delete packet_lock;
packet_queue.pop();
} else {
if (terminate_ or zm_terminate) {

View File

@ -105,7 +105,7 @@ class Event {
void createNotes(std::string &notes);
std::queue<std::shared_ptr<ZMPacket>> packet_queue;
std::queue<ZMLockedPacket *> packet_queue;
std::mutex packet_queue_mutex;
std::condition_variable packet_queue_condition;
@ -134,7 +134,7 @@ class Event {
SystemTimePoint EndTime() const { return end_time; }
TimePoint::duration Duration() const { return end_time - start_time; };
void AddPacket(const std::shared_ptr<ZMPacket> &p);
void AddPacket(ZMLockedPacket *);
void AddPacket_(const std::shared_ptr<ZMPacket> &p);
bool WritePacket(const std::shared_ptr<ZMPacket> &p);
bool SendFrameImage(const Image *image, bool alarm_frame=false);

View File

@ -257,8 +257,8 @@ void zm_dump_stream_format(AVFormatContext *ic, int i, int index, int is_output)
Debug(1, "ids [0x%x]", st->id);
if (lang)
Debug(1, "language (%s)", lang->value);
Debug(1, "frames:%d, frame_size:%d stream timebase: %d/%d",
st->codec_info_nb_frames, codec->frame_size,
Debug(1, "frame_size:%d stream timebase: %d/%d",
codec->frame_size,
st->time_base.num, st->time_base.den
);

View File

@ -24,6 +24,7 @@
#include "zm_utils.h"
#include <algorithm>
#include <fcntl.h>
#include <mutex>
#include <sys/stat.h>
#include <unistd.h>
@ -80,6 +81,8 @@ imgbufcpy_fptr_t fptr_imgbufcpy;
/* Font */
static ZmFont font;
std::mutex jpeg_mutex;
void Image::update_function_pointers() {
/* Because many loops are unrolled and work on 16 colours/time or 4 pixels/time, we have to meet requirements */
if ( pixels % 16 || pixels % 12 ) {
@ -1083,6 +1086,9 @@ bool Image::WriteJpeg(const std::string &filename,
const int &quality_override,
SystemTimePoint timestamp,
bool on_blocking_abort) const {
// jpeg libs are not thread safe
std::unique_lock<std::mutex> lck(jpeg_mutex);
if (config.colour_jpeg_files && (colours == ZM_COLOUR_GRAY8)) {
Image temp_image(*this);
temp_image.Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB);
@ -1366,6 +1372,8 @@ bool Image::EncodeJpeg(JOCTET *outbuffer, int *outbuffer_size, int quality_overr
return temp_image.EncodeJpeg(outbuffer, outbuffer_size, quality_override);
}
std::unique_lock<std::mutex> lck(jpeg_mutex);
int quality = quality_override ? quality_override : config.jpeg_stream_quality;
struct jpeg_compress_struct *cinfo = encodejpg_ccinfo[quality];

View File

@ -2321,34 +2321,37 @@ bool Monitor::Analyse() {
shared_data->state = state = IDLE;
} // end if ( trigger_data->trigger_state != TRIGGER_OFF )
if (event) event->AddPacket(snap);
packetqueue.clearPackets(snap);
if (snap->codec_type == AVMEDIA_TYPE_VIDEO) {
// Only do these if it's a video packet.
shared_data->last_read_index = snap->image_index;
analysis_image_count++;
}
if (event) {
event->AddPacket(packet_lock);
} else {
// In the case where people have pre-alarm frames, the web ui will generate the frame images
// from the mp4. So no one will notice anyways.
if (snap->image and (videowriter == PASSTHROUGH)) {
if (!savejpegs) {
Debug(1, "Deleting image data for %d", snap->image_index);
// Don't need raw images anymore
delete snap->image;
snap->image = nullptr;
}
if (snap->analysis_image and !(savejpegs & 2)) {
Debug(1, "Deleting analysis image data for %d", snap->image_index);
delete snap->analysis_image;
snap->analysis_image = nullptr;
}
}
delete packet_lock;
}
} // end scope for event_lock
// In the case where people have pre-alarm frames, the web ui will generate the frame images
// from the mp4. So no one will notice anyways.
if (snap->image and (videowriter == PASSTHROUGH)) {
if (!savejpegs) {
Debug(1, "Deleting image data for %d", snap->image_index);
// Don't need raw images anymore
delete snap->image;
snap->image = nullptr;
}
if (snap->analysis_image and !(savejpegs & 2)) {
Debug(1, "Deleting analysis image data for %d", snap->image_index);
delete snap->analysis_image;
snap->analysis_image = nullptr;
}
}
packetqueue.clearPackets(snap);
if (snap->codec_type == AVMEDIA_TYPE_VIDEO) {
// Only do these if it's a video packet.
shared_data->last_read_index = snap->image_index;
analysis_image_count++;
}
packetqueue.increment_it(analysis_it);
delete packet_lock;
//packetqueue.unlock(packet_lock);
shared_data->last_read_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
@ -2921,16 +2924,16 @@ Event * Monitor::openEvent(
// Write out starting packets, do not modify packetqueue it will garbage collect itself
while (starting_packet and ((*start_it) != *analysis_it)) {
event->AddPacket(starting_packet);
event->AddPacket(starting_packet_lock);
// Have added the packet, don't want to unlock it until we have locked the next
packetqueue.increment_it(start_it);
if ((*start_it) == *analysis_it) {
if (starting_packet_lock) delete starting_packet_lock;
//if (starting_packet_lock) delete starting_packet_lock;
break;
}
ZMLockedPacket *lp = packetqueue.get_packet(start_it);
delete starting_packet_lock;
//delete starting_packet_lock;
if (!lp) return nullptr; // only on terminate FIXME
starting_packet_lock = lp;
starting_packet = lp->packet_;

View File

@ -22,6 +22,7 @@
#include "zm_config.h"
#include "zm_monitor.h"
#include "zm_packet.h"
#include "zm_signal.h"
RemoteCameraRtsp::RemoteCameraRtsp(
const Monitor *monitor,
@ -126,8 +127,8 @@ int RemoteCameraRtsp::Disconnect() {
int RemoteCameraRtsp::PrimeCapture() {
Debug(2, "Waiting for sources");
for (int i = 0; i < 100 && !rtspThread->hasSources(); i++) {
std::this_thread::sleep_for(Microseconds(100));
for (int i = 100; i && !zm_terminate && !rtspThread->hasSources(); i--) {
std::this_thread::sleep_for(Microseconds(10000));
}
if (!rtspThread->hasSources()) {
@ -168,8 +169,10 @@ int RemoteCameraRtsp::PrimeCapture() {
}
} // end foreach stream
if ( mVideoStreamId == -1 )
Fatal("Unable to locate video stream");
if ( mVideoStreamId == -1 ) {
Error("Unable to locate video stream");
return -1;
}
if ( mAudioStreamId == -1 )
Debug(3, "Unable to locate audio stream");
@ -179,17 +182,22 @@ int RemoteCameraRtsp::PrimeCapture() {
// Find the decoder for the video stream
AVCodec *codec = avcodec_find_decoder(mVideoCodecContext->codec_id);
if ( codec == nullptr )
Panic("Unable to locate codec %d decoder", mVideoCodecContext->codec_id);
if ( codec == nullptr ) {
Error("Unable to locate codec %d decoder", mVideoCodecContext->codec_id);
return -1;
}
// Open codec
if ( avcodec_open2(mVideoCodecContext, codec, nullptr) < 0 )
Panic("Can't open codec");
if ( avcodec_open2(mVideoCodecContext, codec, nullptr) < 0 ) {
Error("Can't open codec");
return -1;
}
int pSize = av_image_get_buffer_size(imagePixFormat, width, height, 1);
if ( (unsigned int)pSize != imagesize ) {
Fatal("Image size mismatch. Required: %d Available: %llu", pSize, imagesize);
Error("Image size mismatch. Required: %d Available: %llu", pSize, imagesize);
return -1;
}
return 1;
@ -208,18 +216,13 @@ int RemoteCameraRtsp::PreCapture() {
int RemoteCameraRtsp::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
int frameComplete = false;
AVPacket *packet = &zm_packet->packet;
if ( !zm_packet->image ) {
Debug(1, "Allocating image %dx%d %d colours %d", width, height, colours, subpixelorder);
zm_packet->image = new Image(width, height, colours, subpixelorder);
}
while (!frameComplete) {
buffer.clear();
if (!rtspThread || rtspThread->IsStopped())
if (!rtspThread || rtspThread->IsStopped() || zm_terminate)
return -1;
if ( rtspThread->getFrame(buffer) ) {
if (rtspThread->getFrame(buffer)) {
Debug(3, "Read frame %d bytes", buffer.size());
Hexdump(4, buffer.head(), 16);
@ -254,36 +257,20 @@ int RemoteCameraRtsp::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
//while ( (!frameComplete) && (buffer.size() > 0) ) {
if ( buffer.size() > 0 ) {
packet->data = buffer.head();
packet->data = (uint8_t*)av_malloc(buffer.size());
memcpy(packet->data, buffer.head(), buffer.size());
//packet->data = buffer.head();
packet->size = buffer.size();
bytes += packet->size;
buffer -= packet->size;
struct timeval now;
gettimeofday(&now, NULL);
gettimeofday(&now, nullptr);
packet->pts = packet->dts = now.tv_sec*1000000+now.tv_usec;
int bytes_consumed = zm_packet->decode(mVideoCodecContext);
if ( bytes_consumed < 0 ) {
Error("Error while decoding frame %d", frameCount);
//Hexdump(Logger::ERROR, buffer.head(), buffer.size()>256?256:buffer.size());
}
buffer -= packet->size;
if ( bytes_consumed ) {
zm_dump_video_frame(zm_packet->in_frame, "remote_rtsp_decode");
if (!mVideoStream->codecpar->width) {
zm_dump_codec(mVideoCodecContext);
zm_dump_codecpar(mVideoStream->codecpar);
mVideoStream->codecpar->width = zm_packet->in_frame->width;
mVideoStream->codecpar->height = zm_packet->in_frame->height;
zm_dump_codecpar(mVideoStream->codecpar);
}
zm_packet->codec_type = mVideoCodecContext->codec_type;
zm_packet->stream = mVideoStream;
frameComplete = true;
Debug(2, "Frame: %d - %d/%d", frameCount, bytes_consumed, buffer.size());
packet->data = nullptr;
packet->size = 0;
}
zm_packet->codec_type = mVideoCodecContext->codec_type;
zm_packet->stream = mVideoStream;
frameComplete = true;
Debug(2, "Frame: %d - %d/%d", frameCount, packet->size, buffer.size());
}
} /* getFrame() */
} // end while true

View File

@ -277,7 +277,7 @@ void RtpCtrlThread::Run() {
TimePoint last_receive = std::chrono::steady_clock::now();
bool timeout = false; // used as a flag that we had a timeout, and then sent an RR to see if we wake back up. Real timeout will happen when this is true.
while (!mTerminate && select.wait() >= 0) {
while (!mTerminate && (select.wait() >= 0)) {
TimePoint now = std::chrono::steady_clock::now();
zm::Select::CommsList readable = select.getReadable();
if ( readable.size() == 0 ) {

View File

@ -45,8 +45,10 @@ RtpSource::RtpSource(
mFrame(65536),
mFrameCount(0),
mFrameGood(true),
prevM(false),
mFrameReady(false),
mFrameProcessed(false)
mFrameProcessed(false),
mTerminate(false)
{
char hostname[256] = "";
gethostname(hostname, sizeof(hostname));

View File

@ -91,8 +91,6 @@ private:
bool mFrameGood;
bool prevM;
bool mTerminate;
bool mFrameReady;
std::condition_variable mFrameReadyCv;
std::mutex mFrameReadyMutex;
@ -100,6 +98,7 @@ private:
bool mFrameProcessed;
std::condition_variable mFrameProcessedCv;
std::mutex mFrameProcessedMutex;
bool mTerminate;
private:
void init(uint16_t seq);

View File

@ -298,6 +298,14 @@ int main(int argc, char *argv[]) {
session->GetMediaSessionId(), xop::channel_1, audioFifoPath);
audioSource->setFrequency(monitor->GetAudioFrequency());
audioSource->setChannels(monitor->GetAudioChannels());
} else if (std::string::npos != audioFifoPath.find("pcm_alaw")) {
Debug(1, "Adding G711A source at %dHz %d channels",
monitor->GetAudioFrequency(), monitor->GetAudioChannels());
session->AddSource(xop::channel_1, xop::G711ASource::CreateNew());
audioSource = new ADTS_ZoneMinderFifoSource(rtspServer,
session->GetMediaSessionId(), xop::channel_1, audioFifoPath);
audioSource->setFrequency(monitor->GetAudioFrequency());
audioSource->setChannels(monitor->GetAudioChannels());
} else {
Warning("Unknown format in %s", audioFifoPath.c_str());
}

View File

@ -118,7 +118,7 @@ commonprep () {
fi
fi
RTSPVER="cd7fd49becad6010a1b8466bfebbd93999a39878"
RTSPVER="eab32851421ffe54fec0229c3efc44c642bc8d46"
if [ -e "build/RtspServer-${RTSPVER}.tar.gz" ]; then
echo "Found existing RtspServer ${RTSPVER} tarball..."
else

View File

@ -187,6 +187,9 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
$col_str = 'E.*, M.Name AS Monitor';
$sql = 'SELECT ' .$col_str. ' FROM `Events` AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id'.$where.($sort?' ORDER BY '.$sort.' '.$order:'');
if ($filter->limit() and !count($filter->pre_sql_conditions()) and !count($filter->post_sql_conditions())) {
$sql .= ' LIMIT '.$filter->limit();
}
$storage_areas = ZM\Storage::find();
$StorageById = array();
@ -213,6 +216,12 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
$unfiltered_rows[] = $row;
} # end foreach row
# Filter limits come before pagination limits.
if ($filter->limit() and ($filter->limit() > count($unfiltered_rows))) {
ZM\Debug("Filtering rows due to filter->limit " . count($unfiltered_rows)." limit: ".$filter->limit());
$unfiltered_rows = array_slice($unfiltered_rows, 0, $filter->limit());
}
ZM\Debug('Have ' . count($unfiltered_rows) . ' events matching base filter.');
$filtered_rows = null;
@ -251,8 +260,10 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
$filtered_rows = $unfiltered_rows;
} # end if search_filter->terms() > 1
if ($limit)
if ($limit) {
ZM\Debug("Filtering rows due to limit " . count($filtered_rows)." offset: $offset limit: $limit");
$filtered_rows = array_slice($filtered_rows, $offset, $limit);
}
$returned_rows = array();
foreach ($filtered_rows as $row) {

View File

@ -38,6 +38,14 @@ function MonitorStream(monitorData) {
}
return this.element;
};
this.getFrame = function() {
if (this.frame) return this.frame;
this.frame = document.getElementById('imageFeed'+this.id);
if (!this.frame) {
console.error("No frame div for #imageFeed"+this.id);
}
return this.frame;
};
/* if the img element didn't have a src, this would fill it in, causing it to show. */
this.show = function() {
@ -161,7 +169,7 @@ function MonitorStream(monitorData) {
this.setup_onclick = function(func) {
this.onclick = func;
const el = this.getElement();
const el = this.getFrame();
if (!el) return;
el.addEventListener('click', this.onclick, false);
};

View File

@ -15,7 +15,11 @@ input[name="newMonitor[Path]"],
input[name="newMonitor[SecondPath]"],
input[name="newMonitor[LabelFormat]"],
input[name="newMonitor[ControlDevice]"],
input[name="newMonitor[ControlAddress]"] {
input[name="newMonitor[ControlAddress]"],
input[name="newMonitor[ONVIF_URL]"],
input[name="newMonitor[ONVIF_Username]"],
input[name="newMonitor[ONVIF_Password]"],
input[name="newMonitor[ONVIF_Options]"] {
width: 100%;
}
input[name="newMonitor[LabelFormat]"]{

View File

@ -109,7 +109,8 @@ stateStrings[STATE_TAPE] = "<?php echo translate('Record') ?>";
<?php
global $config;
foreach ($config as $name=>$c) {
if (!$c['Private'])
echo 'const '. $name . ' = \''.$c['Value'].'\''.PHP_EOL;
if (!$c['Private']) {
echo 'const '. $name . ' = \''.preg_replace('/(\n\r?)/', '\\\\$1', $c['Value']).'\';'.PHP_EOL;
}
}
?>

View File

@ -1,9 +1,3 @@
<?php
if ( ZM_OPT_USE_GEOLOCATION ) {
echo 'var ZM_OPT_GEOLOCATION_TILE_PROVIDER=\''.ZM_OPT_GEOLOCATION_TILE_PROVIDER.'\''.PHP_EOL;
echo 'var ZM_OPT_GEOLOCATION_ACCESS_TOKEN=\''.ZM_OPT_GEOLOCATION_ACCESS_TOKEN.'\''.PHP_EOL;
}
?>
var optControl = <?php echo ZM_OPT_CONTROL ?>;
var hasOnvif = <?php echo ZM_HAS_ONVIF ?>;
var defaultAspectRatio = '<?php echo ZM_DEFAULT_ASPECT_RATIO ?>';