Merge branch 'master' into master
This commit is contained in:
commit
3feb4fcc51
|
@ -21,6 +21,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
|
fetch-depth: '0'
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Run packpack
|
- name: Run packpack
|
||||||
env:
|
env:
|
||||||
|
@ -29,3 +30,13 @@ jobs:
|
||||||
DIST: ${{ matrix.os_dist.dist }}
|
DIST: ${{ matrix.os_dist.dist }}
|
||||||
DOCKER_REPO: iconzm/packpack
|
DOCKER_REPO: iconzm/packpack
|
||||||
run: utils/packpack/startpackpack.sh
|
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/
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
ZoneMinder
|
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)
|
[![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)
|
[![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:
|
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)
|
- Debian from their [default repository](https://packages.debian.org/search?searchon=names&keywords=zoneminder)
|
||||||
- RHEL/CentOS and clones via [RPM Fusion](http://rpmfusion.org)
|
- RHEL/CentOS and clones via [RPM Fusion](http://rpmfusion.org)
|
||||||
- Fedora via [RPM Fusion](http://rpmfusion.org)
|
- Fedora via [RPM Fusion](http://rpmfusion.org)
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit cd7fd49becad6010a1b8466bfebbd93999a39878
|
Subproject commit eab32851421ffe54fec0229c3efc44c642bc8d46
|
|
@ -42,7 +42,8 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
|
||||||
,libswscale5|libswscale4
|
,libswscale5|libswscale4
|
||||||
,libswresample3|libswresample2
|
,libswresample3|libswresample2
|
||||||
,ffmpeg
|
,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
|
,libdbd-mysql-perl
|
||||||
,libphp-serialization-perl
|
,libphp-serialization-perl
|
||||||
,libmodule-load-conditional-perl
|
,libmodule-load-conditional-perl
|
||||||
|
|
|
@ -35,7 +35,7 @@ Run the following commands.
|
||||||
sudo apt install mariadb-server
|
sudo apt install mariadb-server
|
||||||
sudo apt install zoneminder
|
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
|
**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
|
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.
|
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/
|
||||||
|
|
|
@ -444,11 +444,6 @@ if ( $version ) {
|
||||||
|
|
||||||
print( "\nUpgrading database to version ".ZM_VERSION."\n" );
|
print( "\nUpgrading database to version ".ZM_VERSION."\n" );
|
||||||
|
|
||||||
# Update config first of all
|
|
||||||
migratePaths();
|
|
||||||
ZoneMinder::Config::loadConfigFromDB();
|
|
||||||
ZoneMinder::Config::saveConfigToDB();
|
|
||||||
|
|
||||||
my $cascade = undef;
|
my $cascade = undef;
|
||||||
if ( $cascade || $version eq "1.19.0" ) {
|
if ( $cascade || $version eq "1.19.0" ) {
|
||||||
# Patch the database
|
# Patch the database
|
||||||
|
|
|
@ -245,6 +245,7 @@ class Socket : public CommsBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ssize_t recv(std::string &msg) const {
|
virtual ssize_t recv(std::string &msg) const {
|
||||||
|
msg.reserve(ZM_NETWORK_BUFSIZ);
|
||||||
std::vector<char> buffer(msg.capacity());
|
std::vector<char> buffer(msg.capacity());
|
||||||
ssize_t nBytes;
|
ssize_t nBytes;
|
||||||
if ((nBytes = ::recv(mSd, buffer.data(), buffer.size(), 0)) < 0) {
|
if ((nBytes = ::recv(mSd, buffer.data(), buffer.size(), 0)) < 0) {
|
||||||
|
|
|
@ -251,7 +251,7 @@ void zmDbQueue::process() {
|
||||||
mCondition.wait(lock);
|
mCondition.wait(lock);
|
||||||
}
|
}
|
||||||
while (!mQueue.empty()) {
|
while (!mQueue.empty()) {
|
||||||
if (mQueue.size() > 20) {
|
if (mQueue.size() > 30) {
|
||||||
Logger *log = Logger::fetch();
|
Logger *log = Logger::fetch();
|
||||||
Logger::Level db_level = log->databaseLevel();
|
Logger::Level db_level = log->databaseLevel();
|
||||||
log->databaseLevel(Logger::NOLOG);
|
log->databaseLevel(Logger::NOLOG);
|
||||||
|
|
|
@ -301,9 +301,9 @@ void Event::updateNotes(const StringSetMap &newNoteSetMap) {
|
||||||
} // end if update
|
} // end if update
|
||||||
} // void Event::updateNotes(const StringSetMap &newNoteSetMap)
|
} // 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);
|
std::unique_lock<std::mutex> lck(packet_queue_mutex);
|
||||||
packet_queue.push(packet);
|
packet_queue.push(packetlock);
|
||||||
packet_queue_condition.notify_one();
|
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 this is the first frame, we should add a thumbnail to the event directory
|
||||||
if ((frames == 1) || (score > max_score)) {
|
if ((frames == 1) || (score > max_score)) {
|
||||||
write_to_db = true; // web ui might show this as thumbnail, so db needs to know about it.
|
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());
|
WriteFrameImage(image, timestamp, snapshot_file.c_str());
|
||||||
} else {
|
} else {
|
||||||
Debug(1, "Not Writing snapshot because frames %d score %d > max %d", frames, score, max_score);
|
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) {
|
if (!alarm_frame_written) {
|
||||||
write_to_db = true; // OD processing will need it, so the db needs to know about it
|
write_to_db = true; // OD processing will need it, so the db needs to know about it
|
||||||
alarm_frame_written = true;
|
alarm_frame_written = true;
|
||||||
Debug(1, "Writing alarm image");
|
Debug(1, "Writing alarm image to %s", alarm_file.c_str());
|
||||||
WriteFrameImage(image, timestamp, 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 {
|
} else {
|
||||||
Debug(3, "Not Writing alarm image because alarm frame already written");
|
Debug(3, "Not Writing alarm image because alarm frame already written");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (alarm_image and (save_jpegs & 2)) {
|
if (alarm_image and (save_jpegs & 2)) {
|
||||||
std::string event_file = stringtf(staticConfig.analyse_file_format.c_str(), path.c_str(), frames);
|
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)) {
|
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
|
} // end if is an alarm frame
|
||||||
|
@ -682,7 +684,9 @@ void Event::Run() {
|
||||||
while (true) {
|
while (true) {
|
||||||
if (!packet_queue.empty()) {
|
if (!packet_queue.empty()) {
|
||||||
Debug(1, "adding packet");
|
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();
|
packet_queue.pop();
|
||||||
} else {
|
} else {
|
||||||
if (terminate_ or zm_terminate) {
|
if (terminate_ or zm_terminate) {
|
||||||
|
|
|
@ -105,7 +105,7 @@ class Event {
|
||||||
|
|
||||||
void createNotes(std::string ¬es);
|
void createNotes(std::string ¬es);
|
||||||
|
|
||||||
std::queue<std::shared_ptr<ZMPacket>> packet_queue;
|
std::queue<ZMLockedPacket *> packet_queue;
|
||||||
std::mutex packet_queue_mutex;
|
std::mutex packet_queue_mutex;
|
||||||
std::condition_variable packet_queue_condition;
|
std::condition_variable packet_queue_condition;
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ class Event {
|
||||||
SystemTimePoint EndTime() const { return end_time; }
|
SystemTimePoint EndTime() const { return end_time; }
|
||||||
TimePoint::duration Duration() const { return end_time - start_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);
|
void AddPacket_(const std::shared_ptr<ZMPacket> &p);
|
||||||
bool WritePacket(const std::shared_ptr<ZMPacket> &p);
|
bool WritePacket(const std::shared_ptr<ZMPacket> &p);
|
||||||
bool SendFrameImage(const Image *image, bool alarm_frame=false);
|
bool SendFrameImage(const Image *image, bool alarm_frame=false);
|
||||||
|
|
|
@ -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);
|
Debug(1, "ids [0x%x]", st->id);
|
||||||
if (lang)
|
if (lang)
|
||||||
Debug(1, "language (%s)", lang->value);
|
Debug(1, "language (%s)", lang->value);
|
||||||
Debug(1, "frames:%d, frame_size:%d stream timebase: %d/%d",
|
Debug(1, "frame_size:%d stream timebase: %d/%d",
|
||||||
st->codec_info_nb_frames, codec->frame_size,
|
codec->frame_size,
|
||||||
st->time_base.num, st->time_base.den
|
st->time_base.num, st->time_base.den
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "zm_utils.h"
|
#include "zm_utils.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <mutex>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
@ -80,6 +81,8 @@ imgbufcpy_fptr_t fptr_imgbufcpy;
|
||||||
/* Font */
|
/* Font */
|
||||||
static ZmFont font;
|
static ZmFont font;
|
||||||
|
|
||||||
|
std::mutex jpeg_mutex;
|
||||||
|
|
||||||
void Image::update_function_pointers() {
|
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 */
|
/* 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 ) {
|
if ( pixels % 16 || pixels % 12 ) {
|
||||||
|
@ -1083,6 +1086,9 @@ bool Image::WriteJpeg(const std::string &filename,
|
||||||
const int &quality_override,
|
const int &quality_override,
|
||||||
SystemTimePoint timestamp,
|
SystemTimePoint timestamp,
|
||||||
bool on_blocking_abort) const {
|
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)) {
|
if (config.colour_jpeg_files && (colours == ZM_COLOUR_GRAY8)) {
|
||||||
Image temp_image(*this);
|
Image temp_image(*this);
|
||||||
temp_image.Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB);
|
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);
|
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;
|
int quality = quality_override ? quality_override : config.jpeg_stream_quality;
|
||||||
|
|
||||||
struct jpeg_compress_struct *cinfo = encodejpg_ccinfo[quality];
|
struct jpeg_compress_struct *cinfo = encodejpg_ccinfo[quality];
|
||||||
|
|
|
@ -2321,34 +2321,37 @@ bool Monitor::Analyse() {
|
||||||
shared_data->state = state = IDLE;
|
shared_data->state = state = IDLE;
|
||||||
} // end if ( trigger_data->trigger_state != TRIGGER_OFF )
|
} // 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
|
} // 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);
|
packetqueue.increment_it(analysis_it);
|
||||||
delete packet_lock;
|
|
||||||
//packetqueue.unlock(packet_lock);
|
//packetqueue.unlock(packet_lock);
|
||||||
shared_data->last_read_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
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
|
// Write out starting packets, do not modify packetqueue it will garbage collect itself
|
||||||
while (starting_packet and ((*start_it) != *analysis_it)) {
|
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
|
// Have added the packet, don't want to unlock it until we have locked the next
|
||||||
|
|
||||||
packetqueue.increment_it(start_it);
|
packetqueue.increment_it(start_it);
|
||||||
if ((*start_it) == *analysis_it) {
|
if ((*start_it) == *analysis_it) {
|
||||||
if (starting_packet_lock) delete starting_packet_lock;
|
//if (starting_packet_lock) delete starting_packet_lock;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ZMLockedPacket *lp = packetqueue.get_packet(start_it);
|
ZMLockedPacket *lp = packetqueue.get_packet(start_it);
|
||||||
delete starting_packet_lock;
|
//delete starting_packet_lock;
|
||||||
if (!lp) return nullptr; // only on terminate FIXME
|
if (!lp) return nullptr; // only on terminate FIXME
|
||||||
starting_packet_lock = lp;
|
starting_packet_lock = lp;
|
||||||
starting_packet = lp->packet_;
|
starting_packet = lp->packet_;
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "zm_config.h"
|
#include "zm_config.h"
|
||||||
#include "zm_monitor.h"
|
#include "zm_monitor.h"
|
||||||
#include "zm_packet.h"
|
#include "zm_packet.h"
|
||||||
|
#include "zm_signal.h"
|
||||||
|
|
||||||
RemoteCameraRtsp::RemoteCameraRtsp(
|
RemoteCameraRtsp::RemoteCameraRtsp(
|
||||||
const Monitor *monitor,
|
const Monitor *monitor,
|
||||||
|
@ -126,8 +127,8 @@ int RemoteCameraRtsp::Disconnect() {
|
||||||
|
|
||||||
int RemoteCameraRtsp::PrimeCapture() {
|
int RemoteCameraRtsp::PrimeCapture() {
|
||||||
Debug(2, "Waiting for sources");
|
Debug(2, "Waiting for sources");
|
||||||
for (int i = 0; i < 100 && !rtspThread->hasSources(); i++) {
|
for (int i = 100; i && !zm_terminate && !rtspThread->hasSources(); i--) {
|
||||||
std::this_thread::sleep_for(Microseconds(100));
|
std::this_thread::sleep_for(Microseconds(10000));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rtspThread->hasSources()) {
|
if (!rtspThread->hasSources()) {
|
||||||
|
@ -168,8 +169,10 @@ int RemoteCameraRtsp::PrimeCapture() {
|
||||||
}
|
}
|
||||||
} // end foreach stream
|
} // end foreach stream
|
||||||
|
|
||||||
if ( mVideoStreamId == -1 )
|
if ( mVideoStreamId == -1 ) {
|
||||||
Fatal("Unable to locate video stream");
|
Error("Unable to locate video stream");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if ( mAudioStreamId == -1 )
|
if ( mAudioStreamId == -1 )
|
||||||
Debug(3, "Unable to locate audio stream");
|
Debug(3, "Unable to locate audio stream");
|
||||||
|
|
||||||
|
@ -179,17 +182,22 @@ int RemoteCameraRtsp::PrimeCapture() {
|
||||||
|
|
||||||
// Find the decoder for the video stream
|
// Find the decoder for the video stream
|
||||||
AVCodec *codec = avcodec_find_decoder(mVideoCodecContext->codec_id);
|
AVCodec *codec = avcodec_find_decoder(mVideoCodecContext->codec_id);
|
||||||
if ( codec == nullptr )
|
if ( codec == nullptr ) {
|
||||||
Panic("Unable to locate codec %d decoder", mVideoCodecContext->codec_id);
|
Error("Unable to locate codec %d decoder", mVideoCodecContext->codec_id);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
// Open codec
|
// Open codec
|
||||||
if ( avcodec_open2(mVideoCodecContext, codec, nullptr) < 0 )
|
if ( avcodec_open2(mVideoCodecContext, codec, nullptr) < 0 ) {
|
||||||
Panic("Can't open codec");
|
Error("Can't open codec");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
int pSize = av_image_get_buffer_size(imagePixFormat, width, height, 1);
|
int pSize = av_image_get_buffer_size(imagePixFormat, width, height, 1);
|
||||||
|
|
||||||
if ( (unsigned int)pSize != imagesize ) {
|
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;
|
return 1;
|
||||||
|
@ -208,18 +216,13 @@ int RemoteCameraRtsp::PreCapture() {
|
||||||
int RemoteCameraRtsp::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
|
int RemoteCameraRtsp::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
|
||||||
int frameComplete = false;
|
int frameComplete = false;
|
||||||
AVPacket *packet = &zm_packet->packet;
|
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) {
|
while (!frameComplete) {
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
if (!rtspThread || rtspThread->IsStopped())
|
if (!rtspThread || rtspThread->IsStopped() || zm_terminate)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if ( rtspThread->getFrame(buffer) ) {
|
if (rtspThread->getFrame(buffer)) {
|
||||||
Debug(3, "Read frame %d bytes", buffer.size());
|
Debug(3, "Read frame %d bytes", buffer.size());
|
||||||
Hexdump(4, buffer.head(), 16);
|
Hexdump(4, buffer.head(), 16);
|
||||||
|
|
||||||
|
@ -254,36 +257,20 @@ int RemoteCameraRtsp::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
|
||||||
|
|
||||||
//while ( (!frameComplete) && (buffer.size() > 0) ) {
|
//while ( (!frameComplete) && (buffer.size() > 0) ) {
|
||||||
if ( 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();
|
packet->size = buffer.size();
|
||||||
bytes += packet->size;
|
bytes += packet->size;
|
||||||
|
buffer -= packet->size;
|
||||||
|
|
||||||
struct timeval now;
|
struct timeval now;
|
||||||
gettimeofday(&now, NULL);
|
gettimeofday(&now, nullptr);
|
||||||
packet->pts = packet->dts = now.tv_sec*1000000+now.tv_usec;
|
packet->pts = packet->dts = now.tv_sec*1000000+now.tv_usec;
|
||||||
|
zm_packet->codec_type = mVideoCodecContext->codec_type;
|
||||||
int bytes_consumed = zm_packet->decode(mVideoCodecContext);
|
zm_packet->stream = mVideoStream;
|
||||||
if ( bytes_consumed < 0 ) {
|
frameComplete = true;
|
||||||
Error("Error while decoding frame %d", frameCount);
|
Debug(2, "Frame: %d - %d/%d", frameCount, packet->size, buffer.size());
|
||||||
//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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} /* getFrame() */
|
} /* getFrame() */
|
||||||
} // end while true
|
} // end while true
|
||||||
|
|
|
@ -277,7 +277,7 @@ void RtpCtrlThread::Run() {
|
||||||
TimePoint last_receive = std::chrono::steady_clock::now();
|
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.
|
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();
|
TimePoint now = std::chrono::steady_clock::now();
|
||||||
zm::Select::CommsList readable = select.getReadable();
|
zm::Select::CommsList readable = select.getReadable();
|
||||||
if ( readable.size() == 0 ) {
|
if ( readable.size() == 0 ) {
|
||||||
|
|
|
@ -45,8 +45,10 @@ RtpSource::RtpSource(
|
||||||
mFrame(65536),
|
mFrame(65536),
|
||||||
mFrameCount(0),
|
mFrameCount(0),
|
||||||
mFrameGood(true),
|
mFrameGood(true),
|
||||||
|
prevM(false),
|
||||||
mFrameReady(false),
|
mFrameReady(false),
|
||||||
mFrameProcessed(false)
|
mFrameProcessed(false),
|
||||||
|
mTerminate(false)
|
||||||
{
|
{
|
||||||
char hostname[256] = "";
|
char hostname[256] = "";
|
||||||
gethostname(hostname, sizeof(hostname));
|
gethostname(hostname, sizeof(hostname));
|
||||||
|
|
|
@ -91,8 +91,6 @@ private:
|
||||||
bool mFrameGood;
|
bool mFrameGood;
|
||||||
bool prevM;
|
bool prevM;
|
||||||
|
|
||||||
bool mTerminate;
|
|
||||||
|
|
||||||
bool mFrameReady;
|
bool mFrameReady;
|
||||||
std::condition_variable mFrameReadyCv;
|
std::condition_variable mFrameReadyCv;
|
||||||
std::mutex mFrameReadyMutex;
|
std::mutex mFrameReadyMutex;
|
||||||
|
@ -100,6 +98,7 @@ private:
|
||||||
bool mFrameProcessed;
|
bool mFrameProcessed;
|
||||||
std::condition_variable mFrameProcessedCv;
|
std::condition_variable mFrameProcessedCv;
|
||||||
std::mutex mFrameProcessedMutex;
|
std::mutex mFrameProcessedMutex;
|
||||||
|
bool mTerminate;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init(uint16_t seq);
|
void init(uint16_t seq);
|
||||||
|
|
|
@ -298,6 +298,14 @@ int main(int argc, char *argv[]) {
|
||||||
session->GetMediaSessionId(), xop::channel_1, audioFifoPath);
|
session->GetMediaSessionId(), xop::channel_1, audioFifoPath);
|
||||||
audioSource->setFrequency(monitor->GetAudioFrequency());
|
audioSource->setFrequency(monitor->GetAudioFrequency());
|
||||||
audioSource->setChannels(monitor->GetAudioChannels());
|
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 {
|
} else {
|
||||||
Warning("Unknown format in %s", audioFifoPath.c_str());
|
Warning("Unknown format in %s", audioFifoPath.c_str());
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,7 +118,7 @@ commonprep () {
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
RTSPVER="cd7fd49becad6010a1b8466bfebbd93999a39878"
|
RTSPVER="eab32851421ffe54fec0229c3efc44c642bc8d46"
|
||||||
if [ -e "build/RtspServer-${RTSPVER}.tar.gz" ]; then
|
if [ -e "build/RtspServer-${RTSPVER}.tar.gz" ]; then
|
||||||
echo "Found existing RtspServer ${RTSPVER} tarball..."
|
echo "Found existing RtspServer ${RTSPVER} tarball..."
|
||||||
else
|
else
|
||||||
|
|
|
@ -187,6 +187,9 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
|
||||||
|
|
||||||
$col_str = 'E.*, M.Name AS Monitor';
|
$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:'');
|
$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();
|
$storage_areas = ZM\Storage::find();
|
||||||
$StorageById = array();
|
$StorageById = array();
|
||||||
|
@ -213,6 +216,12 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
|
||||||
$unfiltered_rows[] = $row;
|
$unfiltered_rows[] = $row;
|
||||||
} # end foreach 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.');
|
ZM\Debug('Have ' . count($unfiltered_rows) . ' events matching base filter.');
|
||||||
|
|
||||||
$filtered_rows = null;
|
$filtered_rows = null;
|
||||||
|
@ -251,8 +260,10 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
|
||||||
$filtered_rows = $unfiltered_rows;
|
$filtered_rows = $unfiltered_rows;
|
||||||
} # end if search_filter->terms() > 1
|
} # 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);
|
$filtered_rows = array_slice($filtered_rows, $offset, $limit);
|
||||||
|
}
|
||||||
|
|
||||||
$returned_rows = array();
|
$returned_rows = array();
|
||||||
foreach ($filtered_rows as $row) {
|
foreach ($filtered_rows as $row) {
|
||||||
|
|
|
@ -38,6 +38,14 @@ function MonitorStream(monitorData) {
|
||||||
}
|
}
|
||||||
return this.element;
|
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. */
|
/* if the img element didn't have a src, this would fill it in, causing it to show. */
|
||||||
this.show = function() {
|
this.show = function() {
|
||||||
|
@ -161,7 +169,7 @@ function MonitorStream(monitorData) {
|
||||||
|
|
||||||
this.setup_onclick = function(func) {
|
this.setup_onclick = function(func) {
|
||||||
this.onclick = func;
|
this.onclick = func;
|
||||||
const el = this.getElement();
|
const el = this.getFrame();
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
el.addEventListener('click', this.onclick, false);
|
el.addEventListener('click', this.onclick, false);
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,7 +15,11 @@ input[name="newMonitor[Path]"],
|
||||||
input[name="newMonitor[SecondPath]"],
|
input[name="newMonitor[SecondPath]"],
|
||||||
input[name="newMonitor[LabelFormat]"],
|
input[name="newMonitor[LabelFormat]"],
|
||||||
input[name="newMonitor[ControlDevice]"],
|
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%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
input[name="newMonitor[LabelFormat]"]{
|
input[name="newMonitor[LabelFormat]"]{
|
||||||
|
|
|
@ -109,7 +109,8 @@ stateStrings[STATE_TAPE] = "<?php echo translate('Record') ?>";
|
||||||
<?php
|
<?php
|
||||||
global $config;
|
global $config;
|
||||||
foreach ($config as $name=>$c) {
|
foreach ($config as $name=>$c) {
|
||||||
if (!$c['Private'])
|
if (!$c['Private']) {
|
||||||
echo 'const '. $name . ' = \''.$c['Value'].'\''.PHP_EOL;
|
echo 'const '. $name . ' = \''.preg_replace('/(\n\r?)/', '\\\\$1', $c['Value']).'\';'.PHP_EOL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -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 optControl = <?php echo ZM_OPT_CONTROL ?>;
|
||||||
var hasOnvif = <?php echo ZM_HAS_ONVIF ?>;
|
var hasOnvif = <?php echo ZM_HAS_ONVIF ?>;
|
||||||
var defaultAspectRatio = '<?php echo ZM_DEFAULT_ASPECT_RATIO ?>';
|
var defaultAspectRatio = '<?php echo ZM_DEFAULT_ASPECT_RATIO ?>';
|
||||||
|
|
Loading…
Reference in New Issue