Merge branch 'storageareas' of github.com:ZoneMinder/ZoneMinder into storageareas
This commit is contained in:
commit
408fba8d24
|
@ -45,4 +45,4 @@ script:
|
|||
- mysql -uzmuser -pzmpass < db/zm_create.sql
|
||||
- mysql -uzmuser -pzmpass zm < db/test.monitor.sql
|
||||
- sudo zmpkg.pl start
|
||||
- sudo zmfilter.pl -f purgewhenfull
|
||||
- sudo zmfilter.pl --filter purgewhenfull
|
||||
|
|
|
@ -3,51 +3,51 @@
|
|||
set -e
|
||||
|
||||
if [ "$1" = "configure" ]; then
|
||||
if [ -e "/etc/init.d/mysql" ]; then
|
||||
#
|
||||
# Get mysql started if it isn't
|
||||
#
|
||||
if ! $(/etc/init.d/mysql status >/dev/null 2>&1); then
|
||||
invoke-rc.d mysql start
|
||||
fi
|
||||
if $(/etc/init.d/mysql status >/dev/null 2>&1); then
|
||||
mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload
|
||||
# test if database if already present...
|
||||
if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then
|
||||
cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf
|
||||
echo 'grant lock tables, alter,select,insert,update,delete on zm.* to 'zmuser'@localhost identified by "zmpass";' | mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
||||
fi
|
||||
|
||||
invoke-rc.d zoneminder stop || true
|
||||
zmupdate.pl --nointeractive
|
||||
. /etc/zm/zm.conf
|
||||
|
||||
else
|
||||
echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.'
|
||||
fi
|
||||
else
|
||||
echo 'mysql not found, assuming remote server.'
|
||||
fi
|
||||
chown www-data:www-data /var/log/zm
|
||||
chown www-data:www-data /var/lib/zm/
|
||||
# The logs can contain passwords, etc... so by setting group root, only www-data can read them, not people in the www-data group.
|
||||
chown www-data:root /var/log/zm
|
||||
chown www-data:www-data /var/lib/zm
|
||||
if [ -z "$2" ]; then
|
||||
chown www-data:www-data -R /var/cache/zoneminder
|
||||
chown www-data:www-data /var/cache/zoneminder /var/cache/zoneminder/*
|
||||
fi
|
||||
fi
|
||||
# Ensure zoneminder is stopped...
|
||||
if [ -x "/etc/init.d/zoneminder" ]; then
|
||||
if invoke-rc.d zoneminder status ; then
|
||||
invoke-rc.d zoneminder stop || exit $?
|
||||
|
||||
# Do this every time the package is installed or upgraded
|
||||
|
||||
if [ "$ZM_DB_HOST" = "localhost" ]; then
|
||||
if [ -e "/etc/init.d/mysql" ]; then
|
||||
#
|
||||
# Get mysql started if it isn't
|
||||
#
|
||||
if ! $(/etc/init.d/mysql status >/dev/null 2>&1); then
|
||||
invoke-rc.d mysql start
|
||||
fi
|
||||
if $(/etc/init.d/mysql status >/dev/null 2>&1); then
|
||||
mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload
|
||||
# test if database if already present...
|
||||
if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then
|
||||
cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf
|
||||
# This creates the user.
|
||||
echo "grant lock tables, alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
||||
else
|
||||
echo "grant lock tables, alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
||||
fi
|
||||
|
||||
# Ensure zoneminder is stopped
|
||||
invoke-rc.d zoneminder stop || true
|
||||
zmupdate.pl --nointeractive
|
||||
zmupdate.pl --nointeractive -f
|
||||
invoke-rc.d zoneminder start || true
|
||||
else
|
||||
echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.'
|
||||
fi
|
||||
else
|
||||
echo 'mysql not found, assuming remote server.'
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$1" = "configure" ]; then
|
||||
if [ -z "$2" ]; then
|
||||
chown www-data:www-data /var/log/zm
|
||||
chown www-data:www-data /var/lib/zm/
|
||||
chown www-data:www-data -R /var/cache/zoneminder
|
||||
else
|
||||
chown www-data:www-data /var/log/zm
|
||||
zmupdate.pl
|
||||
fi
|
||||
echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)"
|
||||
fi
|
||||
|
||||
fi
|
||||
#DEBHELPER#
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
zoneminder (1.30.2-trusty-2016042801) trusty; urgency=medium
|
||||
|
||||
* Merge video
|
||||
|
||||
-- Isaac Connor <iconnor@connortechnology.com> Thu, 28 Apr 2016 12:54:08 -0400
|
||||
|
||||
zoneminder (1.28.1+1-vivid-SNAPSHOT2015081701) vivid; urgency=medium
|
||||
|
||||
* include api, switch to cmake build
|
||||
|
|
|
@ -3,11 +3,51 @@
|
|||
set -e
|
||||
|
||||
if [ "$1" = "configure" ]; then
|
||||
chown www-data:root /var/log/zm
|
||||
chown www-data:www-data /var/lib/zm
|
||||
if [ -z "$2" ]; then
|
||||
chown www-data:www-data -R /var/cache/zoneminder
|
||||
fi
|
||||
|
||||
. /etc/zm/zm.conf
|
||||
|
||||
# The logs can contain passwords, etc... so by setting group root, only www-data can read them, not people in the www-data group
|
||||
chown www-data:root /var/log/zm
|
||||
chown www-data:www-data /var/lib/zm
|
||||
if [ -z "$2" ]; then
|
||||
chown www-data:www-data /var/cache/zoneminder /var/cache/zoneminder/*
|
||||
fi
|
||||
|
||||
# Do this every time the package is installed or upgraded
|
||||
|
||||
if [ "$ZM_DB_HOST" = "localhost" ]; then
|
||||
if [ -e "/etc/init.d/mysql" ]; then
|
||||
#
|
||||
# Get mysql started if it isn't
|
||||
#
|
||||
if ! $(/etc/init.d/mysql status >/dev/null 2>&1); then
|
||||
invoke-rc.d mysql start
|
||||
fi
|
||||
if $(/etc/init.d/mysql status >/dev/null 2>&1); then
|
||||
mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload
|
||||
# test if database if already present...
|
||||
if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then
|
||||
cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf
|
||||
# This creates the user.
|
||||
echo "grant lock tables, alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
||||
else
|
||||
echo "grant lock tables, alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
||||
fi
|
||||
|
||||
# Ensure zoneminder is stopped
|
||||
invoke-rc.d zoneminder stop || true
|
||||
zmupdate.pl --nointeractive
|
||||
zmupdate.pl --nointeractive -f
|
||||
invoke-rc.d zoneminder start || true
|
||||
else
|
||||
echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.'
|
||||
fi
|
||||
else
|
||||
echo 'mysql not found, assuming remote server.'
|
||||
fi
|
||||
else
|
||||
echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)"
|
||||
fi
|
||||
fi
|
||||
|
||||
#DEBHELPER#
|
||||
|
|
|
@ -1,3 +1,15 @@
|
|||
zoneminder (1.30.2-trusty-2016033001) trusty; urgency=medium
|
||||
|
||||
* merge master
|
||||
|
||||
-- Isaac Connor <iconnor@connortechnology.com> Wed, 30 Mar 2016 14:09:48 -0400
|
||||
|
||||
zoneminder (1.30.2-trusty-2016032901) trusty; urgency=medium
|
||||
|
||||
* filter fixes, merge options rework by Kyle
|
||||
|
||||
-- Isaac Connor <iconnor@connortechnology.com> Tue, 29 Mar 2016 12:27:57 -0400
|
||||
|
||||
zoneminder (1.30.2-trusty-2016030702) trusty; urgency=medium
|
||||
|
||||
*
|
||||
|
|
|
@ -6,6 +6,7 @@ Uploaders: Vagrant Cascadian <vagrant@debian.org>
|
|||
Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apache2-dev, dh-linktree
|
||||
,cmake
|
||||
,libavcodec-ffmpeg-dev, libavformat-ffmpeg-dev, libswscale-ffmpeg-dev, libavutil-ffmpeg-dev, libavdevice-ffmpeg-dev
|
||||
,libx264-dev, libmp4v2-dev,
|
||||
,libbz2-dev
|
||||
,libgcrypt-dev
|
||||
,libcurl4-gnutls-dev
|
||||
|
@ -34,10 +35,8 @@ Architecture: any
|
|||
Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
|
||||
,javascript-common
|
||||
,libav-tools
|
||||
,libdate-manip-perl
|
||||
,libdate-manip-perl, libmime-lite-perl, libmime-tools-perl
|
||||
,libdbd-mysql-perl
|
||||
,libmime-lite-perl
|
||||
,libmime-tools-perl
|
||||
,libphp-serialization-perl
|
||||
,libmodule-load-conditional-perl
|
||||
,libnet-sftp-foreign-perl
|
||||
|
@ -57,12 +56,12 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
|
|||
,libsys-cpu-perl, libsys-meminfo-perl
|
||||
,mysql-client | virtual-mysql-client
|
||||
,perl-modules
|
||||
,php5-mysql
|
||||
,php5-mysql | php-mysql, php5-gd | php-gd
|
||||
,policykit-1
|
||||
,rsyslog | system-log-daemon
|
||||
,zip
|
||||
Recommends: ${misc:Recommends}
|
||||
,libapache2-mod-php5 | php5-fpm
|
||||
,libapache2-mod-php5 | libapache2-mod-php7 | php5-fpm
|
||||
,mysql-server | virtual-mysql-server
|
||||
,zoneminder-doc (>= ${source:Version})
|
||||
Suggests: fcgiwrap, logrotate
|
||||
|
|
|
@ -2,28 +2,57 @@
|
|||
|
||||
set -e
|
||||
|
||||
. /etc/zm/zm.conf
|
||||
|
||||
if [ "$1" = "configure" ]; then
|
||||
chown www-data:root /var/log/zm
|
||||
chown www-data:www-data /var/lib/zm
|
||||
if [ -z "$2" ]; then
|
||||
chown www-data:www-data -R /var/cache/zoneminder
|
||||
fi
|
||||
|
||||
. /etc/zm/zm.conf
|
||||
|
||||
# The logs can contain passwords, etc... so by setting group root, only www-data can read them, not people in the www-data group
|
||||
chown www-data:root /var/log/zm
|
||||
chown www-data:www-data /var/lib/zm
|
||||
if [ -z "$2" ]; then
|
||||
chown www-data:www-data /var/cache/zoneminder /var/cache/zoneminder/*
|
||||
fi
|
||||
|
||||
# Do this every time the package is installed or upgraded
|
||||
# Test for database presence to avoid failure of zmupdate.pl
|
||||
|
||||
# Ensure zoneminder is stopped
|
||||
deb-systemd-invoke stop zoneminder.service || exit $?
|
||||
|
||||
if [ "$ZM_DB_HOST" = "localhost" ]; then
|
||||
echo 'grant lock tables, create, index, alter on zm.* to 'zmuser'@localhost identified by "zmpass";' | mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
||||
# Run the ZoneMinder update tool
|
||||
zmupdate.pl --nointeractive
|
||||
else
|
||||
|
||||
if [ -e "/etc/init.d/mysql" ]; then
|
||||
|
||||
#
|
||||
# Get mysql started if it isn't
|
||||
#
|
||||
if ! $(/etc/init.d/mysql status >/dev/null 2>&1); then
|
||||
deb-systemd-invoke start mysql.service || exit $?
|
||||
fi
|
||||
|
||||
if $(/etc/init.d/mysql status >/dev/null 2>&1); then
|
||||
mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload
|
||||
# test if database if already present...
|
||||
if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then
|
||||
cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf
|
||||
# This creates the user.
|
||||
echo "grant lock tables,alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
||||
else
|
||||
echo "grant lock tables,alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
||||
fi
|
||||
|
||||
# Ensure zoneminder is stopped
|
||||
deb-systemd-invoke stop zoneminder.service || exit $?
|
||||
zmupdate.pl --nointeractive
|
||||
zmupdate.pl --nointeractive -f
|
||||
deb-systemd-invoke start zoneminder.service || exit $?
|
||||
|
||||
else
|
||||
echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.'
|
||||
fi
|
||||
else
|
||||
echo 'mysql not found, assuming remote server.'
|
||||
fi
|
||||
|
||||
else
|
||||
echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)"
|
||||
fi;
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
|
|
|
@ -514,6 +514,8 @@ You do not need to rebuild ZM for X10 support. You will need to install the perl
|
|||
|
||||
Extending Zoneminder
|
||||
------------------------
|
||||
.. _runstate_cron_example:
|
||||
|
||||
How can I get ZM to do different things at different times of day or week?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
|
|
@ -67,8 +67,14 @@ own empty screen.
|
|||
* **D**: This is the core of ZoneMinder - recording events. It gives you a count of how many events were recorded over the hour, day, week, month.
|
||||
* **E**: These are the "Zones". Zones are areas within the camera that you mark as 'hotspots' for motion detection. Simply put, when you first configure your monitors (cameras), by default Zoneminder uses the entire field of view of the camera to detect motion. You may not want this. You may want to create "zones" specifically for detecting motion and ignore others. For example, lets consider a room with a fan that spins. You surely don't want to consider the fan moving continuously a reason for triggering a record? Probably not - in that case, you'd leave the fan out while making your zones.
|
||||
* **F**: This is the "source" column that tells you the type of the camera - if its an IP camera, a USB camera or more. In this example, they are all IP cameras. Note the color red on item F ? Well that means there is something wrong with that camera. No wonder the log also shows red. Good indication for you to tap on logs and investigate
|
||||
* **G**: This defines how Zoneminder will record events. There are various modes. In brief Modect == record if a motion is detected,Record = always record 24x7, Mocord = always record PLUS detect motion, Monitor = just provide a live view but don't record anytime, Nodect = Don't record till an externa entity via zmtrigger tells Zoneminder to (this is advanced usage).
|
||||
* **G**: This defines how Zoneminder will record events. There are various modes. In brief Modect == record if a motion is detected,Record = always record 24x7, Mocord = always record PLUS detect motion, Monitor = just provide a live view but don't record anytime, Nodect = Don't record till an external entity via zmtrigger tells Zoneminder to (this is advanced usage).
|
||||
* **H**: If you click on these links you can view a "Montage" of all your configured monitors or cycle through each one
|
||||
* **I**: One of the most often missed features is the ability of ZoneMinder to maintain "run states". If you click on the "Running" text, ZoneMinder brings up a popup that allows you to define additional "states" (referred to as runstates). A runstate is essentially a snapshot that records the state of each monitor and you can switch between states easily. For example, you might have a run state defined that switches all monitors to "monitor" mode in which they are not recording anything while another state that sets some of the monitors to "modect". Why would you want this? A great example is to disable recording when you are at home and enable when you are away, based on time of day or other triggers. You can switch states by selecting an appropriate state manually, or do it automatically via cron jobs, for example. An example of using cron to automatically switch is provided in the :ref:`FAQ <runstate_cron_example>`. More esoteric examples of switching run states based on phone location can be found `here <https://forums.zoneminder.com/viewtopic.php?f=9&t=23026>`__.
|
||||
|
||||
Here is an example of multiple run states that I've defined. Each one of these runstates changes the mode of specific monitors depending on time of day and other conditions. Use your imagination to decide which conditions require state changes.
|
||||
|
||||
.. image:: images/runstates.png
|
||||
|
||||
|
||||
|
||||
Adding Monitors
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 230 KiB After Width: | Height: | Size: 230 KiB |
Binary file not shown.
After Width: | Height: | Size: 44 KiB |
|
@ -3825,6 +3825,41 @@ body = "ZM alarm detected - %EL% secs, %EF%/%EFA% frames, t%EST%/m%ESM%/a%ESA% s
|
|||
readonly => 1,
|
||||
category => "dynamic",
|
||||
},
|
||||
{
|
||||
name => "ZM_SSMTP_MAIL",
|
||||
default => "no",
|
||||
description => qqq("
|
||||
Use a SSMTP mail server if available.
|
||||
NEW_MAIL_MODULES must be enabled
|
||||
"),
|
||||
requires => [
|
||||
{ name => "ZM_OPT_EMAIL", value => "yes" },
|
||||
{ name => "ZM_OPT_MESSAGE", value => "yes" },
|
||||
{ name => "ZM_NEW_MAIL_MODULES", value => "yes" }
|
||||
],
|
||||
help => qqq("
|
||||
SSMTP is a lightweight and efficient method to send email.
|
||||
The SSMTP application is not installed by default.
|
||||
NEW_MAIL_MODULES must also be enabled.
|
||||
Please visit: http://www.zoneminder.com/wiki/index.php/How_to_get_ssmtp_working_with_Zoneminder
|
||||
for setup and configuration help.
|
||||
"),
|
||||
type => $types{boolean},
|
||||
category => "mail",
|
||||
},
|
||||
{
|
||||
name => "ZM_SSMTP_PATH",
|
||||
default => "",
|
||||
description => "SSMTP executable path",
|
||||
requires => [{ name => "ZM_SSMTP_MAIL", value => "yes" }],
|
||||
help => qqq("
|
||||
Recommend setting the path to the SSMTP application.
|
||||
If path is not defined. Zoneminder will try to determine
|
||||
the path via shell command. Example path: /usr/sbin/ssmtp.
|
||||
"),
|
||||
type => $types{string},
|
||||
category => "mail",
|
||||
},
|
||||
);
|
||||
|
||||
our %options_hash = map { ( $_->{name}, $_ ) } @options;
|
||||
|
|
|
@ -155,10 +155,12 @@ MAIN: while( $loop ) {
|
|||
Fatal("ZM_AUDIT_MIN_AGE is not set in config.");
|
||||
}
|
||||
|
||||
my %Monitors;
|
||||
my $db_monitors;
|
||||
my $monitorSelectSql = "select Id from Monitors order by Id";
|
||||
my $monitorSelectSql = "select * from Monitors order by Id";
|
||||
my $monitorSelectSth = $dbh->prepare_cached( $monitorSelectSql )
|
||||
or Fatal( "Can't prepare '$monitorSelectSql': ".$dbh->errstr() );
|
||||
|
||||
my $eventSelectSql = "SELECT Id, (unix_timestamp() - unix_timestamp(StartTime)) as Age
|
||||
FROM Events WHERE MonitorId = ? ORDER BY Id";
|
||||
my $eventSelectSth = $dbh->prepare_cached( $eventSelectSql )
|
||||
|
@ -169,6 +171,8 @@ MAIN: while( $loop ) {
|
|||
or Fatal( "Can't execute: ".$monitorSelectSth->errstr() );
|
||||
while( my $monitor = $monitorSelectSth->fetchrow_hashref() )
|
||||
{
|
||||
$Monitors{$$monitor{Id}} = $monitor;
|
||||
|
||||
Debug( "Found database monitor '$monitor->{Id}'" );
|
||||
my $db_events = $db_monitors->{$monitor->{Id}} = {};
|
||||
my $res = $eventSelectSth->execute( $monitor->{Id} )
|
||||
|
@ -467,20 +471,30 @@ MAIN: while( $loop ) {
|
|||
|
||||
# New audit to close any events that were left open for longer than MIN_AGE seconds
|
||||
my $selectUnclosedEventsSql =
|
||||
"SELECT E.Id,
|
||||
max(F.TimeStamp) as EndTime,
|
||||
unix_timestamp(max(F.TimeStamp)) - unix_timestamp(E.StartTime) as Length,
|
||||
max(F.FrameId) as Frames,
|
||||
#"SELECT E.Id, ANY_VALUE(E.MonitorId),
|
||||
#
|
||||
#max(F.TimeStamp) as EndTime,
|
||||
#unix_timestamp(max(F.TimeStamp)) - unix_timestamp(E.StartTime) as Length,
|
||||
#max(F.FrameId) as Frames,
|
||||
#count(if(F.Score>0,1,NULL)) as AlarmFrames,
|
||||
#sum(F.Score) as TotScore,
|
||||
#max(F.Score) as MaxScore
|
||||
#FROM Events as E
|
||||
#INNER JOIN Frames as F on E.Id = F.EventId
|
||||
#WHERE isnull(E.Frames) or isnull(E.EndTime)
|
||||
#GROUP BY E.Id HAVING EndTime < (now() - interval ".$Config{ZM_AUDIT_MIN_AGE}." second)"
|
||||
#;
|
||||
"SELECT *, unix_timestamp(StartTime) AS TimeStamp FROM Events WHERE EndTime IS NULL AND StartTime < (now() - interval ".$Config{ZM_AUDIT_MIN_AGE}." second)";
|
||||
|
||||
my $selectFrameDataSql = "SELECT max(TimeStamp) as EndTime, unix_timestamp(max(TimeStamp)) AS EndTimeStamp, max(FrameId) as Frames,
|
||||
count(if(F.Score>0,1,NULL)) as AlarmFrames,
|
||||
sum(F.Score) as TotScore,
|
||||
max(F.Score) as MaxScore,
|
||||
M.EventPrefix as Prefix
|
||||
FROM Events as E
|
||||
LEFT JOIN Monitors as M on E.MonitorId = M.Id
|
||||
INNER JOIN Frames as F on E.Id = F.EventId
|
||||
WHERE isnull(E.Frames) or isnull(E.EndTime)
|
||||
GROUP BY E.Id HAVING EndTime < (now() - interval ".$Config{ZM_AUDIT_MIN_AGE}." second)"
|
||||
;
|
||||
max(F.Score) as MaxScore
|
||||
FROM Frames WHERE EventId=?";
|
||||
my $selectFrameDataSth = $dbh->prepare_cached($selectFrameDataSql)
|
||||
or Fatal( "Can't prepare '$selectFrameDataSql': ".$dbh->errstr() );
|
||||
|
||||
|
||||
my $selectUnclosedEventsSth = $dbh->prepare_cached( $selectUnclosedEventsSql )
|
||||
or Fatal( "Can't prepare '$selectUnclosedEventsSql': ".$dbh->errstr() );
|
||||
my $updateUnclosedEventsSql =
|
||||
|
@ -505,26 +519,32 @@ MAIN: while( $loop ) {
|
|||
aud_print( "Found open event '$event->{Id}'" );
|
||||
if ( confirm( 'close', 'closing' ) )
|
||||
{
|
||||
$res = $updateUnclosedEventsSth->execute
|
||||
(
|
||||
sprintf("%s%d%s",
|
||||
$event->{Prefix},
|
||||
$event->{Id},
|
||||
RECOVER_TAG
|
||||
),
|
||||
$event->{EndTime},
|
||||
$event->{Length},
|
||||
$event->{Frames},
|
||||
$event->{AlarmFrames},
|
||||
$event->{TotScore},
|
||||
$event->{AlarmFrames}
|
||||
? int($event->{TotScore} / $event->{AlarmFrames})
|
||||
: 0
|
||||
,
|
||||
$event->{MaxScore},
|
||||
RECOVER_TEXT,
|
||||
$event->{Id}
|
||||
) or Fatal( "Can't execute: ".$updateUnclosedEventsSth->errstr() );
|
||||
$res = $selectFrameDataSth->execute( $event->{Id} );
|
||||
my $frame = $selectFrameDataSth->fetchrow_hashref();
|
||||
if ( $frame ) {
|
||||
$res = $updateUnclosedEventsSth->execute
|
||||
(
|
||||
sprintf("%s%d%s",
|
||||
$Monitors{$event->{MonitorId}}->{EventPrefix},
|
||||
$event->{Id},
|
||||
RECOVER_TAG
|
||||
),
|
||||
$frame->{EndTime},
|
||||
$frame->{EndTimeStamp} - $event->{TimeStamp},
|
||||
$frame->{Frames},
|
||||
$frame->{AlarmFrames},
|
||||
$frame->{TotScore},
|
||||
$frame->{AlarmFrames}
|
||||
? int($frame->{TotScore} / $frame->{AlarmFrames})
|
||||
: 0
|
||||
,
|
||||
$frame->{MaxScore},
|
||||
RECOVER_TEXT,
|
||||
$event->{Id}
|
||||
) or Fatal( "Can't execute: ".$updateUnclosedEventsSth->errstr() );
|
||||
} else {
|
||||
Error("SHOULD DELETE");
|
||||
} # end if has frame data
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -862,7 +862,7 @@ sub killAll
|
|||
my $killall;
|
||||
if ( '@HOST_OS@' eq 'BSD' )
|
||||
{
|
||||
$killall = 'killall -';
|
||||
$killall = 'killall -q -';
|
||||
} elsif ( '@HOST_OS@' eq 'solaris' ) {
|
||||
$killall = 'pkill -';
|
||||
} else {
|
||||
|
|
|
@ -779,9 +779,31 @@ sub sendEmail
|
|||
Disposition => "attachment"
|
||||
);
|
||||
}
|
||||
if ( $Config{ZM_SSMTP_MAIL} ){
|
||||
|
||||
my $ssmtp_location = $Config{ZM_SSMTP_PATH};
|
||||
|
||||
if( ! $ssmtp_location ){
|
||||
|
||||
$ssmtp_location = qx('which ssmtp');
|
||||
|
||||
if ( logDebugging() )
|
||||
{
|
||||
Debug( "which ssmtp: $ssmtp_location - set ssmtp path in options to suppress this message\n" );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$mail->send( 'sendmail', $ssmtp_location, $Config{ZM_EMAIL_ADDRESS} );
|
||||
|
||||
}else{
|
||||
|
||||
MIME::Lite->send( "smtp", $Config{ZM_EMAIL_HOST}, Timeout=>60 );
|
||||
$mail->send();
|
||||
}
|
||||
### Send the Message
|
||||
MIME::Lite->send( "smtp", $Config{ZM_EMAIL_HOST}, Timeout=>60 );
|
||||
$mail->send();
|
||||
#MIME::Lite->send( "smtp", $Config{ZM_EMAIL_HOST}, Timeout=>60 );
|
||||
#$mail->send();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
4
src/zm.h
4
src/zm.h
|
@ -26,8 +26,8 @@
|
|||
|
||||
#include "zm_config.h"
|
||||
#ifdef SOLARIS
|
||||
#undef DEFAULT_TYPE // pthread defines this which breaks StreamType DEFAULT_TYPE
|
||||
#include <string.h> // define strerror() and friends
|
||||
#undef DEFAULT_TYPE // pthread defines this which breaks StreamType DEFAULT_TYPE
|
||||
#include <string.h> // define strerror() and friends
|
||||
#endif
|
||||
#include "zm_logger.h"
|
||||
|
||||
|
|
12296
src/zm_bigfont.h
12296
src/zm_bigfont.h
File diff suppressed because it is too large
Load Diff
58
src/zm_box.h
58
src/zm_box.h
|
@ -36,39 +36,39 @@
|
|||
class Box
|
||||
{
|
||||
private:
|
||||
Coord lo, hi;
|
||||
Coord size;
|
||||
Coord lo, hi;
|
||||
Coord size;
|
||||
|
||||
public:
|
||||
inline Box()
|
||||
{
|
||||
}
|
||||
inline Box( int p_size ) : lo( 0, 0 ), hi ( p_size-1, p_size-1 ), size( Coord::Range( hi, lo ) ) { }
|
||||
inline Box( int p_x_size, int p_y_size ) : lo( 0, 0 ), hi ( p_x_size-1, p_y_size-1 ), size( Coord::Range( hi, lo ) ) { }
|
||||
inline Box( int lo_x, int lo_y, int hi_x, int hi_y ) : lo( lo_x, lo_y ), hi( hi_x, hi_y ), size( Coord::Range( hi, lo ) ) { }
|
||||
inline Box( const Coord &p_lo, const Coord &p_hi ) : lo( p_lo ), hi( p_hi ), size( Coord::Range( hi, lo ) ) { }
|
||||
inline Box()
|
||||
{
|
||||
}
|
||||
inline Box( int p_size ) : lo( 0, 0 ), hi ( p_size-1, p_size-1 ), size( Coord::Range( hi, lo ) ) { }
|
||||
inline Box( int p_x_size, int p_y_size ) : lo( 0, 0 ), hi ( p_x_size-1, p_y_size-1 ), size( Coord::Range( hi, lo ) ) { }
|
||||
inline Box( int lo_x, int lo_y, int hi_x, int hi_y ) : lo( lo_x, lo_y ), hi( hi_x, hi_y ), size( Coord::Range( hi, lo ) ) { }
|
||||
inline Box( const Coord &p_lo, const Coord &p_hi ) : lo( p_lo ), hi( p_hi ), size( Coord::Range( hi, lo ) ) { }
|
||||
|
||||
inline const Coord &Lo() const { return( lo ); }
|
||||
inline int LoX() const { return( lo.X() ); }
|
||||
inline int LoY() const { return( lo.Y() ); }
|
||||
inline const Coord &Hi() const { return( hi ); }
|
||||
inline int HiX() const { return( hi.X() ); }
|
||||
inline int HiY() const { return( hi.Y() ); }
|
||||
inline const Coord &Size() const { return( size ); }
|
||||
inline int Width() const { return( size.X() ); }
|
||||
inline int Height() const { return( size.Y() ); }
|
||||
inline int Area() const { return( size.X()*size.Y() ); }
|
||||
inline const Coord &Lo() const { return( lo ); }
|
||||
inline int LoX() const { return( lo.X() ); }
|
||||
inline int LoY() const { return( lo.Y() ); }
|
||||
inline const Coord &Hi() const { return( hi ); }
|
||||
inline int HiX() const { return( hi.X() ); }
|
||||
inline int HiY() const { return( hi.Y() ); }
|
||||
inline const Coord &Size() const { return( size ); }
|
||||
inline int Width() const { return( size.X() ); }
|
||||
inline int Height() const { return( size.Y() ); }
|
||||
inline int Area() const { return( size.X()*size.Y() ); }
|
||||
|
||||
inline const Coord Centre() const
|
||||
{
|
||||
int mid_x = int(round(lo.X()+(size.X()/2.0)));
|
||||
int mid_y = int(round(lo.Y()+(size.Y()/2.0)));
|
||||
return( Coord( mid_x, mid_y ) );
|
||||
}
|
||||
inline bool Inside( const Coord &coord ) const
|
||||
{
|
||||
return( coord.X() >= lo.X() && coord.X() <= hi.X() && coord.Y() >= lo.Y() && coord.Y() <= hi.Y() );
|
||||
}
|
||||
inline const Coord Centre() const
|
||||
{
|
||||
int mid_x = int(round(lo.X()+(size.X()/2.0)));
|
||||
int mid_y = int(round(lo.Y()+(size.Y()/2.0)));
|
||||
return( Coord( mid_x, mid_y ) );
|
||||
}
|
||||
inline bool Inside( const Coord &coord ) const
|
||||
{
|
||||
return( coord.X() >= lo.X() && coord.X() <= hi.X() && coord.Y() >= lo.Y() && coord.Y() <= hi.Y() );
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ZM_BOX_H
|
||||
|
|
|
@ -25,57 +25,57 @@
|
|||
|
||||
unsigned int Buffer::assign( const unsigned char *pStorage, unsigned int pSize )
|
||||
{
|
||||
if ( mAllocation < pSize )
|
||||
{
|
||||
delete[] mStorage;
|
||||
mAllocation = pSize;
|
||||
mHead = mStorage = new unsigned char[pSize];
|
||||
}
|
||||
mSize = pSize;
|
||||
memcpy( mStorage, pStorage, mSize );
|
||||
mHead = mStorage;
|
||||
mTail = mHead + mSize;
|
||||
return( mSize );
|
||||
if ( mAllocation < pSize )
|
||||
{
|
||||
delete[] mStorage;
|
||||
mAllocation = pSize;
|
||||
mHead = mStorage = new unsigned char[pSize];
|
||||
}
|
||||
mSize = pSize;
|
||||
memcpy( mStorage, pStorage, mSize );
|
||||
mHead = mStorage;
|
||||
mTail = mHead + mSize;
|
||||
return( mSize );
|
||||
}
|
||||
|
||||
unsigned int Buffer::expand( unsigned int count )
|
||||
{
|
||||
int spare = mAllocation - mSize;
|
||||
int headSpace = mHead - mStorage;
|
||||
int tailSpace = spare - headSpace;
|
||||
int width = mTail - mHead;
|
||||
if ( spare > (int)count )
|
||||
int spare = mAllocation - mSize;
|
||||
int headSpace = mHead - mStorage;
|
||||
int tailSpace = spare - headSpace;
|
||||
int width = mTail - mHead;
|
||||
if ( spare > (int)count )
|
||||
{
|
||||
if ( tailSpace < (int)count )
|
||||
{
|
||||
if ( tailSpace < (int)count )
|
||||
{
|
||||
memmove( mStorage, mHead, mSize );
|
||||
mHead = mStorage;
|
||||
mTail = mHead + width;
|
||||
}
|
||||
memmove( mStorage, mHead, mSize );
|
||||
mHead = mStorage;
|
||||
mTail = mHead + width;
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
mAllocation += count;
|
||||
unsigned char *newStorage = new unsigned char[mAllocation];
|
||||
if ( mStorage )
|
||||
{
|
||||
mAllocation += count;
|
||||
unsigned char *newStorage = new unsigned char[mAllocation];
|
||||
if ( mStorage )
|
||||
{
|
||||
memcpy( newStorage, mHead, mSize );
|
||||
delete[] mStorage;
|
||||
}
|
||||
mStorage = newStorage;
|
||||
mHead = mStorage;
|
||||
mTail = mHead + width;
|
||||
memcpy( newStorage, mHead, mSize );
|
||||
delete[] mStorage;
|
||||
}
|
||||
return( mSize );
|
||||
mStorage = newStorage;
|
||||
mHead = mStorage;
|
||||
mTail = mHead + width;
|
||||
}
|
||||
return( mSize );
|
||||
}
|
||||
|
||||
int Buffer::read_into( int sd, unsigned int bytes ) {
|
||||
// Make sure there is enough space
|
||||
this->expand(bytes);
|
||||
int bytes_read = read( sd, mTail, bytes );
|
||||
if ( bytes_read > 0 ) {
|
||||
mTail += bytes_read;
|
||||
mSize += bytes_read;
|
||||
}
|
||||
return bytes_read;
|
||||
// Make sure there is enough space
|
||||
this->expand(bytes);
|
||||
int bytes_read = read( sd, mTail, bytes );
|
||||
if ( bytes_read > 0 ) {
|
||||
mTail += bytes_read;
|
||||
mSize += bytes_read;
|
||||
}
|
||||
return bytes_read;
|
||||
}
|
||||
|
|
332
src/zm_buffer.h
332
src/zm_buffer.h
|
@ -27,183 +27,183 @@
|
|||
class Buffer
|
||||
{
|
||||
protected:
|
||||
unsigned char *mStorage;
|
||||
unsigned int mAllocation;
|
||||
unsigned int mSize;
|
||||
unsigned char *mHead;
|
||||
unsigned char *mTail;
|
||||
unsigned char *mStorage;
|
||||
unsigned int mAllocation;
|
||||
unsigned int mSize;
|
||||
unsigned char *mHead;
|
||||
unsigned char *mTail;
|
||||
|
||||
public:
|
||||
Buffer() : mStorage( 0 ), mAllocation( 0 ), mSize( 0 ), mHead( 0 ), mTail( 0 )
|
||||
Buffer() : mStorage( 0 ), mAllocation( 0 ), mSize( 0 ), mHead( 0 ), mTail( 0 )
|
||||
{
|
||||
}
|
||||
Buffer( unsigned int pSize ) : mAllocation( pSize ), mSize( 0 )
|
||||
{
|
||||
mHead = mStorage = new unsigned char[mAllocation];
|
||||
mTail = mHead;
|
||||
}
|
||||
Buffer( const unsigned char *pStorage, unsigned int pSize ) : mAllocation( pSize ), mSize( pSize )
|
||||
{
|
||||
mHead = mStorage = new unsigned char[mSize];
|
||||
memcpy( mStorage, pStorage, mSize );
|
||||
mTail = mHead + mSize;
|
||||
}
|
||||
Buffer( const Buffer &buffer ) : mAllocation( buffer.mSize ), mSize( buffer.mSize )
|
||||
{
|
||||
mHead = mStorage = new unsigned char[mSize];
|
||||
memcpy( mStorage, buffer.mHead, mSize );
|
||||
mTail = mHead + mSize;
|
||||
}
|
||||
~Buffer()
|
||||
{
|
||||
delete[] mStorage;
|
||||
}
|
||||
unsigned char *head() const { return( mHead ); }
|
||||
unsigned char *tail() const { return( mTail ); }
|
||||
unsigned int size() const { return( mSize ); }
|
||||
bool empty() const { return( mSize == 0 ); }
|
||||
unsigned int size( unsigned int pSize )
|
||||
{
|
||||
if ( mSize < pSize )
|
||||
{
|
||||
expand( pSize-mSize );
|
||||
}
|
||||
Buffer( unsigned int pSize ) : mAllocation( pSize ), mSize( 0 )
|
||||
{
|
||||
mHead = mStorage = new unsigned char[mAllocation];
|
||||
mTail = mHead;
|
||||
}
|
||||
Buffer( const unsigned char *pStorage, unsigned int pSize ) : mAllocation( pSize ), mSize( pSize )
|
||||
{
|
||||
mHead = mStorage = new unsigned char[mSize];
|
||||
memcpy( mStorage, pStorage, mSize );
|
||||
mTail = mHead + mSize;
|
||||
}
|
||||
Buffer( const Buffer &buffer ) : mAllocation( buffer.mSize ), mSize( buffer.mSize )
|
||||
{
|
||||
mHead = mStorage = new unsigned char[mSize];
|
||||
memcpy( mStorage, buffer.mHead, mSize );
|
||||
mTail = mHead + mSize;
|
||||
}
|
||||
~Buffer()
|
||||
{
|
||||
delete[] mStorage;
|
||||
}
|
||||
unsigned char *head() const { return( mHead ); }
|
||||
unsigned char *tail() const { return( mTail ); }
|
||||
unsigned int size() const { return( mSize ); }
|
||||
bool empty() const { return( mSize == 0 ); }
|
||||
unsigned int size( unsigned int pSize )
|
||||
{
|
||||
if ( mSize < pSize )
|
||||
{
|
||||
expand( pSize-mSize );
|
||||
}
|
||||
return( mSize );
|
||||
}
|
||||
//unsigned int Allocation() const { return( mAllocation ); }
|
||||
return( mSize );
|
||||
}
|
||||
//unsigned int Allocation() const { return( mAllocation ); }
|
||||
|
||||
void clear()
|
||||
void clear()
|
||||
{
|
||||
mSize = 0;
|
||||
mHead = mTail = mStorage;
|
||||
}
|
||||
|
||||
unsigned int assign( const unsigned char *pStorage, unsigned int pSize );
|
||||
unsigned int assign( const Buffer &buffer )
|
||||
{
|
||||
return( assign( buffer.mHead, buffer.mSize ) );
|
||||
}
|
||||
|
||||
// Trim from the front of the buffer
|
||||
unsigned int consume( unsigned int count )
|
||||
{
|
||||
if ( count > mSize )
|
||||
{
|
||||
mSize = 0;
|
||||
Warning( "Attempt to consume %d bytes of buffer, size is only %d bytes", count, mSize );
|
||||
count = mSize;
|
||||
}
|
||||
mHead += count;
|
||||
mSize -= count;
|
||||
tidy( 0 );
|
||||
return( count );
|
||||
}
|
||||
// Trim from the end of the buffer
|
||||
unsigned int shrink( unsigned int count )
|
||||
{
|
||||
if ( count > mSize )
|
||||
{
|
||||
Warning( "Attempt to shrink buffer by %d bytes, size is only %d bytes", count, mSize );
|
||||
count = mSize;
|
||||
}
|
||||
mSize -= count;
|
||||
if ( mTail > (mHead + mSize) )
|
||||
mTail = mHead + mSize;
|
||||
tidy( 0 );
|
||||
return( count );
|
||||
}
|
||||
// Add to the end of the buffer
|
||||
unsigned int expand( unsigned int count );
|
||||
|
||||
// Return pointer to the first pSize bytes and advance the head
|
||||
unsigned char *extract( unsigned int pSize )
|
||||
{
|
||||
if ( pSize > mSize )
|
||||
{
|
||||
Warning( "Attempt to extract %d bytes of buffer, size is only %d bytes", pSize, mSize );
|
||||
pSize = mSize;
|
||||
}
|
||||
unsigned char *oldHead = mHead;
|
||||
mHead += pSize;
|
||||
mSize -= pSize;
|
||||
tidy( 0 );
|
||||
return( oldHead );
|
||||
}
|
||||
// Add bytes to the end of the buffer
|
||||
unsigned int append( const unsigned char *pStorage, unsigned int pSize )
|
||||
{
|
||||
expand( pSize );
|
||||
memcpy( mTail, pStorage, pSize );
|
||||
mTail += pSize;
|
||||
mSize += pSize;
|
||||
return( mSize );
|
||||
}
|
||||
unsigned int append( const char *pStorage, unsigned int pSize )
|
||||
{
|
||||
return( append( (const unsigned char *)pStorage, pSize ) );
|
||||
}
|
||||
unsigned int append( const Buffer &buffer )
|
||||
{
|
||||
return( append( buffer.mHead, buffer.mSize ) );
|
||||
}
|
||||
void tidy( bool level=0 )
|
||||
{
|
||||
if ( mHead != mStorage )
|
||||
{
|
||||
if ( mSize == 0 )
|
||||
mHead = mTail = mStorage;
|
||||
}
|
||||
|
||||
unsigned int assign( const unsigned char *pStorage, unsigned int pSize );
|
||||
unsigned int assign( const Buffer &buffer )
|
||||
{
|
||||
return( assign( buffer.mHead, buffer.mSize ) );
|
||||
}
|
||||
|
||||
// Trim from the front of the buffer
|
||||
unsigned int consume( unsigned int count )
|
||||
{
|
||||
if ( count > mSize )
|
||||
else if ( level )
|
||||
{
|
||||
if ( (mHead-mStorage) > mSize )
|
||||
{
|
||||
Warning( "Attempt to consume %d bytes of buffer, size is only %d bytes", count, mSize );
|
||||
count = mSize;
|
||||
memcpy( mStorage, mHead, mSize );
|
||||
mHead = mStorage;
|
||||
mTail = mHead + mSize;
|
||||
}
|
||||
mHead += count;
|
||||
mSize -= count;
|
||||
tidy( 0 );
|
||||
return( count );
|
||||
}
|
||||
}
|
||||
// Trim from the end of the buffer
|
||||
unsigned int shrink( unsigned int count )
|
||||
{
|
||||
if ( count > mSize )
|
||||
{
|
||||
Warning( "Attempt to shrink buffer by %d bytes, size is only %d bytes", count, mSize );
|
||||
count = mSize;
|
||||
}
|
||||
mSize -= count;
|
||||
if ( mTail > (mHead + mSize) )
|
||||
mTail = mHead + mSize;
|
||||
tidy( 0 );
|
||||
return( count );
|
||||
}
|
||||
// Add to the end of the buffer
|
||||
unsigned int expand( unsigned int count );
|
||||
}
|
||||
|
||||
// Return pointer to the first pSize bytes and advance the head
|
||||
unsigned char *extract( unsigned int pSize )
|
||||
{
|
||||
if ( pSize > mSize )
|
||||
{
|
||||
Warning( "Attempt to extract %d bytes of buffer, size is only %d bytes", pSize, mSize );
|
||||
pSize = mSize;
|
||||
}
|
||||
unsigned char *oldHead = mHead;
|
||||
mHead += pSize;
|
||||
mSize -= pSize;
|
||||
tidy( 0 );
|
||||
return( oldHead );
|
||||
}
|
||||
// Add bytes to the end of the buffer
|
||||
unsigned int append( const unsigned char *pStorage, unsigned int pSize )
|
||||
{
|
||||
expand( pSize );
|
||||
memcpy( mTail, pStorage, pSize );
|
||||
mTail += pSize;
|
||||
mSize += pSize;
|
||||
return( mSize );
|
||||
}
|
||||
unsigned int append( const char *pStorage, unsigned int pSize )
|
||||
{
|
||||
return( append( (const unsigned char *)pStorage, pSize ) );
|
||||
}
|
||||
unsigned int append( const Buffer &buffer )
|
||||
{
|
||||
return( append( buffer.mHead, buffer.mSize ) );
|
||||
}
|
||||
void tidy( bool level=0 )
|
||||
{
|
||||
if ( mHead != mStorage )
|
||||
{
|
||||
if ( mSize == 0 )
|
||||
mHead = mTail = mStorage;
|
||||
else if ( level )
|
||||
{
|
||||
if ( (mHead-mStorage) > mSize )
|
||||
{
|
||||
memcpy( mStorage, mHead, mSize );
|
||||
mHead = mStorage;
|
||||
mTail = mHead + mSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Buffer &operator=( const Buffer &buffer )
|
||||
{
|
||||
assign( buffer );
|
||||
return( *this );
|
||||
}
|
||||
Buffer &operator+=( const Buffer &buffer )
|
||||
{
|
||||
append( buffer );
|
||||
return( *this );
|
||||
}
|
||||
Buffer &operator+=( unsigned int count )
|
||||
{
|
||||
expand( count );
|
||||
return( *this );
|
||||
}
|
||||
Buffer &operator-=( unsigned int count )
|
||||
{
|
||||
consume( count );
|
||||
return( *this );
|
||||
}
|
||||
operator unsigned char *() const
|
||||
{
|
||||
return( mHead );
|
||||
}
|
||||
operator char *() const
|
||||
{
|
||||
return( (char *)mHead );
|
||||
}
|
||||
unsigned char *operator+(int offset) const
|
||||
{
|
||||
return( (unsigned char *)(mHead+offset) );
|
||||
}
|
||||
unsigned char operator[](int index) const
|
||||
{
|
||||
return( *(mHead+index) );
|
||||
}
|
||||
operator int () const
|
||||
{
|
||||
return( (int)mSize );
|
||||
}
|
||||
int read_into( int sd, unsigned int bytes );
|
||||
Buffer &operator=( const Buffer &buffer )
|
||||
{
|
||||
assign( buffer );
|
||||
return( *this );
|
||||
}
|
||||
Buffer &operator+=( const Buffer &buffer )
|
||||
{
|
||||
append( buffer );
|
||||
return( *this );
|
||||
}
|
||||
Buffer &operator+=( unsigned int count )
|
||||
{
|
||||
expand( count );
|
||||
return( *this );
|
||||
}
|
||||
Buffer &operator-=( unsigned int count )
|
||||
{
|
||||
consume( count );
|
||||
return( *this );
|
||||
}
|
||||
operator unsigned char *() const
|
||||
{
|
||||
return( mHead );
|
||||
}
|
||||
operator char *() const
|
||||
{
|
||||
return( (char *)mHead );
|
||||
}
|
||||
unsigned char *operator+(int offset) const
|
||||
{
|
||||
return( (unsigned char *)(mHead+offset) );
|
||||
}
|
||||
unsigned char operator[](int index) const
|
||||
{
|
||||
return( *(mHead+index) );
|
||||
}
|
||||
operator int () const
|
||||
{
|
||||
return( (int)mSize );
|
||||
}
|
||||
int read_into( int sd, unsigned int bytes );
|
||||
};
|
||||
|
||||
#endif // ZM_BUFFER_H
|
||||
|
|
798
src/zm_comms.cpp
798
src/zm_comms.cpp
File diff suppressed because it is too large
Load Diff
1284
src/zm_comms.h
1284
src/zm_comms.h
File diff suppressed because it is too large
Load Diff
|
@ -117,7 +117,8 @@ void zmLoadConfig()
|
|||
|
||||
Debug( 1, "Fetching ZM_SERVER_ID For Name = %s", staticConfig.SERVER_NAME.c_str() );
|
||||
std::string sql = stringtf("SELECT Id FROM Servers WHERE Name='%s'", staticConfig.SERVER_NAME.c_str() );
|
||||
if ( MYSQL_ROW dbrow = zmDbFetchOne( sql.c_str() ) ) {
|
||||
zmDbRow dbrow;
|
||||
if ( dbrow.fetch( sql.c_str() ) ) {
|
||||
staticConfig.SERVER_ID = atoi(dbrow[0]);
|
||||
} else {
|
||||
Fatal("Can't get ServerId for Server %s", staticConfig.SERVER_NAME.c_str() );
|
||||
|
@ -128,7 +129,8 @@ void zmLoadConfig()
|
|||
Debug( 1, "Fetching ZM_SERVER_NAME For Id = %d", staticConfig.SERVER_ID );
|
||||
std::string sql = stringtf("SELECT Name FROM Servers WHERE Id='%d'", staticConfig.SERVER_ID );
|
||||
|
||||
if ( MYSQL_ROW dbrow = zmDbFetchOne( sql.c_str() ) ) {
|
||||
zmDbRow dbrow;
|
||||
if ( dbrow.fetch( sql.c_str() ) ) {
|
||||
staticConfig.SERVER_NAME = std::string(dbrow[0]);
|
||||
} else {
|
||||
Fatal("Can't get ServerName for Server ID %d", staticConfig.SERVER_ID );
|
||||
|
|
|
@ -25,48 +25,48 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
#define ZM_CONFIG "@ZM_CONFIG@" // Path to config file
|
||||
#define ZM_VERSION "@VERSION@" // ZoneMinder Version
|
||||
#define ZM_CONFIG "@ZM_CONFIG@" // Path to config file
|
||||
#define ZM_VERSION "@VERSION@" // ZoneMinder Version
|
||||
|
||||
#define ZM_HAS_V4L1 @ZM_HAS_V4L1@
|
||||
#define ZM_HAS_V4L2 @ZM_HAS_V4L2@
|
||||
#define ZM_HAS_V4L @ZM_HAS_V4L@
|
||||
#define ZM_HAS_V4L1 @ZM_HAS_V4L1@
|
||||
#define ZM_HAS_V4L2 @ZM_HAS_V4L2@
|
||||
#define ZM_HAS_V4L @ZM_HAS_V4L@
|
||||
|
||||
#ifdef HAVE_LIBAVFORMAT
|
||||
#define ZM_HAS_FFMPEG 1
|
||||
#define ZM_HAS_FFMPEG 1
|
||||
#endif // HAVE_LIBAVFORMAT
|
||||
|
||||
#define ZM_MAX_IMAGE_WIDTH 2048 // The largest image we imagine ever handling
|
||||
#define ZM_MAX_IMAGE_HEIGHT 1536 // The largest image we imagine ever handling
|
||||
#define ZM_MAX_IMAGE_COLOURS 4 // The largest image we imagine ever handling
|
||||
#define ZM_MAX_IMAGE_DIM (ZM_MAX_IMAGE_WIDTH*ZM_MAX_IMAGE_HEIGHT)
|
||||
#define ZM_MAX_IMAGE_SIZE (ZM_MAX_IMAGE_DIM*ZM_MAX_IMAGE_COLOURS)
|
||||
#define ZM_MAX_IMAGE_WIDTH 2048 // The largest image we imagine ever handling
|
||||
#define ZM_MAX_IMAGE_HEIGHT 1536 // The largest image we imagine ever handling
|
||||
#define ZM_MAX_IMAGE_COLOURS 4 // The largest image we imagine ever handling
|
||||
#define ZM_MAX_IMAGE_DIM (ZM_MAX_IMAGE_WIDTH*ZM_MAX_IMAGE_HEIGHT)
|
||||
#define ZM_MAX_IMAGE_SIZE (ZM_MAX_IMAGE_DIM*ZM_MAX_IMAGE_COLOURS)
|
||||
|
||||
#define ZM_SCALE_BASE 100 // The factor by which we bump up 'scale' to simulate FP
|
||||
#define ZM_RATE_BASE 100 // The factor by which we bump up 'rate' to simulate FP
|
||||
#define ZM_SCALE_BASE 100 // The factor by which we bump up 'scale' to simulate FP
|
||||
#define ZM_RATE_BASE 100 // The factor by which we bump up 'rate' to simulate FP
|
||||
|
||||
#define ZM_SQL_BATCH_SIZE 50 // Limit the size of multi-row SQL statements
|
||||
#define ZM_SQL_SML_BUFSIZ 256 // Size of SQL buffer
|
||||
#define ZM_SQL_MED_BUFSIZ 1024 // Size of SQL buffer
|
||||
#define ZM_SQL_LGE_BUFSIZ 8192 // Size of SQL buffer
|
||||
#define ZM_SQL_BATCH_SIZE 50 // Limit the size of multi-row SQL statements
|
||||
#define ZM_SQL_SML_BUFSIZ 256 // Size of SQL buffer
|
||||
#define ZM_SQL_MED_BUFSIZ 1024 // Size of SQL buffer
|
||||
#define ZM_SQL_LGE_BUFSIZ 8192 // Size of SQL buffer
|
||||
|
||||
#define ZM_NETWORK_BUFSIZ 32768 // Size of network buffer
|
||||
#define ZM_NETWORK_BUFSIZ 32768 // Size of network buffer
|
||||
|
||||
#define ZM_MAX_FPS 30 // The maximum frame rate we expect to handle
|
||||
#define ZM_SAMPLE_RATE int(1000000/ZM_MAX_FPS) // A general nyquist sample frequency for delays etc
|
||||
#define ZM_SUSPENDED_RATE int(1000000/4) // A slower rate for when disabled etc
|
||||
#define ZM_MAX_FPS 30 // The maximum frame rate we expect to handle
|
||||
#define ZM_SAMPLE_RATE int(1000000/ZM_MAX_FPS) // A general nyquist sample frequency for delays etc
|
||||
#define ZM_SUSPENDED_RATE int(1000000/4) // A slower rate for when disabled etc
|
||||
|
||||
extern void zmLoadConfig();
|
||||
|
||||
struct StaticConfig
|
||||
{
|
||||
std::string DB_HOST;
|
||||
std::string DB_NAME;
|
||||
std::string DB_USER;
|
||||
std::string DB_PASS;
|
||||
std::string PATH_WEB;
|
||||
std::string SERVER_NAME;
|
||||
unsigned int SERVER_ID;
|
||||
std::string DB_HOST;
|
||||
std::string DB_NAME;
|
||||
std::string DB_USER;
|
||||
std::string DB_PASS;
|
||||
std::string PATH_WEB;
|
||||
std::string SERVER_NAME;
|
||||
unsigned int SERVER_ID;
|
||||
};
|
||||
|
||||
extern StaticConfig staticConfig;
|
||||
|
@ -74,63 +74,63 @@ extern StaticConfig staticConfig;
|
|||
class ConfigItem
|
||||
{
|
||||
private:
|
||||
char *name;
|
||||
char *value;
|
||||
char *type;
|
||||
char *name;
|
||||
char *value;
|
||||
char *type;
|
||||
|
||||
mutable enum { CFG_BOOLEAN, CFG_INTEGER, CFG_DECIMAL, CFG_STRING } cfg_type;
|
||||
mutable union
|
||||
{
|
||||
bool boolean_value;
|
||||
int integer_value;
|
||||
double decimal_value;
|
||||
char *string_value;
|
||||
} cfg_value;
|
||||
mutable bool accessed;
|
||||
mutable enum { CFG_BOOLEAN, CFG_INTEGER, CFG_DECIMAL, CFG_STRING } cfg_type;
|
||||
mutable union
|
||||
{
|
||||
bool boolean_value;
|
||||
int integer_value;
|
||||
double decimal_value;
|
||||
char *string_value;
|
||||
} cfg_value;
|
||||
mutable bool accessed;
|
||||
|
||||
public:
|
||||
ConfigItem( const char *p_name, const char *p_value, const char *const p_type );
|
||||
~ConfigItem();
|
||||
void ConvertValue() const;
|
||||
bool BooleanValue() const;
|
||||
int IntegerValue() const;
|
||||
double DecimalValue() const;
|
||||
const char *StringValue() const;
|
||||
ConfigItem( const char *p_name, const char *p_value, const char *const p_type );
|
||||
~ConfigItem();
|
||||
void ConvertValue() const;
|
||||
bool BooleanValue() const;
|
||||
int IntegerValue() const;
|
||||
double DecimalValue() const;
|
||||
const char *StringValue() const;
|
||||
|
||||
inline operator bool() const
|
||||
{
|
||||
return( BooleanValue() );
|
||||
}
|
||||
inline operator int() const
|
||||
{
|
||||
return( IntegerValue() );
|
||||
}
|
||||
inline operator double() const
|
||||
{
|
||||
return( DecimalValue() );
|
||||
}
|
||||
inline operator const char *() const
|
||||
{
|
||||
return( StringValue() );
|
||||
}
|
||||
inline operator bool() const
|
||||
{
|
||||
return( BooleanValue() );
|
||||
}
|
||||
inline operator int() const
|
||||
{
|
||||
return( IntegerValue() );
|
||||
}
|
||||
inline operator double() const
|
||||
{
|
||||
return( DecimalValue() );
|
||||
}
|
||||
inline operator const char *() const
|
||||
{
|
||||
return( StringValue() );
|
||||
}
|
||||
};
|
||||
|
||||
class Config
|
||||
{
|
||||
public:
|
||||
ZM_CFG_DECLARE_LIST
|
||||
ZM_CFG_DECLARE_LIST
|
||||
|
||||
private:
|
||||
int n_items;
|
||||
ConfigItem **items;
|
||||
int n_items;
|
||||
ConfigItem **items;
|
||||
|
||||
public:
|
||||
Config();
|
||||
~Config();
|
||||
Config();
|
||||
~Config();
|
||||
|
||||
void Load();
|
||||
void Assign();
|
||||
const ConfigItem &Item( int id );
|
||||
void Load();
|
||||
void Assign();
|
||||
const ConfigItem &Item( int id );
|
||||
};
|
||||
|
||||
extern Config config;
|
||||
|
|
|
@ -28,40 +28,40 @@
|
|||
class Coord
|
||||
{
|
||||
private:
|
||||
int x, y;
|
||||
int x, y;
|
||||
|
||||
public:
|
||||
inline Coord() : x(0), y(0)
|
||||
{
|
||||
}
|
||||
inline Coord( int p_x, int p_y ) : x(p_x), y(p_y)
|
||||
{
|
||||
}
|
||||
inline Coord( const Coord &p_coord ) : x(p_coord.x), y(p_coord.y)
|
||||
{
|
||||
}
|
||||
inline int &X() { return( x ); }
|
||||
inline const int &X() const { return( x ); }
|
||||
inline int &Y() { return( y ); }
|
||||
inline const int &Y() const { return( y ); }
|
||||
inline Coord() : x(0), y(0)
|
||||
{
|
||||
}
|
||||
inline Coord( int p_x, int p_y ) : x(p_x), y(p_y)
|
||||
{
|
||||
}
|
||||
inline Coord( const Coord &p_coord ) : x(p_coord.x), y(p_coord.y)
|
||||
{
|
||||
}
|
||||
inline int &X() { return( x ); }
|
||||
inline const int &X() const { return( x ); }
|
||||
inline int &Y() { return( y ); }
|
||||
inline const int &Y() const { return( y ); }
|
||||
|
||||
inline static Coord Range( const Coord &coord1, const Coord &coord2 )
|
||||
{
|
||||
Coord result( (coord1.x-coord2.x)+1, (coord1.y-coord2.y)+1 );
|
||||
return( result );
|
||||
}
|
||||
inline static Coord Range( const Coord &coord1, const Coord &coord2 )
|
||||
{
|
||||
Coord result( (coord1.x-coord2.x)+1, (coord1.y-coord2.y)+1 );
|
||||
return( result );
|
||||
}
|
||||
|
||||
inline bool operator==( const Coord &coord ) { return( x == coord.x && y == coord.y ); }
|
||||
inline bool operator!=( const Coord &coord ) { return( x != coord.x || y != coord.y ); }
|
||||
inline bool operator>( const Coord &coord ) { return( x > coord.x && y > coord.y ); }
|
||||
inline bool operator>=( const Coord &coord ) { return( !(operator<(coord)) ); }
|
||||
inline bool operator<( const Coord &coord ) { return( x < coord.x && y < coord.y ); }
|
||||
inline bool operator<=( const Coord &coord ) { return( !(operator>(coord)) ); }
|
||||
inline Coord &operator+=( const Coord &coord ) { x += coord.x; y += coord.y; return( *this ); }
|
||||
inline Coord &operator-=( const Coord &coord ) { x -= coord.x; y -= coord.y; return( *this ); }
|
||||
inline bool operator==( const Coord &coord ) { return( x == coord.x && y == coord.y ); }
|
||||
inline bool operator!=( const Coord &coord ) { return( x != coord.x || y != coord.y ); }
|
||||
inline bool operator>( const Coord &coord ) { return( x > coord.x && y > coord.y ); }
|
||||
inline bool operator>=( const Coord &coord ) { return( !(operator<(coord)) ); }
|
||||
inline bool operator<( const Coord &coord ) { return( x < coord.x && y < coord.y ); }
|
||||
inline bool operator<=( const Coord &coord ) { return( !(operator>(coord)) ); }
|
||||
inline Coord &operator+=( const Coord &coord ) { x += coord.x; y += coord.y; return( *this ); }
|
||||
inline Coord &operator-=( const Coord &coord ) { x -= coord.x; y -= coord.y; return( *this ); }
|
||||
|
||||
inline friend Coord operator+( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result += coord2; return( result ); }
|
||||
inline friend Coord operator-( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result -= coord2; return( result ); }
|
||||
inline friend Coord operator+( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result += coord2; return( result ); }
|
||||
inline friend Coord operator-( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result -= coord2; return( result ); }
|
||||
};
|
||||
|
||||
#endif // ZM_COORD_H
|
||||
|
|
153
src/zm_db.cpp
153
src/zm_db.cpp
|
@ -29,85 +29,104 @@ int zmDbConnected = false;
|
|||
|
||||
void zmDbConnect()
|
||||
{
|
||||
if ( !mysql_init( &dbconn ) )
|
||||
{
|
||||
Error( "Can't initialise database connection: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
my_bool reconnect = 1;
|
||||
if ( mysql_options( &dbconn, MYSQL_OPT_RECONNECT, &reconnect ) )
|
||||
Fatal( "Can't set database auto reconnect option: %s", mysql_error( &dbconn ) );
|
||||
std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":/" );
|
||||
if ( colonIndex != std::string::npos )
|
||||
if ( !mysql_init( &dbconn ) )
|
||||
{
|
||||
Error( "Can't initialise database connection: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
my_bool reconnect = 1;
|
||||
if ( mysql_options( &dbconn, MYSQL_OPT_RECONNECT, &reconnect ) )
|
||||
Fatal( "Can't set database auto reconnect option: %s", mysql_error( &dbconn ) );
|
||||
std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":/" );
|
||||
if ( colonIndex != std::string::npos )
|
||||
{
|
||||
std::string dbHost = staticConfig.DB_HOST.substr( 0, colonIndex );
|
||||
std::string dbPort = staticConfig.DB_HOST.substr( colonIndex+1 );
|
||||
if ( !mysql_real_connect( &dbconn, dbHost.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, atoi(dbPort.c_str()), 0, 0 ) )
|
||||
{
|
||||
std::string dbHost = staticConfig.DB_HOST.substr( 0, colonIndex );
|
||||
std::string dbPort = staticConfig.DB_HOST.substr( colonIndex+1 );
|
||||
if ( !mysql_real_connect( &dbconn, dbHost.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, atoi(dbPort.c_str()), 0, 0 ) )
|
||||
{
|
||||
Error( "Can't connect to server: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
Error( "Can't connect to server: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !mysql_real_connect( &dbconn, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, 0, 0, 0 ) )
|
||||
{
|
||||
if ( !mysql_real_connect( &dbconn, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, 0, 0, 0 ) )
|
||||
{
|
||||
Error( "Can't connect to server: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
Error( "Can't connect to server: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
if ( mysql_select_db( &dbconn, staticConfig.DB_NAME.c_str() ) )
|
||||
{
|
||||
Error( "Can't select database: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
zmDbConnected = true;
|
||||
}
|
||||
if ( mysql_select_db( &dbconn, staticConfig.DB_NAME.c_str() ) )
|
||||
{
|
||||
Error( "Can't select database: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
zmDbConnected = true;
|
||||
}
|
||||
|
||||
void zmDbClose()
|
||||
{
|
||||
if ( zmDbConnected )
|
||||
{
|
||||
mysql_close( &dbconn );
|
||||
// mysql_init() call implicitly mysql_library_init() but
|
||||
// mysql_close() does not call mysql_library_end()
|
||||
mysql_library_end();
|
||||
zmDbConnected = false;
|
||||
}
|
||||
if ( zmDbConnected )
|
||||
{
|
||||
mysql_close( &dbconn );
|
||||
// mysql_init() call implicitly mysql_library_init() but
|
||||
// mysql_close() does not call mysql_library_end()
|
||||
mysql_library_end();
|
||||
zmDbConnected = false;
|
||||
}
|
||||
}
|
||||
|
||||
MYSQL_RES * zmDbFetch( const char * query ) {
|
||||
if ( ! zmDbConnected ) {
|
||||
Error( "Not connected." );
|
||||
return NULL;
|
||||
}
|
||||
if ( ! zmDbConnected ) {
|
||||
Error( "Not connected." );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( mysql_query( &dbconn, query ) ) {
|
||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||
return NULL;
|
||||
}
|
||||
Debug( 4, "Success running query: %s", query );
|
||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||
if ( !result ) {
|
||||
Error( "Can't use query result: %s for query %s", mysql_error( &dbconn ), query );
|
||||
return NULL;
|
||||
}
|
||||
return result;
|
||||
if ( mysql_query( &dbconn, query ) ) {
|
||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||
return NULL;
|
||||
}
|
||||
Debug( 4, "Success running query: %s", query );
|
||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||
if ( !result ) {
|
||||
Error( "Can't use query result: %s for query %s", mysql_error( &dbconn ), query );
|
||||
return NULL;
|
||||
}
|
||||
return result;
|
||||
} // end MYSQL_RES * zmDbFetch( const char * query );
|
||||
|
||||
MYSQL_ROW zmDbFetchOne( const char *query ) {
|
||||
MYSQL_RES *result = zmDbFetch( query );
|
||||
int n_rows = mysql_num_rows( result );
|
||||
if ( n_rows != 1 ) {
|
||||
Error( "Bogus number of lines return from query, %d returned for query %s.", n_rows, query );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MYSQL_ROW dbrow = mysql_fetch_row( result );
|
||||
mysql_free_result( result );
|
||||
if ( ! dbrow ) {
|
||||
Error("Error getting row from query %s. Error is %s", query, mysql_error( &dbconn ) );
|
||||
return NULL;
|
||||
}
|
||||
return dbrow;
|
||||
zmDbRow *zmDbFetchOne( const char *query ) {
|
||||
zmDbRow *row = new zmDbRow();
|
||||
if ( row->fetch( query ) ) {
|
||||
return row;
|
||||
}
|
||||
delete row;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MYSQL_RES *zmDbRow::fetch( const char *query ) {
|
||||
result_set = zmDbFetch( query );
|
||||
if ( ! result_set ) return result_set;
|
||||
|
||||
int n_rows = mysql_num_rows( result_set );
|
||||
if ( n_rows != 1 ) {
|
||||
Error( "Bogus number of lines return from query, %d returned for query %s.", n_rows, query );
|
||||
mysql_free_result( result_set );
|
||||
result_set = NULL;
|
||||
return result_set;
|
||||
}
|
||||
|
||||
row = mysql_fetch_row( result_set );
|
||||
if ( ! row ) {
|
||||
mysql_free_result( result_set );
|
||||
result_set = NULL;
|
||||
Error("Error getting row from query %s. Error is %s", query, mysql_error( &dbconn ) );
|
||||
} else {
|
||||
Debug(3, "Succes");
|
||||
}
|
||||
return result_set;
|
||||
}
|
||||
zmDbRow::~zmDbRow() {
|
||||
if ( result_set )
|
||||
mysql_free_result( result_set );
|
||||
}
|
||||
|
|
17
src/zm_db.h
17
src/zm_db.h
|
@ -22,6 +22,21 @@
|
|||
|
||||
#include <mysql/mysql.h>
|
||||
|
||||
class zmDbRow {
|
||||
private:
|
||||
MYSQL_RES *result_set;
|
||||
MYSQL_ROW row;
|
||||
public:
|
||||
zmDbRow() { result_set = NULL; row = NULL; };
|
||||
MYSQL_RES *fetch( const char *query );
|
||||
zmDbRow( MYSQL_RES *, MYSQL_ROW *row );
|
||||
~zmDbRow();
|
||||
|
||||
char *operator[](unsigned int index) const {
|
||||
return row[index];
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
@ -33,7 +48,7 @@ void zmDbConnect();
|
|||
void zmDbClose();
|
||||
|
||||
MYSQL_RES * zmDbFetch( const char *query );
|
||||
MYSQL_ROW zmDbFetchOne( const char *query );
|
||||
zmDbRow *zmDbFetchOne( const char *query );
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
|
2624
src/zm_event.cpp
2624
src/zm_event.cpp
File diff suppressed because it is too large
Load Diff
324
src/zm_event.h
324
src/zm_event.h
|
@ -42,246 +42,246 @@
|
|||
class Zone;
|
||||
class Monitor;
|
||||
|
||||
#define MAX_PRE_ALARM_FRAMES 16 // Maximum number of prealarm frames that can be stored
|
||||
#define MAX_PRE_ALARM_FRAMES 16 // Maximum number of prealarm frames that can be stored
|
||||
|
||||
//
|
||||
// Class describing events, i.e. captured periods of activity.
|
||||
//
|
||||
class Event
|
||||
{
|
||||
friend class EventStream;
|
||||
friend class EventStream;
|
||||
|
||||
protected:
|
||||
static bool initialised;
|
||||
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 bool initialised;
|
||||
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;
|
||||
protected:
|
||||
static int sd;
|
||||
|
||||
public:
|
||||
typedef std::set<std::string> StringSet;
|
||||
typedef std::map<std::string,StringSet> StringSetMap;
|
||||
public:
|
||||
typedef std::set<std::string> StringSet;
|
||||
typedef std::map<std::string,StringSet> StringSetMap;
|
||||
|
||||
protected:
|
||||
typedef enum { NORMAL, BULK, ALARM } FrameType;
|
||||
protected:
|
||||
typedef enum { NORMAL, BULK, ALARM } FrameType;
|
||||
|
||||
struct PreAlarmData
|
||||
{
|
||||
Image *image;
|
||||
struct timeval timestamp;
|
||||
unsigned int score;
|
||||
Image *alarm_frame;
|
||||
};
|
||||
struct PreAlarmData
|
||||
{
|
||||
Image *image;
|
||||
struct timeval timestamp;
|
||||
unsigned int score;
|
||||
Image *alarm_frame;
|
||||
};
|
||||
|
||||
static int pre_alarm_count;
|
||||
static PreAlarmData pre_alarm_data[MAX_PRE_ALARM_FRAMES];
|
||||
static int pre_alarm_count;
|
||||
static PreAlarmData pre_alarm_data[MAX_PRE_ALARM_FRAMES];
|
||||
|
||||
protected:
|
||||
unsigned int id;
|
||||
Monitor *monitor;
|
||||
struct timeval start_time;
|
||||
struct timeval end_time;
|
||||
std::string cause;
|
||||
StringSetMap noteSetMap;
|
||||
bool videoEvent;
|
||||
int frames;
|
||||
int alarm_frames;
|
||||
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:
|
||||
unsigned int id;
|
||||
Monitor *monitor;
|
||||
struct timeval start_time;
|
||||
struct timeval end_time;
|
||||
std::string cause;
|
||||
StringSetMap noteSetMap;
|
||||
bool videoEvent;
|
||||
int frames;
|
||||
int alarm_frames;
|
||||
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;
|
||||
protected:
|
||||
int last_db_frame;
|
||||
|
||||
protected:
|
||||
static void Initialise()
|
||||
{
|
||||
if ( initialised )
|
||||
return;
|
||||
protected:
|
||||
static void Initialise()
|
||||
{
|
||||
if ( initialised )
|
||||
return;
|
||||
|
||||
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");
|
||||
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;
|
||||
}
|
||||
initialised = true;
|
||||
}
|
||||
|
||||
void createNotes( std::string ¬es );
|
||||
void createNotes( std::string ¬es );
|
||||
|
||||
public:
|
||||
static bool OpenFrameSocket( int );
|
||||
static bool ValidateFrameSocket( int );
|
||||
public:
|
||||
static bool OpenFrameSocket( int );
|
||||
static bool ValidateFrameSocket( int );
|
||||
|
||||
public:
|
||||
Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap, bool p_videoEvent=false );
|
||||
~Event();
|
||||
public:
|
||||
Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap, bool p_videoEvent=false );
|
||||
~Event();
|
||||
|
||||
int Id() const { return( id ); }
|
||||
const std::string &Cause() { return( cause ); }
|
||||
int Frames() const { return( frames ); }
|
||||
int AlarmFrames() const { return( alarm_frames ); }
|
||||
int Id() const { return( id ); }
|
||||
const std::string &Cause() { return( cause ); }
|
||||
int Frames() const { return( frames ); }
|
||||
int AlarmFrames() const { return( alarm_frames ); }
|
||||
|
||||
const struct timeval &StartTime() const { return( start_time ); }
|
||||
const struct timeval &EndTime() const { return( end_time ); }
|
||||
struct timeval &EndTime() { return( end_time ); }
|
||||
const struct timeval &StartTime() const { return( start_time ); }
|
||||
const struct timeval &EndTime() const { return( end_time ); }
|
||||
struct timeval &EndTime() { return( end_time ); }
|
||||
|
||||
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 );
|
||||
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 );
|
||||
void updateNotes( const StringSetMap &stringSetMap );
|
||||
|
||||
void AddFrames( int n_frames, Image **images, struct timeval **timestamps );
|
||||
void AddFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL );
|
||||
void AddFrames( int n_frames, Image **images, struct timeval **timestamps );
|
||||
void AddFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL );
|
||||
|
||||
private:
|
||||
void AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps );
|
||||
private:
|
||||
void AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps );
|
||||
|
||||
public:
|
||||
static const char *getSubPath( struct tm *time )
|
||||
public:
|
||||
static const char *getSubPath( struct tm *time )
|
||||
{
|
||||
static char subpath[PATH_MAX] = "";
|
||||
snprintf( subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec );
|
||||
return( subpath );
|
||||
}
|
||||
static const char *getSubPath( time_t *time )
|
||||
{
|
||||
return( Event::getSubPath( localtime( time ) ) );
|
||||
}
|
||||
|
||||
char* getEventFile(void){
|
||||
return video_file;
|
||||
}
|
||||
|
||||
public:
|
||||
static int PreAlarmCount()
|
||||
{
|
||||
return( pre_alarm_count );
|
||||
}
|
||||
static void EmptyPreAlarmFrames()
|
||||
{
|
||||
if ( pre_alarm_count > 0 )
|
||||
{
|
||||
static char subpath[PATH_MAX] = "";
|
||||
snprintf( subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec );
|
||||
return( subpath );
|
||||
for ( int i = 0; i < MAX_PRE_ALARM_FRAMES; i++ )
|
||||
{
|
||||
delete pre_alarm_data[i].image;
|
||||
delete pre_alarm_data[i].alarm_frame;
|
||||
}
|
||||
memset( pre_alarm_data, 0, sizeof(pre_alarm_data) );
|
||||
}
|
||||
static const char *getSubPath( time_t *time )
|
||||
pre_alarm_count = 0;
|
||||
}
|
||||
static void AddPreAlarmFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL )
|
||||
{
|
||||
pre_alarm_data[pre_alarm_count].image = new Image( *image );
|
||||
pre_alarm_data[pre_alarm_count].timestamp = timestamp;
|
||||
pre_alarm_data[pre_alarm_count].score = score;
|
||||
if ( alarm_frame )
|
||||
{
|
||||
return( Event::getSubPath( localtime( time ) ) );
|
||||
pre_alarm_data[pre_alarm_count].alarm_frame = new Image( *alarm_frame );
|
||||
}
|
||||
|
||||
char* getEventFile(void){
|
||||
return video_file;
|
||||
pre_alarm_count++;
|
||||
}
|
||||
void SavePreAlarmFrames()
|
||||
{
|
||||
for ( int i = 0; i < pre_alarm_count; i++ )
|
||||
{
|
||||
AddFrame( pre_alarm_data[i].image, pre_alarm_data[i].timestamp, pre_alarm_data[i].score, pre_alarm_data[i].alarm_frame );
|
||||
}
|
||||
|
||||
public:
|
||||
static int PreAlarmCount()
|
||||
{
|
||||
return( pre_alarm_count );
|
||||
}
|
||||
static void EmptyPreAlarmFrames()
|
||||
{
|
||||
if ( pre_alarm_count > 0 )
|
||||
{
|
||||
for ( int i = 0; i < MAX_PRE_ALARM_FRAMES; i++ )
|
||||
{
|
||||
delete pre_alarm_data[i].image;
|
||||
delete pre_alarm_data[i].alarm_frame;
|
||||
}
|
||||
memset( pre_alarm_data, 0, sizeof(pre_alarm_data) );
|
||||
}
|
||||
pre_alarm_count = 0;
|
||||
}
|
||||
static void AddPreAlarmFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL )
|
||||
{
|
||||
pre_alarm_data[pre_alarm_count].image = new Image( *image );
|
||||
pre_alarm_data[pre_alarm_count].timestamp = timestamp;
|
||||
pre_alarm_data[pre_alarm_count].score = score;
|
||||
if ( alarm_frame )
|
||||
{
|
||||
pre_alarm_data[pre_alarm_count].alarm_frame = new Image( *alarm_frame );
|
||||
}
|
||||
pre_alarm_count++;
|
||||
}
|
||||
void SavePreAlarmFrames()
|
||||
{
|
||||
for ( int i = 0; i < pre_alarm_count; i++ )
|
||||
{
|
||||
AddFrame( pre_alarm_data[i].image, pre_alarm_data[i].timestamp, pre_alarm_data[i].score, pre_alarm_data[i].alarm_frame );
|
||||
}
|
||||
EmptyPreAlarmFrames();
|
||||
}
|
||||
EmptyPreAlarmFrames();
|
||||
}
|
||||
};
|
||||
|
||||
class EventStream : public StreamBase
|
||||
{
|
||||
public:
|
||||
public:
|
||||
typedef enum { MODE_SINGLE, MODE_ALL, MODE_ALL_GAPLESS } StreamMode;
|
||||
|
||||
protected:
|
||||
protected:
|
||||
struct FrameData {
|
||||
//unsigned long id;
|
||||
time_t timestamp;
|
||||
time_t offset;
|
||||
double delta;
|
||||
bool in_db;
|
||||
//unsigned long id;
|
||||
time_t timestamp;
|
||||
time_t offset;
|
||||
double delta;
|
||||
bool in_db;
|
||||
};
|
||||
|
||||
struct EventData
|
||||
{
|
||||
unsigned long event_id;
|
||||
unsigned long monitor_id;
|
||||
unsigned long storage_id;
|
||||
unsigned long frame_count;
|
||||
time_t start_time;
|
||||
double duration;
|
||||
char path[PATH_MAX];
|
||||
int n_frames;
|
||||
FrameData *frames;
|
||||
char video_file[PATH_MAX];
|
||||
unsigned long event_id;
|
||||
unsigned long monitor_id;
|
||||
unsigned long storage_id;
|
||||
unsigned long frame_count;
|
||||
time_t start_time;
|
||||
double duration;
|
||||
char path[PATH_MAX];
|
||||
int n_frames;
|
||||
FrameData *frames;
|
||||
char video_file[PATH_MAX];
|
||||
};
|
||||
|
||||
protected:
|
||||
protected:
|
||||
static const int STREAM_PAUSE_WAIT = 250000; // Microseconds
|
||||
|
||||
static const StreamMode DEFAULT_MODE = MODE_SINGLE;
|
||||
|
||||
protected:
|
||||
protected:
|
||||
StreamMode mode;
|
||||
bool forceEventChange;
|
||||
|
||||
protected:
|
||||
protected:
|
||||
int curr_frame_id;
|
||||
double curr_stream_time;
|
||||
|
||||
EventData *event_data;
|
||||
|
||||
protected:
|
||||
protected:
|
||||
bool loadEventData( int event_id );
|
||||
bool loadInitialEventData( int init_event_id, int init_frame_id );
|
||||
bool loadInitialEventData( int init_event_id, unsigned int init_frame_id );
|
||||
bool loadInitialEventData( int monitor_id, time_t event_time );
|
||||
|
||||
void checkEventLoaded();
|
||||
void processCommand( const CmdMsg *msg );
|
||||
bool sendFrame( int delta_us );
|
||||
|
||||
public:
|
||||
public:
|
||||
EventStream()
|
||||
{
|
||||
mode = DEFAULT_MODE;
|
||||
mode = DEFAULT_MODE;
|
||||
|
||||
forceEventChange = false;
|
||||
forceEventChange = false;
|
||||
|
||||
curr_frame_id = 0;
|
||||
curr_stream_time = 0.0;
|
||||
curr_frame_id = 0;
|
||||
curr_stream_time = 0.0;
|
||||
|
||||
event_data = 0;
|
||||
event_data = 0;
|
||||
}
|
||||
void setStreamStart( int init_event_id, int init_frame_id=0 )
|
||||
void setStreamStart( int init_event_id, unsigned int init_frame_id=0 )
|
||||
{
|
||||
loadInitialEventData( init_event_id, init_frame_id );
|
||||
loadMonitor( event_data->monitor_id );
|
||||
loadInitialEventData( init_event_id, init_frame_id );
|
||||
loadMonitor( event_data->monitor_id );
|
||||
}
|
||||
void setStreamStart( int monitor_id, time_t event_time )
|
||||
void setStreamStart( int monitor_id, time_t event_time )
|
||||
{
|
||||
loadInitialEventData( monitor_id, event_time );
|
||||
loadMonitor( monitor_id );
|
||||
loadInitialEventData( monitor_id, event_time );
|
||||
loadMonitor( monitor_id );
|
||||
}
|
||||
void setStreamMode( StreamMode p_mode )
|
||||
{
|
||||
mode = p_mode;
|
||||
mode = p_mode;
|
||||
}
|
||||
void runStream();
|
||||
Image *getImage();
|
||||
Image *getImage();
|
||||
};
|
||||
|
||||
#endif // ZM_EVENT_H
|
||||
|
|
|
@ -27,42 +27,42 @@
|
|||
class Exception
|
||||
{
|
||||
protected:
|
||||
typedef enum { INFO, WARNING, ERROR, FATAL } Severity;
|
||||
typedef enum { INFO, WARNING, ERROR, FATAL } Severity;
|
||||
|
||||
protected:
|
||||
std::string mMessage;
|
||||
Severity mSeverity;
|
||||
std::string mMessage;
|
||||
Severity mSeverity;
|
||||
|
||||
public:
|
||||
Exception( const std::string &message, Severity severity=ERROR ) : mMessage( message ), mSeverity( severity )
|
||||
{
|
||||
}
|
||||
Exception( const std::string &message, Severity severity=ERROR ) : mMessage( message ), mSeverity( severity )
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
const std::string &getMessage() const
|
||||
{
|
||||
return( mMessage );
|
||||
}
|
||||
Severity getSeverity() const
|
||||
{
|
||||
return( mSeverity );
|
||||
}
|
||||
bool isInfo() const
|
||||
{
|
||||
return( mSeverity == INFO );
|
||||
}
|
||||
bool isWarning() const
|
||||
{
|
||||
return( mSeverity == WARNING );
|
||||
}
|
||||
bool isError() const
|
||||
{
|
||||
return( mSeverity == ERROR );
|
||||
}
|
||||
bool isFatal() const
|
||||
{
|
||||
return( mSeverity == FATAL );
|
||||
}
|
||||
const std::string &getMessage() const
|
||||
{
|
||||
return( mMessage );
|
||||
}
|
||||
Severity getSeverity() const
|
||||
{
|
||||
return( mSeverity );
|
||||
}
|
||||
bool isInfo() const
|
||||
{
|
||||
return( mSeverity == INFO );
|
||||
}
|
||||
bool isWarning() const
|
||||
{
|
||||
return( mSeverity == WARNING );
|
||||
}
|
||||
bool isError() const
|
||||
{
|
||||
return( mSeverity == ERROR );
|
||||
}
|
||||
bool isFatal() const
|
||||
{
|
||||
return( mSeverity == FATAL );
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ZM_EXCEPTION_H
|
||||
|
|
|
@ -167,12 +167,21 @@ int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint
|
|||
#endif
|
||||
|
||||
/* Check the buffer sizes */
|
||||
size_t insize = avpicture_get_size(in_pf, width, height);
|
||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||
size_t insize = av_image_get_buffer_size(in_pf, width, height,1);
|
||||
#else
|
||||
size_t insize = avpicture_get_size(in_pf, width, height);
|
||||
#endif
|
||||
if(insize != in_buffer_size) {
|
||||
Error("The input buffer size does not match the expected size for the input format. Required: %d Available: %d", insize, in_buffer_size);
|
||||
return -4;
|
||||
}
|
||||
size_t outsize = avpicture_get_size(out_pf, width, height);
|
||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||
size_t outsize = av_image_get_buffer_size(out_pf, width, height,1);
|
||||
#else
|
||||
size_t outsize = avpicture_get_size(out_pf, width, height);
|
||||
#endif
|
||||
|
||||
if(outsize < out_buffer_size) {
|
||||
Error("The output buffer is undersized for the output format. Required: %d Available: %d", outsize, out_buffer_size);
|
||||
return -5;
|
||||
|
@ -186,13 +195,29 @@ int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint
|
|||
}
|
||||
|
||||
/* Fill in the buffers */
|
||||
if(!avpicture_fill( (AVPicture*)input_avframe, (uint8_t*)in_buffer, in_pf, width, height ) ) {
|
||||
Error("Failed filling input frame with input buffer");
|
||||
return -7;
|
||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||
if(av_image_fill_arrays(input_avframe->data, input_avframe->linesize,
|
||||
(uint8_t*)in_buffer, in_pf, width, height, 1) <= 0)
|
||||
{
|
||||
#else
|
||||
if(avpicture_fill( (AVPicture*)input_avframe, (uint8_t*)in_buffer,
|
||||
in_pf, width, height ) <= 0)
|
||||
{
|
||||
#endif
|
||||
Error("Failed filling input frame with input buffer");
|
||||
return -7;
|
||||
}
|
||||
if(!avpicture_fill( (AVPicture*)output_avframe, out_buffer, out_pf, width, height ) ) {
|
||||
Error("Failed filling output frame with output buffer");
|
||||
return -8;
|
||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||
if(av_image_fill_arrays(output_avframe->data, output_avframe->linesize,
|
||||
out_buffer, out_pf, width, height, 1) <= 0)
|
||||
{
|
||||
#else
|
||||
if(avpicture_fill( (AVPicture*)output_avframe, out_buffer,
|
||||
out_pf, width, height ) <= 0)
|
||||
{
|
||||
#endif
|
||||
Error("Failed filling output frame with output buffer");
|
||||
return -8;
|
||||
}
|
||||
|
||||
/* Do the conversion */
|
||||
|
@ -338,15 +363,9 @@ void zm_dump_stream_format(AVFormatContext *ic, int i, int index, int is_output)
|
|||
int flags = (is_output ? ic->oformat->flags : ic->iformat->flags);
|
||||
AVStream *st = ic->streams[i];
|
||||
AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL, 0);
|
||||
unsigned char *separator = ic->dump_separator;
|
||||
char **codec_separator = (char **)av_opt_ptr(st->codec->av_class, st->codec, "dump_separator");
|
||||
int use_format_separator = !*codec_separator;
|
||||
char separator = '\n';
|
||||
|
||||
if (use_format_separator)
|
||||
*codec_separator = av_strdup((const char *)separator);
|
||||
avcodec_string(buf, sizeof(buf), st->codec, is_output);
|
||||
if (use_format_separator)
|
||||
av_freep(codec_separator);
|
||||
Debug(3, " Stream #%d:%d", index, i);
|
||||
|
||||
/* the pid is an important information, so we display it */
|
||||
|
|
|
@ -48,6 +48,12 @@ extern "C" {
|
|||
#else
|
||||
#include <libavcodec/opt.h>
|
||||
#endif
|
||||
|
||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||
#include <libavutil/imgutils.h>
|
||||
#else
|
||||
#include <libavutil/avutil.h>
|
||||
#endif
|
||||
#elif HAVE_FFMPEG_AVUTIL_H
|
||||
#include <ffmpeg/avutil.h>
|
||||
#include <ffmpeg/base64.h>
|
||||
|
|
|
@ -61,6 +61,7 @@ FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::stri
|
|||
mOpenStart = 0;
|
||||
mReopenThread = 0;
|
||||
wasRecording = false;
|
||||
videoStore = NULL;
|
||||
|
||||
#if HAVE_LIBSWSCALE
|
||||
mConvertContext = NULL;
|
||||
|
@ -99,6 +100,7 @@ void FfmpegCamera::Initialise()
|
|||
av_log_set_level( AV_LOG_QUIET );
|
||||
|
||||
av_register_all();
|
||||
avformat_network_init();
|
||||
}
|
||||
|
||||
void FfmpegCamera::Terminate()
|
||||
|
@ -191,7 +193,13 @@ int FfmpegCamera::Capture( Image &image )
|
|||
if ( frameComplete ) {
|
||||
Debug( 4, "Got frame %d", frameCount );
|
||||
|
||||
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height);
|
||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||
av_image_fill_arrays(mFrame->data, mFrame->linesize,
|
||||
directbuffer, imagePixFormat, width, height, 1);
|
||||
#else
|
||||
avpicture_fill( (AVPicture *)mFrame, directbuffer,
|
||||
imagePixFormat, width, height);
|
||||
#endif
|
||||
|
||||
#if HAVE_LIBSWSCALE
|
||||
if(mConvertContext == NULL) {
|
||||
|
@ -350,7 +358,8 @@ int FfmpegCamera::OpenFfmpeg() {
|
|||
if ( mAudioStreamId == -1 )
|
||||
Debug( 2, "Unable to locate audio stream in %s", mPath.c_str() );
|
||||
|
||||
Debug ( 1, "Found video stream" );
|
||||
Debug ( 3, "Found video stream at index %d", mVideoStreamId );
|
||||
Debug ( 3, "Found audio stream at index %d", mAudioStreamId );
|
||||
|
||||
mCodecContext = mFormatContext->streams[mVideoStreamId]->codec;
|
||||
|
||||
|
@ -392,12 +401,17 @@ int FfmpegCamera::OpenFfmpeg() {
|
|||
|
||||
Debug ( 1, "Allocated frames" );
|
||||
|
||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||
int pSize = av_image_get_buffer_size( imagePixFormat, width, height,1 );
|
||||
#else
|
||||
int pSize = avpicture_get_size( imagePixFormat, width, height );
|
||||
#endif
|
||||
|
||||
if( (unsigned int)pSize != imagesize) {
|
||||
Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize);
|
||||
}
|
||||
|
||||
Debug ( 1, "Validated imagesize" );
|
||||
Debug ( 1, "Validated imagesize %d", pSize );
|
||||
|
||||
#if HAVE_LIBSWSCALE
|
||||
Debug ( 1, "Calling sws_isSupportedInput" );
|
||||
|
@ -579,9 +593,11 @@ int FfmpegCamera::CaptureAndRecord( Image &image, bool recording, char* event_fi
|
|||
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height);
|
||||
|
||||
//Keep the last keyframe so we can establish immediate video
|
||||
/*if(packet.flags & AV_PKT_FLAG_KEY)
|
||||
av_copy_packet(&lastKeyframePkt, &packet);*/
|
||||
//TODO I think we need to store the key frame location for seeking as part of the event
|
||||
if(packet.flags & AV_PKT_FLAG_KEY) {
|
||||
//Debug(4, "Have keyframe");
|
||||
//av_copy_packet(&lastKeyframePkt, &packet);
|
||||
//TODO I think we need to store the key frame location for seeking as part of the event
|
||||
}
|
||||
|
||||
//Video recording
|
||||
if ( recording && !wasRecording ) {
|
||||
|
@ -592,6 +608,9 @@ int FfmpegCamera::CaptureAndRecord( Image &image, bool recording, char* event_fi
|
|||
wasRecording = true;
|
||||
strcpy(oldDirectory, event_file);
|
||||
|
||||
|
||||
// Need to write out all the frames from the last keyframe?
|
||||
|
||||
} else if ( ( ! recording ) && wasRecording && videoStore ) {
|
||||
Info("Deleting videoStore instance");
|
||||
delete videoStore;
|
||||
|
@ -649,9 +668,10 @@ int FfmpegCamera::CaptureAndRecord( Image &image, bool recording, char* event_fi
|
|||
}
|
||||
if ( videoStore && recording ) {
|
||||
if ( record_audio ) {
|
||||
Debug(3, "Recording audio packet" );
|
||||
Debug(3, "Recording audio packet streamindex(%d) packetstreamindex(%d)", mAudioStreamId, packet.stream_index );
|
||||
//Write the packet to our video store
|
||||
int ret = videoStore->writeAudioFramePacket(&packet, mFormatContext->streams[packet.stream_index]); //FIXME no relevance of last key frame
|
||||
//FIXME no relevance of last key frame
|
||||
int ret = videoStore->writeAudioFramePacket( &packet, mFormatContext->streams[packet.stream_index] );
|
||||
if ( ret < 0 ) {//Less than zero and we skipped a frame
|
||||
av_free_packet( &packet );
|
||||
return 0;
|
||||
|
@ -661,7 +681,11 @@ int FfmpegCamera::CaptureAndRecord( Image &image, bool recording, char* event_fi
|
|||
}
|
||||
}
|
||||
} else {
|
||||
#if LIBAVUTIL_VERSION_CHECK(54, 23, 0, 23, 0)
|
||||
Debug( 3, "Some other stream index %d, %s", packet.stream_index, av_get_media_type_string( mFormatContext->streams[packet.stream_index]->codec->codec_type) );
|
||||
#else
|
||||
Debug( 3, "Some other stream index %d", packet.stream_index );
|
||||
#endif
|
||||
}
|
||||
av_free_packet( &packet );
|
||||
} // end while ! frameComplete
|
||||
|
|
|
@ -64,7 +64,9 @@ protected:
|
|||
bool wasRecording;
|
||||
VideoStore *videoStore;
|
||||
char oldDirectory[4096];
|
||||
//AVPacket lastKeyframePkt;
|
||||
|
||||
// Last Key frame
|
||||
AVPacket lastKeyframePkt;
|
||||
|
||||
#if HAVE_LIBSWSCALE
|
||||
struct SwsContext *mConvertContext;
|
||||
|
|
6660
src/zm_font.h
6660
src/zm_font.h
File diff suppressed because it is too large
Load Diff
9090
src/zm_image.cpp
9090
src/zm_image.cpp
File diff suppressed because it is too large
Load Diff
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
ImageAnalyser::ImageAnalyser(const ImageAnalyser& source)
|
||||
{
|
||||
m_Detectors = source.m_Detectors;
|
||||
m_Detectors = source.m_Detectors;
|
||||
}
|
||||
|
||||
|
||||
|
@ -17,18 +17,18 @@ ImageAnalyser::ImageAnalyser(const ImageAnalyser& source)
|
|||
*/
|
||||
ImageAnalyser& ImageAnalyser::operator=(const ImageAnalyser& source)
|
||||
{
|
||||
m_Detectors = source.m_Detectors;
|
||||
return *this;
|
||||
m_Detectors = source.m_Detectors;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ImageAnalyser::~ImageAnalyser()
|
||||
{
|
||||
for(DetectorsList::reverse_iterator It = m_Detectors.rbegin();
|
||||
It != m_Detectors.rend();
|
||||
++It)
|
||||
delete *It;
|
||||
for(DetectorsList::reverse_iterator It = m_Detectors.rbegin();
|
||||
It != m_Detectors.rend();
|
||||
++It)
|
||||
delete *It;
|
||||
}
|
||||
|
||||
|
||||
|
@ -42,23 +42,23 @@ ImageAnalyser::~ImageAnalyser()
|
|||
*/
|
||||
int ImageAnalyser::DoDetection(const Image &comp_image, Zone** zones, int n_numZones, Event::StringSetMap noteSetMap, std::string& det_cause)
|
||||
{
|
||||
Event::StringSet zoneSet;
|
||||
int score = 0;
|
||||
Event::StringSet zoneSet;
|
||||
int score = 0;
|
||||
|
||||
for(DetectorsList::iterator It = m_Detectors.begin();
|
||||
It != m_Detectors.end();
|
||||
++It)
|
||||
for(DetectorsList::iterator It = m_Detectors.begin();
|
||||
It != m_Detectors.end();
|
||||
++It)
|
||||
{
|
||||
int detect_score = (*It)->Detect(comp_image, zones, n_numZones, zoneSet);
|
||||
if (detect_score)
|
||||
{
|
||||
int detect_score = (*It)->Detect(comp_image, zones, n_numZones, zoneSet);
|
||||
if (detect_score)
|
||||
{
|
||||
score += detect_score;
|
||||
noteSetMap[(*It)->getDetectionCause()] = zoneSet;
|
||||
if (det_cause.length())
|
||||
det_cause += ", ";
|
||||
det_cause += (*It)->getDetectionCause();
|
||||
}
|
||||
score += detect_score;
|
||||
noteSetMap[(*It)->getDetectionCause()] = zoneSet;
|
||||
if (det_cause.length())
|
||||
det_cause += ", ";
|
||||
det_cause += (*It)->getDetectionCause();
|
||||
}
|
||||
return score;
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,17 +23,17 @@ using namespace std;
|
|||
class ImageAnalyser {
|
||||
public:
|
||||
|
||||
//!Default constructor.
|
||||
ImageAnalyser() {};
|
||||
//!Default constructor.
|
||||
ImageAnalyser() {};
|
||||
|
||||
//! Destructor.
|
||||
~ImageAnalyser();
|
||||
//! Destructor.
|
||||
~ImageAnalyser();
|
||||
|
||||
//! Copy constructor.
|
||||
ImageAnalyser(const ImageAnalyser& source);
|
||||
//! Copy constructor.
|
||||
ImageAnalyser(const ImageAnalyser& source);
|
||||
|
||||
//! Overloaded operator=.
|
||||
ImageAnalyser& operator=(const ImageAnalyser& source);
|
||||
//! Overloaded operator=.
|
||||
ImageAnalyser& operator=(const ImageAnalyser& source);
|
||||
|
||||
private:
|
||||
|
||||
|
|
452
src/zm_jpeg.cpp
452
src/zm_jpeg.cpp
|
@ -32,65 +32,65 @@ static int jpeg_err_count = 0;
|
|||
|
||||
void zm_jpeg_error_exit( j_common_ptr cinfo )
|
||||
{
|
||||
static char buffer[JMSG_LENGTH_MAX];
|
||||
zm_error_ptr zmerr = (zm_error_ptr)cinfo->err;
|
||||
static char buffer[JMSG_LENGTH_MAX];
|
||||
zm_error_ptr zmerr = (zm_error_ptr)cinfo->err;
|
||||
|
||||
(zmerr->pub.format_message)( cinfo, buffer );
|
||||
(zmerr->pub.format_message)( cinfo, buffer );
|
||||
|
||||
Error( "%s", buffer );
|
||||
if ( ++jpeg_err_count == MAX_JPEG_ERRS )
|
||||
{
|
||||
Fatal( "Maximum number (%d) of JPEG errors reached, exiting", jpeg_err_count );
|
||||
}
|
||||
Error( "%s", buffer );
|
||||
if ( ++jpeg_err_count == MAX_JPEG_ERRS )
|
||||
{
|
||||
Fatal( "Maximum number (%d) of JPEG errors reached, exiting", jpeg_err_count );
|
||||
}
|
||||
|
||||
longjmp( zmerr->setjmp_buffer, 1 );
|
||||
longjmp( zmerr->setjmp_buffer, 1 );
|
||||
}
|
||||
|
||||
void zm_jpeg_emit_message( j_common_ptr cinfo, int msg_level )
|
||||
{
|
||||
static char buffer[JMSG_LENGTH_MAX];
|
||||
zm_error_ptr zmerr = (zm_error_ptr)cinfo->err;
|
||||
static char buffer[JMSG_LENGTH_MAX];
|
||||
zm_error_ptr zmerr = (zm_error_ptr)cinfo->err;
|
||||
|
||||
if ( msg_level < 0 )
|
||||
{
|
||||
/* It's a warning message. Since corrupt files may generate many warnings,
|
||||
* the policy implemented here is to show only the first warning,
|
||||
* unless trace_level >= 3.
|
||||
*/
|
||||
if ( zmerr->pub.num_warnings == 0 || zmerr->pub.trace_level >= 3 )
|
||||
{
|
||||
(zmerr->pub.format_message)( cinfo, buffer );
|
||||
if (!strstr(buffer, "Corrupt JPEG data:"))
|
||||
Warning( "%s", buffer );
|
||||
}
|
||||
/* Always count warnings in num_warnings. */
|
||||
zmerr->pub.num_warnings++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* It's a trace message. Show it if trace_level >= msg_level. */
|
||||
if ( zmerr->pub.trace_level >= msg_level )
|
||||
{
|
||||
(zmerr->pub.format_message)( cinfo, buffer );
|
||||
Debug( msg_level, "%s", buffer );
|
||||
}
|
||||
}
|
||||
if ( msg_level < 0 )
|
||||
{
|
||||
/* It's a warning message. Since corrupt files may generate many warnings,
|
||||
* the policy implemented here is to show only the first warning,
|
||||
* unless trace_level >= 3.
|
||||
*/
|
||||
if ( zmerr->pub.num_warnings == 0 || zmerr->pub.trace_level >= 3 )
|
||||
{
|
||||
(zmerr->pub.format_message)( cinfo, buffer );
|
||||
if (!strstr(buffer, "Corrupt JPEG data:"))
|
||||
Warning( "%s", buffer );
|
||||
}
|
||||
/* Always count warnings in num_warnings. */
|
||||
zmerr->pub.num_warnings++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* It's a trace message. Show it if trace_level >= msg_level. */
|
||||
if ( zmerr->pub.trace_level >= msg_level )
|
||||
{
|
||||
(zmerr->pub.format_message)( cinfo, buffer );
|
||||
Debug( msg_level, "%s", buffer );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Expanded data destination object for memory */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct jpeg_destination_mgr pub; /* public fields */
|
||||
struct jpeg_destination_mgr pub; /* public fields */
|
||||
|
||||
JOCTET *outbuffer; /* target buffer */
|
||||
int *outbuffer_size;
|
||||
JOCTET *buffer; /* start of buffer */
|
||||
JOCTET *outbuffer; /* target buffer */
|
||||
int *outbuffer_size;
|
||||
JOCTET *buffer; /* start of buffer */
|
||||
} mem_destination_mgr;
|
||||
|
||||
typedef mem_destination_mgr * mem_dest_ptr;
|
||||
|
||||
#define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */
|
||||
#define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */
|
||||
|
||||
/*
|
||||
* Initialize destination --- called by jpeg_start_compress
|
||||
|
@ -99,15 +99,15 @@ typedef mem_destination_mgr * mem_dest_ptr;
|
|||
|
||||
static void init_destination (j_compress_ptr cinfo)
|
||||
{
|
||||
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
||||
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
||||
|
||||
/* Allocate the output buffer --- it will be released when done with image */
|
||||
dest->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, OUTPUT_BUF_SIZE * SIZEOF(JOCTET));
|
||||
/* Allocate the output buffer --- it will be released when done with image */
|
||||
dest->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, OUTPUT_BUF_SIZE * SIZEOF(JOCTET));
|
||||
|
||||
dest->pub.next_output_byte = dest->buffer;
|
||||
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
|
||||
dest->pub.next_output_byte = dest->buffer;
|
||||
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
|
||||
|
||||
*(dest->outbuffer_size) = 0;
|
||||
*(dest->outbuffer_size) = 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -136,15 +136,15 @@ static void init_destination (j_compress_ptr cinfo)
|
|||
|
||||
static boolean empty_output_buffer (j_compress_ptr cinfo)
|
||||
{
|
||||
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
||||
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
||||
|
||||
memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, OUTPUT_BUF_SIZE );
|
||||
*(dest->outbuffer_size) += OUTPUT_BUF_SIZE;
|
||||
memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, OUTPUT_BUF_SIZE );
|
||||
*(dest->outbuffer_size) += OUTPUT_BUF_SIZE;
|
||||
|
||||
dest->pub.next_output_byte = dest->buffer;
|
||||
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
|
||||
dest->pub.next_output_byte = dest->buffer;
|
||||
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
|
||||
|
||||
return( TRUE );
|
||||
return( TRUE );
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -158,14 +158,14 @@ static boolean empty_output_buffer (j_compress_ptr cinfo)
|
|||
|
||||
static void term_destination (j_compress_ptr cinfo)
|
||||
{
|
||||
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
||||
size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
|
||||
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
||||
size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
|
||||
|
||||
if ( datacount > 0 )
|
||||
{
|
||||
memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, datacount );
|
||||
*(dest->outbuffer_size) += datacount;
|
||||
}
|
||||
if ( datacount > 0 )
|
||||
{
|
||||
memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, datacount );
|
||||
*(dest->outbuffer_size) += datacount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -177,45 +177,45 @@ static void term_destination (j_compress_ptr cinfo)
|
|||
|
||||
void zm_jpeg_mem_dest (j_compress_ptr cinfo, JOCTET *outbuffer, int *outbuffer_size )
|
||||
{
|
||||
mem_dest_ptr dest;
|
||||
mem_dest_ptr dest;
|
||||
|
||||
/* The destination object is made permanent so that multiple JPEG images
|
||||
* can be written to the same file without re-executing jpeg_stdio_dest.
|
||||
* This makes it dangerous to use this manager and a different destination
|
||||
* manager serially with the same JPEG object, because their private object
|
||||
* sizes may be different. Caveat programmer.
|
||||
*/
|
||||
if ( cinfo->dest == NULL )
|
||||
{
|
||||
/* first time for this JPEG object? */
|
||||
cinfo->dest = (struct jpeg_destination_mgr *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, SIZEOF(mem_destination_mgr));
|
||||
}
|
||||
/* The destination object is made permanent so that multiple JPEG images
|
||||
* can be written to the same file without re-executing jpeg_stdio_dest.
|
||||
* This makes it dangerous to use this manager and a different destination
|
||||
* manager serially with the same JPEG object, because their private object
|
||||
* sizes may be different. Caveat programmer.
|
||||
*/
|
||||
if ( cinfo->dest == NULL )
|
||||
{
|
||||
/* first time for this JPEG object? */
|
||||
cinfo->dest = (struct jpeg_destination_mgr *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, SIZEOF(mem_destination_mgr));
|
||||
}
|
||||
|
||||
dest = (mem_dest_ptr) cinfo->dest;
|
||||
dest->pub.init_destination = init_destination;
|
||||
dest->pub.empty_output_buffer = empty_output_buffer;
|
||||
dest->pub.term_destination = term_destination;
|
||||
dest->outbuffer = outbuffer;
|
||||
dest->outbuffer_size = outbuffer_size;
|
||||
dest = (mem_dest_ptr) cinfo->dest;
|
||||
dest->pub.init_destination = init_destination;
|
||||
dest->pub.empty_output_buffer = empty_output_buffer;
|
||||
dest->pub.term_destination = term_destination;
|
||||
dest->outbuffer = outbuffer;
|
||||
dest->outbuffer_size = outbuffer_size;
|
||||
}
|
||||
|
||||
/* Expanded data source object for memory input */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct jpeg_source_mgr pub; /* public fields */
|
||||
struct jpeg_source_mgr pub; /* public fields */
|
||||
|
||||
JOCTET * inbuffer; /* source stream */
|
||||
int inbuffer_size;
|
||||
int inbuffer_size_hwm; /* High water mark */
|
||||
JOCTET * inbuffer; /* source stream */
|
||||
int inbuffer_size;
|
||||
int inbuffer_size_hwm; /* High water mark */
|
||||
|
||||
JOCTET * buffer; /* start of buffer */
|
||||
boolean start_of_data; /* have we gotten any data yet? */
|
||||
JOCTET * buffer; /* start of buffer */
|
||||
boolean start_of_data; /* have we gotten any data yet? */
|
||||
} mem_source_mgr;
|
||||
|
||||
typedef mem_source_mgr * mem_src_ptr;
|
||||
|
||||
#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */
|
||||
#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */
|
||||
|
||||
/*
|
||||
* Initialize source --- called by jpeg_read_header
|
||||
|
@ -224,14 +224,14 @@ typedef mem_source_mgr * mem_src_ptr;
|
|||
|
||||
static void init_source (j_decompress_ptr cinfo)
|
||||
{
|
||||
mem_src_ptr src = (mem_src_ptr) cinfo->src;
|
||||
mem_src_ptr src = (mem_src_ptr) cinfo->src;
|
||||
|
||||
/* We reset the empty-input-file flag for each image,
|
||||
* but we don't clear the input buffer.
|
||||
* This is correct behavior for reading a series of images from one source.
|
||||
*/
|
||||
src->start_of_data = TRUE;
|
||||
src->pub.bytes_in_buffer = 0;
|
||||
/* We reset the empty-input-file flag for each image,
|
||||
* but we don't clear the input buffer.
|
||||
* This is correct behavior for reading a series of images from one source.
|
||||
*/
|
||||
src->start_of_data = TRUE;
|
||||
src->pub.bytes_in_buffer = 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -270,26 +270,26 @@ static void init_source (j_decompress_ptr cinfo)
|
|||
|
||||
static boolean fill_input_buffer (j_decompress_ptr cinfo)
|
||||
{
|
||||
mem_src_ptr src = (mem_src_ptr) cinfo->src;
|
||||
size_t nbytes;
|
||||
mem_src_ptr src = (mem_src_ptr) cinfo->src;
|
||||
size_t nbytes;
|
||||
|
||||
memcpy( src->buffer, src->inbuffer, (size_t) src->inbuffer_size );
|
||||
nbytes = src->inbuffer_size;
|
||||
memcpy( src->buffer, src->inbuffer, (size_t) src->inbuffer_size );
|
||||
nbytes = src->inbuffer_size;
|
||||
|
||||
if ( nbytes <= 0 )
|
||||
{
|
||||
if ( src->start_of_data ) /* Treat empty input file as fatal error */
|
||||
ERREXIT(cinfo, JERR_INPUT_EMPTY);
|
||||
WARNMS(cinfo, JWRN_JPEG_EOF);
|
||||
/* Insert a fake EOI marker */
|
||||
src->buffer[0] = (JOCTET) 0xFF;
|
||||
src->buffer[1] = (JOCTET) JPEG_EOI;
|
||||
nbytes = 2;
|
||||
}
|
||||
if ( nbytes <= 0 )
|
||||
{
|
||||
if ( src->start_of_data ) /* Treat empty input file as fatal error */
|
||||
ERREXIT(cinfo, JERR_INPUT_EMPTY);
|
||||
WARNMS(cinfo, JWRN_JPEG_EOF);
|
||||
/* Insert a fake EOI marker */
|
||||
src->buffer[0] = (JOCTET) 0xFF;
|
||||
src->buffer[1] = (JOCTET) JPEG_EOI;
|
||||
nbytes = 2;
|
||||
}
|
||||
|
||||
src->pub.next_input_byte = src->buffer;
|
||||
src->pub.bytes_in_buffer = nbytes;
|
||||
src->start_of_data = FALSE;
|
||||
src->pub.next_input_byte = src->buffer;
|
||||
src->pub.bytes_in_buffer = nbytes;
|
||||
src->start_of_data = FALSE;
|
||||
|
||||
return( TRUE );
|
||||
}
|
||||
|
@ -309,25 +309,25 @@ static boolean fill_input_buffer (j_decompress_ptr cinfo)
|
|||
|
||||
static void skip_input_data (j_decompress_ptr cinfo, long num_bytes)
|
||||
{
|
||||
mem_src_ptr src = (mem_src_ptr) cinfo->src;
|
||||
mem_src_ptr src = (mem_src_ptr) cinfo->src;
|
||||
|
||||
/* Just a dumb implementation for now. Could use fseek() except
|
||||
* it doesn't work on pipes. Not clear that being smart is worth
|
||||
* any trouble anyway --- large skips are infrequent.
|
||||
*/
|
||||
if ( num_bytes > 0 )
|
||||
/* Just a dumb implementation for now. Could use fseek() except
|
||||
* it doesn't work on pipes. Not clear that being smart is worth
|
||||
* any trouble anyway --- large skips are infrequent.
|
||||
*/
|
||||
if ( num_bytes > 0 )
|
||||
{
|
||||
while ( num_bytes > (long) src->pub.bytes_in_buffer )
|
||||
{
|
||||
while ( num_bytes > (long) src->pub.bytes_in_buffer )
|
||||
{
|
||||
num_bytes -= (long) src->pub.bytes_in_buffer;
|
||||
(void) fill_input_buffer(cinfo);
|
||||
/* note we assume that fill_input_buffer will never return FALSE,
|
||||
* so suspension need not be handled.
|
||||
*/
|
||||
}
|
||||
src->pub.next_input_byte += (size_t) num_bytes;
|
||||
src->pub.bytes_in_buffer -= (size_t) num_bytes;
|
||||
num_bytes -= (long) src->pub.bytes_in_buffer;
|
||||
(void) fill_input_buffer(cinfo);
|
||||
/* note we assume that fill_input_buffer will never return FALSE,
|
||||
* so suspension need not be handled.
|
||||
*/
|
||||
}
|
||||
src->pub.next_input_byte += (size_t) num_bytes;
|
||||
src->pub.bytes_in_buffer -= (size_t) num_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -342,7 +342,7 @@ static void skip_input_data (j_decompress_ptr cinfo, long num_bytes)
|
|||
|
||||
static void term_source (j_decompress_ptr cinfo)
|
||||
{
|
||||
/* no work necessary here */
|
||||
/* no work necessary here */
|
||||
}
|
||||
|
||||
|
||||
|
@ -354,113 +354,113 @@ static void term_source (j_decompress_ptr cinfo)
|
|||
|
||||
void zm_jpeg_mem_src( j_decompress_ptr cinfo, const JOCTET *inbuffer, int inbuffer_size )
|
||||
{
|
||||
mem_src_ptr src;
|
||||
|
||||
/* The source object and input buffer are made permanent so that a series
|
||||
* of JPEG images can be read from the same file by calling zm_jpeg_mem_src
|
||||
* only before the first one. (If we discarded the buffer at the end of
|
||||
* one image, we'd likely lose the start of the next one.)
|
||||
* This makes it unsafe to use this manager and a different source
|
||||
* manager serially with the same JPEG object. Caveat programmer.
|
||||
*/
|
||||
if ( cinfo->src == NULL )
|
||||
{
|
||||
/* first time for this JPEG object? */
|
||||
cinfo->src = (struct jpeg_source_mgr *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, SIZEOF(mem_source_mgr));
|
||||
src = (mem_src_ptr) cinfo->src;
|
||||
src->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, inbuffer_size * SIZEOF(JOCTET));
|
||||
src->inbuffer_size_hwm = inbuffer_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
src = (mem_src_ptr) cinfo->src;
|
||||
if ( src->inbuffer_size_hwm < inbuffer_size )
|
||||
{
|
||||
src->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, inbuffer_size * SIZEOF(JOCTET));
|
||||
src->inbuffer_size_hwm = inbuffer_size;
|
||||
}
|
||||
}
|
||||
mem_src_ptr src;
|
||||
|
||||
/* The source object and input buffer are made permanent so that a series
|
||||
* of JPEG images can be read from the same file by calling zm_jpeg_mem_src
|
||||
* only before the first one. (If we discarded the buffer at the end of
|
||||
* one image, we'd likely lose the start of the next one.)
|
||||
* This makes it unsafe to use this manager and a different source
|
||||
* manager serially with the same JPEG object. Caveat programmer.
|
||||
*/
|
||||
if ( cinfo->src == NULL )
|
||||
{
|
||||
/* first time for this JPEG object? */
|
||||
cinfo->src = (struct jpeg_source_mgr *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, SIZEOF(mem_source_mgr));
|
||||
src = (mem_src_ptr) cinfo->src;
|
||||
src->pub.init_source = init_source;
|
||||
src->pub.fill_input_buffer = fill_input_buffer;
|
||||
src->pub.skip_input_data = skip_input_data;
|
||||
src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
|
||||
src->pub.term_source = term_source;
|
||||
src->inbuffer = (JOCTET *)inbuffer;
|
||||
src->inbuffer_size = inbuffer_size;
|
||||
src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
|
||||
src->pub.next_input_byte = NULL; /* until buffer loaded */
|
||||
src->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, inbuffer_size * SIZEOF(JOCTET));
|
||||
src->inbuffer_size_hwm = inbuffer_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
src = (mem_src_ptr) cinfo->src;
|
||||
if ( src->inbuffer_size_hwm < inbuffer_size )
|
||||
{
|
||||
src->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, inbuffer_size * SIZEOF(JOCTET));
|
||||
src->inbuffer_size_hwm = inbuffer_size;
|
||||
}
|
||||
}
|
||||
|
||||
src = (mem_src_ptr) cinfo->src;
|
||||
src->pub.init_source = init_source;
|
||||
src->pub.fill_input_buffer = fill_input_buffer;
|
||||
src->pub.skip_input_data = skip_input_data;
|
||||
src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
|
||||
src->pub.term_source = term_source;
|
||||
src->inbuffer = (JOCTET *)inbuffer;
|
||||
src->inbuffer_size = inbuffer_size;
|
||||
src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
|
||||
src->pub.next_input_byte = NULL; /* until buffer loaded */
|
||||
}
|
||||
|
||||
void zm_use_std_huff_tables( j_decompress_ptr cinfo ) {
|
||||
/* JPEG standard Huffman tables (cf. JPEG standard section K.3) */
|
||||
/* IMPORTANT: these are only valid for 8-bit data precision! */
|
||||
static const JHUFF_TBL dclumin = {
|
||||
{ /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
|
||||
FALSE
|
||||
};
|
||||
static const JHUFF_TBL dcchrome = {
|
||||
{ /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
|
||||
FALSE
|
||||
};
|
||||
static const JHUFF_TBL aclumin = {
|
||||
{ /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d },
|
||||
{ 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
|
||||
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
|
||||
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
|
||||
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
|
||||
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
|
||||
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
|
||||
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
|
||||
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
|
||||
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
|
||||
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
|
||||
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
|
||||
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
|
||||
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
|
||||
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
|
||||
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
|
||||
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
|
||||
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
|
||||
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
|
||||
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
|
||||
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
||||
0xf9, 0xfa },
|
||||
FALSE
|
||||
};
|
||||
static const JHUFF_TBL acchrome = {
|
||||
{ /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 },
|
||||
{ 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
|
||||
0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
|
||||
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
|
||||
0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
|
||||
0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
|
||||
0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
|
||||
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
|
||||
0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
|
||||
0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
||||
0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
|
||||
0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
||||
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
|
||||
0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
|
||||
0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
|
||||
0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
|
||||
0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
|
||||
0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
|
||||
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
|
||||
0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
||||
0xf9, 0xfa },
|
||||
FALSE
|
||||
};
|
||||
/* JPEG standard Huffman tables (cf. JPEG standard section K.3) */
|
||||
/* IMPORTANT: these are only valid for 8-bit data precision! */
|
||||
static const JHUFF_TBL dclumin = {
|
||||
{ /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
|
||||
FALSE
|
||||
};
|
||||
static const JHUFF_TBL dcchrome = {
|
||||
{ /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
|
||||
FALSE
|
||||
};
|
||||
static const JHUFF_TBL aclumin = {
|
||||
{ /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d },
|
||||
{ 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
|
||||
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
|
||||
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
|
||||
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
|
||||
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
|
||||
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
|
||||
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
|
||||
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
|
||||
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
|
||||
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
|
||||
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
|
||||
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
|
||||
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
|
||||
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
|
||||
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
|
||||
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
|
||||
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
|
||||
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
|
||||
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
|
||||
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
||||
0xf9, 0xfa },
|
||||
FALSE
|
||||
};
|
||||
static const JHUFF_TBL acchrome = {
|
||||
{ /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 },
|
||||
{ 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
|
||||
0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
|
||||
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
|
||||
0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
|
||||
0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
|
||||
0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
|
||||
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
|
||||
0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
|
||||
0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
||||
0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
|
||||
0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
||||
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
|
||||
0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
|
||||
0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
|
||||
0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
|
||||
0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
|
||||
0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
|
||||
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
|
||||
0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
||||
0xf9, 0xfa },
|
||||
FALSE
|
||||
};
|
||||
|
||||
cinfo->dc_huff_tbl_ptrs[0] = (JHUFF_TBL*)&dclumin;
|
||||
cinfo->dc_huff_tbl_ptrs[1] = (JHUFF_TBL*)&dcchrome;
|
||||
cinfo->ac_huff_tbl_ptrs[0] = (JHUFF_TBL*)&aclumin;
|
||||
cinfo->ac_huff_tbl_ptrs[1] = (JHUFF_TBL*)&acchrome;
|
||||
cinfo->dc_huff_tbl_ptrs[0] = (JHUFF_TBL*)&dclumin;
|
||||
cinfo->dc_huff_tbl_ptrs[1] = (JHUFF_TBL*)&dcchrome;
|
||||
cinfo->ac_huff_tbl_ptrs[0] = (JHUFF_TBL*)&aclumin;
|
||||
cinfo->ac_huff_tbl_ptrs[1] = (JHUFF_TBL*)&acchrome;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -32,8 +32,8 @@ extern "C"
|
|||
/* Stuff for overriden error handlers */
|
||||
struct zm_error_mgr
|
||||
{
|
||||
struct jpeg_error_mgr pub;
|
||||
jmp_buf setjmp_buffer;
|
||||
struct jpeg_error_mgr pub;
|
||||
jmp_buf setjmp_buffer;
|
||||
};
|
||||
|
||||
typedef struct zm_error_mgr *zm_error_ptr;
|
||||
|
|
|
@ -644,7 +644,11 @@ LocalCamera::LocalCamera(
|
|||
if ( !tmpPicture )
|
||||
Fatal( "Could not allocate temporary picture" );
|
||||
|
||||
int pSize = avpicture_get_size( imagePixFormat, width, height );
|
||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||
int pSize = av_image_get_buffer_size( imagePixFormat, width, height,1 );
|
||||
#else
|
||||
int pSize = avpicture_get_size( imagePixFormat, width, height );
|
||||
#endif
|
||||
if( (unsigned int)pSize != imagesize) {
|
||||
Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize);
|
||||
}
|
||||
|
@ -877,7 +881,18 @@ void LocalCamera::Initialise()
|
|||
#endif
|
||||
if ( !capturePictures[i] )
|
||||
Fatal( "Could not allocate picture" );
|
||||
avpicture_fill( (AVPicture *)capturePictures[i], (uint8_t*)v4l2_data.buffers[i].start, capturePixFormat, v4l2_data.fmt.fmt.pix.width, v4l2_data.fmt.fmt.pix.height );
|
||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||
av_image_fill_arrays(capturePictures[i]->data,
|
||||
capturePictures[i]->linesize,
|
||||
(uint8_t*)v4l2_data.buffers[i].start,capturePixFormat,
|
||||
v4l2_data.fmt.fmt.pix.width,
|
||||
v4l2_data.fmt.fmt.pix.height, 1);
|
||||
#else
|
||||
avpicture_fill( (AVPicture *)capturePictures[i],
|
||||
(uint8_t*)v4l2_data.buffers[i].start, capturePixFormat,
|
||||
v4l2_data.fmt.fmt.pix.width,
|
||||
v4l2_data.fmt.fmt.pix.height );
|
||||
#endif
|
||||
#endif // HAVE_LIBSWSCALE
|
||||
}
|
||||
|
||||
|
@ -1035,7 +1050,16 @@ void LocalCamera::Initialise()
|
|||
#endif
|
||||
if ( !capturePictures[i] )
|
||||
Fatal( "Could not allocate picture" );
|
||||
avpicture_fill( (AVPicture *)capturePictures[i], (unsigned char *)v4l1_data.bufptr+v4l1_data.frames.offsets[i], capturePixFormat, width, height );
|
||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||
av_image_fill_arrays(capturePictures[i]->data,
|
||||
capturePictures[i]->linesize,
|
||||
(unsigned char *)v4l1_data.bufptr+v4l1_data.frames.offsets[i],
|
||||
capturePixFormat, width, height, 1);
|
||||
#else
|
||||
avpicture_fill( (AVPicture *)capturePictures[i],
|
||||
(unsigned char *)v4l1_data.bufptr+v4l1_data.frames.offsets[i],
|
||||
capturePixFormat, width, height );
|
||||
#endif
|
||||
}
|
||||
#endif // HAVE_LIBSWSCALE
|
||||
|
||||
|
@ -2131,7 +2155,14 @@ int LocalCamera::Capture( Image &image )
|
|||
|
||||
Debug( 9, "Calling sws_scale to perform the conversion" );
|
||||
/* Use swscale to convert the image directly into the shared memory */
|
||||
avpicture_fill( (AVPicture *)tmpPicture, directbuffer, imagePixFormat, width, height );
|
||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||
av_image_fill_arrays(tmpPicture->data,
|
||||
tmpPicture->linesize, directbuffer,
|
||||
imagePixFormat, width, height, 1);
|
||||
#else
|
||||
avpicture_fill( (AVPicture *)tmpPicture, directbuffer,
|
||||
imagePixFormat, width, height );
|
||||
#endif
|
||||
sws_scale( imgConversionContext, capturePictures[capture_frame]->data, capturePictures[capture_frame]->linesize, 0, height, tmpPicture->data, tmpPicture->linesize );
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -615,7 +615,7 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co
|
|||
{
|
||||
int priority = smSyslogPriorities[level];
|
||||
//priority |= LOG_DAEMON;
|
||||
syslog( priority, "%s [%s]", classString, syslogStart );
|
||||
syslog( priority, "%s [%s] [%s]", classString, mId.c_str(), syslogStart );
|
||||
}
|
||||
|
||||
if ( level <= FATAL )
|
||||
|
|
298
src/zm_logger.h
298
src/zm_logger.h
|
@ -33,190 +33,190 @@
|
|||
class Logger
|
||||
{
|
||||
public:
|
||||
enum {
|
||||
NOOPT=-6,
|
||||
NOLOG,
|
||||
PANIC,
|
||||
FATAL,
|
||||
ERROR,
|
||||
WARNING,
|
||||
INFO,
|
||||
DEBUG1,
|
||||
DEBUG2,
|
||||
DEBUG3,
|
||||
DEBUG4,
|
||||
DEBUG5,
|
||||
DEBUG6,
|
||||
DEBUG7,
|
||||
DEBUG8,
|
||||
DEBUG9
|
||||
};
|
||||
enum {
|
||||
NOOPT=-6,
|
||||
NOLOG,
|
||||
PANIC,
|
||||
FATAL,
|
||||
ERROR,
|
||||
WARNING,
|
||||
INFO,
|
||||
DEBUG1,
|
||||
DEBUG2,
|
||||
DEBUG3,
|
||||
DEBUG4,
|
||||
DEBUG5,
|
||||
DEBUG6,
|
||||
DEBUG7,
|
||||
DEBUG8,
|
||||
DEBUG9
|
||||
};
|
||||
|
||||
typedef int Level;
|
||||
typedef int Level;
|
||||
|
||||
typedef std::map<Level,std::string> StringMap;
|
||||
typedef std::map<Level,int> IntMap;
|
||||
typedef std::map<Level,std::string> StringMap;
|
||||
typedef std::map<Level,int> IntMap;
|
||||
|
||||
class Options
|
||||
{
|
||||
public:
|
||||
int mTermLevel;
|
||||
int mDatabaseLevel;
|
||||
int mFileLevel;
|
||||
int mSyslogLevel;
|
||||
class Options
|
||||
{
|
||||
public:
|
||||
int mTermLevel;
|
||||
int mDatabaseLevel;
|
||||
int mFileLevel;
|
||||
int mSyslogLevel;
|
||||
|
||||
std::string mLogPath;
|
||||
std::string mLogFile;
|
||||
|
||||
public:
|
||||
Options( Level termLevel=NOOPT, Level databaseLevel=NOOPT, Level fileLevel=NOOPT, Level syslogLevel=NOOPT, const std::string &logPath=".", const std::string &logFile="" ) :
|
||||
mTermLevel( termLevel ),
|
||||
mDatabaseLevel( databaseLevel ),
|
||||
mFileLevel( fileLevel ),
|
||||
mSyslogLevel( syslogLevel ),
|
||||
mLogPath( logPath ),
|
||||
mLogFile( logFile )
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
static bool smInitialised;
|
||||
static Logger *smInstance;
|
||||
|
||||
static StringMap smCodes;
|
||||
static IntMap smSyslogPriorities;
|
||||
|
||||
private:
|
||||
bool mInitialised;
|
||||
|
||||
std::string mId;
|
||||
std::string mIdRoot;
|
||||
std::string mIdArgs;
|
||||
|
||||
Level mLevel; // Level that is currently in operation
|
||||
Level mTermLevel; // Maximum level output via terminal
|
||||
Level mDatabaseLevel; // Maximum level output via database
|
||||
Level mFileLevel; // Maximum level output via file
|
||||
Level mSyslogLevel; // Maximum level output via syslog
|
||||
Level mEffectiveLevel; // Level optimised to take account of maxima
|
||||
|
||||
bool mDbConnected;
|
||||
MYSQL mDbConnection;
|
||||
std::string mLogPath;
|
||||
std::string mLogFile;
|
||||
FILE *mLogFileFP;
|
||||
|
||||
bool mHasTerm;
|
||||
bool mFlush;
|
||||
public:
|
||||
Options( Level termLevel=NOOPT, Level databaseLevel=NOOPT, Level fileLevel=NOOPT, Level syslogLevel=NOOPT, const std::string &logPath=".", const std::string &logFile="" ) :
|
||||
mTermLevel( termLevel ),
|
||||
mDatabaseLevel( databaseLevel ),
|
||||
mFileLevel( fileLevel ),
|
||||
mSyslogLevel( syslogLevel ),
|
||||
mLogPath( logPath ),
|
||||
mLogFile( logFile )
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
static void usrHandler( int sig );
|
||||
static bool smInitialised;
|
||||
static Logger *smInstance;
|
||||
|
||||
public:
|
||||
friend void logInit( const char *name, const Options &options );
|
||||
friend void logTerm();
|
||||
|
||||
static Logger *fetch()
|
||||
{
|
||||
if ( !smInstance )
|
||||
{
|
||||
smInstance = new Logger();
|
||||
Options options;
|
||||
smInstance->initialise( "undef", options );
|
||||
}
|
||||
return( smInstance );
|
||||
}
|
||||
static StringMap smCodes;
|
||||
static IntMap smSyslogPriorities;
|
||||
|
||||
private:
|
||||
Logger();
|
||||
~Logger();
|
||||
bool mInitialised;
|
||||
|
||||
public:
|
||||
void initialise( const std::string &id, const Options &options );
|
||||
void terminate();
|
||||
std::string mId;
|
||||
std::string mIdRoot;
|
||||
std::string mIdArgs;
|
||||
|
||||
Level mLevel; // Level that is currently in operation
|
||||
Level mTermLevel; // Maximum level output via terminal
|
||||
Level mDatabaseLevel; // Maximum level output via database
|
||||
Level mFileLevel; // Maximum level output via file
|
||||
Level mSyslogLevel; // Maximum level output via syslog
|
||||
Level mEffectiveLevel; // Level optimised to take account of maxima
|
||||
|
||||
bool mDbConnected;
|
||||
MYSQL mDbConnection;
|
||||
std::string mLogPath;
|
||||
std::string mLogFile;
|
||||
FILE *mLogFileFP;
|
||||
|
||||
bool mHasTerm;
|
||||
bool mFlush;
|
||||
|
||||
private:
|
||||
int limit( int level )
|
||||
{
|
||||
if ( level > DEBUG9 )
|
||||
return( DEBUG9 );
|
||||
if ( level < NOLOG )
|
||||
return( NOLOG );
|
||||
return( level );
|
||||
}
|
||||
|
||||
bool boolEnv( const std::string &name, bool defaultValue=false );
|
||||
int intEnv( const std::string &name, bool defaultValue=0 );
|
||||
std::string strEnv( const std::string &name, const std::string defaultValue="" );
|
||||
char *getTargettedEnv( const std::string &name );
|
||||
|
||||
void loadEnv();
|
||||
static void usrHandler( int sig );
|
||||
|
||||
public:
|
||||
const std::string &id() const
|
||||
friend void logInit( const char *name, const Options &options );
|
||||
friend void logTerm();
|
||||
|
||||
static Logger *fetch()
|
||||
{
|
||||
if ( !smInstance )
|
||||
{
|
||||
return( mId );
|
||||
smInstance = new Logger();
|
||||
Options options;
|
||||
smInstance->initialise( "undef", options );
|
||||
}
|
||||
|
||||
const std::string &id( const std::string &id );
|
||||
|
||||
Level level() const
|
||||
{
|
||||
return( mLevel );
|
||||
}
|
||||
Level level( Level=NOOPT );
|
||||
|
||||
bool debugOn()
|
||||
{
|
||||
return( mEffectiveLevel >= DEBUG1 );
|
||||
}
|
||||
|
||||
Level termLevel( Level=NOOPT );
|
||||
Level databaseLevel( Level=NOOPT );
|
||||
Level fileLevel( Level=NOOPT );
|
||||
Level syslogLevel( Level=NOOPT );
|
||||
return( smInstance );
|
||||
}
|
||||
|
||||
private:
|
||||
void logFile( const std::string &logFile );
|
||||
void openFile();
|
||||
void closeFile();
|
||||
void openSyslog();
|
||||
void closeSyslog();
|
||||
void closeDatabase();
|
||||
Logger();
|
||||
~Logger();
|
||||
|
||||
public:
|
||||
void logPrint( bool hex, const char * const filepath, const int line, const int level, const char *fstring, ... );
|
||||
void initialise( const std::string &id, const Options &options );
|
||||
void terminate();
|
||||
|
||||
private:
|
||||
int limit( int level )
|
||||
{
|
||||
if ( level > DEBUG9 )
|
||||
return( DEBUG9 );
|
||||
if ( level < NOLOG )
|
||||
return( NOLOG );
|
||||
return( level );
|
||||
}
|
||||
|
||||
bool boolEnv( const std::string &name, bool defaultValue=false );
|
||||
int intEnv( const std::string &name, bool defaultValue=0 );
|
||||
std::string strEnv( const std::string &name, const std::string defaultValue="" );
|
||||
char *getTargettedEnv( const std::string &name );
|
||||
|
||||
void loadEnv();
|
||||
|
||||
public:
|
||||
const std::string &id() const
|
||||
{
|
||||
return( mId );
|
||||
}
|
||||
|
||||
const std::string &id( const std::string &id );
|
||||
|
||||
Level level() const
|
||||
{
|
||||
return( mLevel );
|
||||
}
|
||||
Level level( Level=NOOPT );
|
||||
|
||||
bool debugOn()
|
||||
{
|
||||
return( mEffectiveLevel >= DEBUG1 );
|
||||
}
|
||||
|
||||
Level termLevel( Level=NOOPT );
|
||||
Level databaseLevel( Level=NOOPT );
|
||||
Level fileLevel( Level=NOOPT );
|
||||
Level syslogLevel( Level=NOOPT );
|
||||
|
||||
private:
|
||||
void logFile( const std::string &logFile );
|
||||
void openFile();
|
||||
void closeFile();
|
||||
void openSyslog();
|
||||
void closeSyslog();
|
||||
void closeDatabase();
|
||||
|
||||
public:
|
||||
void logPrint( bool hex, const char * const filepath, const int line, const int level, const char *fstring, ... );
|
||||
};
|
||||
|
||||
void logInit( const char *name, const Logger::Options &options=Logger::Options() );
|
||||
void logTerm();
|
||||
inline const std::string &logId()
|
||||
{
|
||||
return( Logger::fetch()->id() );
|
||||
return( Logger::fetch()->id() );
|
||||
}
|
||||
inline Logger::Level logLevel()
|
||||
{
|
||||
return( Logger::fetch()->level() );
|
||||
return( Logger::fetch()->level() );
|
||||
}
|
||||
inline void logCapLevel( Logger::Level level )
|
||||
{
|
||||
Logger::fetch()->level( level );
|
||||
Logger::fetch()->level( level );
|
||||
}
|
||||
inline Logger::Level logDebugging()
|
||||
{
|
||||
return( Logger::fetch()->debugOn() );
|
||||
return( Logger::fetch()->debugOn() );
|
||||
}
|
||||
|
||||
#define logPrintf(logLevel,params...) {\
|
||||
if ( logLevel <= Logger::fetch()->level() )\
|
||||
Logger::fetch()->logPrint( false, __FILE__, __LINE__, logLevel, ##params );\
|
||||
}
|
||||
if ( logLevel <= Logger::fetch()->level() )\
|
||||
Logger::fetch()->logPrint( false, __FILE__, __LINE__, logLevel, ##params );\
|
||||
}
|
||||
|
||||
#define logHexdump(logLevel,data,len) {\
|
||||
if ( logLevel <= Logger::fetch()->level() )\
|
||||
Logger::fetch()->logPrint( true, __FILE__, __LINE__, logLevel, "%p (%d)", data, len );\
|
||||
}
|
||||
if ( logLevel <= Logger::fetch()->level() )\
|
||||
Logger::fetch()->logPrint( true, __FILE__, __LINE__, logLevel, "%p (%d)", data, len );\
|
||||
}
|
||||
|
||||
/* Debug compiled out */
|
||||
#ifndef DBG_OFF
|
||||
|
@ -228,16 +228,16 @@ inline Logger::Level logDebugging()
|
|||
#endif
|
||||
|
||||
/* Standard debug calls */
|
||||
#define Info(params...) logPrintf(Logger::INFO,##params)
|
||||
#define Info(params...) logPrintf(Logger::INFO,##params)
|
||||
#define Warning(params...) logPrintf(Logger::WARNING,##params)
|
||||
#define Error(params...) logPrintf(Logger::ERROR,##params)
|
||||
#define Fatal(params...) logPrintf(Logger::FATAL,##params)
|
||||
#define Panic(params...) logPrintf(Logger::PANIC,##params)
|
||||
#define Mark() Info("Mark/%s/%d",__FILE__,__LINE__)
|
||||
#define Log() Info("Log")
|
||||
#define Error(params...) logPrintf(Logger::ERROR,##params)
|
||||
#define Fatal(params...) logPrintf(Logger::FATAL,##params)
|
||||
#define Panic(params...) logPrintf(Logger::PANIC,##params)
|
||||
#define Mark() Info("Mark/%s/%d",__FILE__,__LINE__)
|
||||
#define Log() Info("Log")
|
||||
#ifdef __GNUC__
|
||||
#define Enter(level) logPrintf(level,("Entering %s",__PRETTY_FUNCTION__))
|
||||
#define Exit(level) logPrintf(level,("Exiting %s",__PRETTY_FUNCTION__))
|
||||
#define Enter(level) logPrintf(level,("Entering %s",__PRETTY_FUNCTION__))
|
||||
#define Exit(level) logPrintf(level,("Exiting %s",__PRETTY_FUNCTION__))
|
||||
#else
|
||||
#define Enter(level)
|
||||
#define Exit(level)
|
||||
|
|
|
@ -24,138 +24,138 @@
|
|||
#include "zm.h"
|
||||
|
||||
inline void* zm_mallocaligned(unsigned int reqalignment, size_t reqsize) {
|
||||
uint8_t* retptr;
|
||||
uint8_t* retptr;
|
||||
#if HAVE_POSIX_MEMALIGN
|
||||
if(posix_memalign((void**)&retptr,reqalignment,reqsize) != 0)
|
||||
return NULL;
|
||||
if(posix_memalign((void**)&retptr,reqalignment,reqsize) != 0)
|
||||
return NULL;
|
||||
|
||||
return retptr;
|
||||
return retptr;
|
||||
#else
|
||||
uint8_t* alloc;
|
||||
retptr = (uint8_t*)malloc(reqsize+reqalignment+sizeof(void*));
|
||||
uint8_t* alloc;
|
||||
retptr = (uint8_t*)malloc(reqsize+reqalignment+sizeof(void*));
|
||||
|
||||
if(retptr == NULL)
|
||||
return NULL;
|
||||
if(retptr == NULL)
|
||||
return NULL;
|
||||
|
||||
alloc = retptr + sizeof(void*);
|
||||
alloc = retptr + sizeof(void*);
|
||||
|
||||
if(((long)alloc % reqalignment) != 0)
|
||||
alloc = alloc + (reqalignment - ((long)alloc % reqalignment));
|
||||
if(((long)alloc % reqalignment) != 0)
|
||||
alloc = alloc + (reqalignment - ((long)alloc % reqalignment));
|
||||
|
||||
/* Store a pointer before to the start of the block, just before returned aligned memory */
|
||||
*(void**)(alloc - sizeof(void*)) = retptr;
|
||||
/* Store a pointer before to the start of the block, just before returned aligned memory */
|
||||
*(void**)(alloc - sizeof(void*)) = retptr;
|
||||
|
||||
return alloc;
|
||||
return alloc;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void zm_freealigned(void* ptr) {
|
||||
#if HAVE_POSIX_MEMALIGN
|
||||
free(ptr);
|
||||
free(ptr);
|
||||
#else
|
||||
/* Start of block is stored before the block if it was allocated by zm_mallocaligned */
|
||||
free(*(void**)((uint8_t*)ptr - sizeof(void*)));
|
||||
/* Start of block is stored before the block if it was allocated by zm_mallocaligned */
|
||||
free(*(void**)((uint8_t*)ptr - sizeof(void*)));
|
||||
#endif
|
||||
}
|
||||
|
||||
inline char *mempbrk( register const char *s, const char *accept, size_t limit )
|
||||
{
|
||||
if ( limit <= 0 || !s || !accept || !*accept )
|
||||
return( 0 );
|
||||
|
||||
register unsigned int i,j;
|
||||
size_t acc_len = strlen( accept );
|
||||
|
||||
for ( i = 0; i < limit; s++, i++ )
|
||||
{
|
||||
for ( j = 0; j < acc_len; j++ )
|
||||
{
|
||||
if ( *s == accept[j] )
|
||||
{
|
||||
return( (char *)s );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( limit <= 0 || !s || !accept || !*accept )
|
||||
return( 0 );
|
||||
|
||||
register unsigned int i,j;
|
||||
size_t acc_len = strlen( accept );
|
||||
|
||||
for ( i = 0; i < limit; s++, i++ )
|
||||
{
|
||||
for ( j = 0; j < acc_len; j++ )
|
||||
{
|
||||
if ( *s == accept[j] )
|
||||
{
|
||||
return( (char *)s );
|
||||
}
|
||||
}
|
||||
}
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
inline char *memstr( register const char *s, const char *n, size_t limit )
|
||||
{
|
||||
if ( limit <= 0 || !s || !n )
|
||||
return( 0 );
|
||||
|
||||
if ( !*n )
|
||||
return( (char *)s );
|
||||
|
||||
register unsigned int i,j,k;
|
||||
size_t n_len = strlen( n );
|
||||
|
||||
for ( i = 0; i < limit; i++, s++ )
|
||||
{
|
||||
if ( *s != *n )
|
||||
continue;
|
||||
j = 1;
|
||||
k = 1;
|
||||
while ( true )
|
||||
{
|
||||
if ( k >= n_len )
|
||||
return( (char *)s );
|
||||
if ( s[j++] != n[k++] )
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( limit <= 0 || !s || !n )
|
||||
return( 0 );
|
||||
|
||||
if ( !*n )
|
||||
return( (char *)s );
|
||||
|
||||
register unsigned int i,j,k;
|
||||
size_t n_len = strlen( n );
|
||||
|
||||
for ( i = 0; i < limit; i++, s++ )
|
||||
{
|
||||
if ( *s != *n )
|
||||
continue;
|
||||
j = 1;
|
||||
k = 1;
|
||||
while ( true )
|
||||
{
|
||||
if ( k >= n_len )
|
||||
return( (char *)s );
|
||||
if ( s[j++] != n[k++] )
|
||||
break;
|
||||
}
|
||||
}
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
inline size_t memspn( register const char *s, const char *accept, size_t limit )
|
||||
{
|
||||
if ( limit <= 0 || !s || !accept || !*accept )
|
||||
return( 0 );
|
||||
if ( limit <= 0 || !s || !accept || !*accept )
|
||||
return( 0 );
|
||||
|
||||
register unsigned int i,j;
|
||||
size_t acc_len = strlen( accept );
|
||||
register unsigned int i,j;
|
||||
size_t acc_len = strlen( accept );
|
||||
|
||||
for ( i = 0; i < limit; s++, i++ )
|
||||
for ( i = 0; i < limit; s++, i++ )
|
||||
{
|
||||
register bool found = false;
|
||||
for ( j = 0; j < acc_len; j++ )
|
||||
{
|
||||
register bool found = false;
|
||||
for ( j = 0; j < acc_len; j++ )
|
||||
{
|
||||
if ( *s == accept[j] )
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( !found )
|
||||
{
|
||||
return( i );
|
||||
}
|
||||
if ( *s == accept[j] )
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return( limit );
|
||||
if ( !found )
|
||||
{
|
||||
return( i );
|
||||
}
|
||||
}
|
||||
return( limit );
|
||||
}
|
||||
|
||||
inline size_t memcspn( register const char *s, const char *reject, size_t limit )
|
||||
{
|
||||
if ( limit <= 0 || !s || !reject )
|
||||
return( 0 );
|
||||
if ( limit <= 0 || !s || !reject )
|
||||
return( 0 );
|
||||
|
||||
if ( !*reject )
|
||||
return( limit );
|
||||
|
||||
register unsigned int i,j;
|
||||
size_t rej_len = strlen( reject );
|
||||
|
||||
for ( i = 0; i < limit; s++, i++ )
|
||||
{
|
||||
for ( j = 0; j < rej_len; j++ )
|
||||
{
|
||||
if ( *s == reject[j] )
|
||||
{
|
||||
return( i );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( !*reject )
|
||||
return( limit );
|
||||
|
||||
register unsigned int i,j;
|
||||
size_t rej_len = strlen( reject );
|
||||
|
||||
for ( i = 0; i < limit; s++, i++ )
|
||||
{
|
||||
for ( j = 0; j < rej_len; j++ )
|
||||
{
|
||||
if ( *s == reject[j] )
|
||||
{
|
||||
return( i );
|
||||
}
|
||||
}
|
||||
}
|
||||
return( limit );
|
||||
}
|
||||
|
||||
#endif // ZM_MEM_UTILS_H
|
||||
|
|
7719
src/zm_monitor.cpp
7719
src/zm_monitor.cpp
File diff suppressed because it is too large
Load Diff
838
src/zm_monitor.h
838
src/zm_monitor.h
|
@ -48,505 +48,505 @@
|
|||
//
|
||||
class Monitor
|
||||
{
|
||||
friend class MonitorStream;
|
||||
friend class MonitorStream;
|
||||
|
||||
public:
|
||||
typedef enum
|
||||
{
|
||||
QUERY=0,
|
||||
CAPTURE,
|
||||
ANALYSIS
|
||||
} Purpose;
|
||||
public:
|
||||
typedef enum
|
||||
{
|
||||
QUERY=0,
|
||||
CAPTURE,
|
||||
ANALYSIS
|
||||
} Purpose;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
NONE=1,
|
||||
MONITOR,
|
||||
MODECT,
|
||||
RECORD,
|
||||
MOCORD,
|
||||
NODECT
|
||||
} Function;
|
||||
typedef enum
|
||||
{
|
||||
NONE=1,
|
||||
MONITOR,
|
||||
MODECT,
|
||||
RECORD,
|
||||
MOCORD,
|
||||
NODECT
|
||||
} Function;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
ROTATE_0=1,
|
||||
ROTATE_90,
|
||||
ROTATE_180,
|
||||
ROTATE_270,
|
||||
FLIP_HORI,
|
||||
FLIP_VERT
|
||||
} Orientation;
|
||||
typedef enum
|
||||
{
|
||||
ROTATE_0=1,
|
||||
ROTATE_90,
|
||||
ROTATE_180,
|
||||
ROTATE_270,
|
||||
FLIP_HORI,
|
||||
FLIP_VERT
|
||||
} Orientation;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
IDLE,
|
||||
PREALARM,
|
||||
ALARM,
|
||||
ALERT,
|
||||
TAPE
|
||||
} State;
|
||||
typedef enum
|
||||
{
|
||||
IDLE,
|
||||
PREALARM,
|
||||
ALARM,
|
||||
ALERT,
|
||||
TAPE
|
||||
} State;
|
||||
|
||||
protected:
|
||||
typedef std::set<Zone *> ZoneSet;
|
||||
protected:
|
||||
typedef std::set<Zone *> ZoneSet;
|
||||
|
||||
typedef enum { GET_SETTINGS=0x1, SET_SETTINGS=0x2, RELOAD=0x4, SUSPEND=0x10, RESUME=0x20 } Action;
|
||||
typedef enum { GET_SETTINGS=0x1, SET_SETTINGS=0x2, RELOAD=0x4, SUSPEND=0x10, RESUME=0x20 } Action;
|
||||
|
||||
typedef enum { CLOSE_TIME, CLOSE_IDLE, CLOSE_ALARM } EventCloseMode;
|
||||
typedef enum { CLOSE_TIME, CLOSE_IDLE, CLOSE_ALARM } EventCloseMode;
|
||||
|
||||
/* sizeof(SharedData) expected to be 336 bytes on 32bit and 64bit */
|
||||
typedef struct
|
||||
{
|
||||
uint32_t size; /* +0 */
|
||||
uint32_t last_write_index; /* +4 */
|
||||
uint32_t last_read_index; /* +8 */
|
||||
uint32_t state; /* +12 */
|
||||
uint32_t last_event; /* +16 */
|
||||
uint32_t action; /* +20 */
|
||||
int32_t brightness; /* +24 */
|
||||
int32_t hue; /* +28 */
|
||||
int32_t colour; /* +32 */
|
||||
int32_t contrast; /* +36 */
|
||||
int32_t alarm_x; /* +40 */
|
||||
int32_t alarm_y; /* +44 */
|
||||
uint8_t valid; /* +48 */
|
||||
uint8_t active; /* +49 */
|
||||
uint8_t signal; /* +50 */
|
||||
uint8_t format; /* +51 */
|
||||
uint32_t imagesize; /* +52 */
|
||||
uint32_t epadding1; /* +56 */
|
||||
uint32_t epadding2; /* +60 */
|
||||
/*
|
||||
** This keeps 32bit time_t and 64bit time_t identical and compatible as long as time is before 2038.
|
||||
** Shared memory layout should be identical for both 32bit and 64bit and is multiples of 16.
|
||||
*/
|
||||
union { /* +64 */
|
||||
time_t last_write_time;
|
||||
uint64_t extrapad1;
|
||||
};
|
||||
union { /* +72 */
|
||||
time_t last_read_time;
|
||||
uint64_t extrapad2;
|
||||
};
|
||||
uint8_t control_state[256]; /* +80 */
|
||||
/* sizeof(SharedData) expected to be 336 bytes on 32bit and 64bit */
|
||||
typedef struct
|
||||
{
|
||||
uint32_t size; /* +0 */
|
||||
uint32_t last_write_index; /* +4 */
|
||||
uint32_t last_read_index; /* +8 */
|
||||
uint32_t state; /* +12 */
|
||||
uint32_t last_event; /* +16 */
|
||||
uint32_t action; /* +20 */
|
||||
int32_t brightness; /* +24 */
|
||||
int32_t hue; /* +28 */
|
||||
int32_t colour; /* +32 */
|
||||
int32_t contrast; /* +36 */
|
||||
int32_t alarm_x; /* +40 */
|
||||
int32_t alarm_y; /* +44 */
|
||||
uint8_t valid; /* +48 */
|
||||
uint8_t active; /* +49 */
|
||||
uint8_t signal; /* +50 */
|
||||
uint8_t format; /* +51 */
|
||||
uint32_t imagesize; /* +52 */
|
||||
uint32_t epadding1; /* +56 */
|
||||
uint32_t epadding2; /* +60 */
|
||||
/*
|
||||
** This keeps 32bit time_t and 64bit time_t identical and compatible as long as time is before 2038.
|
||||
** Shared memory layout should be identical for both 32bit and 64bit and is multiples of 16.
|
||||
*/
|
||||
union { /* +64 */
|
||||
time_t last_write_time;
|
||||
uint64_t extrapad1;
|
||||
};
|
||||
union { /* +72 */
|
||||
time_t last_read_time;
|
||||
uint64_t extrapad2;
|
||||
};
|
||||
uint8_t control_state[256]; /* +80 */
|
||||
|
||||
} SharedData;
|
||||
} SharedData;
|
||||
|
||||
typedef enum { TRIGGER_CANCEL, TRIGGER_ON, TRIGGER_OFF } TriggerState;
|
||||
typedef enum { TRIGGER_CANCEL, TRIGGER_ON, TRIGGER_OFF } TriggerState;
|
||||
|
||||
/* sizeof(TriggerData) expected to be 560 on 32bit & and 64bit */
|
||||
typedef struct
|
||||
{
|
||||
uint32_t size;
|
||||
uint32_t trigger_state;
|
||||
uint32_t trigger_score;
|
||||
uint32_t padding;
|
||||
char trigger_cause[32];
|
||||
char trigger_text[256];
|
||||
char trigger_showtext[256];
|
||||
} TriggerData;
|
||||
/* sizeof(TriggerData) expected to be 560 on 32bit & and 64bit */
|
||||
typedef struct
|
||||
{
|
||||
uint32_t size;
|
||||
uint32_t trigger_state;
|
||||
uint32_t trigger_score;
|
||||
uint32_t padding;
|
||||
char trigger_cause[32];
|
||||
char trigger_text[256];
|
||||
char trigger_showtext[256];
|
||||
} TriggerData;
|
||||
|
||||
/* sizeof(Snapshot) expected to be 16 bytes on 32bit and 32 bytes on 64bit */
|
||||
struct Snapshot
|
||||
{
|
||||
struct timeval *timestamp;
|
||||
Image *image;
|
||||
void* padding;
|
||||
};
|
||||
/* sizeof(Snapshot) expected to be 16 bytes on 32bit and 32 bytes on 64bit */
|
||||
struct Snapshot
|
||||
{
|
||||
struct timeval *timestamp;
|
||||
Image *image;
|
||||
void* padding;
|
||||
};
|
||||
|
||||
//TODO: Technically we can't exclude this struct when people don't have avformat as the Memory.pm module doesn't know about avformat
|
||||
//TODO: Technically we can't exclude this struct when people don't have avformat as the Memory.pm module doesn't know about avformat
|
||||
#if 1
|
||||
//sizeOf(VideoStoreData) expected to be 4104 bytes on 32bit and 64bit
|
||||
typedef struct
|
||||
{
|
||||
uint32_t size;
|
||||
char event_file[4096];
|
||||
uint32_t recording; //bool arch dependent so use uint32 instead
|
||||
//uint32_t frameNumber;
|
||||
} VideoStoreData;
|
||||
//sizeOf(VideoStoreData) expected to be 4104 bytes on 32bit and 64bit
|
||||
typedef struct
|
||||
{
|
||||
uint32_t size;
|
||||
char event_file[4096];
|
||||
uint32_t recording; //bool arch dependent so use uint32 instead
|
||||
//uint32_t frameNumber;
|
||||
} VideoStoreData;
|
||||
|
||||
#endif // HAVE_LIBAVFORMAT
|
||||
|
||||
class MonitorLink {
|
||||
protected:
|
||||
unsigned int id;
|
||||
char name[64];
|
||||
class MonitorLink {
|
||||
protected:
|
||||
unsigned int id;
|
||||
char name[64];
|
||||
|
||||
bool connected;
|
||||
time_t last_connect_time;
|
||||
bool connected;
|
||||
time_t last_connect_time;
|
||||
|
||||
#if ZM_MEM_MAPPED
|
||||
int map_fd;
|
||||
char mem_file[PATH_MAX];
|
||||
int map_fd;
|
||||
char mem_file[PATH_MAX];
|
||||
#else // ZM_MEM_MAPPED
|
||||
int shm_id;
|
||||
int shm_id;
|
||||
#endif // ZM_MEM_MAPPED
|
||||
off_t mem_size;
|
||||
unsigned char *mem_ptr;
|
||||
off_t mem_size;
|
||||
unsigned char *mem_ptr;
|
||||
|
||||
volatile SharedData *shared_data;
|
||||
volatile TriggerData *trigger_data;
|
||||
volatile VideoStoreData *video_store_data;
|
||||
volatile SharedData *shared_data;
|
||||
volatile TriggerData *trigger_data;
|
||||
volatile VideoStoreData *video_store_data;
|
||||
|
||||
int last_state;
|
||||
int last_event;
|
||||
int last_state;
|
||||
int last_event;
|
||||
|
||||
|
||||
public:
|
||||
MonitorLink( int p_id, const char *p_name );
|
||||
~MonitorLink();
|
||||
public:
|
||||
MonitorLink( int p_id, const char *p_name );
|
||||
~MonitorLink();
|
||||
|
||||
inline int Id() const {
|
||||
return( id );
|
||||
}
|
||||
inline const char *Name() const {
|
||||
return( name );
|
||||
}
|
||||
inline int Id() const {
|
||||
return( id );
|
||||
}
|
||||
inline const char *Name() const {
|
||||
return( name );
|
||||
}
|
||||
|
||||
inline bool isConnected() const {
|
||||
return( connected );
|
||||
}
|
||||
inline time_t getLastConnectTime() const {
|
||||
return( last_connect_time );
|
||||
}
|
||||
inline bool isConnected() const {
|
||||
return( connected );
|
||||
}
|
||||
inline time_t getLastConnectTime() const {
|
||||
return( last_connect_time );
|
||||
}
|
||||
|
||||
bool connect();
|
||||
bool disconnect();
|
||||
bool connect();
|
||||
bool disconnect();
|
||||
|
||||
bool isAlarmed();
|
||||
bool inAlarm();
|
||||
bool hasAlarmed();
|
||||
};
|
||||
bool isAlarmed();
|
||||
bool inAlarm();
|
||||
bool hasAlarmed();
|
||||
};
|
||||
|
||||
protected:
|
||||
// These are read from the DB and thereafter remain unchanged
|
||||
unsigned int id;
|
||||
char name[64];
|
||||
unsigned int server_id; // Id of the Server object
|
||||
unsigned int storage_id; // Id of the Storage Object, which currently will just provide a path, but in future may do more.
|
||||
Function function; // What the monitor is doing
|
||||
bool enabled; // Whether the monitor is enabled or asleep
|
||||
unsigned int width; // Normally the same as the camera, but not if partly rotated
|
||||
unsigned int height; // Normally the same as the camera, but not if partly rotated
|
||||
bool v4l_multi_buffer;
|
||||
unsigned int v4l_captures_per_frame;
|
||||
Orientation orientation; // Whether the image has to be rotated at all
|
||||
unsigned int deinterlacing;
|
||||
protected:
|
||||
// These are read from the DB and thereafter remain unchanged
|
||||
unsigned int id;
|
||||
char name[64];
|
||||
unsigned int server_id; // Id of the Server object
|
||||
unsigned int storage_id; // Id of the Storage Object, which currently will just provide a path, but in future may do more.
|
||||
Function function; // What the monitor is doing
|
||||
bool enabled; // Whether the monitor is enabled or asleep
|
||||
unsigned int width; // Normally the same as the camera, but not if partly rotated
|
||||
unsigned int height; // Normally the same as the camera, but not if partly rotated
|
||||
bool v4l_multi_buffer;
|
||||
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;
|
||||
bool record_audio; // Whether to store the audio that we receive
|
||||
int savejpegspref;
|
||||
int videowriterpref;
|
||||
std::string encoderparams;
|
||||
std::vector<EncoderParameter_t> encoderparamsvec;
|
||||
bool record_audio; // Whether to store the audio that we receive
|
||||
|
||||
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
|
||||
int colour; // The statically saved colour of the camera
|
||||
char event_prefix[64]; // The prefix applied to event names as they are created
|
||||
char label_format[64]; // The format of the timestamp on the images
|
||||
Coord label_coord; // The coordinates of the timestamp on the images
|
||||
int label_size; // Size of the timestamp on the images
|
||||
int image_buffer_count; // Size of circular image buffer, at least twice the size of the pre_event_count
|
||||
int pre_event_buffer_count; // Size of dedicated circular pre event buffer used when analysis is not performed at capturing framerate,
|
||||
// value is pre_event_count + alarm_frame_count - 1
|
||||
int warmup_count; // How many images to process before looking for events
|
||||
int pre_event_count; // How many images to hold and prepend to an alarm event
|
||||
int post_event_count; // How many unalarmed images must occur before the alarm state is reset
|
||||
int stream_replay_buffer; // How many frames to store to support DVR functions, IGNORED from this object, passed directly into zms now
|
||||
int section_length; // How long events should last in continuous modes
|
||||
bool adaptive_skip; // Whether to use the newer adaptive algorithm for this monitor
|
||||
int frame_skip; // How many frames to skip in continuous modes
|
||||
int motion_frame_skip; // How many frames to skip in motion detection
|
||||
double analysis_fps; // Target framerate for video analysis
|
||||
unsigned int analysis_update_delay; // How long we wait before updating analysis parameters
|
||||
int capture_delay; // How long we wait between capture frames
|
||||
int alarm_capture_delay; // How long we wait between capture frames when in alarm state
|
||||
int alarm_frame_count; // How many alarm frames are required before an event is triggered
|
||||
int fps_report_interval; // How many images should be captured/processed between reporting the current FPS
|
||||
int ref_blend_perc; // Percentage of new image going into reference image.
|
||||
int alarm_ref_blend_perc; // Percentage of new image going into reference image during alarm.
|
||||
bool track_motion; // Whether this monitor tries to track detected motion
|
||||
Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected
|
||||
bool embed_exif; // Whether to embed Exif data into each image frame or not
|
||||
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
|
||||
int colour; // The statically saved colour of the camera
|
||||
char event_prefix[64]; // The prefix applied to event names as they are created
|
||||
char label_format[64]; // The format of the timestamp on the images
|
||||
Coord label_coord; // The coordinates of the timestamp on the images
|
||||
int label_size; // Size of the timestamp on the images
|
||||
int image_buffer_count; // Size of circular image buffer, at least twice the size of the pre_event_count
|
||||
int pre_event_buffer_count; // Size of dedicated circular pre event buffer used when analysis is not performed at capturing framerate,
|
||||
// value is pre_event_count + alarm_frame_count - 1
|
||||
int warmup_count; // How many images to process before looking for events
|
||||
int pre_event_count; // How many images to hold and prepend to an alarm event
|
||||
int post_event_count; // How many unalarmed images must occur before the alarm state is reset
|
||||
int stream_replay_buffer; // How many frames to store to support DVR functions, IGNORED from this object, passed directly into zms now
|
||||
int section_length; // How long events should last in continuous modes
|
||||
bool adaptive_skip; // Whether to use the newer adaptive algorithm for this monitor
|
||||
int frame_skip; // How many frames to skip in continuous modes
|
||||
int motion_frame_skip; // How many frames to skip in motion detection
|
||||
double analysis_fps; // Target framerate for video analysis
|
||||
unsigned int analysis_update_delay; // How long we wait before updating analysis parameters
|
||||
int capture_delay; // How long we wait between capture frames
|
||||
int alarm_capture_delay; // How long we wait between capture frames when in alarm state
|
||||
int alarm_frame_count; // How many alarm frames are required before an event is triggered
|
||||
int fps_report_interval; // How many images should be captured/processed between reporting the current FPS
|
||||
int ref_blend_perc; // Percentage of new image going into reference image.
|
||||
int alarm_ref_blend_perc; // Percentage of new image going into reference image during alarm.
|
||||
bool track_motion; // Whether this monitor tries to track detected motion
|
||||
Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected
|
||||
bool embed_exif; // Whether to embed Exif data into each image frame or not
|
||||
|
||||
double fps;
|
||||
Image delta_image;
|
||||
Image ref_image;
|
||||
Image alarm_image; // Used in creating analysis images, will be initialized in Analysis
|
||||
Image write_image; // Used when creating snapshot images
|
||||
double fps;
|
||||
Image delta_image;
|
||||
Image ref_image;
|
||||
Image alarm_image; // Used in creating analysis images, will be initialized in Analysis
|
||||
Image write_image; // Used when creating snapshot images
|
||||
|
||||
Purpose purpose; // What this monitor has been created to do
|
||||
int event_count;
|
||||
int image_count;
|
||||
int ready_count;
|
||||
int first_alarm_count;
|
||||
int last_alarm_count;
|
||||
int buffer_count;
|
||||
int prealarm_count;
|
||||
State state;
|
||||
time_t start_time;
|
||||
time_t last_fps_time;
|
||||
time_t auto_resume_time;
|
||||
unsigned int last_motion_score;
|
||||
Purpose purpose; // What this monitor has been created to do
|
||||
int event_count;
|
||||
int image_count;
|
||||
int ready_count;
|
||||
int first_alarm_count;
|
||||
int last_alarm_count;
|
||||
int buffer_count;
|
||||
int prealarm_count;
|
||||
State state;
|
||||
time_t start_time;
|
||||
time_t last_fps_time;
|
||||
time_t auto_resume_time;
|
||||
unsigned int last_motion_score;
|
||||
|
||||
EventCloseMode event_close_mode;
|
||||
EventCloseMode event_close_mode;
|
||||
|
||||
#if ZM_MEM_MAPPED
|
||||
int map_fd;
|
||||
char mem_file[PATH_MAX];
|
||||
int map_fd;
|
||||
char mem_file[PATH_MAX];
|
||||
#else // ZM_MEM_MAPPED
|
||||
int shm_id;
|
||||
int shm_id;
|
||||
#endif // ZM_MEM_MAPPED
|
||||
off_t mem_size;
|
||||
unsigned char *mem_ptr;
|
||||
Storage *storage;
|
||||
off_t mem_size;
|
||||
unsigned char *mem_ptr;
|
||||
Storage *storage;
|
||||
|
||||
SharedData *shared_data;
|
||||
TriggerData *trigger_data;
|
||||
VideoStoreData *video_store_data;
|
||||
SharedData *shared_data;
|
||||
TriggerData *trigger_data;
|
||||
VideoStoreData *video_store_data;
|
||||
|
||||
Snapshot *image_buffer;
|
||||
Snapshot next_buffer; /* Used by four field deinterlacing */
|
||||
Snapshot *pre_event_buffer;
|
||||
Snapshot *image_buffer;
|
||||
Snapshot next_buffer; /* Used by four field deinterlacing */
|
||||
Snapshot *pre_event_buffer;
|
||||
|
||||
Camera *camera;
|
||||
Camera *camera;
|
||||
|
||||
Event *event;
|
||||
Event *event;
|
||||
|
||||
int n_zones;
|
||||
Zone **zones;
|
||||
int n_zones;
|
||||
Zone **zones;
|
||||
|
||||
struct timeval **timestamps;
|
||||
Image **images;
|
||||
struct timeval **timestamps;
|
||||
Image **images;
|
||||
|
||||
const unsigned char *privacy_bitmask;
|
||||
const unsigned char *privacy_bitmask;
|
||||
|
||||
int n_linked_monitors;
|
||||
MonitorLink **linked_monitors;
|
||||
int n_linked_monitors;
|
||||
MonitorLink **linked_monitors;
|
||||
|
||||
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,
|
||||
unsigned int p_server_id,
|
||||
unsigned int p_storage_id,
|
||||
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,
|
||||
bool p_record_audio,
|
||||
const char *p_event_prefix,
|
||||
const char *p_label_format,
|
||||
const Coord &p_label_coord,
|
||||
int label_size,
|
||||
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,
|
||||
double p_analysis_fps,
|
||||
unsigned int p_analysis_update_delay,
|
||||
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,
|
||||
bool p_embed_exif,
|
||||
Purpose p_purpose,
|
||||
int p_n_zones=0,
|
||||
Zone *p_zones[]=0
|
||||
);
|
||||
~Monitor();
|
||||
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,
|
||||
unsigned int p_server_id,
|
||||
unsigned int p_storage_id,
|
||||
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,
|
||||
bool p_record_audio,
|
||||
const char *p_event_prefix,
|
||||
const char *p_label_format,
|
||||
const Coord &p_label_coord,
|
||||
int label_size,
|
||||
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,
|
||||
double p_analysis_fps,
|
||||
unsigned int p_analysis_update_delay,
|
||||
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,
|
||||
bool p_embed_exif,
|
||||
Purpose p_purpose,
|
||||
int p_n_zones=0,
|
||||
Zone *p_zones[]=0
|
||||
);
|
||||
~Monitor();
|
||||
|
||||
void AddZones( int p_n_zones, Zone *p_zones[] );
|
||||
void AddPrivacyBitmask( Zone *p_zones[] );
|
||||
void AddZones( int p_n_zones, Zone *p_zones[] );
|
||||
void AddPrivacyBitmask( Zone *p_zones[] );
|
||||
|
||||
bool connect();
|
||||
inline int ShmValid() const {
|
||||
return( shared_data->valid );
|
||||
}
|
||||
bool connect();
|
||||
inline int ShmValid() const {
|
||||
return( shared_data->valid );
|
||||
}
|
||||
|
||||
inline int Id() const {
|
||||
return( id );
|
||||
}
|
||||
inline const char *Name() const {
|
||||
return( name );
|
||||
}
|
||||
inline Storage *getStorage() {
|
||||
if ( ! storage ) {
|
||||
storage = new Storage( storage_id );
|
||||
}
|
||||
return( storage );
|
||||
}
|
||||
inline Function GetFunction() const {
|
||||
return( function );
|
||||
}
|
||||
inline bool Enabled() {
|
||||
if ( function <= MONITOR )
|
||||
return( false );
|
||||
return( enabled );
|
||||
}
|
||||
inline const char *EventPrefix() const {
|
||||
return( event_prefix );
|
||||
}
|
||||
inline bool Ready() {
|
||||
if ( function <= MONITOR )
|
||||
return( false );
|
||||
return( image_count > ready_count );
|
||||
}
|
||||
inline bool Active() {
|
||||
if ( function <= MONITOR )
|
||||
return( false );
|
||||
return( enabled && shared_data->active );
|
||||
}
|
||||
inline bool Exif() {
|
||||
return( embed_exif );
|
||||
}
|
||||
inline int Id() const {
|
||||
return( id );
|
||||
}
|
||||
inline const char *Name() const {
|
||||
return( name );
|
||||
}
|
||||
inline Storage *getStorage() {
|
||||
if ( ! storage ) {
|
||||
storage = new Storage( storage_id );
|
||||
}
|
||||
return( storage );
|
||||
}
|
||||
inline Function GetFunction() const {
|
||||
return( function );
|
||||
}
|
||||
inline bool Enabled() {
|
||||
if ( function <= MONITOR )
|
||||
return( false );
|
||||
return( enabled );
|
||||
}
|
||||
inline const char *EventPrefix() const {
|
||||
return( event_prefix );
|
||||
}
|
||||
inline bool Ready() {
|
||||
if ( function <= MONITOR )
|
||||
return( false );
|
||||
return( image_count > ready_count );
|
||||
}
|
||||
inline bool Active() {
|
||||
if ( function <= MONITOR )
|
||||
return( false );
|
||||
return( enabled && shared_data->active );
|
||||
}
|
||||
inline bool Exif() {
|
||||
return( embed_exif );
|
||||
}
|
||||
|
||||
unsigned int Width() const { return( width ); }
|
||||
unsigned int Height() const { return( height ); }
|
||||
unsigned int Colours() const { return( camera->Colours() ); }
|
||||
unsigned int SubpixelOrder() const { return( camera->SubpixelOrder() ); }
|
||||
unsigned int Width() const { return width; }
|
||||
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 ); }
|
||||
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 );
|
||||
struct timeval GetTimestamp( int index=-1 ) const;
|
||||
void UpdateAdaptiveSkip();
|
||||
useconds_t GetAnalysisRate();
|
||||
unsigned int GetAnalysisUpdateDelay() const { return( analysis_update_delay ); }
|
||||
int GetCaptureDelay() const { return( capture_delay ); }
|
||||
int GetAlarmCaptureDelay() const { return( alarm_capture_delay ); }
|
||||
unsigned int GetLastReadIndex() const;
|
||||
unsigned int GetLastWriteIndex() const;
|
||||
unsigned int GetLastEvent() const;
|
||||
double GetFPS() const;
|
||||
void ForceAlarmOn( int force_score, const char *force_case, const char *force_text="" );
|
||||
void ForceAlarmOff();
|
||||
void CancelForced();
|
||||
TriggerState GetTriggerState() const { return( (TriggerState)(trigger_data?trigger_data->trigger_state:TRIGGER_CANCEL )); }
|
||||
State GetState() const;
|
||||
int GetImage( int index=-1, int scale=100 );
|
||||
struct timeval GetTimestamp( int index=-1 ) const;
|
||||
void UpdateAdaptiveSkip();
|
||||
useconds_t GetAnalysisRate();
|
||||
unsigned int GetAnalysisUpdateDelay() const { return( analysis_update_delay ); }
|
||||
int GetCaptureDelay() const { return( capture_delay ); }
|
||||
int GetAlarmCaptureDelay() const { return( alarm_capture_delay ); }
|
||||
unsigned int GetLastReadIndex() const;
|
||||
unsigned int GetLastWriteIndex() const;
|
||||
unsigned int GetLastEvent() const;
|
||||
double GetFPS() const;
|
||||
void ForceAlarmOn( int force_score, const char *force_case, const char *force_text="" );
|
||||
void ForceAlarmOff();
|
||||
void CancelForced();
|
||||
TriggerState GetTriggerState() const { return( (TriggerState)(trigger_data?trigger_data->trigger_state:TRIGGER_CANCEL )); }
|
||||
|
||||
void actionReload();
|
||||
void actionEnable();
|
||||
void actionDisable();
|
||||
void actionSuspend();
|
||||
void actionResume();
|
||||
void actionReload();
|
||||
void actionEnable();
|
||||
void actionDisable();
|
||||
void actionSuspend();
|
||||
void actionResume();
|
||||
|
||||
int actionBrightness( int p_brightness=-1 );
|
||||
int actionHue( int p_hue=-1 );
|
||||
int actionColour( int p_colour=-1 );
|
||||
int actionContrast( int p_contrast=-1 );
|
||||
int actionBrightness( int p_brightness=-1 );
|
||||
int actionHue( int p_hue=-1 );
|
||||
int actionColour( int p_colour=-1 );
|
||||
int actionContrast( int p_contrast=-1 );
|
||||
|
||||
inline int PrimeCapture() {
|
||||
return( camera->PrimeCapture() );
|
||||
}
|
||||
inline int PreCapture() {
|
||||
return( camera->PreCapture() );
|
||||
}
|
||||
int Capture();
|
||||
int PostCapture() {
|
||||
return( camera->PostCapture() );
|
||||
}
|
||||
inline int PrimeCapture() {
|
||||
return( camera->PrimeCapture() );
|
||||
}
|
||||
inline int PreCapture() {
|
||||
return( camera->PreCapture() );
|
||||
}
|
||||
int Capture();
|
||||
int PostCapture() {
|
||||
return( camera->PostCapture() );
|
||||
}
|
||||
|
||||
unsigned int DetectMotion( const Image &comp_image, Event::StringSet &zoneSet );
|
||||
// DetectBlack seems to be unused. Check it on zm_monitor.cpp for more info.
|
||||
//unsigned int DetectBlack( const Image &comp_image, Event::StringSet &zoneSet );
|
||||
bool CheckSignal( const Image *image );
|
||||
bool Analyse();
|
||||
void DumpImage( Image *dump_image ) const;
|
||||
void TimestampImage( Image *ts_image, const struct timeval *ts_time ) const;
|
||||
bool closeEvent();
|
||||
unsigned int DetectMotion( const Image &comp_image, Event::StringSet &zoneSet );
|
||||
// DetectBlack seems to be unused. Check it on zm_monitor.cpp for more info.
|
||||
//unsigned int DetectBlack( const Image &comp_image, Event::StringSet &zoneSet );
|
||||
bool CheckSignal( const Image *image );
|
||||
bool Analyse();
|
||||
void DumpImage( Image *dump_image ) const;
|
||||
void TimestampImage( Image *ts_image, const struct timeval *ts_time ) const;
|
||||
bool closeEvent();
|
||||
|
||||
void Reload();
|
||||
void ReloadZones();
|
||||
void ReloadLinkedMonitors( const char * );
|
||||
void Reload();
|
||||
void ReloadZones();
|
||||
void ReloadLinkedMonitors( const char * );
|
||||
|
||||
bool DumpSettings( char *output, bool verbose );
|
||||
void DumpZoneImage( const char *zone_string=0 );
|
||||
bool DumpSettings( char *output, bool verbose );
|
||||
void DumpZoneImage( const char *zone_string=0 );
|
||||
|
||||
#if ZM_HAS_V4L
|
||||
static int LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose purpose );
|
||||
static int LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose purpose );
|
||||
#endif // ZM_HAS_V4L
|
||||
static int LoadRemoteMonitors( const char *protocol, const char *host, const char*port, const char*path, Monitor **&monitors, Purpose purpose );
|
||||
static int LoadFileMonitors( const char *file, Monitor **&monitors, Purpose purpose );
|
||||
static int LoadRemoteMonitors( const char *protocol, const char *host, const char*port, const char*path, Monitor **&monitors, Purpose purpose );
|
||||
static int LoadFileMonitors( const char *file, Monitor **&monitors, Purpose purpose );
|
||||
#if HAVE_LIBAVFORMAT
|
||||
static int LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose purpose );
|
||||
static int LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose purpose );
|
||||
#endif // HAVE_LIBAVFORMAT
|
||||
static Monitor *Load( unsigned int id, bool load_zones, Purpose purpose );
|
||||
//void writeStreamImage( Image *image, struct timeval *timestamp, int scale, int mag, int x, int y );
|
||||
//void StreamImages( int scale=100, int maxfps=10, time_t ttl=0, int msq_id=0 );
|
||||
//void StreamImagesRaw( int scale=100, int maxfps=10, time_t ttl=0 );
|
||||
//void StreamImagesZip( int scale=100, int maxfps=10, time_t ttl=0 );
|
||||
void SingleImage( int scale=100 );
|
||||
void SingleImageRaw( int scale=100 );
|
||||
void SingleImageZip( int scale=100 );
|
||||
static Monitor *Load( unsigned int id, bool load_zones, Purpose purpose );
|
||||
//void writeStreamImage( Image *image, struct timeval *timestamp, int scale, int mag, int x, int y );
|
||||
//void StreamImages( int scale=100, int maxfps=10, time_t ttl=0, int msq_id=0 );
|
||||
//void StreamImagesRaw( int scale=100, int maxfps=10, time_t ttl=0 );
|
||||
//void StreamImagesZip( int scale=100, int maxfps=10, time_t ttl=0 );
|
||||
void SingleImage( int scale=100 );
|
||||
void SingleImageRaw( int scale=100 );
|
||||
void SingleImageZip( int scale=100 );
|
||||
#if HAVE_LIBAVCODEC
|
||||
//void StreamMpeg( const char *format, int scale=100, int maxfps=10, int bitrate=100000 );
|
||||
//void StreamMpeg( const char *format, int scale=100, int maxfps=10, int bitrate=100000 );
|
||||
#endif // HAVE_LIBAVCODEC
|
||||
};
|
||||
|
||||
#define MOD_ADD( var, delta, limit ) (((var)+(limit)+(delta))%(limit))
|
||||
|
||||
class MonitorStream : public StreamBase {
|
||||
protected:
|
||||
typedef struct SwapImage {
|
||||
bool valid;
|
||||
struct timeval timestamp;
|
||||
char file_name[PATH_MAX];
|
||||
} SwapImage;
|
||||
protected:
|
||||
typedef struct SwapImage {
|
||||
bool valid;
|
||||
struct timeval timestamp;
|
||||
char file_name[PATH_MAX];
|
||||
} SwapImage;
|
||||
|
||||
private:
|
||||
SwapImage *temp_image_buffer;
|
||||
int temp_image_buffer_count;
|
||||
int temp_read_index;
|
||||
int temp_write_index;
|
||||
private:
|
||||
SwapImage *temp_image_buffer;
|
||||
int temp_image_buffer_count;
|
||||
int temp_read_index;
|
||||
int temp_write_index;
|
||||
|
||||
protected:
|
||||
time_t ttl;
|
||||
protected:
|
||||
time_t ttl;
|
||||
|
||||
protected:
|
||||
int playback_buffer;
|
||||
bool delayed;
|
||||
protected:
|
||||
int playback_buffer;
|
||||
bool delayed;
|
||||
|
||||
int frame_count;
|
||||
int frame_count;
|
||||
|
||||
protected:
|
||||
bool checkSwapPath( const char *path, bool create_path );
|
||||
protected:
|
||||
bool checkSwapPath( const char *path, bool create_path );
|
||||
|
||||
bool sendFrame( const char *filepath, struct timeval *timestamp );
|
||||
bool sendFrame( Image *image, struct timeval *timestamp );
|
||||
void processCommand( const CmdMsg *msg );
|
||||
bool sendFrame( const char *filepath, struct timeval *timestamp );
|
||||
bool sendFrame( Image *image, struct timeval *timestamp );
|
||||
void processCommand( const CmdMsg *msg );
|
||||
|
||||
public:
|
||||
MonitorStream() : playback_buffer( 0 ), delayed( false ), frame_count( 0 ) {
|
||||
}
|
||||
void setStreamBuffer( int p_playback_buffer ) {
|
||||
playback_buffer = p_playback_buffer;
|
||||
}
|
||||
void setStreamTTL( time_t p_ttl ) {
|
||||
ttl = p_ttl;
|
||||
}
|
||||
bool setStreamStart( int monitor_id ) {
|
||||
return loadMonitor( monitor_id );
|
||||
}
|
||||
void runStream();
|
||||
public:
|
||||
MonitorStream() : playback_buffer( 0 ), delayed( false ), frame_count( 0 ) {
|
||||
}
|
||||
void setStreamBuffer( int p_playback_buffer ) {
|
||||
playback_buffer = p_playback_buffer;
|
||||
}
|
||||
void setStreamTTL( time_t p_ttl ) {
|
||||
ttl = p_ttl;
|
||||
}
|
||||
bool setStreamStart( int monitor_id ) {
|
||||
return loadMonitor( monitor_id );
|
||||
}
|
||||
void runStream();
|
||||
};
|
||||
|
||||
#endif // ZM_MONITOR_H
|
||||
|
|
|
@ -333,7 +333,13 @@ void VideoStream::OpenStream( )
|
|||
Panic( "Could not allocate opicture" );
|
||||
}
|
||||
|
||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||
int size = av_image_get_buffer_size( c->pix_fmt, c->width,
|
||||
c->height, 1 );
|
||||
#else
|
||||
int size = avpicture_get_size( c->pix_fmt, c->width, c->height );
|
||||
#endif
|
||||
|
||||
uint8_t *opicture_buf = (uint8_t *)av_malloc( size );
|
||||
if ( !opicture_buf )
|
||||
{
|
||||
|
@ -344,7 +350,13 @@ void VideoStream::OpenStream( )
|
|||
#endif
|
||||
Panic( "Could not allocate opicture_buf" );
|
||||
}
|
||||
avpicture_fill( (AVPicture *)opicture, opicture_buf, c->pix_fmt, c->width, c->height );
|
||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||
av_image_fill_arrays(opicture->data, opicture->linesize,
|
||||
opicture_buf, c->pix_fmt, c->width, c->height, 1);
|
||||
#else
|
||||
avpicture_fill( (AVPicture *)opicture, opicture_buf, c->pix_fmt,
|
||||
c->width, c->height );
|
||||
#endif
|
||||
|
||||
/* if the output format is not identical to the input format, then a temporary
|
||||
picture is needed too. It is then converted to the required
|
||||
|
@ -361,7 +373,12 @@ void VideoStream::OpenStream( )
|
|||
{
|
||||
Panic( "Could not allocate tmp_opicture" );
|
||||
}
|
||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||
int size = av_image_get_buffer_size( pf, c->width,
|
||||
c->height,1 );
|
||||
#else
|
||||
int size = avpicture_get_size( pf, c->width, c->height );
|
||||
#endif
|
||||
uint8_t *tmp_opicture_buf = (uint8_t *)av_malloc( size );
|
||||
if ( !tmp_opicture_buf )
|
||||
{
|
||||
|
@ -372,7 +389,14 @@ void VideoStream::OpenStream( )
|
|||
#endif
|
||||
Panic( "Could not allocate tmp_opicture_buf" );
|
||||
}
|
||||
avpicture_fill( (AVPicture *)tmp_opicture, tmp_opicture_buf, pf, c->width, c->height );
|
||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||
av_image_fill_arrays(tmp_opicture->data,
|
||||
tmp_opicture->linesize, tmp_opicture_buf, pf,
|
||||
c->width, c->height, 1);
|
||||
#else
|
||||
avpicture_fill( (AVPicture *)tmp_opicture,
|
||||
tmp_opicture_buf, pf, c->width, c->height );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -678,14 +702,14 @@ double VideoStream::ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size,
|
|||
#endif
|
||||
if ( got_packet )
|
||||
{
|
||||
if ( c->coded_frame->key_frame )
|
||||
{
|
||||
#if LIBAVCODEC_VERSION_CHECK(52, 30, 2, 30, 2)
|
||||
pkt->flags |= AV_PKT_FLAG_KEY;
|
||||
#else
|
||||
pkt->flags |= PKT_FLAG_KEY;
|
||||
#endif
|
||||
}
|
||||
// if ( c->coded_frame->key_frame )
|
||||
// {
|
||||
//#if LIBAVCODEC_VERSION_CHECK(52, 30, 2, 30, 2)
|
||||
// pkt->flags |= AV_PKT_FLAG_KEY;
|
||||
//#else
|
||||
// pkt->flags |= PKT_FLAG_KEY;
|
||||
//#endif
|
||||
// }
|
||||
|
||||
if ( pkt->pts != (int64_t)AV_NOPTS_VALUE )
|
||||
{
|
||||
|
|
|
@ -27,60 +27,60 @@
|
|||
class VideoStream
|
||||
{
|
||||
protected:
|
||||
struct MimeData
|
||||
{
|
||||
const char *format;
|
||||
const char *mime_type;
|
||||
};
|
||||
struct MimeData
|
||||
{
|
||||
const char *format;
|
||||
const char *mime_type;
|
||||
};
|
||||
|
||||
protected:
|
||||
static bool initialised;
|
||||
static struct MimeData mime_data[];
|
||||
static bool initialised;
|
||||
static struct MimeData mime_data[];
|
||||
|
||||
protected:
|
||||
char *codec_and_format;
|
||||
const char *filename;
|
||||
const char *format;
|
||||
const char *codec_name;
|
||||
enum _AVPIXELFORMAT pf;
|
||||
AVOutputFormat *of;
|
||||
AVFormatContext *ofc;
|
||||
AVStream *ost;
|
||||
AVCodec *codec;
|
||||
AVFrame *opicture;
|
||||
AVFrame *tmp_opicture;
|
||||
uint8_t *video_outbuf;
|
||||
int video_outbuf_size;
|
||||
double last_pts;
|
||||
char *codec_and_format;
|
||||
const char *filename;
|
||||
const char *format;
|
||||
const char *codec_name;
|
||||
enum _AVPIXELFORMAT pf;
|
||||
AVOutputFormat *of;
|
||||
AVFormatContext *ofc;
|
||||
AVStream *ost;
|
||||
AVCodec *codec;
|
||||
AVFrame *opicture;
|
||||
AVFrame *tmp_opicture;
|
||||
uint8_t *video_outbuf;
|
||||
int video_outbuf_size;
|
||||
double last_pts;
|
||||
|
||||
pthread_t streaming_thread;
|
||||
bool do_streaming;
|
||||
uint8_t *buffer_copy;
|
||||
bool add_timestamp;
|
||||
unsigned int timestamp;
|
||||
pthread_mutex_t *buffer_copy_lock;
|
||||
int buffer_copy_size;
|
||||
int buffer_copy_used;
|
||||
AVPacket** packet_buffers;
|
||||
int packet_index;
|
||||
int SendPacket(AVPacket *packet);
|
||||
static void* StreamingThreadCallback(void *ctx);
|
||||
pthread_t streaming_thread;
|
||||
bool do_streaming;
|
||||
uint8_t *buffer_copy;
|
||||
bool add_timestamp;
|
||||
unsigned int timestamp;
|
||||
pthread_mutex_t *buffer_copy_lock;
|
||||
int buffer_copy_size;
|
||||
int buffer_copy_used;
|
||||
AVPacket** packet_buffers;
|
||||
int packet_index;
|
||||
int SendPacket(AVPacket *packet);
|
||||
static void* StreamingThreadCallback(void *ctx);
|
||||
|
||||
protected:
|
||||
static void Initialise();
|
||||
static void Initialise();
|
||||
|
||||
void SetupFormat( );
|
||||
void SetupCodec( int colours, int subpixelorder, int width, int height, int bitrate, double frame_rate );
|
||||
void SetParameters();
|
||||
void ActuallyOpenStream();
|
||||
double ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 );
|
||||
void SetupFormat( );
|
||||
void SetupCodec( int colours, int subpixelorder, int width, int height, int bitrate, double frame_rate );
|
||||
void SetParameters();
|
||||
void ActuallyOpenStream();
|
||||
double ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 );
|
||||
|
||||
public:
|
||||
VideoStream( const char *filename, const char *format, int bitrate, double frame_rate, int colours, int subpixelorder, int width, int height );
|
||||
~VideoStream();
|
||||
const char *MimeType() const;
|
||||
void OpenStream();
|
||||
double EncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 );
|
||||
VideoStream( const char *filename, const char *format, int bitrate, double frame_rate, int colours, int subpixelorder, int width, int height );
|
||||
~VideoStream();
|
||||
const char *MimeType() const;
|
||||
void OpenStream();
|
||||
double EncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 );
|
||||
};
|
||||
|
||||
#endif // HAVE_LIBAVCODEC
|
||||
|
|
140
src/zm_poly.cpp
140
src/zm_poly.cpp
|
@ -28,95 +28,95 @@
|
|||
|
||||
void Polygon::calcArea()
|
||||
{
|
||||
double float_area = 0.0L;
|
||||
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
||||
{
|
||||
double trap_area = ((coords[i].X()-coords[j].X())*((coords[i].Y()+coords[j].Y())))/2.0L;
|
||||
float_area += trap_area;
|
||||
//printf( "%.2f (%.2f)\n", float_area, trap_area );
|
||||
}
|
||||
area = (int)round(fabs(float_area));
|
||||
double float_area = 0.0L;
|
||||
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
||||
{
|
||||
double trap_area = ((coords[i].X()-coords[j].X())*((coords[i].Y()+coords[j].Y())))/2.0L;
|
||||
float_area += trap_area;
|
||||
//printf( "%.2f (%.2f)\n", float_area, trap_area );
|
||||
}
|
||||
area = (int)round(fabs(float_area));
|
||||
}
|
||||
|
||||
void Polygon::calcCentre()
|
||||
{
|
||||
if ( !area && n_coords )
|
||||
calcArea();
|
||||
double float_x = 0.0L, float_y = 0.0L;
|
||||
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
||||
{
|
||||
float_x += ((coords[i].Y()-coords[j].Y())*((coords[i].X()*2)+(coords[i].X()*coords[j].X())+(coords[j].X()*2)));
|
||||
float_y += ((coords[j].X()-coords[i].X())*((coords[i].Y()*2)+(coords[i].Y()*coords[j].Y())+(coords[j].Y()*2)));
|
||||
}
|
||||
float_x /= (6*area);
|
||||
float_y /= (6*area);
|
||||
//printf( "%.2f,%.2f\n", float_x, float_y );
|
||||
centre = Coord( (int)round(float_x), (int)round(float_y) );
|
||||
if ( !area && n_coords )
|
||||
calcArea();
|
||||
double float_x = 0.0L, float_y = 0.0L;
|
||||
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
||||
{
|
||||
float_x += ((coords[i].Y()-coords[j].Y())*((coords[i].X()*2)+(coords[i].X()*coords[j].X())+(coords[j].X()*2)));
|
||||
float_y += ((coords[j].X()-coords[i].X())*((coords[i].Y()*2)+(coords[i].Y()*coords[j].Y())+(coords[j].Y()*2)));
|
||||
}
|
||||
float_x /= (6*area);
|
||||
float_y /= (6*area);
|
||||
//printf( "%.2f,%.2f\n", float_x, float_y );
|
||||
centre = Coord( (int)round(float_x), (int)round(float_y) );
|
||||
}
|
||||
|
||||
Polygon::Polygon( int p_n_coords, const Coord *p_coords ) : n_coords( p_n_coords )
|
||||
{
|
||||
coords = new Coord[n_coords];
|
||||
coords = new Coord[n_coords];
|
||||
|
||||
int min_x = -1;
|
||||
int max_x = -1;
|
||||
int min_y = -1;
|
||||
int max_y = -1;
|
||||
for( int i = 0; i < n_coords; i++ )
|
||||
{
|
||||
coords[i] = p_coords[i];
|
||||
if ( min_x == -1 || coords[i].X() < min_x )
|
||||
min_x = coords[i].X();
|
||||
if ( max_x == -1 || coords[i].X() > max_x )
|
||||
max_x = coords[i].X();
|
||||
if ( min_y == -1 || coords[i].Y() < min_y )
|
||||
min_y = coords[i].Y();
|
||||
if ( max_y == -1 || coords[i].Y() > max_y )
|
||||
max_y = coords[i].Y();
|
||||
}
|
||||
extent = Box( min_x, min_y, max_x, max_y );
|
||||
calcArea();
|
||||
calcCentre();
|
||||
int min_x = -1;
|
||||
int max_x = -1;
|
||||
int min_y = -1;
|
||||
int max_y = -1;
|
||||
for( int i = 0; i < n_coords; i++ )
|
||||
{
|
||||
coords[i] = p_coords[i];
|
||||
if ( min_x == -1 || coords[i].X() < min_x )
|
||||
min_x = coords[i].X();
|
||||
if ( max_x == -1 || coords[i].X() > max_x )
|
||||
max_x = coords[i].X();
|
||||
if ( min_y == -1 || coords[i].Y() < min_y )
|
||||
min_y = coords[i].Y();
|
||||
if ( max_y == -1 || coords[i].Y() > max_y )
|
||||
max_y = coords[i].Y();
|
||||
}
|
||||
extent = Box( min_x, min_y, max_x, max_y );
|
||||
calcArea();
|
||||
calcCentre();
|
||||
}
|
||||
|
||||
Polygon::Polygon( const Polygon &p_polygon ) : n_coords( p_polygon.n_coords ), extent( p_polygon.extent ), area( p_polygon.area ), centre( p_polygon.centre )
|
||||
{
|
||||
coords = new Coord[n_coords];
|
||||
for( int i = 0; i < n_coords; i++ )
|
||||
{
|
||||
coords[i] = p_polygon.coords[i];
|
||||
}
|
||||
coords = new Coord[n_coords];
|
||||
for( int i = 0; i < n_coords; i++ )
|
||||
{
|
||||
coords[i] = p_polygon.coords[i];
|
||||
}
|
||||
}
|
||||
|
||||
Polygon &Polygon::operator=( const Polygon &p_polygon )
|
||||
{
|
||||
if ( n_coords < p_polygon.n_coords )
|
||||
{
|
||||
delete[] coords;
|
||||
coords = new Coord[p_polygon.n_coords];
|
||||
}
|
||||
n_coords = p_polygon.n_coords;
|
||||
for( int i = 0; i < n_coords; i++ )
|
||||
{
|
||||
coords[i] = p_polygon.coords[i];
|
||||
}
|
||||
extent = p_polygon.extent;
|
||||
area = p_polygon.area;
|
||||
centre = p_polygon.centre;
|
||||
return( *this );
|
||||
if ( n_coords < p_polygon.n_coords )
|
||||
{
|
||||
delete[] coords;
|
||||
coords = new Coord[p_polygon.n_coords];
|
||||
}
|
||||
n_coords = p_polygon.n_coords;
|
||||
for( int i = 0; i < n_coords; i++ )
|
||||
{
|
||||
coords[i] = p_polygon.coords[i];
|
||||
}
|
||||
extent = p_polygon.extent;
|
||||
area = p_polygon.area;
|
||||
centre = p_polygon.centre;
|
||||
return( *this );
|
||||
}
|
||||
|
||||
bool Polygon::isInside( const Coord &coord ) const
|
||||
{
|
||||
bool inside = false;
|
||||
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
||||
{
|
||||
if ( (((coords[i].Y() <= coord.Y()) && (coord.Y() < coords[j].Y()) )
|
||||
|| ((coords[j].Y() <= coord.Y()) && (coord.Y() < coords[i].Y())))
|
||||
&& (coord.X() < (coords[j].X() - coords[i].X()) * (coord.Y() - coords[i].Y()) / (coords[j].Y() - coords[i].Y()) + coords[i].X()))
|
||||
{
|
||||
inside = !inside;
|
||||
}
|
||||
}
|
||||
return( inside );
|
||||
bool inside = false;
|
||||
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
||||
{
|
||||
if ( (((coords[i].Y() <= coord.Y()) && (coord.Y() < coords[j].Y()) )
|
||||
|| ((coords[j].Y() <= coord.Y()) && (coord.Y() < coords[i].Y())))
|
||||
&& (coord.X() < (coords[j].X() - coords[i].X()) * (coord.Y() - coords[i].Y()) / (coords[j].Y() - coords[i].Y()) + coords[i].X()))
|
||||
{
|
||||
inside = !inside;
|
||||
}
|
||||
}
|
||||
return( inside );
|
||||
}
|
||||
|
|
148
src/zm_poly.h
148
src/zm_poly.h
|
@ -33,93 +33,93 @@
|
|||
class Polygon
|
||||
{
|
||||
protected:
|
||||
struct Edge
|
||||
{
|
||||
int min_y;
|
||||
int max_y;
|
||||
double min_x;
|
||||
double _1_m;
|
||||
struct Edge
|
||||
{
|
||||
int min_y;
|
||||
int max_y;
|
||||
double min_x;
|
||||
double _1_m;
|
||||
|
||||
static int CompareYX( const void *p1, const void *p2 )
|
||||
{
|
||||
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
|
||||
if ( e1->min_y == e2->min_y )
|
||||
return( int(e1->min_x - e2->min_x) );
|
||||
else
|
||||
return( int(e1->min_y - e2->min_y) );
|
||||
}
|
||||
static int CompareX( const void *p1, const void *p2 )
|
||||
{
|
||||
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
|
||||
return( int(e1->min_x - e2->min_x) );
|
||||
}
|
||||
};
|
||||
static int CompareYX( const void *p1, const void *p2 )
|
||||
{
|
||||
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
|
||||
if ( e1->min_y == e2->min_y )
|
||||
return( int(e1->min_x - e2->min_x) );
|
||||
else
|
||||
return( int(e1->min_y - e2->min_y) );
|
||||
}
|
||||
static int CompareX( const void *p1, const void *p2 )
|
||||
{
|
||||
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
|
||||
return( int(e1->min_x - e2->min_x) );
|
||||
}
|
||||
};
|
||||
|
||||
struct Slice
|
||||
{
|
||||
int min_x;
|
||||
int max_x;
|
||||
int n_edges;
|
||||
int *edges;
|
||||
struct Slice
|
||||
{
|
||||
int min_x;
|
||||
int max_x;
|
||||
int n_edges;
|
||||
int *edges;
|
||||
|
||||
Slice()
|
||||
{
|
||||
n_edges = 0;
|
||||
edges = 0;
|
||||
}
|
||||
~Slice()
|
||||
{
|
||||
delete edges;
|
||||
}
|
||||
};
|
||||
Slice()
|
||||
{
|
||||
n_edges = 0;
|
||||
edges = 0;
|
||||
}
|
||||
~Slice()
|
||||
{
|
||||
delete edges;
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
int n_coords;
|
||||
Coord *coords;
|
||||
Box extent;
|
||||
int area;
|
||||
Coord centre;
|
||||
Edge *edges;
|
||||
Slice *slices;
|
||||
int n_coords;
|
||||
Coord *coords;
|
||||
Box extent;
|
||||
int area;
|
||||
Coord centre;
|
||||
Edge *edges;
|
||||
Slice *slices;
|
||||
|
||||
protected:
|
||||
void initialiseEdges();
|
||||
void calcArea();
|
||||
void calcCentre();
|
||||
void initialiseEdges();
|
||||
void calcArea();
|
||||
void calcCentre();
|
||||
|
||||
public:
|
||||
inline Polygon() : n_coords( 0 ), coords( 0 ), area( 0 )
|
||||
{
|
||||
}
|
||||
Polygon( int p_n_coords, const Coord *p_coords );
|
||||
Polygon( const Polygon &p_polygon );
|
||||
~Polygon()
|
||||
{
|
||||
delete[] coords;
|
||||
}
|
||||
inline Polygon() : n_coords( 0 ), coords( 0 ), area( 0 )
|
||||
{
|
||||
}
|
||||
Polygon( int p_n_coords, const Coord *p_coords );
|
||||
Polygon( const Polygon &p_polygon );
|
||||
~Polygon()
|
||||
{
|
||||
delete[] coords;
|
||||
}
|
||||
|
||||
Polygon &operator=( const Polygon &p_polygon );
|
||||
Polygon &operator=( const Polygon &p_polygon );
|
||||
|
||||
inline int getNumCoords() const { return( n_coords ); }
|
||||
inline const Coord &getCoord( int index ) const
|
||||
{
|
||||
return( coords[index] );
|
||||
}
|
||||
inline int getNumCoords() const { return( n_coords ); }
|
||||
inline const Coord &getCoord( int index ) const
|
||||
{
|
||||
return( coords[index] );
|
||||
}
|
||||
|
||||
inline const Box &Extent() const { return( extent ); }
|
||||
inline int LoX() const { return( extent.LoX() ); }
|
||||
inline int HiX() const { return( extent.HiX() ); }
|
||||
inline int LoY() const { return( extent.LoY() ); }
|
||||
inline int HiY() const { return( extent.HiY() ); }
|
||||
inline int Width() const { return( extent.Width() ); }
|
||||
inline int Height() const { return( extent.Height() ); }
|
||||
inline const Box &Extent() const { return( extent ); }
|
||||
inline int LoX() const { return( extent.LoX() ); }
|
||||
inline int HiX() const { return( extent.HiX() ); }
|
||||
inline int LoY() const { return( extent.LoY() ); }
|
||||
inline int HiY() const { return( extent.HiY() ); }
|
||||
inline int Width() const { return( extent.Width() ); }
|
||||
inline int Height() const { return( extent.Height() ); }
|
||||
|
||||
inline int Area() const { return( area ); }
|
||||
inline const Coord &Centre() const
|
||||
{
|
||||
return( centre );
|
||||
}
|
||||
bool isInside( const Coord &coord ) const;
|
||||
inline int Area() const { return( area ); }
|
||||
inline const Coord &Centre() const
|
||||
{
|
||||
return( centre );
|
||||
}
|
||||
bool isInside( const Coord &coord ) const;
|
||||
};
|
||||
|
||||
#endif // ZM_POLY_H
|
||||
|
|
|
@ -26,99 +26,99 @@
|
|||
|
||||
RegExpr::RegExpr( const char *pattern, int flags, int p_max_matches ) : max_matches( p_max_matches ), match_buffers( 0 ), match_lengths( 0 ), match_valid( 0 )
|
||||
{
|
||||
const char *errstr;
|
||||
int erroffset = 0;
|
||||
if ( !(regex = pcre_compile( pattern, flags, &errstr, &erroffset, 0 )) )
|
||||
{
|
||||
Panic( "pcre_compile(%s): %s at %d", pattern, errstr, erroffset );
|
||||
}
|
||||
const char *errstr;
|
||||
int erroffset = 0;
|
||||
if ( !(regex = pcre_compile( pattern, flags, &errstr, &erroffset, 0 )) )
|
||||
{
|
||||
Panic( "pcre_compile(%s): %s at %d", pattern, errstr, erroffset );
|
||||
}
|
||||
|
||||
regextra = pcre_study( regex, 0, &errstr );
|
||||
if ( errstr )
|
||||
{
|
||||
Panic( "pcre_study(%s): %s", pattern, errstr );
|
||||
}
|
||||
regextra = pcre_study( regex, 0, &errstr );
|
||||
if ( errstr )
|
||||
{
|
||||
Panic( "pcre_study(%s): %s", pattern, errstr );
|
||||
}
|
||||
|
||||
if ( (ok = (bool)regex) )
|
||||
{
|
||||
match_vectors = new int[3*max_matches];
|
||||
memset( match_vectors, 0, sizeof(*match_vectors)*3*max_matches );
|
||||
match_buffers = new char *[max_matches];
|
||||
memset( match_buffers, 0, sizeof(*match_buffers)*max_matches );
|
||||
match_lengths = new int[max_matches];
|
||||
memset( match_lengths, 0, sizeof(*match_lengths)*max_matches );
|
||||
match_valid = new bool[max_matches];
|
||||
memset( match_valid, 0, sizeof(*match_valid)*max_matches );
|
||||
}
|
||||
n_matches = 0;
|
||||
if ( (ok = (bool)regex) )
|
||||
{
|
||||
match_vectors = new int[3*max_matches];
|
||||
memset( match_vectors, 0, sizeof(*match_vectors)*3*max_matches );
|
||||
match_buffers = new char *[max_matches];
|
||||
memset( match_buffers, 0, sizeof(*match_buffers)*max_matches );
|
||||
match_lengths = new int[max_matches];
|
||||
memset( match_lengths, 0, sizeof(*match_lengths)*max_matches );
|
||||
match_valid = new bool[max_matches];
|
||||
memset( match_valid, 0, sizeof(*match_valid)*max_matches );
|
||||
}
|
||||
n_matches = 0;
|
||||
}
|
||||
|
||||
RegExpr::~RegExpr()
|
||||
{
|
||||
for ( int i = 0; i < max_matches; i++ )
|
||||
{
|
||||
if ( match_buffers[i] )
|
||||
{
|
||||
delete[] match_buffers[i];
|
||||
}
|
||||
}
|
||||
delete[] match_valid;
|
||||
delete[] match_lengths;
|
||||
delete[] match_buffers;
|
||||
delete[] match_vectors;
|
||||
for ( int i = 0; i < max_matches; i++ )
|
||||
{
|
||||
if ( match_buffers[i] )
|
||||
{
|
||||
delete[] match_buffers[i];
|
||||
}
|
||||
}
|
||||
delete[] match_valid;
|
||||
delete[] match_lengths;
|
||||
delete[] match_buffers;
|
||||
delete[] match_vectors;
|
||||
}
|
||||
|
||||
int RegExpr::Match( const char *subject_string, int subject_length, int flags )
|
||||
{
|
||||
match_string = subject_string;
|
||||
match_string = subject_string;
|
||||
|
||||
n_matches = pcre_exec( regex, regextra, subject_string, subject_length, 0, flags, match_vectors, 2*max_matches );
|
||||
n_matches = pcre_exec( regex, regextra, subject_string, subject_length, 0, flags, match_vectors, 2*max_matches );
|
||||
|
||||
if ( n_matches <= 0 )
|
||||
{
|
||||
if ( n_matches < PCRE_ERROR_NOMATCH )
|
||||
{
|
||||
Error( "Error %d executing regular expression", n_matches );
|
||||
}
|
||||
return( n_matches = 0 );
|
||||
}
|
||||
if ( n_matches <= 0 )
|
||||
{
|
||||
if ( n_matches < PCRE_ERROR_NOMATCH )
|
||||
{
|
||||
Error( "Error %d executing regular expression", n_matches );
|
||||
}
|
||||
return( n_matches = 0 );
|
||||
}
|
||||
|
||||
for( int i = 0; i < max_matches; i++ )
|
||||
{
|
||||
match_valid[i] = false;
|
||||
}
|
||||
return( n_matches );
|
||||
for( int i = 0; i < max_matches; i++ )
|
||||
{
|
||||
match_valid[i] = false;
|
||||
}
|
||||
return( n_matches );
|
||||
}
|
||||
|
||||
const char *RegExpr::MatchString( int match_index ) const
|
||||
{
|
||||
if ( match_index > n_matches )
|
||||
{
|
||||
return( 0 );
|
||||
}
|
||||
if ( !match_valid[match_index] )
|
||||
{
|
||||
int match_len = match_vectors[(2*match_index)+1]-match_vectors[2*match_index];
|
||||
if ( match_lengths[match_index] < (match_len+1) )
|
||||
{
|
||||
delete[] match_buffers[match_index];
|
||||
match_buffers[match_index] = new char[match_len+1];
|
||||
match_lengths[match_index] = match_len+1;
|
||||
}
|
||||
memcpy( match_buffers[match_index], match_string+match_vectors[2*match_index], match_len );
|
||||
match_buffers[match_index][match_len] = '\0';
|
||||
match_valid[match_index] = true;
|
||||
}
|
||||
return( match_buffers[match_index] );
|
||||
if ( match_index > n_matches )
|
||||
{
|
||||
return( 0 );
|
||||
}
|
||||
if ( !match_valid[match_index] )
|
||||
{
|
||||
int match_len = match_vectors[(2*match_index)+1]-match_vectors[2*match_index];
|
||||
if ( match_lengths[match_index] < (match_len+1) )
|
||||
{
|
||||
delete[] match_buffers[match_index];
|
||||
match_buffers[match_index] = new char[match_len+1];
|
||||
match_lengths[match_index] = match_len+1;
|
||||
}
|
||||
memcpy( match_buffers[match_index], match_string+match_vectors[2*match_index], match_len );
|
||||
match_buffers[match_index][match_len] = '\0';
|
||||
match_valid[match_index] = true;
|
||||
}
|
||||
return( match_buffers[match_index] );
|
||||
}
|
||||
|
||||
int RegExpr::MatchLength( int match_index ) const
|
||||
{
|
||||
if ( match_index > n_matches )
|
||||
{
|
||||
return( 0 );
|
||||
}
|
||||
return( match_vectors[(2*match_index)+1]-match_vectors[2*match_index] );
|
||||
if ( match_index > n_matches )
|
||||
{
|
||||
return( 0 );
|
||||
}
|
||||
return( match_vectors[(2*match_index)+1]-match_vectors[2*match_index] );
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBPCRE
|
||||
|
|
|
@ -35,29 +35,29 @@
|
|||
class RegExpr
|
||||
{
|
||||
protected:
|
||||
pcre *regex;
|
||||
pcre_extra *regextra;
|
||||
int max_matches;
|
||||
int *match_vectors;
|
||||
mutable char **match_buffers;
|
||||
int *match_lengths;
|
||||
bool *match_valid;
|
||||
pcre *regex;
|
||||
pcre_extra *regextra;
|
||||
int max_matches;
|
||||
int *match_vectors;
|
||||
mutable char **match_buffers;
|
||||
int *match_lengths;
|
||||
bool *match_valid;
|
||||
|
||||
protected:
|
||||
const char *match_string;
|
||||
int n_matches;
|
||||
const char *match_string;
|
||||
int n_matches;
|
||||
|
||||
protected:
|
||||
bool ok;
|
||||
bool ok;
|
||||
|
||||
public:
|
||||
RegExpr( const char *pattern, int cflags=0, int p_max_matches=32 );
|
||||
~RegExpr();
|
||||
bool Ok() const { return( ok ); }
|
||||
int MatchCount() const { return( n_matches ); }
|
||||
int Match( const char *subject_string, int subject_length, int flags=0 );
|
||||
const char *MatchString( int match_index ) const;
|
||||
int MatchLength( int match_index ) const;
|
||||
RegExpr( const char *pattern, int cflags=0, int p_max_matches=32 );
|
||||
~RegExpr();
|
||||
bool Ok() const { return( ok ); }
|
||||
int MatchCount() const { return( n_matches ); }
|
||||
int Match( const char *subject_string, int subject_length, int flags=0 );
|
||||
const char *MatchString( int match_index ) const;
|
||||
int MatchLength( int match_index ) const;
|
||||
};
|
||||
|
||||
#endif // HAVE_LIBPCRE
|
||||
|
|
|
@ -238,13 +238,18 @@ int RemoteCameraRtsp::PrimeCapture()
|
|||
mFrame = avcodec_alloc_frame();
|
||||
#endif
|
||||
|
||||
if(mRawFrame == NULL || mFrame == NULL)
|
||||
Fatal( "Unable to allocate frame(s)");
|
||||
if(mRawFrame == NULL || mFrame == NULL)
|
||||
Fatal( "Unable to allocate frame(s)");
|
||||
|
||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||
int pSize = av_image_get_buffer_size( imagePixFormat, width, height, 1 );
|
||||
#else
|
||||
int pSize = avpicture_get_size( imagePixFormat, width, height );
|
||||
if( (unsigned int)pSize != imagesize) {
|
||||
Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize);
|
||||
}
|
||||
#endif
|
||||
|
||||
if( (unsigned int)pSize != imagesize) {
|
||||
Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize);
|
||||
}
|
||||
/*
|
||||
#if HAVE_LIBSWSCALE
|
||||
if(!sws_isSupportedInput(mCodecContext->pix_fmt)) {
|
||||
|
@ -299,24 +304,6 @@ int RemoteCameraRtsp::Capture( Image &image ) {
|
|||
if ( !buffer.size() )
|
||||
return( -1 );
|
||||
|
||||
int avResult = av_read_frame( mFormatContext, &packet );
|
||||
if ( avResult < 0 ) {
|
||||
char errbuf[AV_ERROR_MAX_STRING_SIZE];
|
||||
av_strerror(avResult, errbuf, AV_ERROR_MAX_STRING_SIZE);
|
||||
if (
|
||||
// Check if EOF.
|
||||
(avResult == AVERROR_EOF || (mFormatContext->pb && mFormatContext->pb->eof_reached)) ||
|
||||
// Check for Connection failure.
|
||||
(avResult == -110)
|
||||
) {
|
||||
Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf);
|
||||
//ReopenFfmpeg();
|
||||
}
|
||||
|
||||
Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, avResult, errbuf );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if(mCodecContext->codec_id == AV_CODEC_ID_H264) {
|
||||
// SPS and PPS frames should be saved and appended to IDR frames
|
||||
int nalType = (buffer.head()[3] & 0x1f);
|
||||
|
@ -339,6 +326,8 @@ int avResult = av_read_frame( mFormatContext, &packet );
|
|||
buffer += lastSps;
|
||||
buffer += lastPps;
|
||||
}
|
||||
} else {
|
||||
Debug(3, "Not an h264 packet");
|
||||
}
|
||||
|
||||
av_init_packet( &packet );
|
||||
|
@ -498,9 +487,15 @@ int RemoteCameraRtsp::CaptureAndRecord( Image &image, bool recording, char* even
|
|||
|
||||
Debug( 3, "Got frame %d", frameCount );
|
||||
|
||||
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height );
|
||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||
av_image_fill_arrays(mFrame->data, mFrame->linesize,
|
||||
directbuffer, imagePixFormat, width, height, 1);
|
||||
#else
|
||||
avpicture_fill( (AVPicture *)mFrame, directbuffer,
|
||||
imagePixFormat, width, height);
|
||||
#endif
|
||||
|
||||
//Video recording
|
||||
//Video recording
|
||||
if ( recording && !wasRecording ) {
|
||||
//Instantiate the video storage module
|
||||
|
||||
|
|
180
src/zm_rgb.h
180
src/zm_rgb.h
|
@ -20,80 +20,80 @@
|
|||
#ifndef ZM_RGB_H
|
||||
#define ZM_RGB_H
|
||||
|
||||
typedef uint32_t Rgb; // RGB colour type
|
||||
typedef uint32_t Rgb; // RGB colour type
|
||||
|
||||
#define WHITE 0xff
|
||||
#define WHITE_R 0xff
|
||||
#define WHITE_G 0xff
|
||||
#define WHITE_B 0xff
|
||||
#define WHITE 0xff
|
||||
#define WHITE_R 0xff
|
||||
#define WHITE_G 0xff
|
||||
#define WHITE_B 0xff
|
||||
|
||||
#define BLACK 0x00
|
||||
#define BLACK_R 0x00
|
||||
#define BLACK_G 0x00
|
||||
#define BLACK_B 0x00
|
||||
#define BLACK 0x00
|
||||
#define BLACK_R 0x00
|
||||
#define BLACK_G 0x00
|
||||
#define BLACK_B 0x00
|
||||
|
||||
#define RGB_WHITE (0x00ffffff)
|
||||
#define RGB_BLACK (0x00000000)
|
||||
#define RGB_RED (0x000000ff)
|
||||
#define RGB_GREEN (0x0000ff00)
|
||||
#define RGB_BLUE (0x00ff0000)
|
||||
#define RGB_ORANGE (0x0000a5ff)
|
||||
#define RGB_PURPLE (0x00800080)
|
||||
#define RGB_TRANSPARENT (0x01000000)
|
||||
#define RGB_WHITE (0x00ffffff)
|
||||
#define RGB_BLACK (0x00000000)
|
||||
#define RGB_RED (0x000000ff)
|
||||
#define RGB_GREEN (0x0000ff00)
|
||||
#define RGB_BLUE (0x00ff0000)
|
||||
#define RGB_ORANGE (0x0000a5ff)
|
||||
#define RGB_PURPLE (0x00800080)
|
||||
#define RGB_TRANSPARENT (0x01000000)
|
||||
|
||||
#define RGB_VAL(v,c) (((v)>>(16-((c)*8)))&0xff)
|
||||
#define RGB_VAL(v,c) (((v)>>(16-((c)*8)))&0xff)
|
||||
|
||||
/* RGB or RGBA macros */
|
||||
#define BLUE_VAL_RGBA(v) (((v)>>16)&0xff)
|
||||
#define GREEN_VAL_RGBA(v) (((v)>>8)&0xff)
|
||||
#define RED_VAL_RGBA(v) ((v)&0xff)
|
||||
#define ALPHA_VAL_RGBA(v) ((v)>>24)&0xff)
|
||||
#define RED_PTR_RGBA(ptr) (*((uint8_t*)ptr))
|
||||
#define GREEN_PTR_RGBA(ptr) (*((uint8_t*)ptr+1))
|
||||
#define BLUE_PTR_RGBA(ptr) (*((uint8_t*)ptr+2))
|
||||
#define ALPHA_PTR_RGBA(ptr) (*((uint8_t*)ptr+3))
|
||||
#define BLUE_VAL_RGBA(v) (((v)>>16)&0xff)
|
||||
#define GREEN_VAL_RGBA(v) (((v)>>8)&0xff)
|
||||
#define RED_VAL_RGBA(v) ((v)&0xff)
|
||||
#define ALPHA_VAL_RGBA(v) ((v)>>24)&0xff)
|
||||
#define RED_PTR_RGBA(ptr) (*((uint8_t*)ptr))
|
||||
#define GREEN_PTR_RGBA(ptr) (*((uint8_t*)ptr+1))
|
||||
#define BLUE_PTR_RGBA(ptr) (*((uint8_t*)ptr+2))
|
||||
#define ALPHA_PTR_RGBA(ptr) (*((uint8_t*)ptr+3))
|
||||
|
||||
/* BGR or BGRA */
|
||||
#define RED_VAL_BGRA(v) (((v)>>16)&0xff)
|
||||
#define GREEN_VAL_BGRA(v) (((v)>>8)&0xff)
|
||||
#define BLUE_VAL_BGRA(v) ((v)&0xff)
|
||||
#define ALPHA_VAL_BGRA(v) ((v)>>24)&0xff)
|
||||
#define RED_PTR_BGRA(ptr) (*((uint8_t*)ptr+2))
|
||||
#define GREEN_PTR_BGRA(ptr) (*((uint8_t*)ptr+1))
|
||||
#define BLUE_PTR_BGRA(ptr) (*((uint8_t*)ptr))
|
||||
#define ALPHA_PTR_BGRA(ptr) (*((uint8_t*)ptr+3))
|
||||
#define RED_VAL_BGRA(v) (((v)>>16)&0xff)
|
||||
#define GREEN_VAL_BGRA(v) (((v)>>8)&0xff)
|
||||
#define BLUE_VAL_BGRA(v) ((v)&0xff)
|
||||
#define ALPHA_VAL_BGRA(v) ((v)>>24)&0xff)
|
||||
#define RED_PTR_BGRA(ptr) (*((uint8_t*)ptr+2))
|
||||
#define GREEN_PTR_BGRA(ptr) (*((uint8_t*)ptr+1))
|
||||
#define BLUE_PTR_BGRA(ptr) (*((uint8_t*)ptr))
|
||||
#define ALPHA_PTR_BGRA(ptr) (*((uint8_t*)ptr+3))
|
||||
|
||||
/* ARGB */
|
||||
#define BLUE_VAL_ARGB(v) (((v)>>24)&0xff)
|
||||
#define GREEN_VAL_ARGB(v) (((v)>>16)&0xff)
|
||||
#define RED_VAL_ARGB(v) (((v)>>8)&0xff)
|
||||
#define ALPHA_VAL_ARGB(v) ((v)&0xff)
|
||||
#define RED_PTR_ARGB(ptr) (*((uint8_t*)ptr+1))
|
||||
#define GREEN_PTR_ARGB(ptr) (*((uint8_t*)ptr+2))
|
||||
#define BLUE_PTR_ARGB(ptr) (*((uint8_t*)ptr+3))
|
||||
#define ALPHA_PTR_ARGB(ptr) (*((uint8_t*)ptr))
|
||||
#define BLUE_VAL_ARGB(v) (((v)>>24)&0xff)
|
||||
#define GREEN_VAL_ARGB(v) (((v)>>16)&0xff)
|
||||
#define RED_VAL_ARGB(v) (((v)>>8)&0xff)
|
||||
#define ALPHA_VAL_ARGB(v) ((v)&0xff)
|
||||
#define RED_PTR_ARGB(ptr) (*((uint8_t*)ptr+1))
|
||||
#define GREEN_PTR_ARGB(ptr) (*((uint8_t*)ptr+2))
|
||||
#define BLUE_PTR_ARGB(ptr) (*((uint8_t*)ptr+3))
|
||||
#define ALPHA_PTR_ARGB(ptr) (*((uint8_t*)ptr))
|
||||
|
||||
/* ABGR */
|
||||
#define BLUE_VAL_ABGR(v) (((v)>>8)&0xff)
|
||||
#define GREEN_VAL_ABGR(v) (((v)>>16)&0xff)
|
||||
#define RED_VAL_ABGR(v) (((v)>>24)&0xff)
|
||||
#define ALPHA_VAL_ABGR(v) ((v)&0xff)
|
||||
#define RED_PTR_ABGR(ptr) (*((uint8_t*)ptr+3))
|
||||
#define GREEN_PTR_ABGR(ptr) (*((uint8_t*)ptr+2))
|
||||
#define BLUE_PTR_ABGR(ptr) (*((uint8_t*)ptr+1))
|
||||
#define ALPHA_PTR_ABGR(ptr) (*((uint8_t*)ptr))
|
||||
#define BLUE_VAL_ABGR(v) (((v)>>8)&0xff)
|
||||
#define GREEN_VAL_ABGR(v) (((v)>>16)&0xff)
|
||||
#define RED_VAL_ABGR(v) (((v)>>24)&0xff)
|
||||
#define ALPHA_VAL_ABGR(v) ((v)&0xff)
|
||||
#define RED_PTR_ABGR(ptr) (*((uint8_t*)ptr+3))
|
||||
#define GREEN_PTR_ABGR(ptr) (*((uint8_t*)ptr+2))
|
||||
#define BLUE_PTR_ABGR(ptr) (*((uint8_t*)ptr+1))
|
||||
#define ALPHA_PTR_ABGR(ptr) (*((uint8_t*)ptr))
|
||||
|
||||
|
||||
#define RGBA_BGRA_ZEROALPHA(v) ((v)&0x00ffffff)
|
||||
#define ARGB_ABGR_ZEROALPHA(v) ((v)&0xffffff00)
|
||||
#define RGBA_BGRA_ZEROALPHA(v) ((v)&0x00ffffff)
|
||||
#define ARGB_ABGR_ZEROALPHA(v) ((v)&0xffffff00)
|
||||
|
||||
/* ITU-R BT.709: Y = (0.2126 * R) + (0.7152 * G) + (0.0722 * B) */
|
||||
/* ITU-R BT.601: Y = (0.299 * R) + (0.587 * G) + (0.114 * B) */
|
||||
/* The formulas below produce an almost identical result to the weighted algorithms from the ITU-R BT.601 standard and the newer ITU-R BT.709 standard, but a lot faster */
|
||||
// #define RGB_FASTLUM_SINGLE_ITU709(v) ((RED(v)+RED(v)+BLUE(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v))>>3)
|
||||
// #define RGB_FASTLUM_VALUES_ITU709(ra,ga,ba) (((ra)+(ra)+(ba)+(ga)+(ga)+(ga)+(ga)+(ga))>>3)
|
||||
// #define RGB_FASTLUM_SINGLE_ITU601(v) ((RED(v)+RED(v)+RED(v)+BLUE(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v))>>3)
|
||||
// #define RGB_FASTLUM_VALUES_ITU601(ra,ga,ba) (((ra)+(ra)+(ra)+(ba)+(ga)+(ga)+(ga)+(ga))>>3)
|
||||
// #define RGB_FASTLUM_SINGLE_ITU709(v) ((RED(v)+RED(v)+BLUE(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v))>>3)
|
||||
// #define RGB_FASTLUM_VALUES_ITU709(ra,ga,ba) (((ra)+(ra)+(ba)+(ga)+(ga)+(ga)+(ga)+(ga))>>3)
|
||||
// #define RGB_FASTLUM_SINGLE_ITU601(v) ((RED(v)+RED(v)+RED(v)+BLUE(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v))>>3)
|
||||
// #define RGB_FASTLUM_VALUES_ITU601(ra,ga,ba) (((ra)+(ra)+(ra)+(ba)+(ga)+(ga)+(ga)+(ga))>>3)
|
||||
|
||||
/* ZM colours */
|
||||
#define ZM_COLOUR_RGB32 4
|
||||
|
@ -112,46 +112,46 @@ typedef uint32_t Rgb; // RGB colour type
|
|||
|
||||
/* A macro to use default subpixel order for a specified colour. */
|
||||
/* for grayscale it will use NONE, for 3 colours it will use R,G,B, for 4 colours it will use R,G,B,A */
|
||||
#define ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(c) ((c)<<1)
|
||||
#define ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(c) ((c)<<1)
|
||||
|
||||
/* Convert RGB colour value into BGR\ARGB\ABGR */
|
||||
inline Rgb rgb_convert(Rgb p_col, int p_subpixorder) {
|
||||
Rgb result;
|
||||
Rgb result;
|
||||
|
||||
switch(p_subpixorder) {
|
||||
switch(p_subpixorder) {
|
||||
|
||||
case ZM_SUBPIX_ORDER_BGR:
|
||||
case ZM_SUBPIX_ORDER_BGRA:
|
||||
{
|
||||
BLUE_PTR_BGRA(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_BGRA(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_BGRA(&result) = RED_VAL_RGBA(p_col);
|
||||
}
|
||||
break;
|
||||
case ZM_SUBPIX_ORDER_ARGB:
|
||||
{
|
||||
BLUE_PTR_ARGB(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_ARGB(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_ARGB(&result) = RED_VAL_RGBA(p_col);
|
||||
}
|
||||
break;
|
||||
case ZM_SUBPIX_ORDER_ABGR:
|
||||
{
|
||||
BLUE_PTR_ABGR(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_ABGR(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_ABGR(&result) = RED_VAL_RGBA(p_col);
|
||||
}
|
||||
break;
|
||||
/* Grayscale */
|
||||
case ZM_SUBPIX_ORDER_NONE:
|
||||
result = p_col & 0xff;
|
||||
break;
|
||||
default:
|
||||
return p_col;
|
||||
break;
|
||||
}
|
||||
case ZM_SUBPIX_ORDER_BGR:
|
||||
case ZM_SUBPIX_ORDER_BGRA:
|
||||
{
|
||||
BLUE_PTR_BGRA(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_BGRA(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_BGRA(&result) = RED_VAL_RGBA(p_col);
|
||||
}
|
||||
break;
|
||||
case ZM_SUBPIX_ORDER_ARGB:
|
||||
{
|
||||
BLUE_PTR_ARGB(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_ARGB(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_ARGB(&result) = RED_VAL_RGBA(p_col);
|
||||
}
|
||||
break;
|
||||
case ZM_SUBPIX_ORDER_ABGR:
|
||||
{
|
||||
BLUE_PTR_ABGR(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_ABGR(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_ABGR(&result) = RED_VAL_RGBA(p_col);
|
||||
}
|
||||
break;
|
||||
/* Grayscale */
|
||||
case ZM_SUBPIX_ORDER_NONE:
|
||||
result = p_col & 0xff;
|
||||
break;
|
||||
default:
|
||||
return p_col;
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // ZM_RGB_H
|
||||
|
|
|
@ -34,343 +34,343 @@ RtpCtrlThread::RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource ) : m
|
|||
|
||||
int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen )
|
||||
{
|
||||
const RtcpPacket *rtcpPacket;
|
||||
rtcpPacket = (RtcpPacket *)packet;
|
||||
const RtcpPacket *rtcpPacket;
|
||||
rtcpPacket = (RtcpPacket *)packet;
|
||||
|
||||
int consumed = 0;
|
||||
int consumed = 0;
|
||||
|
||||
//printf( "C: " );
|
||||
//for ( int i = 0; i < packetLen; i++ )
|
||||
//printf( "%02x ", (unsigned char)packet[i] );
|
||||
//printf( "\n" );
|
||||
int ver = rtcpPacket->header.version;
|
||||
int count = rtcpPacket->header.count;
|
||||
int pt = rtcpPacket->header.pt;
|
||||
int len = ntohs(rtcpPacket->header.lenN);
|
||||
//printf( "C: " );
|
||||
//for ( int i = 0; i < packetLen; i++ )
|
||||
//printf( "%02x ", (unsigned char)packet[i] );
|
||||
//printf( "\n" );
|
||||
int ver = rtcpPacket->header.version;
|
||||
int count = rtcpPacket->header.count;
|
||||
int pt = rtcpPacket->header.pt;
|
||||
int len = ntohs(rtcpPacket->header.lenN);
|
||||
|
||||
Debug( 5, "RTCP Ver: %d", ver );
|
||||
Debug( 5, "RTCP Count: %d", count );
|
||||
Debug( 5, "RTCP Pt: %d", pt );
|
||||
Debug( 5, "RTCP len: %d", len );
|
||||
Debug( 5, "RTCP Ver: %d", ver );
|
||||
Debug( 5, "RTCP Count: %d", count );
|
||||
Debug( 5, "RTCP Pt: %d", pt );
|
||||
Debug( 5, "RTCP len: %d", len );
|
||||
|
||||
switch( pt )
|
||||
switch( pt )
|
||||
{
|
||||
case RTCP_SR :
|
||||
{
|
||||
case RTCP_SR :
|
||||
{
|
||||
uint32_t ssrc = ntohl(rtcpPacket->body.sr.ssrcN);
|
||||
uint32_t ssrc = ntohl(rtcpPacket->body.sr.ssrcN);
|
||||
|
||||
Debug( 5, "RTCP Got SR (%x)", ssrc );
|
||||
if ( mRtpSource.getSsrc() )
|
||||
{
|
||||
if ( ssrc != mRtpSource.getSsrc() )
|
||||
{
|
||||
Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
else if ( ssrc )
|
||||
{
|
||||
mRtpSource.setSsrc( ssrc );
|
||||
}
|
||||
Debug( 5, "RTCP Got SR (%x)", ssrc );
|
||||
if ( mRtpSource.getSsrc() )
|
||||
{
|
||||
if ( ssrc != mRtpSource.getSsrc() )
|
||||
{
|
||||
Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
else if ( ssrc )
|
||||
{
|
||||
mRtpSource.setSsrc( ssrc );
|
||||
}
|
||||
|
||||
if ( len > 1 )
|
||||
{
|
||||
//printf( "NTPts:%d.%d, RTPts:%d\n", $ntptsmsb, $ntptslsb, $rtpts );
|
||||
uint16_t ntptsmsb = ntohl(rtcpPacket->body.sr.ntpSecN);
|
||||
uint16_t ntptslsb = ntohl(rtcpPacket->body.sr.ntpFracN);
|
||||
//printf( "NTPts:%x.%04x, RTPts:%x\n", $ntptsmsb, $ntptslsb, $rtpts );
|
||||
//printf( "Pkts:$sendpkts, Octs:$sendocts\n" );
|
||||
uint32_t rtpTime = ntohl(rtcpPacket->body.sr.rtpTsN);
|
||||
if ( len > 1 )
|
||||
{
|
||||
//printf( "NTPts:%d.%d, RTPts:%d\n", $ntptsmsb, $ntptslsb, $rtpts );
|
||||
uint16_t ntptsmsb = ntohl(rtcpPacket->body.sr.ntpSecN);
|
||||
uint16_t ntptslsb = ntohl(rtcpPacket->body.sr.ntpFracN);
|
||||
//printf( "NTPts:%x.%04x, RTPts:%x\n", $ntptsmsb, $ntptslsb, $rtpts );
|
||||
//printf( "Pkts:$sendpkts, Octs:$sendocts\n" );
|
||||
uint32_t rtpTime = ntohl(rtcpPacket->body.sr.rtpTsN);
|
||||
|
||||
mRtpSource.updateRtcpData( ntptsmsb, ntptslsb, rtpTime );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RTCP_SDES :
|
||||
{
|
||||
ssize_t contentLen = packetLen - sizeof(rtcpPacket->header);
|
||||
while ( contentLen )
|
||||
{
|
||||
Debug( 5, "RTCP CL: %zd", contentLen );
|
||||
uint32_t ssrc = ntohl(rtcpPacket->body.sdes.srcN);
|
||||
|
||||
Debug( 5, "RTCP Got SDES (%x), %d items", ssrc, count );
|
||||
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) )
|
||||
{
|
||||
Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
unsigned char *sdesPtr = (unsigned char *)&rtcpPacket->body.sdes.item;
|
||||
for ( int i = 0; i < count; i++ )
|
||||
{
|
||||
RtcpSdesItem *item = (RtcpSdesItem *)sdesPtr;
|
||||
Debug( 5, "RTCP Item length %d", item->len );
|
||||
switch( item->type )
|
||||
{
|
||||
case RTCP_SDES_CNAME :
|
||||
{
|
||||
std::string cname( item->data, item->len );
|
||||
Debug( 5, "RTCP Got CNAME %s", cname.c_str() );
|
||||
break;
|
||||
}
|
||||
case RTCP_SDES_END :
|
||||
case RTCP_SDES_NAME :
|
||||
case RTCP_SDES_EMAIL :
|
||||
case RTCP_SDES_PHONE :
|
||||
case RTCP_SDES_LOC :
|
||||
case RTCP_SDES_TOOL :
|
||||
case RTCP_SDES_NOTE :
|
||||
case RTCP_SDES_PRIV :
|
||||
default :
|
||||
{
|
||||
Error( "Received unexpected SDES item type %d, ignoring", item->type );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
int paddedLen = 4+2+item->len+1; // Add null byte
|
||||
paddedLen = (((paddedLen-1)/4)+1)*4; // Round to nearest multiple of 4
|
||||
Debug( 5, "RTCP PL:%d", paddedLen );
|
||||
sdesPtr += paddedLen;
|
||||
contentLen = ( paddedLen <= contentLen ) ? ( contentLen - paddedLen ) : 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RTCP_BYE :
|
||||
{
|
||||
Debug( 5, "RTCP Got BYE" );
|
||||
mStop = true;
|
||||
break;
|
||||
}
|
||||
case RTCP_APP :
|
||||
{
|
||||
// Ignoring as per RFC 3550
|
||||
Debug( 5, "Received RTCP_APP packet, ignoring.");
|
||||
break;
|
||||
}
|
||||
case RTCP_RR :
|
||||
{
|
||||
Error( "Received RTCP_RR packet." );
|
||||
return( -1 );
|
||||
}
|
||||
default :
|
||||
{
|
||||
// Ignore unknown packet types. Some cameras do this by design.
|
||||
Debug( 5, "Received unexpected packet type %d, ignoring", pt );
|
||||
break;
|
||||
}
|
||||
mRtpSource.updateRtcpData( ntptsmsb, ntptslsb, rtpTime );
|
||||
}
|
||||
break;
|
||||
}
|
||||
consumed = sizeof(uint32_t)*(len+1);
|
||||
return( consumed );
|
||||
case RTCP_SDES :
|
||||
{
|
||||
ssize_t contentLen = packetLen - sizeof(rtcpPacket->header);
|
||||
while ( contentLen )
|
||||
{
|
||||
Debug( 5, "RTCP CL: %zd", contentLen );
|
||||
uint32_t ssrc = ntohl(rtcpPacket->body.sdes.srcN);
|
||||
|
||||
Debug( 5, "RTCP Got SDES (%x), %d items", ssrc, count );
|
||||
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) )
|
||||
{
|
||||
Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
unsigned char *sdesPtr = (unsigned char *)&rtcpPacket->body.sdes.item;
|
||||
for ( int i = 0; i < count; i++ )
|
||||
{
|
||||
RtcpSdesItem *item = (RtcpSdesItem *)sdesPtr;
|
||||
Debug( 5, "RTCP Item length %d", item->len );
|
||||
switch( item->type )
|
||||
{
|
||||
case RTCP_SDES_CNAME :
|
||||
{
|
||||
std::string cname( item->data, item->len );
|
||||
Debug( 5, "RTCP Got CNAME %s", cname.c_str() );
|
||||
break;
|
||||
}
|
||||
case RTCP_SDES_END :
|
||||
case RTCP_SDES_NAME :
|
||||
case RTCP_SDES_EMAIL :
|
||||
case RTCP_SDES_PHONE :
|
||||
case RTCP_SDES_LOC :
|
||||
case RTCP_SDES_TOOL :
|
||||
case RTCP_SDES_NOTE :
|
||||
case RTCP_SDES_PRIV :
|
||||
default :
|
||||
{
|
||||
Error( "Received unexpected SDES item type %d, ignoring", item->type );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
int paddedLen = 4+2+item->len+1; // Add null byte
|
||||
paddedLen = (((paddedLen-1)/4)+1)*4; // Round to nearest multiple of 4
|
||||
Debug( 5, "RTCP PL:%d", paddedLen );
|
||||
sdesPtr += paddedLen;
|
||||
contentLen = ( paddedLen <= contentLen ) ? ( contentLen - paddedLen ) : 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RTCP_BYE :
|
||||
{
|
||||
Debug( 5, "RTCP Got BYE" );
|
||||
mStop = true;
|
||||
break;
|
||||
}
|
||||
case RTCP_APP :
|
||||
{
|
||||
// Ignoring as per RFC 3550
|
||||
Debug( 5, "Received RTCP_APP packet, ignoring.");
|
||||
break;
|
||||
}
|
||||
case RTCP_RR :
|
||||
{
|
||||
Error( "Received RTCP_RR packet." );
|
||||
return( -1 );
|
||||
}
|
||||
default :
|
||||
{
|
||||
// Ignore unknown packet types. Some cameras do this by design.
|
||||
Debug( 5, "Received unexpected packet type %d, ignoring", pt );
|
||||
break;
|
||||
}
|
||||
}
|
||||
consumed = sizeof(uint32_t)*(len+1);
|
||||
return( consumed );
|
||||
}
|
||||
|
||||
int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen )
|
||||
{
|
||||
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
|
||||
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
|
||||
|
||||
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.rr)+sizeof(rtcpPacket->body.rr.rr[0]);
|
||||
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
||||
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.rr)+sizeof(rtcpPacket->body.rr.rr[0]);
|
||||
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
||||
|
||||
rtcpPacket->header.version = RTP_VERSION;
|
||||
rtcpPacket->header.p = 0;
|
||||
rtcpPacket->header.pt = RTCP_RR;
|
||||
rtcpPacket->header.count = 1;
|
||||
rtcpPacket->header.lenN = htons(wordLen-1);
|
||||
rtcpPacket->header.version = RTP_VERSION;
|
||||
rtcpPacket->header.p = 0;
|
||||
rtcpPacket->header.pt = RTCP_RR;
|
||||
rtcpPacket->header.count = 1;
|
||||
rtcpPacket->header.lenN = htons(wordLen-1);
|
||||
|
||||
mRtpSource.updateRtcpStats();
|
||||
mRtpSource.updateRtcpStats();
|
||||
|
||||
Debug( 5, "Ssrc = %d", mRtspThread.getSsrc()+1 );
|
||||
Debug( 5, "Ssrc_1 = %d", mRtpSource.getSsrc() );
|
||||
Debug( 5, "Last Seq = %d", mRtpSource.getMaxSeq() );
|
||||
Debug( 5, "Jitter = %d", mRtpSource.getJitter() );
|
||||
Debug( 5, "Last SR = %d", mRtpSource.getLastSrTimestamp() );
|
||||
Debug( 5, "Ssrc = %d", mRtspThread.getSsrc()+1 );
|
||||
Debug( 5, "Ssrc_1 = %d", mRtpSource.getSsrc() );
|
||||
Debug( 5, "Last Seq = %d", mRtpSource.getMaxSeq() );
|
||||
Debug( 5, "Jitter = %d", mRtpSource.getJitter() );
|
||||
Debug( 5, "Last SR = %d", mRtpSource.getLastSrTimestamp() );
|
||||
|
||||
rtcpPacket->body.rr.ssrcN = htonl(mRtspThread.getSsrc()+1);
|
||||
rtcpPacket->body.rr.rr[0].ssrcN = htonl(mRtpSource.getSsrc());
|
||||
rtcpPacket->body.rr.rr[0].lost = mRtpSource.getLostPackets();
|
||||
rtcpPacket->body.rr.rr[0].fraction = mRtpSource.getLostFraction();
|
||||
rtcpPacket->body.rr.rr[0].lastSeqN = htonl(mRtpSource.getMaxSeq());
|
||||
rtcpPacket->body.rr.rr[0].jitterN = htonl(mRtpSource.getJitter());
|
||||
rtcpPacket->body.rr.rr[0].lsrN = htonl(mRtpSource.getLastSrTimestamp());
|
||||
rtcpPacket->body.rr.rr[0].dlsrN = 0;
|
||||
rtcpPacket->body.rr.ssrcN = htonl(mRtspThread.getSsrc()+1);
|
||||
rtcpPacket->body.rr.rr[0].ssrcN = htonl(mRtpSource.getSsrc());
|
||||
rtcpPacket->body.rr.rr[0].lost = mRtpSource.getLostPackets();
|
||||
rtcpPacket->body.rr.rr[0].fraction = mRtpSource.getLostFraction();
|
||||
rtcpPacket->body.rr.rr[0].lastSeqN = htonl(mRtpSource.getMaxSeq());
|
||||
rtcpPacket->body.rr.rr[0].jitterN = htonl(mRtpSource.getJitter());
|
||||
rtcpPacket->body.rr.rr[0].lsrN = htonl(mRtpSource.getLastSrTimestamp());
|
||||
rtcpPacket->body.rr.rr[0].dlsrN = 0;
|
||||
|
||||
return( wordLen*sizeof(uint32_t) );
|
||||
return( wordLen*sizeof(uint32_t) );
|
||||
}
|
||||
|
||||
int RtpCtrlThread::generateSdes( const unsigned char *packet, ssize_t packetLen )
|
||||
{
|
||||
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
|
||||
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
|
||||
|
||||
const std::string &cname = mRtpSource.getCname();
|
||||
const std::string &cname = mRtpSource.getCname();
|
||||
|
||||
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.sdes)+sizeof(rtcpPacket->body.sdes.item[0])+cname.size();
|
||||
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
||||
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.sdes)+sizeof(rtcpPacket->body.sdes.item[0])+cname.size();
|
||||
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
||||
|
||||
rtcpPacket->header.version = RTP_VERSION;
|
||||
rtcpPacket->header.p = 0;
|
||||
rtcpPacket->header.pt = RTCP_SDES;
|
||||
rtcpPacket->header.count = 1;
|
||||
rtcpPacket->header.lenN = htons(wordLen-1);
|
||||
rtcpPacket->header.version = RTP_VERSION;
|
||||
rtcpPacket->header.p = 0;
|
||||
rtcpPacket->header.pt = RTCP_SDES;
|
||||
rtcpPacket->header.count = 1;
|
||||
rtcpPacket->header.lenN = htons(wordLen-1);
|
||||
|
||||
rtcpPacket->body.sdes.srcN = htonl(mRtpSource.getSsrc()+1);
|
||||
rtcpPacket->body.sdes.item[0].type = RTCP_SDES_CNAME;
|
||||
rtcpPacket->body.sdes.item[0].len = cname.size();
|
||||
memcpy( rtcpPacket->body.sdes.item[0].data, cname.data(), cname.size() );
|
||||
rtcpPacket->body.sdes.srcN = htonl(mRtpSource.getSsrc()+1);
|
||||
rtcpPacket->body.sdes.item[0].type = RTCP_SDES_CNAME;
|
||||
rtcpPacket->body.sdes.item[0].len = cname.size();
|
||||
memcpy( rtcpPacket->body.sdes.item[0].data, cname.data(), cname.size() );
|
||||
|
||||
return( wordLen*sizeof(uint32_t) );
|
||||
return( wordLen*sizeof(uint32_t) );
|
||||
}
|
||||
|
||||
int RtpCtrlThread::generateBye( const unsigned char *packet, ssize_t packetLen )
|
||||
{
|
||||
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
|
||||
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
|
||||
|
||||
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.bye)+sizeof(rtcpPacket->body.bye.srcN[0]);
|
||||
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
||||
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.bye)+sizeof(rtcpPacket->body.bye.srcN[0]);
|
||||
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
||||
|
||||
rtcpPacket->header.version = RTP_VERSION;
|
||||
rtcpPacket->header.p = 0;
|
||||
rtcpPacket->header.pt = RTCP_BYE;
|
||||
rtcpPacket->header.count = 1;
|
||||
rtcpPacket->header.lenN = htons(wordLen-1);
|
||||
rtcpPacket->header.version = RTP_VERSION;
|
||||
rtcpPacket->header.p = 0;
|
||||
rtcpPacket->header.pt = RTCP_BYE;
|
||||
rtcpPacket->header.count = 1;
|
||||
rtcpPacket->header.lenN = htons(wordLen-1);
|
||||
|
||||
rtcpPacket->body.bye.srcN[0] = htonl(mRtpSource.getSsrc());
|
||||
rtcpPacket->body.bye.srcN[0] = htonl(mRtpSource.getSsrc());
|
||||
|
||||
return( wordLen*sizeof(uint32_t) );
|
||||
return( wordLen*sizeof(uint32_t) );
|
||||
}
|
||||
|
||||
int RtpCtrlThread::recvPackets( unsigned char *buffer, ssize_t nBytes )
|
||||
{
|
||||
unsigned char *bufferPtr = buffer;
|
||||
unsigned char *bufferPtr = buffer;
|
||||
|
||||
// u_int32 len; /* length of compound RTCP packet in words */
|
||||
// rtcp_t *r; /* RTCP header */
|
||||
// rtcp_t *end; /* end of compound RTCP packet */
|
||||
// u_int32 len; /* length of compound RTCP packet in words */
|
||||
// rtcp_t *r; /* RTCP header */
|
||||
// rtcp_t *end; /* end of compound RTCP packet */
|
||||
|
||||
// if ((*(u_int16 *)r & RTCP_VALID_MASK) != RTCP_VALID_VALUE) {
|
||||
// /* something wrong with packet format */
|
||||
// }
|
||||
// end = (rtcp_t *)((u_int32 *)r + len);
|
||||
// if ((*(u_int16 *)r & RTCP_VALID_MASK) != RTCP_VALID_VALUE) {
|
||||
// /* something wrong with packet format */
|
||||
// }
|
||||
// end = (rtcp_t *)((u_int32 *)r + len);
|
||||
|
||||
// do r = (rtcp_t *)((u_int32 *)r + r->common.length + 1);
|
||||
// while (r < end && r->common.version == 2);
|
||||
// do r = (rtcp_t *)((u_int32 *)r + r->common.length + 1);
|
||||
// while (r < end && r->common.version == 2);
|
||||
|
||||
// if (r != end) {
|
||||
// /* something wrong with packet format */
|
||||
// }
|
||||
// if (r != end) {
|
||||
// /* something wrong with packet format */
|
||||
// }
|
||||
|
||||
while ( nBytes > 0 )
|
||||
{
|
||||
int consumed = recvPacket( bufferPtr, nBytes );
|
||||
if ( consumed <= 0 )
|
||||
break;
|
||||
bufferPtr += consumed;
|
||||
nBytes -= consumed;
|
||||
}
|
||||
return( nBytes );
|
||||
while ( nBytes > 0 )
|
||||
{
|
||||
int consumed = recvPacket( bufferPtr, nBytes );
|
||||
if ( consumed <= 0 )
|
||||
break;
|
||||
bufferPtr += consumed;
|
||||
nBytes -= consumed;
|
||||
}
|
||||
return( nBytes );
|
||||
}
|
||||
|
||||
int RtpCtrlThread::run()
|
||||
{
|
||||
Debug( 2, "Starting control thread %x on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalCtrlPort() );
|
||||
SockAddrInet localAddr, remoteAddr;
|
||||
Debug( 2, "Starting control thread %x on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalCtrlPort() );
|
||||
SockAddrInet localAddr, remoteAddr;
|
||||
|
||||
bool sendReports;
|
||||
UdpInetSocket rtpCtrlServer;
|
||||
if ( mRtpSource.getLocalHost() != "" )
|
||||
bool sendReports;
|
||||
UdpInetSocket rtpCtrlServer;
|
||||
if ( mRtpSource.getLocalHost() != "" )
|
||||
{
|
||||
localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort(), "udp" );
|
||||
if ( !rtpCtrlServer.bind( localAddr ) )
|
||||
Fatal( "Failed to bind RTCP server" );
|
||||
sendReports = false;
|
||||
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
|
||||
}
|
||||
else
|
||||
{
|
||||
localAddr.resolve( mRtpSource.getLocalCtrlPort(), "udp" );
|
||||
if ( !rtpCtrlServer.bind( localAddr ) )
|
||||
Fatal( "Failed to bind RTCP server" );
|
||||
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
|
||||
remoteAddr.resolve( mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort(), "udp" );
|
||||
if ( !rtpCtrlServer.connect( remoteAddr ) )
|
||||
Fatal( "Failed to connect RTCP server" );
|
||||
Debug( 3, "Connected to %s:%d", mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort() );
|
||||
sendReports = true;
|
||||
}
|
||||
|
||||
// The only reason I can think of why we would have a timeout period is so that we can regularly send RR packets.
|
||||
// Why 10 seconds? If anything I think this should be whatever timeout value was given in the DESCRIBE response
|
||||
Select select( 10 );
|
||||
select.addReader( &rtpCtrlServer );
|
||||
|
||||
unsigned char buffer[ZM_NETWORK_BUFSIZ];
|
||||
|
||||
time_t last_receive = time(NULL);
|
||||
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 ( !mStop && select.wait() >= 0 ) {
|
||||
|
||||
time_t now = time(NULL);
|
||||
Select::CommsList readable = select.getReadable();
|
||||
if ( readable.size() == 0 )
|
||||
{
|
||||
localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort(), "udp" );
|
||||
if ( !rtpCtrlServer.bind( localAddr ) )
|
||||
Fatal( "Failed to bind RTCP server" );
|
||||
sendReports = false;
|
||||
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
|
||||
if ( ! timeout ) {
|
||||
// With this code here, we will send an SDES and RR packet every 10 seconds
|
||||
ssize_t nBytes;
|
||||
unsigned char *bufferPtr = buffer;
|
||||
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
Debug( 3, "Preventing timeout by sending %zd bytes on sd %d. Time since last receive: %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc(), ( now-last_receive) );
|
||||
if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 )
|
||||
Error( "Unable to send: %s", strerror( errno ) );
|
||||
timeout = true;
|
||||
continue;
|
||||
} else {
|
||||
//Error( "RTCP timed out" );
|
||||
Debug(1, "RTCP timed out. Time since last receive: %d", ( now-last_receive) );
|
||||
continue;
|
||||
//break;
|
||||
}
|
||||
} else {
|
||||
timeout = false;
|
||||
last_receive = time(NULL);
|
||||
}
|
||||
else
|
||||
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); iter++ )
|
||||
{
|
||||
localAddr.resolve( mRtpSource.getLocalCtrlPort(), "udp" );
|
||||
if ( !rtpCtrlServer.bind( localAddr ) )
|
||||
Fatal( "Failed to bind RTCP server" );
|
||||
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
|
||||
remoteAddr.resolve( mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort(), "udp" );
|
||||
if ( !rtpCtrlServer.connect( remoteAddr ) )
|
||||
Fatal( "Failed to connect RTCP server" );
|
||||
Debug( 3, "Connected to %s:%d", mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort() );
|
||||
sendReports = true;
|
||||
}
|
||||
if ( UdpInetSocket *socket = dynamic_cast<UdpInetSocket *>(*iter) )
|
||||
{
|
||||
ssize_t nBytes = socket->recv( buffer, sizeof(buffer) );
|
||||
Debug( 4, "Read %zd bytes on sd %d", nBytes, socket->getReadDesc() );
|
||||
|
||||
// The only reason I can think of why we would have a timeout period is so that we can regularly send RR packets.
|
||||
// Why 10 seconds? If anything I think this should be whatever timeout value was given in the DESCRIBE response
|
||||
Select select( 10 );
|
||||
select.addReader( &rtpCtrlServer );
|
||||
|
||||
unsigned char buffer[ZM_NETWORK_BUFSIZ];
|
||||
|
||||
time_t last_receive = time(NULL);
|
||||
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 ( !mStop && select.wait() >= 0 ) {
|
||||
|
||||
time_t now = time(NULL);
|
||||
Select::CommsList readable = select.getReadable();
|
||||
if ( readable.size() == 0 )
|
||||
if ( nBytes )
|
||||
{
|
||||
if ( ! timeout ) {
|
||||
// With this code here, we will send an SDES and RR packet every 10 seconds
|
||||
ssize_t nBytes;
|
||||
unsigned char *bufferPtr = buffer;
|
||||
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
Debug( 3, "Preventing timeout by sending %zd bytes on sd %d. Time since last receive: %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc(), ( now-last_receive) );
|
||||
if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 )
|
||||
Error( "Unable to send: %s", strerror( errno ) );
|
||||
timeout = true;
|
||||
continue;
|
||||
} else {
|
||||
//Error( "RTCP timed out" );
|
||||
Debug(1, "RTCP timed out. Time since last receive: %d", ( now-last_receive) );
|
||||
continue;
|
||||
//break;
|
||||
}
|
||||
} else {
|
||||
timeout = false;
|
||||
last_receive = time(NULL);
|
||||
}
|
||||
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); iter++ )
|
||||
{
|
||||
if ( UdpInetSocket *socket = dynamic_cast<UdpInetSocket *>(*iter) )
|
||||
{
|
||||
ssize_t nBytes = socket->recv( buffer, sizeof(buffer) );
|
||||
Debug( 4, "Read %zd bytes on sd %d", nBytes, socket->getReadDesc() );
|
||||
recvPackets( buffer, nBytes );
|
||||
|
||||
if ( nBytes )
|
||||
{
|
||||
recvPackets( buffer, nBytes );
|
||||
|
||||
if ( sendReports )
|
||||
{
|
||||
unsigned char *bufferPtr = buffer;
|
||||
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
Debug( 3, "Sending %zd bytes on sd %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc() );
|
||||
if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 )
|
||||
Error( "Unable to send: %s", strerror( errno ) );
|
||||
//Debug( 4, "Sent %d bytes on sd %d", nBytes, rtpCtrlServer.getWriteDesc() );
|
||||
}
|
||||
} else {
|
||||
// Here is another case of not receiving some data causing us to terminate... why? Sometimes there are pauses in the interwebs.
|
||||
mStop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Panic( "Barfed" );
|
||||
}
|
||||
if ( sendReports )
|
||||
{
|
||||
unsigned char *bufferPtr = buffer;
|
||||
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
Debug( 3, "Sending %zd bytes on sd %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc() );
|
||||
if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 )
|
||||
Error( "Unable to send: %s", strerror( errno ) );
|
||||
//Debug( 4, "Sent %d bytes on sd %d", nBytes, rtpCtrlServer.getWriteDesc() );
|
||||
}
|
||||
} else {
|
||||
// Here is another case of not receiving some data causing us to terminate... why? Sometimes there are pauses in the interwebs.
|
||||
mStop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Panic( "Barfed" );
|
||||
}
|
||||
}
|
||||
rtpCtrlServer.close();
|
||||
mRtspThread.stop();
|
||||
return( 0 );
|
||||
}
|
||||
rtpCtrlServer.close();
|
||||
mRtspThread.stop();
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBAVFORMAT
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include "zm_thread.h"
|
||||
|
||||
// Defined in ffmpeg rtp.h
|
||||
//#define RTP_MAX_SDES 255 // maximum text length for SDES
|
||||
//#define RTP_MAX_SDES 255 // maximum text length for SDES
|
||||
|
||||
// Big-endian mask for version, padding bit and packet type pair
|
||||
#define RTCP_VALID_MASK (0xc000 | 0x2000 | 0xfe)
|
||||
|
@ -39,119 +39,119 @@ class RtpCtrlThread : public Thread
|
|||
friend class RtspThread;
|
||||
|
||||
private:
|
||||
typedef enum
|
||||
typedef enum
|
||||
{
|
||||
RTCP_SR = 200,
|
||||
RTCP_RR = 201,
|
||||
RTCP_SDES = 202,
|
||||
RTCP_BYE = 203,
|
||||
RTCP_APP = 204
|
||||
} RtcpType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
RTCP_SDES_END = 0,
|
||||
RTCP_SDES_CNAME = 1,
|
||||
RTCP_SDES_NAME = 2,
|
||||
RTCP_SDES_EMAIL = 3,
|
||||
RTCP_SDES_PHONE = 4,
|
||||
RTCP_SDES_LOC = 5,
|
||||
RTCP_SDES_TOOL = 6,
|
||||
RTCP_SDES_NOTE = 7,
|
||||
RTCP_SDES_PRIV = 8
|
||||
} RtcpSdesType;
|
||||
|
||||
struct RtcpCommonHeader
|
||||
{
|
||||
uint8_t count:5; // varies by packet type
|
||||
uint8_t p:1; // padding flag
|
||||
uint8_t version:2; // protocol version
|
||||
uint8_t pt; // RTCP packet type
|
||||
uint16_t lenN; // pkt len in words, w/o this word, network order
|
||||
};
|
||||
|
||||
// Reception report block
|
||||
struct RtcpRr
|
||||
{
|
||||
uint32_t ssrcN; // data source being reported
|
||||
int32_t lost:24; // cumul. no. pkts lost (signed!)
|
||||
uint32_t fraction:8; // fraction lost since last SR/RR
|
||||
uint32_t lastSeqN; // extended last seq. no. received, network order
|
||||
uint32_t jitterN; // interarrival jitter, network order
|
||||
uint32_t lsrN; // last SR packet from this source, network order
|
||||
uint32_t dlsrN; // delay since last SR packet, network order
|
||||
};
|
||||
|
||||
// SDES item
|
||||
struct RtcpSdesItem
|
||||
{
|
||||
uint8_t type; // type of item (rtcp_sdes_type_t)
|
||||
uint8_t len; // length of item (in octets)
|
||||
char data[]; // text, not null-terminated
|
||||
};
|
||||
|
||||
// RTCP packet
|
||||
struct RtcpPacket
|
||||
{
|
||||
RtcpCommonHeader header; // common header
|
||||
union
|
||||
{
|
||||
RTCP_SR = 200,
|
||||
RTCP_RR = 201,
|
||||
RTCP_SDES = 202,
|
||||
RTCP_BYE = 203,
|
||||
RTCP_APP = 204
|
||||
} RtcpType;
|
||||
// Sender Report (SR)
|
||||
struct Sr
|
||||
{
|
||||
uint32_t ssrcN; // sender generating this report, network order
|
||||
uint32_t ntpSecN; // NTP timestamp, network order
|
||||
uint32_t ntpFracN;
|
||||
uint32_t rtpTsN; // RTP timestamp, network order
|
||||
uint32_t pSentN; // packets sent, network order
|
||||
uint32_t oSentN; // octets sent, network order
|
||||
RtcpRr rr[]; // variable-length list
|
||||
} sr;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
RTCP_SDES_END = 0,
|
||||
RTCP_SDES_CNAME = 1,
|
||||
RTCP_SDES_NAME = 2,
|
||||
RTCP_SDES_EMAIL = 3,
|
||||
RTCP_SDES_PHONE = 4,
|
||||
RTCP_SDES_LOC = 5,
|
||||
RTCP_SDES_TOOL = 6,
|
||||
RTCP_SDES_NOTE = 7,
|
||||
RTCP_SDES_PRIV = 8
|
||||
} RtcpSdesType;
|
||||
// Reception Report (RR)
|
||||
struct Rr
|
||||
{
|
||||
uint32_t ssrcN; // receiver generating this report
|
||||
RtcpRr rr[]; // variable-length list
|
||||
} rr;
|
||||
|
||||
struct RtcpCommonHeader
|
||||
{
|
||||
uint8_t count:5; // varies by packet type
|
||||
uint8_t p:1; // padding flag
|
||||
uint8_t version:2; // protocol version
|
||||
uint8_t pt; // RTCP packet type
|
||||
uint16_t lenN; // pkt len in words, w/o this word, network order
|
||||
};
|
||||
// source description (SDES)
|
||||
struct Sdes
|
||||
{
|
||||
uint32_t srcN; // first SSRC/CSRC
|
||||
RtcpSdesItem item[]; // list of SDES items
|
||||
} sdes;
|
||||
|
||||
// Reception report block
|
||||
struct RtcpRr
|
||||
{
|
||||
uint32_t ssrcN; // data source being reported
|
||||
int32_t lost:24; // cumul. no. pkts lost (signed!)
|
||||
uint32_t fraction:8; // fraction lost since last SR/RR
|
||||
uint32_t lastSeqN; // extended last seq. no. received, network order
|
||||
uint32_t jitterN; // interarrival jitter, network order
|
||||
uint32_t lsrN; // last SR packet from this source, network order
|
||||
uint32_t dlsrN; // delay since last SR packet, network order
|
||||
};
|
||||
|
||||
// SDES item
|
||||
struct RtcpSdesItem
|
||||
{
|
||||
uint8_t type; // type of item (rtcp_sdes_type_t)
|
||||
uint8_t len; // length of item (in octets)
|
||||
char data[]; // text, not null-terminated
|
||||
};
|
||||
|
||||
// RTCP packet
|
||||
struct RtcpPacket
|
||||
{
|
||||
RtcpCommonHeader header; // common header
|
||||
union
|
||||
{
|
||||
// Sender Report (SR)
|
||||
struct Sr
|
||||
{
|
||||
uint32_t ssrcN; // sender generating this report, network order
|
||||
uint32_t ntpSecN; // NTP timestamp, network order
|
||||
uint32_t ntpFracN;
|
||||
uint32_t rtpTsN; // RTP timestamp, network order
|
||||
uint32_t pSentN; // packets sent, network order
|
||||
uint32_t oSentN; // octets sent, network order
|
||||
RtcpRr rr[]; // variable-length list
|
||||
} sr;
|
||||
|
||||
// Reception Report (RR)
|
||||
struct Rr
|
||||
{
|
||||
uint32_t ssrcN; // receiver generating this report
|
||||
RtcpRr rr[]; // variable-length list
|
||||
} rr;
|
||||
|
||||
// source description (SDES)
|
||||
struct Sdes
|
||||
{
|
||||
uint32_t srcN; // first SSRC/CSRC
|
||||
RtcpSdesItem item[]; // list of SDES items
|
||||
} sdes;
|
||||
|
||||
// BYE
|
||||
struct Bye
|
||||
{
|
||||
uint32_t srcN[]; // list of sources
|
||||
// can't express trailing text for reason (what does this mean? it's not even english!)
|
||||
} bye;
|
||||
} body;
|
||||
};
|
||||
// BYE
|
||||
struct Bye
|
||||
{
|
||||
uint32_t srcN[]; // list of sources
|
||||
// can't express trailing text for reason (what does this mean? it's not even english!)
|
||||
} bye;
|
||||
} body;
|
||||
};
|
||||
|
||||
private:
|
||||
RtspThread &mRtspThread;
|
||||
RtpSource &mRtpSource;
|
||||
int mPort;
|
||||
bool mStop;
|
||||
RtspThread &mRtspThread;
|
||||
RtpSource &mRtpSource;
|
||||
int mPort;
|
||||
bool mStop;
|
||||
|
||||
private:
|
||||
int recvPacket( const unsigned char *packet, ssize_t packetLen );
|
||||
int generateRr( const unsigned char *packet, ssize_t packetLen );
|
||||
int generateSdes( const unsigned char *packet, ssize_t packetLen );
|
||||
int generateBye( const unsigned char *packet, ssize_t packetLen );
|
||||
int recvPackets( unsigned char *buffer, ssize_t nBytes );
|
||||
int run();
|
||||
int recvPacket( const unsigned char *packet, ssize_t packetLen );
|
||||
int generateRr( const unsigned char *packet, ssize_t packetLen );
|
||||
int generateSdes( const unsigned char *packet, ssize_t packetLen );
|
||||
int generateBye( const unsigned char *packet, ssize_t packetLen );
|
||||
int recvPackets( unsigned char *buffer, ssize_t nBytes );
|
||||
int run();
|
||||
|
||||
public:
|
||||
RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource );
|
||||
RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource );
|
||||
|
||||
void stop()
|
||||
{
|
||||
mStop = true;
|
||||
}
|
||||
void stop()
|
||||
{
|
||||
mStop = true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ZM_RTP_CTRL_H
|
||||
|
|
|
@ -33,88 +33,88 @@ RtpDataThread::RtpDataThread( RtspThread &rtspThread, RtpSource &rtpSource ) : m
|
|||
|
||||
bool RtpDataThread::recvPacket( const unsigned char *packet, size_t packetLen )
|
||||
{
|
||||
const RtpDataHeader *rtpHeader;
|
||||
rtpHeader = (RtpDataHeader *)packet;
|
||||
const RtpDataHeader *rtpHeader;
|
||||
rtpHeader = (RtpDataHeader *)packet;
|
||||
|
||||
//printf( "D: " );
|
||||
//for ( int i = 0; i < 32; i++ )
|
||||
//printf( "%02x ", (unsigned char)packet[i] );
|
||||
//printf( "\n" );
|
||||
//printf( "D: " );
|
||||
//for ( int i = 0; i < 32; i++ )
|
||||
//printf( "%02x ", (unsigned char)packet[i] );
|
||||
//printf( "\n" );
|
||||
|
||||
Debug( 5, "Ver: %d", rtpHeader->version );
|
||||
Debug( 5, "P: %d", rtpHeader->p );
|
||||
Debug( 5, "Pt: %d", rtpHeader->pt );
|
||||
Debug( 5, "Mk: %d", rtpHeader->m );
|
||||
Debug( 5, "Seq: %d", ntohs(rtpHeader->seqN) );
|
||||
Debug( 5, "T/S: %x", ntohl(rtpHeader->timestampN) );
|
||||
Debug( 5, "SSRC: %x", ntohl(rtpHeader->ssrcN) );
|
||||
Debug( 5, "Ver: %d", rtpHeader->version );
|
||||
Debug( 5, "P: %d", rtpHeader->p );
|
||||
Debug( 5, "Pt: %d", rtpHeader->pt );
|
||||
Debug( 5, "Mk: %d", rtpHeader->m );
|
||||
Debug( 5, "Seq: %d", ntohs(rtpHeader->seqN) );
|
||||
Debug( 5, "T/S: %x", ntohl(rtpHeader->timestampN) );
|
||||
Debug( 5, "SSRC: %x", ntohl(rtpHeader->ssrcN) );
|
||||
|
||||
//unsigned short seq = ntohs(rtpHeader->seqN);
|
||||
unsigned long ssrc = ntohl(rtpHeader->ssrcN);
|
||||
//unsigned short seq = ntohs(rtpHeader->seqN);
|
||||
unsigned long ssrc = ntohl(rtpHeader->ssrcN);
|
||||
|
||||
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) )
|
||||
{
|
||||
Warning( "Discarding packet for unrecognised ssrc %lx", ssrc );
|
||||
return( false );
|
||||
}
|
||||
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) )
|
||||
{
|
||||
Warning( "Discarding packet for unrecognised ssrc %lx", ssrc );
|
||||
return( false );
|
||||
}
|
||||
|
||||
return( mRtpSource.handlePacket( packet, packetLen ) );
|
||||
return( mRtpSource.handlePacket( packet, packetLen ) );
|
||||
}
|
||||
|
||||
int RtpDataThread::run()
|
||||
{
|
||||
Debug( 2, "Starting data thread %d on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalDataPort() );
|
||||
Debug( 2, "Starting data thread %d on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalDataPort() );
|
||||
|
||||
SockAddrInet localAddr;
|
||||
UdpInetServer rtpDataSocket;
|
||||
if ( mRtpSource.getLocalHost() != "" )
|
||||
localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort(), "udp" );
|
||||
else
|
||||
localAddr.resolve( mRtpSource.getLocalDataPort(), "udp" );
|
||||
if ( !rtpDataSocket.bind( localAddr ) )
|
||||
Fatal( "Failed to bind RTP server" );
|
||||
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort() );
|
||||
SockAddrInet localAddr;
|
||||
UdpInetServer rtpDataSocket;
|
||||
if ( mRtpSource.getLocalHost() != "" )
|
||||
localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort(), "udp" );
|
||||
else
|
||||
localAddr.resolve( mRtpSource.getLocalDataPort(), "udp" );
|
||||
if ( !rtpDataSocket.bind( localAddr ) )
|
||||
Fatal( "Failed to bind RTP server" );
|
||||
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort() );
|
||||
|
||||
Select select( 3 );
|
||||
select.addReader( &rtpDataSocket );
|
||||
Select select( 3 );
|
||||
select.addReader( &rtpDataSocket );
|
||||
|
||||
unsigned char buffer[ZM_NETWORK_BUFSIZ];
|
||||
while ( !mStop && select.wait() >= 0 )
|
||||
{
|
||||
if ( mStop )
|
||||
break;
|
||||
Select::CommsList readable = select.getReadable();
|
||||
if ( readable.size() == 0 )
|
||||
unsigned char buffer[ZM_NETWORK_BUFSIZ];
|
||||
while ( !mStop && select.wait() >= 0 )
|
||||
{
|
||||
if ( mStop )
|
||||
break;
|
||||
Select::CommsList readable = select.getReadable();
|
||||
if ( readable.size() == 0 )
|
||||
{
|
||||
Error( "RTP timed out" );
|
||||
mStop = true;
|
||||
break;
|
||||
}
|
||||
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); iter++ )
|
||||
{
|
||||
if ( UdpInetServer *socket = dynamic_cast<UdpInetServer *>(*iter) )
|
||||
{
|
||||
int nBytes = socket->recv( buffer, sizeof(buffer) );
|
||||
Debug( 4, "Got %d bytes on sd %d", nBytes, socket->getReadDesc() );
|
||||
if ( nBytes )
|
||||
{
|
||||
Error( "RTP timed out" );
|
||||
mStop = true;
|
||||
break;
|
||||
recvPacket( buffer, nBytes );
|
||||
}
|
||||
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); iter++ )
|
||||
else
|
||||
{
|
||||
if ( UdpInetServer *socket = dynamic_cast<UdpInetServer *>(*iter) )
|
||||
{
|
||||
int nBytes = socket->recv( buffer, sizeof(buffer) );
|
||||
Debug( 4, "Got %d bytes on sd %d", nBytes, socket->getReadDesc() );
|
||||
if ( nBytes )
|
||||
{
|
||||
recvPacket( buffer, nBytes );
|
||||
}
|
||||
else
|
||||
{
|
||||
mStop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Panic( "Barfed" );
|
||||
}
|
||||
mStop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rtpDataSocket.close();
|
||||
mRtspThread.stop();
|
||||
return( 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
Panic( "Barfed" );
|
||||
}
|
||||
}
|
||||
}
|
||||
rtpDataSocket.close();
|
||||
mRtspThread.stop();
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBAVFORMAT
|
||||
|
|
|
@ -30,16 +30,16 @@ class RtpSource;
|
|||
|
||||
struct RtpDataHeader
|
||||
{
|
||||
uint8_t cc:4; // CSRC count
|
||||
uint8_t x:1; // header extension flag
|
||||
uint8_t p:1; // padding flag
|
||||
uint8_t version:2; // protocol version
|
||||
uint8_t pt:7; // payload type
|
||||
uint8_t m:1; // marker bit
|
||||
uint16_t seqN; // sequence number, network order
|
||||
uint32_t timestampN; // timestamp, network order
|
||||
uint32_t ssrcN; // synchronization source, network order
|
||||
uint32_t csrc[]; // optional CSRC list
|
||||
uint8_t cc:4; // CSRC count
|
||||
uint8_t x:1; // header extension flag
|
||||
uint8_t p:1; // padding flag
|
||||
uint8_t version:2; // protocol version
|
||||
uint8_t pt:7; // payload type
|
||||
uint8_t m:1; // marker bit
|
||||
uint16_t seqN; // sequence number, network order
|
||||
uint32_t timestampN; // timestamp, network order
|
||||
uint32_t ssrcN; // synchronization source, network order
|
||||
uint32_t csrc[]; // optional CSRC list
|
||||
};
|
||||
|
||||
class RtpDataThread : public Thread
|
||||
|
@ -47,21 +47,21 @@ class RtpDataThread : public Thread
|
|||
friend class RtspThread;
|
||||
|
||||
private:
|
||||
RtspThread &mRtspThread;
|
||||
RtpSource &mRtpSource;
|
||||
bool mStop;
|
||||
RtspThread &mRtspThread;
|
||||
RtpSource &mRtpSource;
|
||||
bool mStop;
|
||||
|
||||
private:
|
||||
bool recvPacket( const unsigned char *packet, size_t packetLen );
|
||||
int run();
|
||||
bool recvPacket( const unsigned char *packet, size_t packetLen );
|
||||
int run();
|
||||
|
||||
public:
|
||||
RtpDataThread( RtspThread &rtspThread, RtpSource &rtpSource );
|
||||
RtpDataThread( RtspThread &rtspThread, RtpSource &rtpSource );
|
||||
|
||||
void stop()
|
||||
{
|
||||
mStop = true;
|
||||
}
|
||||
void stop()
|
||||
{
|
||||
mStop = true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ZM_RTP_DATA_H
|
||||
|
|
|
@ -35,152 +35,152 @@ struct RtpDataHeader;
|
|||
class RtpSource
|
||||
{
|
||||
public:
|
||||
typedef enum { EMPTY, FILLING, READY } FrameState;
|
||||
typedef enum { EMPTY, FILLING, READY } FrameState;
|
||||
private:
|
||||
static const int RTP_SEQ_MOD = 1<<16;
|
||||
static const int MAX_DROPOUT = 3000;
|
||||
static const int MAX_MISORDER = 100;
|
||||
static const int MIN_SEQUENTIAL = 2;
|
||||
static const int RTP_SEQ_MOD = 1<<16;
|
||||
static const int MAX_DROPOUT = 3000;
|
||||
static const int MAX_MISORDER = 100;
|
||||
static const int MIN_SEQUENTIAL = 2;
|
||||
|
||||
private:
|
||||
// Identity
|
||||
int mId; // General id (usually monitor id)
|
||||
std::string mCname; // Canonical name, for SDES
|
||||
// Identity
|
||||
int mId; // General id (usually monitor id)
|
||||
std::string mCname; // Canonical name, for SDES
|
||||
|
||||
// RTP/RTCP fields
|
||||
uint32_t mSsrc;
|
||||
uint16_t mMaxSeq; // highest seq. number seen
|
||||
uint32_t mCycles; // shifted count of seq. number cycles
|
||||
uint32_t mBaseSeq; // base seq number
|
||||
uint32_t mBadSeq; // last 'bad' seq number + 1
|
||||
uint32_t mProbation; // sequ. packets till source is valid
|
||||
uint32_t mReceivedPackets; // packets received
|
||||
uint32_t mExpectedPrior; // packet expected at last interval
|
||||
uint32_t mReceivedPrior; // packet received at last interval
|
||||
uint32_t mTransit; // relative trans time for prev pkt
|
||||
uint32_t mJitter; // estimated jitter
|
||||
// RTP/RTCP fields
|
||||
uint32_t mSsrc;
|
||||
uint16_t mMaxSeq; // highest seq. number seen
|
||||
uint32_t mCycles; // shifted count of seq. number cycles
|
||||
uint32_t mBaseSeq; // base seq number
|
||||
uint32_t mBadSeq; // last 'bad' seq number + 1
|
||||
uint32_t mProbation; // sequ. packets till source is valid
|
||||
uint32_t mReceivedPackets; // packets received
|
||||
uint32_t mExpectedPrior; // packet expected at last interval
|
||||
uint32_t mReceivedPrior; // packet received at last interval
|
||||
uint32_t mTransit; // relative trans time for prev pkt
|
||||
uint32_t mJitter; // estimated jitter
|
||||
|
||||
// Ports/Channels
|
||||
std::string mLocalHost;
|
||||
int mLocalPortChans[2];
|
||||
std::string mRemoteHost;
|
||||
int mRemotePortChans[2];
|
||||
// Ports/Channels
|
||||
std::string mLocalHost;
|
||||
int mLocalPortChans[2];
|
||||
std::string mRemoteHost;
|
||||
int mRemotePortChans[2];
|
||||
|
||||
// Time keys
|
||||
uint32_t mRtpClock;
|
||||
uint32_t mRtpFactor;
|
||||
struct timeval mBaseTimeReal;
|
||||
struct timeval mBaseTimeNtp;
|
||||
uint32_t mBaseTimeRtp;
|
||||
// Time keys
|
||||
uint32_t mRtpClock;
|
||||
uint32_t mRtpFactor;
|
||||
struct timeval mBaseTimeReal;
|
||||
struct timeval mBaseTimeNtp;
|
||||
uint32_t mBaseTimeRtp;
|
||||
|
||||
struct timeval mLastSrTimeReal;
|
||||
uint32_t mLastSrTimeNtpSecs;
|
||||
uint32_t mLastSrTimeNtpFrac;
|
||||
struct timeval mLastSrTimeNtp;
|
||||
uint32_t mLastSrTimeRtp;
|
||||
struct timeval mLastSrTimeReal;
|
||||
uint32_t mLastSrTimeNtpSecs;
|
||||
uint32_t mLastSrTimeNtpFrac;
|
||||
struct timeval mLastSrTimeNtp;
|
||||
uint32_t mLastSrTimeRtp;
|
||||
|
||||
// Stats, intermittently updated
|
||||
uint32_t mExpectedPackets;
|
||||
uint32_t mLostPackets;
|
||||
uint8_t mLostFraction;
|
||||
// Stats, intermittently updated
|
||||
uint32_t mExpectedPackets;
|
||||
uint32_t mLostPackets;
|
||||
uint8_t mLostFraction;
|
||||
|
||||
_AVCODECID mCodecId;
|
||||
_AVCODECID mCodecId;
|
||||
|
||||
Buffer mFrame;
|
||||
int mFrameCount;
|
||||
bool mFrameGood;
|
||||
bool prevM;
|
||||
ThreadData<bool> mFrameReady;
|
||||
ThreadData<bool> mFrameProcessed;
|
||||
Buffer mFrame;
|
||||
int mFrameCount;
|
||||
bool mFrameGood;
|
||||
bool prevM;
|
||||
ThreadData<bool> mFrameReady;
|
||||
ThreadData<bool> mFrameProcessed;
|
||||
|
||||
private:
|
||||
void init( uint16_t seq );
|
||||
void init( uint16_t seq );
|
||||
|
||||
public:
|
||||
RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId );
|
||||
RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId );
|
||||
|
||||
bool updateSeq( uint16_t seq );
|
||||
void updateJitter( const RtpDataHeader *header );
|
||||
void updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime );
|
||||
void updateRtcpStats();
|
||||
bool updateSeq( uint16_t seq );
|
||||
void updateJitter( const RtpDataHeader *header );
|
||||
void updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime );
|
||||
void updateRtcpStats();
|
||||
|
||||
bool handlePacket( const unsigned char *packet, size_t packetLen );
|
||||
bool handlePacket( const unsigned char *packet, size_t packetLen );
|
||||
|
||||
uint32_t getSsrc() const
|
||||
{
|
||||
return( mSsrc );
|
||||
}
|
||||
void setSsrc( uint32_t ssrc )
|
||||
{
|
||||
mSsrc = ssrc;
|
||||
}
|
||||
uint32_t getSsrc() const
|
||||
{
|
||||
return( mSsrc );
|
||||
}
|
||||
void setSsrc( uint32_t ssrc )
|
||||
{
|
||||
mSsrc = ssrc;
|
||||
}
|
||||
|
||||
bool getFrame( Buffer &buffer );
|
||||
bool getFrame( Buffer &buffer );
|
||||
|
||||
const std::string &getCname() const
|
||||
{
|
||||
return( mCname );
|
||||
}
|
||||
const std::string &getCname() const
|
||||
{
|
||||
return( mCname );
|
||||
}
|
||||
|
||||
const std::string &getLocalHost() const
|
||||
{
|
||||
return( mLocalHost );
|
||||
}
|
||||
const std::string &getLocalHost() const
|
||||
{
|
||||
return( mLocalHost );
|
||||
}
|
||||
|
||||
int getLocalDataPort() const
|
||||
{
|
||||
return( mLocalPortChans[0] );
|
||||
}
|
||||
int getLocalDataPort() const
|
||||
{
|
||||
return( mLocalPortChans[0] );
|
||||
}
|
||||
|
||||
int getLocalCtrlPort() const
|
||||
{
|
||||
return( mLocalPortChans[1] );
|
||||
}
|
||||
int getLocalCtrlPort() const
|
||||
{
|
||||
return( mLocalPortChans[1] );
|
||||
}
|
||||
|
||||
const std::string &getRemoteHost() const
|
||||
{
|
||||
return( mRemoteHost );
|
||||
}
|
||||
const std::string &getRemoteHost() const
|
||||
{
|
||||
return( mRemoteHost );
|
||||
}
|
||||
|
||||
int getRemoteDataPort() const
|
||||
{
|
||||
return( mRemotePortChans[0] );
|
||||
}
|
||||
int getRemoteDataPort() const
|
||||
{
|
||||
return( mRemotePortChans[0] );
|
||||
}
|
||||
|
||||
int getRemoteCtrlPort() const
|
||||
{
|
||||
return( mRemotePortChans[1] );
|
||||
}
|
||||
int getRemoteCtrlPort() const
|
||||
{
|
||||
return( mRemotePortChans[1] );
|
||||
}
|
||||
|
||||
uint32_t getMaxSeq() const
|
||||
{
|
||||
return( mCycles + mMaxSeq );
|
||||
}
|
||||
uint32_t getMaxSeq() const
|
||||
{
|
||||
return( mCycles + mMaxSeq );
|
||||
}
|
||||
|
||||
uint32_t getExpectedPackets() const
|
||||
{
|
||||
return( mExpectedPackets );
|
||||
}
|
||||
uint32_t getExpectedPackets() const
|
||||
{
|
||||
return( mExpectedPackets );
|
||||
}
|
||||
|
||||
uint32_t getLostPackets() const
|
||||
{
|
||||
return( mLostPackets );
|
||||
}
|
||||
uint32_t getLostPackets() const
|
||||
{
|
||||
return( mLostPackets );
|
||||
}
|
||||
|
||||
uint8_t getLostFraction() const
|
||||
{
|
||||
return( mLostFraction );
|
||||
}
|
||||
uint8_t getLostFraction() const
|
||||
{
|
||||
return( mLostFraction );
|
||||
}
|
||||
|
||||
uint32_t getJitter() const
|
||||
{
|
||||
return( mJitter >> 4 );
|
||||
}
|
||||
uint32_t getJitter() const
|
||||
{
|
||||
return( mJitter >> 4 );
|
||||
}
|
||||
|
||||
uint32_t getLastSrTimestamp() const
|
||||
{
|
||||
return( ((mLastSrTimeNtpSecs&0xffff)<<16)|(mLastSrTimeNtpFrac>>16) );
|
||||
}
|
||||
uint32_t getLastSrTimestamp() const
|
||||
{
|
||||
return( ((mLastSrTimeNtpSecs&0xffff)<<16)|(mLastSrTimeNtpFrac>>16) );
|
||||
}
|
||||
};
|
||||
|
||||
#endif // HAVE_LIBAVCODEC
|
||||
|
|
1508
src/zm_rtsp.cpp
1508
src/zm_rtsp.cpp
File diff suppressed because it is too large
Load Diff
152
src/zm_rtsp.h
152
src/zm_rtsp.h
|
@ -34,110 +34,110 @@
|
|||
class RtspThread : public Thread
|
||||
{
|
||||
public:
|
||||
typedef enum { RTP_UNICAST, RTP_MULTICAST, RTP_RTSP, RTP_RTSP_HTTP } RtspMethod;
|
||||
typedef enum { UNDEFINED, UNICAST, MULTICAST } RtspDist;
|
||||
typedef enum { RTP_UNICAST, RTP_MULTICAST, RTP_RTSP, RTP_RTSP_HTTP } RtspMethod;
|
||||
typedef enum { UNDEFINED, UNICAST, MULTICAST } RtspDist;
|
||||
|
||||
private:
|
||||
typedef std::set<int> PortSet;
|
||||
typedef std::set<uint32_t> SsrcSet;
|
||||
typedef std::map<uint32_t,RtpSource *> SourceMap;
|
||||
typedef std::set<int> PortSet;
|
||||
typedef std::set<uint32_t> SsrcSet;
|
||||
typedef std::map<uint32_t,RtpSource *> SourceMap;
|
||||
|
||||
private:
|
||||
static int smMinDataPort;
|
||||
static int smMaxDataPort;
|
||||
static PortSet smLocalSsrcs;
|
||||
static PortSet smAssignedPorts;
|
||||
static int smMinDataPort;
|
||||
static int smMaxDataPort;
|
||||
static PortSet smLocalSsrcs;
|
||||
static PortSet smAssignedPorts;
|
||||
|
||||
private:
|
||||
int mId;
|
||||
int mId;
|
||||
|
||||
RtspMethod mMethod;
|
||||
std::string mProtocol;
|
||||
std::string mHost;
|
||||
std::string mPort;
|
||||
std::string mPath;
|
||||
bool mRtspDescribe;
|
||||
std::string mUrl;
|
||||
RtspMethod mMethod;
|
||||
std::string mProtocol;
|
||||
std::string mHost;
|
||||
std::string mPort;
|
||||
std::string mPath;
|
||||
bool mRtspDescribe;
|
||||
std::string mUrl;
|
||||
|
||||
// Reworked authentication system
|
||||
// First try without authentication, even if we have a username and password
|
||||
// on receiving a 401 response, select authentication method (basic or digest)
|
||||
// fill required fields and set needAuth
|
||||
// subsequent requests can set the required authentication header.
|
||||
bool mNeedAuth;
|
||||
int respCode;
|
||||
zm::Authenticator* mAuthenticator;
|
||||
// Reworked authentication system
|
||||
// First try without authentication, even if we have a username and password
|
||||
// on receiving a 401 response, select authentication method (basic or digest)
|
||||
// fill required fields and set needAuth
|
||||
// subsequent requests can set the required authentication header.
|
||||
bool mNeedAuth;
|
||||
int respCode;
|
||||
zm::Authenticator* mAuthenticator;
|
||||
|
||||
|
||||
std::string mHttpSession; ///< Only for RTSP over HTTP sessions
|
||||
std::string mHttpSession; ///< Only for RTSP over HTTP sessions
|
||||
|
||||
TcpInetClient mRtspSocket;
|
||||
TcpInetClient mRtspSocket2;
|
||||
TcpInetClient mRtspSocket;
|
||||
TcpInetClient mRtspSocket2;
|
||||
|
||||
SourceMap mSources;
|
||||
SourceMap mSources;
|
||||
|
||||
SessionDescriptor *mSessDesc;
|
||||
AVFormatContext *mFormatContext;
|
||||
SessionDescriptor *mSessDesc;
|
||||
AVFormatContext *mFormatContext;
|
||||
|
||||
uint16_t mSeq;
|
||||
uint32_t mSession;
|
||||
uint32_t mSsrc;
|
||||
uint16_t mSeq;
|
||||
uint32_t mSession;
|
||||
uint32_t mSsrc;
|
||||
|
||||
int mRemotePorts[2];
|
||||
int mRemoteChannels[2];
|
||||
RtspDist mDist;
|
||||
int mRemotePorts[2];
|
||||
int mRemoteChannels[2];
|
||||
RtspDist mDist;
|
||||
|
||||
unsigned long mRtpTime;
|
||||
unsigned long mRtpTime;
|
||||
|
||||
bool mStop;
|
||||
bool mStop;
|
||||
|
||||
private:
|
||||
bool sendCommand( std::string message );
|
||||
bool recvResponse( std::string &response );
|
||||
void checkAuthResponse(std::string &response);
|
||||
bool sendCommand( std::string message );
|
||||
bool recvResponse( std::string &response );
|
||||
void checkAuthResponse(std::string &response);
|
||||
|
||||
public:
|
||||
RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, const std::string &port, const std::string &path, const std::string &auth, bool rtsp_describe );
|
||||
~RtspThread();
|
||||
RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, const std::string &port, const std::string &path, const std::string &auth, bool rtsp_describe );
|
||||
~RtspThread();
|
||||
|
||||
public:
|
||||
int requestPorts();
|
||||
void releasePorts( int port );
|
||||
int requestPorts();
|
||||
void releasePorts( int port );
|
||||
|
||||
bool isValidSsrc( uint32_t ssrc );
|
||||
bool updateSsrc( uint32_t ssrc, const RtpDataHeader *header );
|
||||
bool isValidSsrc( uint32_t ssrc );
|
||||
bool updateSsrc( uint32_t ssrc, const RtpDataHeader *header );
|
||||
|
||||
uint32_t getSsrc() const
|
||||
{
|
||||
return( mSsrc );
|
||||
}
|
||||
uint32_t getSsrc() const
|
||||
{
|
||||
return( mSsrc );
|
||||
}
|
||||
|
||||
bool hasSources() const
|
||||
{
|
||||
return( !mSources.empty() );
|
||||
}
|
||||
bool hasSources() const
|
||||
{
|
||||
return( !mSources.empty() );
|
||||
}
|
||||
|
||||
AVFormatContext *getFormatContext()
|
||||
{
|
||||
return( mFormatContext );
|
||||
}
|
||||
AVFormatContext *getFormatContext()
|
||||
{
|
||||
return( mFormatContext );
|
||||
}
|
||||
|
||||
bool getFrame( Buffer &frame )
|
||||
{
|
||||
SourceMap::iterator iter = mSources.begin();
|
||||
if ( iter == mSources.end() )
|
||||
return( false );
|
||||
return( iter->second->getFrame( frame ) );
|
||||
}
|
||||
int run();
|
||||
void stop()
|
||||
{
|
||||
mStop = true;
|
||||
}
|
||||
bool stopped() const
|
||||
{
|
||||
return( mStop );
|
||||
}
|
||||
bool getFrame( Buffer &frame )
|
||||
{
|
||||
SourceMap::iterator iter = mSources.begin();
|
||||
if ( iter == mSources.end() )
|
||||
return( false );
|
||||
return( iter->second->getFrame( frame ) );
|
||||
}
|
||||
int run();
|
||||
void stop()
|
||||
{
|
||||
mStop = true;
|
||||
}
|
||||
bool stopped() const
|
||||
{
|
||||
return( mStop );
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ZM_RTSP_H
|
||||
|
|
|
@ -28,206 +28,206 @@ namespace zm {
|
|||
|
||||
Authenticator::Authenticator(std::string &username, std::string password) {
|
||||
#ifdef HAVE_GCRYPT_H
|
||||
// Special initialisation for libgcrypt
|
||||
if ( !gcry_check_version( GCRYPT_VERSION ) )
|
||||
{
|
||||
Fatal( "Unable to initialise libgcrypt" );
|
||||
}
|
||||
gcry_control( GCRYCTL_DISABLE_SECMEM, 0 );
|
||||
gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 );
|
||||
// Special initialisation for libgcrypt
|
||||
if ( !gcry_check_version( GCRYPT_VERSION ) )
|
||||
{
|
||||
Fatal( "Unable to initialise libgcrypt" );
|
||||
}
|
||||
gcry_control( GCRYCTL_DISABLE_SECMEM, 0 );
|
||||
gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 );
|
||||
#endif // HAVE_GCRYPT_H
|
||||
|
||||
fAuthMethod = AUTH_UNDEFINED;
|
||||
fUsername = username;
|
||||
fPassword = password;
|
||||
nc = 1;
|
||||
fCnonce = "0a4f113b";
|
||||
fAuthMethod = AUTH_UNDEFINED;
|
||||
fUsername = username;
|
||||
fPassword = password;
|
||||
nc = 1;
|
||||
fCnonce = "0a4f113b";
|
||||
}
|
||||
|
||||
Authenticator::~Authenticator() {
|
||||
reset();
|
||||
reset();
|
||||
}
|
||||
|
||||
void Authenticator::reset() {
|
||||
fRealm.clear();
|
||||
fNonce.clear();
|
||||
fUsername.clear();
|
||||
fPassword.clear();
|
||||
fAuthMethod = AUTH_UNDEFINED;
|
||||
fRealm.clear();
|
||||
fNonce.clear();
|
||||
fUsername.clear();
|
||||
fPassword.clear();
|
||||
fAuthMethod = AUTH_UNDEFINED;
|
||||
}
|
||||
|
||||
void Authenticator::authHandleHeader(std::string headerData)
|
||||
{
|
||||
const char* basic_match = "Basic ";
|
||||
const char* digest_match = "Digest ";
|
||||
size_t digest_match_len = strlen(digest_match);
|
||||
const char* basic_match = "Basic ";
|
||||
const char* digest_match = "Digest ";
|
||||
size_t digest_match_len = strlen(digest_match);
|
||||
|
||||
// Check if basic auth
|
||||
if (strncasecmp(headerData.c_str(),basic_match,strlen(basic_match)) == 0)
|
||||
// Check if basic auth
|
||||
if (strncasecmp(headerData.c_str(),basic_match,strlen(basic_match)) == 0)
|
||||
{
|
||||
fAuthMethod = AUTH_BASIC;
|
||||
Debug( 2, "Set authMethod to Basic");
|
||||
}
|
||||
// Check if digest auth
|
||||
else if (strncasecmp( headerData.c_str(),digest_match,digest_match_len ) == 0)
|
||||
{
|
||||
fAuthMethod = AUTH_DIGEST;
|
||||
Debug( 2, "Set authMethod to Digest");
|
||||
StringVector subparts = split(headerData.substr(digest_match_len, headerData.length() - digest_match_len), ",");
|
||||
// subparts are key="value"
|
||||
for ( size_t i = 0; i < subparts.size(); i++ )
|
||||
{
|
||||
fAuthMethod = AUTH_BASIC;
|
||||
Debug( 2, "Set authMethod to Basic");
|
||||
}
|
||||
// Check if digest auth
|
||||
else if (strncasecmp( headerData.c_str(),digest_match,digest_match_len ) == 0)
|
||||
{
|
||||
fAuthMethod = AUTH_DIGEST;
|
||||
Debug( 2, "Set authMethod to Digest");
|
||||
StringVector subparts = split(headerData.substr(digest_match_len, headerData.length() - digest_match_len), ",");
|
||||
// subparts are key="value"
|
||||
for ( size_t i = 0; i < subparts.size(); i++ )
|
||||
{
|
||||
StringVector kvPair = split( trimSpaces( subparts[i] ), "=" );
|
||||
std::string key = trimSpaces( kvPair[0] );
|
||||
if (key == "realm") {
|
||||
fRealm = trimSet( kvPair[1], "\"");
|
||||
continue;
|
||||
}
|
||||
if (key == "nonce") {
|
||||
fNonce = trimSet( kvPair[1], "\"");
|
||||
continue;
|
||||
}
|
||||
if (key == "qop") {
|
||||
fQop = trimSet( kvPair[1], "\"");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Debug( 2, "Auth data completed. User: %s, realm: %s, nonce: %s, qop: %s", username().c_str(), fRealm.c_str(), fNonce.c_str(), fQop.c_str() );
|
||||
StringVector kvPair = split( trimSpaces( subparts[i] ), "=" );
|
||||
std::string key = trimSpaces( kvPair[0] );
|
||||
if (key == "realm") {
|
||||
fRealm = trimSet( kvPair[1], "\"");
|
||||
continue;
|
||||
}
|
||||
if (key == "nonce") {
|
||||
fNonce = trimSet( kvPair[1], "\"");
|
||||
continue;
|
||||
}
|
||||
if (key == "qop") {
|
||||
fQop = trimSet( kvPair[1], "\"");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Debug( 2, "Auth data completed. User: %s, realm: %s, nonce: %s, qop: %s", username().c_str(), fRealm.c_str(), fNonce.c_str(), fQop.c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
std::string Authenticator::quote(std::string src)
|
||||
{
|
||||
return replaceAll(replaceAll(src, "\\", "\\\\"), "\"", "\\\"");
|
||||
return replaceAll(replaceAll(src, "\\", "\\\\"), "\"", "\\\"");
|
||||
}
|
||||
|
||||
std::string Authenticator::getAuthHeader(std::string method, std::string uri)
|
||||
{
|
||||
std::string result = "Authorization: ";
|
||||
if (fAuthMethod == AUTH_BASIC)
|
||||
{
|
||||
result += "Basic " + base64Encode( username() + ":" + password() );
|
||||
std::string result = "Authorization: ";
|
||||
if (fAuthMethod == AUTH_BASIC)
|
||||
{
|
||||
result += "Basic " + base64Encode( username() + ":" + password() );
|
||||
}
|
||||
else if (fAuthMethod == AUTH_DIGEST)
|
||||
{
|
||||
result += std::string("Digest ") +
|
||||
"username=\"" + quote(username()) + "\", realm=\"" + quote(realm()) + "\", " +
|
||||
"nonce=\"" + quote(nonce()) + "\", uri=\"" + quote(uri) + "\"";
|
||||
if ( ! fQop.empty() ) {
|
||||
result += ", qop=" + fQop;
|
||||
result += ", nc=" + stringtf("%08x",nc);
|
||||
result += ", cnonce=\"" + fCnonce + "\"";
|
||||
}
|
||||
else if (fAuthMethod == AUTH_DIGEST)
|
||||
{
|
||||
result += std::string("Digest ") +
|
||||
"username=\"" + quote(username()) + "\", realm=\"" + quote(realm()) + "\", " +
|
||||
"nonce=\"" + quote(nonce()) + "\", uri=\"" + quote(uri) + "\"";
|
||||
if ( ! fQop.empty() ) {
|
||||
result += ", qop=" + fQop;
|
||||
result += ", nc=" + stringtf("%08x",nc);
|
||||
result += ", cnonce=\"" + fCnonce + "\"";
|
||||
}
|
||||
result += ", response=\"" + computeDigestResponse(method, uri) + "\"";
|
||||
result += ", algorithm=\"MD5\"";
|
||||
result += ", response=\"" + computeDigestResponse(method, uri) + "\"";
|
||||
result += ", algorithm=\"MD5\"";
|
||||
|
||||
//Authorization: Digest username="zm",
|
||||
// realm="NC-336PW-HD-1080P",
|
||||
// nonce="de8859d97609a6fcc16eaba490dcfd80",
|
||||
// uri="rtsp://10.192.16.8:554/live/0/h264.sdp",
|
||||
// response="4092120557d3099a163bd51a0d59744d",
|
||||
// algorithm=MD5,
|
||||
// opaque="5ccc069c403ebaf9f0171e9517f40e41",
|
||||
// qop="auth",
|
||||
// cnonce="c8051140765877dc",
|
||||
// nc=00000001
|
||||
//Authorization: Digest username="zm",
|
||||
// realm="NC-336PW-HD-1080P",
|
||||
// nonce="de8859d97609a6fcc16eaba490dcfd80",
|
||||
// uri="rtsp://10.192.16.8:554/live/0/h264.sdp",
|
||||
// response="4092120557d3099a163bd51a0d59744d",
|
||||
// algorithm=MD5,
|
||||
// opaque="5ccc069c403ebaf9f0171e9517f40e41",
|
||||
// qop="auth",
|
||||
// cnonce="c8051140765877dc",
|
||||
// nc=00000001
|
||||
|
||||
}
|
||||
result += "\r\n";
|
||||
return result;
|
||||
}
|
||||
result += "\r\n";
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string Authenticator::computeDigestResponse(std::string &method, std::string &uri) {
|
||||
#if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT
|
||||
// The "response" field is computed as:
|
||||
// md5(md5(<username>:<realm>:<password>):<nonce>:md5(<cmd>:<url>))
|
||||
size_t md5len = 16;
|
||||
unsigned char md5buf[md5len];
|
||||
char md5HexBuf[md5len*2+1];
|
||||
// The "response" field is computed as:
|
||||
// md5(md5(<username>:<realm>:<password>):<nonce>:md5(<cmd>:<url>))
|
||||
size_t md5len = 16;
|
||||
unsigned char md5buf[md5len];
|
||||
char md5HexBuf[md5len*2+1];
|
||||
|
||||
// Step 1: md5(<username>:<realm>:<password>)
|
||||
std::string ha1Data = username() + ":" + realm() + ":" + password();
|
||||
Debug( 2, "HA1 pre-md5: %s", ha1Data.c_str() );
|
||||
// Step 1: md5(<username>:<realm>:<password>)
|
||||
std::string ha1Data = username() + ":" + realm() + ":" + password();
|
||||
Debug( 2, "HA1 pre-md5: %s", ha1Data.c_str() );
|
||||
#if HAVE_DECL_MD5
|
||||
MD5((unsigned char*)ha1Data.c_str(), ha1Data.length(), md5buf);
|
||||
MD5((unsigned char*)ha1Data.c_str(), ha1Data.length(), md5buf);
|
||||
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
||||
gnutls_datum_t md5dataha1 = { (unsigned char*)ha1Data.c_str(), ha1Data.length() };
|
||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha1, md5buf, &md5len );
|
||||
gnutls_datum_t md5dataha1 = { (unsigned char*)ha1Data.c_str(), ha1Data.length() };
|
||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha1, md5buf, &md5len );
|
||||
#endif
|
||||
for ( unsigned int j = 0; j < md5len; j++ )
|
||||
{
|
||||
sprintf(&md5HexBuf[2*j], "%02x", md5buf[j] );
|
||||
}
|
||||
md5HexBuf[md5len*2]='\0';
|
||||
std::string ha1Hash = md5HexBuf;
|
||||
for ( unsigned int j = 0; j < md5len; j++ )
|
||||
{
|
||||
sprintf(&md5HexBuf[2*j], "%02x", md5buf[j] );
|
||||
}
|
||||
md5HexBuf[md5len*2]='\0';
|
||||
std::string ha1Hash = md5HexBuf;
|
||||
|
||||
// Step 2: md5(<cmd>:<url>)
|
||||
std::string ha2Data = method + ":" + uri;
|
||||
Debug( 2, "HA2 pre-md5: %s", ha2Data.c_str() );
|
||||
// Step 2: md5(<cmd>:<url>)
|
||||
std::string ha2Data = method + ":" + uri;
|
||||
Debug( 2, "HA2 pre-md5: %s", ha2Data.c_str() );
|
||||
#if HAVE_DECL_MD5
|
||||
MD5((unsigned char*)ha2Data.c_str(), ha2Data.length(), md5buf );
|
||||
MD5((unsigned char*)ha2Data.c_str(), ha2Data.length(), md5buf );
|
||||
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
||||
gnutls_datum_t md5dataha2 = { (unsigned char*)ha2Data.c_str(), ha2Data.length() };
|
||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha2, md5buf, &md5len );
|
||||
gnutls_datum_t md5dataha2 = { (unsigned char*)ha2Data.c_str(), ha2Data.length() };
|
||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha2, md5buf, &md5len );
|
||||
#endif
|
||||
for ( unsigned int j = 0; j < md5len; j++ )
|
||||
{
|
||||
sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
|
||||
}
|
||||
md5HexBuf[md5len*2]='\0';
|
||||
std::string ha2Hash = md5HexBuf;
|
||||
for ( unsigned int j = 0; j < md5len; j++ )
|
||||
{
|
||||
sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
|
||||
}
|
||||
md5HexBuf[md5len*2]='\0';
|
||||
std::string ha2Hash = md5HexBuf;
|
||||
|
||||
// Step 3: md5(ha1:<nonce>:ha2)
|
||||
std::string digestData = ha1Hash + ":" + nonce();
|
||||
if ( ! fQop.empty() ) {
|
||||
digestData += ":" + stringtf("%08x", nc) + ":"+fCnonce + ":" + fQop;
|
||||
nc ++;
|
||||
// if qop was specified, then we have to include t and a cnonce and an nccount
|
||||
}
|
||||
digestData += ":" + ha2Hash;
|
||||
Debug( 2, "pre-md5: %s", digestData.c_str() );
|
||||
// Step 3: md5(ha1:<nonce>:ha2)
|
||||
std::string digestData = ha1Hash + ":" + nonce();
|
||||
if ( ! fQop.empty() ) {
|
||||
digestData += ":" + stringtf("%08x", nc) + ":"+fCnonce + ":" + fQop;
|
||||
nc ++;
|
||||
// if qop was specified, then we have to include t and a cnonce and an nccount
|
||||
}
|
||||
digestData += ":" + ha2Hash;
|
||||
Debug( 2, "pre-md5: %s", digestData.c_str() );
|
||||
#if HAVE_DECL_MD5
|
||||
MD5((unsigned char*)digestData.c_str(), digestData.length(), md5buf);
|
||||
MD5((unsigned char*)digestData.c_str(), digestData.length(), md5buf);
|
||||
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
||||
gnutls_datum_t md5datadigest = { (unsigned char*)digestData.c_str(), digestData.length() };
|
||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5datadigest, md5buf, &md5len );
|
||||
gnutls_datum_t md5datadigest = { (unsigned char*)digestData.c_str(), digestData.length() };
|
||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5datadigest, md5buf, &md5len );
|
||||
#endif
|
||||
for ( unsigned int j = 0; j < md5len; j++ )
|
||||
{
|
||||
sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
|
||||
}
|
||||
md5HexBuf[md5len*2]='\0';
|
||||
for ( unsigned int j = 0; j < md5len; j++ )
|
||||
{
|
||||
sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
|
||||
}
|
||||
md5HexBuf[md5len*2]='\0';
|
||||
|
||||
return md5HexBuf;
|
||||
return md5HexBuf;
|
||||
#else // HAVE_DECL_MD5
|
||||
Error( "You need to build with gnutls or openssl installed to use digest authentication" );
|
||||
return( 0 );
|
||||
Error( "You need to build with gnutls or openssl installed to use digest authentication" );
|
||||
return( 0 );
|
||||
#endif // HAVE_DECL_MD5
|
||||
}
|
||||
|
||||
void Authenticator::checkAuthResponse(std::string &response) {
|
||||
std::string authLine;
|
||||
StringVector lines = split( response, "\r\n" );
|
||||
const char* authenticate_match = "WWW-Authenticate:";
|
||||
size_t authenticate_match_len = strlen(authenticate_match);
|
||||
std::string authLine;
|
||||
StringVector lines = split( response, "\r\n" );
|
||||
const char* authenticate_match = "WWW-Authenticate:";
|
||||
size_t authenticate_match_len = strlen(authenticate_match);
|
||||
|
||||
for ( size_t i = 0; i < lines.size(); i++ ) {
|
||||
// stop at end of headers
|
||||
if (lines[i].length()==0)
|
||||
break;
|
||||
for ( size_t i = 0; i < lines.size(); i++ ) {
|
||||
// stop at end of headers
|
||||
if (lines[i].length()==0)
|
||||
break;
|
||||
|
||||
if (strncasecmp(lines[i].c_str(),authenticate_match,authenticate_match_len) == 0) {
|
||||
authLine = lines[i];
|
||||
Debug( 2, "Found auth line at %d", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!authLine.empty()) {
|
||||
Debug( 2, "Analyze auth line %s", authLine.c_str());
|
||||
authHandleHeader( trimSpaces(authLine.substr(authenticate_match_len,authLine.length()-authenticate_match_len)) );
|
||||
} else {
|
||||
Debug( 2, "Didn't find auth line in %s", authLine.c_str());
|
||||
}
|
||||
if (strncasecmp(lines[i].c_str(),authenticate_match,authenticate_match_len) == 0) {
|
||||
authLine = lines[i];
|
||||
Debug( 2, "Found auth line at %d", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!authLine.empty()) {
|
||||
Debug( 2, "Analyze auth line %s", authLine.c_str());
|
||||
authHandleHeader( trimSpaces(authLine.substr(authenticate_match_len,authLine.length()-authenticate_match_len)) );
|
||||
} else {
|
||||
Debug( 2, "Didn't find auth line in %s", authLine.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace zm
|
||||
|
|
|
@ -37,19 +37,19 @@ namespace zm {
|
|||
enum AuthMethod { AUTH_UNDEFINED = 0, AUTH_BASIC = 1, AUTH_DIGEST = 2 };
|
||||
class Authenticator {
|
||||
public:
|
||||
Authenticator(std::string &username, std::string password);
|
||||
virtual ~Authenticator();
|
||||
void reset();
|
||||
Authenticator(std::string &username, std::string password);
|
||||
virtual ~Authenticator();
|
||||
void reset();
|
||||
|
||||
std::string realm() { return fRealm; }
|
||||
std::string nonce() { return fNonce; }
|
||||
std::string username() { return fUsername; }
|
||||
AuthMethod auth_method() const { return fAuthMethod; }
|
||||
std::string realm() { return fRealm; }
|
||||
std::string nonce() { return fNonce; }
|
||||
std::string username() { return fUsername; }
|
||||
AuthMethod auth_method() const { return fAuthMethod; }
|
||||
|
||||
std::string computeDigestResponse( std::string &cmd, std::string &url );
|
||||
void authHandleHeader( std::string headerData );
|
||||
std::string getAuthHeader( std::string method, std::string path );
|
||||
void checkAuthResponse(std::string &response);
|
||||
std::string computeDigestResponse( std::string &cmd, std::string &url );
|
||||
void authHandleHeader( std::string headerData );
|
||||
std::string getAuthHeader( std::string method, std::string path );
|
||||
void checkAuthResponse(std::string &response);
|
||||
|
||||
private:
|
||||
std::string password() { return fPassword; }
|
||||
|
@ -61,7 +61,7 @@ private:
|
|||
std::string fUsername;
|
||||
std::string fPassword;
|
||||
std::string quote( std::string src );
|
||||
int nc;
|
||||
int nc;
|
||||
};
|
||||
|
||||
} // namespace zm
|
||||
|
|
824
src/zm_sdp.cpp
824
src/zm_sdp.cpp
|
@ -25,489 +25,489 @@
|
|||
|
||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||
SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = {
|
||||
{ 0, "PCMU", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_MULAW, 8000, 1 },
|
||||
{ 3, "GSM", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 4, "G723", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 5, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 6, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 16000, 1 },
|
||||
{ 7, "LPC", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 8, "PCMA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_ALAW, 8000, 1 },
|
||||
{ 9, "G722", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 10, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 2 },
|
||||
{ 11, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 1 },
|
||||
{ 12, "QCELP", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_QCELP, 8000, 1 },
|
||||
{ 13, "CN", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP2, -1, -1 },
|
||||
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3, -1, -1 },
|
||||
{ 15, "G728", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 16, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 11025, 1 },
|
||||
{ 17, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 22050, 1 },
|
||||
{ 18, "G729", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 25, "CelB", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
|
||||
{ 26, "JPEG", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MJPEG, 90000, -1 },
|
||||
{ 28, "nv", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
|
||||
{ 31, "H261", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H261, 90000, -1 },
|
||||
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG1VIDEO, 90000, -1 },
|
||||
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO, 90000, -1 },
|
||||
{ 33, "MP2T", AVMEDIA_TYPE_DATA, AV_CODEC_ID_MPEG2TS, 90000, -1 },
|
||||
{ 34, "H263", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H263, 90000, -1 },
|
||||
{ -1, "", AVMEDIA_TYPE_UNKNOWN, AV_CODEC_ID_NONE, -1, -1 }
|
||||
{ 0, "PCMU", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_MULAW, 8000, 1 },
|
||||
{ 3, "GSM", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 4, "G723", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 5, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 6, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 16000, 1 },
|
||||
{ 7, "LPC", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 8, "PCMA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_ALAW, 8000, 1 },
|
||||
{ 9, "G722", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 10, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 2 },
|
||||
{ 11, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 1 },
|
||||
{ 12, "QCELP", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_QCELP, 8000, 1 },
|
||||
{ 13, "CN", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP2, -1, -1 },
|
||||
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3, -1, -1 },
|
||||
{ 15, "G728", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 16, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 11025, 1 },
|
||||
{ 17, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 22050, 1 },
|
||||
{ 18, "G729", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 25, "CelB", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
|
||||
{ 26, "JPEG", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MJPEG, 90000, -1 },
|
||||
{ 28, "nv", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
|
||||
{ 31, "H261", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H261, 90000, -1 },
|
||||
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG1VIDEO, 90000, -1 },
|
||||
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO, 90000, -1 },
|
||||
{ 33, "MP2T", AVMEDIA_TYPE_DATA, AV_CODEC_ID_MPEG2TS, 90000, -1 },
|
||||
{ 34, "H263", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H263, 90000, -1 },
|
||||
{ -1, "", AVMEDIA_TYPE_UNKNOWN, AV_CODEC_ID_NONE, -1, -1 }
|
||||
};
|
||||
|
||||
SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = {
|
||||
{ "MP4V-ES", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 },
|
||||
{ "mpeg4-generic", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC },
|
||||
{ "H264", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 },
|
||||
{ "AMR", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AMR_NB },
|
||||
{ "vnd.onvif.metadata", AVMEDIA_TYPE_DATA, AV_CODEC_ID_NONE }
|
||||
{ "MP4V-ES", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 },
|
||||
{ "mpeg4-generic", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC },
|
||||
{ "H264", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 },
|
||||
{ "AMR", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AMR_NB },
|
||||
{ "vnd.onvif.metadata", AVMEDIA_TYPE_DATA, AV_CODEC_ID_NONE }
|
||||
};
|
||||
#else
|
||||
SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = {
|
||||
{ 0, "PCMU", CODEC_TYPE_AUDIO, CODEC_ID_PCM_MULAW, 8001, 1 },
|
||||
{ 3, "GSM", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 4, "G723", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 5, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 6, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 16000, 1 },
|
||||
{ 7, "LPC", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 8, "PCMA", CODEC_TYPE_AUDIO, CODEC_ID_PCM_ALAW, 8000, 1 },
|
||||
{ 9, "G722", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 10, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 2 },
|
||||
{ 11, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 1 },
|
||||
{ 12, "QCELP", CODEC_TYPE_AUDIO, CODEC_ID_QCELP, 8000, 1 },
|
||||
{ 13, "CN", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP2, -1, -1 },
|
||||
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP3, -1, -1 },
|
||||
{ 15, "G728", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 16, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 11025, 1 },
|
||||
{ 17, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 22050, 1 },
|
||||
{ 18, "G729", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 25, "CelB", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
|
||||
{ 26, "JPEG", CODEC_TYPE_VIDEO, CODEC_ID_MJPEG, 90000, -1 },
|
||||
{ 28, "nv", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
|
||||
{ 31, "H261", CODEC_TYPE_VIDEO, CODEC_ID_H261, 90000, -1 },
|
||||
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG1VIDEO, 90000, -1 },
|
||||
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO, 90000, -1 },
|
||||
{ 33, "MP2T", CODEC_TYPE_DATA, CODEC_ID_MPEG2TS, 90000, -1 },
|
||||
{ 34, "H263", CODEC_TYPE_VIDEO, CODEC_ID_H263, 90000, -1 },
|
||||
{ -1, "", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1 }
|
||||
{ 0, "PCMU", CODEC_TYPE_AUDIO, CODEC_ID_PCM_MULAW, 8001, 1 },
|
||||
{ 3, "GSM", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 4, "G723", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 5, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 6, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 16000, 1 },
|
||||
{ 7, "LPC", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 8, "PCMA", CODEC_TYPE_AUDIO, CODEC_ID_PCM_ALAW, 8000, 1 },
|
||||
{ 9, "G722", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 10, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 2 },
|
||||
{ 11, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 1 },
|
||||
{ 12, "QCELP", CODEC_TYPE_AUDIO, CODEC_ID_QCELP, 8000, 1 },
|
||||
{ 13, "CN", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP2, -1, -1 },
|
||||
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP3, -1, -1 },
|
||||
{ 15, "G728", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 16, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 11025, 1 },
|
||||
{ 17, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 22050, 1 },
|
||||
{ 18, "G729", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 25, "CelB", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
|
||||
{ 26, "JPEG", CODEC_TYPE_VIDEO, CODEC_ID_MJPEG, 90000, -1 },
|
||||
{ 28, "nv", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
|
||||
{ 31, "H261", CODEC_TYPE_VIDEO, CODEC_ID_H261, 90000, -1 },
|
||||
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG1VIDEO, 90000, -1 },
|
||||
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO, 90000, -1 },
|
||||
{ 33, "MP2T", CODEC_TYPE_DATA, CODEC_ID_MPEG2TS, 90000, -1 },
|
||||
{ 34, "H263", CODEC_TYPE_VIDEO, CODEC_ID_H263, 90000, -1 },
|
||||
{ -1, "", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1 }
|
||||
};
|
||||
|
||||
SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = {
|
||||
{ "MP4V-ES", CODEC_TYPE_VIDEO, CODEC_ID_MPEG4 },
|
||||
{ "mpeg4-generic", CODEC_TYPE_AUDIO, CODEC_ID_AAC },
|
||||
{ "H264", CODEC_TYPE_VIDEO, CODEC_ID_H264 },
|
||||
{ "AMR", CODEC_TYPE_AUDIO, CODEC_ID_AMR_NB },
|
||||
{ "vnd.onvif.metadata", CODEC_TYPE_DATA, CODEC_ID_NONE }
|
||||
{ "MP4V-ES", CODEC_TYPE_VIDEO, CODEC_ID_MPEG4 },
|
||||
{ "mpeg4-generic", CODEC_TYPE_AUDIO, CODEC_ID_AAC },
|
||||
{ "H264", CODEC_TYPE_VIDEO, CODEC_ID_H264 },
|
||||
{ "AMR", CODEC_TYPE_AUDIO, CODEC_ID_AMR_NB },
|
||||
{ "vnd.onvif.metadata", CODEC_TYPE_DATA, CODEC_ID_NONE }
|
||||
};
|
||||
#endif
|
||||
|
||||
SessionDescriptor::ConnInfo::ConnInfo( const std::string &connInfo ) :
|
||||
mTtl( 16 ),
|
||||
mNoAddresses( 0 )
|
||||
mTtl( 16 ),
|
||||
mNoAddresses( 0 )
|
||||
{
|
||||
StringVector tokens = split( connInfo, " " );
|
||||
if ( tokens.size() < 3 )
|
||||
throw Exception( "Unable to parse SDP connection info from '"+connInfo+"'" );
|
||||
mNetworkType = tokens[0];
|
||||
if ( mNetworkType != "IN" )
|
||||
throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" );
|
||||
mAddressType = tokens[1];
|
||||
if ( mAddressType != "IP4" )
|
||||
throw Exception( "Invalid SDP address type '"+mAddressType+"' in connection info '"+connInfo+"'" );
|
||||
StringVector addressTokens = split( tokens[2], "/" );
|
||||
if ( addressTokens.size() < 1 )
|
||||
throw Exception( "Invalid SDP address '"+tokens[2]+"' in connection info '"+connInfo+"'" );
|
||||
mAddress = addressTokens[0];
|
||||
if ( addressTokens.size() >= 2 )
|
||||
mTtl = atoi(addressTokens[1].c_str());
|
||||
if ( addressTokens.size() >= 3 )
|
||||
mNoAddresses = atoi(addressTokens[2].c_str());
|
||||
StringVector tokens = split( connInfo, " " );
|
||||
if ( tokens.size() < 3 )
|
||||
throw Exception( "Unable to parse SDP connection info from '"+connInfo+"'" );
|
||||
mNetworkType = tokens[0];
|
||||
if ( mNetworkType != "IN" )
|
||||
throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" );
|
||||
mAddressType = tokens[1];
|
||||
if ( mAddressType != "IP4" )
|
||||
throw Exception( "Invalid SDP address type '"+mAddressType+"' in connection info '"+connInfo+"'" );
|
||||
StringVector addressTokens = split( tokens[2], "/" );
|
||||
if ( addressTokens.size() < 1 )
|
||||
throw Exception( "Invalid SDP address '"+tokens[2]+"' in connection info '"+connInfo+"'" );
|
||||
mAddress = addressTokens[0];
|
||||
if ( addressTokens.size() >= 2 )
|
||||
mTtl = atoi(addressTokens[1].c_str());
|
||||
if ( addressTokens.size() >= 3 )
|
||||
mNoAddresses = atoi(addressTokens[2].c_str());
|
||||
}
|
||||
|
||||
SessionDescriptor::BandInfo::BandInfo( const std::string &bandInfo ) :
|
||||
mValue( 0 )
|
||||
mValue( 0 )
|
||||
{
|
||||
StringVector tokens = split( bandInfo, ":" );
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP bandwidth info from '"+bandInfo+"'" );
|
||||
mType = tokens[0];
|
||||
//if ( mNetworkType != "IN" )
|
||||
//throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" );
|
||||
mValue = atoi(tokens[1].c_str());
|
||||
StringVector tokens = split( bandInfo, ":" );
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP bandwidth info from '"+bandInfo+"'" );
|
||||
mType = tokens[0];
|
||||
//if ( mNetworkType != "IN" )
|
||||
//throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" );
|
||||
mValue = atoi(tokens[1].c_str());
|
||||
}
|
||||
|
||||
SessionDescriptor::MediaDescriptor::MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType ) :
|
||||
mType( type ),
|
||||
mPort( port ),
|
||||
mNumPorts( numPorts ),
|
||||
mTransport( transport ),
|
||||
mPayloadType( payloadType ),
|
||||
mFrameRate( 0.0 ),
|
||||
mClock( 0 ),
|
||||
mWidth( 0 ),
|
||||
mHeight( 0 ),
|
||||
mSprops( "" ),
|
||||
mConnInfo( 0 )
|
||||
mType( type ),
|
||||
mPort( port ),
|
||||
mNumPorts( numPorts ),
|
||||
mTransport( transport ),
|
||||
mPayloadType( payloadType ),
|
||||
mFrameRate( 0.0 ),
|
||||
mClock( 0 ),
|
||||
mWidth( 0 ),
|
||||
mHeight( 0 ),
|
||||
mSprops( "" ),
|
||||
mConnInfo( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
SessionDescriptor::SessionDescriptor( const std::string &url, const std::string &sdp ) :
|
||||
mUrl( url ),
|
||||
mConnInfo( 0 ),
|
||||
mBandInfo( 0 )
|
||||
mUrl( url ),
|
||||
mConnInfo( 0 ),
|
||||
mBandInfo( 0 )
|
||||
{
|
||||
MediaDescriptor *currMedia = 0;
|
||||
MediaDescriptor *currMedia = 0;
|
||||
|
||||
StringVector lines = split( sdp, "\r\n" );
|
||||
for ( StringVector::const_iterator iter = lines.begin(); iter != lines.end(); iter++ )
|
||||
StringVector lines = split( sdp, "\r\n" );
|
||||
for ( StringVector::const_iterator iter = lines.begin(); iter != lines.end(); iter++ )
|
||||
{
|
||||
std::string line = *iter;
|
||||
if ( line.empty() )
|
||||
break;
|
||||
|
||||
Debug( 3, "Processing SDP line '%s'", line.c_str() );
|
||||
const char sdpType = line[0];
|
||||
if ( line[1] != '=' )
|
||||
throw Exception( "Invalid SDP format at '"+line+"'" );
|
||||
|
||||
line.erase( 0, 2 );
|
||||
switch( sdpType )
|
||||
{
|
||||
std::string line = *iter;
|
||||
if ( line.empty() )
|
||||
break;
|
||||
|
||||
Debug( 3, "Processing SDP line '%s'", line.c_str() );
|
||||
const char sdpType = line[0];
|
||||
if ( line[1] != '=' )
|
||||
throw Exception( "Invalid SDP format at '"+line+"'" );
|
||||
|
||||
line.erase( 0, 2 );
|
||||
switch( sdpType )
|
||||
case 'v' :
|
||||
mVersion = line;
|
||||
break;
|
||||
case 'o' :
|
||||
mOwner = line;
|
||||
break;
|
||||
case 's' :
|
||||
mName = line;
|
||||
break;
|
||||
case 'i' :
|
||||
mInfo = line;
|
||||
break;
|
||||
case 'c' :
|
||||
// This prevent a memory leak if the field appears more than one time
|
||||
if ( mConnInfo )
|
||||
delete mConnInfo;
|
||||
mConnInfo = new ConnInfo( line );
|
||||
break;
|
||||
case 'b' :
|
||||
// This prevent a memory leak if the field appears more than one time
|
||||
if ( mBandInfo )
|
||||
delete mBandInfo;
|
||||
mBandInfo = new BandInfo( line );
|
||||
break;
|
||||
case 't' :
|
||||
mTimeInfo = line;
|
||||
break;
|
||||
case 'a' :
|
||||
{
|
||||
mAttributes.push_back( line );
|
||||
StringVector tokens = split( line, ":", 2 );
|
||||
std::string attrName = tokens[0];
|
||||
if ( currMedia )
|
||||
{
|
||||
case 'v' :
|
||||
mVersion = line;
|
||||
break;
|
||||
case 'o' :
|
||||
mOwner = line;
|
||||
break;
|
||||
case 's' :
|
||||
mName = line;
|
||||
break;
|
||||
case 'i' :
|
||||
mInfo = line;
|
||||
break;
|
||||
case 'c' :
|
||||
// This prevent a memory leak if the field appears more than one time
|
||||
if ( mConnInfo )
|
||||
delete mConnInfo;
|
||||
mConnInfo = new ConnInfo( line );
|
||||
break;
|
||||
case 'b' :
|
||||
// This prevent a memory leak if the field appears more than one time
|
||||
if ( mBandInfo )
|
||||
delete mBandInfo;
|
||||
mBandInfo = new BandInfo( line );
|
||||
break;
|
||||
case 't' :
|
||||
mTimeInfo = line;
|
||||
break;
|
||||
case 'a' :
|
||||
if ( attrName == "control" )
|
||||
{
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP control attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||
currMedia->setControlUrl( tokens[1] );
|
||||
}
|
||||
else if ( attrName == "range" )
|
||||
{
|
||||
}
|
||||
else if ( attrName == "rtpmap" )
|
||||
{
|
||||
// a=rtpmap:96 MP4V-ES/90000
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP rtpmap attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||
StringVector attrTokens = split( tokens[1], " " );
|
||||
int payloadType = atoi(attrTokens[0].c_str());
|
||||
if ( payloadType != currMedia->getPayloadType() )
|
||||
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
|
||||
std::string payloadDesc = attrTokens[1];
|
||||
//currMedia->setPayloadType( payloadType );
|
||||
if ( attrTokens.size() > 1 )
|
||||
{
|
||||
mAttributes.push_back( line );
|
||||
StringVector tokens = split( line, ":", 2 );
|
||||
std::string attrName = tokens[0];
|
||||
if ( currMedia )
|
||||
StringVector payloadTokens = split( attrTokens[1], "/" );
|
||||
std::string payloadDesc = payloadTokens[0];
|
||||
int payloadClock = atoi(payloadTokens[1].c_str());
|
||||
currMedia->setPayloadDesc( payloadDesc );
|
||||
currMedia->setClock( payloadClock );
|
||||
}
|
||||
}
|
||||
else if ( attrName == "framesize" )
|
||||
{
|
||||
// a=framesize:96 320-240
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP framesize attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||
StringVector attrTokens = split( tokens[1], " " );
|
||||
int payloadType = atoi(attrTokens[0].c_str());
|
||||
if ( payloadType != currMedia->getPayloadType() )
|
||||
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
|
||||
//currMedia->setPayloadType( payloadType );
|
||||
StringVector sizeTokens = split( attrTokens[1], "-" );
|
||||
int width = atoi(sizeTokens[0].c_str());
|
||||
int height = atoi(sizeTokens[1].c_str());
|
||||
currMedia->setFrameSize( width, height );
|
||||
}
|
||||
else if ( attrName == "framerate" )
|
||||
{
|
||||
// a=framerate:5.0
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP framerate attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||
double frameRate = atof(tokens[1].c_str());
|
||||
currMedia->setFrameRate( frameRate );
|
||||
}
|
||||
else if ( attrName == "fmtp" )
|
||||
{
|
||||
// a=fmtp:96 profile-level-id=247; config=000001B0F7000001B509000001000000012008D48D8803250F042D14440F
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP fmtp attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||
StringVector attrTokens = split( tokens[1], " ", 2 );
|
||||
int payloadType = atoi(attrTokens[0].c_str());
|
||||
if ( payloadType != currMedia->getPayloadType() )
|
||||
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
|
||||
//currMedia->setPayloadType( payloadType );
|
||||
if ( attrTokens.size() > 1 )
|
||||
{
|
||||
StringVector attr2Tokens = split( attrTokens[1], "; " );
|
||||
for ( unsigned int i = 0; i < attr2Tokens.size(); i++ )
|
||||
{
|
||||
StringVector attr3Tokens = split( attr2Tokens[i], "=" );
|
||||
//Info( "Name = %s, Value = %s", attr3Tokens[0].c_str(), attr3Tokens[1].c_str() );
|
||||
if ( attr3Tokens[0] == "profile-level-id" )
|
||||
{
|
||||
if ( attrName == "control" )
|
||||
{
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP control attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||
currMedia->setControlUrl( tokens[1] );
|
||||
}
|
||||
else if ( attrName == "range" )
|
||||
{
|
||||
}
|
||||
else if ( attrName == "rtpmap" )
|
||||
{
|
||||
// a=rtpmap:96 MP4V-ES/90000
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP rtpmap attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||
StringVector attrTokens = split( tokens[1], " " );
|
||||
int payloadType = atoi(attrTokens[0].c_str());
|
||||
if ( payloadType != currMedia->getPayloadType() )
|
||||
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
|
||||
std::string payloadDesc = attrTokens[1];
|
||||
//currMedia->setPayloadType( payloadType );
|
||||
if ( attrTokens.size() > 1 )
|
||||
{
|
||||
StringVector payloadTokens = split( attrTokens[1], "/" );
|
||||
std::string payloadDesc = payloadTokens[0];
|
||||
int payloadClock = atoi(payloadTokens[1].c_str());
|
||||
currMedia->setPayloadDesc( payloadDesc );
|
||||
currMedia->setClock( payloadClock );
|
||||
}
|
||||
}
|
||||
else if ( attrName == "framesize" )
|
||||
{
|
||||
// a=framesize:96 320-240
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP framesize attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||
StringVector attrTokens = split( tokens[1], " " );
|
||||
int payloadType = atoi(attrTokens[0].c_str());
|
||||
if ( payloadType != currMedia->getPayloadType() )
|
||||
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
|
||||
//currMedia->setPayloadType( payloadType );
|
||||
StringVector sizeTokens = split( attrTokens[1], "-" );
|
||||
int width = atoi(sizeTokens[0].c_str());
|
||||
int height = atoi(sizeTokens[1].c_str());
|
||||
currMedia->setFrameSize( width, height );
|
||||
}
|
||||
else if ( attrName == "framerate" )
|
||||
{
|
||||
// a=framerate:5.0
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP framerate attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||
double frameRate = atof(tokens[1].c_str());
|
||||
currMedia->setFrameRate( frameRate );
|
||||
}
|
||||
else if ( attrName == "fmtp" )
|
||||
{
|
||||
// a=fmtp:96 profile-level-id=247; config=000001B0F7000001B509000001000000012008D48D8803250F042D14440F
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP fmtp attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||
StringVector attrTokens = split( tokens[1], " ", 2 );
|
||||
int payloadType = atoi(attrTokens[0].c_str());
|
||||
if ( payloadType != currMedia->getPayloadType() )
|
||||
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
|
||||
//currMedia->setPayloadType( payloadType );
|
||||
if ( attrTokens.size() > 1 )
|
||||
{
|
||||
StringVector attr2Tokens = split( attrTokens[1], "; " );
|
||||
for ( unsigned int i = 0; i < attr2Tokens.size(); i++ )
|
||||
{
|
||||
StringVector attr3Tokens = split( attr2Tokens[i], "=" );
|
||||
//Info( "Name = %s, Value = %s", attr3Tokens[0].c_str(), attr3Tokens[1].c_str() );
|
||||
if ( attr3Tokens[0] == "profile-level-id" )
|
||||
{
|
||||
}
|
||||
else if ( attr3Tokens[0] == "config" )
|
||||
{
|
||||
}
|
||||
else if ( attr3Tokens[0] == "sprop-parameter-sets" )
|
||||
{
|
||||
size_t t = attr2Tokens[i].find("=");
|
||||
char *c = (char *)attr2Tokens[i].c_str() + t + 1;
|
||||
Debug(4, "sprop-parameter-sets value %s", c);
|
||||
currMedia->setSprops(std::string(c));
|
||||
}
|
||||
else if ( attr3Tokens[0] == "sprop-parameter-sets" )
|
||||
{
|
||||
size_t t = attr2Tokens[i].find("=");
|
||||
char *c = (char *)attr2Tokens[i].c_str() + t + 1;
|
||||
Debug(4, "sprop-parameter-sets value %s", c);
|
||||
currMedia->setSprops(std::string(c));
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug( 3, "Ignoring SDP fmtp attribute '%s' for media '%s'", attr3Tokens[0].c_str(), currMedia->getType().c_str() )
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( attrName == "mpeg4-iod" )
|
||||
{
|
||||
// a=mpeg4-iod: "data:application/mpeg4-iod;base64,AoEAAE8BAf73AQOAkwABQHRkYXRhOmFwcGxpY2F0aW9uL21wZWc0LW9kLWF1O2Jhc2U2NCxBVGdCR3dVZkF4Y0F5U1FBWlFRTklCRUVrK0FBQWEyd0FBR3RzQVlCQkFFWkFwOERGUUJsQlFRTlFCVUFDN2dBQVBvQUFBRDZBQVlCQXc9PQQNAQUABAAAAAAAAAAAAAYJAQAAAAAAAAAAA0IAAkA+ZGF0YTphcHBsaWNhdGlvbi9tcGVnNC1iaWZzLWF1O2Jhc2U2NCx3QkFTZ1RBcUJYSmhCSWhRUlFVL0FBPT0EEgINAAACAAAAAAAAAAAFAwAAQAYJAQAAAAAAAAAA"
|
||||
}
|
||||
else if ( attrName == "mpeg4-esid" )
|
||||
{
|
||||
// a=mpeg4-esid:201
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug( 3, "Ignoring SDP attribute '%s' for media '%s'", line.c_str(), currMedia->getType().c_str() )
|
||||
}
|
||||
}
|
||||
else if ( attr3Tokens[0] == "config" )
|
||||
{
|
||||
}
|
||||
else if ( attr3Tokens[0] == "sprop-parameter-sets" )
|
||||
{
|
||||
size_t t = attr2Tokens[i].find("=");
|
||||
char *c = (char *)attr2Tokens[i].c_str() + t + 1;
|
||||
Debug(4, "sprop-parameter-sets value %s", c);
|
||||
currMedia->setSprops(std::string(c));
|
||||
}
|
||||
else if ( attr3Tokens[0] == "sprop-parameter-sets" )
|
||||
{
|
||||
size_t t = attr2Tokens[i].find("=");
|
||||
char *c = (char *)attr2Tokens[i].c_str() + t + 1;
|
||||
Debug(4, "sprop-parameter-sets value %s", c);
|
||||
currMedia->setSprops(std::string(c));
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug( 3, "Ignoring general SDP attribute '%s'", line.c_str() );
|
||||
Debug( 3, "Ignoring SDP fmtp attribute '%s' for media '%s'", attr3Tokens[0].c_str(), currMedia->getType().c_str() )
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'm' :
|
||||
{
|
||||
StringVector tokens = split( line, " " );
|
||||
if ( tokens.size() < 4 )
|
||||
throw Exception( "Can't parse SDP media description '"+line+"'" );
|
||||
std::string mediaType = tokens[0];
|
||||
if ( mediaType != "audio" && mediaType != "video" && mediaType != "application" )
|
||||
throw Exception( "Unsupported media type '"+mediaType+"' in SDP media attribute '"+line+"'" );
|
||||
StringVector portTokens = split( tokens[1], "/" );
|
||||
int mediaPort = atoi(portTokens[0].c_str());
|
||||
int mediaNumPorts = 1;
|
||||
if ( portTokens.size() > 1 )
|
||||
mediaNumPorts = atoi(portTokens[1].c_str());
|
||||
std::string mediaTransport = tokens[2];
|
||||
if ( mediaTransport != "RTP/AVP" )
|
||||
throw Exception( "Unsupported media transport '"+mediaTransport+"' in SDP media attribute '"+line+"'" );
|
||||
int payloadType = atoi(tokens[3].c_str());
|
||||
currMedia = new MediaDescriptor( mediaType, mediaPort, mediaNumPorts, mediaTransport, payloadType );
|
||||
mMediaList.push_back( currMedia );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( attrName == "mpeg4-iod" )
|
||||
{
|
||||
// a=mpeg4-iod: "data:application/mpeg4-iod;base64,AoEAAE8BAf73AQOAkwABQHRkYXRhOmFwcGxpY2F0aW9uL21wZWc0LW9kLWF1O2Jhc2U2NCxBVGdCR3dVZkF4Y0F5U1FBWlFRTklCRUVrK0FBQWEyd0FBR3RzQVlCQkFFWkFwOERGUUJsQlFRTlFCVUFDN2dBQVBvQUFBRDZBQVlCQXc9PQQNAQUABAAAAAAAAAAAAAYJAQAAAAAAAAAAA0IAAkA+ZGF0YTphcHBsaWNhdGlvbi9tcGVnNC1iaWZzLWF1O2Jhc2U2NCx3QkFTZ1RBcUJYSmhCSWhRUlFVL0FBPT0EEgINAAACAAAAAAAAAAAFAwAAQAYJAQAAAAAAAAAA"
|
||||
}
|
||||
else if ( attrName == "mpeg4-esid" )
|
||||
{
|
||||
// a=mpeg4-esid:201
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug( 3, "Ignoring SDP attribute '%s' for media '%s'", line.c_str(), currMedia->getType().c_str() )
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug( 3, "Ignoring general SDP attribute '%s'", line.c_str() );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'm' :
|
||||
{
|
||||
StringVector tokens = split( line, " " );
|
||||
if ( tokens.size() < 4 )
|
||||
throw Exception( "Can't parse SDP media description '"+line+"'" );
|
||||
std::string mediaType = tokens[0];
|
||||
if ( mediaType != "audio" && mediaType != "video" && mediaType != "application" )
|
||||
throw Exception( "Unsupported media type '"+mediaType+"' in SDP media attribute '"+line+"'" );
|
||||
StringVector portTokens = split( tokens[1], "/" );
|
||||
int mediaPort = atoi(portTokens[0].c_str());
|
||||
int mediaNumPorts = 1;
|
||||
if ( portTokens.size() > 1 )
|
||||
mediaNumPorts = atoi(portTokens[1].c_str());
|
||||
std::string mediaTransport = tokens[2];
|
||||
if ( mediaTransport != "RTP/AVP" )
|
||||
throw Exception( "Unsupported media transport '"+mediaTransport+"' in SDP media attribute '"+line+"'" );
|
||||
int payloadType = atoi(tokens[3].c_str());
|
||||
currMedia = new MediaDescriptor( mediaType, mediaPort, mediaNumPorts, mediaTransport, payloadType );
|
||||
mMediaList.push_back( currMedia );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SessionDescriptor::~SessionDescriptor()
|
||||
{
|
||||
if ( mConnInfo )
|
||||
delete mConnInfo;
|
||||
if ( mBandInfo )
|
||||
delete mBandInfo;
|
||||
for ( unsigned int i = 0; i < mMediaList.size(); i++ )
|
||||
delete mMediaList[i];
|
||||
if ( mConnInfo )
|
||||
delete mConnInfo;
|
||||
if ( mBandInfo )
|
||||
delete mBandInfo;
|
||||
for ( unsigned int i = 0; i < mMediaList.size(); i++ )
|
||||
delete mMediaList[i];
|
||||
}
|
||||
|
||||
AVFormatContext *SessionDescriptor::generateFormatContext() const
|
||||
{
|
||||
AVFormatContext *formatContext = avformat_alloc_context();
|
||||
AVFormatContext *formatContext = avformat_alloc_context();
|
||||
|
||||
strncpy( formatContext->filename, mUrl.c_str(), sizeof(formatContext->filename) );
|
||||
strncpy( formatContext->filename, mUrl.c_str(), sizeof(formatContext->filename) );
|
||||
/*
|
||||
if ( mName.length() )
|
||||
strncpy( formatContext->title, mName.c_str(), sizeof(formatContext->title) );
|
||||
if ( mInfo.length() )
|
||||
strncpy( formatContext->comment, mInfo.c_str(), sizeof(formatContext->comment) );
|
||||
if ( mName.length() )
|
||||
strncpy( formatContext->title, mName.c_str(), sizeof(formatContext->title) );
|
||||
if ( mInfo.length() )
|
||||
strncpy( formatContext->comment, mInfo.c_str(), sizeof(formatContext->comment) );
|
||||
*/
|
||||
//formatContext->nb_streams = mMediaList.size();
|
||||
for ( unsigned int i = 0; i < mMediaList.size(); i++ )
|
||||
{
|
||||
const MediaDescriptor *mediaDesc = mMediaList[i];
|
||||
//formatContext->nb_streams = mMediaList.size();
|
||||
for ( unsigned int i = 0; i < mMediaList.size(); i++ )
|
||||
{
|
||||
const MediaDescriptor *mediaDesc = mMediaList[i];
|
||||
#if !LIBAVFORMAT_VERSION_CHECK(53, 10, 0, 17, 0)
|
||||
AVStream *stream = av_new_stream( formatContext, i );
|
||||
AVStream *stream = av_new_stream( formatContext, i );
|
||||
#else
|
||||
AVStream *stream = avformat_new_stream( formatContext, NULL );
|
||||
stream->id = i;
|
||||
AVStream *stream = avformat_new_stream( formatContext, NULL );
|
||||
stream->id = i;
|
||||
#endif
|
||||
|
||||
Debug( 1, "Looking for codec for %s payload type %d / %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
|
||||
Debug( 1, "Looking for codec for %s payload type %d / %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
|
||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||
if ( mediaDesc->getType() == "video" )
|
||||
stream->codec->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||
else if ( mediaDesc->getType() == "audio" )
|
||||
stream->codec->codec_type = AVMEDIA_TYPE_AUDIO;
|
||||
else if ( mediaDesc->getType() == "application" )
|
||||
stream->codec->codec_type = AVMEDIA_TYPE_DATA;
|
||||
if ( mediaDesc->getType() == "video" )
|
||||
stream->codec->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||
else if ( mediaDesc->getType() == "audio" )
|
||||
stream->codec->codec_type = AVMEDIA_TYPE_AUDIO;
|
||||
else if ( mediaDesc->getType() == "application" )
|
||||
stream->codec->codec_type = AVMEDIA_TYPE_DATA;
|
||||
#else
|
||||
if ( mediaDesc->getType() == "video" )
|
||||
stream->codec->codec_type = CODEC_TYPE_VIDEO;
|
||||
else if ( mediaDesc->getType() == "audio" )
|
||||
stream->codec->codec_type = CODEC_TYPE_AUDIO;
|
||||
else if ( mediaDesc->getType() == "application" )
|
||||
stream->codec->codec_type = CODEC_TYPE_DATA;
|
||||
if ( mediaDesc->getType() == "video" )
|
||||
stream->codec->codec_type = CODEC_TYPE_VIDEO;
|
||||
else if ( mediaDesc->getType() == "audio" )
|
||||
stream->codec->codec_type = CODEC_TYPE_AUDIO;
|
||||
else if ( mediaDesc->getType() == "application" )
|
||||
stream->codec->codec_type = CODEC_TYPE_DATA;
|
||||
#endif
|
||||
|
||||
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
||||
std::string codec_name;
|
||||
std::string codec_name;
|
||||
#endif
|
||||
if ( mediaDesc->getPayloadType() < PAYLOAD_TYPE_DYNAMIC )
|
||||
if ( mediaDesc->getPayloadType() < PAYLOAD_TYPE_DYNAMIC )
|
||||
{
|
||||
// Look in static table
|
||||
for ( unsigned int i = 0; i < (sizeof(smStaticPayloads)/sizeof(*smStaticPayloads)); i++ )
|
||||
{
|
||||
if ( smStaticPayloads[i].payloadType == mediaDesc->getPayloadType() )
|
||||
{
|
||||
// Look in static table
|
||||
for ( unsigned int i = 0; i < (sizeof(smStaticPayloads)/sizeof(*smStaticPayloads)); i++ )
|
||||
{
|
||||
if ( smStaticPayloads[i].payloadType == mediaDesc->getPayloadType() )
|
||||
{
|
||||
Debug( 1, "Got static payload type %d, %s", smStaticPayloads[i].payloadType, smStaticPayloads[i].payloadName );
|
||||
Debug( 1, "Got static payload type %d, %s", smStaticPayloads[i].payloadType, smStaticPayloads[i].payloadName );
|
||||
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
||||
codec_name = std::string( smStaticPayloads[i].payloadName );
|
||||
codec_name = std::string( smStaticPayloads[i].payloadName );
|
||||
#else
|
||||
strncpy( stream->codec->codec_name, smStaticPayloads[i].payloadName, sizeof(stream->codec->codec_name) );;
|
||||
strncpy( stream->codec->codec_name, smStaticPayloads[i].payloadName, sizeof(stream->codec->codec_name) );;
|
||||
#endif
|
||||
stream->codec->codec_type = smStaticPayloads[i].codecType;
|
||||
stream->codec->codec_id = smStaticPayloads[i].codecId;
|
||||
stream->codec->sample_rate = smStaticPayloads[i].clockRate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
stream->codec->codec_type = smStaticPayloads[i].codecType;
|
||||
stream->codec->codec_id = smStaticPayloads[i].codecId;
|
||||
stream->codec->sample_rate = smStaticPayloads[i].clockRate;
|
||||
break;
|
||||
}
|
||||
else
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Look in dynamic table
|
||||
for ( unsigned int i = 0; i < (sizeof(smDynamicPayloads)/sizeof(*smDynamicPayloads)); i++ )
|
||||
{
|
||||
if ( smDynamicPayloads[i].payloadName == mediaDesc->getPayloadDesc() )
|
||||
{
|
||||
// Look in dynamic table
|
||||
for ( unsigned int i = 0; i < (sizeof(smDynamicPayloads)/sizeof(*smDynamicPayloads)); i++ )
|
||||
{
|
||||
if ( smDynamicPayloads[i].payloadName == mediaDesc->getPayloadDesc() )
|
||||
{
|
||||
Debug( 1, "Got dynamic payload type %d, %s", mediaDesc->getPayloadType(), smDynamicPayloads[i].payloadName );
|
||||
Debug( 1, "Got dynamic payload type %d, %s", mediaDesc->getPayloadType(), smDynamicPayloads[i].payloadName );
|
||||
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
||||
codec_name = std::string( smStaticPayloads[i].payloadName );
|
||||
codec_name = std::string( smStaticPayloads[i].payloadName );
|
||||
#else
|
||||
strncpy( stream->codec->codec_name, smDynamicPayloads[i].payloadName, sizeof(stream->codec->codec_name) );;
|
||||
strncpy( stream->codec->codec_name, smDynamicPayloads[i].payloadName, sizeof(stream->codec->codec_name) );;
|
||||
#endif
|
||||
stream->codec->codec_type = smDynamicPayloads[i].codecType;
|
||||
stream->codec->codec_id = smDynamicPayloads[i].codecId;
|
||||
stream->codec->sample_rate = mediaDesc->getClock();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
||||
if ( codec_name.empty() )
|
||||
#else
|
||||
if ( !stream->codec->codec_name[0] )
|
||||
#endif
|
||||
{
|
||||
Warning( "Can't find payload details for %s payload type %d, name %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
|
||||
//return( 0 );
|
||||
}
|
||||
if ( mediaDesc->getWidth() )
|
||||
stream->codec->width = mediaDesc->getWidth();
|
||||
if ( mediaDesc->getHeight() )
|
||||
stream->codec->height = mediaDesc->getHeight();
|
||||
if ( stream->codec->codec_id == AV_CODEC_ID_H264 && mediaDesc->getSprops().size())
|
||||
{
|
||||
uint8_t start_sequence[]= { 0, 0, 1 };
|
||||
stream->codec->extradata_size= 0;
|
||||
stream->codec->extradata= NULL;
|
||||
char pvalue[1024], *value = pvalue;
|
||||
|
||||
strcpy(pvalue, mediaDesc->getSprops().c_str());
|
||||
|
||||
while (*value) {
|
||||
char base64packet[1024];
|
||||
uint8_t decoded_packet[1024];
|
||||
uint32_t packet_size;
|
||||
char *dst = base64packet;
|
||||
|
||||
while (*value && *value != ','
|
||||
&& (dst - base64packet) < (long)(sizeof(base64packet)) - 1) {
|
||||
*dst++ = *value++;
|
||||
}
|
||||
*dst++ = '\0';
|
||||
|
||||
if (*value == ',')
|
||||
value++;
|
||||
|
||||
packet_size= av_base64_decode(decoded_packet, (const char *)base64packet, (int)sizeof(decoded_packet));
|
||||
Hexdump(4, (char *)decoded_packet, packet_size);
|
||||
if (packet_size) {
|
||||
uint8_t *dest =
|
||||
(uint8_t *)av_malloc(packet_size + sizeof(start_sequence) +
|
||||
stream->codec->extradata_size +
|
||||
FF_INPUT_BUFFER_PADDING_SIZE);
|
||||
if(dest) {
|
||||
if(stream->codec->extradata_size) {
|
||||
// av_realloc?
|
||||
memcpy(dest, stream->codec->extradata, stream->codec->extradata_size);
|
||||
av_free(stream->codec->extradata);
|
||||
}
|
||||
|
||||
memcpy(dest+stream->codec->extradata_size, start_sequence, sizeof(start_sequence));
|
||||
memcpy(dest+stream->codec->extradata_size+sizeof(start_sequence), decoded_packet, packet_size);
|
||||
memset(dest+stream->codec->extradata_size+sizeof(start_sequence)+
|
||||
packet_size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
|
||||
|
||||
stream->codec->extradata= dest;
|
||||
stream->codec->extradata_size+= sizeof(start_sequence)+packet_size;
|
||||
// } else {
|
||||
// av_log(codec, AV_LOG_ERROR, "Unable to allocate memory for extradata!");
|
||||
// return AVERROR(ENOMEM);
|
||||
}
|
||||
}
|
||||
}
|
||||
stream->codec->codec_type = smDynamicPayloads[i].codecType;
|
||||
stream->codec->codec_id = smDynamicPayloads[i].codecId;
|
||||
stream->codec->sample_rate = mediaDesc->getClock();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return( formatContext );
|
||||
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
||||
if ( codec_name.empty() )
|
||||
#else
|
||||
if ( !stream->codec->codec_name[0] )
|
||||
#endif
|
||||
{
|
||||
Warning( "Can't find payload details for %s payload type %d, name %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
|
||||
//return( 0 );
|
||||
}
|
||||
if ( mediaDesc->getWidth() )
|
||||
stream->codec->width = mediaDesc->getWidth();
|
||||
if ( mediaDesc->getHeight() )
|
||||
stream->codec->height = mediaDesc->getHeight();
|
||||
if ( stream->codec->codec_id == AV_CODEC_ID_H264 && mediaDesc->getSprops().size())
|
||||
{
|
||||
uint8_t start_sequence[]= { 0, 0, 1 };
|
||||
stream->codec->extradata_size= 0;
|
||||
stream->codec->extradata= NULL;
|
||||
char pvalue[1024], *value = pvalue;
|
||||
|
||||
strcpy(pvalue, mediaDesc->getSprops().c_str());
|
||||
|
||||
while (*value) {
|
||||
char base64packet[1024];
|
||||
uint8_t decoded_packet[1024];
|
||||
uint32_t packet_size;
|
||||
char *dst = base64packet;
|
||||
|
||||
while (*value && *value != ','
|
||||
&& (dst - base64packet) < (long)(sizeof(base64packet)) - 1) {
|
||||
*dst++ = *value++;
|
||||
}
|
||||
*dst++ = '\0';
|
||||
|
||||
if (*value == ',')
|
||||
value++;
|
||||
|
||||
packet_size= av_base64_decode(decoded_packet, (const char *)base64packet, (int)sizeof(decoded_packet));
|
||||
Hexdump(4, (char *)decoded_packet, packet_size);
|
||||
if (packet_size) {
|
||||
uint8_t *dest =
|
||||
(uint8_t *)av_malloc(packet_size + sizeof(start_sequence) +
|
||||
stream->codec->extradata_size +
|
||||
FF_INPUT_BUFFER_PADDING_SIZE);
|
||||
if(dest) {
|
||||
if(stream->codec->extradata_size) {
|
||||
// av_realloc?
|
||||
memcpy(dest, stream->codec->extradata, stream->codec->extradata_size);
|
||||
av_free(stream->codec->extradata);
|
||||
}
|
||||
|
||||
memcpy(dest+stream->codec->extradata_size, start_sequence, sizeof(start_sequence));
|
||||
memcpy(dest+stream->codec->extradata_size+sizeof(start_sequence), decoded_packet, packet_size);
|
||||
memset(dest+stream->codec->extradata_size+sizeof(start_sequence)+
|
||||
packet_size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
|
||||
|
||||
stream->codec->extradata= dest;
|
||||
stream->codec->extradata_size+= sizeof(start_sequence)+packet_size;
|
||||
// } else {
|
||||
// av_log(codec, AV_LOG_ERROR, "Unable to allocate memory for extradata!");
|
||||
// return AVERROR(ENOMEM);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return( formatContext );
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBAVFORMAT
|
||||
|
|
348
src/zm_sdp.h
348
src/zm_sdp.h
|
@ -34,204 +34,204 @@
|
|||
class SessionDescriptor
|
||||
{
|
||||
protected:
|
||||
enum { PAYLOAD_TYPE_DYNAMIC=96 };
|
||||
enum { PAYLOAD_TYPE_DYNAMIC=96 };
|
||||
|
||||
struct StaticPayloadDesc
|
||||
{
|
||||
int payloadType;
|
||||
const char payloadName[6];
|
||||
struct StaticPayloadDesc
|
||||
{
|
||||
int payloadType;
|
||||
const char payloadName[6];
|
||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||
AVMediaType codecType;
|
||||
AVMediaType codecType;
|
||||
#else
|
||||
enum CodecType codecType;
|
||||
enum CodecType codecType;
|
||||
#endif
|
||||
_AVCODECID codecId;
|
||||
int clockRate;
|
||||
int autoChannels;
|
||||
};
|
||||
_AVCODECID codecId;
|
||||
int clockRate;
|
||||
int autoChannels;
|
||||
};
|
||||
|
||||
struct DynamicPayloadDesc
|
||||
{
|
||||
const char payloadName[32];
|
||||
struct DynamicPayloadDesc
|
||||
{
|
||||
const char payloadName[32];
|
||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||
AVMediaType codecType;
|
||||
AVMediaType codecType;
|
||||
#else
|
||||
enum CodecType codecType;
|
||||
enum CodecType codecType;
|
||||
#endif
|
||||
_AVCODECID codecId;
|
||||
_AVCODECID codecId;
|
||||
|
||||
//int clockRate;
|
||||
//int autoChannels;
|
||||
};
|
||||
//int clockRate;
|
||||
//int autoChannels;
|
||||
};
|
||||
|
||||
public:
|
||||
class ConnInfo
|
||||
{
|
||||
protected:
|
||||
std::string mNetworkType;
|
||||
std::string mAddressType;
|
||||
std::string mAddress;
|
||||
int mTtl;
|
||||
int mNoAddresses;
|
||||
class ConnInfo
|
||||
{
|
||||
protected:
|
||||
std::string mNetworkType;
|
||||
std::string mAddressType;
|
||||
std::string mAddress;
|
||||
int mTtl;
|
||||
int mNoAddresses;
|
||||
|
||||
public:
|
||||
ConnInfo( const std::string &connInfo );
|
||||
};
|
||||
public:
|
||||
ConnInfo( const std::string &connInfo );
|
||||
};
|
||||
|
||||
class BandInfo
|
||||
{
|
||||
protected:
|
||||
std::string mType;
|
||||
int mValue;
|
||||
class BandInfo
|
||||
{
|
||||
protected:
|
||||
std::string mType;
|
||||
int mValue;
|
||||
|
||||
public:
|
||||
BandInfo( const std::string &bandInfo );
|
||||
};
|
||||
public:
|
||||
BandInfo( const std::string &bandInfo );
|
||||
};
|
||||
|
||||
class MediaDescriptor
|
||||
{
|
||||
protected:
|
||||
std::string mType;
|
||||
int mPort;
|
||||
int mNumPorts;
|
||||
std::string mTransport;
|
||||
int mPayloadType;
|
||||
class MediaDescriptor
|
||||
{
|
||||
protected:
|
||||
std::string mType;
|
||||
int mPort;
|
||||
int mNumPorts;
|
||||
std::string mTransport;
|
||||
int mPayloadType;
|
||||
|
||||
std::string mPayloadDesc;
|
||||
std::string mControlUrl;
|
||||
double mFrameRate;
|
||||
int mClock;
|
||||
int mWidth;
|
||||
int mHeight;
|
||||
std::string mSprops;
|
||||
|
||||
ConnInfo *mConnInfo;
|
||||
|
||||
public:
|
||||
MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType );
|
||||
|
||||
const std::string &getType() const
|
||||
{
|
||||
return( mType );
|
||||
}
|
||||
int getPort() const
|
||||
{
|
||||
return( mPort );
|
||||
}
|
||||
int getNumPorts() const
|
||||
{
|
||||
return( mNumPorts );
|
||||
}
|
||||
const std::string &getTransport() const
|
||||
{
|
||||
return( mTransport );
|
||||
}
|
||||
const int getPayloadType() const
|
||||
{
|
||||
return( mPayloadType );
|
||||
}
|
||||
|
||||
const std::string &getPayloadDesc() const
|
||||
{
|
||||
return( mPayloadDesc );
|
||||
}
|
||||
void setPayloadDesc( const std::string &payloadDesc )
|
||||
{
|
||||
mPayloadDesc = payloadDesc;
|
||||
}
|
||||
|
||||
const std::string &getControlUrl() const
|
||||
{
|
||||
return( mControlUrl );
|
||||
}
|
||||
void setControlUrl( const std::string &controlUrl )
|
||||
{
|
||||
mControlUrl = controlUrl;
|
||||
}
|
||||
|
||||
const int getClock() const
|
||||
{
|
||||
return( mClock );
|
||||
}
|
||||
void setClock( int clock )
|
||||
{
|
||||
mClock = clock;
|
||||
}
|
||||
|
||||
void setFrameSize( int width, int height )
|
||||
{
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
}
|
||||
int getWidth() const
|
||||
{
|
||||
return( mWidth );
|
||||
}
|
||||
int getHeight() const
|
||||
{
|
||||
return( mHeight );
|
||||
}
|
||||
|
||||
void setSprops(const std::string props)
|
||||
{
|
||||
mSprops = props;
|
||||
}
|
||||
const std::string getSprops() const
|
||||
{
|
||||
return ( mSprops );
|
||||
}
|
||||
const double getFrameRate() const
|
||||
{
|
||||
return( mFrameRate );
|
||||
}
|
||||
void setFrameRate( double frameRate )
|
||||
{
|
||||
mFrameRate = frameRate;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::vector<MediaDescriptor *> MediaList;
|
||||
|
||||
protected:
|
||||
static StaticPayloadDesc smStaticPayloads[];
|
||||
static DynamicPayloadDesc smDynamicPayloads[];
|
||||
|
||||
protected:
|
||||
std::string mUrl;
|
||||
|
||||
std::string mVersion;
|
||||
std::string mOwner;
|
||||
std::string mName;
|
||||
std::string mInfo;
|
||||
std::string mPayloadDesc;
|
||||
std::string mControlUrl;
|
||||
double mFrameRate;
|
||||
int mClock;
|
||||
int mWidth;
|
||||
int mHeight;
|
||||
std::string mSprops;
|
||||
|
||||
ConnInfo *mConnInfo;
|
||||
BandInfo *mBandInfo;
|
||||
std::string mTimeInfo;
|
||||
StringVector mAttributes;
|
||||
|
||||
MediaList mMediaList;
|
||||
public:
|
||||
MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType );
|
||||
|
||||
const std::string &getType() const
|
||||
{
|
||||
return( mType );
|
||||
}
|
||||
int getPort() const
|
||||
{
|
||||
return( mPort );
|
||||
}
|
||||
int getNumPorts() const
|
||||
{
|
||||
return( mNumPorts );
|
||||
}
|
||||
const std::string &getTransport() const
|
||||
{
|
||||
return( mTransport );
|
||||
}
|
||||
const int getPayloadType() const
|
||||
{
|
||||
return( mPayloadType );
|
||||
}
|
||||
|
||||
const std::string &getPayloadDesc() const
|
||||
{
|
||||
return( mPayloadDesc );
|
||||
}
|
||||
void setPayloadDesc( const std::string &payloadDesc )
|
||||
{
|
||||
mPayloadDesc = payloadDesc;
|
||||
}
|
||||
|
||||
const std::string &getControlUrl() const
|
||||
{
|
||||
return( mControlUrl );
|
||||
}
|
||||
void setControlUrl( const std::string &controlUrl )
|
||||
{
|
||||
mControlUrl = controlUrl;
|
||||
}
|
||||
|
||||
const int getClock() const
|
||||
{
|
||||
return( mClock );
|
||||
}
|
||||
void setClock( int clock )
|
||||
{
|
||||
mClock = clock;
|
||||
}
|
||||
|
||||
void setFrameSize( int width, int height )
|
||||
{
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
}
|
||||
int getWidth() const
|
||||
{
|
||||
return( mWidth );
|
||||
}
|
||||
int getHeight() const
|
||||
{
|
||||
return( mHeight );
|
||||
}
|
||||
|
||||
void setSprops(const std::string props)
|
||||
{
|
||||
mSprops = props;
|
||||
}
|
||||
const std::string getSprops() const
|
||||
{
|
||||
return ( mSprops );
|
||||
}
|
||||
const double getFrameRate() const
|
||||
{
|
||||
return( mFrameRate );
|
||||
}
|
||||
void setFrameRate( double frameRate )
|
||||
{
|
||||
mFrameRate = frameRate;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::vector<MediaDescriptor *> MediaList;
|
||||
|
||||
protected:
|
||||
static StaticPayloadDesc smStaticPayloads[];
|
||||
static DynamicPayloadDesc smDynamicPayloads[];
|
||||
|
||||
protected:
|
||||
std::string mUrl;
|
||||
|
||||
std::string mVersion;
|
||||
std::string mOwner;
|
||||
std::string mName;
|
||||
std::string mInfo;
|
||||
|
||||
ConnInfo *mConnInfo;
|
||||
BandInfo *mBandInfo;
|
||||
std::string mTimeInfo;
|
||||
StringVector mAttributes;
|
||||
|
||||
MediaList mMediaList;
|
||||
|
||||
public:
|
||||
SessionDescriptor( const std::string &url, const std::string &sdp );
|
||||
~SessionDescriptor();
|
||||
SessionDescriptor( const std::string &url, const std::string &sdp );
|
||||
~SessionDescriptor();
|
||||
|
||||
const std::string &getUrl() const
|
||||
{
|
||||
return( mUrl );
|
||||
}
|
||||
const std::string &getUrl() const
|
||||
{
|
||||
return( mUrl );
|
||||
}
|
||||
|
||||
int getNumStreams() const
|
||||
{
|
||||
return( mMediaList.size() );
|
||||
}
|
||||
MediaDescriptor *getStream( int index )
|
||||
{
|
||||
if ( index < 0 || (unsigned int)index >= mMediaList.size() )
|
||||
return( 0 );
|
||||
return( mMediaList[index] );
|
||||
}
|
||||
int getNumStreams() const
|
||||
{
|
||||
return( mMediaList.size() );
|
||||
}
|
||||
MediaDescriptor *getStream( int index )
|
||||
{
|
||||
if ( index < 0 || (unsigned int)index >= mMediaList.size() )
|
||||
return( 0 );
|
||||
return( mMediaList[index] );
|
||||
}
|
||||
|
||||
AVFormatContext *generateFormatContext() const;
|
||||
AVFormatContext *generateFormatContext() const;
|
||||
};
|
||||
#if 0
|
||||
v=0
|
||||
|
|
|
@ -1,30 +1,30 @@
|
|||
#ifdef HAVE_SENDFILE4_SUPPORT
|
||||
#include <sys/sendfile.h>
|
||||
int zm_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) {
|
||||
int err;
|
||||
int err;
|
||||
|
||||
err = sendfile(out_fd, in_fd, offset, size);
|
||||
if (err < 0)
|
||||
return -errno;
|
||||
err = sendfile(out_fd, in_fd, offset, size);
|
||||
if (err < 0)
|
||||
return -errno;
|
||||
|
||||
return err;
|
||||
return err;
|
||||
}
|
||||
#elif HAVE_SENDFILE7_SUPPORT
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/uio.h>
|
||||
int zm_sendfile(int out_fd, int in_fd, off_t *offset, off_t size) {
|
||||
int err;
|
||||
err = sendfile(in_fd, out_fd, *offset, size, NULL, &size, 0);
|
||||
if (err && errno != EAGAIN)
|
||||
return -errno;
|
||||
int err;
|
||||
err = sendfile(in_fd, out_fd, *offset, size, NULL, &size, 0);
|
||||
if (err && errno != EAGAIN)
|
||||
return -errno;
|
||||
|
||||
if (size) {
|
||||
*offset += size;
|
||||
return size;
|
||||
}
|
||||
if (size) {
|
||||
*offset += size;
|
||||
return size;
|
||||
}
|
||||
|
||||
return -EAGAIN;
|
||||
return -EAGAIN;
|
||||
}
|
||||
#else
|
||||
#error "Your platform does not support sendfile. Sorry."
|
||||
|
|
|
@ -47,15 +47,12 @@ RETSIGTYPE zm_die_handler(int signal, siginfo_t * info, void *context)
|
|||
RETSIGTYPE zm_die_handler(int signal)
|
||||
#endif
|
||||
{
|
||||
#if (defined(__i386__) || defined(__x86_64__))
|
||||
void *cr2 = 0;
|
||||
void *ip = 0;
|
||||
#endif
|
||||
Error("Got signal %d (%s), crashing", signal, strsignal(signal));
|
||||
|
||||
#if (defined(__i386__) || defined(__x86_64__))
|
||||
// Get more information if available
|
||||
#if ( HAVE_SIGINFO_T && HAVE_UCONTEXT_T )
|
||||
#if ( HAVE_SIGINFO_T && HAVE_UCONTEXT_T )
|
||||
void *ip = 0;
|
||||
void *cr2 = 0;
|
||||
if (info && context) {
|
||||
|
||||
Debug(1,
|
||||
|
@ -65,19 +62,19 @@ RETSIGTYPE zm_die_handler(int signal)
|
|||
|
||||
ucontext_t *uc = (ucontext_t *) context;
|
||||
cr2 = info->si_addr;
|
||||
#if defined(__x86_64__)
|
||||
#ifdef __FreeBSD_kernel__
|
||||
#if defined(__x86_64__)
|
||||
#ifdef __FreeBSD_kernel__
|
||||
ip = (void *)(uc->uc_mcontext.mc_rip);
|
||||
#else
|
||||
#else
|
||||
ip = (void *)(uc->uc_mcontext.gregs[REG_RIP]);
|
||||
#endif
|
||||
#else
|
||||
#ifdef __FreeBSD_kernel__
|
||||
#endif
|
||||
#else
|
||||
#ifdef __FreeBSD_kernel__
|
||||
ip = (void *)(uc->uc_mcontext.mc_eip);
|
||||
#else
|
||||
#else
|
||||
ip = (void *)(uc->uc_mcontext.gregs[REG_EIP]);
|
||||
#endif
|
||||
#endif // defined(__x86_64__)
|
||||
#endif
|
||||
#endif // defined(__x86_64__)
|
||||
|
||||
// Print the signal address and instruction pointer if available
|
||||
if (ip) {
|
||||
|
@ -86,11 +83,11 @@ RETSIGTYPE zm_die_handler(int signal)
|
|||
Error("Signal address is %p, no instruction pointer", cr2);
|
||||
}
|
||||
}
|
||||
#endif // ( HAVE_SIGINFO_T && HAVE_UCONTEXT_T )
|
||||
#endif // ( HAVE_SIGINFO_T && HAVE_UCONTEXT_T )
|
||||
|
||||
|
||||
// Print backtrace if enabled and available
|
||||
#if ( !defined(ZM_NO_CRASHTRACE) && HAVE_DECL_BACKTRACE && HAVE_DECL_BACKTRACE_SYMBOLS )
|
||||
#if ( !defined(ZM_NO_CRASHTRACE) && HAVE_DECL_BACKTRACE && HAVE_DECL_BACKTRACE_SYMBOLS )
|
||||
void *trace[TRACE_SIZE];
|
||||
int trace_size = 0;
|
||||
trace_size = backtrace(trace, TRACE_SIZE);
|
||||
|
@ -111,7 +108,7 @@ RETSIGTYPE zm_die_handler(int signal)
|
|||
|
||||
Info("Backtrace complete, please execute the following command for more information");
|
||||
Info(cmd);
|
||||
#endif // ( !defined(ZM_NO_CRASHTRACE) && HAVE_DECL_BACKTRACE && HAVE_DECL_BACKTRACE_SYMBOLS )
|
||||
#endif // ( !defined(ZM_NO_CRASHTRACE) && HAVE_DECL_BACKTRACE && HAVE_DECL_BACKTRACE_SYMBOLS )
|
||||
#endif // (defined(__i386__) || defined(__x86_64__)
|
||||
exit(signal);
|
||||
}
|
||||
|
|
|
@ -50,8 +50,8 @@ Storage::Storage( unsigned int p_id ) {
|
|||
char sql[ZM_SQL_SML_BUFSIZ];
|
||||
snprintf( sql, sizeof(sql), "SELECT Id, Name, Path from Storage WHERE Id=%d", p_id );
|
||||
Debug(1,"Loading Storage for %d using %s", p_id, sql );
|
||||
MYSQL_ROW dbrow = zmDbFetchOne( sql );
|
||||
if ( ! dbrow ) {
|
||||
zmDbRow dbrow;
|
||||
if ( ! dbrow.fetch( sql ) ) {
|
||||
Error( "Unable to load storage area for id %d: %s", p_id, mysql_error( &dbconn ) );
|
||||
} else {
|
||||
unsigned int index = 0;
|
||||
|
|
|
@ -32,326 +32,326 @@
|
|||
StreamBase::~StreamBase()
|
||||
{
|
||||
#if HAVE_LIBAVCODEC
|
||||
if ( vid_stream )
|
||||
{
|
||||
delete vid_stream;
|
||||
vid_stream = NULL;
|
||||
}
|
||||
if ( vid_stream )
|
||||
{
|
||||
delete vid_stream;
|
||||
vid_stream = NULL;
|
||||
}
|
||||
#endif
|
||||
closeComms();
|
||||
closeComms();
|
||||
}
|
||||
|
||||
bool StreamBase::loadMonitor( int monitor_id )
|
||||
{
|
||||
if ( !(monitor = Monitor::Load( monitor_id, false, Monitor::QUERY )) )
|
||||
{
|
||||
Fatal( "Unable to load monitor id %d for streaming", monitor_id );
|
||||
return( false );
|
||||
}
|
||||
monitor->connect();
|
||||
return( true );
|
||||
if ( !(monitor = Monitor::Load( monitor_id, false, Monitor::QUERY )) )
|
||||
{
|
||||
Fatal( "Unable to load monitor id %d for streaming", monitor_id );
|
||||
return( false );
|
||||
}
|
||||
monitor->connect();
|
||||
return( true );
|
||||
}
|
||||
|
||||
bool StreamBase::checkInitialised()
|
||||
{
|
||||
if ( !monitor )
|
||||
{
|
||||
Fatal( "Cannot stream, not initialised" );
|
||||
return( false );
|
||||
}
|
||||
return( true );
|
||||
if ( !monitor )
|
||||
{
|
||||
Fatal( "Cannot stream, not initialised" );
|
||||
return( false );
|
||||
}
|
||||
return( true );
|
||||
}
|
||||
|
||||
void StreamBase::updateFrameRate( double fps )
|
||||
{
|
||||
base_fps = fps;
|
||||
effective_fps = (base_fps*abs(replay_rate))/ZM_RATE_BASE;
|
||||
frame_mod = 1;
|
||||
Debug( 3, "FPS:%.2f, MXFPS:%.2f, BFPS:%.2f, EFPS:%.2f, FM:%d", fps, maxfps, base_fps, effective_fps, frame_mod );
|
||||
// Min frame repeat?
|
||||
while( effective_fps > maxfps )
|
||||
{
|
||||
effective_fps /= 2.0;
|
||||
frame_mod *= 2;
|
||||
}
|
||||
Debug( 3, "aEFPS:%.2f, aFM:%d", effective_fps, frame_mod );
|
||||
base_fps = fps;
|
||||
effective_fps = (base_fps*abs(replay_rate))/ZM_RATE_BASE;
|
||||
frame_mod = 1;
|
||||
Debug( 3, "FPS:%.2f, MXFPS:%.2f, BFPS:%.2f, EFPS:%.2f, FM:%d", fps, maxfps, base_fps, effective_fps, frame_mod );
|
||||
// Min frame repeat?
|
||||
while( effective_fps > maxfps )
|
||||
{
|
||||
effective_fps /= 2.0;
|
||||
frame_mod *= 2;
|
||||
}
|
||||
Debug( 3, "aEFPS:%.2f, aFM:%d", effective_fps, frame_mod );
|
||||
}
|
||||
|
||||
bool StreamBase::checkCommandQueue()
|
||||
{
|
||||
if ( sd >= 0 )
|
||||
if ( sd >= 0 )
|
||||
{
|
||||
CmdMsg msg;
|
||||
memset( &msg, 0, sizeof(msg) );
|
||||
int nbytes = recvfrom( sd, &msg, sizeof(msg), MSG_DONTWAIT, 0, 0 );
|
||||
if ( nbytes < 0 )
|
||||
{
|
||||
CmdMsg msg;
|
||||
memset( &msg, 0, sizeof(msg) );
|
||||
int nbytes = recvfrom( sd, &msg, sizeof(msg), MSG_DONTWAIT, 0, 0 );
|
||||
if ( nbytes < 0 )
|
||||
{
|
||||
if ( errno != EAGAIN )
|
||||
{
|
||||
Fatal( "recvfrom(), errno = %d, error = %s", errno, strerror(errno) );
|
||||
}
|
||||
}
|
||||
//else if ( (nbytes != sizeof(msg)) )
|
||||
//{
|
||||
//Error( "Partial message received, expected %d bytes, got %d", sizeof(msg), nbytes );
|
||||
//}
|
||||
else
|
||||
{
|
||||
processCommand( &msg );
|
||||
return( true );
|
||||
}
|
||||
if ( errno != EAGAIN )
|
||||
{
|
||||
Fatal( "recvfrom(), errno = %d, error = %s", errno, strerror(errno) );
|
||||
}
|
||||
}
|
||||
return( false );
|
||||
//else if ( (nbytes != sizeof(msg)) )
|
||||
//{
|
||||
//Error( "Partial message received, expected %d bytes, got %d", sizeof(msg), nbytes );
|
||||
//}
|
||||
else
|
||||
{
|
||||
processCommand( &msg );
|
||||
return( true );
|
||||
}
|
||||
}
|
||||
return( false );
|
||||
}
|
||||
|
||||
Image *StreamBase::prepareImage( Image *image )
|
||||
{
|
||||
static int last_scale = 0;
|
||||
static int last_zoom = 0;
|
||||
static int last_x = 0;
|
||||
static int last_y = 0;
|
||||
static int last_scale = 0;
|
||||
static int last_zoom = 0;
|
||||
static int last_x = 0;
|
||||
static int last_y = 0;
|
||||
|
||||
if ( !last_scale )
|
||||
last_scale = scale;
|
||||
if ( !last_zoom )
|
||||
last_zoom = zoom;
|
||||
|
||||
// Do not bother to scale zoomed in images, just crop them and let the browser scale
|
||||
// Works in FF2 but breaks FF3 which doesn't like image sizes changing in mid stream.
|
||||
bool optimisedScaling = false;
|
||||
|
||||
bool image_copied = false;
|
||||
|
||||
int mag = (scale * zoom) / ZM_SCALE_BASE;
|
||||
int act_mag = optimisedScaling?(mag > ZM_SCALE_BASE?ZM_SCALE_BASE:mag):mag;
|
||||
Debug( 3, "Scaling by %d, zooming by %d = magnifying by %d(%d)", scale, zoom, mag, act_mag );
|
||||
|
||||
int last_mag = (last_scale * last_zoom) / ZM_SCALE_BASE;
|
||||
int last_act_mag = last_mag > ZM_SCALE_BASE?ZM_SCALE_BASE:last_mag;
|
||||
Debug( 3, "Last scaling by %d, zooming by %d = magnifying by %d(%d)", last_scale, last_zoom, last_mag, last_act_mag );
|
||||
|
||||
int base_image_width = image->Width(), base_image_height = image->Height();
|
||||
Debug( 3, "Base image width = %d, height = %d", base_image_width, base_image_height );
|
||||
|
||||
int virt_image_width = (base_image_width * mag) / ZM_SCALE_BASE, virt_image_height = (base_image_height * mag) / ZM_SCALE_BASE;
|
||||
Debug( 3, "Virtual image width = %d, height = %d", virt_image_width, virt_image_height );
|
||||
|
||||
int last_virt_image_width = (base_image_width * last_mag) / ZM_SCALE_BASE, last_virt_image_height = (base_image_height * last_mag) / ZM_SCALE_BASE;
|
||||
Debug( 3, "Last virtual image width = %d, height = %d", last_virt_image_width, last_virt_image_height );
|
||||
|
||||
int act_image_width = (base_image_width * act_mag ) / ZM_SCALE_BASE, act_image_height = (base_image_height * act_mag ) / ZM_SCALE_BASE;
|
||||
Debug( 3, "Actual image width = %d, height = %d", act_image_width, act_image_height );
|
||||
|
||||
int last_act_image_width = (base_image_width * last_act_mag ) / ZM_SCALE_BASE, last_act_image_height = (base_image_height * last_act_mag ) / ZM_SCALE_BASE;
|
||||
Debug( 3, "Last actual image width = %d, height = %d", last_act_image_width, last_act_image_height );
|
||||
|
||||
int disp_image_width = (image->Width() * scale) / ZM_SCALE_BASE, disp_image_height = (image->Height() * scale) / ZM_SCALE_BASE;
|
||||
Debug( 3, "Display image width = %d, height = %d", disp_image_width, disp_image_height );
|
||||
|
||||
int last_disp_image_width = (image->Width() * last_scale) / ZM_SCALE_BASE, last_disp_image_height = (image->Height() * last_scale) / ZM_SCALE_BASE;
|
||||
Debug( 3, "Last display image width = %d, height = %d", last_disp_image_width, last_disp_image_height );
|
||||
|
||||
int send_image_width = (disp_image_width * act_mag ) / mag, send_image_height = (disp_image_height * act_mag ) / mag;
|
||||
Debug( 3, "Send image width = %d, height = %d", send_image_width, send_image_height );
|
||||
|
||||
int last_send_image_width = (last_disp_image_width * last_act_mag ) / last_mag, last_send_image_height = (last_disp_image_height * last_act_mag ) / last_mag;
|
||||
Debug( 3, "Last send image width = %d, height = %d", last_send_image_width, last_send_image_height );
|
||||
|
||||
if ( mag != ZM_SCALE_BASE )
|
||||
{
|
||||
if ( act_mag != ZM_SCALE_BASE )
|
||||
{
|
||||
Debug( 3, "Magnifying by %d", mag );
|
||||
if ( !image_copied )
|
||||
{
|
||||
static Image copy_image;
|
||||
copy_image.Assign( *image );
|
||||
image = ©_image;
|
||||
image_copied = true;
|
||||
}
|
||||
image->Scale( mag );
|
||||
}
|
||||
}
|
||||
|
||||
Debug( 3, "Real image width = %d, height = %d", image->Width(), image->Height() );
|
||||
|
||||
if ( disp_image_width < virt_image_width || disp_image_height < virt_image_height )
|
||||
{
|
||||
static Box last_crop;
|
||||
|
||||
if ( mag != last_mag || x != last_x || y != last_y )
|
||||
{
|
||||
Debug( 3, "Got click at %d,%d x %d", x, y, mag );
|
||||
|
||||
//if ( !last_mag )
|
||||
//last_mag = mag;
|
||||
|
||||
if ( !(last_disp_image_width < last_virt_image_width || last_disp_image_height < last_virt_image_height) )
|
||||
last_crop = Box();
|
||||
|
||||
Debug( 3, "Recalculating crop" );
|
||||
// Recalculate crop parameters, as %ges
|
||||
int click_x = (last_crop.LoX() * 100 ) / last_act_image_width; // Initial crop offset from last image
|
||||
click_x += ( x * 100 ) / last_virt_image_width;
|
||||
int click_y = (last_crop.LoY() * 100 ) / last_act_image_height; // Initial crop offset from last image
|
||||
click_y += ( y * 100 ) / last_virt_image_height;
|
||||
Debug( 3, "Got adjusted click at %d%%,%d%%", click_x, click_y );
|
||||
|
||||
// Convert the click locations to the current image pixels
|
||||
click_x = ( click_x * act_image_width ) / 100;
|
||||
click_y = ( click_y * act_image_height ) / 100;
|
||||
Debug( 3, "Got readjusted click at %d,%d", click_x, click_y );
|
||||
|
||||
int lo_x = click_x - (send_image_width/2);
|
||||
if ( lo_x < 0 )
|
||||
lo_x = 0;
|
||||
int hi_x = lo_x + (send_image_width-1);
|
||||
if ( hi_x >= act_image_width )
|
||||
{
|
||||
hi_x = act_image_width - 1;
|
||||
lo_x = hi_x - (send_image_width - 1);
|
||||
}
|
||||
|
||||
int lo_y = click_y - (send_image_height/2);
|
||||
if ( lo_y < 0 )
|
||||
lo_y = 0;
|
||||
int hi_y = lo_y + (send_image_height-1);
|
||||
if ( hi_y >= act_image_height )
|
||||
{
|
||||
hi_y = act_image_height - 1;
|
||||
lo_y = hi_y - (send_image_height - 1);
|
||||
}
|
||||
last_crop = Box( lo_x, lo_y, hi_x, hi_y );
|
||||
}
|
||||
Debug( 3, "Cropping to %d,%d -> %d,%d", last_crop.LoX(), last_crop.LoY(), last_crop.HiX(), last_crop.HiY() );
|
||||
if ( !image_copied )
|
||||
{
|
||||
static Image copy_image;
|
||||
copy_image.Assign( *image );
|
||||
image = ©_image;
|
||||
image_copied = true;
|
||||
}
|
||||
image->Crop( last_crop );
|
||||
}
|
||||
if ( !last_scale )
|
||||
last_scale = scale;
|
||||
if ( !last_zoom )
|
||||
last_zoom = zoom;
|
||||
last_x = x;
|
||||
last_y = y;
|
||||
|
||||
return( image );
|
||||
// Do not bother to scale zoomed in images, just crop them and let the browser scale
|
||||
// Works in FF2 but breaks FF3 which doesn't like image sizes changing in mid stream.
|
||||
bool optimisedScaling = false;
|
||||
|
||||
bool image_copied = false;
|
||||
|
||||
int mag = (scale * zoom) / ZM_SCALE_BASE;
|
||||
int act_mag = optimisedScaling?(mag > ZM_SCALE_BASE?ZM_SCALE_BASE:mag):mag;
|
||||
Debug( 3, "Scaling by %d, zooming by %d = magnifying by %d(%d)", scale, zoom, mag, act_mag );
|
||||
|
||||
int last_mag = (last_scale * last_zoom) / ZM_SCALE_BASE;
|
||||
int last_act_mag = last_mag > ZM_SCALE_BASE?ZM_SCALE_BASE:last_mag;
|
||||
Debug( 3, "Last scaling by %d, zooming by %d = magnifying by %d(%d)", last_scale, last_zoom, last_mag, last_act_mag );
|
||||
|
||||
int base_image_width = image->Width(), base_image_height = image->Height();
|
||||
Debug( 3, "Base image width = %d, height = %d", base_image_width, base_image_height );
|
||||
|
||||
int virt_image_width = (base_image_width * mag) / ZM_SCALE_BASE, virt_image_height = (base_image_height * mag) / ZM_SCALE_BASE;
|
||||
Debug( 3, "Virtual image width = %d, height = %d", virt_image_width, virt_image_height );
|
||||
|
||||
int last_virt_image_width = (base_image_width * last_mag) / ZM_SCALE_BASE, last_virt_image_height = (base_image_height * last_mag) / ZM_SCALE_BASE;
|
||||
Debug( 3, "Last virtual image width = %d, height = %d", last_virt_image_width, last_virt_image_height );
|
||||
|
||||
int act_image_width = (base_image_width * act_mag ) / ZM_SCALE_BASE, act_image_height = (base_image_height * act_mag ) / ZM_SCALE_BASE;
|
||||
Debug( 3, "Actual image width = %d, height = %d", act_image_width, act_image_height );
|
||||
|
||||
int last_act_image_width = (base_image_width * last_act_mag ) / ZM_SCALE_BASE, last_act_image_height = (base_image_height * last_act_mag ) / ZM_SCALE_BASE;
|
||||
Debug( 3, "Last actual image width = %d, height = %d", last_act_image_width, last_act_image_height );
|
||||
|
||||
int disp_image_width = (image->Width() * scale) / ZM_SCALE_BASE, disp_image_height = (image->Height() * scale) / ZM_SCALE_BASE;
|
||||
Debug( 3, "Display image width = %d, height = %d", disp_image_width, disp_image_height );
|
||||
|
||||
int last_disp_image_width = (image->Width() * last_scale) / ZM_SCALE_BASE, last_disp_image_height = (image->Height() * last_scale) / ZM_SCALE_BASE;
|
||||
Debug( 3, "Last display image width = %d, height = %d", last_disp_image_width, last_disp_image_height );
|
||||
|
||||
int send_image_width = (disp_image_width * act_mag ) / mag, send_image_height = (disp_image_height * act_mag ) / mag;
|
||||
Debug( 3, "Send image width = %d, height = %d", send_image_width, send_image_height );
|
||||
|
||||
int last_send_image_width = (last_disp_image_width * last_act_mag ) / last_mag, last_send_image_height = (last_disp_image_height * last_act_mag ) / last_mag;
|
||||
Debug( 3, "Last send image width = %d, height = %d", last_send_image_width, last_send_image_height );
|
||||
|
||||
if ( mag != ZM_SCALE_BASE )
|
||||
{
|
||||
if ( act_mag != ZM_SCALE_BASE )
|
||||
{
|
||||
Debug( 3, "Magnifying by %d", mag );
|
||||
if ( !image_copied )
|
||||
{
|
||||
static Image copy_image;
|
||||
copy_image.Assign( *image );
|
||||
image = ©_image;
|
||||
image_copied = true;
|
||||
}
|
||||
image->Scale( mag );
|
||||
}
|
||||
}
|
||||
|
||||
Debug( 3, "Real image width = %d, height = %d", image->Width(), image->Height() );
|
||||
|
||||
if ( disp_image_width < virt_image_width || disp_image_height < virt_image_height )
|
||||
{
|
||||
static Box last_crop;
|
||||
|
||||
if ( mag != last_mag || x != last_x || y != last_y )
|
||||
{
|
||||
Debug( 3, "Got click at %d,%d x %d", x, y, mag );
|
||||
|
||||
//if ( !last_mag )
|
||||
//last_mag = mag;
|
||||
|
||||
if ( !(last_disp_image_width < last_virt_image_width || last_disp_image_height < last_virt_image_height) )
|
||||
last_crop = Box();
|
||||
|
||||
Debug( 3, "Recalculating crop" );
|
||||
// Recalculate crop parameters, as %ges
|
||||
int click_x = (last_crop.LoX() * 100 ) / last_act_image_width; // Initial crop offset from last image
|
||||
click_x += ( x * 100 ) / last_virt_image_width;
|
||||
int click_y = (last_crop.LoY() * 100 ) / last_act_image_height; // Initial crop offset from last image
|
||||
click_y += ( y * 100 ) / last_virt_image_height;
|
||||
Debug( 3, "Got adjusted click at %d%%,%d%%", click_x, click_y );
|
||||
|
||||
// Convert the click locations to the current image pixels
|
||||
click_x = ( click_x * act_image_width ) / 100;
|
||||
click_y = ( click_y * act_image_height ) / 100;
|
||||
Debug( 3, "Got readjusted click at %d,%d", click_x, click_y );
|
||||
|
||||
int lo_x = click_x - (send_image_width/2);
|
||||
if ( lo_x < 0 )
|
||||
lo_x = 0;
|
||||
int hi_x = lo_x + (send_image_width-1);
|
||||
if ( hi_x >= act_image_width )
|
||||
{
|
||||
hi_x = act_image_width - 1;
|
||||
lo_x = hi_x - (send_image_width - 1);
|
||||
}
|
||||
|
||||
int lo_y = click_y - (send_image_height/2);
|
||||
if ( lo_y < 0 )
|
||||
lo_y = 0;
|
||||
int hi_y = lo_y + (send_image_height-1);
|
||||
if ( hi_y >= act_image_height )
|
||||
{
|
||||
hi_y = act_image_height - 1;
|
||||
lo_y = hi_y - (send_image_height - 1);
|
||||
}
|
||||
last_crop = Box( lo_x, lo_y, hi_x, hi_y );
|
||||
}
|
||||
Debug( 3, "Cropping to %d,%d -> %d,%d", last_crop.LoX(), last_crop.LoY(), last_crop.HiX(), last_crop.HiY() );
|
||||
if ( !image_copied )
|
||||
{
|
||||
static Image copy_image;
|
||||
copy_image.Assign( *image );
|
||||
image = ©_image;
|
||||
image_copied = true;
|
||||
}
|
||||
image->Crop( last_crop );
|
||||
}
|
||||
last_scale = scale;
|
||||
last_zoom = zoom;
|
||||
last_x = x;
|
||||
last_y = y;
|
||||
|
||||
return( image );
|
||||
}
|
||||
|
||||
bool StreamBase::sendTextFrame( const char *frame_text )
|
||||
{
|
||||
Debug( 2, "Sending text frame '%s'", frame_text );
|
||||
Debug( 2, "Sending text frame '%s'", frame_text );
|
||||
|
||||
Image image( monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder() );
|
||||
image.Annotate( frame_text, image.centreCoord( frame_text ) );
|
||||
Image image( monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder() );
|
||||
image.Annotate( frame_text, image.centreCoord( frame_text ) );
|
||||
|
||||
if ( scale != 100 )
|
||||
{
|
||||
image.Scale( scale );
|
||||
}
|
||||
if ( scale != 100 )
|
||||
{
|
||||
image.Scale( scale );
|
||||
}
|
||||
#if HAVE_LIBAVCODEC
|
||||
if ( type == STREAM_MPEG )
|
||||
if ( type == STREAM_MPEG )
|
||||
{
|
||||
if ( !vid_stream )
|
||||
{
|
||||
if ( !vid_stream )
|
||||
{
|
||||
vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, image.Colours(), image.SubpixelOrder(), image.Width(), image.Height() );
|
||||
fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() );
|
||||
vid_stream->OpenStream();
|
||||
}
|
||||
/* double pts = */ vid_stream->EncodeFrame( image.Buffer(), image.Size() );
|
||||
vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, image.Colours(), image.SubpixelOrder(), image.Width(), image.Height() );
|
||||
fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() );
|
||||
vid_stream->OpenStream();
|
||||
}
|
||||
else
|
||||
/* double pts = */ vid_stream->EncodeFrame( image.Buffer(), image.Size() );
|
||||
}
|
||||
else
|
||||
#endif // HAVE_LIBAVCODEC
|
||||
{
|
||||
static unsigned char buffer[ZM_MAX_IMAGE_SIZE];
|
||||
int n_bytes = 0;
|
||||
|
||||
image.EncodeJpeg( buffer, &n_bytes );
|
||||
|
||||
fprintf( stdout, "--ZoneMinderFrame\r\n" );
|
||||
fprintf( stdout, "Content-Length: %d\r\n", n_bytes );
|
||||
fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" );
|
||||
if ( fwrite( buffer, n_bytes, 1, stdout ) != 1 )
|
||||
{
|
||||
static unsigned char buffer[ZM_MAX_IMAGE_SIZE];
|
||||
int n_bytes = 0;
|
||||
|
||||
image.EncodeJpeg( buffer, &n_bytes );
|
||||
|
||||
fprintf( stdout, "--ZoneMinderFrame\r\n" );
|
||||
fprintf( stdout, "Content-Length: %d\r\n", n_bytes );
|
||||
fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" );
|
||||
if ( fwrite( buffer, n_bytes, 1, stdout ) != 1 )
|
||||
{
|
||||
Error( "Unable to send stream text frame: %s", strerror(errno) );
|
||||
return( false );
|
||||
}
|
||||
fprintf( stdout, "\r\n\r\n" );
|
||||
fflush( stdout );
|
||||
Error( "Unable to send stream text frame: %s", strerror(errno) );
|
||||
return( false );
|
||||
}
|
||||
last_frame_sent = TV_2_FLOAT( now );
|
||||
return( true );
|
||||
fprintf( stdout, "\r\n\r\n" );
|
||||
fflush( stdout );
|
||||
}
|
||||
last_frame_sent = TV_2_FLOAT( now );
|
||||
return( true );
|
||||
}
|
||||
|
||||
void StreamBase::openComms()
|
||||
{
|
||||
if ( connkey > 0 )
|
||||
if ( connkey > 0 )
|
||||
{
|
||||
|
||||
snprintf( sock_path_lock, sizeof(sock_path_lock), "%s/zms-%06d.lock", config.path_socks, connkey);
|
||||
|
||||
lock_fd = open(sock_path_lock, O_CREAT|O_WRONLY, S_IRUSR | S_IWUSR);
|
||||
if ( lock_fd <= 0 )
|
||||
{
|
||||
|
||||
snprintf( sock_path_lock, sizeof(sock_path_lock), "%s/zms-%06d.lock", config.path_socks, connkey);
|
||||
|
||||
lock_fd = open(sock_path_lock, O_CREAT|O_WRONLY, S_IRUSR | S_IWUSR);
|
||||
if ( lock_fd <= 0 )
|
||||
{
|
||||
Error("Unable to open sock lock file %s: %s", sock_path_lock, strerror(errno) );
|
||||
lock_fd = 0;
|
||||
}
|
||||
else if ( flock(lock_fd, LOCK_EX) != 0 )
|
||||
{
|
||||
Error("Unable to lock sock lock file %s: %s", sock_path_lock, strerror(errno) );
|
||||
|
||||
close(lock_fd);
|
||||
lock_fd = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug( 1, "We have obtained a lock on %s fd: %d", sock_path_lock, lock_fd);
|
||||
}
|
||||
|
||||
|
||||
sd = socket( AF_UNIX, SOCK_DGRAM, 0 );
|
||||
if ( sd < 0 )
|
||||
{
|
||||
Fatal( "Can't create socket: %s", strerror(errno) );
|
||||
}
|
||||
|
||||
snprintf( loc_sock_path, sizeof(loc_sock_path), "%s/zms-%06ds.sock", config.path_socks, connkey );
|
||||
unlink( loc_sock_path );
|
||||
|
||||
strncpy( loc_addr.sun_path, loc_sock_path, sizeof(loc_addr.sun_path) );
|
||||
loc_addr.sun_family = AF_UNIX;
|
||||
if ( bind( sd, (struct sockaddr *)&loc_addr, strlen(loc_addr.sun_path)+sizeof(loc_addr.sun_family)) < 0 )
|
||||
{
|
||||
Fatal( "Can't bind: %s", strerror(errno) );
|
||||
}
|
||||
|
||||
snprintf( rem_sock_path, sizeof(rem_sock_path), "%s/zms-%06dw.sock", config.path_socks, connkey );
|
||||
strncpy( rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path) );
|
||||
rem_addr.sun_family = AF_UNIX;
|
||||
Error("Unable to open sock lock file %s: %s", sock_path_lock, strerror(errno) );
|
||||
lock_fd = 0;
|
||||
}
|
||||
else if ( flock(lock_fd, LOCK_EX) != 0 )
|
||||
{
|
||||
Error("Unable to lock sock lock file %s: %s", sock_path_lock, strerror(errno) );
|
||||
|
||||
close(lock_fd);
|
||||
lock_fd = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug( 1, "We have obtained a lock on %s fd: %d", sock_path_lock, lock_fd);
|
||||
}
|
||||
|
||||
|
||||
sd = socket( AF_UNIX, SOCK_DGRAM, 0 );
|
||||
if ( sd < 0 )
|
||||
{
|
||||
Fatal( "Can't create socket: %s", strerror(errno) );
|
||||
}
|
||||
|
||||
snprintf( loc_sock_path, sizeof(loc_sock_path), "%s/zms-%06ds.sock", config.path_socks, connkey );
|
||||
unlink( loc_sock_path );
|
||||
|
||||
strncpy( loc_addr.sun_path, loc_sock_path, sizeof(loc_addr.sun_path) );
|
||||
loc_addr.sun_family = AF_UNIX;
|
||||
if ( bind( sd, (struct sockaddr *)&loc_addr, strlen(loc_addr.sun_path)+sizeof(loc_addr.sun_family)) < 0 )
|
||||
{
|
||||
Fatal( "Can't bind: %s", strerror(errno) );
|
||||
}
|
||||
|
||||
snprintf( rem_sock_path, sizeof(rem_sock_path), "%s/zms-%06dw.sock", config.path_socks, connkey );
|
||||
strncpy( rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path) );
|
||||
rem_addr.sun_family = AF_UNIX;
|
||||
}
|
||||
}
|
||||
|
||||
void StreamBase::closeComms()
|
||||
{
|
||||
if ( connkey > 0 )
|
||||
if ( connkey > 0 )
|
||||
{
|
||||
if ( sd >= 0 )
|
||||
{
|
||||
if ( sd >= 0 )
|
||||
{
|
||||
close( sd );
|
||||
sd = -1;
|
||||
}
|
||||
if ( loc_sock_path[0] )
|
||||
{
|
||||
unlink( loc_sock_path );
|
||||
}
|
||||
if (lock_fd > 0)
|
||||
{
|
||||
close(lock_fd); //close it rather than unlock it incase it got deleted.
|
||||
unlink(sock_path_lock);
|
||||
}
|
||||
close( sd );
|
||||
sd = -1;
|
||||
}
|
||||
if ( loc_sock_path[0] )
|
||||
{
|
||||
unlink( loc_sock_path );
|
||||
}
|
||||
if (lock_fd > 0)
|
||||
{
|
||||
close(lock_fd); //close it rather than unlock it incase it got deleted.
|
||||
unlink(sock_path_lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
220
src/zm_stream.h
220
src/zm_stream.h
|
@ -33,149 +33,149 @@ class Monitor;
|
|||
class StreamBase
|
||||
{
|
||||
public:
|
||||
typedef enum { STREAM_JPEG, STREAM_RAW, STREAM_ZIP, STREAM_SINGLE, STREAM_MPEG } StreamType;
|
||||
typedef enum { STREAM_JPEG, STREAM_RAW, STREAM_ZIP, STREAM_SINGLE, STREAM_MPEG } StreamType;
|
||||
|
||||
protected:
|
||||
static const int MAX_STREAM_DELAY = 5; // Seconds
|
||||
static const int MAX_STREAM_DELAY = 5; // Seconds
|
||||
|
||||
static const StreamType DEFAULT_TYPE = STREAM_JPEG;
|
||||
enum { DEFAULT_RATE=ZM_RATE_BASE };
|
||||
enum { DEFAULT_SCALE=ZM_SCALE_BASE };
|
||||
enum { DEFAULT_ZOOM=ZM_SCALE_BASE };
|
||||
enum { DEFAULT_MAXFPS=10 };
|
||||
enum { DEFAULT_BITRATE=100000 };
|
||||
static const StreamType DEFAULT_TYPE = STREAM_JPEG;
|
||||
enum { DEFAULT_RATE=ZM_RATE_BASE };
|
||||
enum { DEFAULT_SCALE=ZM_SCALE_BASE };
|
||||
enum { DEFAULT_ZOOM=ZM_SCALE_BASE };
|
||||
enum { DEFAULT_MAXFPS=10 };
|
||||
enum { DEFAULT_BITRATE=100000 };
|
||||
|
||||
protected:
|
||||
typedef struct {
|
||||
int msg_type;
|
||||
char msg_data[16];
|
||||
} CmdMsg;
|
||||
typedef struct {
|
||||
int msg_type;
|
||||
char msg_data[16];
|
||||
} CmdMsg;
|
||||
|
||||
typedef struct {
|
||||
int msg_type;
|
||||
char msg_data[256];
|
||||
} DataMsg;
|
||||
typedef struct {
|
||||
int msg_type;
|
||||
char msg_data[256];
|
||||
} DataMsg;
|
||||
|
||||
typedef enum { MSG_CMD=1, MSG_DATA_WATCH, MSG_DATA_EVENT } MsgType;
|
||||
typedef enum { CMD_NONE=0, CMD_PAUSE, CMD_PLAY, CMD_STOP, CMD_FASTFWD, CMD_SLOWFWD, CMD_SLOWREV, CMD_FASTREV, CMD_ZOOMIN, CMD_ZOOMOUT, CMD_PAN, CMD_SCALE, CMD_PREV, CMD_NEXT, CMD_SEEK, CMD_VARPLAY, CMD_GET_IMAGE, CMD_QUIT, CMD_QUERY=99 } MsgCommand;
|
||||
typedef enum { MSG_CMD=1, MSG_DATA_WATCH, MSG_DATA_EVENT } MsgType;
|
||||
typedef enum { CMD_NONE=0, CMD_PAUSE, CMD_PLAY, CMD_STOP, CMD_FASTFWD, CMD_SLOWFWD, CMD_SLOWREV, CMD_FASTREV, CMD_ZOOMIN, CMD_ZOOMOUT, CMD_PAN, CMD_SCALE, CMD_PREV, CMD_NEXT, CMD_SEEK, CMD_VARPLAY, CMD_GET_IMAGE, CMD_QUIT, CMD_QUERY=99 } MsgCommand;
|
||||
|
||||
protected:
|
||||
Monitor *monitor;
|
||||
Monitor *monitor;
|
||||
|
||||
StreamType type;
|
||||
const char *format;
|
||||
int replay_rate;
|
||||
int scale;
|
||||
int zoom;
|
||||
double maxfps;
|
||||
int bitrate;
|
||||
unsigned short x, y;
|
||||
StreamType type;
|
||||
const char *format;
|
||||
int replay_rate;
|
||||
int scale;
|
||||
int zoom;
|
||||
double maxfps;
|
||||
int bitrate;
|
||||
unsigned short x, y;
|
||||
|
||||
protected:
|
||||
int connkey;
|
||||
int sd;
|
||||
char loc_sock_path[PATH_MAX];
|
||||
struct sockaddr_un loc_addr;
|
||||
char rem_sock_path[PATH_MAX];
|
||||
struct sockaddr_un rem_addr;
|
||||
char sock_path_lock[PATH_MAX];
|
||||
int lock_fd;
|
||||
int connkey;
|
||||
int sd;
|
||||
char loc_sock_path[PATH_MAX];
|
||||
struct sockaddr_un loc_addr;
|
||||
char rem_sock_path[PATH_MAX];
|
||||
struct sockaddr_un rem_addr;
|
||||
char sock_path_lock[PATH_MAX];
|
||||
int lock_fd;
|
||||
|
||||
protected:
|
||||
bool paused;
|
||||
int step;
|
||||
bool paused;
|
||||
int step;
|
||||
|
||||
struct timeval now;
|
||||
struct timeval now;
|
||||
|
||||
double base_fps;
|
||||
double effective_fps;
|
||||
int frame_mod;
|
||||
double base_fps;
|
||||
double effective_fps;
|
||||
int frame_mod;
|
||||
|
||||
double last_frame_sent;
|
||||
struct timeval last_frame_timestamp;
|
||||
double last_frame_sent;
|
||||
struct timeval last_frame_timestamp;
|
||||
|
||||
#if HAVE_LIBAVCODEC
|
||||
VideoStream *vid_stream;
|
||||
VideoStream *vid_stream;
|
||||
#endif // HAVE_LIBAVCODEC
|
||||
|
||||
CmdMsg msg;
|
||||
CmdMsg msg;
|
||||
|
||||
protected:
|
||||
bool loadMonitor( int monitor_id );
|
||||
bool checkInitialised();
|
||||
void updateFrameRate( double fps );
|
||||
Image *prepareImage( Image *image );
|
||||
bool sendTextFrame( const char *text );
|
||||
bool checkCommandQueue();
|
||||
virtual void processCommand( const CmdMsg *msg )=0;
|
||||
bool loadMonitor( int monitor_id );
|
||||
bool checkInitialised();
|
||||
void updateFrameRate( double fps );
|
||||
Image *prepareImage( Image *image );
|
||||
bool sendTextFrame( const char *text );
|
||||
bool checkCommandQueue();
|
||||
virtual void processCommand( const CmdMsg *msg )=0;
|
||||
|
||||
public:
|
||||
StreamBase()
|
||||
{
|
||||
monitor = 0;
|
||||
StreamBase()
|
||||
{
|
||||
monitor = 0;
|
||||
|
||||
type = DEFAULT_TYPE;
|
||||
format = "";
|
||||
replay_rate = DEFAULT_RATE;
|
||||
scale = DEFAULT_SCALE;
|
||||
zoom = DEFAULT_ZOOM;
|
||||
maxfps = DEFAULT_MAXFPS;
|
||||
bitrate = DEFAULT_BITRATE;
|
||||
type = DEFAULT_TYPE;
|
||||
format = "";
|
||||
replay_rate = DEFAULT_RATE;
|
||||
scale = DEFAULT_SCALE;
|
||||
zoom = DEFAULT_ZOOM;
|
||||
maxfps = DEFAULT_MAXFPS;
|
||||
bitrate = DEFAULT_BITRATE;
|
||||
|
||||
paused = false;
|
||||
step = 0;
|
||||
x = 0;
|
||||
y = 0;
|
||||
paused = false;
|
||||
step = 0;
|
||||
x = 0;
|
||||
y = 0;
|
||||
|
||||
connkey = 0;
|
||||
sd = -1;
|
||||
lock_fd = 0;
|
||||
memset( &loc_sock_path, 0, sizeof(loc_sock_path) );
|
||||
memset( &loc_addr, 0, sizeof(loc_addr) );
|
||||
memset( &rem_sock_path, 0, sizeof(rem_sock_path) );
|
||||
memset( &rem_addr, 0, sizeof(rem_addr) );
|
||||
connkey = 0;
|
||||
sd = -1;
|
||||
lock_fd = 0;
|
||||
memset( &loc_sock_path, 0, sizeof(loc_sock_path) );
|
||||
memset( &loc_addr, 0, sizeof(loc_addr) );
|
||||
memset( &rem_sock_path, 0, sizeof(rem_sock_path) );
|
||||
memset( &rem_addr, 0, sizeof(rem_addr) );
|
||||
|
||||
base_fps = 0.0;
|
||||
effective_fps = 0.0;
|
||||
frame_mod = 1;
|
||||
base_fps = 0.0;
|
||||
effective_fps = 0.0;
|
||||
frame_mod = 1;
|
||||
|
||||
#if HAVE_LIBAVCODEC
|
||||
vid_stream = 0;
|
||||
vid_stream = 0;
|
||||
#endif // HAVE_LIBAVCODEC
|
||||
}
|
||||
virtual ~StreamBase();
|
||||
}
|
||||
virtual ~StreamBase();
|
||||
|
||||
void setStreamType( StreamType p_type )
|
||||
{
|
||||
type = p_type;
|
||||
}
|
||||
void setStreamFormat( const char *p_format )
|
||||
{
|
||||
format = p_format;
|
||||
}
|
||||
void setStreamScale( int p_scale )
|
||||
{
|
||||
scale = p_scale;
|
||||
}
|
||||
void setStreamReplayRate( int p_rate )
|
||||
{
|
||||
replay_rate = p_rate;
|
||||
}
|
||||
void setStreamMaxFPS( double p_maxfps )
|
||||
{
|
||||
maxfps = p_maxfps;
|
||||
}
|
||||
void setStreamBitrate( int p_bitrate )
|
||||
{
|
||||
bitrate = p_bitrate;
|
||||
}
|
||||
void setStreamQueue( int p_connkey )
|
||||
{
|
||||
connkey = p_connkey;
|
||||
}
|
||||
virtual void openComms();
|
||||
virtual void closeComms();
|
||||
virtual void runStream()=0;
|
||||
void setStreamType( StreamType p_type )
|
||||
{
|
||||
type = p_type;
|
||||
}
|
||||
void setStreamFormat( const char *p_format )
|
||||
{
|
||||
format = p_format;
|
||||
}
|
||||
void setStreamScale( int p_scale )
|
||||
{
|
||||
scale = p_scale;
|
||||
}
|
||||
void setStreamReplayRate( int p_rate )
|
||||
{
|
||||
replay_rate = p_rate;
|
||||
}
|
||||
void setStreamMaxFPS( double p_maxfps )
|
||||
{
|
||||
maxfps = p_maxfps;
|
||||
}
|
||||
void setStreamBitrate( int p_bitrate )
|
||||
{
|
||||
bitrate = p_bitrate;
|
||||
}
|
||||
void setStreamQueue( int p_connkey )
|
||||
{
|
||||
connkey = p_connkey;
|
||||
}
|
||||
virtual void openComms();
|
||||
virtual void closeComms();
|
||||
virtual void runStream()=0;
|
||||
};
|
||||
|
||||
#endif // ZM_STREAM_H
|
||||
|
|
|
@ -29,311 +29,310 @@
|
|||
|
||||
struct timespec getTimeout( int secs )
|
||||
{
|
||||
struct timespec timeout;
|
||||
struct timeval temp_timeout;
|
||||
gettimeofday( &temp_timeout, 0 );
|
||||
timeout.tv_sec = temp_timeout.tv_sec + secs;
|
||||
timeout.tv_nsec = temp_timeout.tv_usec*1000;
|
||||
return( timeout );
|
||||
struct timespec timeout;
|
||||
struct timeval temp_timeout;
|
||||
gettimeofday( &temp_timeout, 0 );
|
||||
timeout.tv_sec = temp_timeout.tv_sec + secs;
|
||||
timeout.tv_nsec = temp_timeout.tv_usec*1000;
|
||||
return( timeout );
|
||||
}
|
||||
|
||||
struct timespec getTimeout( double secs )
|
||||
{
|
||||
struct timespec timeout;
|
||||
struct timeval temp_timeout;
|
||||
gettimeofday( &temp_timeout, 0 );
|
||||
timeout.tv_sec = temp_timeout.tv_sec + int(secs);
|
||||
timeout.tv_nsec = temp_timeout.tv_usec += (long int)(1000000000.0*(secs-int(secs)));
|
||||
if ( timeout.tv_nsec > 1000000000 )
|
||||
{
|
||||
timeout.tv_sec += 1;
|
||||
timeout.tv_nsec -= 1000000000;
|
||||
}
|
||||
return( timeout );
|
||||
struct timespec timeout;
|
||||
struct timeval temp_timeout;
|
||||
gettimeofday( &temp_timeout, 0 );
|
||||
timeout.tv_sec = temp_timeout.tv_sec + int(secs);
|
||||
timeout.tv_nsec = temp_timeout.tv_usec += (long int)(1000000000.0*(secs-int(secs)));
|
||||
if ( timeout.tv_nsec > 1000000000 )
|
||||
{
|
||||
timeout.tv_sec += 1;
|
||||
timeout.tv_nsec -= 1000000000;
|
||||
}
|
||||
return( timeout );
|
||||
}
|
||||
|
||||
Mutex::Mutex()
|
||||
{
|
||||
if ( pthread_mutex_init( &mMutex, NULL ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to create pthread mutex: %s", strerror(errno) ) );
|
||||
if ( pthread_mutex_init( &mMutex, NULL ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to create pthread mutex: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
Mutex::~Mutex()
|
||||
{
|
||||
if ( locked() )
|
||||
Warning( "Destroying mutex when locked" );
|
||||
if ( pthread_mutex_destroy( &mMutex ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to destroy pthread mutex: %s", strerror(errno) ) );
|
||||
if ( locked() )
|
||||
Warning( "Destroying mutex when locked" );
|
||||
if ( pthread_mutex_destroy( &mMutex ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to destroy pthread mutex: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
void Mutex::lock()
|
||||
{
|
||||
if ( pthread_mutex_lock( &mMutex ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to lock pthread mutex: %s", strerror(errno) ) );
|
||||
if ( pthread_mutex_lock( &mMutex ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to lock pthread mutex: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
void Mutex::lock( int secs )
|
||||
{
|
||||
struct timespec timeout = getTimeout( secs );
|
||||
if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) );
|
||||
struct timespec timeout = getTimeout( secs );
|
||||
if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
void Mutex::lock( double secs )
|
||||
{
|
||||
struct timespec timeout = getTimeout( secs );
|
||||
if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) );
|
||||
struct timespec timeout = getTimeout( secs );
|
||||
if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
void Mutex::unlock()
|
||||
{
|
||||
if ( pthread_mutex_unlock( &mMutex ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to unlock pthread mutex: %s", strerror(errno) ) );
|
||||
if ( pthread_mutex_unlock( &mMutex ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to unlock pthread mutex: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
bool Mutex::locked()
|
||||
{
|
||||
int state = pthread_mutex_trylock( &mMutex );
|
||||
if ( state != 0 && state != EBUSY )
|
||||
throw ThreadException( stringtf( "Unable to trylock pthread mutex: %s", strerror(errno) ) );
|
||||
if ( state != EBUSY )
|
||||
unlock();
|
||||
return( state == EBUSY );
|
||||
int state = pthread_mutex_trylock( &mMutex );
|
||||
if ( state != 0 && state != EBUSY )
|
||||
throw ThreadException( stringtf( "Unable to trylock pthread mutex: %s", strerror(errno) ) );
|
||||
if ( state != EBUSY )
|
||||
unlock();
|
||||
return( state == EBUSY );
|
||||
}
|
||||
|
||||
Condition::Condition( Mutex &mutex ) : mMutex( mutex )
|
||||
{
|
||||
if ( pthread_cond_init( &mCondition, NULL ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to create pthread condition: %s", strerror(errno) ) );
|
||||
if ( pthread_cond_init( &mCondition, NULL ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to create pthread condition: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
Condition::~Condition()
|
||||
{
|
||||
if ( pthread_cond_destroy( &mCondition ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to destroy pthread condition: %s", strerror(errno) ) );
|
||||
if ( pthread_cond_destroy( &mCondition ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to destroy pthread condition: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
void Condition::wait()
|
||||
{
|
||||
// Locking done outside of this function
|
||||
if ( pthread_cond_wait( &mCondition, mMutex.getMutex() ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to wait pthread condition: %s", strerror(errno) ) );
|
||||
// Locking done outside of this function
|
||||
if ( pthread_cond_wait( &mCondition, mMutex.getMutex() ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to wait pthread condition: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
bool Condition::wait( int secs )
|
||||
{
|
||||
// Locking done outside of this function
|
||||
Debug( 8, "Waiting for %d seconds", secs );
|
||||
struct timespec timeout = getTimeout( secs );
|
||||
if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT )
|
||||
throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) );
|
||||
return( errno != ETIMEDOUT );
|
||||
// Locking done outside of this function
|
||||
Debug( 8, "Waiting for %d seconds", secs );
|
||||
struct timespec timeout = getTimeout( secs );
|
||||
if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT )
|
||||
throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) );
|
||||
return( errno != ETIMEDOUT );
|
||||
}
|
||||
|
||||
bool Condition::wait( double secs )
|
||||
{
|
||||
// Locking done outside of this function
|
||||
struct timespec timeout = getTimeout( secs );
|
||||
if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT )
|
||||
throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) );
|
||||
return( errno != ETIMEDOUT );
|
||||
// Locking done outside of this function
|
||||
struct timespec timeout = getTimeout( secs );
|
||||
if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT )
|
||||
throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) );
|
||||
return( errno != ETIMEDOUT );
|
||||
}
|
||||
|
||||
void Condition::signal()
|
||||
{
|
||||
if ( pthread_cond_signal( &mCondition ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to signal pthread condition: %s", strerror(errno) ) );
|
||||
if ( pthread_cond_signal( &mCondition ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to signal pthread condition: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
void Condition::broadcast()
|
||||
{
|
||||
if ( pthread_cond_broadcast( &mCondition ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to broadcast pthread condition: %s", strerror(errno) ) );
|
||||
if ( pthread_cond_broadcast( &mCondition ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to broadcast pthread condition: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
template <class T> const T ThreadData<T>::getValue() const
|
||||
{
|
||||
mMutex.lock();
|
||||
const T valueCopy = mValue;
|
||||
mMutex.unlock();
|
||||
return( valueCopy );
|
||||
mMutex.lock();
|
||||
const T valueCopy = mValue;
|
||||
mMutex.unlock();
|
||||
return( valueCopy );
|
||||
}
|
||||
|
||||
template <class T> T ThreadData<T>::setValue( const T value )
|
||||
{
|
||||
mMutex.lock();
|
||||
const T valueCopy = mValue = value;
|
||||
mMutex.unlock();
|
||||
return( valueCopy );
|
||||
mMutex.lock();
|
||||
const T valueCopy = mValue = value;
|
||||
mMutex.unlock();
|
||||
return( valueCopy );
|
||||
}
|
||||
|
||||
template <class T> const T ThreadData<T>::getUpdatedValue() const
|
||||
{
|
||||
Debug( 8, "Waiting for value update, %p", this );
|
||||
mMutex.lock();
|
||||
mChanged = false;
|
||||
//do {
|
||||
mCondition.wait();
|
||||
//} while ( !mChanged );
|
||||
const T valueCopy = mValue;
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Got value update, %p", this );
|
||||
return( valueCopy );
|
||||
Debug( 8, "Waiting for value update, %p", this );
|
||||
mMutex.lock();
|
||||
mChanged = false;
|
||||
//do {
|
||||
mCondition.wait();
|
||||
//} while ( !mChanged );
|
||||
const T valueCopy = mValue;
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Got value update, %p", this );
|
||||
return( valueCopy );
|
||||
}
|
||||
|
||||
template <class T> const T ThreadData<T>::getUpdatedValue( double secs ) const
|
||||
{
|
||||
Debug( 8, "Waiting for value update, %.2f secs, %p", secs, this );
|
||||
mMutex.lock();
|
||||
mChanged = false;
|
||||
//do {
|
||||
mCondition.wait( secs );
|
||||
//} while ( !mChanged );
|
||||
const T valueCopy = mValue;
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Got value update, %p", this );
|
||||
return( valueCopy );
|
||||
Debug( 8, "Waiting for value update, %.2f secs, %p", secs, this );
|
||||
mMutex.lock();
|
||||
mChanged = false;
|
||||
//do {
|
||||
mCondition.wait( secs );
|
||||
//} while ( !mChanged );
|
||||
const T valueCopy = mValue;
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Got value update, %p", this );
|
||||
return( valueCopy );
|
||||
}
|
||||
|
||||
template <class T> const T ThreadData<T>::getUpdatedValue( int secs ) const
|
||||
{
|
||||
Debug( 8, "Waiting for value update, %d secs, %p", secs, this );
|
||||
mMutex.lock();
|
||||
mChanged = false;
|
||||
//do {
|
||||
mCondition.wait( secs );
|
||||
//} while ( !mChanged );
|
||||
const T valueCopy = mValue;
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Got value update, %p", this );
|
||||
return( valueCopy );
|
||||
Debug( 8, "Waiting for value update, %d secs, %p", secs, this );
|
||||
mMutex.lock();
|
||||
mChanged = false;
|
||||
//do {
|
||||
mCondition.wait( secs );
|
||||
//} while ( !mChanged );
|
||||
const T valueCopy = mValue;
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Got value update, %p", this );
|
||||
return( valueCopy );
|
||||
}
|
||||
|
||||
template <class T> void ThreadData<T>::updateValueSignal( const T value )
|
||||
{
|
||||
Debug( 8, "Updating value with signal, %p", this );
|
||||
mMutex.lock();
|
||||
mValue = value;
|
||||
mChanged = true;
|
||||
mCondition.signal();
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Updated value, %p", this );
|
||||
Debug( 8, "Updating value with signal, %p", this );
|
||||
mMutex.lock();
|
||||
mValue = value;
|
||||
mChanged = true;
|
||||
mCondition.signal();
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Updated value, %p", this );
|
||||
}
|
||||
|
||||
template <class T> void ThreadData<T>::updateValueBroadcast( const T value )
|
||||
{
|
||||
Debug( 8, "Updating value with broadcast, %p", this );
|
||||
mMutex.lock();
|
||||
mValue = value;
|
||||
mChanged = true;
|
||||
mCondition.broadcast();
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Updated value, %p", this );
|
||||
Debug( 8, "Updating value with broadcast, %p", this );
|
||||
mMutex.lock();
|
||||
mValue = value;
|
||||
mChanged = true;
|
||||
mCondition.broadcast();
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Updated value, %p", this );
|
||||
}
|
||||
|
||||
Thread::Thread() :
|
||||
mThreadCondition( mThreadMutex ),
|
||||
mPid( -1 ),
|
||||
mStarted( false ),
|
||||
mRunning( false )
|
||||
mThreadCondition( mThreadMutex ),
|
||||
mPid( -1 ),
|
||||
mStarted( false ),
|
||||
mRunning( false )
|
||||
{
|
||||
Debug( 1, "Creating thread" );
|
||||
Debug( 1, "Creating thread" );
|
||||
}
|
||||
|
||||
Thread::~Thread()
|
||||
{
|
||||
Debug( 1, "Destroying thread %d", mPid );
|
||||
if ( mStarted )
|
||||
join();
|
||||
Debug( 1, "Destroying thread %d", mPid );
|
||||
if ( mStarted )
|
||||
join();
|
||||
}
|
||||
|
||||
void *Thread::mThreadFunc( void *arg )
|
||||
{
|
||||
Debug( 2, "Invoking thread" );
|
||||
Debug( 2, "Invoking thread" );
|
||||
|
||||
Thread *thisPtr = (Thread *)arg;
|
||||
void *status = 0;
|
||||
try
|
||||
{
|
||||
thisPtr->mThreadMutex.lock();
|
||||
thisPtr->mPid = thisPtr->id();
|
||||
thisPtr->mThreadCondition.signal();
|
||||
thisPtr->mThreadMutex.unlock();
|
||||
thisPtr->mRunning = true;
|
||||
int run=(thisPtr->run());
|
||||
status = (void *)&run;
|
||||
thisPtr->mRunning = false;
|
||||
Debug( 2, "Exiting thread, status %p", status );
|
||||
}
|
||||
catch ( const ThreadException &e )
|
||||
{
|
||||
Error( "%s", e.getMessage().c_str() );
|
||||
thisPtr->mRunning = false;
|
||||
status = (void *)-1;
|
||||
Debug( 2, "Exiting thread after exception, status %p", status );
|
||||
}
|
||||
return( status );
|
||||
Thread *thisPtr = (Thread *)arg;
|
||||
thisPtr->status = 0;
|
||||
try
|
||||
{
|
||||
thisPtr->mThreadMutex.lock();
|
||||
thisPtr->mPid = thisPtr->id();
|
||||
thisPtr->mThreadCondition.signal();
|
||||
thisPtr->mThreadMutex.unlock();
|
||||
thisPtr->mRunning = true;
|
||||
thisPtr->status = thisPtr->run();
|
||||
thisPtr->mRunning = false;
|
||||
Debug( 2, "Exiting thread, status %p", (void *)&(thisPtr->status) );
|
||||
return (void *)&(thisPtr->status);
|
||||
}
|
||||
catch ( const ThreadException &e )
|
||||
{
|
||||
Error( "%s", e.getMessage().c_str() );
|
||||
thisPtr->mRunning = false;
|
||||
Debug( 2, "Exiting thread after exception, status %p", (void *)-1 );
|
||||
return (void *)-1;
|
||||
}
|
||||
}
|
||||
|
||||
void Thread::start()
|
||||
{
|
||||
Debug( 1, "Starting thread" );
|
||||
if ( isThread() )
|
||||
throw ThreadException( "Can't self start thread" );
|
||||
mThreadMutex.lock();
|
||||
if ( !mStarted )
|
||||
{
|
||||
pthread_attr_t threadAttrs;
|
||||
pthread_attr_init( &threadAttrs );
|
||||
pthread_attr_setscope( &threadAttrs, PTHREAD_SCOPE_SYSTEM );
|
||||
Debug( 1, "Starting thread" );
|
||||
if ( isThread() )
|
||||
throw ThreadException( "Can't self start thread" );
|
||||
mThreadMutex.lock();
|
||||
if ( !mStarted )
|
||||
{
|
||||
pthread_attr_t threadAttrs;
|
||||
pthread_attr_init( &threadAttrs );
|
||||
pthread_attr_setscope( &threadAttrs, PTHREAD_SCOPE_SYSTEM );
|
||||
|
||||
mStarted = true;
|
||||
if ( pthread_create( &mThread, &threadAttrs, mThreadFunc, this ) < 0 )
|
||||
throw ThreadException( stringtf( "Can't create thread: %s", strerror(errno) ) );
|
||||
pthread_attr_destroy( &threadAttrs );
|
||||
}
|
||||
else
|
||||
{
|
||||
Error( "Attempt to start already running thread %d", mPid );
|
||||
}
|
||||
mThreadCondition.wait();
|
||||
mThreadMutex.unlock();
|
||||
Debug( 1, "Started thread %d", mPid );
|
||||
mStarted = true;
|
||||
if ( pthread_create( &mThread, &threadAttrs, mThreadFunc, this ) < 0 )
|
||||
throw ThreadException( stringtf( "Can't create thread: %s", strerror(errno) ) );
|
||||
pthread_attr_destroy( &threadAttrs );
|
||||
}
|
||||
else
|
||||
{
|
||||
Error( "Attempt to start already running thread %d", mPid );
|
||||
}
|
||||
mThreadCondition.wait();
|
||||
mThreadMutex.unlock();
|
||||
Debug( 1, "Started thread %d", mPid );
|
||||
}
|
||||
|
||||
void Thread::join()
|
||||
{
|
||||
Debug( 1, "Joining thread %d", mPid );
|
||||
if ( isThread() )
|
||||
throw ThreadException( "Can't self join thread" );
|
||||
mThreadMutex.lock();
|
||||
if ( mPid >= 0 )
|
||||
Debug( 1, "Joining thread %d", mPid );
|
||||
if ( isThread() )
|
||||
throw ThreadException( "Can't self join thread" );
|
||||
mThreadMutex.lock();
|
||||
if ( mPid >= 0 )
|
||||
{
|
||||
if ( mStarted )
|
||||
{
|
||||
if ( mStarted )
|
||||
{
|
||||
void *threadStatus = 0;
|
||||
if ( pthread_join( mThread, &threadStatus ) < 0 )
|
||||
throw ThreadException( stringtf( "Can't join sender thread: %s", strerror(errno) ) );
|
||||
mStarted = false;
|
||||
Debug( 1, "Thread %d exited, status %p", mPid, threadStatus );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Attempt to join already finished thread %d", mPid );
|
||||
}
|
||||
void *threadStatus = 0;
|
||||
if ( pthread_join( mThread, &threadStatus ) < 0 )
|
||||
throw ThreadException( stringtf( "Can't join sender thread: %s", strerror(errno) ) );
|
||||
mStarted = false;
|
||||
Debug( 1, "Thread %d exited, status %p", mPid, threadStatus );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Attempt to join non-started thread %d", mPid );
|
||||
Warning( "Attempt to join already finished thread %d", mPid );
|
||||
}
|
||||
mThreadMutex.unlock();
|
||||
Debug( 1, "Joined thread %d", mPid );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Attempt to join non-started thread %d", mPid );
|
||||
}
|
||||
mThreadMutex.unlock();
|
||||
Debug( 1, "Joined thread %d", mPid );
|
||||
}
|
||||
|
||||
void Thread::kill( int signal )
|
||||
{
|
||||
pthread_kill( mThread, signal );
|
||||
pthread_kill( mThread, signal );
|
||||
}
|
||||
|
||||
// Some explicit template instantiations
|
||||
|
|
307
src/zm_thread.h
307
src/zm_thread.h
|
@ -36,27 +36,27 @@ class ThreadException : public Exception
|
|||
{
|
||||
private:
|
||||
#ifndef SOLARIS
|
||||
pid_t pid() {
|
||||
pid_t pid() {
|
||||
pid_t tid;
|
||||
#ifdef __FreeBSD__
|
||||
long lwpid;
|
||||
thr_self(&lwpid);
|
||||
tid = lwpid;
|
||||
#else
|
||||
#ifdef __FreeBSD_kernel__
|
||||
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
||||
# else
|
||||
tid=syscall(SYS_gettid);
|
||||
#endif
|
||||
#ifdef __FreeBSD_kernel__
|
||||
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
||||
# else
|
||||
tid=syscall(SYS_gettid);
|
||||
#endif
|
||||
#endif
|
||||
return tid;
|
||||
}
|
||||
}
|
||||
#else
|
||||
pthread_t pid() { return( pthread_self() ); }
|
||||
pthread_t pid() { return( pthread_self() ); }
|
||||
#endif
|
||||
public:
|
||||
ThreadException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)pid() ) ) {
|
||||
}
|
||||
ThreadException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)pid() ) ) {
|
||||
}
|
||||
};
|
||||
|
||||
class Mutex
|
||||
|
@ -64,214 +64,215 @@ class Mutex
|
|||
friend class Condition;
|
||||
|
||||
private:
|
||||
pthread_mutex_t mMutex;
|
||||
pthread_mutex_t mMutex;
|
||||
|
||||
public:
|
||||
Mutex();
|
||||
~Mutex();
|
||||
Mutex();
|
||||
~Mutex();
|
||||
|
||||
private:
|
||||
pthread_mutex_t *getMutex()
|
||||
{
|
||||
return( &mMutex );
|
||||
}
|
||||
pthread_mutex_t *getMutex()
|
||||
{
|
||||
return( &mMutex );
|
||||
}
|
||||
|
||||
public:
|
||||
void lock();
|
||||
void lock( int secs );
|
||||
void lock( double secs );
|
||||
void unlock();
|
||||
bool locked();
|
||||
void lock();
|
||||
void lock( int secs );
|
||||
void lock( double secs );
|
||||
void unlock();
|
||||
bool locked();
|
||||
};
|
||||
|
||||
class ScopedMutex
|
||||
{
|
||||
private:
|
||||
Mutex &mMutex;
|
||||
Mutex &mMutex;
|
||||
|
||||
public:
|
||||
ScopedMutex( Mutex &mutex ) : mMutex( mutex )
|
||||
{
|
||||
mMutex.lock();
|
||||
}
|
||||
~ScopedMutex()
|
||||
{
|
||||
mMutex.unlock();
|
||||
}
|
||||
ScopedMutex( Mutex &mutex ) : mMutex( mutex )
|
||||
{
|
||||
mMutex.lock();
|
||||
}
|
||||
~ScopedMutex()
|
||||
{
|
||||
mMutex.unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedMutex( const ScopedMutex & );
|
||||
ScopedMutex( const ScopedMutex & );
|
||||
};
|
||||
|
||||
class Condition
|
||||
{
|
||||
private:
|
||||
Mutex &mMutex;
|
||||
pthread_cond_t mCondition;
|
||||
Mutex &mMutex;
|
||||
pthread_cond_t mCondition;
|
||||
|
||||
public:
|
||||
Condition( Mutex &mutex );
|
||||
~Condition();
|
||||
Condition( Mutex &mutex );
|
||||
~Condition();
|
||||
|
||||
void wait();
|
||||
bool wait( int secs );
|
||||
bool wait( double secs );
|
||||
void signal();
|
||||
void broadcast();
|
||||
void wait();
|
||||
bool wait( int secs );
|
||||
bool wait( double secs );
|
||||
void signal();
|
||||
void broadcast();
|
||||
};
|
||||
|
||||
class Semaphore : public Condition
|
||||
{
|
||||
private:
|
||||
Mutex mMutex;
|
||||
Mutex mMutex;
|
||||
|
||||
public:
|
||||
Semaphore() : Condition( mMutex )
|
||||
{
|
||||
}
|
||||
Semaphore() : Condition( mMutex )
|
||||
{
|
||||
}
|
||||
|
||||
void wait()
|
||||
{
|
||||
mMutex.lock();
|
||||
Condition::wait();
|
||||
mMutex.unlock();
|
||||
}
|
||||
bool wait( int secs )
|
||||
{
|
||||
mMutex.lock();
|
||||
bool result = Condition::wait( secs );
|
||||
mMutex.unlock();
|
||||
return( result );
|
||||
}
|
||||
bool wait( double secs )
|
||||
{
|
||||
mMutex.lock();
|
||||
bool result = Condition::wait( secs );
|
||||
mMutex.unlock();
|
||||
return( result );
|
||||
}
|
||||
void signal()
|
||||
{
|
||||
mMutex.lock();
|
||||
Condition::signal();
|
||||
mMutex.unlock();
|
||||
}
|
||||
void broadcast()
|
||||
{
|
||||
mMutex.lock();
|
||||
Condition::broadcast();
|
||||
mMutex.unlock();
|
||||
}
|
||||
void wait()
|
||||
{
|
||||
mMutex.lock();
|
||||
Condition::wait();
|
||||
mMutex.unlock();
|
||||
}
|
||||
bool wait( int secs )
|
||||
{
|
||||
mMutex.lock();
|
||||
bool result = Condition::wait( secs );
|
||||
mMutex.unlock();
|
||||
return( result );
|
||||
}
|
||||
bool wait( double secs )
|
||||
{
|
||||
mMutex.lock();
|
||||
bool result = Condition::wait( secs );
|
||||
mMutex.unlock();
|
||||
return( result );
|
||||
}
|
||||
void signal()
|
||||
{
|
||||
mMutex.lock();
|
||||
Condition::signal();
|
||||
mMutex.unlock();
|
||||
}
|
||||
void broadcast()
|
||||
{
|
||||
mMutex.lock();
|
||||
Condition::broadcast();
|
||||
mMutex.unlock();
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> class ThreadData
|
||||
{
|
||||
private:
|
||||
T mValue;
|
||||
mutable bool mChanged;
|
||||
mutable Mutex mMutex;
|
||||
mutable Condition mCondition;
|
||||
T mValue;
|
||||
mutable bool mChanged;
|
||||
mutable Mutex mMutex;
|
||||
mutable Condition mCondition;
|
||||
|
||||
public:
|
||||
__attribute__((used)) ThreadData() : mCondition( mMutex )
|
||||
{
|
||||
}
|
||||
__attribute__((used)) ThreadData( T value ) : mValue( value ), mCondition( mMutex )
|
||||
{
|
||||
}
|
||||
//~ThreadData() {}
|
||||
__attribute__((used)) ThreadData() : mCondition( mMutex )
|
||||
{
|
||||
}
|
||||
__attribute__((used)) ThreadData( T value ) : mValue( value ), mCondition( mMutex )
|
||||
{
|
||||
}
|
||||
//~ThreadData() {}
|
||||
|
||||
__attribute__((used)) operator T() const
|
||||
{
|
||||
return( getValue() );
|
||||
}
|
||||
__attribute__((used)) const T operator=( const T value )
|
||||
{
|
||||
return( setValue( value ) );
|
||||
}
|
||||
__attribute__((used)) operator T() const
|
||||
{
|
||||
return( getValue() );
|
||||
}
|
||||
__attribute__((used)) const T operator=( const T value )
|
||||
{
|
||||
return( setValue( value ) );
|
||||
}
|
||||
|
||||
__attribute__((used)) const T getValueImmediate() const
|
||||
{
|
||||
return( mValue );
|
||||
}
|
||||
__attribute__((used)) T setValueImmediate( const T value )
|
||||
{
|
||||
return( mValue = value );
|
||||
}
|
||||
__attribute__((used)) const T getValue() const;
|
||||
__attribute__((used)) T setValue( const T value );
|
||||
__attribute__((used)) const T getUpdatedValue() const;
|
||||
__attribute__((used)) const T getUpdatedValue( double secs ) const;
|
||||
__attribute__((used)) const T getUpdatedValue( int secs ) const;
|
||||
__attribute__((used)) void updateValueSignal( const T value );
|
||||
__attribute__((used)) void updateValueBroadcast( const T value );
|
||||
__attribute__((used)) const T getValueImmediate() const
|
||||
{
|
||||
return( mValue );
|
||||
}
|
||||
__attribute__((used)) T setValueImmediate( const T value )
|
||||
{
|
||||
return( mValue = value );
|
||||
}
|
||||
__attribute__((used)) const T getValue() const;
|
||||
__attribute__((used)) T setValue( const T value );
|
||||
__attribute__((used)) const T getUpdatedValue() const;
|
||||
__attribute__((used)) const T getUpdatedValue( double secs ) const;
|
||||
__attribute__((used)) const T getUpdatedValue( int secs ) const;
|
||||
__attribute__((used)) void updateValueSignal( const T value );
|
||||
__attribute__((used)) void updateValueBroadcast( const T value );
|
||||
};
|
||||
|
||||
class Thread
|
||||
{
|
||||
public:
|
||||
typedef void *(*ThreadFunc)( void * );
|
||||
typedef void *(*ThreadFunc)( void * );
|
||||
|
||||
protected:
|
||||
pthread_t mThread;
|
||||
pthread_t mThread;
|
||||
|
||||
Mutex mThreadMutex;
|
||||
Condition mThreadCondition;
|
||||
Mutex mThreadMutex;
|
||||
Condition mThreadCondition;
|
||||
#ifndef SOLARIS
|
||||
pid_t mPid;
|
||||
pid_t mPid;
|
||||
#else
|
||||
pthread_t mPid;
|
||||
pthread_t mPid;
|
||||
#endif
|
||||
bool mStarted;
|
||||
bool mRunning;
|
||||
bool mStarted;
|
||||
bool mRunning;
|
||||
int status; // Used in various funcions to get around return a local variable
|
||||
|
||||
protected:
|
||||
Thread();
|
||||
virtual ~Thread();
|
||||
Thread();
|
||||
virtual ~Thread();
|
||||
|
||||
#ifndef SOLARIS
|
||||
pid_t id() const
|
||||
{
|
||||
pid_t tid;
|
||||
pid_t id() const
|
||||
{
|
||||
pid_t tid;
|
||||
#ifdef __FreeBSD__
|
||||
long lwpid;
|
||||
thr_self(&lwpid);
|
||||
tid = lwpid;
|
||||
long lwpid;
|
||||
thr_self(&lwpid);
|
||||
tid = lwpid;
|
||||
#else
|
||||
#ifdef __FreeBSD_kernel__
|
||||
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
||||
#ifdef __FreeBSD_kernel__
|
||||
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
||||
|
||||
#else
|
||||
tid=syscall(SYS_gettid);
|
||||
#endif
|
||||
#else
|
||||
tid=syscall(SYS_gettid);
|
||||
#endif
|
||||
#endif
|
||||
return tid;
|
||||
}
|
||||
}
|
||||
#else
|
||||
pthread_t id() const
|
||||
{
|
||||
return( pthread_self() );
|
||||
}
|
||||
pthread_t id() const
|
||||
{
|
||||
return( pthread_self() );
|
||||
}
|
||||
#endif
|
||||
void exit( int status = 0 )
|
||||
{
|
||||
//INFO( "Exiting" );
|
||||
pthread_exit( (void *)&status );
|
||||
}
|
||||
static void *mThreadFunc( void *arg );
|
||||
void exit( int p_status = 0 )
|
||||
{
|
||||
//INFO( "Exiting" );
|
||||
pthread_exit( (void *)&p_status );
|
||||
}
|
||||
static void *mThreadFunc( void *arg );
|
||||
|
||||
public:
|
||||
virtual int run() = 0;
|
||||
virtual int run() = 0;
|
||||
|
||||
void start();
|
||||
void join();
|
||||
void kill( int signal );
|
||||
bool isThread()
|
||||
{
|
||||
return( mPid > -1 && pthread_equal( pthread_self(), mThread ) );
|
||||
}
|
||||
bool isStarted() const { return( mStarted ); }
|
||||
bool isRunning() const { return( mRunning ); }
|
||||
void start();
|
||||
void join();
|
||||
void kill( int signal );
|
||||
bool isThread()
|
||||
{
|
||||
return( mPid > -1 && pthread_equal( pthread_self(), mThread ) );
|
||||
}
|
||||
bool isStarted() const { return( mStarted ); }
|
||||
bool isRunning() const { return( mRunning ); }
|
||||
};
|
||||
|
||||
#endif // ZM_THREAD_H
|
||||
|
|
188
src/zm_time.h
188
src/zm_time.h
|
@ -29,48 +29,48 @@
|
|||
|
||||
struct DeltaTimeval
|
||||
{
|
||||
bool positive;
|
||||
unsigned long delta;
|
||||
unsigned long sec;
|
||||
unsigned long fsec;
|
||||
unsigned long prec;
|
||||
bool positive;
|
||||
unsigned long delta;
|
||||
unsigned long sec;
|
||||
unsigned long fsec;
|
||||
unsigned long prec;
|
||||
};
|
||||
|
||||
#define DT_GRAN_1000000 1000000
|
||||
#define DT_PREC_6 DT_GRAN_1000000
|
||||
#define DT_GRAN_100000 100000
|
||||
#define DT_PREC_5 DT_GRAN_100000
|
||||
#define DT_GRAN_10000 10000
|
||||
#define DT_PREC_4 DT_GRAN_10000
|
||||
#define DT_GRAN_1000 1000
|
||||
#define DT_PREC_3 DT_GRAN_1000
|
||||
#define DT_GRAN_100 100
|
||||
#define DT_PREC_2 DT_GRAN_100
|
||||
#define DT_GRAN_10 10
|
||||
#define DT_PREC_1 DT_GRAN_10
|
||||
#define DT_GRAN_1000000 1000000
|
||||
#define DT_PREC_6 DT_GRAN_1000000
|
||||
#define DT_GRAN_100000 100000
|
||||
#define DT_PREC_5 DT_GRAN_100000
|
||||
#define DT_GRAN_10000 10000
|
||||
#define DT_PREC_4 DT_GRAN_10000
|
||||
#define DT_GRAN_1000 1000
|
||||
#define DT_PREC_3 DT_GRAN_1000
|
||||
#define DT_GRAN_100 100
|
||||
#define DT_PREC_2 DT_GRAN_100
|
||||
#define DT_GRAN_10 10
|
||||
#define DT_PREC_1 DT_GRAN_10
|
||||
|
||||
#define DT_MAXGRAN DT_GRAN_1000000
|
||||
#define DT_MAXGRAN DT_GRAN_1000000
|
||||
|
||||
// This obviously wouldn't work for massive deltas but as it's mostly
|
||||
// for frames it will only usually be a fraction of a second or so
|
||||
#define DELTA_TIMEVAL( result, time1, time2, precision ) \
|
||||
{ \
|
||||
int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \
|
||||
result.positive = (delta>=0); \
|
||||
result.delta = abs(delta); \
|
||||
result.sec = result.delta/(precision); \
|
||||
result.fsec = result.delta%(precision); \
|
||||
result.prec = (precision); \
|
||||
int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \
|
||||
result.positive = (delta>=0); \
|
||||
result.delta = abs(delta); \
|
||||
result.sec = result.delta/(precision); \
|
||||
result.fsec = result.delta%(precision); \
|
||||
result.prec = (precision); \
|
||||
}
|
||||
|
||||
#define TIMEVAL_INTERVAL( result, time1, time2, precision ) \
|
||||
{ \
|
||||
int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \
|
||||
result.positive = (delta>=0); \
|
||||
result.delta = abs(delta); \
|
||||
result.sec = result.delta/(precision); \
|
||||
result.fsec = result.delta%(precision); \
|
||||
result.prec = (precision); \
|
||||
int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \
|
||||
result.positive = (delta>=0); \
|
||||
result.delta = abs(delta); \
|
||||
result.sec = result.delta/(precision); \
|
||||
result.fsec = result.delta%(precision); \
|
||||
result.prec = (precision); \
|
||||
}
|
||||
|
||||
#define USEC_PER_SEC 1000000
|
||||
|
@ -82,128 +82,128 @@ typedef typeof(tv.tv_usec) ast_suseconds_t;
|
|||
|
||||
inline int tvDiffUsec( struct timeval first, struct timeval last )
|
||||
{
|
||||
return( (last.tv_sec - first.tv_sec) * USEC_PER_SEC) + ((USEC_PER_SEC + last.tv_usec - first.tv_usec) - USEC_PER_SEC );
|
||||
return( (last.tv_sec - first.tv_sec) * USEC_PER_SEC) + ((USEC_PER_SEC + last.tv_usec - first.tv_usec) - USEC_PER_SEC );
|
||||
}
|
||||
|
||||
inline int tvDiffUsec( struct timeval first )
|
||||
{
|
||||
struct timeval now;
|
||||
gettimeofday( &now, NULL );
|
||||
return( tvDiffUsec( first, now ) );
|
||||
struct timeval now;
|
||||
gettimeofday( &now, NULL );
|
||||
return( tvDiffUsec( first, now ) );
|
||||
}
|
||||
|
||||
inline int tvDiffMsec( struct timeval first, struct timeval last )
|
||||
{
|
||||
return( (last.tv_sec - first.tv_sec) * MSEC_PER_SEC) + (((MSEC_PER_SEC + last.tv_usec - first.tv_usec) / MSEC_PER_SEC) - MSEC_PER_SEC );
|
||||
return( (last.tv_sec - first.tv_sec) * MSEC_PER_SEC) + (((MSEC_PER_SEC + last.tv_usec - first.tv_usec) / MSEC_PER_SEC) - MSEC_PER_SEC );
|
||||
}
|
||||
|
||||
inline int tvDiffMsec( struct timeval first )
|
||||
{
|
||||
struct timeval now;
|
||||
gettimeofday( &now, NULL );
|
||||
return( tvDiffMsec( first, now ) );
|
||||
struct timeval now;
|
||||
gettimeofday( &now, NULL );
|
||||
return( tvDiffMsec( first, now ) );
|
||||
}
|
||||
|
||||
inline double tvDiffSec( struct timeval first, struct timeval last )
|
||||
{
|
||||
return( double(last.tv_sec - first.tv_sec) + double(((USEC_PER_SEC + last.tv_usec - first.tv_usec) - USEC_PER_SEC) / (1.0*USEC_PER_SEC) ) );
|
||||
return( double(last.tv_sec - first.tv_sec) + double(((USEC_PER_SEC + last.tv_usec - first.tv_usec) - USEC_PER_SEC) / (1.0*USEC_PER_SEC) ) );
|
||||
}
|
||||
|
||||
inline double tvDiffSec( struct timeval first )
|
||||
{
|
||||
struct timeval now;
|
||||
gettimeofday( &now, NULL );
|
||||
return( tvDiffSec( first, now ) );
|
||||
struct timeval now;
|
||||
gettimeofday( &now, NULL );
|
||||
return( tvDiffSec( first, now ) );
|
||||
}
|
||||
|
||||
inline struct timeval tvZero()
|
||||
{
|
||||
struct timeval t = { 0, 0 };
|
||||
return( t );
|
||||
struct timeval t = { 0, 0 };
|
||||
return( t );
|
||||
}
|
||||
|
||||
inline int tvIsZero( const struct timeval t )
|
||||
{
|
||||
return( t.tv_sec == 0 && t.tv_usec == 0 );
|
||||
return( t.tv_sec == 0 && t.tv_usec == 0 );
|
||||
}
|
||||
|
||||
inline int tvCmp( struct timeval t1, struct timeval t2 )
|
||||
{
|
||||
if ( t1.tv_sec < t2.tv_sec )
|
||||
return( -1 );
|
||||
if ( t1.tv_sec > t2.tv_sec )
|
||||
return( 1 );
|
||||
if ( t1.tv_usec < t2.tv_usec )
|
||||
return( -1 );
|
||||
if ( t1.tv_usec > t2.tv_usec )
|
||||
return( 1 );
|
||||
return( 0 );
|
||||
if ( t1.tv_sec < t2.tv_sec )
|
||||
return( -1 );
|
||||
if ( t1.tv_sec > t2.tv_sec )
|
||||
return( 1 );
|
||||
if ( t1.tv_usec < t2.tv_usec )
|
||||
return( -1 );
|
||||
if ( t1.tv_usec > t2.tv_usec )
|
||||
return( 1 );
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
inline int tvEq( struct timeval t1, struct timeval t2 )
|
||||
{
|
||||
return( t1.tv_sec == t2.tv_sec && t1.tv_usec == t2.tv_usec );
|
||||
return( t1.tv_sec == t2.tv_sec && t1.tv_usec == t2.tv_usec );
|
||||
}
|
||||
|
||||
inline struct timeval tvNow( void )
|
||||
{
|
||||
struct timeval t;
|
||||
gettimeofday( &t, NULL );
|
||||
return( t );
|
||||
struct timeval t;
|
||||
gettimeofday( &t, NULL );
|
||||
return( t );
|
||||
}
|
||||
|
||||
inline struct timeval tvCheck( struct timeval &t )
|
||||
{
|
||||
if ( t.tv_usec >= USEC_PER_SEC )
|
||||
{
|
||||
Warning( "Timestamp too large %ld.%ld\n", t.tv_sec, (long int) t.tv_usec );
|
||||
t.tv_sec += t.tv_usec / USEC_PER_SEC;
|
||||
t.tv_usec %= USEC_PER_SEC;
|
||||
}
|
||||
else if ( t.tv_usec < 0 )
|
||||
{
|
||||
Warning( "Got negative timestamp %ld.%ld\n", t.tv_sec, (long int)t.tv_usec );
|
||||
t.tv_usec = 0;
|
||||
}
|
||||
return( t );
|
||||
if ( t.tv_usec >= USEC_PER_SEC )
|
||||
{
|
||||
Warning( "Timestamp too large %ld.%ld\n", t.tv_sec, (long int) t.tv_usec );
|
||||
t.tv_sec += t.tv_usec / USEC_PER_SEC;
|
||||
t.tv_usec %= USEC_PER_SEC;
|
||||
}
|
||||
else if ( t.tv_usec < 0 )
|
||||
{
|
||||
Warning( "Got negative timestamp %ld.%ld\n", t.tv_sec, (long int)t.tv_usec );
|
||||
t.tv_usec = 0;
|
||||
}
|
||||
return( t );
|
||||
}
|
||||
|
||||
// Add t2 to t1
|
||||
inline struct timeval tvAdd( struct timeval t1, struct timeval t2 )
|
||||
{
|
||||
tvCheck(t1);
|
||||
tvCheck(t2);
|
||||
t1.tv_sec += t2.tv_sec;
|
||||
t1.tv_usec += t2.tv_usec;
|
||||
if ( t1.tv_usec >= USEC_PER_SEC )
|
||||
{
|
||||
t1.tv_sec++;
|
||||
t1.tv_usec -= USEC_PER_SEC;
|
||||
}
|
||||
return( t1 );
|
||||
tvCheck(t1);
|
||||
tvCheck(t2);
|
||||
t1.tv_sec += t2.tv_sec;
|
||||
t1.tv_usec += t2.tv_usec;
|
||||
if ( t1.tv_usec >= USEC_PER_SEC )
|
||||
{
|
||||
t1.tv_sec++;
|
||||
t1.tv_usec -= USEC_PER_SEC;
|
||||
}
|
||||
return( t1 );
|
||||
}
|
||||
|
||||
// Subtract t2 from t1
|
||||
inline struct timeval tvSub( struct timeval t1, struct timeval t2 )
|
||||
{
|
||||
tvCheck(t1);
|
||||
tvCheck(t2);
|
||||
t1.tv_sec -= t2.tv_sec;
|
||||
t1.tv_usec -= t2.tv_usec;
|
||||
if ( t1.tv_usec < 0 )
|
||||
{
|
||||
t1.tv_sec--;
|
||||
t1.tv_usec += USEC_PER_SEC;
|
||||
}
|
||||
return( t1 ) ;
|
||||
tvCheck(t1);
|
||||
tvCheck(t2);
|
||||
t1.tv_sec -= t2.tv_sec;
|
||||
t1.tv_usec -= t2.tv_usec;
|
||||
if ( t1.tv_usec < 0 )
|
||||
{
|
||||
t1.tv_sec--;
|
||||
t1.tv_usec += USEC_PER_SEC;
|
||||
}
|
||||
return( t1 ) ;
|
||||
}
|
||||
|
||||
inline struct timeval tvMake( time_t sec, suseconds_t usec )
|
||||
{
|
||||
struct timeval t;
|
||||
t.tv_sec = sec;
|
||||
t.tv_usec = usec;
|
||||
return( t );
|
||||
struct timeval t;
|
||||
t.tv_sec = sec;
|
||||
t.tv_usec = usec;
|
||||
return( t );
|
||||
}
|
||||
|
||||
#endif // ZM_TIME_H
|
||||
|
|
114
src/zm_timer.cpp
114
src/zm_timer.cpp
|
@ -24,96 +24,96 @@
|
|||
int Timer::TimerThread::mNextTimerId = 0;
|
||||
|
||||
Timer::TimerThread::TimerThread( Timer &timer, int duration, bool repeat ) :
|
||||
mTimerId( 0 ),
|
||||
mTimer( timer ),
|
||||
mDuration( duration ),
|
||||
mRepeat( repeat ),
|
||||
mReset( false ),
|
||||
mExpiryFlag( true )
|
||||
mTimerId( 0 ),
|
||||
mTimer( timer ),
|
||||
mDuration( duration ),
|
||||
mRepeat( repeat ),
|
||||
mReset( false ),
|
||||
mExpiryFlag( true )
|
||||
{
|
||||
mAccessMutex.lock();
|
||||
mTimerId = mNextTimerId++;
|
||||
Debug( 5, "Creating timer %d for %d seconds%s", mTimerId, mDuration, mRepeat?", repeating":"" );
|
||||
mAccessMutex.unlock();
|
||||
mAccessMutex.lock();
|
||||
mTimerId = mNextTimerId++;
|
||||
Debug( 5, "Creating timer %d for %d seconds%s", mTimerId, mDuration, mRepeat?", repeating":"" );
|
||||
mAccessMutex.unlock();
|
||||
}
|
||||
|
||||
Timer::TimerThread::~TimerThread()
|
||||
{
|
||||
cancel();
|
||||
cancel();
|
||||
}
|
||||
|
||||
void Timer::TimerThread::cancel()
|
||||
{
|
||||
mAccessMutex.lock();
|
||||
if ( mRunning )
|
||||
{
|
||||
Debug( 4, "Cancelling timer %d", mTimerId );
|
||||
mRepeat = false;
|
||||
mReset = false;
|
||||
mExpiryFlag.updateValueSignal( false );
|
||||
}
|
||||
mAccessMutex.unlock();
|
||||
mAccessMutex.lock();
|
||||
if ( mRunning )
|
||||
{
|
||||
Debug( 4, "Cancelling timer %d", mTimerId );
|
||||
mRepeat = false;
|
||||
mReset = false;
|
||||
mExpiryFlag.updateValueSignal( false );
|
||||
}
|
||||
mAccessMutex.unlock();
|
||||
}
|
||||
|
||||
void Timer::TimerThread::reset()
|
||||
{
|
||||
mAccessMutex.lock();
|
||||
if ( mRunning )
|
||||
{
|
||||
Debug( 4, "Resetting timer" );
|
||||
mReset = true;
|
||||
mExpiryFlag.updateValueSignal( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
Error( "Attempting to reset expired timer %d", mTimerId );
|
||||
}
|
||||
mAccessMutex.unlock();
|
||||
mAccessMutex.lock();
|
||||
if ( mRunning )
|
||||
{
|
||||
Debug( 4, "Resetting timer" );
|
||||
mReset = true;
|
||||
mExpiryFlag.updateValueSignal( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
Error( "Attempting to reset expired timer %d", mTimerId );
|
||||
}
|
||||
mAccessMutex.unlock();
|
||||
}
|
||||
|
||||
int Timer::TimerThread::run()
|
||||
{
|
||||
Debug( 4, "Starting timer %d for %d seconds", mTimerId, mDuration );
|
||||
bool timerExpired = false;
|
||||
do
|
||||
Debug( 4, "Starting timer %d for %d seconds", mTimerId, mDuration );
|
||||
bool timerExpired = false;
|
||||
do
|
||||
{
|
||||
mAccessMutex.lock();
|
||||
mReset = false;
|
||||
mExpiryFlag.setValue( true );
|
||||
mAccessMutex.unlock();
|
||||
timerExpired = mExpiryFlag.getUpdatedValue( mDuration );
|
||||
mAccessMutex.lock();
|
||||
if ( timerExpired )
|
||||
{
|
||||
mAccessMutex.lock();
|
||||
mReset = false;
|
||||
mExpiryFlag.setValue( true );
|
||||
mAccessMutex.unlock();
|
||||
timerExpired = mExpiryFlag.getUpdatedValue( mDuration );
|
||||
mAccessMutex.lock();
|
||||
if ( timerExpired )
|
||||
{
|
||||
Debug( 4, "Timer %d expired", mTimerId );
|
||||
mTimer.expire();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug( 4, "Timer %d %s", mTimerId, mReset?"reset":"cancelled" );
|
||||
}
|
||||
mAccessMutex.unlock();
|
||||
} while ( mRepeat || (mReset && !timerExpired) );
|
||||
return( timerExpired );
|
||||
Debug( 4, "Timer %d expired", mTimerId );
|
||||
mTimer.expire();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug( 4, "Timer %d %s", mTimerId, mReset?"reset":"cancelled" );
|
||||
}
|
||||
mAccessMutex.unlock();
|
||||
} while ( mRepeat || (mReset && !timerExpired) );
|
||||
return( timerExpired );
|
||||
}
|
||||
|
||||
Timer::Timer( int timeout, bool repeat ) : mTimerThread( *this, timeout, repeat )
|
||||
{
|
||||
mTimerThread.start();
|
||||
mTimerThread.start();
|
||||
}
|
||||
|
||||
Timer::~Timer()
|
||||
{
|
||||
//cancel();
|
||||
//cancel();
|
||||
}
|
||||
|
||||
void Timer::Timer::cancel()
|
||||
{
|
||||
mTimerThread.cancel();
|
||||
mTimerThread.cancel();
|
||||
}
|
||||
|
||||
void Timer::Timer::reset()
|
||||
{
|
||||
mTimerThread.reset();
|
||||
mTimerThread.reset();
|
||||
}
|
||||
|
||||
|
|
120
src/zm_timer.h
120
src/zm_timer.h
|
@ -30,81 +30,81 @@
|
|||
class Timer
|
||||
{
|
||||
private:
|
||||
class TimerException : public Exception
|
||||
{
|
||||
private:
|
||||
class TimerException : public Exception
|
||||
{
|
||||
private:
|
||||
#ifndef SOLARIS
|
||||
pid_t pid() {
|
||||
pid_t tid;
|
||||
pid_t pid() {
|
||||
pid_t tid;
|
||||
#ifdef __FreeBSD__
|
||||
long lwpid;
|
||||
thr_self(&lwpid);
|
||||
tid = lwpid;
|
||||
long lwpid;
|
||||
thr_self(&lwpid);
|
||||
tid = lwpid;
|
||||
#else
|
||||
#ifdef __FreeBSD_kernel__
|
||||
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
||||
#else
|
||||
tid=syscall(SYS_gettid);
|
||||
#endif
|
||||
#ifdef __FreeBSD_kernel__
|
||||
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
||||
#else
|
||||
tid=syscall(SYS_gettid);
|
||||
#endif
|
||||
#endif
|
||||
return tid;
|
||||
}
|
||||
return tid;
|
||||
}
|
||||
#else
|
||||
pthread_t pid() { return( pthread_self() ); }
|
||||
pthread_t pid() { return( pthread_self() ); }
|
||||
#endif
|
||||
public:
|
||||
TimerException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)pid() ) )
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class TimerThread : public Thread
|
||||
public:
|
||||
TimerException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)pid() ) )
|
||||
{
|
||||
private:
|
||||
typedef ThreadData<bool> ExpiryFlag;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
static int mNextTimerId;
|
||||
class TimerThread : public Thread
|
||||
{
|
||||
private:
|
||||
typedef ThreadData<bool> ExpiryFlag;
|
||||
|
||||
private:
|
||||
int mTimerId;
|
||||
Timer &mTimer;
|
||||
int mDuration;
|
||||
int mRepeat;
|
||||
int mReset;
|
||||
ExpiryFlag mExpiryFlag;
|
||||
Mutex mAccessMutex;
|
||||
private:
|
||||
static int mNextTimerId;
|
||||
|
||||
private:
|
||||
void quit()
|
||||
{
|
||||
cancel();
|
||||
}
|
||||
private:
|
||||
int mTimerId;
|
||||
Timer &mTimer;
|
||||
int mDuration;
|
||||
int mRepeat;
|
||||
int mReset;
|
||||
ExpiryFlag mExpiryFlag;
|
||||
Mutex mAccessMutex;
|
||||
|
||||
public:
|
||||
TimerThread( Timer &timer, int timeout, bool repeat );
|
||||
~TimerThread();
|
||||
private:
|
||||
void quit()
|
||||
{
|
||||
cancel();
|
||||
}
|
||||
|
||||
void cancel();
|
||||
void reset();
|
||||
int run();
|
||||
};
|
||||
public:
|
||||
TimerThread( Timer &timer, int timeout, bool repeat );
|
||||
~TimerThread();
|
||||
|
||||
protected:
|
||||
TimerThread mTimerThread;
|
||||
|
||||
protected:
|
||||
Timer( int timeout, bool repeat=false );
|
||||
|
||||
public:
|
||||
virtual ~Timer();
|
||||
|
||||
protected:
|
||||
virtual void expire()=0;
|
||||
|
||||
public:
|
||||
void cancel();
|
||||
void reset();
|
||||
int run();
|
||||
};
|
||||
|
||||
protected:
|
||||
TimerThread mTimerThread;
|
||||
|
||||
protected:
|
||||
Timer( int timeout, bool repeat=false );
|
||||
|
||||
public:
|
||||
virtual ~Timer();
|
||||
|
||||
protected:
|
||||
virtual void expire()=0;
|
||||
|
||||
public:
|
||||
void cancel();
|
||||
void reset();
|
||||
};
|
||||
|
||||
#endif // ZM_TIMER_H
|
||||
|
|
332
src/zm_user.cpp
332
src/zm_user.cpp
|
@ -29,119 +29,119 @@
|
|||
|
||||
User::User()
|
||||
{
|
||||
username[0] = password[0] = 0;
|
||||
enabled = false;
|
||||
stream = events = control = monitors = system = PERM_NONE;
|
||||
monitor_ids = 0;
|
||||
username[0] = password[0] = 0;
|
||||
enabled = false;
|
||||
stream = events = control = monitors = system = PERM_NONE;
|
||||
monitor_ids = 0;
|
||||
}
|
||||
|
||||
User::User( MYSQL_ROW &dbrow )
|
||||
{
|
||||
int index = 0;
|
||||
strncpy( username, dbrow[index++], sizeof(username) );
|
||||
strncpy( password, dbrow[index++], sizeof(password) );
|
||||
enabled = (bool)atoi( dbrow[index++] );
|
||||
stream = (Permission)atoi( dbrow[index++] );
|
||||
events = (Permission)atoi( dbrow[index++] );
|
||||
control = (Permission)atoi( dbrow[index++] );
|
||||
monitors = (Permission)atoi( dbrow[index++] );
|
||||
system = (Permission)atoi( dbrow[index++] );
|
||||
monitor_ids = 0;
|
||||
char *monitor_ids_str = dbrow[index++];
|
||||
if ( monitor_ids_str && *monitor_ids_str )
|
||||
{
|
||||
monitor_ids = new int[strlen(monitor_ids_str)];
|
||||
int n_monitor_ids = 0;
|
||||
const char *ptr = monitor_ids_str;
|
||||
do
|
||||
{
|
||||
int id = 0;
|
||||
while( isdigit( *ptr ) )
|
||||
{
|
||||
id *= 10;
|
||||
id += *ptr-'0';
|
||||
ptr++;
|
||||
}
|
||||
if ( id )
|
||||
{
|
||||
monitor_ids[n_monitor_ids++] = id;
|
||||
if ( !*ptr )
|
||||
break;
|
||||
}
|
||||
while ( !isdigit( *ptr ) )
|
||||
ptr++;
|
||||
} while( *ptr );
|
||||
monitor_ids[n_monitor_ids] = 0;
|
||||
}
|
||||
int index = 0;
|
||||
strncpy( username, dbrow[index++], sizeof(username) );
|
||||
strncpy( password, dbrow[index++], sizeof(password) );
|
||||
enabled = (bool)atoi( dbrow[index++] );
|
||||
stream = (Permission)atoi( dbrow[index++] );
|
||||
events = (Permission)atoi( dbrow[index++] );
|
||||
control = (Permission)atoi( dbrow[index++] );
|
||||
monitors = (Permission)atoi( dbrow[index++] );
|
||||
system = (Permission)atoi( dbrow[index++] );
|
||||
monitor_ids = 0;
|
||||
char *monitor_ids_str = dbrow[index++];
|
||||
if ( monitor_ids_str && *monitor_ids_str )
|
||||
{
|
||||
monitor_ids = new int[strlen(monitor_ids_str)];
|
||||
int n_monitor_ids = 0;
|
||||
const char *ptr = monitor_ids_str;
|
||||
do
|
||||
{
|
||||
int id = 0;
|
||||
while( isdigit( *ptr ) )
|
||||
{
|
||||
id *= 10;
|
||||
id += *ptr-'0';
|
||||
ptr++;
|
||||
}
|
||||
if ( id )
|
||||
{
|
||||
monitor_ids[n_monitor_ids++] = id;
|
||||
if ( !*ptr )
|
||||
break;
|
||||
}
|
||||
while ( !isdigit( *ptr ) )
|
||||
ptr++;
|
||||
} while( *ptr );
|
||||
monitor_ids[n_monitor_ids] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
User::~User()
|
||||
{
|
||||
delete monitor_ids;
|
||||
delete monitor_ids;
|
||||
}
|
||||
|
||||
bool User::canAccess( int monitor_id )
|
||||
{
|
||||
if ( !monitor_ids )
|
||||
{
|
||||
return( true );
|
||||
}
|
||||
for ( int i = 0; monitor_ids[i]; i++ )
|
||||
{
|
||||
if ( monitor_ids[i] == monitor_id )
|
||||
{
|
||||
return( true );
|
||||
}
|
||||
}
|
||||
return( false );
|
||||
if ( !monitor_ids )
|
||||
{
|
||||
return( true );
|
||||
}
|
||||
for ( int i = 0; monitor_ids[i]; i++ )
|
||||
{
|
||||
if ( monitor_ids[i] == monitor_id )
|
||||
{
|
||||
return( true );
|
||||
}
|
||||
}
|
||||
return( false );
|
||||
}
|
||||
|
||||
// Function to load a user from username and password
|
||||
// Please note that in auth relay mode = none, password is NULL
|
||||
User *zmLoadUser( const char *username, const char *password )
|
||||
{
|
||||
char sql[ZM_SQL_SML_BUFSIZ] = "";
|
||||
char safer_username[65]; // current db username size is 32
|
||||
char safer_password[129]; // current db password size is 64
|
||||
char sql[ZM_SQL_SML_BUFSIZ] = "";
|
||||
char safer_username[65]; // current db username size is 32
|
||||
char safer_password[129]; // current db password size is 64
|
||||
|
||||
// According to docs, size of safer_whatever must be 2*length+1 due to unicode conversions + null terminator.
|
||||
mysql_real_escape_string(&dbconn, safer_username, username, strlen( username ) );
|
||||
// According to docs, size of safer_whatever must be 2*length+1 due to unicode conversions + null terminator.
|
||||
mysql_real_escape_string(&dbconn, safer_username, username, strlen( username ) );
|
||||
|
||||
if ( password ) {
|
||||
mysql_real_escape_string(&dbconn, safer_password, password, strlen( password ) );
|
||||
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Password = password('%s') and Enabled = 1", safer_username, safer_password );
|
||||
} else {
|
||||
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Enabled = 1", safer_username );
|
||||
}
|
||||
if ( password ) {
|
||||
mysql_real_escape_string(&dbconn, safer_password, password, strlen( password ) );
|
||||
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Password = password('%s') and Enabled = 1", safer_username, safer_password );
|
||||
} else {
|
||||
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Enabled = 1", safer_username );
|
||||
}
|
||||
|
||||
if ( mysql_query( &dbconn, sql ) )
|
||||
{
|
||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
if ( mysql_query( &dbconn, sql ) )
|
||||
{
|
||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
|
||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||
if ( !result )
|
||||
{
|
||||
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
int n_users = mysql_num_rows( result );
|
||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||
if ( !result )
|
||||
{
|
||||
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
int n_users = mysql_num_rows( result );
|
||||
|
||||
if ( n_users != 1 )
|
||||
{
|
||||
Warning( "Unable to authenticate user %s", username );
|
||||
return( 0 );
|
||||
}
|
||||
if ( n_users != 1 )
|
||||
{
|
||||
Warning( "Unable to authenticate user %s", username );
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
MYSQL_ROW dbrow = mysql_fetch_row( result );
|
||||
MYSQL_ROW dbrow = mysql_fetch_row( result );
|
||||
|
||||
User *user = new User( dbrow );
|
||||
Info( "Authenticated user '%s'", user->getUsername() );
|
||||
User *user = new User( dbrow );
|
||||
Info( "Authenticated user '%s'", user->getUsername() );
|
||||
|
||||
mysql_free_result( result );
|
||||
mysql_free_result( result );
|
||||
|
||||
return( user );
|
||||
return( user );
|
||||
}
|
||||
|
||||
// Function to validate an authentication string
|
||||
|
@ -149,102 +149,102 @@ User *zmLoadAuthUser( const char *auth, bool use_remote_addr )
|
|||
{
|
||||
#if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT
|
||||
#ifdef HAVE_GCRYPT_H
|
||||
// Special initialisation for libgcrypt
|
||||
if ( !gcry_check_version( GCRYPT_VERSION ) )
|
||||
{
|
||||
Fatal( "Unable to initialise libgcrypt" );
|
||||
}
|
||||
gcry_control( GCRYCTL_DISABLE_SECMEM, 0 );
|
||||
gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 );
|
||||
// Special initialisation for libgcrypt
|
||||
if ( !gcry_check_version( GCRYPT_VERSION ) )
|
||||
{
|
||||
Fatal( "Unable to initialise libgcrypt" );
|
||||
}
|
||||
gcry_control( GCRYCTL_DISABLE_SECMEM, 0 );
|
||||
gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 );
|
||||
#endif // HAVE_GCRYPT_H
|
||||
|
||||
const char *remote_addr = "";
|
||||
if ( use_remote_addr )
|
||||
{
|
||||
remote_addr = getenv( "REMOTE_ADDR" );
|
||||
if ( !remote_addr )
|
||||
{
|
||||
Warning( "Can't determine remote address, using null" );
|
||||
remote_addr = "";
|
||||
}
|
||||
}
|
||||
const char *remote_addr = "";
|
||||
if ( use_remote_addr )
|
||||
{
|
||||
remote_addr = getenv( "REMOTE_ADDR" );
|
||||
if ( !remote_addr )
|
||||
{
|
||||
Warning( "Can't determine remote address, using null" );
|
||||
remote_addr = "";
|
||||
}
|
||||
}
|
||||
|
||||
Debug( 1, "Attempting to authenticate user from auth string '%s'", auth );
|
||||
char sql[ZM_SQL_SML_BUFSIZ] = "";
|
||||
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Enabled = 1" );
|
||||
Debug( 1, "Attempting to authenticate user from auth string '%s'", auth );
|
||||
char sql[ZM_SQL_SML_BUFSIZ] = "";
|
||||
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Enabled = 1" );
|
||||
|
||||
if ( mysql_query( &dbconn, sql ) )
|
||||
{
|
||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
if ( mysql_query( &dbconn, sql ) )
|
||||
{
|
||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
|
||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||
if ( !result )
|
||||
{
|
||||
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
int n_users = mysql_num_rows( result );
|
||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||
if ( !result )
|
||||
{
|
||||
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
int n_users = mysql_num_rows( result );
|
||||
|
||||
if ( n_users < 1 )
|
||||
{
|
||||
Warning( "Unable to authenticate user" );
|
||||
return( 0 );
|
||||
}
|
||||
if ( n_users < 1 )
|
||||
{
|
||||
Warning( "Unable to authenticate user" );
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
while( MYSQL_ROW dbrow = mysql_fetch_row( result ) )
|
||||
{
|
||||
const char *user = dbrow[0];
|
||||
const char *pass = dbrow[1];
|
||||
while( MYSQL_ROW dbrow = mysql_fetch_row( result ) )
|
||||
{
|
||||
const char *user = dbrow[0];
|
||||
const char *pass = dbrow[1];
|
||||
|
||||
char auth_key[512] = "";
|
||||
char auth_md5[32+1] = "";
|
||||
size_t md5len = 16;
|
||||
unsigned char md5sum[md5len];
|
||||
char auth_key[512] = "";
|
||||
char auth_md5[32+1] = "";
|
||||
size_t md5len = 16;
|
||||
unsigned char md5sum[md5len];
|
||||
|
||||
time_t now = time( 0 );
|
||||
int max_tries = 2;
|
||||
time_t now = time( 0 );
|
||||
int max_tries = 2;
|
||||
|
||||
for ( int i = 0; i < max_tries; i++, now -= (60*60) )
|
||||
{
|
||||
struct tm *now_tm = localtime( &now );
|
||||
for ( int i = 0; i < max_tries; i++, now -= (60*60) )
|
||||
{
|
||||
struct tm *now_tm = localtime( &now );
|
||||
|
||||
snprintf( auth_key, sizeof(auth_key), "%s%s%s%s%d%d%d%d",
|
||||
config.auth_hash_secret,
|
||||
user,
|
||||
pass,
|
||||
remote_addr,
|
||||
now_tm->tm_hour,
|
||||
now_tm->tm_mday,
|
||||
now_tm->tm_mon,
|
||||
now_tm->tm_year
|
||||
);
|
||||
snprintf( auth_key, sizeof(auth_key), "%s%s%s%s%d%d%d%d",
|
||||
config.auth_hash_secret,
|
||||
user,
|
||||
pass,
|
||||
remote_addr,
|
||||
now_tm->tm_hour,
|
||||
now_tm->tm_mday,
|
||||
now_tm->tm_mon,
|
||||
now_tm->tm_year
|
||||
);
|
||||
|
||||
#if HAVE_DECL_MD5
|
||||
MD5( (unsigned char *)auth_key, strlen(auth_key), md5sum );
|
||||
MD5( (unsigned char *)auth_key, strlen(auth_key), md5sum );
|
||||
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
||||
gnutls_datum_t md5data = { (unsigned char *)auth_key, strlen(auth_key) };
|
||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5data, md5sum, &md5len );
|
||||
gnutls_datum_t md5data = { (unsigned char *)auth_key, strlen(auth_key) };
|
||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5data, md5sum, &md5len );
|
||||
#endif
|
||||
auth_md5[0] = '\0';
|
||||
for ( unsigned int j = 0; j < md5len; j++ )
|
||||
{
|
||||
sprintf( &auth_md5[2*j], "%02x", md5sum[j] );
|
||||
}
|
||||
Debug( 1, "Checking auth_key '%s' -> auth_md5 '%s'", auth_key, auth_md5 );
|
||||
auth_md5[0] = '\0';
|
||||
for ( unsigned int j = 0; j < md5len; j++ )
|
||||
{
|
||||
sprintf( &auth_md5[2*j], "%02x", md5sum[j] );
|
||||
}
|
||||
Debug( 1, "Checking auth_key '%s' -> auth_md5 '%s'", auth_key, auth_md5 );
|
||||
|
||||
if ( !strcmp( auth, auth_md5 ) )
|
||||
{
|
||||
// We have a match
|
||||
User *user = new User( dbrow );
|
||||
Debug(1, "Authenticated user '%s'", user->getUsername() );
|
||||
return( user );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( !strcmp( auth, auth_md5 ) )
|
||||
{
|
||||
// We have a match
|
||||
User *user = new User( dbrow );
|
||||
Debug(1, "Authenticated user '%s'", user->getUsername() );
|
||||
return( user );
|
||||
}
|
||||
}
|
||||
}
|
||||
#else // HAVE_DECL_MD5
|
||||
Error( "You need to build with gnutls or openssl installed to use hash based authentication" );
|
||||
Error( "You need to build with gnutls or openssl installed to use hash based authentication" );
|
||||
#endif // HAVE_DECL_MD5
|
||||
return( 0 );
|
||||
return( 0 );
|
||||
}
|
||||
|
|
|
@ -39,33 +39,33 @@
|
|||
class User
|
||||
{
|
||||
public:
|
||||
typedef enum { PERM_NONE=1, PERM_VIEW, PERM_EDIT } Permission;
|
||||
typedef enum { PERM_NONE=1, PERM_VIEW, PERM_EDIT } Permission;
|
||||
|
||||
protected:
|
||||
char username[32+1];
|
||||
char password[64+1];
|
||||
bool enabled;
|
||||
Permission stream;
|
||||
Permission events;
|
||||
Permission control;
|
||||
Permission monitors;
|
||||
Permission system;
|
||||
int *monitor_ids;
|
||||
char username[32+1];
|
||||
char password[64+1];
|
||||
bool enabled;
|
||||
Permission stream;
|
||||
Permission events;
|
||||
Permission control;
|
||||
Permission monitors;
|
||||
Permission system;
|
||||
int *monitor_ids;
|
||||
|
||||
public:
|
||||
User();
|
||||
User( MYSQL_ROW &dbrow );
|
||||
~User();
|
||||
User();
|
||||
User( MYSQL_ROW &dbrow );
|
||||
~User();
|
||||
|
||||
const char *getUsername() const { return( username ); }
|
||||
const char *getPassword() const { return( password ); }
|
||||
bool isEnabled() const { return( enabled ); }
|
||||
Permission getStream() const { return( stream ); }
|
||||
Permission getEvents() const { return( events ); }
|
||||
Permission getControl() const { return( control ); }
|
||||
Permission getMonitors() const { return( monitors ); }
|
||||
Permission getSystem() const { return( system ); }
|
||||
bool canAccess( int monitor_id );
|
||||
const char *getUsername() const { return( username ); }
|
||||
const char *getPassword() const { return( password ); }
|
||||
bool isEnabled() const { return( enabled ); }
|
||||
Permission getStream() const { return( stream ); }
|
||||
Permission getEvents() const { return( events ); }
|
||||
Permission getControl() const { return( control ); }
|
||||
Permission getMonitors() const { return( monitors ); }
|
||||
Permission getSystem() const { return( system ); }
|
||||
bool canAccess( int monitor_id );
|
||||
};
|
||||
|
||||
User *zmLoadUser( const char *username, const char *password=0 );
|
||||
|
|
440
src/zm_utils.cpp
440
src/zm_utils.cpp
|
@ -28,256 +28,256 @@
|
|||
unsigned int sseversion = 0;
|
||||
|
||||
std::string trimSet(std::string str, std::string trimset) {
|
||||
// Trim Both leading and trailing sets
|
||||
size_t startpos = str.find_first_not_of(trimset); // Find the first character position after excluding leading blank spaces
|
||||
size_t endpos = str.find_last_not_of(trimset); // Find the first character position from reverse af
|
||||
// Trim Both leading and trailing sets
|
||||
size_t startpos = str.find_first_not_of(trimset); // Find the first character position after excluding leading blank spaces
|
||||
size_t endpos = str.find_last_not_of(trimset); // Find the first character position from reverse af
|
||||
|
||||
// if all spaces or empty return an empty string
|
||||
if(( std::string::npos == startpos ) || ( std::string::npos == endpos))
|
||||
{
|
||||
return std::string("");
|
||||
}
|
||||
else
|
||||
return str.substr( startpos, endpos-startpos+1 );
|
||||
// if all spaces or empty return an empty string
|
||||
if(( std::string::npos == startpos ) || ( std::string::npos == endpos))
|
||||
{
|
||||
return std::string("");
|
||||
}
|
||||
else
|
||||
return str.substr( startpos, endpos-startpos+1 );
|
||||
}
|
||||
|
||||
std::string trimSpaces(std::string str)
|
||||
{
|
||||
return trimSet(str, " \t");
|
||||
return trimSet(str, " \t");
|
||||
}
|
||||
|
||||
std::string replaceAll(std::string str, std::string from, std::string to) {
|
||||
if(from.empty())
|
||||
return str;
|
||||
size_t start_pos = 0;
|
||||
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
||||
str.replace(start_pos, from.length(), to);
|
||||
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
|
||||
}
|
||||
if(from.empty())
|
||||
return str;
|
||||
size_t start_pos = 0;
|
||||
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
||||
str.replace(start_pos, from.length(), to);
|
||||
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
const std::string stringtf( const char *format, ... )
|
||||
{
|
||||
va_list ap;
|
||||
char tempBuffer[8192];
|
||||
std::string tempString;
|
||||
va_list ap;
|
||||
char tempBuffer[8192];
|
||||
std::string tempString;
|
||||
|
||||
va_start(ap, format );
|
||||
vsnprintf( tempBuffer, sizeof(tempBuffer), format , ap );
|
||||
va_end(ap);
|
||||
va_start(ap, format );
|
||||
vsnprintf( tempBuffer, sizeof(tempBuffer), format , ap );
|
||||
va_end(ap);
|
||||
|
||||
tempString = tempBuffer;
|
||||
tempString = tempBuffer;
|
||||
|
||||
return( tempString );
|
||||
return( tempString );
|
||||
}
|
||||
|
||||
const std::string stringtf( const std::string &format, ... )
|
||||
{
|
||||
va_list ap;
|
||||
char tempBuffer[8192];
|
||||
std::string tempString;
|
||||
va_list ap;
|
||||
char tempBuffer[8192];
|
||||
std::string tempString;
|
||||
|
||||
va_start(ap, format );
|
||||
vsnprintf( tempBuffer, sizeof(tempBuffer), format.c_str() , ap );
|
||||
va_end(ap);
|
||||
va_start(ap, format );
|
||||
vsnprintf( tempBuffer, sizeof(tempBuffer), format.c_str() , ap );
|
||||
va_end(ap);
|
||||
|
||||
tempString = tempBuffer;
|
||||
tempString = tempBuffer;
|
||||
|
||||
return( tempString );
|
||||
return( tempString );
|
||||
}
|
||||
|
||||
bool startsWith( const std::string &haystack, const std::string &needle )
|
||||
{
|
||||
return( haystack.substr( 0, needle.length() ) == needle );
|
||||
return( haystack.substr( 0, needle.length() ) == needle );
|
||||
}
|
||||
|
||||
StringVector split( const std::string &string, const std::string chars, int limit )
|
||||
{
|
||||
StringVector stringVector;
|
||||
std::string tempString = string;
|
||||
std::string::size_type startIndex = 0;
|
||||
std::string::size_type endIndex = 0;
|
||||
StringVector stringVector;
|
||||
std::string tempString = string;
|
||||
std::string::size_type startIndex = 0;
|
||||
std::string::size_type endIndex = 0;
|
||||
|
||||
//Info( "Looking for '%s' in '%s', limit %d", chars.c_str(), string.c_str(), limit );
|
||||
do
|
||||
//Info( "Looking for '%s' in '%s', limit %d", chars.c_str(), string.c_str(), limit );
|
||||
do
|
||||
{
|
||||
// Find delimiters
|
||||
endIndex = string.find_first_of( chars, startIndex );
|
||||
//Info( "Got endIndex at %d", endIndex );
|
||||
if ( endIndex > 0 )
|
||||
{
|
||||
// Find delimiters
|
||||
endIndex = string.find_first_of( chars, startIndex );
|
||||
//Info( "Got endIndex at %d", endIndex );
|
||||
if ( endIndex > 0 )
|
||||
{
|
||||
//Info( "Adding '%s'", string.substr( startIndex, endIndex-startIndex ).c_str() );
|
||||
stringVector.push_back( string.substr( startIndex, endIndex-startIndex ) );
|
||||
}
|
||||
if ( endIndex == std::string::npos )
|
||||
break;
|
||||
// Find non-delimiters
|
||||
startIndex = tempString.find_first_not_of( chars, endIndex );
|
||||
if ( limit && (stringVector.size() == (unsigned int)(limit-1)) )
|
||||
{
|
||||
stringVector.push_back( string.substr( startIndex ) );
|
||||
break;
|
||||
}
|
||||
//Info( "Got new startIndex at %d", startIndex );
|
||||
} while ( startIndex != std::string::npos );
|
||||
//Info( "Finished with %d strings", stringVector.size() );
|
||||
//Info( "Adding '%s'", string.substr( startIndex, endIndex-startIndex ).c_str() );
|
||||
stringVector.push_back( string.substr( startIndex, endIndex-startIndex ) );
|
||||
}
|
||||
if ( endIndex == std::string::npos )
|
||||
break;
|
||||
// Find non-delimiters
|
||||
startIndex = tempString.find_first_not_of( chars, endIndex );
|
||||
if ( limit && (stringVector.size() == (unsigned int)(limit-1)) )
|
||||
{
|
||||
stringVector.push_back( string.substr( startIndex ) );
|
||||
break;
|
||||
}
|
||||
//Info( "Got new startIndex at %d", startIndex );
|
||||
} while ( startIndex != std::string::npos );
|
||||
//Info( "Finished with %d strings", stringVector.size() );
|
||||
|
||||
return( stringVector );
|
||||
return( stringVector );
|
||||
}
|
||||
|
||||
const std::string join(const StringVector v, const char * delim ) {
|
||||
std::stringstream ss;
|
||||
std::stringstream ss;
|
||||
|
||||
for(size_t i = 0; i < v.size(); ++i) {
|
||||
if(i != 0)
|
||||
ss << ",";
|
||||
ss << v[i];
|
||||
}
|
||||
return ss.str();
|
||||
for(size_t i = 0; i < v.size(); ++i) {
|
||||
if(i != 0)
|
||||
ss << ",";
|
||||
ss << v[i];
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
const std::string base64Encode( const std::string &inString )
|
||||
{
|
||||
static char base64_table[64] = { '\0' };
|
||||
static char base64_table[64] = { '\0' };
|
||||
|
||||
if ( !base64_table[0] )
|
||||
{
|
||||
int i = 0;
|
||||
for ( char c = 'A'; c <= 'Z'; c++ )
|
||||
base64_table[i++] = c;
|
||||
for ( char c = 'a'; c <= 'z'; c++ )
|
||||
base64_table[i++] = c;
|
||||
for ( char c = '0'; c <= '9'; c++ )
|
||||
base64_table[i++] = c;
|
||||
base64_table[i++] = '+';
|
||||
base64_table[i++] = '/';
|
||||
}
|
||||
if ( !base64_table[0] )
|
||||
{
|
||||
int i = 0;
|
||||
for ( char c = 'A'; c <= 'Z'; c++ )
|
||||
base64_table[i++] = c;
|
||||
for ( char c = 'a'; c <= 'z'; c++ )
|
||||
base64_table[i++] = c;
|
||||
for ( char c = '0'; c <= '9'; c++ )
|
||||
base64_table[i++] = c;
|
||||
base64_table[i++] = '+';
|
||||
base64_table[i++] = '/';
|
||||
}
|
||||
|
||||
std::string outString;
|
||||
outString.reserve( 2 * inString.size() );
|
||||
std::string outString;
|
||||
outString.reserve( 2 * inString.size() );
|
||||
|
||||
const char *inPtr = inString.c_str();
|
||||
while( *inPtr )
|
||||
{
|
||||
unsigned char selection = *inPtr >> 2;
|
||||
unsigned char remainder = (*inPtr++ & 0x03) << 4;
|
||||
outString += base64_table[selection];
|
||||
const char *inPtr = inString.c_str();
|
||||
while( *inPtr )
|
||||
{
|
||||
unsigned char selection = *inPtr >> 2;
|
||||
unsigned char remainder = (*inPtr++ & 0x03) << 4;
|
||||
outString += base64_table[selection];
|
||||
|
||||
if ( *inPtr )
|
||||
{
|
||||
selection = remainder | (*inPtr >> 4);
|
||||
remainder = (*inPtr++ & 0x0f) << 2;
|
||||
outString += base64_table[selection];
|
||||
if ( *inPtr )
|
||||
{
|
||||
selection = remainder | (*inPtr >> 4);
|
||||
remainder = (*inPtr++ & 0x0f) << 2;
|
||||
outString += base64_table[selection];
|
||||
|
||||
if ( *inPtr )
|
||||
{
|
||||
selection = remainder | (*inPtr >> 6);
|
||||
outString += base64_table[selection];
|
||||
selection = (*inPtr++ & 0x3f);
|
||||
outString += base64_table[selection];
|
||||
}
|
||||
else
|
||||
{
|
||||
outString += base64_table[remainder];
|
||||
outString += '=';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
outString += base64_table[remainder];
|
||||
outString += '=';
|
||||
outString += '=';
|
||||
}
|
||||
}
|
||||
return( outString );
|
||||
if ( *inPtr )
|
||||
{
|
||||
selection = remainder | (*inPtr >> 6);
|
||||
outString += base64_table[selection];
|
||||
selection = (*inPtr++ & 0x3f);
|
||||
outString += base64_table[selection];
|
||||
}
|
||||
else
|
||||
{
|
||||
outString += base64_table[remainder];
|
||||
outString += '=';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
outString += base64_table[remainder];
|
||||
outString += '=';
|
||||
outString += '=';
|
||||
}
|
||||
}
|
||||
return( outString );
|
||||
}
|
||||
|
||||
int split(const char* string, const char delim, std::vector<std::string>& items) {
|
||||
if(string == NULL)
|
||||
return -1;
|
||||
if(string == NULL)
|
||||
return -1;
|
||||
|
||||
if(string[0] == 0)
|
||||
return -2;
|
||||
if(string[0] == 0)
|
||||
return -2;
|
||||
|
||||
std::string str(string);
|
||||
size_t pos;
|
||||
std::string str(string);
|
||||
size_t pos;
|
||||
|
||||
while(true) {
|
||||
pos = str.find(delim);
|
||||
items.push_back(str.substr(0, pos));
|
||||
str.erase(0, pos+1);
|
||||
while(true) {
|
||||
pos = str.find(delim);
|
||||
items.push_back(str.substr(0, pos));
|
||||
str.erase(0, pos+1);
|
||||
|
||||
if(pos == std::string::npos)
|
||||
break;
|
||||
}
|
||||
if(pos == std::string::npos)
|
||||
break;
|
||||
}
|
||||
|
||||
return items.size();
|
||||
return items.size();
|
||||
}
|
||||
|
||||
int pairsplit(const char* string, const char delim, std::string& name, std::string& value) {
|
||||
if(string == NULL)
|
||||
return -1;
|
||||
if(string == NULL)
|
||||
return -1;
|
||||
|
||||
if(string[0] == 0)
|
||||
return -2;
|
||||
if(string[0] == 0)
|
||||
return -2;
|
||||
|
||||
std::string str(string);
|
||||
size_t pos = str.find(delim);
|
||||
std::string str(string);
|
||||
size_t pos = str.find(delim);
|
||||
|
||||
if(pos == std::string::npos || pos == 0 || pos >= str.length())
|
||||
return -3;
|
||||
if(pos == std::string::npos || pos == 0 || pos >= str.length())
|
||||
return -3;
|
||||
|
||||
name = str.substr(0, pos);
|
||||
value = str.substr(pos+1, std::string::npos);
|
||||
name = str.substr(0, pos);
|
||||
value = str.substr(pos+1, std::string::npos);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sets sse_version */
|
||||
void ssedetect() {
|
||||
#if (defined(__i386__) || defined(__x86_64__))
|
||||
/* x86 or x86-64 processor */
|
||||
uint32_t r_edx, r_ecx;
|
||||
/* x86 or x86-64 processor */
|
||||
uint32_t r_edx, r_ecx;
|
||||
|
||||
__asm__ __volatile__(
|
||||
__asm__ __volatile__(
|
||||
#if defined(__i386__)
|
||||
"pushl %%ebx;\n\t"
|
||||
"pushl %%ebx;\n\t"
|
||||
#endif
|
||||
"mov $0x1,%%eax\n\t"
|
||||
"cpuid\n\t"
|
||||
"mov $0x1,%%eax\n\t"
|
||||
"cpuid\n\t"
|
||||
#if defined(__i386__)
|
||||
"popl %%ebx;\n\t"
|
||||
"popl %%ebx;\n\t"
|
||||
#endif
|
||||
: "=d" (r_edx), "=c" (r_ecx)
|
||||
:
|
||||
: "%eax"
|
||||
: "=d" (r_edx), "=c" (r_ecx)
|
||||
:
|
||||
: "%eax"
|
||||
#if !defined(__i386__)
|
||||
, "%ebx"
|
||||
, "%ebx"
|
||||
#endif
|
||||
);
|
||||
);
|
||||
|
||||
if (r_ecx & 0x00000200) {
|
||||
sseversion = 35; /* SSSE3 */
|
||||
Debug(1,"Detected a x86\\x86-64 processor with SSSE3");
|
||||
} else if (r_ecx & 0x00000001) {
|
||||
sseversion = 30; /* SSE3 */
|
||||
Debug(1,"Detected a x86\\x86-64 processor with SSE3");
|
||||
} else if (r_edx & 0x04000000) {
|
||||
sseversion = 20; /* SSE2 */
|
||||
Debug(1,"Detected a x86\\x86-64 processor with SSE2");
|
||||
} else if (r_edx & 0x02000000) {
|
||||
sseversion = 10; /* SSE */
|
||||
Debug(1,"Detected a x86\\x86-64 processor with SSE");
|
||||
} else {
|
||||
sseversion = 0;
|
||||
Debug(1,"Detected a x86\\x86-64 processor");
|
||||
}
|
||||
if (r_ecx & 0x00000200) {
|
||||
sseversion = 35; /* SSSE3 */
|
||||
Debug(1,"Detected a x86\\x86-64 processor with SSSE3");
|
||||
} else if (r_ecx & 0x00000001) {
|
||||
sseversion = 30; /* SSE3 */
|
||||
Debug(1,"Detected a x86\\x86-64 processor with SSE3");
|
||||
} else if (r_edx & 0x04000000) {
|
||||
sseversion = 20; /* SSE2 */
|
||||
Debug(1,"Detected a x86\\x86-64 processor with SSE2");
|
||||
} else if (r_edx & 0x02000000) {
|
||||
sseversion = 10; /* SSE */
|
||||
Debug(1,"Detected a x86\\x86-64 processor with SSE");
|
||||
} else {
|
||||
sseversion = 0;
|
||||
Debug(1,"Detected a x86\\x86-64 processor");
|
||||
}
|
||||
|
||||
#else
|
||||
/* Non x86 or x86-64 processor, SSE2 is not available */
|
||||
Debug(1,"Detected a non x86\\x86-64 processor");
|
||||
sseversion = 0;
|
||||
/* Non x86 or x86-64 processor, SSE2 is not available */
|
||||
Debug(1,"Detected a non x86\\x86-64 processor");
|
||||
sseversion = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -288,60 +288,60 @@ __attribute__((noinline,__target__("sse2")))
|
|||
#endif
|
||||
void* sse2_aligned_memcpy(void* dest, const void* src, size_t bytes) {
|
||||
#if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE))
|
||||
if(bytes > 128) {
|
||||
unsigned int remainder = bytes % 128;
|
||||
const uint8_t* lastsrc = (uint8_t*)src + (bytes - remainder);
|
||||
if(bytes > 128) {
|
||||
unsigned int remainder = bytes % 128;
|
||||
const uint8_t* lastsrc = (uint8_t*)src + (bytes - remainder);
|
||||
|
||||
__asm__ __volatile__(
|
||||
"sse2_copy_iter:\n\t"
|
||||
"movdqa (%0),%%xmm0\n\t"
|
||||
"movdqa 0x10(%0),%%xmm1\n\t"
|
||||
"movdqa 0x20(%0),%%xmm2\n\t"
|
||||
"movdqa 0x30(%0),%%xmm3\n\t"
|
||||
"movdqa 0x40(%0),%%xmm4\n\t"
|
||||
"movdqa 0x50(%0),%%xmm5\n\t"
|
||||
"movdqa 0x60(%0),%%xmm6\n\t"
|
||||
"movdqa 0x70(%0),%%xmm7\n\t"
|
||||
"movntdq %%xmm0,(%1)\n\t"
|
||||
"movntdq %%xmm1,0x10(%1)\n\t"
|
||||
"movntdq %%xmm2,0x20(%1)\n\t"
|
||||
"movntdq %%xmm3,0x30(%1)\n\t"
|
||||
"movntdq %%xmm4,0x40(%1)\n\t"
|
||||
"movntdq %%xmm5,0x50(%1)\n\t"
|
||||
"movntdq %%xmm6,0x60(%1)\n\t"
|
||||
"movntdq %%xmm7,0x70(%1)\n\t"
|
||||
"add $0x80, %0\n\t"
|
||||
"add $0x80, %1\n\t"
|
||||
"cmp %2, %0\n\t"
|
||||
"jb sse2_copy_iter\n\t"
|
||||
"test %3, %3\n\t"
|
||||
"jz sse2_copy_finish\n\t"
|
||||
"cld\n\t"
|
||||
"rep movsb\n\t"
|
||||
"sse2_copy_finish:\n\t"
|
||||
:
|
||||
: "S" (src), "D" (dest), "r" (lastsrc), "c" (remainder)
|
||||
: "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory"
|
||||
);
|
||||
__asm__ __volatile__(
|
||||
"sse2_copy_iter:\n\t"
|
||||
"movdqa (%0),%%xmm0\n\t"
|
||||
"movdqa 0x10(%0),%%xmm1\n\t"
|
||||
"movdqa 0x20(%0),%%xmm2\n\t"
|
||||
"movdqa 0x30(%0),%%xmm3\n\t"
|
||||
"movdqa 0x40(%0),%%xmm4\n\t"
|
||||
"movdqa 0x50(%0),%%xmm5\n\t"
|
||||
"movdqa 0x60(%0),%%xmm6\n\t"
|
||||
"movdqa 0x70(%0),%%xmm7\n\t"
|
||||
"movntdq %%xmm0,(%1)\n\t"
|
||||
"movntdq %%xmm1,0x10(%1)\n\t"
|
||||
"movntdq %%xmm2,0x20(%1)\n\t"
|
||||
"movntdq %%xmm3,0x30(%1)\n\t"
|
||||
"movntdq %%xmm4,0x40(%1)\n\t"
|
||||
"movntdq %%xmm5,0x50(%1)\n\t"
|
||||
"movntdq %%xmm6,0x60(%1)\n\t"
|
||||
"movntdq %%xmm7,0x70(%1)\n\t"
|
||||
"add $0x80, %0\n\t"
|
||||
"add $0x80, %1\n\t"
|
||||
"cmp %2, %0\n\t"
|
||||
"jb sse2_copy_iter\n\t"
|
||||
"test %3, %3\n\t"
|
||||
"jz sse2_copy_finish\n\t"
|
||||
"cld\n\t"
|
||||
"rep movsb\n\t"
|
||||
"sse2_copy_finish:\n\t"
|
||||
:
|
||||
: "S" (src), "D" (dest), "r" (lastsrc), "c" (remainder)
|
||||
: "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory"
|
||||
);
|
||||
|
||||
} else {
|
||||
/* Standard memcpy */
|
||||
__asm__ __volatile__("cld; rep movsb" :: "S"(src), "D"(dest), "c"(bytes) : "cc", "memory");
|
||||
}
|
||||
} else {
|
||||
/* Standard memcpy */
|
||||
__asm__ __volatile__("cld; rep movsb" :: "S"(src), "D"(dest), "c"(bytes) : "cc", "memory");
|
||||
}
|
||||
#else
|
||||
/* Non x86\x86-64 platform, use memcpy */
|
||||
memcpy(dest,src,bytes);
|
||||
/* Non x86\x86-64 platform, use memcpy */
|
||||
memcpy(dest,src,bytes);
|
||||
#endif
|
||||
return dest;
|
||||
return dest;
|
||||
}
|
||||
|
||||
void timespec_diff(struct timespec *start, struct timespec *end, struct timespec *diff) {
|
||||
if (((end->tv_nsec)-(start->tv_nsec))<0) {
|
||||
diff->tv_sec = end->tv_sec-start->tv_sec-1;
|
||||
diff->tv_nsec = 1000000000+end->tv_nsec-start->tv_nsec;
|
||||
} else {
|
||||
diff->tv_sec = end->tv_sec-start->tv_sec;
|
||||
diff->tv_nsec = end->tv_nsec-start->tv_nsec;
|
||||
}
|
||||
if (((end->tv_nsec)-(start->tv_nsec))<0) {
|
||||
diff->tv_sec = end->tv_sec-start->tv_sec-1;
|
||||
diff->tv_nsec = 1000000000+end->tv_nsec-start->tv_nsec;
|
||||
} else {
|
||||
diff->tv_sec = end->tv_sec-start->tv_sec;
|
||||
diff->tv_nsec = end->tv_nsec-start->tv_nsec;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -46,12 +46,12 @@ int pairsplit(const char* string, const char delim, std::string& name, std::stri
|
|||
|
||||
inline int max( int a, int b )
|
||||
{
|
||||
return( a>=b?a:b );
|
||||
return( a>=b?a:b );
|
||||
}
|
||||
|
||||
inline int min( int a, int b )
|
||||
{
|
||||
return( a<=b?a:b );
|
||||
return( a<=b?a:b );
|
||||
}
|
||||
|
||||
void ssedetect();
|
||||
|
|
|
@ -29,277 +29,321 @@
|
|||
#include "zm_videostore.h"
|
||||
|
||||
extern "C"{
|
||||
#include "libavutil/time.h"
|
||||
#include "libavutil/time.h"
|
||||
}
|
||||
|
||||
VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
||||
AVStream *input_st, AVStream *inpaud_st,
|
||||
AVStream *input_st,
|
||||
AVStream *inpaud_st,
|
||||
int64_t nStartTime) {
|
||||
|
||||
AVDictionary *pmetadata = NULL;
|
||||
int dsr;
|
||||
AVDictionary *pmetadata = NULL;
|
||||
int dsr;
|
||||
|
||||
//store inputs in variables local to class
|
||||
filename = filename_in;
|
||||
format = format_in;
|
||||
//store inputs in variables local to class
|
||||
filename = filename_in;
|
||||
format = format_in;
|
||||
|
||||
keyframeMessage = false;
|
||||
keyframeSkipNumber = 0;
|
||||
keyframeMessage = false;
|
||||
keyframeSkipNumber = 0;
|
||||
|
||||
Info("Opening video storage stream %s format: %d\n", filename, format);
|
||||
|
||||
Info("Opening video storage stream %s\n", filename);
|
||||
//Init everything we need
|
||||
int ret;
|
||||
av_register_all();
|
||||
|
||||
//Init everything we need
|
||||
int ret;
|
||||
av_register_all();
|
||||
ret = avformat_alloc_output_context2(&oc, NULL, NULL, filename);
|
||||
if ( ret < 0 ) {
|
||||
Warning("Could not create video storage stream %s as no output context"
|
||||
" could be assigned based on filename: %s",
|
||||
filename,
|
||||
av_make_error_string(ret).c_str()
|
||||
);
|
||||
}
|
||||
|
||||
avformat_alloc_output_context2(&oc, NULL, NULL, filename);
|
||||
|
||||
//Couldn't deduce format from filename, trying from format name
|
||||
//Couldn't deduce format from filename, trying from format name
|
||||
if (!oc) {
|
||||
avformat_alloc_output_context2(&oc, NULL, format, filename);
|
||||
if (!oc) {
|
||||
avformat_alloc_output_context2(&oc, NULL, format, filename);
|
||||
if (!oc) {
|
||||
Fatal("Could not create video storage stream %s as no output context"
|
||||
" could not be assigned based on filename or format %s",
|
||||
filename, format);
|
||||
}
|
||||
Fatal("Could not create video storage stream %s as no output context"
|
||||
" could not be assigned based on filename or format %s",
|
||||
filename, format);
|
||||
}
|
||||
}
|
||||
|
||||
dsr = av_dict_set(&pmetadata, "title", "Zoneminder Security Recording", 0);
|
||||
if (dsr < 0) Warning("%s:%d: title set failed", __FILE__, __LINE__ );
|
||||
dsr = av_dict_set(&pmetadata, "title", "Zoneminder Security Recording", 0);
|
||||
if (dsr < 0) Warning("%s:%d: title set failed", __FILE__, __LINE__ );
|
||||
|
||||
oc->metadata = pmetadata;
|
||||
oc->metadata = pmetadata;
|
||||
|
||||
fmt = oc->oformat;
|
||||
fmt = oc->oformat;
|
||||
|
||||
video_st = avformat_new_stream(oc, input_st->codec->codec);
|
||||
if (!video_st) {
|
||||
Fatal("Unable to create video out stream\n");
|
||||
}
|
||||
video_st = avformat_new_stream(oc, (AVCodec *)input_st->codec->codec);
|
||||
if (!video_st) {
|
||||
Fatal("Unable to create video out stream\n");
|
||||
}
|
||||
|
||||
ret = avcodec_copy_context(video_st->codec, input_st->codec);
|
||||
if (ret < 0) {
|
||||
Fatal("Unable to copy input video context to output video context "
|
||||
"%s\n", av_make_error_string(ret).c_str());
|
||||
}
|
||||
ret = avcodec_copy_context(video_st->codec, input_st->codec);
|
||||
if (ret < 0) {
|
||||
Fatal("Unable to copy input video context to output video context %s\n",
|
||||
av_make_error_string(ret).c_str());
|
||||
}
|
||||
|
||||
video_st->codec->codec_tag = 0;
|
||||
if (oc->oformat->flags & AVFMT_GLOBALHEADER) {
|
||||
video_st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
|
||||
}
|
||||
video_st->codec->codec_tag = 0;
|
||||
if (oc->oformat->flags & AVFMT_GLOBALHEADER) {
|
||||
video_st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
|
||||
}
|
||||
|
||||
if (inpaud_st) {
|
||||
audio_st = avformat_new_stream(oc, inpaud_st->codec->codec);
|
||||
if (!audio_st) {
|
||||
Fatal("Unable to create audio out stream\n");
|
||||
}
|
||||
ret=avcodec_copy_context(audio_st->codec, inpaud_st->codec);
|
||||
if (ret < 0) {
|
||||
Fatal("Unable to copy audio context %s\n", av_make_error_string(ret).c_str());
|
||||
}
|
||||
audio_st->codec->codec_tag = 0;
|
||||
if (oc->oformat->flags & AVFMT_GLOBALHEADER) {
|
||||
audio_st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
|
||||
}
|
||||
if (inpaud_st) {
|
||||
|
||||
audio_st = avformat_new_stream(oc, (AVCodec *)inpaud_st->codec->codec);
|
||||
if (!audio_st) {
|
||||
Error("Unable to create audio out stream\n");
|
||||
audio_st = NULL;
|
||||
} else {
|
||||
audio_st = NULL;
|
||||
ret = avcodec_copy_context(audio_st->codec, inpaud_st->codec);
|
||||
if (ret < 0) {
|
||||
Fatal("Unable to copy audio context %s\n", av_make_error_string(ret).c_str());
|
||||
}
|
||||
audio_st->codec->codec_tag = 0;
|
||||
if ( audio_st->codec->channels > 1 ) {
|
||||
Warning("Audio isn't mono, changing it.");
|
||||
audio_st->codec->channels = 1;
|
||||
}
|
||||
if (oc->oformat->flags & AVFMT_GLOBALHEADER) {
|
||||
audio_st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Debug(3, "No Audio output stream");
|
||||
audio_st = NULL;
|
||||
}
|
||||
|
||||
/* open the output file, if needed */
|
||||
if (!(fmt->flags & AVFMT_NOFILE)) {
|
||||
ret = avio_open2(&oc->pb, filename, AVIO_FLAG_WRITE,NULL,NULL);
|
||||
if (ret < 0) {
|
||||
Fatal("Could not open output file '%s': %s\n", filename,
|
||||
av_make_error_string(ret).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Write the stream header, if any. */
|
||||
ret = avformat_write_header(oc, NULL);
|
||||
/* open the output file, if needed */
|
||||
if (!(fmt->flags & AVFMT_NOFILE)) {
|
||||
ret = avio_open2(&oc->pb, filename, AVIO_FLAG_WRITE,NULL,NULL);
|
||||
if (ret < 0) {
|
||||
Fatal("Error occurred when writing output file header: %s\n",
|
||||
av_make_error_string(ret).c_str());
|
||||
Fatal("Could not open output file '%s': %s\n", filename,
|
||||
av_make_error_string(ret).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
prevDts = 0;
|
||||
startPts = 0;
|
||||
startDts = 0;
|
||||
filter_in_rescale_delta_last = AV_NOPTS_VALUE;
|
||||
//av_dict_set(&opts, "movflags", "frag_custom+dash+delay_moov", 0);
|
||||
//if ((ret = avformat_write_header(ctx, &opts)) < 0) {
|
||||
//}
|
||||
//os->ctx_inited = 1;
|
||||
//avio_flush(ctx->pb);
|
||||
//av_dict_free(&opts);
|
||||
|
||||
startTime=av_gettime()-nStartTime;//oc->start_time;
|
||||
Info("VideoStore startTime=%d\n",startTime);
|
||||
}
|
||||
/* Write the stream header, if any. */
|
||||
ret = avformat_write_header(oc, NULL);
|
||||
if (ret < 0) {
|
||||
zm_dump_stream_format( oc, 0, 0, 1 );
|
||||
Fatal("Error occurred when writing output file header to %s: %s\n",
|
||||
filename,
|
||||
av_make_error_string(ret).c_str());
|
||||
}
|
||||
|
||||
prevDts = 0;
|
||||
startPts = 0;
|
||||
startDts = 0;
|
||||
filter_in_rescale_delta_last = AV_NOPTS_VALUE;
|
||||
|
||||
startTime=av_gettime()-nStartTime;//oc->start_time;
|
||||
Info("VideoStore startTime=%d\n",startTime);
|
||||
} // VideoStore::VideoStore
|
||||
|
||||
|
||||
VideoStore::~VideoStore(){
|
||||
/* Write the trailer before close */
|
||||
if ( int rc = av_write_trailer(oc) ) {
|
||||
Error("Error writing trailer %s", av_err2str( rc ) );
|
||||
} else {
|
||||
Debug(3, "Sucess Writing trailer");
|
||||
}
|
||||
/* Write the trailer before close */
|
||||
if ( int rc = av_write_trailer(oc) ) {
|
||||
Error("Error writing trailer %s", av_err2str( rc ) );
|
||||
} else {
|
||||
Debug(3, "Sucess Writing trailer");
|
||||
}
|
||||
|
||||
// I wonder if we should be closing the file first.
|
||||
// I also wonder if we really need to be doing all the context allocation/de-allocation constantly, or whether we can just re-use it. Just do a file open/close/writeheader/etc.
|
||||
// What if we were only doing audio recording?
|
||||
if ( video_st ) {
|
||||
avcodec_close(video_st->codec);
|
||||
if (audio_st) {
|
||||
avcodec_close(audio_st->codec);
|
||||
}
|
||||
}
|
||||
if (audio_st) {
|
||||
avcodec_close(audio_st->codec);
|
||||
}
|
||||
|
||||
if (!(fmt->flags & AVFMT_NOFILE)) {
|
||||
// WHen will be not using a file ?
|
||||
if (!(fmt->flags & AVFMT_NOFILE)) {
|
||||
/* Close the output file. */
|
||||
if ( int rc= avio_close(oc->pb) ) {
|
||||
Error("Error closing avio %s", av_err2str( rc ) );
|
||||
}
|
||||
if ( int rc = avio_close(oc->pb) ) {
|
||||
Error("Error closing avio %s", av_err2str( rc ) );
|
||||
}
|
||||
} else {
|
||||
Debug(3, "Not closing avio because we are not writing to a file.");
|
||||
}
|
||||
|
||||
/* free the stream */
|
||||
avformat_free_context(oc);
|
||||
/* free the stream */
|
||||
avformat_free_context(oc);
|
||||
}
|
||||
|
||||
|
||||
void VideoStore::dumpPacket( AVPacket *pkt ){
|
||||
char b[10240];
|
||||
char b[10240];
|
||||
|
||||
snprintf(b, sizeof(b), " pts: %" PRId64 ", dts: %" PRId64 ", data: %p, size: %d, sindex: %d, dflags: %04x, s-pos: %" PRId64 ", c-duration: %" PRId64 "\n"
|
||||
, pkt->pts
|
||||
, pkt->dts
|
||||
, pkt->data
|
||||
, pkt->size
|
||||
, pkt->stream_index
|
||||
, pkt->flags
|
||||
, pkt->pos
|
||||
, pkt->convergence_duration
|
||||
);
|
||||
Info("%s:%d:DEBUG: %s", __FILE__, __LINE__, b);
|
||||
snprintf(b, sizeof(b), " pts: %" PRId64 ", dts: %" PRId64 ", data: %p, size: %d, sindex: %d, dflags: %04x, s-pos: %" PRId64 ", c-duration: %" PRId64 "\n"
|
||||
, pkt->pts
|
||||
, pkt->dts
|
||||
, pkt->data
|
||||
, pkt->size
|
||||
, pkt->stream_index
|
||||
, pkt->flags
|
||||
, pkt->pos
|
||||
, pkt->convergence_duration
|
||||
);
|
||||
Info("%s:%d:DEBUG: %s", __FILE__, __LINE__, b);
|
||||
}
|
||||
|
||||
int VideoStore::writeVideoFramePacket(AVPacket *ipkt, AVStream *input_st){//, AVPacket *lastKeyframePkt){
|
||||
|
||||
int64_t ost_tb_start_time = av_rescale_q(startTime, AV_TIME_BASE_Q, video_st->time_base);
|
||||
int64_t ost_tb_start_time = av_rescale_q(startTime, AV_TIME_BASE_Q, video_st->time_base);
|
||||
|
||||
AVPacket opkt, safepkt;
|
||||
AVPicture pict;
|
||||
AVPacket opkt, safepkt;
|
||||
AVPicture pict;
|
||||
|
||||
av_init_packet(&opkt);
|
||||
av_init_packet(&opkt);
|
||||
|
||||
//Scale the PTS of the outgoing packet to be the correct time base
|
||||
if (ipkt->pts != AV_NOPTS_VALUE) {
|
||||
opkt.pts = av_rescale_q(ipkt->pts-startPts, input_st->time_base, video_st->time_base) - ost_tb_start_time;
|
||||
} else {
|
||||
opkt.pts = AV_NOPTS_VALUE;
|
||||
}
|
||||
//Scale the PTS of the outgoing packet to be the correct time base
|
||||
if (ipkt->pts != AV_NOPTS_VALUE) {
|
||||
opkt.pts = av_rescale_q(ipkt->pts-startPts, input_st->time_base, video_st->time_base) - ost_tb_start_time;
|
||||
} else {
|
||||
opkt.pts = AV_NOPTS_VALUE;
|
||||
}
|
||||
|
||||
//Scale the DTS of the outgoing packet to be the correct time base
|
||||
if(ipkt->dts == AV_NOPTS_VALUE) {
|
||||
opkt.dts = av_rescale_q(input_st->cur_dts-startDts, AV_TIME_BASE_Q, video_st->time_base);
|
||||
} else {
|
||||
opkt.dts = av_rescale_q(ipkt->dts-startDts, input_st->time_base, video_st->time_base);
|
||||
}
|
||||
//Scale the DTS of the outgoing packet to be the correct time base
|
||||
if(ipkt->dts == AV_NOPTS_VALUE) {
|
||||
opkt.dts = av_rescale_q(input_st->cur_dts-startDts, AV_TIME_BASE_Q, video_st->time_base);
|
||||
} else {
|
||||
opkt.dts = av_rescale_q(ipkt->dts-startDts, input_st->time_base, video_st->time_base);
|
||||
}
|
||||
|
||||
opkt.dts -= ost_tb_start_time;
|
||||
opkt.dts -= ost_tb_start_time;
|
||||
|
||||
opkt.duration = av_rescale_q(ipkt->duration, input_st->time_base, video_st->time_base);
|
||||
opkt.flags = ipkt->flags;
|
||||
opkt.pos=-1;
|
||||
opkt.duration = av_rescale_q(ipkt->duration, input_st->time_base, video_st->time_base);
|
||||
opkt.flags = ipkt->flags;
|
||||
opkt.pos=-1;
|
||||
|
||||
opkt.data = ipkt->data;
|
||||
opkt.size = ipkt->size;
|
||||
opkt.stream_index = ipkt->stream_index;
|
||||
/*opkt.flags |= AV_PKT_FLAG_KEY;*/
|
||||
opkt.data = ipkt->data;
|
||||
opkt.size = ipkt->size;
|
||||
opkt.stream_index = ipkt->stream_index;
|
||||
/*opkt.flags |= AV_PKT_FLAG_KEY;*/
|
||||
|
||||
if (video_st->codec->codec_type == AVMEDIA_TYPE_VIDEO && (fmt->flags & AVFMT_RAWPICTURE)) {
|
||||
/* store AVPicture in AVPacket, as expected by the output format */
|
||||
avpicture_fill(&pict, opkt.data, video_st->codec->pix_fmt, video_st->codec->width, video_st->codec->height);
|
||||
opkt.data = (uint8_t *)&pict;
|
||||
opkt.size = sizeof(AVPicture);
|
||||
opkt.flags |= AV_PKT_FLAG_KEY;
|
||||
if (video_st->codec->codec_type == AVMEDIA_TYPE_VIDEO && (fmt->flags & AVFMT_RAWPICTURE)) {
|
||||
/* store AVPicture in AVPacket, as expected by the output format */
|
||||
avpicture_fill(&pict, opkt.data, video_st->codec->pix_fmt, video_st->codec->width, video_st->codec->height);
|
||||
opkt.data = (uint8_t *)&pict;
|
||||
opkt.size = sizeof(AVPicture);
|
||||
opkt.flags |= AV_PKT_FLAG_KEY;
|
||||
}
|
||||
|
||||
memcpy(&safepkt, &opkt, sizeof(AVPacket));
|
||||
|
||||
if ((opkt.data == NULL)||(opkt.size < 1)) {
|
||||
Warning("%s:%d: Mangled AVPacket: discarding frame", __FILE__, __LINE__ );
|
||||
dumpPacket(&opkt);
|
||||
|
||||
} else if ((prevDts > 0) && (prevDts >= opkt.dts)) {
|
||||
Warning("%s:%d: DTS out of order: %lld \u226E %lld; discarding frame", __FILE__, __LINE__, prevDts, opkt.dts);
|
||||
prevDts = opkt.dts;
|
||||
dumpPacket(&opkt);
|
||||
|
||||
} else {
|
||||
int ret;
|
||||
|
||||
prevDts = opkt.dts; // Unsure if av_interleaved_write_frame() clobbers opkt.dts when out of order, so storing in advance
|
||||
ret = av_interleaved_write_frame(oc, &opkt);
|
||||
if(ret<0){
|
||||
// There's nothing we can really do if the frame is rejected, just drop it and get on with the next
|
||||
Warning("%s:%d: Writing frame [av_interleaved_write_frame()] failed: %s(%d) ", __FILE__, __LINE__, av_make_error_string(ret).c_str(), (ret));
|
||||
dumpPacket(&safepkt);
|
||||
}
|
||||
|
||||
memcpy(&safepkt, &opkt, sizeof(AVPacket));
|
||||
|
||||
if ((opkt.data == NULL)||(opkt.size < 1)) {
|
||||
Warning("%s:%d: Mangled AVPacket: discarding frame", __FILE__, __LINE__ );
|
||||
dumpPacket(&opkt);
|
||||
|
||||
} else if ((prevDts > 0) && (prevDts >= opkt.dts)) {
|
||||
Warning("%s:%d: DTS out of order: %lld \u226E %lld; discarding frame", __FILE__, __LINE__, prevDts, opkt.dts);
|
||||
prevDts = opkt.dts;
|
||||
dumpPacket(&opkt);
|
||||
|
||||
} else {
|
||||
int ret;
|
||||
|
||||
prevDts = opkt.dts; // Unsure if av_interleaved_write_frame() clobbers opkt.dts when out of order, so storing in advance
|
||||
ret = av_interleaved_write_frame(oc, &opkt);
|
||||
if(ret<0){
|
||||
// There's nothing we can really do if the frame is rejected, just drop it and get on with the next
|
||||
Warning("%s:%d: Writing frame [av_interleaved_write_frame()] failed: %s(%d) ", __FILE__, __LINE__, av_make_error_string(ret).c_str(), (ret));
|
||||
dumpPacket(&safepkt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
av_free_packet(&opkt);
|
||||
av_free_packet(&opkt);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int VideoStore::writeAudioFramePacket(AVPacket *ipkt, AVStream *input_st){
|
||||
|
||||
if(!audio_st)
|
||||
return -1;//FIXME -ve return codes do not free packet in ffmpeg_camera at the moment
|
||||
/*if(!keyframeMessage)
|
||||
return -1;*/
|
||||
if(!audio_st) {
|
||||
Error("Called writeAudioFramePacket when no audio_st");
|
||||
return -1;//FIXME -ve return codes do not free packet in ffmpeg_camera at the moment
|
||||
}
|
||||
/*if(!keyframeMessage)
|
||||
return -1;*/
|
||||
//zm_dump_stream_format( oc, ipkt->stream_index, 0, 1 );
|
||||
|
||||
int64_t ost_tb_start_time = av_rescale_q(startTime, AV_TIME_BASE_Q, video_st->time_base);
|
||||
// What is this doing? Getting the time of the start of this video chunk? Does that actually make sense?
|
||||
int64_t ost_tb_start_time = av_rescale_q(startTime, AV_TIME_BASE_Q, audio_st->time_base);
|
||||
|
||||
AVPacket opkt;
|
||||
AVPacket opkt;
|
||||
|
||||
av_init_packet(&opkt);
|
||||
av_init_packet(&opkt);
|
||||
Debug(3, "after init packet" );
|
||||
|
||||
|
||||
//Scale the PTS of the outgoing packet to be the correct time base
|
||||
if (ipkt->pts != AV_NOPTS_VALUE)
|
||||
opkt.pts = av_rescale_q(ipkt->pts-startPts, input_st->time_base, audio_st->time_base) - ost_tb_start_time;
|
||||
else
|
||||
opkt.pts = AV_NOPTS_VALUE;
|
||||
//Scale the PTS of the outgoing packet to be the correct time base
|
||||
if (ipkt->pts != AV_NOPTS_VALUE) {
|
||||
Debug(3, "Rescaling output pts");
|
||||
opkt.pts = av_rescale_q(ipkt->pts-startPts, input_st->time_base, audio_st->time_base) - ost_tb_start_time;
|
||||
} else {
|
||||
Debug(3, "Setting output pts to AV_NOPTS_VALUE");
|
||||
opkt.pts = AV_NOPTS_VALUE;
|
||||
}
|
||||
|
||||
//Scale the DTS of the outgoing packet to be the correct time base
|
||||
if(ipkt->dts == AV_NOPTS_VALUE) {
|
||||
opkt.dts = av_rescale_q(input_st->cur_dts-startDts, AV_TIME_BASE_Q, audio_st->time_base);
|
||||
Debug(3, "ipkt->dts == AV_NOPTS_VALUE %d to %d", AV_NOPTS_VALUE, opkt.dts );
|
||||
} else
|
||||
opkt.dts = av_rescale_q(ipkt->dts-startDts, input_st->time_base, audio_st->time_base);
|
||||
opkt.dts -= ost_tb_start_time;
|
||||
//Scale the DTS of the outgoing packet to be the correct time base
|
||||
if(ipkt->dts == AV_NOPTS_VALUE) {
|
||||
Debug(4, "ipkt->dts == AV_NOPTS_VALUE %d to %d", AV_NOPTS_VALUE, opkt.dts );
|
||||
opkt.dts = av_rescale_q(input_st->cur_dts-startDts, AV_TIME_BASE_Q, audio_st->time_base);
|
||||
Debug(4, "ipkt->dts == AV_NOPTS_VALUE %d to %d", AV_NOPTS_VALUE, opkt.dts );
|
||||
} else {
|
||||
Debug(4, "ipkt->dts != AV_NOPTS_VALUE %d to %d", AV_NOPTS_VALUE, opkt.dts );
|
||||
opkt.dts = av_rescale_q(ipkt->dts-startDts, input_st->time_base, audio_st->time_base);
|
||||
Debug(4, "ipkt->dts != AV_NOPTS_VALUE %d to %d", AV_NOPTS_VALUE, opkt.dts );
|
||||
}
|
||||
opkt.dts -= ost_tb_start_time;
|
||||
|
||||
if (audio_st->codec->codec_type == AVMEDIA_TYPE_AUDIO && ipkt->dts != AV_NOPTS_VALUE) {
|
||||
Debug( 3, "code is audio, dts != AV_NOPTS_VALUE " );
|
||||
int duration = av_get_audio_frame_duration(input_st->codec, ipkt->size);
|
||||
if(!duration)
|
||||
duration = input_st->codec->frame_size;
|
||||
// Seems like it would be really weird for the codec type to NOT be audiu
|
||||
if (audio_st->codec->codec_type == AVMEDIA_TYPE_AUDIO && ipkt->dts != AV_NOPTS_VALUE) {
|
||||
Debug( 4, "code is audio, dts != AV_NOPTS_VALUE " );
|
||||
int duration = av_get_audio_frame_duration(input_st->codec, ipkt->size);
|
||||
if(!duration)
|
||||
duration = input_st->codec->frame_size;
|
||||
|
||||
//FIXME where to get filter_in_rescale_delta_last
|
||||
//FIXME av_rescale_delta doesn't exist in ubuntu vivid libavtools
|
||||
opkt.dts = opkt.pts = av_rescale_delta(input_st->time_base, ipkt->dts,
|
||||
(AVRational){1, input_st->codec->sample_rate}, duration, &filter_in_rescale_delta_last,
|
||||
audio_st->time_base) - ost_tb_start_time;
|
||||
}
|
||||
//FIXME where to get filter_in_rescale_delta_last
|
||||
//FIXME av_rescale_delta doesn't exist in ubuntu vivid libavtools
|
||||
opkt.dts = opkt.pts = av_rescale_delta(input_st->time_base, ipkt->dts,
|
||||
(AVRational){1, input_st->codec->sample_rate}, duration, &filter_in_rescale_delta_last,
|
||||
audio_st->time_base) - ost_tb_start_time;
|
||||
}
|
||||
|
||||
opkt.duration = av_rescale_q(ipkt->duration, input_st->time_base, audio_st->time_base);
|
||||
opkt.pos=-1;
|
||||
opkt.flags = ipkt->flags;
|
||||
opkt.duration = av_rescale_q(ipkt->duration, input_st->time_base, audio_st->time_base);
|
||||
opkt.pos=-1;
|
||||
opkt.flags = ipkt->flags;
|
||||
|
||||
opkt.data = ipkt->data;
|
||||
opkt.size = ipkt->size;
|
||||
opkt.stream_index = ipkt->stream_index;
|
||||
opkt.data = ipkt->data;
|
||||
opkt.size = ipkt->size;
|
||||
opkt.stream_index = ipkt->stream_index;
|
||||
|
||||
int ret;
|
||||
ret = av_interleaved_write_frame(oc, &opkt);
|
||||
if(ret!=0){
|
||||
Fatal("Error encoding audio frame packet: %s\n", av_make_error_string(ret).c_str());
|
||||
}
|
||||
|
||||
av_free_packet(&opkt);
|
||||
return 0;
|
||||
int ret;
|
||||
ret = av_interleaved_write_frame(oc, &opkt);
|
||||
if(ret!=0){
|
||||
Fatal("Error encoding audio frame packet: %s\n", av_make_error_string(ret).c_str());
|
||||
}
|
||||
Debug(4,"Success writing audio frame" );
|
||||
av_free_packet(&opkt);
|
||||
return 0;
|
||||
}
|
||||
|
|
226
src/zm_zone.h
226
src/zm_zone.h
|
@ -35,141 +35,141 @@ class Monitor;
|
|||
class Zone
|
||||
{
|
||||
protected:
|
||||
struct Range
|
||||
{
|
||||
int lo_x;
|
||||
int hi_x;
|
||||
int off_x;
|
||||
};
|
||||
struct Range
|
||||
{
|
||||
int lo_x;
|
||||
int hi_x;
|
||||
int off_x;
|
||||
};
|
||||
|
||||
public:
|
||||
typedef enum { ACTIVE=1, INCLUSIVE, EXCLUSIVE, PRECLUSIVE, INACTIVE, PRIVACY } ZoneType;
|
||||
typedef enum { ALARMED_PIXELS=1, FILTERED_PIXELS, BLOBS } CheckMethod;
|
||||
typedef enum { ACTIVE=1, INCLUSIVE, EXCLUSIVE, PRECLUSIVE, INACTIVE, PRIVACY } ZoneType;
|
||||
typedef enum { ALARMED_PIXELS=1, FILTERED_PIXELS, BLOBS } CheckMethod;
|
||||
|
||||
protected:
|
||||
// Inputs
|
||||
Monitor *monitor;
|
||||
// Inputs
|
||||
Monitor *monitor;
|
||||
|
||||
int id;
|
||||
char *label;
|
||||
ZoneType type;
|
||||
Polygon polygon;
|
||||
Rgb alarm_rgb;
|
||||
CheckMethod check_method;
|
||||
int id;
|
||||
char *label;
|
||||
ZoneType type;
|
||||
Polygon polygon;
|
||||
Rgb alarm_rgb;
|
||||
CheckMethod check_method;
|
||||
|
||||
int min_pixel_threshold;
|
||||
int max_pixel_threshold;
|
||||
int min_pixel_threshold;
|
||||
int max_pixel_threshold;
|
||||
|
||||
int min_alarm_pixels;
|
||||
int max_alarm_pixels;
|
||||
int min_alarm_pixels;
|
||||
int max_alarm_pixels;
|
||||
|
||||
Coord filter_box;
|
||||
int min_filter_pixels;
|
||||
int max_filter_pixels;
|
||||
Coord filter_box;
|
||||
int min_filter_pixels;
|
||||
int max_filter_pixels;
|
||||
|
||||
int min_blob_pixels;
|
||||
int max_blob_pixels;
|
||||
int min_blobs;
|
||||
int max_blobs;
|
||||
int min_blob_pixels;
|
||||
int max_blob_pixels;
|
||||
int min_blobs;
|
||||
int max_blobs;
|
||||
|
||||
int overload_frames;
|
||||
int extend_alarm_frames;
|
||||
int overload_frames;
|
||||
int extend_alarm_frames;
|
||||
|
||||
// Outputs/Statistics
|
||||
bool alarmed;
|
||||
int pixel_diff;
|
||||
unsigned int alarm_pixels;
|
||||
int alarm_filter_pixels;
|
||||
int alarm_blob_pixels;
|
||||
int alarm_blobs;
|
||||
int min_blob_size;
|
||||
int max_blob_size;
|
||||
Box alarm_box;
|
||||
Coord alarm_centre;
|
||||
unsigned int score;
|
||||
Image *pg_image;
|
||||
Range *ranges;
|
||||
Image *image;
|
||||
// Outputs/Statistics
|
||||
bool alarmed;
|
||||
int pixel_diff;
|
||||
unsigned int alarm_pixels;
|
||||
int alarm_filter_pixels;
|
||||
int alarm_blob_pixels;
|
||||
int alarm_blobs;
|
||||
int min_blob_size;
|
||||
int max_blob_size;
|
||||
Box alarm_box;
|
||||
Coord alarm_centre;
|
||||
unsigned int score;
|
||||
Image *pg_image;
|
||||
Range *ranges;
|
||||
Image *image;
|
||||
|
||||
int overload_count;
|
||||
int extend_alarm_count;
|
||||
int overload_count;
|
||||
int extend_alarm_count;
|
||||
|
||||
protected:
|
||||
void Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold, int p_max_pixel_threshold, int p_min_alarm_pixels, int p_max_alarm_pixels, const Coord &p_filter_box, int p_min_filter_pixels, int p_max_filter_pixels, int p_min_blob_pixels, int p_max_blob_pixels, int p_min_blobs, int p_max_blobs, int p_overload_frames, int p_extend_alarm_frames );
|
||||
void std_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsigned int* pixel_count, unsigned int* pixel_sum);
|
||||
void Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold, int p_max_pixel_threshold, int p_min_alarm_pixels, int p_max_alarm_pixels, const Coord &p_filter_box, int p_min_filter_pixels, int p_max_filter_pixels, int p_min_blob_pixels, int p_max_blob_pixels, int p_min_blobs, int p_max_blobs, int p_overload_frames, int p_extend_alarm_frames );
|
||||
void std_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsigned int* pixel_count, unsigned int* pixel_sum);
|
||||
|
||||
public:
|
||||
Zone( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold=15, int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, const Coord &p_filter_box=Coord( 3, 3 ), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, int p_max_blob_pixels=0, int p_min_blobs=0, int p_max_blobs=0, int p_overload_frames=0, int p_extend_alarm_frames=0 )
|
||||
{
|
||||
Setup( p_monitor, p_id, p_label, p_type, p_polygon, p_alarm_rgb, p_check_method, p_min_pixel_threshold, p_max_pixel_threshold, p_min_alarm_pixels, p_max_alarm_pixels, p_filter_box, p_min_filter_pixels, p_max_filter_pixels, p_min_blob_pixels, p_max_blob_pixels, p_min_blobs, p_max_blobs, p_overload_frames, p_extend_alarm_frames );
|
||||
}
|
||||
Zone( Monitor *p_monitor, int p_id, const char *p_label, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold=15, int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, const Coord &p_filter_box=Coord( 3, 3 ), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, int p_max_blob_pixels=0, int p_min_blobs=0, int p_max_blobs=0, int p_overload_frames=0, int p_extend_alarm_frames=0)
|
||||
{
|
||||
Setup( p_monitor, p_id, p_label, Zone::ACTIVE, p_polygon, p_alarm_rgb, p_check_method, p_min_pixel_threshold, p_max_pixel_threshold, p_min_alarm_pixels, p_max_alarm_pixels, p_filter_box, p_min_filter_pixels, p_max_filter_pixels, p_min_blob_pixels, p_max_blob_pixels, p_min_blobs, p_max_blobs, p_overload_frames, p_extend_alarm_frames );
|
||||
}
|
||||
Zone( Monitor *p_monitor, int p_id, const char *p_label, const Polygon &p_polygon )
|
||||
{
|
||||
Setup( p_monitor, p_id, p_label, Zone::INACTIVE, p_polygon, RGB_BLACK, (Zone::CheckMethod)0, 0, 0, 0, 0, Coord( 0, 0 ), 0, 0, 0, 0, 0, 0, 0, 0 );
|
||||
}
|
||||
Zone( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon )
|
||||
{
|
||||
Setup( p_monitor, p_id, p_label, p_type, p_polygon, RGB_BLACK, (Zone::CheckMethod)0, 0, 0, 0, 0, Coord( 0, 0 ), 0, 0, 0, 0, 0, 0, 0, 0 );
|
||||
}
|
||||
Zone( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold=15, int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, const Coord &p_filter_box=Coord( 3, 3 ), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, int p_max_blob_pixels=0, int p_min_blobs=0, int p_max_blobs=0, int p_overload_frames=0, int p_extend_alarm_frames=0 )
|
||||
{
|
||||
Setup( p_monitor, p_id, p_label, p_type, p_polygon, p_alarm_rgb, p_check_method, p_min_pixel_threshold, p_max_pixel_threshold, p_min_alarm_pixels, p_max_alarm_pixels, p_filter_box, p_min_filter_pixels, p_max_filter_pixels, p_min_blob_pixels, p_max_blob_pixels, p_min_blobs, p_max_blobs, p_overload_frames, p_extend_alarm_frames );
|
||||
}
|
||||
Zone( Monitor *p_monitor, int p_id, const char *p_label, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold=15, int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, const Coord &p_filter_box=Coord( 3, 3 ), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, int p_max_blob_pixels=0, int p_min_blobs=0, int p_max_blobs=0, int p_overload_frames=0, int p_extend_alarm_frames=0)
|
||||
{
|
||||
Setup( p_monitor, p_id, p_label, Zone::ACTIVE, p_polygon, p_alarm_rgb, p_check_method, p_min_pixel_threshold, p_max_pixel_threshold, p_min_alarm_pixels, p_max_alarm_pixels, p_filter_box, p_min_filter_pixels, p_max_filter_pixels, p_min_blob_pixels, p_max_blob_pixels, p_min_blobs, p_max_blobs, p_overload_frames, p_extend_alarm_frames );
|
||||
}
|
||||
Zone( Monitor *p_monitor, int p_id, const char *p_label, const Polygon &p_polygon )
|
||||
{
|
||||
Setup( p_monitor, p_id, p_label, Zone::INACTIVE, p_polygon, RGB_BLACK, (Zone::CheckMethod)0, 0, 0, 0, 0, Coord( 0, 0 ), 0, 0, 0, 0, 0, 0, 0, 0 );
|
||||
}
|
||||
Zone( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon )
|
||||
{
|
||||
Setup( p_monitor, p_id, p_label, p_type, p_polygon, RGB_BLACK, (Zone::CheckMethod)0, 0, 0, 0, 0, Coord( 0, 0 ), 0, 0, 0, 0, 0, 0, 0, 0 );
|
||||
}
|
||||
|
||||
public:
|
||||
~Zone();
|
||||
~Zone();
|
||||
|
||||
inline int Id() const { return( id ); }
|
||||
inline const char *Label() const { return( label ); }
|
||||
inline ZoneType Type() const { return( type ); }
|
||||
inline bool IsActive() const { return( type == ACTIVE ); }
|
||||
inline bool IsInclusive() const { return( type == INCLUSIVE ); }
|
||||
inline bool IsExclusive() const { return( type == EXCLUSIVE ); }
|
||||
inline bool IsPreclusive() const { return( type == PRECLUSIVE ); }
|
||||
inline bool IsInactive() const { return( type == INACTIVE ); }
|
||||
inline bool IsPrivacy() const { return( type == PRIVACY ); }
|
||||
inline const Image *AlarmImage() const { return( image ); }
|
||||
inline const Polygon &GetPolygon() const { return( polygon ); }
|
||||
inline bool Alarmed() const { return( alarmed ); }
|
||||
inline void SetAlarm() { alarmed = true; }
|
||||
inline void ClearAlarm() { alarmed = false; }
|
||||
inline Coord GetAlarmCentre() const { return( alarm_centre ); }
|
||||
inline unsigned int Score() const { return( score ); }
|
||||
inline int Id() const { return( id ); }
|
||||
inline const char *Label() const { return( label ); }
|
||||
inline ZoneType Type() const { return( type ); }
|
||||
inline bool IsActive() const { return( type == ACTIVE ); }
|
||||
inline bool IsInclusive() const { return( type == INCLUSIVE ); }
|
||||
inline bool IsExclusive() const { return( type == EXCLUSIVE ); }
|
||||
inline bool IsPreclusive() const { return( type == PRECLUSIVE ); }
|
||||
inline bool IsInactive() const { return( type == INACTIVE ); }
|
||||
inline bool IsPrivacy() const { return( type == PRIVACY ); }
|
||||
inline const Image *AlarmImage() const { return( image ); }
|
||||
inline const Polygon &GetPolygon() const { return( polygon ); }
|
||||
inline bool Alarmed() const { return( alarmed ); }
|
||||
inline void SetAlarm() { alarmed = true; }
|
||||
inline void ClearAlarm() { alarmed = false; }
|
||||
inline Coord GetAlarmCentre() const { return( alarm_centre ); }
|
||||
inline unsigned int Score() const { return( score ); }
|
||||
|
||||
inline void ResetStats()
|
||||
{
|
||||
alarmed = false;
|
||||
pixel_diff = 0;
|
||||
alarm_pixels = 0;
|
||||
alarm_filter_pixels = 0;
|
||||
alarm_blob_pixels = 0;
|
||||
alarm_blobs = 0;
|
||||
min_blob_size = 0;
|
||||
max_blob_size = 0;
|
||||
score = 0;
|
||||
}
|
||||
void RecordStats( const Event *event );
|
||||
bool CheckAlarms( const Image *delta_image );
|
||||
bool DumpSettings( char *output, bool verbose );
|
||||
inline void ResetStats()
|
||||
{
|
||||
alarmed = false;
|
||||
pixel_diff = 0;
|
||||
alarm_pixels = 0;
|
||||
alarm_filter_pixels = 0;
|
||||
alarm_blob_pixels = 0;
|
||||
alarm_blobs = 0;
|
||||
min_blob_size = 0;
|
||||
max_blob_size = 0;
|
||||
score = 0;
|
||||
}
|
||||
void RecordStats( const Event *event );
|
||||
bool CheckAlarms( const Image *delta_image );
|
||||
bool DumpSettings( char *output, bool verbose );
|
||||
|
||||
static bool ParsePolygonString( const char *polygon_string, Polygon &polygon );
|
||||
static bool ParseZoneString( const char *zone_string, int &zone_id, int &colour, Polygon &polygon );
|
||||
static int Load( Monitor *monitor, Zone **&zones );
|
||||
//=================================================
|
||||
bool CheckOverloadCount();
|
||||
int GetOverloadCount();
|
||||
void SetOverloadCount(int nOverCount);
|
||||
int GetOverloadFrames();
|
||||
//=================================================
|
||||
bool CheckExtendAlarmCount();
|
||||
int GetExtendAlarmCount();
|
||||
void SetExtendAlarmCount(int nOverCount);
|
||||
int GetExtendAlarmFrames();
|
||||
void SetScore(unsigned int nScore);
|
||||
void SetAlarmImage(const Image* srcImage);
|
||||
static bool ParsePolygonString( const char *polygon_string, Polygon &polygon );
|
||||
static bool ParseZoneString( const char *zone_string, int &zone_id, int &colour, Polygon &polygon );
|
||||
static int Load( Monitor *monitor, Zone **&zones );
|
||||
//=================================================
|
||||
bool CheckOverloadCount();
|
||||
int GetOverloadCount();
|
||||
void SetOverloadCount(int nOverCount);
|
||||
int GetOverloadFrames();
|
||||
//=================================================
|
||||
bool CheckExtendAlarmCount();
|
||||
int GetExtendAlarmCount();
|
||||
void SetExtendAlarmCount(int nOverCount);
|
||||
int GetExtendAlarmFrames();
|
||||
void SetScore(unsigned int nScore);
|
||||
void SetAlarmImage(const Image* srcImage);
|
||||
|
||||
inline const Image *getPgImage() const { return( pg_image ); }
|
||||
inline const Range *getRanges() const { return( ranges ); }
|
||||
inline const Image *getPgImage() const { return( pg_image ); }
|
||||
inline const Range *getRanges() const { return( ranges ); }
|
||||
|
||||
};
|
||||
|
||||
|
|
238
src/zma.cpp
238
src/zma.cpp
|
@ -41,9 +41,9 @@ behind.
|
|||
|
||||
=head1 OPTIONS
|
||||
|
||||
-m, --monitor_id - ID of the monitor to analyse
|
||||
-h, --help - Display usage information
|
||||
-v, --version - Print the installed version of ZoneMinder
|
||||
-m, --monitor_id - ID of the monitor to analyse
|
||||
-h, --help - Display usage information
|
||||
-v, --version - Print the installed version of ZoneMinder
|
||||
|
||||
=cut
|
||||
|
||||
|
@ -59,146 +59,146 @@ behind.
|
|||
|
||||
void Usage()
|
||||
{
|
||||
fprintf( stderr, "zma -m <monitor_id>\n" );
|
||||
fprintf( stderr, "Options:\n" );
|
||||
fprintf( stderr, " -m, --monitor <monitor_id> : Specify which monitor to use\n" );
|
||||
fprintf( stderr, " -h, --help : This screen\n" );
|
||||
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
|
||||
exit( 0 );
|
||||
fprintf( stderr, "zma -m <monitor_id>\n" );
|
||||
fprintf( stderr, "Options:\n" );
|
||||
fprintf( stderr, " -m, --monitor <monitor_id> : Specify which monitor to use\n" );
|
||||
fprintf( stderr, " -h, --help : This screen\n" );
|
||||
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
int main( int argc, char *argv[] )
|
||||
{
|
||||
self = argv[0];
|
||||
self = argv[0];
|
||||
|
||||
srand( getpid() * time( 0 ) );
|
||||
srand( getpid() * time( 0 ) );
|
||||
|
||||
int id = -1;
|
||||
int id = -1;
|
||||
|
||||
static struct option long_options[] = {
|
||||
{"monitor", 1, 0, 'm'},
|
||||
{"help", 0, 0, 'h'},
|
||||
{"version", 0, 0, 'v'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
static struct option long_options[] = {
|
||||
{"monitor", 1, 0, 'm'},
|
||||
{"help", 0, 0, 'h'},
|
||||
{"version", 0, 0, 'v'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
while (1)
|
||||
{
|
||||
int option_index = 0;
|
||||
while (1)
|
||||
{
|
||||
int option_index = 0;
|
||||
|
||||
int c = getopt_long (argc, argv, "m:h:v", long_options, &option_index);
|
||||
if (c == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
int c = getopt_long (argc, argv, "m:h:v", long_options, &option_index);
|
||||
if (c == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case 'm':
|
||||
id = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
Usage();
|
||||
break;
|
||||
case 'v':
|
||||
std::cout << ZM_VERSION << "\n";
|
||||
exit(0);
|
||||
default:
|
||||
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (c)
|
||||
{
|
||||
case 'm':
|
||||
id = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
Usage();
|
||||
break;
|
||||
case 'v':
|
||||
std::cout << ZM_VERSION << "\n";
|
||||
exit(0);
|
||||
default:
|
||||
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc)
|
||||
{
|
||||
fprintf( stderr, "Extraneous options, " );
|
||||
while (optind < argc)
|
||||
printf ("%s ", argv[optind++]);
|
||||
printf ("\n");
|
||||
Usage();
|
||||
}
|
||||
if (optind < argc)
|
||||
{
|
||||
fprintf( stderr, "Extraneous options, " );
|
||||
while (optind < argc)
|
||||
printf ("%s ", argv[optind++]);
|
||||
printf ("\n");
|
||||
Usage();
|
||||
}
|
||||
|
||||
if ( id < 0 )
|
||||
{
|
||||
fprintf( stderr, "Bogus monitor %d\n", id );
|
||||
Usage();
|
||||
exit( 0 );
|
||||
}
|
||||
if ( id < 0 )
|
||||
{
|
||||
fprintf( stderr, "Bogus monitor %d\n", id );
|
||||
Usage();
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
char log_id_string[16];
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zma_m%d", id );
|
||||
char log_id_string[16];
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zma_m%d", id );
|
||||
|
||||
zmLoadConfig();
|
||||
zmLoadConfig();
|
||||
|
||||
logInit( log_id_string );
|
||||
logInit( log_id_string );
|
||||
|
||||
ssedetect();
|
||||
ssedetect();
|
||||
|
||||
Monitor *monitor = Monitor::Load( id, true, Monitor::ANALYSIS );
|
||||
Monitor *monitor = Monitor::Load( id, true, Monitor::ANALYSIS );
|
||||
|
||||
if ( monitor )
|
||||
{
|
||||
Info( "In mode %d/%d, warming up", monitor->GetFunction(), monitor->Enabled() );
|
||||
if ( monitor )
|
||||
{
|
||||
Info( "In mode %d/%d, warming up", monitor->GetFunction(), monitor->Enabled() );
|
||||
|
||||
if ( config.opt_frame_server )
|
||||
{
|
||||
Event::OpenFrameSocket( monitor->Id() );
|
||||
}
|
||||
if ( config.opt_frame_server )
|
||||
{
|
||||
Event::OpenFrameSocket( monitor->Id() );
|
||||
}
|
||||
|
||||
zmSetDefaultHupHandler();
|
||||
zmSetDefaultTermHandler();
|
||||
zmSetDefaultDieHandler();
|
||||
zmSetDefaultHupHandler();
|
||||
zmSetDefaultTermHandler();
|
||||
zmSetDefaultDieHandler();
|
||||
|
||||
sigset_t block_set;
|
||||
sigemptyset( &block_set );
|
||||
sigset_t block_set;
|
||||
sigemptyset( &block_set );
|
||||
|
||||
useconds_t analysis_rate = monitor->GetAnalysisRate();
|
||||
unsigned int analysis_update_delay = monitor->GetAnalysisUpdateDelay();
|
||||
time_t last_analysis_update_time, cur_time;
|
||||
monitor->UpdateAdaptiveSkip();
|
||||
last_analysis_update_time = time( 0 );
|
||||
useconds_t analysis_rate = monitor->GetAnalysisRate();
|
||||
unsigned int analysis_update_delay = monitor->GetAnalysisUpdateDelay();
|
||||
time_t last_analysis_update_time, cur_time;
|
||||
monitor->UpdateAdaptiveSkip();
|
||||
last_analysis_update_time = time( 0 );
|
||||
|
||||
while( !zm_terminate )
|
||||
{
|
||||
// Process the next image
|
||||
sigprocmask( SIG_BLOCK, &block_set, 0 );
|
||||
while( !zm_terminate )
|
||||
{
|
||||
// Process the next image
|
||||
sigprocmask( SIG_BLOCK, &block_set, 0 );
|
||||
|
||||
// Some periodic updates are required for variable capturing framerate
|
||||
if ( analysis_update_delay )
|
||||
{
|
||||
cur_time = time( 0 );
|
||||
if ( ( cur_time - last_analysis_update_time ) > analysis_update_delay )
|
||||
{
|
||||
analysis_rate = monitor->GetAnalysisRate();
|
||||
monitor->UpdateAdaptiveSkip();
|
||||
last_analysis_update_time = cur_time;
|
||||
}
|
||||
}
|
||||
// Some periodic updates are required for variable capturing framerate
|
||||
if ( analysis_update_delay )
|
||||
{
|
||||
cur_time = time( 0 );
|
||||
if ( ( cur_time - last_analysis_update_time ) > analysis_update_delay )
|
||||
{
|
||||
analysis_rate = monitor->GetAnalysisRate();
|
||||
monitor->UpdateAdaptiveSkip();
|
||||
last_analysis_update_time = cur_time;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !monitor->Analyse() )
|
||||
{
|
||||
usleep( monitor->Active()?ZM_SAMPLE_RATE:ZM_SUSPENDED_RATE );
|
||||
}
|
||||
else if ( analysis_rate )
|
||||
{
|
||||
usleep( analysis_rate );
|
||||
}
|
||||
if ( !monitor->Analyse() )
|
||||
{
|
||||
usleep( monitor->Active()?ZM_SAMPLE_RATE:ZM_SUSPENDED_RATE );
|
||||
}
|
||||
else if ( analysis_rate )
|
||||
{
|
||||
usleep( analysis_rate );
|
||||
}
|
||||
|
||||
if ( zm_reload )
|
||||
{
|
||||
monitor->Reload();
|
||||
zm_reload = false;
|
||||
}
|
||||
sigprocmask( SIG_UNBLOCK, &block_set, 0 );
|
||||
}
|
||||
delete monitor;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr, "Can't find monitor with id of %d\n", id );
|
||||
}
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
return( 0 );
|
||||
if ( zm_reload )
|
||||
{
|
||||
monitor->Reload();
|
||||
zm_reload = false;
|
||||
}
|
||||
sigprocmask( SIG_UNBLOCK, &block_set, 0 );
|
||||
}
|
||||
delete monitor;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr, "Can't find monitor with id of %d\n", id );
|
||||
}
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
return( 0 );
|
||||
}
|
||||
|
|
508
src/zmc.cpp
508
src/zmc.cpp
|
@ -44,12 +44,12 @@ possible, this should run at more or less constant speed.
|
|||
|
||||
=head1 OPTIONS
|
||||
|
||||
-d, --device <device_path> - For local cameras, device to access. e.g /dev/video0 etc
|
||||
-d, --device <device_path> - For local cameras, device to access. e.g /dev/video0 etc
|
||||
-r <proto> -H <host> -P <port> -p <path> - For remote cameras
|
||||
-f, --file <file_path> - For local images, jpg file to access.
|
||||
-m, --monitor_id - ID of the monitor to analyse
|
||||
-h, --help - Display usage information
|
||||
-v, --version - Print the installed version of ZoneMinder
|
||||
-f, --file <file_path> - For local images, jpg file to access.
|
||||
-m, --monitor_id - ID of the monitor to analyse
|
||||
-h, --help - Display usage information
|
||||
-v, --version - Print the installed version of ZoneMinder
|
||||
|
||||
=cut
|
||||
|
||||
|
@ -75,290 +75,290 @@ possible, this should run at more or less constant speed.
|
|||
|
||||
void Usage()
|
||||
{
|
||||
fprintf( stderr, "zmc -d <device_path> or -r <proto> -H <host> -P <port> -p <path> or -f <file_path> or -m <monitor_id>\n" );
|
||||
fprintf( stderr, "zmc -d <device_path> or -r <proto> -H <host> -P <port> -p <path> or -f <file_path> or -m <monitor_id>\n" );
|
||||
|
||||
fprintf( stderr, "Options:\n" );
|
||||
fprintf( stderr, "Options:\n" );
|
||||
#if defined(BSD)
|
||||
fprintf( stderr, " -d, --device <device_path> : For local cameras, device to access. E.g /dev/bktr0 etc\n" );
|
||||
fprintf( stderr, " -d, --device <device_path> : For local cameras, device to access. E.g /dev/bktr0 etc\n" );
|
||||
#else
|
||||
fprintf( stderr, " -d, --device <device_path> : For local cameras, device to access. E.g /dev/video0 etc\n" );
|
||||
fprintf( stderr, " -d, --device <device_path> : For local cameras, device to access. E.g /dev/video0 etc\n" );
|
||||
#endif
|
||||
fprintf( stderr, " -r <proto> -H <host> -P <port> -p <path> : For remote cameras\n" );
|
||||
fprintf( stderr, " -f, --file <file_path> : For local images, jpg file to access.\n" );
|
||||
fprintf( stderr, " -m, --monitor <monitor_id> : For sources associated with a single monitor\n" );
|
||||
fprintf( stderr, " -h, --help : This screen\n" );
|
||||
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
|
||||
exit( 0 );
|
||||
fprintf( stderr, " -r <proto> -H <host> -P <port> -p <path> : For remote cameras\n" );
|
||||
fprintf( stderr, " -f, --file <file_path> : For local images, jpg file to access.\n" );
|
||||
fprintf( stderr, " -m, --monitor <monitor_id> : For sources associated with a single monitor\n" );
|
||||
fprintf( stderr, " -h, --help : This screen\n" );
|
||||
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
int main( int argc, char *argv[] )
|
||||
{
|
||||
self = argv[0];
|
||||
self = argv[0];
|
||||
|
||||
srand( getpid() * time( 0 ) );
|
||||
srand( getpid() * time( 0 ) );
|
||||
|
||||
const char *device = "";
|
||||
const char *protocol = "";
|
||||
const char *host = "";
|
||||
const char *port = "";
|
||||
const char *path = "";
|
||||
const char *file = "";
|
||||
int monitor_id = -1;
|
||||
const char *device = "";
|
||||
const char *protocol = "";
|
||||
const char *host = "";
|
||||
const char *port = "";
|
||||
const char *path = "";
|
||||
const char *file = "";
|
||||
int monitor_id = -1;
|
||||
|
||||
static struct option long_options[] = {
|
||||
{"device", 1, 0, 'd'},
|
||||
{"protocol", 1, 0, 'r'},
|
||||
{"host", 1, 0, 'H'},
|
||||
{"port", 1, 0, 'P'},
|
||||
{"path", 1, 0, 'p'},
|
||||
{"file", 1, 0, 'f'},
|
||||
{"monitor", 1, 0, 'm'},
|
||||
{"help", 0, 0, 'h'},
|
||||
{"version", 0, 0, 'v'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
static struct option long_options[] = {
|
||||
{"device", 1, 0, 'd'},
|
||||
{"protocol", 1, 0, 'r'},
|
||||
{"host", 1, 0, 'H'},
|
||||
{"port", 1, 0, 'P'},
|
||||
{"path", 1, 0, 'p'},
|
||||
{"file", 1, 0, 'f'},
|
||||
{"monitor", 1, 0, 'm'},
|
||||
{"help", 0, 0, 'h'},
|
||||
{"version", 0, 0, 'v'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
while (1)
|
||||
{
|
||||
int option_index = 0;
|
||||
while (1)
|
||||
{
|
||||
int option_index = 0;
|
||||
|
||||
int c = getopt_long (argc, argv, "d:H:P:p:f:m:h:v", long_options, &option_index);
|
||||
if (c == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
int c = getopt_long (argc, argv, "d:H:P:p:f:m:h:v", long_options, &option_index);
|
||||
if (c == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case 'd':
|
||||
device = optarg;
|
||||
break;
|
||||
case 'H':
|
||||
host = optarg;
|
||||
break;
|
||||
case 'P':
|
||||
port = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
path = optarg;
|
||||
break;
|
||||
case 'f':
|
||||
file = optarg;
|
||||
break;
|
||||
case 'm':
|
||||
monitor_id = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
Usage();
|
||||
break;
|
||||
case 'v':
|
||||
std::cout << ZM_VERSION << "\n";
|
||||
exit(0);
|
||||
default:
|
||||
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (c)
|
||||
{
|
||||
case 'd':
|
||||
device = optarg;
|
||||
break;
|
||||
case 'H':
|
||||
host = optarg;
|
||||
break;
|
||||
case 'P':
|
||||
port = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
path = optarg;
|
||||
break;
|
||||
case 'f':
|
||||
file = optarg;
|
||||
break;
|
||||
case 'm':
|
||||
monitor_id = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
Usage();
|
||||
break;
|
||||
case 'v':
|
||||
std::cout << ZM_VERSION << "\n";
|
||||
exit(0);
|
||||
default:
|
||||
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc)
|
||||
{
|
||||
fprintf( stderr, "Extraneous options, " );
|
||||
while (optind < argc)
|
||||
printf ("%s ", argv[optind++]);
|
||||
printf ("\n");
|
||||
Usage();
|
||||
}
|
||||
if (optind < argc)
|
||||
{
|
||||
fprintf( stderr, "Extraneous options, " );
|
||||
while (optind < argc)
|
||||
printf ("%s ", argv[optind++]);
|
||||
printf ("\n");
|
||||
Usage();
|
||||
}
|
||||
|
||||
int modes = ( device[0]?1:0 + host[0]?1:0 + file[0]?1:0 + (monitor_id>0?1:0) );
|
||||
if ( modes > 1 )
|
||||
{
|
||||
fprintf( stderr, "Only one of device, host/port/path, file or monitor id allowed\n" );
|
||||
Usage();
|
||||
exit( 0 );
|
||||
}
|
||||
int modes = ( device[0]?1:0 + host[0]?1:0 + file[0]?1:0 + (monitor_id>0?1:0) );
|
||||
if ( modes > 1 )
|
||||
{
|
||||
fprintf( stderr, "Only one of device, host/port/path, file or monitor id allowed\n" );
|
||||
Usage();
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
if ( modes < 1 )
|
||||
{
|
||||
fprintf( stderr, "One of device, host/port/path, file or monitor id must be specified\n" );
|
||||
Usage();
|
||||
exit( 0 );
|
||||
}
|
||||
if ( modes < 1 )
|
||||
{
|
||||
fprintf( stderr, "One of device, host/port/path, file or monitor id must be specified\n" );
|
||||
Usage();
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
char log_id_string[32] = "";
|
||||
if ( device[0] )
|
||||
{
|
||||
const char *slash_ptr = strrchr( device, '/' );
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_d%s", slash_ptr?slash_ptr+1:device );
|
||||
}
|
||||
else if ( host[0] )
|
||||
{
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_h%s", host );
|
||||
}
|
||||
else if ( file[0] )
|
||||
{
|
||||
const char *slash_ptr = strrchr( file, '/' );
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_f%s", slash_ptr?slash_ptr+1:file );
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_m%d", monitor_id );
|
||||
}
|
||||
char log_id_string[32] = "";
|
||||
if ( device[0] )
|
||||
{
|
||||
const char *slash_ptr = strrchr( device, '/' );
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_d%s", slash_ptr?slash_ptr+1:device );
|
||||
}
|
||||
else if ( host[0] )
|
||||
{
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_h%s", host );
|
||||
}
|
||||
else if ( file[0] )
|
||||
{
|
||||
const char *slash_ptr = strrchr( file, '/' );
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_f%s", slash_ptr?slash_ptr+1:file );
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_m%d", monitor_id );
|
||||
}
|
||||
|
||||
zmLoadConfig();
|
||||
zmLoadConfig();
|
||||
|
||||
logInit( log_id_string );
|
||||
logInit( log_id_string );
|
||||
|
||||
ssedetect();
|
||||
ssedetect();
|
||||
|
||||
Monitor **monitors = 0;
|
||||
int n_monitors = 0;
|
||||
Monitor **monitors = 0;
|
||||
int n_monitors = 0;
|
||||
#if ZM_HAS_V4L
|
||||
if ( device[0] )
|
||||
{
|
||||
n_monitors = Monitor::LoadLocalMonitors( device, monitors, Monitor::CAPTURE );
|
||||
}
|
||||
else
|
||||
if ( device[0] )
|
||||
{
|
||||
n_monitors = Monitor::LoadLocalMonitors( device, monitors, Monitor::CAPTURE );
|
||||
}
|
||||
else
|
||||
#endif // ZM_HAS_V4L
|
||||
if ( host[0] )
|
||||
{
|
||||
if ( !port )
|
||||
port = "80";
|
||||
n_monitors = Monitor::LoadRemoteMonitors( protocol, host, port, path, monitors, Monitor::CAPTURE );
|
||||
}
|
||||
else if ( file[0] )
|
||||
{
|
||||
n_monitors = Monitor::LoadFileMonitors( file, monitors, Monitor::CAPTURE );
|
||||
}
|
||||
else
|
||||
{
|
||||
Monitor *monitor = Monitor::Load( monitor_id, true, Monitor::CAPTURE );
|
||||
if ( monitor )
|
||||
{
|
||||
monitors = new Monitor *[1];
|
||||
monitors[0] = monitor;
|
||||
n_monitors = 1;
|
||||
}
|
||||
}
|
||||
if ( host[0] )
|
||||
{
|
||||
if ( !port )
|
||||
port = "80";
|
||||
n_monitors = Monitor::LoadRemoteMonitors( protocol, host, port, path, monitors, Monitor::CAPTURE );
|
||||
}
|
||||
else if ( file[0] )
|
||||
{
|
||||
n_monitors = Monitor::LoadFileMonitors( file, monitors, Monitor::CAPTURE );
|
||||
}
|
||||
else
|
||||
{
|
||||
Monitor *monitor = Monitor::Load( monitor_id, true, Monitor::CAPTURE );
|
||||
if ( monitor )
|
||||
{
|
||||
monitors = new Monitor *[1];
|
||||
monitors[0] = monitor;
|
||||
n_monitors = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !n_monitors )
|
||||
{
|
||||
Error( "No monitors found" );
|
||||
exit ( -1 );
|
||||
}
|
||||
if ( !n_monitors )
|
||||
{
|
||||
Error( "No monitors found" );
|
||||
exit ( -1 );
|
||||
}
|
||||
|
||||
Info( "Starting Capture version %s", ZM_VERSION );
|
||||
Info( "Starting Capture version %s", ZM_VERSION );
|
||||
|
||||
zmSetDefaultTermHandler();
|
||||
zmSetDefaultDieHandler();
|
||||
zmSetDefaultTermHandler();
|
||||
zmSetDefaultDieHandler();
|
||||
|
||||
sigset_t block_set;
|
||||
sigemptyset( &block_set );
|
||||
sigset_t block_set;
|
||||
sigemptyset( &block_set );
|
||||
|
||||
sigaddset( &block_set, SIGUSR1 );
|
||||
sigaddset( &block_set, SIGUSR2 );
|
||||
sigaddset( &block_set, SIGUSR1 );
|
||||
sigaddset( &block_set, SIGUSR2 );
|
||||
|
||||
if ( monitors[0]->PrimeCapture() < 0 )
|
||||
{
|
||||
Error( "Failed to prime capture of initial monitor" );
|
||||
exit( -1 );
|
||||
}
|
||||
if ( monitors[0]->PrimeCapture() < 0 )
|
||||
{
|
||||
Error( "Failed to prime capture of initial monitor" );
|
||||
exit( -1 );
|
||||
}
|
||||
|
||||
long *capture_delays = new long[n_monitors];
|
||||
long *alarm_capture_delays = new long[n_monitors];
|
||||
long *next_delays = new long[n_monitors];
|
||||
struct timeval * last_capture_times = new struct timeval[n_monitors];
|
||||
for ( int i = 0; i < n_monitors; i++ )
|
||||
{
|
||||
last_capture_times[i].tv_sec = last_capture_times[i].tv_usec = 0;
|
||||
capture_delays[i] = monitors[i]->GetCaptureDelay();
|
||||
alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay();
|
||||
}
|
||||
long *capture_delays = new long[n_monitors];
|
||||
long *alarm_capture_delays = new long[n_monitors];
|
||||
long *next_delays = new long[n_monitors];
|
||||
struct timeval * last_capture_times = new struct timeval[n_monitors];
|
||||
for ( int i = 0; i < n_monitors; i++ )
|
||||
{
|
||||
last_capture_times[i].tv_sec = last_capture_times[i].tv_usec = 0;
|
||||
capture_delays[i] = monitors[i]->GetCaptureDelay();
|
||||
alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay();
|
||||
}
|
||||
|
||||
int result = 0;
|
||||
struct timeval now;
|
||||
struct DeltaTimeval delta_time;
|
||||
while( !zm_terminate )
|
||||
{
|
||||
sigprocmask( SIG_BLOCK, &block_set, 0 );
|
||||
for ( int i = 0; i < n_monitors; i++ )
|
||||
{
|
||||
long min_delay = MAXINT;
|
||||
int result = 0;
|
||||
struct timeval now;
|
||||
struct DeltaTimeval delta_time;
|
||||
while( !zm_terminate )
|
||||
{
|
||||
sigprocmask( SIG_BLOCK, &block_set, 0 );
|
||||
for ( int i = 0; i < n_monitors; i++ )
|
||||
{
|
||||
long min_delay = MAXINT;
|
||||
|
||||
gettimeofday( &now, NULL );
|
||||
for ( int j = 0; j < n_monitors; j++ )
|
||||
{
|
||||
if ( last_capture_times[j].tv_sec )
|
||||
{
|
||||
DELTA_TIMEVAL( delta_time, now, last_capture_times[j], DT_PREC_3 );
|
||||
if ( monitors[i]->GetState() == Monitor::ALARM )
|
||||
next_delays[j] = alarm_capture_delays[j]-delta_time.delta;
|
||||
else
|
||||
next_delays[j] = capture_delays[j]-delta_time.delta;
|
||||
if ( next_delays[j] < 0 )
|
||||
next_delays[j] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
next_delays[j] = 0;
|
||||
}
|
||||
if ( next_delays[j] <= min_delay )
|
||||
{
|
||||
min_delay = next_delays[j];
|
||||
}
|
||||
}
|
||||
gettimeofday( &now, NULL );
|
||||
for ( int j = 0; j < n_monitors; j++ )
|
||||
{
|
||||
if ( last_capture_times[j].tv_sec )
|
||||
{
|
||||
DELTA_TIMEVAL( delta_time, now, last_capture_times[j], DT_PREC_3 );
|
||||
if ( monitors[i]->GetState() == Monitor::ALARM )
|
||||
next_delays[j] = alarm_capture_delays[j]-delta_time.delta;
|
||||
else
|
||||
next_delays[j] = capture_delays[j]-delta_time.delta;
|
||||
if ( next_delays[j] < 0 )
|
||||
next_delays[j] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
next_delays[j] = 0;
|
||||
}
|
||||
if ( next_delays[j] <= min_delay )
|
||||
{
|
||||
min_delay = next_delays[j];
|
||||
}
|
||||
}
|
||||
|
||||
if ( next_delays[i] <= min_delay || next_delays[i] <= 0 )
|
||||
{
|
||||
if ( monitors[i]->PreCapture() < 0 )
|
||||
{
|
||||
Error( "Failed to pre-capture monitor %d %d (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors );
|
||||
zm_terminate = true;
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
if ( monitors[i]->Capture() < 0 )
|
||||
{
|
||||
Error( "Failed to capture image from monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors );
|
||||
zm_terminate = true;
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
if ( monitors[i]->PostCapture() < 0 )
|
||||
{
|
||||
Error( "Failed to post-capture monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors );
|
||||
zm_terminate = true;
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
if ( next_delays[i] <= min_delay || next_delays[i] <= 0 )
|
||||
{
|
||||
if ( monitors[i]->PreCapture() < 0 )
|
||||
{
|
||||
Error( "Failed to pre-capture monitor %d %d (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors );
|
||||
zm_terminate = true;
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
if ( monitors[i]->Capture() < 0 )
|
||||
{
|
||||
Error( "Failed to capture image from monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors );
|
||||
zm_terminate = true;
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
if ( monitors[i]->PostCapture() < 0 )
|
||||
{
|
||||
Error( "Failed to post-capture monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors );
|
||||
zm_terminate = true;
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( next_delays[i] > 0 )
|
||||
{
|
||||
gettimeofday( &now, NULL );
|
||||
DELTA_TIMEVAL( delta_time, now, last_capture_times[i], DT_PREC_3 );
|
||||
long sleep_time = next_delays[i]-delta_time.delta;
|
||||
if ( sleep_time > 0 )
|
||||
{
|
||||
usleep( sleep_time*(DT_MAXGRAN/DT_PREC_3) );
|
||||
}
|
||||
}
|
||||
gettimeofday( &(last_capture_times[i]), NULL );
|
||||
}
|
||||
}
|
||||
sigprocmask( SIG_UNBLOCK, &block_set, 0 );
|
||||
}
|
||||
for ( int i = 0; i < n_monitors; i++ )
|
||||
{
|
||||
delete monitors[i];
|
||||
}
|
||||
delete [] monitors;
|
||||
delete [] alarm_capture_delays;
|
||||
delete [] capture_delays;
|
||||
delete [] next_delays;
|
||||
delete [] last_capture_times;
|
||||
if ( next_delays[i] > 0 )
|
||||
{
|
||||
gettimeofday( &now, NULL );
|
||||
DELTA_TIMEVAL( delta_time, now, last_capture_times[i], DT_PREC_3 );
|
||||
long sleep_time = next_delays[i]-delta_time.delta;
|
||||
if ( sleep_time > 0 )
|
||||
{
|
||||
usleep( sleep_time*(DT_MAXGRAN/DT_PREC_3) );
|
||||
}
|
||||
}
|
||||
gettimeofday( &(last_capture_times[i]), NULL );
|
||||
}
|
||||
}
|
||||
sigprocmask( SIG_UNBLOCK, &block_set, 0 );
|
||||
}
|
||||
for ( int i = 0; i < n_monitors; i++ )
|
||||
{
|
||||
delete monitors[i];
|
||||
}
|
||||
delete [] monitors;
|
||||
delete [] alarm_capture_delays;
|
||||
delete [] capture_delays;
|
||||
delete [] next_delays;
|
||||
delete [] last_capture_times;
|
||||
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
|
||||
return( result );
|
||||
return( result );
|
||||
}
|
||||
|
|
10
src/zmf.h
10
src/zmf.h
|
@ -22,11 +22,11 @@
|
|||
|
||||
struct FrameHeader
|
||||
{
|
||||
unsigned long event_id;
|
||||
time_t event_time;
|
||||
unsigned long frame_id;
|
||||
bool alarm_frame;
|
||||
unsigned long image_length;
|
||||
unsigned long event_id;
|
||||
time_t event_time;
|
||||
unsigned long frame_id;
|
||||
bool alarm_frame;
|
||||
unsigned long image_length;
|
||||
};
|
||||
|
||||
#endif // ZMFILE_H
|
||||
|
|
|
@ -62,7 +62,7 @@ int main( int argc, const char *argv[] )
|
|||
int monitor_id = 0;
|
||||
time_t event_time = 0;
|
||||
int event_id = 0;
|
||||
int frame_id = 1;
|
||||
unsigned int frame_id = 1;
|
||||
unsigned int scale = 100;
|
||||
unsigned int rate = 100;
|
||||
double maxfps = 10.0;
|
||||
|
|
|
@ -0,0 +1,252 @@
|
|||
//
|
||||
// ZoneMinder Streamer, $Date: 2010-10-14 23:21:00 +0200 (Thu, 14 Oct 2010) $
|
||||
// Copyright (C) 2001-2010 Philip Coombes, Chris Kistner
|
||||
//
|
||||
// This program is based on revision 3143 of
|
||||
// http://svn.zoneminder.com/svn/zm/trunk/src/zms.cpp
|
||||
//
|
||||
// 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.
|
||||
|
||||
/*
|
||||
|
||||
=head1 NAME
|
||||
|
||||
zmstreamer - eyeZM video streamer
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
zmstreamer -e <mode>
|
||||
zmstreamer -o <format>
|
||||
zmstreamer -u <buffer size>
|
||||
zmstreamer -f <maximum fps>
|
||||
zmstreamer -s <scale>
|
||||
zmstreamer -b <bitrate in bps>
|
||||
zmstreamer -m <monitor id>
|
||||
zmstreamer -d <debug mode>
|
||||
zmstreamer -i
|
||||
zmstreamer -?
|
||||
zmstreamer -h
|
||||
zmstreamer -v
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
*DEPRECIATED* The xml skin and all files associated with the xml skin are now
|
||||
depreciated. Please use the ZoneMinder API instead.
|
||||
|
||||
This binary works in conjunction with the XML skin to stream video to iPhones
|
||||
running the eyeZm app.
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
-e <mode> - Specify output mode: mpeg/jpg/zip/single/raw.
|
||||
-o <format> - Specify output format.
|
||||
-u <buffer size> - Specify buffer size in ms.
|
||||
-f <maximum fps> - Specify maximum framerate.
|
||||
-s <scale> - Specify scale.
|
||||
-b <bitrate in bps> - Specify bitrate.
|
||||
-m <monitor id> - Specify monitor id.
|
||||
-d <debug mode> - 0 = off, 1 = no streaming, 2 = with streaming.
|
||||
-i, -?, -h - Display usage information
|
||||
-v - Print the installed version of ZoneMinder
|
||||
|
||||
=cut
|
||||
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/msg.h>
|
||||
|
||||
#include "zm.h"
|
||||
#include "zm_db.h"
|
||||
#include "zm_user.h"
|
||||
#include "zm_signal.h"
|
||||
#include "zm_monitor.h"
|
||||
#include "zm_stream.h"
|
||||
|
||||
// Possible command-line options
|
||||
#define OPTIONS "e:o:u:f:s:b:m:d:i:?:h:v"
|
||||
|
||||
// Default ZMS values
|
||||
#define ZMS_DEFAULT_DEBUG 0
|
||||
#define ZMS_DEFAULT_ID 1
|
||||
#define ZMS_DEFAULT_BITRATE 100000
|
||||
#define ZMS_DEFAULT_SCALE 100
|
||||
#define ZMS_DEFAULT_MODE "mpeg"
|
||||
#define ZMS_DEFAULT_FORMAT "asf"
|
||||
#define ZMS_DEFAULT_FPS 25.0
|
||||
#define ZMS_DEFAULT_BUFFER 1000
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
self = argv[0];
|
||||
// Set initial values to the default values
|
||||
int debug = ZMS_DEFAULT_DEBUG;
|
||||
int id = ZMS_DEFAULT_ID;
|
||||
int bitrate = ZMS_DEFAULT_BITRATE;
|
||||
int scale = ZMS_DEFAULT_SCALE;
|
||||
char mode[32];
|
||||
sprintf(mode, "%s", ZMS_DEFAULT_MODE);
|
||||
char format[32];
|
||||
sprintf(format, "%s", ZMS_DEFAULT_FORMAT);
|
||||
double maxfps = ZMS_DEFAULT_FPS;
|
||||
int buffer = ZMS_DEFAULT_BUFFER;
|
||||
|
||||
// Parse command-line options
|
||||
int arg;
|
||||
while ((arg = getopt(argc, argv, OPTIONS)) != -1) {
|
||||
switch (arg) {
|
||||
case 'e':
|
||||
sprintf(mode, "%s", optarg);
|
||||
break;
|
||||
case 'o':
|
||||
sprintf(format, "%s", optarg);
|
||||
break;
|
||||
case 'u':
|
||||
buffer = atoi(optarg);
|
||||
break;
|
||||
case 'f':
|
||||
maxfps = atof(optarg);
|
||||
break;
|
||||
case 's':
|
||||
scale = atoi(optarg);
|
||||
break;
|
||||
case 'b':
|
||||
bitrate = atoi(optarg);
|
||||
break;
|
||||
case 'm':
|
||||
id = atoi(optarg);
|
||||
break;
|
||||
case 'd':
|
||||
debug = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
case 'i':
|
||||
case '?':
|
||||
printf("-e <mode> : Specify output mode: mpeg/jpg/zip/single/raw. Default = %s\n", ZMS_DEFAULT_MODE);
|
||||
printf("-o <format> : Specify output format. Default = %s\n", ZMS_DEFAULT_FORMAT);
|
||||
printf("-u <buffer size> : Specify buffer size in ms. Default = %d\n", ZMS_DEFAULT_BUFFER);
|
||||
printf("-f <maximum fps> : Specify maximum framerate. Default = %lf\n", ZMS_DEFAULT_FPS);
|
||||
printf("-s <scale> : Specify scale. Default = %d\n", ZMS_DEFAULT_SCALE);
|
||||
printf("-b <bitrate in bps> : Specify bitrate. Default = %d\n", ZMS_DEFAULT_BITRATE);
|
||||
printf("-m <monitor id> : Specify monitor id. Default = %d\n", ZMS_DEFAULT_ID);
|
||||
printf("-d <debug mode> : 0 = off, 1 = no streaming, 2 = with streaming. Default = 0\n");
|
||||
printf("-i or -? or -h: This information\n");
|
||||
printf("-v : This installed version of ZoneMinder\n");
|
||||
return EXIT_SUCCESS;
|
||||
case 'v':
|
||||
std::cout << ZM_VERSION << "\n";
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Set stream type
|
||||
StreamBase::StreamType streamtype;
|
||||
if (!strcasecmp("raw", mode))
|
||||
streamtype = MonitorStream::STREAM_RAW;
|
||||
else if (!strcasecmp("mpeg", mode))
|
||||
streamtype = MonitorStream::STREAM_MPEG;
|
||||
else if (!strcasecmp("jpg", mode))
|
||||
streamtype = MonitorStream::STREAM_JPEG;
|
||||
else if (!strcasecmp("single", mode))
|
||||
streamtype = MonitorStream::STREAM_SINGLE;
|
||||
else if (!strcasecmp("zip", mode))
|
||||
streamtype = MonitorStream::STREAM_ZIP;
|
||||
else
|
||||
streamtype = MonitorStream::STREAM_MPEG;
|
||||
|
||||
if (debug) {
|
||||
// Show stream parameters
|
||||
printf("Stream parameters:\n");
|
||||
switch (streamtype) {
|
||||
case MonitorStream::STREAM_MPEG:
|
||||
printf("Output mode (-e) = %s\n", "mpeg");
|
||||
printf("Output format (-o) = %s\n", format);
|
||||
break;
|
||||
default:
|
||||
printf("Output mode (-e) = %s\n", mode);
|
||||
}
|
||||
printf("Buffer size (-u) = %d ms\n", buffer);
|
||||
printf("Maximum FPS (-f) = %lf FPS\n", maxfps);
|
||||
printf("Scale (-s) = %d%%\n", scale);
|
||||
printf("Bitrate (-b) = %d bps\n", bitrate);
|
||||
printf("Monitor Id (-m) = %d\n", id);
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
// Set ZM debugger to print to stdout
|
||||
printf("Setting up ZoneMinder debugger to print to stdout...");
|
||||
setenv("ZM_DBG_PRINT", "1", 1);
|
||||
printf("Done.\n");
|
||||
}
|
||||
|
||||
// Loading ZM configurations
|
||||
printf("Loading ZoneMinder configurations...");
|
||||
zmLoadConfig();
|
||||
printf("Done.\n");
|
||||
|
||||
logInit("zmstreamer");
|
||||
|
||||
ssedetect();
|
||||
|
||||
// Setting stream parameters
|
||||
MonitorStream stream;
|
||||
stream.setStreamScale(scale); // default = 100 (scale)
|
||||
stream.setStreamReplayRate(100); // default = 100 (rate)
|
||||
stream.setStreamMaxFPS(maxfps); // default = 10 (maxfps)
|
||||
if (debug) stream.setStreamTTL(1);
|
||||
else stream.setStreamTTL(0); // default = 0 (ttl)
|
||||
stream.setStreamQueue(0); // default = 0 (connkey)
|
||||
stream.setStreamBuffer(buffer); // default = 0 (buffer)
|
||||
stream.setStreamStart(id); // default = 0 (monitor_id)
|
||||
stream.setStreamType(streamtype);
|
||||
if (streamtype == MonitorStream::STREAM_MPEG) {
|
||||
#if HAVE_LIBAVCODEC
|
||||
if (debug) printf("HAVE_LIBAVCODEC is set\n");
|
||||
stream.setStreamFormat(format); // default = "" (format)
|
||||
stream.setStreamBitrate(bitrate); // default = 100000 (bitrate)
|
||||
#else
|
||||
fprintf(stderr, "MPEG streaming is disabled.\nYou should configure with the --with-ffmpeg option and rebuild to use this functionality.\n");
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
return EXIT_FAILURE;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (debug != 1) {
|
||||
if (debug) printf("Running stream...");
|
||||
|
||||
// Output headers
|
||||
fprintf(stdout, "Server: ZoneMinder Video Server/%s\r\n", ZM_VERSION);
|
||||
time_t now = time(0);
|
||||
char date_string[64];
|
||||
strftime(date_string, sizeof (date_string) - 1, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now));
|
||||
fprintf(stdout, "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n");
|
||||
fprintf(stdout, "Last-Modified: %s\r\n", date_string);
|
||||
fprintf(stdout, "Cache-Control: no-store, no-cache, must-revalidate\r\n");
|
||||
fprintf(stdout, "Cache-Control: post-check=0, pre-check=0\r\n");
|
||||
fprintf(stdout, "Pragma: no-cache\r\n");
|
||||
|
||||
// Run stream
|
||||
stream.runStream();
|
||||
}
|
||||
if (debug) printf("Done.\n");
|
||||
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
|
||||
return (EXIT_SUCCESS);
|
||||
}
|
|
@ -756,15 +756,14 @@ int main( int argc, char *argv[] )
|
|||
|
||||
if ( function & ZMU_LIST )
|
||||
{
|
||||
char sql[ZM_SQL_SML_BUFSIZ];
|
||||
strncpy( sql, "select Id, Function+0 from Monitors", sizeof(sql) );
|
||||
std::string sql = "select Id, Function+0 from Monitors";
|
||||
if ( !verbose )
|
||||
{
|
||||
strncat( sql, " where Function != 'None'", sizeof(sql)-strlen(sql) );
|
||||
sql += "where Function != 'None'";
|
||||
}
|
||||
strncat( sql, " order by Id asc", sizeof(sql)-strlen(sql) );
|
||||
sql += " order by Id asc";
|
||||
|
||||
if ( mysql_query( &dbconn, sql ) )
|
||||
if ( mysql_query( &dbconn, sql.c_str() ) )
|
||||
{
|
||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
require_once( 'database.php' );
|
||||
require_once( 'Event.php' );
|
||||
|
||||
class Frame {
|
||||
public function __construct( $IdOrRow ) {
|
||||
$row = NULL;
|
||||
if ( $IdOrRow ) {
|
||||
if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) {
|
||||
$row = dbFetchOne( 'SELECT * FROM Frames WHERE Id=?', NULL, array( $IdOrRow ) );
|
||||
if ( ! $row ) {
|
||||
Error("Unable to load Frame record for Id=" . $IdOrRow );
|
||||
}
|
||||
} elseif ( is_array( $IdOrRow ) ) {
|
||||
$row = $IdOrRow;
|
||||
} else {
|
||||
Error("Unknown argument passed to Frame Constructor ($IdOrRow)");
|
||||
return;
|
||||
}
|
||||
} # end if isset($IdOrRow)
|
||||
|
||||
if ( $row ) {
|
||||
foreach ($row as $k => $v) {
|
||||
$this->{$k} = $v;
|
||||
}
|
||||
} else {
|
||||
Error("No row for Frame " . $IdOrRow );
|
||||
}
|
||||
} // end function __construct
|
||||
public function Storage() {
|
||||
return $this->Event()->Storage();
|
||||
}
|
||||
public function Event() {
|
||||
return new Event( $this->{'EventId'} );
|
||||
}
|
||||
public function __call( $fn, array $args){
|
||||
if(isset($this->{$fn})){
|
||||
return $this->{$fn};
|
||||
#array_unshift($args, $this);
|
||||
#call_user_func_array( $this->{$fn}, $args);
|
||||
}
|
||||
}
|
||||
|
||||
public function Path() {
|
||||
$Storage = $this->Storage();
|
||||
return $Storage->Path().'/'.$this->Relative_Path();
|
||||
}
|
||||
public function Relative_Path() {
|
||||
$event_path = "";
|
||||
|
||||
if ( ZM_USE_DEEP_STORAGE )
|
||||
{
|
||||
$event_path =
|
||||
$this->{'MonitorId'}
|
||||
.'/'.strftime( "%y/%m/%d/%H/%M/%S",
|
||||
$this->Time()
|
||||
)
|
||||
;
|
||||
}
|
||||
else
|
||||
{
|
||||
$event_path =
|
||||
$this->{'MonitorId'}
|
||||
.'/'.$this->{'Id'}
|
||||
;
|
||||
}
|
||||
|
||||
return( $event_path );
|
||||
|
||||
}
|
||||
|
||||
public function getImageSrc( ) {
|
||||
return ZM_BASE_URL.'/index.php?view=image&fid='.$this->{'Id'};
|
||||
} // end function getImageSrc
|
||||
} # end class
|
||||
?>
|
|
@ -527,6 +527,7 @@ if ( !empty($action) )
|
|||
'DoNativeMotDet' => 'toggle',
|
||||
'Exif' => 'toggle',
|
||||
'RTSPDescribe' => 'toggle',
|
||||
'RecordAudio' => 'toggle',
|
||||
);
|
||||
|
||||
$columns = getTableColumns( 'Monitors' );
|
||||
|
|
|
@ -104,6 +104,7 @@ define( "CMD_PREV", 12 );
|
|||
define( "CMD_NEXT", 13 );
|
||||
define( "CMD_SEEK", 14 );
|
||||
define( "CMD_VARPLAY", 15 );
|
||||
define( "CMD_QUIT", 17 );
|
||||
define( "CMD_QUERY", 99 );
|
||||
|
||||
//
|
||||
|
|
|
@ -217,14 +217,20 @@ function getMimeType( $file )
|
|||
return( trim( exec( 'file -bi '.escapeshellarg( $file ).' 2>/dev/null' ) ) );
|
||||
}
|
||||
|
||||
function outputVideoStream( $id, $src, $width, $height, $format, $title="" )
|
||||
{
|
||||
if ( file_exists( $src ) )
|
||||
function outputVideoStream( $id, $src, $width, $height, $format, $title="" ) {
|
||||
echo getVideoStreamHTML( $id, $src, $width, $height, $format, $title );
|
||||
}
|
||||
|
||||
function getVideoStreamHTML( $id, $src, $width, $height, $format, $title="" ) {
|
||||
$html = '';
|
||||
$width = validInt($width);
|
||||
$height = validInt($height);
|
||||
$title = validHtmlStr($title);
|
||||
|
||||
if ( file_exists( $src ) ) {
|
||||
$mimeType = getMimeType( $src );
|
||||
else
|
||||
{
|
||||
switch( $format )
|
||||
{
|
||||
} else {
|
||||
switch( $format ) {
|
||||
case 'asf' :
|
||||
$mimeType = "video/x-ms-asf";
|
||||
break;
|
||||
|
@ -252,118 +258,95 @@ function outputVideoStream( $id, $src, $width, $height, $format, $title="" )
|
|||
}
|
||||
if ( !$mimeType || ($mimeType == 'application/octet-stream') )
|
||||
$mimeType = 'video/'.$format;
|
||||
$objectTag = false;
|
||||
if ( ZM_WEB_USE_OBJECT_TAGS )
|
||||
{
|
||||
switch( $mimeType )
|
||||
{
|
||||
if ( ZM_WEB_USE_OBJECT_TAGS ) {
|
||||
switch( $mimeType ) {
|
||||
case "video/x-ms-asf" :
|
||||
case "video/x-msvideo" :
|
||||
case "video/mp4" :
|
||||
{
|
||||
if ( isWindows() )
|
||||
{
|
||||
?>
|
||||
<object id="<?php echo $id ?>" width="<?php echo validNum($width) ?>" height="<?php echo validNum($height) ?>"
|
||||
if ( isWindows() ) {
|
||||
return '<object id="'.$id.'" width="'.$width.'" height="'.$height.'
|
||||
classid="CLSID:22D6F312-B0F6-11D0-94AB-0080C74C7E95"
|
||||
codebase="http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=6,0,02,902"
|
||||
standby="Loading Microsoft Windows Media Player components..."
|
||||
type="<?php echo $mimeType ?>">
|
||||
<param name="FileName" value="<?php echo $src ?>"/>
|
||||
type="'.$mimeType.'">
|
||||
<param name="FileName" value="'.$src.'"/>
|
||||
<param name="autoStart" value="1"/>
|
||||
<param name="showControls" value="0"/>
|
||||
<embed type="<?php echo $mimeType ?>"
|
||||
<embed type="'.$mimeType.'"
|
||||
pluginspage="http://www.microsoft.com/Windows/MediaPlayer/"
|
||||
src="<?php echo $src ?>"
|
||||
name="<?php echo validHtmlStr($title) ?>"
|
||||
width="<?php echo validNum($width) ?>"
|
||||
height="<?php echo validInt($height) ?>"
|
||||
src="'.$src.'"
|
||||
name="'.$title.'"
|
||||
width="'.$width.'"
|
||||
height="'.$height.'"
|
||||
autostart="1"
|
||||
showcontrols="0">
|
||||
</embed>
|
||||
</object>
|
||||
<?php
|
||||
$objectTag = true;
|
||||
</object>';
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "video/quicktime" :
|
||||
{
|
||||
?>
|
||||
<object id="<?php echo $id ?>" width="<?php echo $width ?>" height="<?php echo $height ?>"
|
||||
return '<object id="'.$id.'" width="'.$width.'" height="'.$height.'"
|
||||
classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B"
|
||||
codebase="http://www.apple.com/qtactivex/qtplugin.cab"
|
||||
type="<?php echo $mimeType ?>">
|
||||
<param name="src" value="<?php echo $src ?>"/>
|
||||
type="'.$mimeType.'">
|
||||
<param name="src" value="'.$src.'"/>
|
||||
<param name="autoplay" VALUE="true"/>
|
||||
<param name="controller" VALUE="false"/>
|
||||
<embed type="<?php echo $mimeType ?>"
|
||||
src="<?php echo $src ?>"
|
||||
<embed type="'.$mimeType.'"
|
||||
src="'.$src.'"
|
||||
pluginspage="http://www.apple.com/quicktime/download/"
|
||||
name="<?php echo validHtmlStr($title) ?>"
|
||||
width="<?php echo validInt($width) ?>"
|
||||
height="<?php echo validInt($height) ?>"
|
||||
name="'.$title.'" width="'.$width.'" height="'.$height.'"
|
||||
autoplay="true"
|
||||
controller="true">
|
||||
</embed>
|
||||
</object>
|
||||
<?php
|
||||
$objectTag = true;
|
||||
break;
|
||||
</object>';
|
||||
}
|
||||
case "application/x-shockwave-flash" :
|
||||
{
|
||||
?>
|
||||
<object id="<?php echo $id ?>" width="<?php echo $width ?>" height="<?php echo $height ?>"
|
||||
return '<object id="'.$id.'" width="'.$width.'" height="'.$height.'"
|
||||
classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
|
||||
codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"
|
||||
type="<?php echo $mimeType ?>">
|
||||
<param name="movie" value="<?php echo $src ?>"/>
|
||||
type="'.$mimeType.'">
|
||||
<param name="movie" value="'.$src.'"/>
|
||||
<param name="quality" value="high"/>
|
||||
<param name="bgcolor" value="#ffffff"/>
|
||||
<embed type="<?php echo $mimeType ?>"
|
||||
<embed type="'.$mimeType.'"
|
||||
pluginspage="http://www.macromedia.com/go/getflashplayer"
|
||||
src="<?php echo $src ?>"
|
||||
name="<?php echo validHtmlStr($title) ?>"
|
||||
width="<?php echo validInt($width) ?>"
|
||||
height="<?php echo validInt($height) ?>"
|
||||
src="'.$src.'"
|
||||
name="'.$title.'"
|
||||
width="'.$width.'"
|
||||
height="'.$height.'"
|
||||
quality="high"
|
||||
bgcolor="#ffffff">
|
||||
</embed>
|
||||
</object>
|
||||
<?php
|
||||
$objectTag = true;
|
||||
break;
|
||||
</object>';
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( !$objectTag )
|
||||
{
|
||||
?>
|
||||
<embed<?php echo isset($mimeType)?(' type="'.$mimeType.'"'):"" ?>
|
||||
src="<?php echo $src ?>"
|
||||
name="<?php echo validHtmlStr($title) ?>"
|
||||
width="<?php echo validInt($width) ?>"
|
||||
height="<?php echo validInt($height) ?>"
|
||||
} # end switch
|
||||
} # end if use object tags
|
||||
return '<embed'. ( isset($mimeType)?(' type="'.$mimeType.'"'):'' ). '
|
||||
src="'.$src.'"
|
||||
name="'.$title.'"
|
||||
width="'.$width.'"
|
||||
height="'.$height.'"
|
||||
autostart="1"
|
||||
autoplay="1"
|
||||
showcontrols="0"
|
||||
controller="0">
|
||||
</embed>
|
||||
<?php
|
||||
}
|
||||
</embed>';
|
||||
}
|
||||
|
||||
function outputImageStream( $id, $src, $width, $height, $title="" )
|
||||
{
|
||||
function outputImageStream( $id, $src, $width, $height, $title="" ) {
|
||||
echo getImageStream( $id, $src, $width, $height, $title );
|
||||
}
|
||||
|
||||
|
||||
function getImageStream( $id, $src, $width, $height, $title="" ) {
|
||||
if ( canStreamIframe() ) {
|
||||
?>
|
||||
<iframe id="<?php echo $id ?>" src="<?php echo $src ?>" alt="<?php echo validHtmlStr($title) ?>" width="<?php echo $width ?>" height="<?php echo $height ?>"/>
|
||||
<?php
|
||||
return '<iframe id="'.$id.'" src="'.$src.'" alt="'. validHtmlStr($title) .'" width="'. validInt($width)." height=".validInt($height).'"/>';
|
||||
} else {
|
||||
?>
|
||||
<img id="<?php echo $id ?>" src="<?php echo $src ?>" alt="<?php echo validHtmlStr($title) ?>" width="<?php echo $width ?>" height="<?php echo $height ?>"/>
|
||||
<?php
|
||||
return '<img id="'.$id.'" src="'.$src.'" alt="'. validHtmlStr($title) .'" width="'. validInt($width) .'" height="'. validInt( $height ).'"/>';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1685,7 +1668,11 @@ function getDiskPercent()
|
|||
Error("disk_total_space returned false for " . ZM_DIR_EVENTS );
|
||||
return 0;
|
||||
}
|
||||
$space = round(($total - disk_free_space(ZM_DIR_EVENTS)) / $total * 100);
|
||||
$free = disk_free_space(ZM_DIR_EVENTS);
|
||||
if ( ! $free ) {
|
||||
Error("disk_free_space returned false for " . ZM_DIR_EVENTS );
|
||||
}
|
||||
$space = round(($total - $free) / $total * 100);
|
||||
return( $space );
|
||||
}
|
||||
|
||||
|
@ -2507,11 +2494,11 @@ function getStreamHTML( $monitor, $scale=100 ) {
|
|||
//FIXME, the width and height of the image need to be scaled.
|
||||
if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
|
||||
$streamSrc = $monitor->getStreamSrc( array( "mode=mpeg", "scale=".$scale, "bitrate=".ZM_WEB_VIDEO_BITRATE, "maxfps=".ZM_WEB_VIDEO_MAXFPS, "format=".ZM_MPEG_LIVE_FORMAT ) );
|
||||
outputVideoStream( "liveStream", $streamSrc, reScale( $monitor->Width(), $scale ), reScale( $monitor->Height(), $scale ), ZM_MPEG_LIVE_FORMAT, $monitor->Name() );
|
||||
return getVideoStream( "liveStream", $streamSrc, reScale( $monitor->Width(), $scale ), reScale( $monitor->Height(), $scale ), ZM_MPEG_LIVE_FORMAT, $monitor->Name() );
|
||||
} else if ( canStream() ) {
|
||||
$streamSrc = $monitor->getStreamSrc( array( 'mode=jpeg', 'scale='.$scale, 'maxfps='.ZM_WEB_VIDEO_MAXFPS, 'buffer='.$monitor->StreamReplayBuffer() ) );
|
||||
if ( canStreamNative() )
|
||||
outputImageStream( "liveStream", $streamSrc, reScale( $monitor->Width(), $scale ), reScale( $monitor->Height(), $scale ), $monitor->Name() );
|
||||
return getImageStream( "liveStream", $streamSrc, reScale( $monitor->Width(), $scale ), reScale( $monitor->Height(), $scale ), $monitor->Name() );
|
||||
elseif ( canStreamApplet() )
|
||||
outputHelperStream( "liveStream", $streamSrc, reScale( $monitor->Width(), $scale ), reScale( $monitor->Height(), $scale ), $monitor->Name() );
|
||||
} else {
|
||||
|
|
|
@ -230,6 +230,7 @@ $SLANG = array(
|
|||
'ChooseLogFormat' => 'Choose a log format',
|
||||
'ChooseLogSelection' => 'Choose a log selection',
|
||||
'ChoosePreset' => 'Choose Preset',
|
||||
'CloneMonitor' => 'Clone Monitor',
|
||||
'Close' => 'Close',
|
||||
'Colour' => 'Colour',
|
||||
'Command' => 'Command',
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
|
||||
#menuBar1 {
|
||||
width: 100%;
|
||||
height: 1.5em;
|
||||
padding: 3px 0;
|
||||
text-align: center;
|
||||
clear: both;
|
||||
|
@ -41,7 +40,6 @@
|
|||
|
||||
#menuBar2 {
|
||||
width: 100%;
|
||||
height: 1.2em;
|
||||
padding: 3px 0;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
@ -57,6 +55,16 @@
|
|||
text-align: right;
|
||||
}
|
||||
|
||||
#menuBar1:after,
|
||||
#menuBar2:after {
|
||||
content: ".";
|
||||
display: block;
|
||||
height: 0;
|
||||
font-size: 0;
|
||||
clear: both;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#imageFeed {
|
||||
text-align: center;
|
||||
}
|
||||
|
|
|
@ -15,12 +15,12 @@
|
|||
|
||||
#menuBar1 {
|
||||
width: 100%;
|
||||
height: 1.5em;
|
||||
padding: 3px 0;
|
||||
text-align: center;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
|
||||
#menuBar1 #nameControl {
|
||||
float: left;
|
||||
}
|
||||
|
@ -41,7 +41,6 @@
|
|||
|
||||
#menuBar2 {
|
||||
width: 100%;
|
||||
height: 1.2em;
|
||||
padding: 3px 0;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
@ -56,6 +55,15 @@
|
|||
float: right;
|
||||
text-align: right;
|
||||
}
|
||||
#menuBar1:after,
|
||||
#menuBar2:after {
|
||||
content: ".";
|
||||
display: block;
|
||||
height: 0;
|
||||
font-size: 0;
|
||||
clear: both;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#imageFeed {
|
||||
text-align: center;
|
||||
|
|
|
@ -30,7 +30,7 @@ function getControlCommands( $monitor )
|
|||
$cmds['PresetGoto'] = "presetGoto";
|
||||
$cmds['PresetHome'] = "presetHome";
|
||||
|
||||
if ( !empty($monitor->CanZoom) )
|
||||
if ( !empty($monitor->CanZoom()) )
|
||||
{
|
||||
if ( $monitor->CanZoomCon() )
|
||||
$cmds['ZoomRoot'] = "zoomCon";
|
||||
|
@ -45,7 +45,7 @@ function getControlCommands( $monitor )
|
|||
$cmds['ZoomMan'] = "zoomMan";
|
||||
}
|
||||
|
||||
if ( !empty($monitor->CanFocus) )
|
||||
if ( !empty($monitor->CanFocus()) )
|
||||
{
|
||||
if ( $monitor->CanFocusCon() )
|
||||
$cmds['FocusRoot'] = "focusCon";
|
||||
|
@ -60,7 +60,7 @@ function getControlCommands( $monitor )
|
|||
$cmds['FocusMan'] = "focusMan";
|
||||
}
|
||||
|
||||
if ( !empty($monitor->CanIris) )
|
||||
if ( !empty($monitor->CanIris()) )
|
||||
{
|
||||
if ( $monitor->CanIrisCon() )
|
||||
$cmds['IrisRoot'] = "irisCon";
|
||||
|
@ -75,7 +75,7 @@ function getControlCommands( $monitor )
|
|||
$cmds['IrisMan'] = "irisMan";
|
||||
}
|
||||
|
||||
if ( !empty($monitor->CanWhite) )
|
||||
if ( !empty($monitor->CanWhite()) )
|
||||
{
|
||||
if ( $monitor->CanWhiteCon() )
|
||||
$cmds['WhiteRoot'] = "whiteCon";
|
||||
|
@ -89,7 +89,7 @@ function getControlCommands( $monitor )
|
|||
$cmds['WhiteMan'] = "whiteMan";
|
||||
}
|
||||
|
||||
if ( !empty($monitor->CanGain) )
|
||||
if ( !empty($monitor->CanGain()) )
|
||||
{
|
||||
if ( $monitor->CanGainCon() )
|
||||
$cmds['GainRoot'] = "gainCon";
|
||||
|
@ -103,7 +103,7 @@ function getControlCommands( $monitor )
|
|||
$cmds['GainMan'] = "gainMan";
|
||||
}
|
||||
|
||||
if ( !empty($monitor->CanMove) )
|
||||
if ( !empty($monitor->CanMove()) )
|
||||
{
|
||||
if ( $monitor->CanMoveCon() )
|
||||
{
|
||||
|
@ -246,9 +246,9 @@ function controlPanTilt( $monitor, $cmds )
|
|||
<div class="pantiltLabel"><?php echo translate('PanTilt') ?></div>
|
||||
<div class="pantiltButtons">
|
||||
<?php
|
||||
$hasPan = $monitor->CanPan;
|
||||
$hasTilt = $monitor->CanTilt;
|
||||
$hasDiag = $hasPan && $hasTilt && $monitor->CanMoveDiag;
|
||||
$hasPan = $monitor->CanPan();
|
||||
$hasTilt = $monitor->CanTilt();
|
||||
$hasDiag = $hasPan && $hasTilt && $monitor->CanMoveDiag();
|
||||
?>
|
||||
<div class="arrowBtn upLeftBtn<?php echo $hasDiag?'':' invisible' ?>" onclick="controlCmd('<?php echo $cmds['MoveUpLeft'] ?>',event,-1,-1)"></div>
|
||||
<div class="arrowBtn upBtn<?php echo $hasTilt?'':' invisible' ?>" onclick="controlCmd('<?php echo $cmds['MoveUp'] ?>',event,0,-1)"></div>
|
||||
|
@ -278,7 +278,7 @@ function controlPresets( $monitor, $cmds )
|
|||
$labels[$row['Preset']] = $row['Label'];
|
||||
}
|
||||
|
||||
$presetBreak = (int)(($monitor->NumPresets+1)/((int)(($monitor->NumPresets-1)/MAX_PRESETS)+1));
|
||||
$presetBreak = (int)(($monitor->NumPresets()+1)/((int)(($monitor->NumPresets()-1)/MAX_PRESETS)+1));
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
|
@ -286,7 +286,7 @@ function controlPresets( $monitor, $cmds )
|
|||
<!--<div><?php echo translate('Presets') ?></div>-->
|
||||
<div>
|
||||
<?php
|
||||
for ( $i = 1; $i <= $monitor->NumPresets; $i++ )
|
||||
for ( $i = 1; $i <= $monitor->NumPresets(); $i++ )
|
||||
{
|
||||
?><input type="button" class="ptzNumBtn" title="<?php echo isset($labels[$i])?$labels[$i]:"" ?>" value="<?php echo $i ?>" onclick="controlCmd('<?php echo $cmds['PresetGoto'] ?><?php echo $i ?>');"/><?php
|
||||
if ( $i && (($i%$presetBreak) == 0) )
|
||||
|
|
|
@ -589,7 +589,6 @@ else if (document.layers) window.onload=start_slider;
|
|||
|
||||
function exportEventImagesMaster( $eids )
|
||||
{
|
||||
global $SLANG;
|
||||
ob_start();
|
||||
exportHeader( translate('Images').' Master' );
|
||||
?>
|
||||
|
@ -599,9 +598,9 @@ function exportEventImagesMaster( $eids )
|
|||
foreach ($eids as $eid) {
|
||||
//get monitor id and event id
|
||||
$sql = 'SELECT E.MonitorId, E.StartTime, E.Id
|
||||
FROM Monitors AS M INNER JOIN Events AS E ON (M.Id = E.MonitorId)
|
||||
WHERE E.Id = ?
|
||||
';
|
||||
FROM Monitors AS M INNER JOIN Events AS E ON (M.Id = E.MonitorId)
|
||||
WHERE E.Id = ?
|
||||
';
|
||||
$event = dbFetchOne( $sql, NULL, array( $eid ) );
|
||||
$eventMonitorId[$eid] = $event['MonitorId'];
|
||||
$eventPath[$eid] = mygetEventPath( $event );
|
||||
|
|
|
@ -48,7 +48,7 @@ var popupSizes = {
|
|||
'log': { 'width': 1080, 'height': 720 },
|
||||
'login': { 'width': 720, 'height': 480 },
|
||||
'logout': { 'width': 260, 'height': 100 },
|
||||
'monitor': { 'width': 450, 'height': 440 },
|
||||
'monitor': { 'width': 700, 'height': 640 },
|
||||
'monitorpreset':{ 'width': 440, 'height': 200 },
|
||||
'monitorprobe': { 'width': 500, 'height': 240 },
|
||||
'monitorselect':{ 'width': 160, 'height': 200 },
|
||||
|
|
|
@ -48,7 +48,7 @@ var popupSizes = {
|
|||
'log': { 'width': 1080, 'height': 720 },
|
||||
'login': { 'width': 720, 'height': 480 },
|
||||
'logout': { 'width': 260, 'height': 150 },
|
||||
'monitor': { 'width': 600, 'height': 780 },
|
||||
'monitor': { 'width': 800, 'height': 780 },
|
||||
'monitorpreset':{ 'width': 440, 'height': 200 },
|
||||
'monitorprobe': { 'width': 500, 'height': 240 },
|
||||
'monitorselect':{ 'width': 160, 'height': 200 },
|
||||
|
|
|
@ -21,7 +21,7 @@ xhtmlHeaders( __FILE__, translate('Console') );
|
|||
<th class="colName"><?php echo translate('Name') ?></th>
|
||||
<th class="colFunction"><?php echo translate('Function') ?></th>
|
||||
<?php if ( count($servers) ) { ?>
|
||||
<th class="colServer"><?php echo translate('Server') ?></th>
|
||||
<th class="colServer"><?php echo translate('Server') ?></th>
|
||||
<?php } ?>
|
||||
<th class="colSource"><?php echo translate('Source') ?></th>
|
||||
<?php if ( $show_storage_areas ) { ?>
|
||||
|
@ -54,7 +54,9 @@ if ( canEdit('Monitors') )
|
|||
if ( $show_storage_areas ) { $columns += 1; }
|
||||
echo $columns;
|
||||
?>">
|
||||
<input type="button" class="btn btn-default" value="<?php echo translate('AddNewMonitor'); ?>" onclick="createPopup( '?view=monitor', 'zmMonitor0', 'monitor' ); return( false );"></input>
|
||||
<input type="button" name="addBtn" value="<?php echo translate('AddNewMonitor') ?>" onclick="addMonitor( this )"/>
|
||||
<!-- <?php echo makePopupButton( '?view=monitor', 'zmMonitor0', 'monitor', translate('AddNewMonitor'), (canEdit( 'Monitors' ) && !$user['MonitorIds']) ) ?> -->
|
||||
|
||||
</td>
|
||||
<?php
|
||||
for ( $i = 0; $i < count($eventCounts); $i++ )
|
||||
|
@ -80,7 +82,7 @@ foreach( $displayMonitors as $monitor )
|
|||
$dclass = "errorText";
|
||||
else
|
||||
{
|
||||
// https://github.com/ZoneMinder/ZoneMinder/issues/1082
|
||||
// https://github.com/ZoneMinder/ZoneMinder/issues/1082
|
||||
if ( !$monitor['zma'] && $monitor['Function']!='Monitor' )
|
||||
$dclass = "warnText";
|
||||
else
|
||||
|
@ -99,7 +101,7 @@ foreach( $displayMonitors as $monitor )
|
|||
<td class="colName"><?php echo makePopupLink( '?view=watch&mid='.$monitor['Id'], 'zmWatch'.$monitor['Id'], array( 'watch', reScale( $monitor['Width'], $scale ), reScale( $monitor['Height'], $scale ) ), $monitor['Name'], $running && ($monitor['Function'] != 'None') && canView( 'Stream' ) ) ?></td>
|
||||
<td class="colFunction"><?php echo makePopupLink( '?view=function&mid='.$monitor['Id'], 'zmFunction', 'function', '<span class="'.$fclass.'">'.translate('Fn'.$monitor['Function']).( empty($monitor['Enabled']) ? ', disabled' : '' ) .'</span>', canEdit( 'Monitors' ) ) ?></td>
|
||||
<?php if ( count($servers) ) { ?>
|
||||
<td class="colServer"><?php
|
||||
<td class="colServer"><?php
|
||||
$Server = new Server( $monitor['ServerId'] );
|
||||
echo $Server->Name();
|
||||
?></td>
|
||||
|
@ -113,9 +115,9 @@ echo $Server->Name();
|
|||
<?php } elseif ( $monitor['Type'] == "Ffmpeg" || $monitor['Type'] == "Libvlc" ) {
|
||||
$domain = parse_url( $monitor['Path'], PHP_URL_HOST );
|
||||
$shortpath = $domain ? $domain : preg_replace( '/^.*\//', '', $monitor['Path'] );
|
||||
if ( $shortpath == '' ) {
|
||||
$shortpath = 'Monitor ' . $monitor['Id'];
|
||||
}
|
||||
if ( $shortpath == '' ) {
|
||||
$shortpath = 'Monitor ' . $monitor['Id'];
|
||||
}
|
||||
?>
|
||||
<td class="colSource"><?php echo makePopupLink( '?view=monitor&mid='.$monitor['Id'], 'zmMonitor'.$monitor['Id'], 'monitor', '<span class="'.$dclass.'">'.$shortpath.'</span>', canEdit( 'Monitors' ) ) ?></td>
|
||||
<?php } elseif ( $monitor['Type'] == "cURL" ) { ?>
|
||||
|
|
|
@ -27,7 +27,7 @@ if ( !canView( 'Events' ) )
|
|||
$eid = validInt( $_REQUEST['eid'] );
|
||||
$fid = !empty($_REQUEST['fid'])?validInt($_REQUEST['fid']):1;
|
||||
|
||||
$sql = 'SELECT E.*,M.Name AS MonitorName,M.Width,M.Height,M.DefaultRate,M.DefaultScale,M.VideoWriter,M.SaveJPEGs,M.Orientation,M.LabelFormat FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE E.Id = ?';
|
||||
$sql = 'SELECT E.*,M.Name AS MonitorName,E.Width,E.Height,M.DefaultRate,M.DefaultScale,M.VideoWriter,M.SaveJPEGs,M.Orientation,M.LabelFormat FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE E.Id = ?';
|
||||
$sql_values = array( $eid );
|
||||
|
||||
if ( $user['MonitorIds'] ) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue