Merge branch 'storageareas' of github.com:ConnorTechnology/ZoneMinder into storageareas
commit
d629bd43a0
|
@ -801,7 +801,7 @@ endif(ZM_PERL_SEARCH_PATH)
|
|||
|
||||
# If this is an out-of-source build, copy the files we need to the binary directory
|
||||
if(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR))
|
||||
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/conf.d" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/conf.d" PATTERN "*.in" EXCLUDE)
|
||||
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/conf.d" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}" PATTERN "*.in" EXCLUDE)
|
||||
endif(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR))
|
||||
|
||||
# Generate files from the .in files
|
||||
|
|
3
INSTALL
3
INSTALL
|
@ -45,6 +45,9 @@ Possible configuration options:
|
|||
ZM_DB_NAME Name of ZoneMinder database, default: zm
|
||||
ZM_DB_USER Name of ZoneMinder database user, default: zmuser
|
||||
ZM_DB_PASS Password of ZoneMinder database user, default: zmpass
|
||||
ZM_DB_SSL_CA_CERT Path to SSL CA certificate, default: empty; SSL not enabled
|
||||
ZM_DB_SSL_CLIENT_KEY Path to SSL client key, default: empty; SSL not enabled
|
||||
ZM_DB_SSL_CLIENT_CERT Path to SSL client certificate, default: empty; SSL not enabled
|
||||
ZM_WEB_USER The user apache or the local web server runs on. Leave empty for automatic detection. If that fails, you can use this variable to force
|
||||
ZM_WEB_GROUP The group apache or the local web server runs on, Leave empty to be the same as the web user
|
||||
ZM_DIR_EVENTS Location where events are recorded to, default: ZM_CONTENTDIR/events
|
||||
|
|
|
@ -27,8 +27,8 @@ This is the recommended method to install ZoneMinder onto your system. ZoneMinde
|
|||
|
||||
- Ubuntu via [Iconnor's PPA](https://launchpad.net/~iconnor/+archive/ubuntu/zoneminder)
|
||||
- Debian from their [default repository](https://packages.debian.org/search?searchon=names&keywords=zoneminder)
|
||||
- RHEL/CentOS and clones via [zmrepo](http://zmrepo.zoneminder.com/)
|
||||
- Fedora via [zmrepo](http://zmrepo.zoneminder.com/)
|
||||
- RHEL/CentOS and clones via [RPM Fusion](http://rpmfusion.org)
|
||||
- Fedora via [RPM Fusion](http://rpmfusion.org)
|
||||
- OpenSuse via [third party repository](http://www.zoneminder.com/wiki/index.php/Installing_using_ZoneMinder_RPMs_for_SuSE)
|
||||
- Mageia from their default repository
|
||||
|
||||
|
|
|
@ -55,6 +55,9 @@ echo "Database host : $ZM_DB_HOST"
|
|||
echo "Database name : $ZM_DB_NAME"
|
||||
echo "Database user : $ZM_DB_USER"
|
||||
echo "Database password : Not shown"
|
||||
echo "Database SSL CA Cert : $ZM_DB_SSL_CA_CERT"
|
||||
echo "Database SSL Client Key : $ZM_DB_SSL_CLIENT_KEY"
|
||||
echo "Database SSL Client Cert : $ZM_DB_SSL_CLIENT_CERT"
|
||||
|
||||
|
||||
CMPATH="CACHE PATH \"Imported by cmakecacheimport.sh\" FORCE"
|
||||
|
@ -72,6 +75,9 @@ echo "set(ZM_DB_HOST \"$ZM_DB_HOST\" $CMSTRING)">>zm_conf.cmake
|
|||
echo "set(ZM_DB_NAME \"$ZM_DB_NAME\" $CMSTRING)">>zm_conf.cmake
|
||||
echo "set(ZM_DB_USER \"$ZM_DB_USER\" $CMSTRING)">>zm_conf.cmake
|
||||
echo "set(ZM_DB_PASS \"$ZM_DB_PASS\" $CMSTRING)">>zm_conf.cmake
|
||||
echo "set(ZM_DB_SSL_CA_CERT \"$ZM_DB_SSL_CA_CERT\" $CMSTRING)">>zm_conf.cmake
|
||||
echo "set(ZM_DB_SSL_CLIENT_KEY \"$ZM_DB_SSL_CLIENT_KEY\" $CMSTRING)">>zm_conf.cmake
|
||||
echo "set(ZM_DB_SSL_CLIENT_CERT \"$ZM_DB_SSL_CLIENT_CERT\" $CMSTRING)">>zm_conf.cmake
|
||||
|
||||
echo ""
|
||||
echo "Wrote zm_conf.cmake"
|
||||
|
|
|
@ -10,7 +10,7 @@ Background: RHEL, CentOS, and Clones
|
|||
|
||||
These distributions are classified as enterprise operating systems and have a long operating lifetime of many years. By design, they will not have the latest and greatest versions of any package. Instead, stable packages are the emphasis.
|
||||
|
||||
Replacing any core package in these distributions with a newer package from a third party is expressly verboten. The ZoneMinder development team will not do this, and neither should you. If you have the perception that you've got to have a newer version of mysql, gnome, apache, etc. then, rather than upgrade these packages, you should instead consider using a different distribution such as Fedora.
|
||||
Replacing any core package in these distributions with a newer package from a third party is expressly verboten. The ZoneMinder development team will not do this, and neither should you. If you have the perception that you've got to have a newer version of php, mysql, gnome, apache, etc. then, rather than upgrade these packages, you should instead consider using a different distribution such as Fedora.
|
||||
|
||||
The ZoneMinder team will not provide support for systems which have had any core package replaced with a package from a third party.
|
||||
|
||||
|
@ -23,39 +23,77 @@ Fedora has a short life-cycle of just 6 months. However, Fedora, and consequentl
|
|||
|
||||
If you desire newer packages than what is available in RHEL or CentOS, you should consider using Fedora.
|
||||
|
||||
Zmrepo – A ZoneMinder RPM Repository
|
||||
------------------------------------
|
||||
How To Avoid Known Installation Problems
|
||||
----------------------------------------
|
||||
|
||||
Zmrepo is a turn key solution. It will install all of ZoneMinder's dependencies for you. This is the easiest and the recommended way to install ZoneMinder on any system running a Redhat based distribution.
|
||||
The following notes are based on real problems which have occurred by those who came before you:
|
||||
|
||||
Zmrepo supports the two most recent, major releases of each Redhat based distro.
|
||||
|
||||
The following notes are based on real problems which have occurred:
|
||||
|
||||
- Zmrepo assumes you have installed the underlying distribution **using the official installation media for that distro**. Third party "Spins" are not supported and may not work correctly.
|
||||
- Zmrepo assumes you have installed the underlying distribution **using the official installation media for that distro**. Third party "Spins" may not work correctly.
|
||||
|
||||
- ZoneMinder is intended to be installed in an environment dedicated to ZoneMinder. While ZoneMinder will play well with many applications, some invariably will not. Asterisk is one such example.
|
||||
|
||||
- Be advised that you need to start with a clean system before using zmrepo.
|
||||
- Be advised that you need to start with a clean system before installing ZoneMinder.
|
||||
|
||||
- If you have previously installed ZoneMinder from-source, then your system is **NOT** clean. You must manually search for and delete all ZoneMinder related files before using zmrepo (look under /usr/local). Make uninstall helps, but it will not do this for you correctly. You **WILL** have problems if you ignore this step.
|
||||
- If you have previously installed ZoneMinder from-source, then your system is **NOT** clean. You must manually search for and delete all ZoneMinder related files first (look under /usr/local). Issuing a "make uninstall" helps, but it will not do this for you correctly. You **WILL** have problems if you ignore this step.
|
||||
|
||||
- It is not necessary, and not recommended, to install a LAMP stack ahead of time.
|
||||
- Unlike Debian/Ubuntu distros, it is not necessary, and not recommended, to install a LAMP stack ahead of time.
|
||||
|
||||
- Disable other third party repos and uninstall any of ZoneMinder's third party dependencies, which might already be on the system, especially ffmpeg and vlc. Attempting to install dependencies yourself often causes problems.
|
||||
- Disable any other third party repos and uninstall any of ZoneMinder's third party dependencies, which might already be on the system, especially ffmpeg and vlc. Attempting to install dependencies yourself often causes problems.
|
||||
|
||||
- Each ZoneMinder rpm includes a README file under /usr/share/doc. You must follow the all steps in this README file, precisely, each and every time ZoneMinder is installed or upgraded. **Failure to do so is guaranteed to result in a non-functional system.**
|
||||
- Each ZoneMinder rpm includes a README file under /usr/share/doc. You must follow all the steps in this README file, precisely, each and every time ZoneMinder is installed or upgraded. **Failure to do so is guaranteed to result in a non-functional system.**
|
||||
|
||||
To begin the installation of ZoneMinder on your Redhat based distro, please navigate to: http://zmrepo.zoneminder.com
|
||||
How to Install ZoneMinder
|
||||
-------------------------
|
||||
|
||||
How to Build a (Custom) ZoneMinder Package
|
||||
These instructions apply to all redhat distros and compatible clones, except for RHEL/CentOS 6.
|
||||
|
||||
ZoneMinder releases are now being hosted at RPM Fusion. New users should navigate the `RPM Fusion site <https://rpmfusion.org>`_ then follow the instructions to enable that repo. RHEL/CentOS users must also navaigate to the `EPEL Site <https://fedoraproject.org/wiki/EPEL>`_ and enable that repo as well. Once enabled, install ZoneMinder from the commandline:
|
||||
|
||||
::
|
||||
|
||||
sudo dnf install zoneminder
|
||||
|
||||
Note that RHEL/CentOS 7 users should use yum instead of dnf.
|
||||
|
||||
Once ZoneMinder has been installed, it is critically important that you read the README file under /usr/share/doc/zoneminder. ZoneMinder will not run without completing the steps outlined in the README.
|
||||
|
||||
How to Install ZoneMinder on RHEL/CentOS 6
|
||||
------------------------------------------
|
||||
|
||||
If you are looking to do development or the packages in zmrepo just don't suit you, then you should follow these steps to learn how to build your own ZoneMinder RPM.
|
||||
We continue to encounter build problems, caused by the age of this distro. It is unforuntate, but we can see the writing on the wall. We do not have a date set, but the end of the line for this distros is near.
|
||||
|
||||
Please be advised that we do not recommend any new ZoneMinder installations using CentOS 6. However, for the time being, ZoneMinder rpms will continue to be hosted at `zmrepo <https://www.zoneminder.com>`_.
|
||||
|
||||
How to Install Nightly Development Builds
|
||||
-----------------------------------------
|
||||
|
||||
ZoneMinder development packages, which represent the most recent build from our master branch, are available from `zmrepo <https://www.zoneminder.com>`_.
|
||||
|
||||
The feedback we get from those who use these development packages is extremely helpful. However, please understand these packages are intended for testing the latest master branch only. They are not intended to be used on any production system. There will be new bugs, and new features may not be documented. This is bleeding edge, and there might be breakage. Please keep that in mind when using this repo. We know from our user forum that this can't be stated enough.
|
||||
|
||||
How to Change from Zmrepo to RPM Fusion
|
||||
---------------------------------------
|
||||
|
||||
As mentioned above, the place to get the latest ZoneMinder release is now `RPM Fusion <https://rpmfusion.org>`_. If you are currently using ZoneMinder release packages from Zmrepo, then the following steps will change you over to RPM Fusion:
|
||||
|
||||
- Navigate to the `RPM Fusion site <https://rpmfusion.org>`_ and enable RPM Fusion on your system
|
||||
- Now issue the following from the command line:
|
||||
|
||||
::
|
||||
|
||||
sudo dnf remove zmrepo
|
||||
sudo dnf update
|
||||
|
||||
Note that RHEL/CentOS 7 users should use yum instead of dnf.
|
||||
|
||||
How to Build a Your Own ZoneMinder Package
|
||||
------------------------------------------
|
||||
|
||||
If you are looking to do development or the available packages just don't suit you, then you can follow these steps to build your own ZoneMinder RPM.
|
||||
|
||||
Background
|
||||
**********
|
||||
The following method documents how to build ZoneMinder into an RPM package, compatible with Fedora, Redhat, CentOS, and other compatible clones. This is exactly how the RPMS in zmrepo are built.
|
||||
The following method documents how to build ZoneMinder into an RPM package, for Fedora, Redhat, CentOS, and other compatible clones. This is exactly how the RPMS in zmrepo are built.
|
||||
|
||||
The method documented below was chosen because:
|
||||
|
||||
|
@ -67,22 +105,20 @@ The method documented below was chosen because:
|
|||
|
||||
- Troubleshooting becomes easier if we are all building ZoneMinder the same way.
|
||||
|
||||
The build instructions below make use of a custom script called "buildzm.sh". Advanced users are encouraged to view the contents of this script. Notice that the script doesn't really do a whole lot. The goal of the script is to simply make the process a little easier for the first time user. Once you become familar with the build process, you can issue the mock commands found in the buildzm.sh script yourself if you so desire.
|
||||
|
||||
***IMPORTANT***
|
||||
Certain commands in these instructions require root privileges while other commands do not. Pay close attention to this. If the instructions below state to issue a command without a “sudo” prefix, then you should *not* be root while issuing the command. Getting this incorrect will result in a failed build.
|
||||
Certain commands in these instructions require root privileges while other commands do not. Pay close attention to this. If the instructions below state to issue a command without a “sudo” prefix, then you should *not* be root while issuing the command. Getting this incorrect will result in a failed build, or worse a broken system.
|
||||
|
||||
Set Up Your Environment
|
||||
***********************
|
||||
Before you begin, set up an rpmbuild environment by following `this guide <http://wiki.centos.org/HowTos/SetupRpmBuildEnvironment>`_ by the CentOS developers.
|
||||
|
||||
Next, navigate to `Zmrepo <http://zmrepo.zoneminder.com/>`_, and follow the instructions to enable zmrepo on your system.
|
||||
In addition, make sure RPM Fusion is enabled as described in the previous section `How to Install ZoneMinder`_.
|
||||
|
||||
With zmrepo enabled, issue the following command:
|
||||
With RPM Fusion enabled, issue the following command:
|
||||
|
||||
::
|
||||
|
||||
sudo yum install zmrepo-mock-configs mock
|
||||
sudo yum install mock-rpmfusion-free mock
|
||||
|
||||
|
||||
Add your user account to the group mock:
|
||||
|
@ -96,73 +132,67 @@ Your build environment is now set up.
|
|||
|
||||
Build from SRPM
|
||||
***************
|
||||
To continue, you need a ZoneMinder SRPM. For starters, let's use one of the SRPMS from zmrepo. Go browse the `Zmrepo <http://zmrepo.zoneminder.com/>`_ site and choose an appropriate SRPM and place it into the ~/rpmbuild/SRPMS folder.
|
||||
To continue, you need a ZoneMinder SRPM. If you wish to rebuild a ZoneMinder release, then browse the `RPM Fusion site <http://zmrepo.zoneminder.com/>`_. If instead you wish to rebuild the latest source rpm from our master branch then browse the `Zmrepo site <http://zmrepo.zoneminder.com/>`_.
|
||||
|
||||
For CentOS 7, I have chosen the following SRPM:
|
||||
For this example, I'll use one of the source rpms from zmrepo:
|
||||
|
||||
::
|
||||
|
||||
wget -P ~/rpmbuild/SRPMS http://zmrepo.zoneminder.com/el/7/SRPMS/zoneminder-1.28.1-2.el7.centos.src.rpm
|
||||
wget -P ~/rpmbuild/SRPMS http://zmrepo.zoneminder.com/el/7/SRPMS/zoneminder-1.31.1-1.el7.centos.src.rpm
|
||||
|
||||
|
||||
Now comes the fun part. To build ZoneMinder, issue the following command:
|
||||
|
||||
::
|
||||
|
||||
buildzm.sh zmrepo-el7-x86_64 ~/rpmbuild/SRPMS/zoneminder-1.28.1-2.el7.centos.src.rpm
|
||||
mock -r epel-7-x86_64-rpmfusion_free ~/rpmbuild/SRPMS/zoneminder-1.31.1-1.el7.centos.src.rpm
|
||||
|
||||
|
||||
Want to build ZoneMinder for Fedora, instead of CentOS, from the same host? Once you download the Fedora SRPM, issue the following:
|
||||
|
||||
::
|
||||
|
||||
buildzm.sh zmrepo-f21-x86_64 ~/rpmbuild/SRPMS/zoneminder-1.28.1-1.fc21.src.rpm
|
||||
mock -r fedora-26-x86_64-rpmfusion_free ~/rpmbuild/SRPMS/zoneminder-1.31.1-1.el7.centos.src.rpm
|
||||
|
||||
Notice that the buildzm.sh tool requires the following parameters:
|
||||
Notice that the mock tool requires the following parameters:
|
||||
|
||||
::
|
||||
|
||||
buildzm.sh MOCKCONFIG ZONEMINDER_SRPM
|
||||
mock -r MOCKCONFIG ZONEMINDER_SRPM
|
||||
|
||||
The list of available Mock config files are available here:
|
||||
|
||||
::
|
||||
|
||||
ls /etc/mock/zmrepo*.cfg
|
||||
ls /etc/mock/*rpmfusion_free.cfg
|
||||
|
||||
|
||||
You choose the config file based on the desired distro (e.g. el6, el7, f20, f21) and basearch (e.g. x86, x86_64, arhmhfp). Notice that, when specifying the Mock config as a commandline parameter, you should leave off the ".cfg" filename extension.
|
||||
|
||||
Installation
|
||||
************
|
||||
Once the build completes, you will be presented with a folder containing the RPM's that were built. Copy the newly built ZoneMinder RPM to the desired system, enable zmrepo per the instruction on the `Zmrepo <http://zmrepo.zoneminder.com/>`_
|
||||
website, and then install the rpm by issuing the appropriate yum install command. Finish the installation by following the zoneminder setup instructions in the distro specific readme file, named README.{distroname}, which will be installed into the /usr/share/doc/zoneminder* folder.
|
||||
|
||||
Finally, you may want to consider editing the zmrepo repo file under /etc/yum.repos.d and placing an “exclude=zoneminder*” line into the config file. This will prevent your system from overwriting your manually built RPM with the ZoneMinder RPM found in the repo.
|
||||
|
||||
How to Modify the Source Prior to Build
|
||||
***************************************
|
||||
Before attempting this part of the instructions, make sure and follow the previous instructions for building one of the unmodified SRPMS from zmrepo. Knowing this part works will assist in troubleshooting should something go wrong.
|
||||
|
||||
These instructions may vary depending on what exactly you want to do. The following example assumes you want to build a development snapshot from the master branch.
|
||||
|
||||
From the previous instructions, we downloaded a CentOS 7 ZoneMinder SRPM and placed it into ~/rpmbuild/SRPMS. For this example, install it onto your system:
|
||||
Once the build completes, you will be presented with a message stating where the newly built rpms can be found. It will look similar to this:
|
||||
|
||||
::
|
||||
|
||||
rpm -ivh ~/rpmbuild/SRPMS/zoneminder-1.28.1-2.el7.centos.src.rpm
|
||||
INFO: Results and/or logs in: /var/lib/mock/fedora-26-x86_64/result
|
||||
|
||||
Copy the newly built ZoneMinder RPMs to the desired system, enable RPM Fusion as described in `How to Install ZoneMinder`_, and then install the rpm by issuing the appropriate yum/dnf install command. Finish the installation by following the zoneminder setup instructions in the distro specific readme file, named README.{distroname}, which will be installed into the /usr/share/doc/zoneminder* folder.
|
||||
|
||||
IMPORTANT: This operation must be done with your normal user account. Do *not* perform this command as root.
|
||||
Finally, you may want to consider editing the rpmfusion repo file under /etc/yum.repos.d and placing an “exclude=zoneminder*” line into the config file. This will prevent your system from overwriting your manually built RPM with the ZoneMinder RPM found in the repo.
|
||||
|
||||
Make sure you have git installed:
|
||||
How to Create Your Own Source RPM
|
||||
*********************************
|
||||
In the previous section we described how to rebuild an existing ZoneMinder SRPM. The instructions which follow show how to build the ZoneMinder git source tree into a source rpm, which can be used in the previous section to build an rpm.
|
||||
|
||||
Make sure git and rpmdevtools are installed:
|
||||
|
||||
::
|
||||
|
||||
sudo yum install git
|
||||
sudo yum install git rpmdevtools
|
||||
|
||||
|
||||
Now clone the ZoneMinder git repository:
|
||||
Now clone the ZoneMinder git repository from your home folder:
|
||||
|
||||
::
|
||||
|
||||
|
@ -170,32 +200,62 @@ Now clone the ZoneMinder git repository:
|
|||
git clone https://github.com/ZoneMinder/ZoneMinder
|
||||
cd ZoneMinder
|
||||
|
||||
This will create a sub-folder called ZoneMinder, which will contain the latest development.
|
||||
This will create a sub-folder called ZoneMinder, which will contain the latest development source code.
|
||||
|
||||
We want to turn this into a tarball, but first we need to figure out what to name it. Look here:
|
||||
If you have previsouly cloned the ZoneMinder git repo and wish to update it to the most recent, then issue these commands instead:
|
||||
|
||||
::
|
||||
|
||||
ls ~/rpmbuild/SOURCES
|
||||
|
||||
The tarball from the previsouly installed SRPM should be there. This is the name we will use. For this example, the name is ZoneMinder-1.28.1.tar.gz. From the root folder of the local ZoneMinder git repository, execute the following:
|
||||
cd ~\ZoneMinder
|
||||
git pull origin master
|
||||
|
||||
Get the crud submodule tarball:
|
||||
|
||||
::
|
||||
|
||||
git archive --prefix=ZoneMinder-1.28.1/ -o ~/rpmbuild/SOURCES/zoneminder-1.28.1.tar.gz HEAD
|
||||
spectool -f -g -R -s 1 ~/ZoneMinder/distros/redhat/zoneminder.spec
|
||||
|
||||
Note that we are overwriting the original tarball. If you wish to keep the original tarball then create a copy prior to creating the new tarball.
|
||||
At this point, you can make changes to the source code. Depending on what you want to do with those changes, you generally want to create a new branch first:
|
||||
|
||||
::
|
||||
|
||||
cd ~\ZoneMinder
|
||||
git checkout -b mynewbranch
|
||||
|
||||
Again, depending on what you want to do with those changes, you may want to commit your changes:
|
||||
|
||||
::
|
||||
|
||||
cd ~\ZoneMinder
|
||||
git add .
|
||||
git commit
|
||||
|
||||
Once you have made your changes, it is time to turn your work into a new tarball, but first we need to look in the rpm specfile:
|
||||
|
||||
::
|
||||
|
||||
less ~/ZoneMinder/distros/redhat/zoneminder.spec
|
||||
|
||||
Scroll down until you see the Version field. Note the value, which will be in the format x.xx.x. Now create the tarball with the following command:
|
||||
|
||||
::
|
||||
|
||||
cd ~\ZoneMinder
|
||||
git archive --prefix=ZoneMinder-1.31.1/ -o ~/rpmbuild/SOURCES/zoneminder-1.31.1.tar.gz HEAD
|
||||
|
||||
Replace "1.31.1" with the Version shown in the rpm specfile.
|
||||
|
||||
From the root of the local ZoneMinder git repo, execute the following:
|
||||
|
||||
::
|
||||
|
||||
cd ~\ZoneMinder
|
||||
rpmbuild -bs --nodeps distros/redhat/zoneminder.spec
|
||||
|
||||
Notice we used the rpm specfile that is part of the latest master branch you just downloaded, rather than the one that may be in your ~/rpmbbuild/SOURCES folder.
|
||||
|
||||
This step will overwrite the SRPM you originally downloaded, so you may want to back it up prior to completing this step. Note that the name of the specfile will vary slightly depending on the target distro.
|
||||
|
||||
You should now have a new SRPM under ~/rpmbuild/SRPMS. In our example, the SRPM is called zoneminder-1.28.1-2.el7.centos.src.rpm. Now follow the previous instructions that describe how to use the buildzm script, using ~/rpmbuild/SRPMS/zoneminder-1.28.1-2.el7.centos.src.rpm as the path to your SRPM.
|
||||
This step will create a source rpm and it will tell you where it was saved. For example:
|
||||
|
||||
::
|
||||
|
||||
Wrote: /home/abauer/rpmbuild/SRPMS/zoneminder-1.31.1-1.fc26.src.rpm
|
||||
|
||||
Now follow the previous instructions `Build from SRPM`_ which describe how to build that source rpm into an rpm.
|
||||
|
|
|
@ -101,8 +101,17 @@ BEGIN {
|
|||
} else {
|
||||
$socket = ";host=".$Config{ZM_DB_HOST};
|
||||
}
|
||||
my $sslOptions = "";
|
||||
if ( $Config{ZM_DB_SSL_CA_CERT} ) {
|
||||
$sslOptions = ';'.join(';',
|
||||
"mysql_ssl=1",
|
||||
"mysql_ssl_ca_file=".$Config{ZM_DB_SSL_CA_CERT},
|
||||
"mysql_ssl_client_key=".$Config{ZM_DB_SSL_CLIENT_KEY},
|
||||
"mysql_ssl_client_cert=".$Config{ZM_DB_SSL_CLIENT_CERT}
|
||||
);
|
||||
}
|
||||
my $dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
|
||||
.$socket
|
||||
.$socket.$sslOptions
|
||||
, $Config{ZM_DB_USER}
|
||||
, $Config{ZM_DB_PASS}
|
||||
) or croak( "Can't connect to db" );
|
||||
|
|
|
@ -90,8 +90,19 @@ sub zmDbConnect {
|
|||
} else {
|
||||
$socket = ";host=".$Config{ZM_DB_HOST};
|
||||
}
|
||||
|
||||
my $sslOptions = "";
|
||||
if ( $Config{ZM_DB_SSL_CA_CERT} ) {
|
||||
$sslOptions = ';'.join(';',
|
||||
"mysql_ssl=1",
|
||||
"mysql_ssl_ca_file=".$Config{ZM_DB_SSL_CA_CERT},
|
||||
"mysql_ssl_client_key=".$Config{ZM_DB_SSL_CLIENT_KEY},
|
||||
"mysql_ssl_client_cert=".$Config{ZM_DB_SSL_CLIENT_CERT}
|
||||
);
|
||||
}
|
||||
|
||||
$dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
|
||||
.$socket . ($options?';'.join(';', map { $_.'='.$$options{$_} } keys %{$options} ) : '' )
|
||||
.$socket . $sslOptions . ($options?';'.join(';', map { $_.'='.$$options{$_} } keys %{$options} ) : '' )
|
||||
, $Config{ZM_DB_USER}
|
||||
, $Config{ZM_DB_PASS}
|
||||
);
|
||||
|
|
|
@ -438,8 +438,17 @@ sub databaseLevel {
|
|||
} else {
|
||||
$socket = ";host=".$Config{ZM_DB_HOST};
|
||||
}
|
||||
my $sslOptions = "";
|
||||
if ( $Config{ZM_DB_SSL_CA_CERT} ) {
|
||||
$sslOptions = ';'.join(';',
|
||||
"mysql_ssl=1",
|
||||
"mysql_ssl_ca_file=".$Config{ZM_DB_SSL_CA_CERT},
|
||||
"mysql_ssl_client_key=".$Config{ZM_DB_SSL_CLIENT_KEY},
|
||||
"mysql_ssl_client_cert=".$Config{ZM_DB_SSL_CLIENT_CERT}
|
||||
);
|
||||
}
|
||||
$this->{dbh} = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
|
||||
.$socket
|
||||
.$socket.$sslOptions
|
||||
, $Config{ZM_DB_USER}
|
||||
, $Config{ZM_DB_PASS}
|
||||
);
|
||||
|
|
|
@ -153,6 +153,12 @@ void process_configfile( char* configFile) {
|
|||
staticConfig.DB_USER = std::string(val_ptr);
|
||||
else if ( strcasecmp( name_ptr, "ZM_DB_PASS" ) == 0 )
|
||||
staticConfig.DB_PASS = std::string(val_ptr);
|
||||
else if ( strcasecmp( name_ptr, "ZM_DB_SSL_CA_CERT" ) == 0 )
|
||||
staticConfig.DB_SSL_CA_CERT = std::string(val_ptr);
|
||||
else if ( strcasecmp( name_ptr, "ZM_DB_SSL_CLIENT_KEY" ) == 0 )
|
||||
staticConfig.DB_SSL_CLIENT_KEY = std::string(val_ptr);
|
||||
else if ( strcasecmp( name_ptr, "ZM_DB_SSL_CLIENT_CERT" ) == 0 )
|
||||
staticConfig.DB_SSL_CLIENT_CERT = std::string(val_ptr);
|
||||
else if ( strcasecmp( name_ptr, "ZM_PATH_WEB" ) == 0 )
|
||||
staticConfig.PATH_WEB = std::string(val_ptr);
|
||||
else if ( strcasecmp( name_ptr, "ZM_SERVER_HOST" ) == 0 )
|
||||
|
@ -288,8 +294,10 @@ Config::~Config() {
|
|||
if ( items ) {
|
||||
for ( int i = 0; i < n_items; i++ ) {
|
||||
delete items[i];
|
||||
items[i] = NULL;
|
||||
}
|
||||
delete[] items;
|
||||
items = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -67,6 +67,9 @@ struct StaticConfig
|
|||
std::string DB_NAME;
|
||||
std::string DB_USER;
|
||||
std::string DB_PASS;
|
||||
std::string DB_SSL_CA_CERT;
|
||||
std::string DB_SSL_CLIENT_KEY;
|
||||
std::string DB_SSL_CLIENT_CERT;
|
||||
std::string PATH_WEB;
|
||||
std::string SERVER_NAME;
|
||||
unsigned int SERVER_ID;
|
||||
|
|
|
@ -38,6 +38,8 @@ void zmDbConnect() {
|
|||
my_bool reconnect = 1;
|
||||
if ( mysql_options( &dbconn, MYSQL_OPT_RECONNECT, &reconnect ) )
|
||||
Fatal( "Can't set database auto reconnect option: %s", mysql_error( &dbconn ) );
|
||||
if ( !staticConfig.DB_SSL_CA_CERT.empty() )
|
||||
mysql_ssl_set( &dbconn, staticConfig.DB_SSL_CLIENT_KEY.c_str(), staticConfig.DB_SSL_CLIENT_CERT.c_str(), staticConfig.DB_SSL_CA_CERT.c_str(), NULL, NULL );
|
||||
std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":" );
|
||||
if ( colonIndex == std::string::npos ) {
|
||||
if ( !mysql_real_connect( &dbconn, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, 0, NULL, 0 ) ) {
|
||||
|
|
|
@ -115,13 +115,20 @@ int FfmpegCamera::PrimeCapture() {
|
|||
mAudioStreamId = -1;
|
||||
Info( "Priming capture from %s", mPath.c_str() );
|
||||
|
||||
#if THREAD
|
||||
if ( OpenFfmpeg() != 0 ) {
|
||||
ReopenFfmpeg();
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
return OpenFfmpeg();
|
||||
#endif
|
||||
}
|
||||
|
||||
int FfmpegCamera::PreCapture() {
|
||||
// If Reopen was called, then ffmpeg is closed and we need to reopen it.
|
||||
if ( ! mCanCapture )
|
||||
return OpenFfmpeg();
|
||||
// Nothing to do here
|
||||
return( 0 );
|
||||
}
|
||||
|
@ -165,9 +172,14 @@ int FfmpegCamera::Capture( Image &image ) {
|
|||
Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, avResult, errbuf );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
int keyframe = packet.flags & AV_PKT_FLAG_KEY;
|
||||
if ( keyframe )
|
||||
have_video_keyframe = true;
|
||||
|
||||
Debug( 5, "Got packet from stream %d dts (%d) pts(%d)", packet.stream_index, packet.pts, packet.dts );
|
||||
// What about audio stream? Maybe someday we could do sound detection...
|
||||
if ( packet.stream_index == mVideoStreamId ) {
|
||||
if ( ( packet.stream_index == mVideoStreamId ) && ( keyframe || have_video_keyframe ) ) {
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||
ret = avcodec_send_packet( mVideoCodecContext, &packet );
|
||||
if ( ret < 0 ) {
|
||||
|
@ -257,6 +269,7 @@ int FfmpegCamera::OpenFfmpeg() {
|
|||
|
||||
mOpenStart = time(NULL);
|
||||
mIsOpening = true;
|
||||
have_video_keyframe = false;
|
||||
|
||||
// Open the input, not necessarily a file
|
||||
#if !LIBAVFORMAT_VERSION_CHECK(53, 2, 0, 4, 0)
|
||||
|
@ -271,23 +284,30 @@ int FfmpegCamera::OpenFfmpeg() {
|
|||
}
|
||||
|
||||
// Set transport method as specified by method field, rtpUni is default
|
||||
if ( Method() == "rtpMulti" ) {
|
||||
const std::string method = Method();
|
||||
if ( method == "rtpMulti" ) {
|
||||
ret = av_dict_set(&opts, "rtsp_transport", "udp_multicast", 0);
|
||||
} else if ( Method() == "rtpRtsp" ) {
|
||||
} else if ( method == "rtpRtsp" ) {
|
||||
ret = av_dict_set(&opts, "rtsp_transport", "tcp", 0);
|
||||
} else if ( Method() == "rtpRtspHttp" ) {
|
||||
} else if ( method == "rtpRtspHttp" ) {
|
||||
ret = av_dict_set(&opts, "rtsp_transport", "http", 0);
|
||||
} else {
|
||||
Warning("Unknown method (%s)", method);
|
||||
}
|
||||
|
||||
if ( ret < 0 ) {
|
||||
Warning("Could not set rtsp_transport method '%s'\n", Method().c_str());
|
||||
Warning("Could not set rtsp_transport method '%s'\n", method.c_str());
|
||||
}
|
||||
|
||||
Debug ( 1, "Calling avformat_open_input" );
|
||||
Debug ( 1, "Calling avformat_open_input for %s", mPath.c_str() );
|
||||
|
||||
mFormatContext = avformat_alloc_context( );
|
||||
mFormatContext->interrupt_callback.callback = FfmpegInterruptCallback;
|
||||
mFormatContext->interrupt_callback.opaque = this;
|
||||
//mFormatContext->interrupt_callback.callback = FfmpegInterruptCallback;
|
||||
//mFormatContext->interrupt_callback.opaque = this;
|
||||
// Speed up find_stream_info
|
||||
//FIXME can speed up initial analysis but need sensible parameters...
|
||||
//mFormatContext->probesize = 32;
|
||||
//mFormatContext->max_analyze_duration = 32;
|
||||
|
||||
if ( avformat_open_input( &mFormatContext, mPath.c_str(), NULL, &opts ) != 0 )
|
||||
#endif
|
||||
|
@ -307,10 +327,6 @@ int FfmpegCamera::OpenFfmpeg() {
|
|||
|
||||
Info( "Stream open %s", mPath.c_str() );
|
||||
|
||||
//FIXME can speed up initial analysis but need sensible parameters...
|
||||
//mFormatContext->probesize = 32;
|
||||
//mFormatContext->max_analyze_duration = 32;
|
||||
// Locate stream info from avformat_open_input
|
||||
#if !LIBAVFORMAT_VERSION_CHECK(53, 6, 0, 6, 0)
|
||||
Debug ( 1, "Calling av_find_stream_info" );
|
||||
if ( av_find_stream_info( mFormatContext ) < 0 )
|
||||
|
@ -481,11 +497,17 @@ int FfmpegCamera::ReopenFfmpeg() {
|
|||
|
||||
Debug(2, "ReopenFfmpeg called.");
|
||||
|
||||
#if THREAD
|
||||
mCanCapture = false;
|
||||
if (pthread_create( &mReopenThread, NULL, ReopenFfmpegThreadCallback, (void*) this) != 0){
|
||||
if ( pthread_create( &mReopenThread, NULL, ReopenFfmpegThreadCallback, (void*) this) != 0 ) {
|
||||
// Log a fatal error and exit the process.
|
||||
Fatal( "ReopenFfmpeg failed to create worker thread." );
|
||||
}
|
||||
#else
|
||||
CloseFfmpeg();
|
||||
OpenFfmpeg();
|
||||
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -496,8 +518,14 @@ int FfmpegCamera::CloseFfmpeg() {
|
|||
|
||||
mCanCapture = false;
|
||||
|
||||
av_frame_free( &mFrame );
|
||||
av_frame_free( &mRawFrame );
|
||||
if ( mFrame ) {
|
||||
av_frame_free( &mFrame );
|
||||
mFrame = NULL;
|
||||
}
|
||||
if ( mRawFrame ) {
|
||||
av_frame_free( &mRawFrame );
|
||||
mRawFrame = NULL;
|
||||
}
|
||||
|
||||
#if HAVE_LIBSWSCALE
|
||||
if ( mConvertContext ) {
|
||||
|
@ -508,12 +536,12 @@ int FfmpegCamera::CloseFfmpeg() {
|
|||
|
||||
if ( mVideoCodecContext ) {
|
||||
avcodec_close(mVideoCodecContext);
|
||||
av_free(mVideoCodecContext);
|
||||
//av_free(mVideoCodecContext);
|
||||
mVideoCodecContext = NULL; // Freed by av_close_input_file
|
||||
}
|
||||
if ( mAudioCodecContext ) {
|
||||
avcodec_close(mAudioCodecContext);
|
||||
av_free(mAudioCodecContext);
|
||||
//av_free(mAudioCodecContext);
|
||||
mAudioCodecContext = NULL; // Freed by av_close_input_file
|
||||
}
|
||||
|
||||
|
@ -530,11 +558,12 @@ int FfmpegCamera::CloseFfmpeg() {
|
|||
}
|
||||
|
||||
int FfmpegCamera::FfmpegInterruptCallback(void *ctx) {
|
||||
Debug(3,"FfmpegInteruptCallback");
|
||||
FfmpegCamera* camera = reinterpret_cast<FfmpegCamera*>(ctx);
|
||||
if ( camera->mIsOpening ) {
|
||||
int now = time(NULL);
|
||||
if ( (now - camera->mOpenStart) > config.ffmpeg_open_timeout ) {
|
||||
Error ( "Open video took more than %d seconds.", config.ffmpeg_open_timeout );
|
||||
Error( "Open video took more than %d seconds.", config.ffmpeg_open_timeout );
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -543,11 +572,12 @@ int FfmpegCamera::FfmpegInterruptCallback(void *ctx) {
|
|||
}
|
||||
|
||||
void *FfmpegCamera::ReopenFfmpegThreadCallback(void *ctx){
|
||||
if (ctx == NULL) return NULL;
|
||||
Debug(3,"FfmpegReopenThreadtCallback");
|
||||
if ( ctx == NULL ) return NULL;
|
||||
|
||||
FfmpegCamera* camera = reinterpret_cast<FfmpegCamera*>(ctx);
|
||||
|
||||
while (1){
|
||||
while (1) {
|
||||
// Close current stream.
|
||||
camera->CloseFfmpeg();
|
||||
|
||||
|
@ -611,11 +641,11 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
|
|||
return( -1 );
|
||||
}
|
||||
|
||||
int key_frame = packet.flags & AV_PKT_FLAG_KEY;
|
||||
int keyframe = packet.flags & AV_PKT_FLAG_KEY;
|
||||
|
||||
Debug( 4, "Got packet from stream %d packet pts (%d) dts(%d), key?(%d)",
|
||||
Debug( 4, "Got packet from stream %d packet pts (%u) dts(%u), key?(%d)",
|
||||
packet.stream_index, packet.pts, packet.dts,
|
||||
key_frame
|
||||
keyframe
|
||||
);
|
||||
|
||||
//Video recording
|
||||
|
@ -722,7 +752,7 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
|
|||
// Buffer video packets, since we are not recording.
|
||||
// All audio packets are keyframes, so only if it's a video keyframe
|
||||
if ( packet.stream_index == mVideoStreamId ) {
|
||||
if ( key_frame ) {
|
||||
if ( keyframe ) {
|
||||
Debug(3, "Clearing queue");
|
||||
packetqueue.clearQueue( monitor->GetPreEventCount(), mVideoStreamId );
|
||||
}
|
||||
|
@ -745,81 +775,86 @@ else if ( packet.pts && video_last_pts > packet.pts ) {
|
|||
packetqueue.queuePacket( &packet );
|
||||
}
|
||||
} else if ( packet.stream_index == mVideoStreamId ) {
|
||||
if ( key_frame || packetqueue.size() ) // it's a keyframe or we already have something in the queue
|
||||
if ( keyframe || packetqueue.size() ) // it's a keyframe or we already have something in the queue
|
||||
packetqueue.queuePacket( &packet );
|
||||
}
|
||||
} // end if recording or not
|
||||
|
||||
if ( packet.stream_index == mVideoStreamId ) {
|
||||
if ( videoStore && ( have_video_keyframe || key_frame ) ) {
|
||||
|
||||
//Write the packet to our video store
|
||||
int ret = videoStore->writeVideoFramePacket( &packet );
|
||||
if ( ret < 0 ) { //Less than zero and we skipped a frame
|
||||
zm_av_packet_unref( &packet );
|
||||
return 0;
|
||||
// only do decode if we have had a keyframe, should save a few cycles.
|
||||
if ( have_video_keyframe || keyframe ) {
|
||||
|
||||
if ( videoStore ) {
|
||||
|
||||
//Write the packet to our video store
|
||||
int ret = videoStore->writeVideoFramePacket( &packet );
|
||||
if ( ret < 0 ) { //Less than zero and we skipped a frame
|
||||
zm_av_packet_unref( &packet );
|
||||
return 0;
|
||||
}
|
||||
have_video_keyframe = true;
|
||||
}
|
||||
have_video_keyframe = true;
|
||||
}
|
||||
Debug(4, "about to decode video" );
|
||||
|
||||
} // end if keyframe or have_video_keyframe
|
||||
|
||||
Debug(4, "about to decode video" );
|
||||
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||
ret = avcodec_send_packet( mVideoCodecContext, &packet );
|
||||
if ( ret < 0 ) {
|
||||
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||
Error( "Unable to send packet at frame %d: %s, continuing", frameCount, errbuf );
|
||||
zm_av_packet_unref( &packet );
|
||||
continue;
|
||||
}
|
||||
ret = avcodec_receive_frame( mVideoCodecContext, mRawFrame );
|
||||
if ( ret < 0 ) {
|
||||
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||
Debug( 1, "Unable to send packet at frame %d: %s, continuing", frameCount, errbuf );
|
||||
zm_av_packet_unref( &packet );
|
||||
continue;
|
||||
}
|
||||
frameComplete = 1;
|
||||
# else
|
||||
ret = zm_avcodec_decode_video( mVideoCodecContext, mRawFrame, &frameComplete, &packet );
|
||||
if ( ret < 0 ) {
|
||||
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||
Error( "Unable to decode frame at frame %d: %s, continuing", frameCount, errbuf );
|
||||
zm_av_packet_unref( &packet );
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
Debug( 4, "Decoded video packet at frame %d", frameCount );
|
||||
|
||||
if ( frameComplete ) {
|
||||
Debug( 4, "Got frame %d", frameCount );
|
||||
|
||||
uint8_t* directbuffer;
|
||||
|
||||
/* Request a writeable buffer of the target image */
|
||||
directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
|
||||
if ( directbuffer == NULL ) {
|
||||
Error("Failed requesting writeable buffer for the captured image.");
|
||||
ret = avcodec_send_packet( mVideoCodecContext, &packet );
|
||||
if ( ret < 0 ) {
|
||||
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||
Error( "Unable to send packet at frame %d: %s, continuing", frameCount, errbuf );
|
||||
zm_av_packet_unref( &packet );
|
||||
return (-1);
|
||||
continue;
|
||||
}
|
||||
ret = avcodec_receive_frame( mVideoCodecContext, mRawFrame );
|
||||
if ( ret < 0 ) {
|
||||
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||
Debug( 1, "Unable to send packet at frame %d: %s, continuing", frameCount, errbuf );
|
||||
zm_av_packet_unref( &packet );
|
||||
continue;
|
||||
}
|
||||
frameComplete = 1;
|
||||
# else
|
||||
ret = zm_avcodec_decode_video( mVideoCodecContext, mRawFrame, &frameComplete, &packet );
|
||||
if ( ret < 0 ) {
|
||||
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||
Error( "Unable to decode frame at frame %d: %s, continuing", frameCount, errbuf );
|
||||
zm_av_packet_unref( &packet );
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
Debug( 4, "Decoded video packet at frame %d", frameCount );
|
||||
|
||||
if ( frameComplete ) {
|
||||
Debug( 4, "Got frame %d", frameCount );
|
||||
|
||||
uint8_t* directbuffer;
|
||||
|
||||
/* Request a writeable buffer of the target image */
|
||||
directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
|
||||
if ( directbuffer == NULL ) {
|
||||
Error("Failed requesting writeable buffer for the captured image.");
|
||||
zm_av_packet_unref( &packet );
|
||||
return (-1);
|
||||
}
|
||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||
av_image_fill_arrays(mFrame->data, mFrame->linesize, directbuffer, imagePixFormat, width, height, 1);
|
||||
av_image_fill_arrays(mFrame->data, mFrame->linesize, directbuffer, imagePixFormat, width, height, 1);
|
||||
#else
|
||||
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height);
|
||||
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height);
|
||||
#endif
|
||||
|
||||
|
||||
if (sws_scale(mConvertContext, mRawFrame->data, mRawFrame->linesize,
|
||||
0, mVideoCodecContext->height, mFrame->data, mFrame->linesize) < 0) {
|
||||
Fatal("Unable to convert raw format %u to target format %u at frame %d",
|
||||
if (sws_scale(mConvertContext, mRawFrame->data, mRawFrame->linesize,
|
||||
0, mVideoCodecContext->height, mFrame->data, mFrame->linesize) < 0) {
|
||||
Fatal("Unable to convert raw format %u to target format %u at frame %d",
|
||||
mVideoCodecContext->pix_fmt, imagePixFormat, frameCount);
|
||||
}
|
||||
}
|
||||
|
||||
frameCount++;
|
||||
} else {
|
||||
Debug( 3, "Not framecomplete after av_read_frame" );
|
||||
} // end if frameComplete
|
||||
frameCount++;
|
||||
} else {
|
||||
Debug( 3, "Not framecomplete after av_read_frame" );
|
||||
} // end if frameComplete
|
||||
} else if ( packet.stream_index == mAudioStreamId ) { //FIXME best way to copy all other streams
|
||||
if ( videoStore ) {
|
||||
if ( record_audio ) {
|
||||
|
@ -834,7 +869,7 @@ else if ( packet.pts && video_last_pts > packet.pts ) {
|
|||
return 0;
|
||||
}
|
||||
} else {
|
||||
Debug(3, "Not recording audio because we don't have a bvideo keyframe yet");
|
||||
Debug(3, "Not recording audio yet because we don't have a video keyframe yet");
|
||||
}
|
||||
} else {
|
||||
Debug(4, "Not doing recording of audio packet" );
|
||||
|
@ -848,7 +883,7 @@ else if ( packet.pts && video_last_pts > packet.pts ) {
|
|||
#else
|
||||
Debug( 3, "Some other stream index %d", packet.stream_index );
|
||||
#endif
|
||||
}
|
||||
} // end if is video or audio or something else
|
||||
|
||||
// the packet contents are ref counted... when queuing, we allocate another packet and reference it with that one, so we should always need to unref here, which should not affect the queued version.
|
||||
zm_av_packet_unref( &packet );
|
||||
|
|
|
@ -107,8 +107,7 @@ Image::Image( const char *filename ) {
|
|||
text[0] = '\0';
|
||||
}
|
||||
|
||||
Image::Image( int p_width, int p_height, int p_colours, int p_subpixelorder, uint8_t *p_buffer )
|
||||
{
|
||||
Image::Image( int p_width, int p_height, int p_colours, int p_subpixelorder, uint8_t *p_buffer ) {
|
||||
if ( !initialised )
|
||||
Initialise();
|
||||
width = p_width;
|
||||
|
@ -119,21 +118,17 @@ Image::Image( int p_width, int p_height, int p_colours, int p_subpixelorder, uin
|
|||
size = pixels*colours;
|
||||
buffer = 0;
|
||||
holdbuffer = 0;
|
||||
if ( p_buffer )
|
||||
{
|
||||
if ( p_buffer ) {
|
||||
allocation = size;
|
||||
buffertype = ZM_BUFTYPE_DONTFREE;
|
||||
buffer = p_buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
AllocImgBuffer(size);
|
||||
}
|
||||
text[0] = '\0';
|
||||
}
|
||||
|
||||
Image::Image( const Image &p_image )
|
||||
{
|
||||
Image::Image( const Image &p_image ) {
|
||||
if ( !initialised )
|
||||
Initialise();
|
||||
width = p_image.width;
|
||||
|
@ -170,8 +165,7 @@ void Image::Deinitialise() {
|
|||
delete readjpg_dcinfo;
|
||||
readjpg_dcinfo = 0;
|
||||
}
|
||||
if ( decodejpg_dcinfo )
|
||||
{
|
||||
if ( decodejpg_dcinfo ) {
|
||||
jpeg_destroy_decompress( decodejpg_dcinfo );
|
||||
delete decodejpg_dcinfo;
|
||||
decodejpg_dcinfo = 0;
|
||||
|
@ -186,14 +180,13 @@ void Image::Deinitialise() {
|
|||
}
|
||||
}
|
||||
|
||||
void Image::Initialise()
|
||||
{
|
||||
void Image::Initialise() {
|
||||
/* Assign the blend pointer to function */
|
||||
if(config.fast_image_blends) {
|
||||
if(config.cpu_extensions && sseversion >= 20) {
|
||||
if ( config.fast_image_blends ) {
|
||||
if ( config.cpu_extensions && sseversion >= 20 ) {
|
||||
fptr_blend = &sse2_fastblend; /* SSE2 fast blend */
|
||||
Debug(4,"Blend: Using SSE2 fast blend function");
|
||||
} else if(config.cpu_extensions && neonversion >= 1) {
|
||||
} else if ( config.cpu_extensions && neonversion >= 1 ) {
|
||||
#if defined(__aarch64__)
|
||||
fptr_blend = &neon64_armv8_fastblend; /* ARM Neon (AArch64) fast blend */
|
||||
Debug(4,"Blend: Using ARM Neon (AArch64) fast blend function");
|
||||
|
@ -236,8 +229,8 @@ void Image::Initialise()
|
|||
(*fptr_blend)(blend1,blend2,blendres,128,12.0);
|
||||
|
||||
/* Compare results with expected results */
|
||||
for(int i=0;i<128;i++) {
|
||||
if(abs(blendexp[i] - blendres[i]) > 3) {
|
||||
for ( int i=0; i < 128; i ++ ) {
|
||||
if ( abs(blendexp[i] - blendres[i]) > 3 ) {
|
||||
Panic("Blend function failed self-test: Results differ from the expected results. Column %u Expected %u Got %u",i,blendexp[i],blendres[i]);
|
||||
}
|
||||
}
|
||||
|
@ -246,8 +239,8 @@ void Image::Initialise()
|
|||
fptr_delta8_bgr = &std_delta8_bgr;
|
||||
|
||||
/* Assign the delta functions */
|
||||
if(config.cpu_extensions) {
|
||||
if(sseversion >= 35) {
|
||||
if ( config.cpu_extensions ) {
|
||||
if ( sseversion >= 35 ) {
|
||||
/* SSSE3 available */
|
||||
fptr_delta8_rgba = &ssse3_delta8_rgba;
|
||||
fptr_delta8_bgra = &ssse3_delta8_bgra;
|
||||
|
@ -255,7 +248,7 @@ void Image::Initialise()
|
|||
fptr_delta8_abgr = &ssse3_delta8_abgr;
|
||||
fptr_delta8_gray8 = &sse2_delta8_gray8;
|
||||
Debug(4,"Delta: Using SSSE3 delta functions");
|
||||
} else if(sseversion >= 20) {
|
||||
} else if ( sseversion >= 20 ) {
|
||||
/* SSE2 available */
|
||||
fptr_delta8_rgba = &sse2_delta8_rgba;
|
||||
fptr_delta8_bgra = &sse2_delta8_bgra;
|
||||
|
@ -263,7 +256,7 @@ void Image::Initialise()
|
|||
fptr_delta8_abgr = &sse2_delta8_abgr;
|
||||
fptr_delta8_gray8 = &sse2_delta8_gray8;
|
||||
Debug(4,"Delta: Using SSE2 delta functions");
|
||||
} else if(neonversion >= 1) {
|
||||
} else if ( neonversion >= 1 ) {
|
||||
/* ARM Neon available */
|
||||
#if defined(__aarch64__)
|
||||
fptr_delta8_rgba = &neon64_armv8_delta8_rgba;
|
||||
|
@ -329,8 +322,8 @@ void Image::Initialise()
|
|||
(*fptr_delta8_gray8)(delta8_1,delta8_2,delta8_gray8_res,128);
|
||||
|
||||
/* Compare results with expected results */
|
||||
for(int i=0;i<128;i++) {
|
||||
if(abs(delta8_gray8_exp[i] - delta8_gray8_res[i]) > 7) {
|
||||
for ( int i=0; i < 128; i++ ) {
|
||||
if ( abs(delta8_gray8_exp[i] - delta8_gray8_res[i]) > 7 ) {
|
||||
Panic("Delta grayscale function failed self-test: Results differ from the expected results. Column %u Expected %u Got %u",i,delta8_gray8_exp[i],delta8_gray8_res[i]);
|
||||
}
|
||||
}
|
||||
|
@ -339,8 +332,8 @@ void Image::Initialise()
|
|||
(*fptr_delta8_rgba)(delta8_1,delta8_2,delta8_rgba_res,32);
|
||||
|
||||
/* Compare results with expected results */
|
||||
for(int i=0;i<32;i++) {
|
||||
if(abs(delta8_rgba_exp[i] - delta8_rgba_res[i]) > 7) {
|
||||
for ( int i=0; i < 32; i++ ) {
|
||||
if ( abs(delta8_rgba_exp[i] - delta8_rgba_res[i]) > 7 ) {
|
||||
Panic("Delta RGBA function failed self-test: Results differ from the expected results. Column %u Expected %u Got %u",i,delta8_rgba_exp[i],delta8_rgba_res[i]);
|
||||
}
|
||||
}
|
||||
|
@ -359,7 +352,7 @@ void Image::Initialise()
|
|||
|
||||
#if defined(__i386__) && !defined(__x86_64__)
|
||||
/* Use SSE2 aligned memory copy? */
|
||||
if(config.cpu_extensions && sseversion >= 20) {
|
||||
if ( config.cpu_extensions && sseversion >= 20 ) {
|
||||
fptr_imgbufcpy = &sse2_aligned_memcpy;
|
||||
Debug(4,"Image buffer copy: Using SSE2 aligned memcpy");
|
||||
} else {
|
||||
|
@ -425,24 +418,24 @@ void Image::Initialise()
|
|||
uint8_t* Image::WriteBuffer(const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder) {
|
||||
unsigned int newsize;
|
||||
|
||||
if(p_colours != ZM_COLOUR_GRAY8 && p_colours != ZM_COLOUR_RGB24 && p_colours != ZM_COLOUR_RGB32) {
|
||||
if ( p_colours != ZM_COLOUR_GRAY8 && p_colours != ZM_COLOUR_RGB24 && p_colours != ZM_COLOUR_RGB32 ) {
|
||||
Error("WriteBuffer called with unexpected colours: %d",p_colours);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(!p_height || !p_width) {
|
||||
if ( !p_height || !p_width ) {
|
||||
Error("WriteBuffer called with invalid width or height: %d %d",p_width,p_height);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(p_width != width || p_height != height || p_colours != colours || p_subpixelorder != subpixelorder) {
|
||||
if ( p_width != width || p_height != height || p_colours != colours || p_subpixelorder != subpixelorder ) {
|
||||
newsize = (p_width * p_height) * p_colours;
|
||||
|
||||
if(buffer == NULL) {
|
||||
if ( buffer == NULL ) {
|
||||
AllocImgBuffer(newsize);
|
||||
} else {
|
||||
if(allocation < newsize) {
|
||||
if(holdbuffer) {
|
||||
if ( allocation < newsize ) {
|
||||
if ( holdbuffer ) {
|
||||
Error("Held buffer is undersized for requested buffer");
|
||||
return NULL;
|
||||
} else {
|
||||
|
@ -467,30 +460,30 @@ uint8_t* Image::WriteBuffer(const unsigned int p_width, const unsigned int p_hei
|
|||
|
||||
/* Assign an existing buffer to the image instead of copying from a source buffer. The goal is to reduce the amount of memory copying and increase efficiency and buffer reusing. */
|
||||
void Image::AssignDirect( const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder, uint8_t *new_buffer, const size_t buffer_size, const int p_buffertype) {
|
||||
if(new_buffer == NULL) {
|
||||
if ( new_buffer == NULL ) {
|
||||
Error("Attempt to directly assign buffer from a NULL pointer");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!p_height || !p_width) {
|
||||
if ( !p_height || !p_width ) {
|
||||
Error("Attempt to directly assign buffer with invalid width or height: %d %d",p_width,p_height);
|
||||
return;
|
||||
}
|
||||
|
||||
if(p_colours != ZM_COLOUR_GRAY8 && p_colours != ZM_COLOUR_RGB24 && p_colours != ZM_COLOUR_RGB32) {
|
||||
if ( p_colours != ZM_COLOUR_GRAY8 && p_colours != ZM_COLOUR_RGB24 && p_colours != ZM_COLOUR_RGB32 ) {
|
||||
Error("Attempt to directly assign buffer with unexpected colours per pixel: %d",p_colours);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int new_buffer_size = ((p_width*p_height)*p_colours);
|
||||
|
||||
if(buffer_size < new_buffer_size) {
|
||||
if ( buffer_size < new_buffer_size ) {
|
||||
Error("Attempt to directly assign buffer from an undersized buffer of size: %zu, needed %dx%d*%d colours = %zu",buffer_size, p_width, p_height, p_colours, new_buffer_size );
|
||||
return;
|
||||
}
|
||||
|
||||
if(holdbuffer && buffer) {
|
||||
if(new_buffer_size > allocation) {
|
||||
if ( holdbuffer && buffer ) {
|
||||
if ( new_buffer_size > allocation ) {
|
||||
Error("Held buffer is undersized for assigned buffer");
|
||||
return;
|
||||
} else {
|
||||
|
@ -502,7 +495,7 @@ void Image::AssignDirect( const unsigned int p_width, const unsigned int p_heigh
|
|||
size = new_buffer_size; // was pixels*colours, but we already calculated it above as new_buffer_size
|
||||
|
||||
/* Copy into the held buffer */
|
||||
if(new_buffer != buffer)
|
||||
if ( new_buffer != buffer )
|
||||
(*fptr_imgbufcpy)(buffer, new_buffer, size);
|
||||
|
||||
/* Free the new buffer */
|
||||
|
@ -529,35 +522,35 @@ void Image::AssignDirect( const unsigned int p_width, const unsigned int p_heigh
|
|||
void Image::Assign(const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder, const uint8_t* new_buffer, const size_t buffer_size) {
|
||||
unsigned int new_size = (p_width * p_height) * p_colours;
|
||||
|
||||
if(new_buffer == NULL) {
|
||||
if ( new_buffer == NULL ) {
|
||||
Error("Attempt to assign buffer from a NULL pointer");
|
||||
return;
|
||||
}
|
||||
|
||||
if(buffer_size < new_size) {
|
||||
if ( buffer_size < new_size ) {
|
||||
Error("Attempt to assign buffer from an undersized buffer of size: %zu",buffer_size);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!p_height || !p_width) {
|
||||
if ( !p_height || !p_width ) {
|
||||
Error("Attempt to assign buffer with invalid width or height: %d %d",p_width,p_height);
|
||||
return;
|
||||
}
|
||||
|
||||
if(p_colours != ZM_COLOUR_GRAY8 && p_colours != ZM_COLOUR_RGB24 && p_colours != ZM_COLOUR_RGB32) {
|
||||
if ( p_colours != ZM_COLOUR_GRAY8 && p_colours != ZM_COLOUR_RGB24 && p_colours != ZM_COLOUR_RGB32 ) {
|
||||
Error("Attempt to assign buffer with unexpected colours per pixel: %d",p_colours);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !buffer || p_width != width || p_height != height || p_colours != colours || p_subpixelorder != subpixelorder) {
|
||||
if ( !buffer || p_width != width || p_height != height || p_colours != colours || p_subpixelorder != subpixelorder ) {
|
||||
|
||||
if (holdbuffer && buffer) {
|
||||
if (new_size > allocation) {
|
||||
if ( holdbuffer && buffer ) {
|
||||
if ( new_size > allocation ) {
|
||||
Error("Held buffer is undersized for assigned buffer");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if(new_size > allocation || !buffer) {
|
||||
if ( new_size > allocation || !buffer ) {
|
||||
DumpImgBuffer();
|
||||
AllocImgBuffer(new_size);
|
||||
}
|
||||
|
|
|
@ -55,23 +55,26 @@ extern imgbufcpy_fptr_t fptr_imgbufcpy;
|
|||
/* Should be called from Image class functions */
|
||||
inline static uint8_t* AllocBuffer(size_t p_bufsize) {
|
||||
uint8_t* buffer = (uint8_t*)zm_mallocaligned(64,p_bufsize);
|
||||
if(buffer == NULL)
|
||||
if ( buffer == NULL )
|
||||
Fatal("Memory allocation failed: %s",strerror(errno));
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
inline static void DumpBuffer(uint8_t* buffer, int buffertype) {
|
||||
if (buffer && buffertype != ZM_BUFTYPE_DONTFREE) {
|
||||
if(buffertype == ZM_BUFTYPE_ZM)
|
||||
zm_freealigned(buffer);
|
||||
else if(buffertype == ZM_BUFTYPE_MALLOC)
|
||||
free(buffer);
|
||||
else if(buffertype == ZM_BUFTYPE_NEW)
|
||||
delete buffer;
|
||||
if ( buffer && buffertype != ZM_BUFTYPE_DONTFREE ) {
|
||||
if ( buffertype == ZM_BUFTYPE_ZM ) {
|
||||
zm_freealigned(buffer);
|
||||
} else if ( buffertype == ZM_BUFTYPE_MALLOC ) {
|
||||
free(buffer);
|
||||
} else if ( buffertype == ZM_BUFTYPE_NEW ) {
|
||||
delete buffer;
|
||||
/*else if(buffertype == ZM_BUFTYPE_AVMALLOC)
|
||||
av_free(buffer);
|
||||
*/
|
||||
} else {
|
||||
Error( "Unknown buffer type in DumpBuffer(%d)", buffertype );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,27 +83,23 @@ inline static void DumpBuffer(uint8_t* buffer, int buffertype) {
|
|||
// This is image class, and represents a frame captured from a
|
||||
// camera in raw form.
|
||||
//
|
||||
class Image
|
||||
{
|
||||
class Image {
|
||||
protected:
|
||||
|
||||
struct Edge
|
||||
{
|
||||
struct Edge {
|
||||
int min_y;
|
||||
int max_y;
|
||||
double min_x;
|
||||
double _1_m;
|
||||
|
||||
static int CompareYX( const void *p1, const void *p2 )
|
||||
{
|
||||
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 )
|
||||
{
|
||||
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) );
|
||||
}
|
||||
|
@ -113,7 +112,7 @@ protected:
|
|||
}
|
||||
|
||||
inline void AllocImgBuffer(size_t p_bufsize) {
|
||||
if(buffer)
|
||||
if ( buffer )
|
||||
DumpImgBuffer();
|
||||
|
||||
buffer = AllocBuffer(p_bufsize);
|
||||
|
@ -176,27 +175,24 @@ public:
|
|||
inline void HoldBuffer(int tohold) { holdbuffer = tohold; }
|
||||
|
||||
inline void Empty() {
|
||||
if(!holdbuffer)
|
||||
DumpImgBuffer();
|
||||
|
||||
width = height = colours = size = pixels = subpixelorder = 0;
|
||||
if ( !holdbuffer )
|
||||
DumpImgBuffer();
|
||||
|
||||
width = height = colours = size = pixels = subpixelorder = 0;
|
||||
}
|
||||
|
||||
void Assign( unsigned int p_width, unsigned int p_height, unsigned int p_colours, unsigned int p_subpixelorder, const uint8_t* new_buffer, const size_t buffer_size);
|
||||
void Assign( const Image &image );
|
||||
void AssignDirect( const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder, uint8_t *new_buffer, const size_t buffer_size, const int p_buffertype);
|
||||
|
||||
inline void CopyBuffer( const Image &image )
|
||||
{
|
||||
inline void CopyBuffer( const Image &image ) {
|
||||
Assign(image);
|
||||
}
|
||||
inline Image &operator=( const Image &image )
|
||||
{
|
||||
inline Image &operator=( const Image &image ) {
|
||||
Assign(image);
|
||||
return *this;
|
||||
}
|
||||
inline Image &operator=( const unsigned char *new_buffer )
|
||||
{
|
||||
inline Image &operator=( const unsigned char *new_buffer ) {
|
||||
(*fptr_imgbufcpy)(buffer, new_buffer, size);
|
||||
return( *this );
|
||||
}
|
||||
|
@ -207,9 +203,9 @@ public:
|
|||
bool ReadJpeg( const char *filename, unsigned int p_colours, unsigned int p_subpixelorder);
|
||||
|
||||
bool WriteJpeg ( const char *filename) const;
|
||||
bool WriteJpeg ( const char *filename, int quality_override ) const;
|
||||
bool WriteJpeg ( const char *filename, struct timeval timestamp ) const;
|
||||
bool WriteJpeg ( const char *filename, int quality_override, struct timeval timestamp ) const;
|
||||
bool WriteJpeg ( const char *filename, int quality_override ) const;
|
||||
bool WriteJpeg ( const char *filename, struct timeval timestamp ) const;
|
||||
bool WriteJpeg ( const char *filename, int quality_override, struct timeval timestamp ) const;
|
||||
|
||||
bool DecodeJpeg( const JOCTET *inbuffer, int inbuffer_size, unsigned int p_colours, unsigned int p_subpixelorder);
|
||||
bool EncodeJpeg( JOCTET *outbuffer, int *outbuffer_size, int quality_override=0 ) const;
|
||||
|
@ -232,7 +228,7 @@ public:
|
|||
void Delta( const Image &image, Image* targetimage) const;
|
||||
|
||||
const Coord centreCoord( const char *text ) const;
|
||||
void MaskPrivacy( const unsigned char *p_bitmask, const Rgb pixel_colour=0x00222222 );
|
||||
void MaskPrivacy( const unsigned char *p_bitmask, const Rgb pixel_colour=0x00222222 );
|
||||
void Annotate( const char *p_text, const Coord &coord, const unsigned int size=1, const Rgb fg_colour=RGB_WHITE, const Rgb bg_colour=RGB_BLACK );
|
||||
Image *HighlightEdges( Rgb colour, unsigned int p_colours, unsigned int p_subpixelorder, const Box *limits=0 );
|
||||
//Image *HighlightEdges( Rgb colour, const Polygon &polygon );
|
||||
|
|
|
@ -112,6 +112,16 @@ Logger::Logger() :
|
|||
|
||||
Logger::~Logger() {
|
||||
terminate();
|
||||
smCodes.clear();
|
||||
smSyslogPriorities.clear();
|
||||
#if 0
|
||||
for ( StringMap::iterator itr = smCodes.begin(); itr != smCodes.end(); itr ++ ) {
|
||||
smCodes.erase( itr );
|
||||
}
|
||||
for ( IntMap::iterator itr = smSyslogPriorities.begin(); itr != smSyslogPriorities.end(); itr ++ ) {
|
||||
smSyslogPriorities.erase(itr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Logger::initialise( const std::string &id, const Options &options ) {
|
||||
|
@ -414,124 +424,128 @@ void Logger::closeSyslog() {
|
|||
}
|
||||
|
||||
void Logger::logPrint( bool hex, const char * const filepath, const int line, const int level, const char *fstring, ... ) {
|
||||
if ( level <= mEffectiveLevel ) {
|
||||
char timeString[64];
|
||||
char logString[8192];
|
||||
va_list argPtr;
|
||||
struct timeval timeVal;
|
||||
if ( level > mEffectiveLevel )
|
||||
return;
|
||||
char timeString[64];
|
||||
char logString[8192];
|
||||
va_list argPtr;
|
||||
struct timeval timeVal;
|
||||
|
||||
char *filecopy = strdup(filepath);
|
||||
const char * const file = basename(filecopy);
|
||||
const char *classString = smCodes[level].c_str();
|
||||
char *filecopy = strdup(filepath);
|
||||
const char * const file = basename(filecopy);
|
||||
const char *classString = smCodes[level].c_str();
|
||||
|
||||
if ( level < PANIC || level > DEBUG9 )
|
||||
Panic( "Invalid logger level %d", level );
|
||||
if ( level < PANIC || level > DEBUG9 )
|
||||
Panic( "Invalid logger level %d", level );
|
||||
|
||||
gettimeofday( &timeVal, NULL );
|
||||
gettimeofday( &timeVal, NULL );
|
||||
|
||||
#if 0
|
||||
if ( logRuntime ) {
|
||||
static struct timeval logStart;
|
||||
if ( logRuntime ) {
|
||||
static struct timeval logStart;
|
||||
|
||||
subtractTime( &timeVal, &logStart );
|
||||
subtractTime( &timeVal, &logStart );
|
||||
|
||||
snprintf( timeString, sizeof(timeString), "%ld.%03ld", timeVal.tv_sec, timeVal.tv_usec/1000 );
|
||||
} else {
|
||||
snprintf( timeString, sizeof(timeString), "%ld.%03ld", timeVal.tv_sec, timeVal.tv_usec/1000 );
|
||||
} else {
|
||||
#endif
|
||||
char *timePtr = timeString;
|
||||
timePtr += strftime( timePtr, sizeof(timeString), "%x %H:%M:%S", localtime(&timeVal.tv_sec) );
|
||||
snprintf( timePtr, sizeof(timeString)-(timePtr-timeString), ".%06ld", timeVal.tv_usec );
|
||||
char *timePtr = timeString;
|
||||
timePtr += strftime( timePtr, sizeof(timeString), "%x %H:%M:%S", localtime(&timeVal.tv_sec) );
|
||||
snprintf( timePtr, sizeof(timeString)-(timePtr-timeString), ".%06ld", timeVal.tv_usec );
|
||||
#if 0
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
pid_t tid;
|
||||
pid_t tid;
|
||||
#ifdef __FreeBSD__
|
||||
long lwpid;
|
||||
thr_self(&lwpid);
|
||||
tid = lwpid;
|
||||
long lwpid;
|
||||
thr_self(&lwpid);
|
||||
tid = lwpid;
|
||||
|
||||
if (tid < 0 ) // Thread/Process id
|
||||
if (tid < 0 ) // Thread/Process id
|
||||
#else
|
||||
#ifdef HAVE_SYSCALL
|
||||
#ifdef __FreeBSD_kernel__
|
||||
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
||||
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
||||
|
||||
# else
|
||||
// SOLARIS doesn't have SYS_gettid; don't assume
|
||||
// SOLARIS doesn't have SYS_gettid; don't assume
|
||||
#ifdef SYS_gettid
|
||||
if ( (tid = syscall(SYS_gettid)) < 0 ) // Thread/Process id
|
||||
if ( (tid = syscall(SYS_gettid)) < 0 ) // Thread/Process id
|
||||
#endif // SYS_gettid
|
||||
#endif
|
||||
#endif // HAVE_SYSCALL
|
||||
#endif
|
||||
tid = getpid(); // Process id
|
||||
tid = getpid(); // Process id
|
||||
|
||||
char *logPtr = logString;
|
||||
logPtr += snprintf( logPtr, sizeof(logString), "%s %s[%d].%s-%s/%d [",
|
||||
timeString,
|
||||
mId.c_str(),
|
||||
tid,
|
||||
classString,
|
||||
file,
|
||||
line
|
||||
);
|
||||
char *syslogStart = logPtr;
|
||||
char *logPtr = logString;
|
||||
logPtr += snprintf( logPtr, sizeof(logString), "%s %s[%d].%s-%s/%d [",
|
||||
timeString,
|
||||
mId.c_str(),
|
||||
tid,
|
||||
classString,
|
||||
file,
|
||||
line
|
||||
);
|
||||
char *syslogStart = logPtr;
|
||||
|
||||
va_start( argPtr, fstring );
|
||||
if ( hex ) {
|
||||
unsigned char *data = va_arg( argPtr, unsigned char * );
|
||||
int len = va_arg( argPtr, int );
|
||||
int i;
|
||||
logPtr += snprintf( logPtr, sizeof(logString)-(logPtr-logString), "%d:", len );
|
||||
for ( i = 0; i < len; i++ ) {
|
||||
logPtr += snprintf( logPtr, sizeof(logString)-(logPtr-logString), " %02x", data[i] );
|
||||
}
|
||||
} else {
|
||||
logPtr += vsnprintf( logPtr, sizeof(logString)-(logPtr-logString), fstring, argPtr );
|
||||
va_start( argPtr, fstring );
|
||||
if ( hex ) {
|
||||
unsigned char *data = va_arg( argPtr, unsigned char * );
|
||||
int len = va_arg( argPtr, int );
|
||||
int i;
|
||||
logPtr += snprintf( logPtr, sizeof(logString)-(logPtr-logString), "%d:", len );
|
||||
for ( i = 0; i < len; i++ ) {
|
||||
logPtr += snprintf( logPtr, sizeof(logString)-(logPtr-logString), " %02x", data[i] );
|
||||
}
|
||||
va_end(argPtr);
|
||||
char *syslogEnd = logPtr;
|
||||
strncpy( logPtr, "]\n", sizeof(logString)-(logPtr-logString) );
|
||||
} else {
|
||||
logPtr += vsnprintf( logPtr, sizeof(logString)-(logPtr-logString), fstring, argPtr );
|
||||
}
|
||||
va_end(argPtr);
|
||||
char *syslogEnd = logPtr;
|
||||
strncpy( logPtr, "]\n", sizeof(logString)-(logPtr-logString) );
|
||||
|
||||
if ( level <= mTermLevel ) {
|
||||
printf( "%s", logString );
|
||||
fflush( stdout );
|
||||
}
|
||||
if ( level <= mFileLevel ) {
|
||||
fprintf( mLogFileFP, "%s", logString );
|
||||
if ( level <= mTermLevel ) {
|
||||
puts( logString );
|
||||
fflush( stdout );
|
||||
}
|
||||
if ( level <= mFileLevel ) {
|
||||
if ( mLogFileFP ) {
|
||||
fputs( logString, mLogFileFP );
|
||||
if ( mFlush )
|
||||
fflush( mLogFileFP );
|
||||
} else {
|
||||
puts("Logging to file, but file not open\n");
|
||||
}
|
||||
*syslogEnd = '\0';
|
||||
if ( level <= mDatabaseLevel ) {
|
||||
char sql[ZM_SQL_MED_BUFSIZ];
|
||||
char escapedString[(strlen(syslogStart)*2)+1];
|
||||
}
|
||||
*syslogEnd = '\0';
|
||||
if ( level <= mDatabaseLevel ) {
|
||||
char sql[ZM_SQL_MED_BUFSIZ];
|
||||
char escapedString[(strlen(syslogStart)*2)+1];
|
||||
|
||||
mysql_real_escape_string( &dbconn, escapedString, syslogStart, strlen(syslogStart) );
|
||||
mysql_real_escape_string( &dbconn, escapedString, syslogStart, strlen(syslogStart) );
|
||||
|
||||
snprintf( sql, sizeof(sql), "insert into Logs ( TimeKey, Component, ServerId, Pid, Level, Code, Message, File, Line ) values ( %ld.%06ld, '%s', %d, %d, %d, '%s', '%s', '%s', %d )", timeVal.tv_sec, timeVal.tv_usec, mId.c_str(), staticConfig.SERVER_ID, tid, level, classString, escapedString, file, line );
|
||||
if ( mysql_query( &dbconn, sql ) ) {
|
||||
Level tempDatabaseLevel = mDatabaseLevel;
|
||||
databaseLevel( NOLOG );
|
||||
Error( "Can't insert log entry: sql(%s) error(%s)", sql, mysql_error( &dbconn ) );
|
||||
databaseLevel(tempDatabaseLevel);
|
||||
}
|
||||
}
|
||||
if ( level <= mSyslogLevel ) {
|
||||
int priority = smSyslogPriorities[level];
|
||||
//priority |= LOG_DAEMON;
|
||||
syslog( priority, "%s [%s] [%s]", classString, mId.c_str(), syslogStart );
|
||||
snprintf( sql, sizeof(sql), "insert into Logs ( TimeKey, Component, ServerId, Pid, Level, Code, Message, File, Line ) values ( %ld.%06ld, '%s', %d, %d, %d, '%s', '%s', '%s', %d )", timeVal.tv_sec, timeVal.tv_usec, mId.c_str(), staticConfig.SERVER_ID, tid, level, classString, escapedString, file, line );
|
||||
if ( mysql_query( &dbconn, sql ) ) {
|
||||
Level tempDatabaseLevel = mDatabaseLevel;
|
||||
databaseLevel( NOLOG );
|
||||
Error( "Can't insert log entry: sql(%s) error(%s)", sql, mysql_error( &dbconn ) );
|
||||
databaseLevel(tempDatabaseLevel);
|
||||
}
|
||||
}
|
||||
if ( level <= mSyslogLevel ) {
|
||||
int priority = smSyslogPriorities[level];
|
||||
//priority |= LOG_DAEMON;
|
||||
syslog( priority, "%s [%s] [%s]", classString, mId.c_str(), syslogStart );
|
||||
}
|
||||
|
||||
free(filecopy);
|
||||
if ( level <= FATAL ) {
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
if ( level <= PANIC )
|
||||
abort();
|
||||
exit( -1 );
|
||||
}
|
||||
free(filecopy);
|
||||
if ( level <= FATAL ) {
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
if ( level <= PANIC )
|
||||
abort();
|
||||
exit( -1 );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -544,6 +558,8 @@ void logInit( const char *name, const Logger::Options &options ) {
|
|||
}
|
||||
|
||||
void logTerm() {
|
||||
if ( Logger::smInstance )
|
||||
if ( Logger::smInstance ) {
|
||||
delete Logger::smInstance;
|
||||
Logger::smInstance = NULL;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,8 +30,7 @@
|
|||
#endif // HAVE_SYS_SYSCALL_H
|
||||
#include <mysql/mysql.h>
|
||||
|
||||
class Logger
|
||||
{
|
||||
class Logger {
|
||||
public:
|
||||
enum {
|
||||
NOOPT=-6,
|
||||
|
|
|
@ -396,8 +396,10 @@ Monitor::Monitor(
|
|||
|
||||
if ( purpose == CAPTURE ) {
|
||||
|
||||
this->connect();
|
||||
if ( ! mem_ptr ) exit(-1);
|
||||
if ( ! this->connect() ) {
|
||||
Error("unable to connect, but doing capture");
|
||||
exit(-1);
|
||||
}
|
||||
memset( mem_ptr, 0, mem_size );
|
||||
shared_data->size = sizeof(SharedData);
|
||||
shared_data->active = enabled;
|
||||
|
@ -486,38 +488,53 @@ bool Monitor::connect() {
|
|||
#if ZM_MEM_MAPPED
|
||||
snprintf( mem_file, sizeof(mem_file), "%s/zm.mmap.%d", staticConfig.PATH_MAP.c_str(), id );
|
||||
map_fd = open( mem_file, O_RDWR|O_CREAT, (mode_t)0600 );
|
||||
if ( map_fd < 0 )
|
||||
if ( map_fd < 0 ) {
|
||||
Fatal( "Can't open memory map file %s, probably not enough space free: %s", mem_file, strerror(errno) );
|
||||
} else {
|
||||
Debug(3, "Success opening mmap file at (%s)", mem_file );
|
||||
}
|
||||
|
||||
struct stat map_stat;
|
||||
if ( fstat( map_fd, &map_stat ) < 0 )
|
||||
Fatal( "Can't stat memory map file %s: %s, is the zmc process for this monitor running?", mem_file, strerror(errno) );
|
||||
if ( map_stat.st_size != mem_size && purpose == CAPTURE ) {
|
||||
// Allocate the size
|
||||
if ( ftruncate( map_fd, mem_size ) < 0 ) {
|
||||
Fatal( "Can't extend memory map file %s to %d bytes: %s", mem_file, mem_size, strerror(errno) );
|
||||
}
|
||||
} else if ( map_stat.st_size == 0 ) {
|
||||
Error( "Got empty memory map file size %ld, is the zmc process for this monitor running?", map_stat.st_size, mem_size );
|
||||
return false;
|
||||
} else if ( map_stat.st_size != mem_size ) {
|
||||
Error( "Got unexpected memory map file size %ld, expected %d", map_stat.st_size, mem_size );
|
||||
return false;
|
||||
} else {
|
||||
#ifdef MAP_LOCKED
|
||||
mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, map_fd, 0 );
|
||||
if ( mem_ptr == MAP_FAILED ) {
|
||||
if ( errno == EAGAIN ) {
|
||||
Debug( 1, "Unable to map file %s (%d bytes) to locked memory, trying unlocked", mem_file, mem_size );
|
||||
#endif
|
||||
mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0 );
|
||||
Debug( 1, "Mapped file %s (%d bytes) to locked memory, unlocked", mem_file, mem_size );
|
||||
#ifdef MAP_LOCKED
|
||||
|
||||
if ( map_stat.st_size != mem_size ) {
|
||||
if ( purpose == CAPTURE ) {
|
||||
// Allocate the size
|
||||
if ( ftruncate( map_fd, mem_size ) < 0 ) {
|
||||
Fatal( "Can't extend memory map file %s to %d bytes: %s", mem_file, mem_size, strerror(errno) );
|
||||
}
|
||||
} else if ( map_stat.st_size == 0 ) {
|
||||
Error( "Got empty memory map file size %ld, is the zmc process for this monitor running?", map_stat.st_size, mem_size );
|
||||
return false;
|
||||
} else {
|
||||
Error( "Got unexpected memory map file size %ld, expected %d", map_stat.st_size, mem_size );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Debug(3, "MMap file size is %ld", map_stat.st_size );
|
||||
#ifdef MAP_LOCKED
|
||||
mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, map_fd, 0 );
|
||||
if ( mem_ptr == MAP_FAILED ) {
|
||||
if ( errno == EAGAIN ) {
|
||||
Debug( 1, "Unable to map file %s (%d bytes) to locked memory, trying unlocked", mem_file, mem_size );
|
||||
|
||||
#endif
|
||||
if ( mem_ptr == MAP_FAILED )
|
||||
Fatal( "Can't map file %s (%d bytes) to memory: %s(%d)", mem_file, mem_size, strerror(errno), errno );
|
||||
mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0 );
|
||||
Debug( 1, "Mapped file %s (%d bytes) to unlocked memory", mem_file, mem_size );
|
||||
#ifdef MAP_LOCKED
|
||||
} else {
|
||||
Error( "Unable to map file %s (%d bytes) to locked memory (%s)", mem_file, mem_size , strerror(errno) );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if ( mem_ptr == MAP_FAILED )
|
||||
Fatal( "Can't map file %s (%d bytes) to memory: %s(%d)", mem_file, mem_size, strerror(errno), errno );
|
||||
if ( mem_ptr == NULL ) {
|
||||
Error( "mmap gave a null address:" );
|
||||
} else {
|
||||
Debug(3, "mmapped to %p", mem_ptr );
|
||||
}
|
||||
#else // ZM_MEM_MAPPED
|
||||
shm_id = shmget( (config.shm_key&0xffff0000)|id, mem_size, IPC_CREAT|0700 );
|
||||
|
@ -536,26 +553,26 @@ bool Monitor::connect() {
|
|||
video_store_data = (VideoStoreData *)((char *)trigger_data + sizeof(TriggerData));
|
||||
struct timeval *shared_timestamps = (struct timeval *)((char *)video_store_data + sizeof(VideoStoreData));
|
||||
unsigned char *shared_images = (unsigned char *)((char *)shared_timestamps + (image_buffer_count*sizeof(struct timeval)));
|
||||
|
||||
|
||||
if(((unsigned long)shared_images % 64) != 0) {
|
||||
if ( ((unsigned long)shared_images % 64) != 0 ) {
|
||||
/* Align images buffer to nearest 64 byte boundary */
|
||||
Debug(3,"Aligning shared memory images to the next 64 byte boundary");
|
||||
shared_images = (uint8_t*)((unsigned long)shared_images + (64 - ((unsigned long)shared_images % 64)));
|
||||
}
|
||||
Debug(3, "Allocating %d image buffers", image_buffer_count );
|
||||
image_buffer = new Snapshot[image_buffer_count];
|
||||
for ( int i = 0; i < image_buffer_count; i++ ) {
|
||||
image_buffer[i].timestamp = &(shared_timestamps[i]);
|
||||
image_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder(), &(shared_images[i*camera->ImageSize()]) );
|
||||
image_buffer[i].image->HoldBuffer(true); /* Don't release the internal buffer or replace it with another */
|
||||
}
|
||||
if ( (deinterlacing & 0xff) == 4) {
|
||||
/* Four field motion adaptive deinterlacing in use */
|
||||
/* Allocate a buffer for the next image */
|
||||
next_buffer.image = new Image( width, height, camera->Colours(), camera->SubpixelOrder());
|
||||
next_buffer.timestamp = new struct timeval;
|
||||
}
|
||||
|
||||
Debug(3, "Allocating %d image buffers", image_buffer_count );
|
||||
image_buffer = new Snapshot[image_buffer_count];
|
||||
for ( int i = 0; i < image_buffer_count; i++ ) {
|
||||
image_buffer[i].timestamp = &(shared_timestamps[i]);
|
||||
image_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder(), &(shared_images[i*camera->ImageSize()]) );
|
||||
image_buffer[i].image->HoldBuffer(true); /* Don't release the internal buffer or replace it with another */
|
||||
}
|
||||
if ( (deinterlacing & 0xff) == 4) {
|
||||
/* Four field motion adaptive deinterlacing in use */
|
||||
/* Allocate a buffer for the next image */
|
||||
next_buffer.image = new Image( width, height, camera->Colours(), camera->SubpixelOrder());
|
||||
next_buffer.timestamp = new struct timeval;
|
||||
}
|
||||
if ( ( purpose == ANALYSIS ) && analysis_fps ) {
|
||||
// Size of pre event buffer must be greater than pre_event_count
|
||||
// if alarm_frame_count > 1, because in this case the buffer contains
|
||||
|
@ -567,7 +584,7 @@ bool Monitor::connect() {
|
|||
pre_event_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder());
|
||||
}
|
||||
}
|
||||
|
||||
Debug(3, "Success connecting");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -484,7 +484,10 @@ void MonitorStream::runStream() {
|
|||
|
||||
openComms();
|
||||
|
||||
checkInitialised();
|
||||
if ( ! checkInitialised() ) {
|
||||
Error("Not initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
updateFrameRate( monitor->GetFPS() );
|
||||
|
||||
|
|
|
@ -63,24 +63,24 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
|||
av_make_error_string(ret).c_str()
|
||||
);
|
||||
} else {
|
||||
Debug(2, "Success allocating output context");
|
||||
Debug(4, "Success allocating output format context");
|
||||
}
|
||||
|
||||
//Couldn't deduce format from filename, trying from format name
|
||||
if ( ! oc ) {
|
||||
if ( !oc ) {
|
||||
avformat_alloc_output_context2(&oc, NULL, format, filename);
|
||||
if (!oc) {
|
||||
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);
|
||||
} else {
|
||||
Debug(4, "Success alocateing output context");
|
||||
}
|
||||
} else {
|
||||
Debug(2, "Success alocateing output context");
|
||||
}
|
||||
} // end if ! oc
|
||||
|
||||
AVDictionary *pmetadata = NULL;
|
||||
int dsr = av_dict_set(&pmetadata, "title", "Zoneminder Security Recording", 0);
|
||||
if (dsr < 0) Warning("%s:%d: title set failed", __FILE__, __LINE__ );
|
||||
if ( dsr < 0 ) Warning("%s:%d: title set failed", __FILE__, __LINE__ );
|
||||
|
||||
oc->metadata = pmetadata;
|
||||
output_format = oc->oformat;
|
||||
|
@ -228,11 +228,11 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
|||
audio_output_context = avcodec_alloc_context3( audio_output_codec );
|
||||
// Copy params from inputstream to context
|
||||
ret = avcodec_parameters_to_context( audio_output_context, audio_input_stream->codecpar );
|
||||
if (ret < 0) {
|
||||
if ( ret < 0 ) {
|
||||
Error("Unable to copy audio params to context %s\n", av_make_error_string(ret).c_str());
|
||||
}
|
||||
ret = avcodec_parameters_from_context( audio_output_stream->codecpar, audio_output_context );
|
||||
if (ret < 0) {
|
||||
if ( ret < 0 ) {
|
||||
Error("Unable to copy audio params to stream %s\n", av_make_error_string(ret).c_str());
|
||||
}
|
||||
|
||||
|
@ -324,11 +324,13 @@ VideoStore::~VideoStore(){
|
|||
|
||||
while ( 1 ) {
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||
#if 0
|
||||
ret = avcodec_send_frame( audio_output_context, NULL );
|
||||
if ( ret < 0 ) {
|
||||
Error("Error sending flush to encoder (%d) (%s)", ret, av_err2str( ret ));
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
ret = avcodec_receive_packet( audio_output_context, &pkt );
|
||||
if ( ret < 0 ) {
|
||||
Error("ERror encoding audio while flushing (%d) (%s)", ret, av_err2str( ret ));
|
||||
|
@ -348,7 +350,7 @@ VideoStore::~VideoStore(){
|
|||
#endif
|
||||
Debug(2, "writing flushed packet pts(%d) dts(%d) duration(%d)", pkt.pts, pkt.dts, pkt.duration);
|
||||
pkt.pts = audio_next_pts;
|
||||
pkt.dts = audio_next_dts;
|
||||
pkt.dts = audio_next_dts;
|
||||
|
||||
if ( pkt.duration > 0 )
|
||||
pkt.duration = av_rescale_q(pkt.duration, audio_output_context->time_base, audio_output_stream->time_base);
|
||||
|
@ -377,18 +379,28 @@ VideoStore::~VideoStore(){
|
|||
// What if we were only doing audio recording?
|
||||
if ( video_output_stream ) {
|
||||
avcodec_close(video_output_context);
|
||||
av_free(video_output_context);
|
||||
video_output_context = NULL;
|
||||
}
|
||||
if (audio_output_stream) {
|
||||
avcodec_close(audio_output_context);
|
||||
av_free(audio_output_context);
|
||||
audio_output_context = NULL;
|
||||
#ifdef HAVE_LIBAVRESAMPLE
|
||||
if ( resample_context ) {
|
||||
avresample_close( resample_context );
|
||||
avresample_free( &resample_context );
|
||||
}
|
||||
if ( input_frame ) {
|
||||
av_frame_free( &input_frame );
|
||||
input_frame = NULL;
|
||||
}
|
||||
if ( output_frame ) {
|
||||
av_frame_free( &output_frame );
|
||||
output_frame = NULL;
|
||||
}
|
||||
if ( converted_input_samples ) {
|
||||
av_free( converted_input_samples );
|
||||
converted_input_samples =NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -404,15 +416,6 @@ VideoStore::~VideoStore(){
|
|||
|
||||
/* free the stream */
|
||||
avformat_free_context(oc);
|
||||
|
||||
if ( input_frame ) {
|
||||
av_frame_free( &input_frame );
|
||||
input_frame = NULL;
|
||||
}
|
||||
if ( output_frame ) {
|
||||
av_frame_free( &output_frame );
|
||||
output_frame = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool VideoStore::setup_resampler() {
|
||||
|
@ -482,16 +485,6 @@ bool VideoStore::setup_resampler() {
|
|||
|
||||
audio_output_context->time_base = (AVRational){ 1, audio_output_context->sample_rate };
|
||||
|
||||
|
||||
Debug(1, "Audio output bit_rate (%d) sample_rate(%d) channels(%d) fmt(%d) layout(%d) frame_size(%d)",
|
||||
audio_output_context->bit_rate,
|
||||
audio_output_context->sample_rate,
|
||||
audio_output_context->channels,
|
||||
audio_output_context->sample_fmt,
|
||||
audio_output_context->channel_layout,
|
||||
audio_output_context->frame_size
|
||||
);
|
||||
|
||||
// Now copy them to the output stream
|
||||
audio_output_stream = avformat_new_stream( oc, audio_output_codec );
|
||||
|
||||
|
@ -625,7 +618,7 @@ bool VideoStore::setup_resampler() {
|
|||
Error("Not built with libavresample library. Cannot do audio conversion to AAC");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
} // end bool VideoStore::setup_resampler()
|
||||
|
||||
|
||||
void VideoStore::dumpPacket( AVPacket *pkt ){
|
||||
|
@ -651,14 +644,20 @@ int VideoStore::writeVideoFramePacket( AVPacket *ipkt ) {
|
|||
opkt.dts = video_next_dts;
|
||||
|
||||
int duration;
|
||||
if ( video_last_pts && ( ipkt->duration == AV_NOPTS_VALUE || ! ipkt->duration ) ) {
|
||||
if ( ! video_last_pts ) {
|
||||
duration = 0;
|
||||
} else {
|
||||
duration = av_rescale_q( ipkt->pts - video_last_pts, video_input_stream->time_base, video_output_stream->time_base);
|
||||
opkt.duration = 0;
|
||||
}
|
||||
|
||||
//#if ( 0 && video_last_pts && ( ipkt->duration == AV_NOPTS_VALUE || ! ipkt->duration ) ) {
|
||||
// Video packets don't really have a duration. Audio does.
|
||||
//opkt.duration = av_rescale_q(duration, video_input_stream->time_base, video_output_stream->time_base);
|
||||
opkt.duration = 0;
|
||||
} else {
|
||||
duration = opkt.duration = av_rescale_q(ipkt->duration, video_input_stream->time_base, video_output_stream->time_base);
|
||||
}
|
||||
//opkt.duration = 0;
|
||||
//} else {
|
||||
//duration = opkt.duration = av_rescale_q(ipkt->duration, video_input_stream->time_base, video_output_stream->time_base);
|
||||
//}
|
||||
video_last_pts = ipkt->pts;
|
||||
video_last_dts = ipkt->dts;
|
||||
|
||||
|
@ -746,7 +745,7 @@ int VideoStore::writeVideoFramePacket( AVPacket *ipkt ) {
|
|||
|
||||
} else {
|
||||
|
||||
video_next_dts = opkt.dts + duration; // Unsure if av_interleaved_write_frame() clobbers opkt.dts when out of order, so storing in advance
|
||||
video_next_dts = opkt.dts + duration;
|
||||
video_next_pts = opkt.pts + duration;
|
||||
ret = av_interleaved_write_frame(oc, &opkt);
|
||||
if ( ret < 0 ) {
|
||||
|
@ -775,6 +774,7 @@ int VideoStore::writeAudioFramePacket( AVPacket *ipkt ) {
|
|||
}
|
||||
|
||||
if ( audio_output_codec ) {
|
||||
Debug(3, "Have audio codec");
|
||||
#ifdef HAVE_LIBAVRESAMPLE
|
||||
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||
|
@ -789,12 +789,11 @@ int VideoStore::writeAudioFramePacket( AVPacket *ipkt ) {
|
|||
Error("avcodec_receive_frame fail %s", av_make_error_string(ret).c_str());
|
||||
return 0;
|
||||
}
|
||||
Debug(2, "Frame: samples(%d), format(%d), sample_rate(%d), channel layout(%d) refd(%d)",
|
||||
Debug(2, "Input Frame: samples(%d), format(%d), sample_rate(%d), channel layout(%d)",
|
||||
input_frame->nb_samples,
|
||||
input_frame->format,
|
||||
input_frame->sample_rate,
|
||||
input_frame->channel_layout,
|
||||
audio_output_context->refcounted_frames
|
||||
input_frame->channel_layout
|
||||
);
|
||||
#else
|
||||
/**
|
||||
|
@ -803,8 +802,8 @@ int VideoStore::writeAudioFramePacket( AVPacket *ipkt ) {
|
|||
* If we are at the end of the file, pass an empty packet to the decoder
|
||||
* to flush it.
|
||||
*/
|
||||
if ((ret = avcodec_decode_audio4(audio_input_context, input_frame,
|
||||
&data_present, ipkt)) < 0) {
|
||||
if ( (ret = avcodec_decode_audio4(audio_input_context, input_frame,
|
||||
&data_present, ipkt)) < 0 ) {
|
||||
Error( "Could not decode frame (error '%s')\n",
|
||||
av_make_error_string(ret).c_str());
|
||||
dumpPacket( ipkt );
|
||||
|
@ -816,8 +815,7 @@ int VideoStore::writeAudioFramePacket( AVPacket *ipkt ) {
|
|||
return 0;
|
||||
}
|
||||
#endif
|
||||
int frame_size = input_frame->nb_samples;
|
||||
Debug(4, "Frame size: %d", frame_size );
|
||||
int frame_size = output_frame->nb_samples;
|
||||
|
||||
// Resample the input into the audioSampleBuffer until we proceed the whole decoded data
|
||||
if ( (ret = avresample_convert( resample_context,
|
||||
|
@ -829,34 +827,34 @@ int VideoStore::writeAudioFramePacket( AVPacket *ipkt ) {
|
|||
input_frame->nb_samples )) < 0 ) {
|
||||
Error( "Could not resample frame (error '%s')\n",
|
||||
av_make_error_string(ret).c_str());
|
||||
av_frame_unref( input_frame );
|
||||
return 0;
|
||||
}
|
||||
av_frame_unref( input_frame );
|
||||
|
||||
int samples_available = avresample_available( resample_context );
|
||||
|
||||
if ( samples_available < frame_size ) {
|
||||
Debug(1, "Not enough samples yet (%d)", samples_available);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( avresample_available( resample_context ) < output_frame->nb_samples ) {
|
||||
Debug(1, "No enough samples yet");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Debug(3, "Output_frame samples (%d)", output_frame->nb_samples );
|
||||
// Read a frame audio data from the resample fifo
|
||||
if ( avresample_read( resample_context, output_frame->data, output_frame->nb_samples ) != output_frame->nb_samples ) {
|
||||
if ( avresample_read( resample_context, output_frame->data, frame_size ) != frame_size ) {
|
||||
Warning( "Error reading resampled audio: " );
|
||||
return 0;
|
||||
}
|
||||
Debug(2, "Frame: samples(%d), format(%d), sample_rate(%d), channel layout(%d)",
|
||||
output_frame->nb_samples,
|
||||
output_frame->format,
|
||||
output_frame->sample_rate,
|
||||
output_frame->channel_layout
|
||||
);
|
||||
|
||||
av_init_packet(&opkt);
|
||||
Debug(5, "after init packet" );
|
||||
|
||||
/** Set a timestamp based on the sample rate for the container. */
|
||||
//output_frame->pts = av_rescale_q( opkt.pts, audio_output_context->time_base, audio_output_stream->time_base );
|
||||
|
||||
// convert the packet to the codec timebase from the stream timebase
|
||||
//Debug(3, "output_frame->pts(%d) best effort(%d)", output_frame->pts,
|
||||
//av_frame_get_best_effort_timestamp(output_frame)
|
||||
//);
|
||||
/**
|
||||
* Encode the audio frame and store it in the temporary packet.
|
||||
* The output audio stream encoder is used to do this.
|
||||
*/
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||
if (( ret = avcodec_send_frame( audio_output_context, output_frame ) ) < 0 ) {
|
||||
Error( "Could not send frame (error '%s')",
|
||||
|
@ -865,10 +863,20 @@ int VideoStore::writeAudioFramePacket( AVPacket *ipkt ) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (( ret = avcodec_receive_packet( audio_output_context, &opkt )) < 0 ) {
|
||||
//av_frame_unref( output_frame );
|
||||
|
||||
if ( ( ret = avcodec_receive_packet( audio_output_context, &opkt ) ) < 0 ) {
|
||||
if ( EAGAIN == ret ) {
|
||||
// THe codec may need more samples than it has, perfectly valid
|
||||
Debug( 3, "Could not recieve packet (error '%s')",
|
||||
av_make_error_string(ret).c_str());
|
||||
} else {
|
||||
Error( "Could not recieve packet (error '%s')",
|
||||
av_make_error_string(ret).c_str());
|
||||
}
|
||||
zm_av_packet_unref(&opkt);
|
||||
av_frame_unref( input_frame );
|
||||
//av_frame_unref( output_frame );
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
|
@ -918,9 +926,8 @@ int VideoStore::writeAudioFramePacket( AVPacket *ipkt ) {
|
|||
}
|
||||
#else
|
||||
opkt.pts = audio_next_pts;
|
||||
#endif
|
||||
|
||||
opkt.dts = audio_next_dts;
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
if ( ipkt->dts == AV_NOPTS_VALUE ) {
|
||||
|
@ -945,14 +952,20 @@ int VideoStore::writeAudioFramePacket( AVPacket *ipkt ) {
|
|||
}
|
||||
}
|
||||
#endif
|
||||
audio_last_dts = ipkt->dts;
|
||||
//audio_last_dts = ipkt->dts;
|
||||
if ( opkt.dts > opkt.pts ) {
|
||||
Debug(1,"opkt.dts(%d) must be <= opkt.pts(%d). Decompression must happen before presentation.", opkt.dts, opkt.pts );
|
||||
opkt.dts = opkt.pts;
|
||||
}
|
||||
|
||||
// I wonder if we could just use duration instead of all the hoop jumping above?
|
||||
opkt.duration = av_rescale_q(ipkt->duration, audio_input_stream->time_base, audio_output_stream->time_base);
|
||||
//
|
||||
if ( output_frame ) {
|
||||
opkt.duration = output_frame->nb_samples;
|
||||
} else {
|
||||
opkt.duration = ipkt->duration;
|
||||
}
|
||||
//opkt.duration = av_rescale_q(ipkt->duration, audio_input_stream->time_base, audio_output_stream->time_base);
|
||||
Debug( 2, "opkt.pts (%d), opkt.dts(%d) opkt.duration = (%d)", opkt.pts, opkt.dts, opkt.duration );
|
||||
|
||||
// pkt.pos: byte position in stream, -1 if unknown
|
||||
|
|
|
@ -70,6 +70,9 @@ class DATABASE_CONFIG {
|
|||
'login' => ZM_DB_USER,
|
||||
'password' => ZM_DB_PASS,
|
||||
'database' => ZM_DB_NAME,
|
||||
'ssl_ca' => ZM_DB_SSL_CA_CERT,
|
||||
'ssl_key' => ZM_DB_SSL_CLIENT_KEY,
|
||||
'ssl_cert' => ZM_DB_SSL_CLIENT_CERT,
|
||||
'prefix' => '',
|
||||
'encoding' => 'utf8',
|
||||
);
|
||||
|
|
|
@ -202,7 +202,7 @@ class Event {
|
|||
|
||||
// frame is an array representing the db row for a frame.
|
||||
function getImageSrc( $frame, $scale=SCALE_BASE, $captureOnly=false, $overwrite=false ) {
|
||||
$Storage = new Storage( isset($this->{'StorageId'}) ? $this->{'StorageId'} : NULL );
|
||||
$Storage = new Storage( isset($this->{'StorageId'}) ? $this->{'StorageId'} : NULL );
|
||||
$Event = $this;
|
||||
$eventPath = $Event->Path();
|
||||
|
||||
|
|
|
@ -211,8 +211,9 @@ function process_configfile($configFile) {
|
|||
continue;
|
||||
elseif ( preg_match( '/^\s*#/', $str ))
|
||||
continue;
|
||||
elseif ( preg_match( '/^\s*([^=\s]+)\s*=\s*(.*?)\s*$/', $str, $matches ))
|
||||
elseif ( preg_match( '/^\s*([^=\s]+)\s*=\s*(.*?)\s*$/', $str, $matches )) {
|
||||
$configvals[$matches[1]] = $matches[2];
|
||||
}
|
||||
}
|
||||
fclose( $cfg );
|
||||
return( $configvals );
|
||||
|
|
|
@ -42,11 +42,23 @@ function dbConnect() {
|
|||
}
|
||||
|
||||
try {
|
||||
$dbConn = new PDO( ZM_DB_TYPE . $socket . ';dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS );
|
||||
|
||||
$dbOptions = null;
|
||||
if ( defined( 'ZM_DB_SSL_CA_CERT' ) and ZM_DB_SSL_CA_CERT ) {
|
||||
$dbOptions = array(
|
||||
PDO::MYSQL_ATTR_SSL_CA => ZM_DB_SSL_CA_CERT,
|
||||
PDO::MYSQL_ATTR_SSL_KEY => ZM_DB_SSL_CLIENT_KEY,
|
||||
PDO::MYSQL_ATTR_SSL_CERT => ZM_DB_SSL_CLIENT_CERT,
|
||||
);
|
||||
$dbConn = new PDO( ZM_DB_TYPE . $socket . ';dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS, $dbOptions );
|
||||
} else {
|
||||
$dbConn = new PDO( ZM_DB_TYPE . $socket . ';dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS );
|
||||
}
|
||||
$dbConn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
|
||||
$dbConn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
} catch(PDOException $ex ) {
|
||||
echo "Unable to connect to ZM db." . $ex->getMessage();
|
||||
error_log("Unable to connect to ZM DB " . $ex->getMessage() );
|
||||
$dbConn = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,7 +88,7 @@ $j( function() {
|
|||
function applySort(event, ui) {
|
||||
var monitor_ids = $j(this).sortable('toArray');
|
||||
var ajax = new Request.JSON( {
|
||||
url: '/index.php?request=console',
|
||||
url: '?request=console',
|
||||
data: { monitor_ids: monitor_ids, action: 'sort' },
|
||||
method: 'post',
|
||||
timeout: AJAX_TIMEOUT
|
||||
|
|
|
@ -18,10 +18,9 @@
|
|||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
|
||||
if ( !canView( 'Events' ) )
|
||||
{
|
||||
$view = "error";
|
||||
return;
|
||||
if ( !canView( 'Events' ) ) {
|
||||
$view = "error";
|
||||
return;
|
||||
}
|
||||
|
||||
require_once('includes/Event.php');
|
||||
|
@ -32,80 +31,69 @@ $sql = 'SELECT E.*,M.Name AS MonitorName,M.DefaultRate,M.DefaultScale FROM Event
|
|||
$sql_values = array( $eid );
|
||||
|
||||
if ( $user['MonitorIds'] ) {
|
||||
$monitor_ids = explode( ',', $user['MonitorIds'] );
|
||||
$sql .= ' AND MonitorId IN (' .implode( ',', array_fill(0,count($monitor_ids),'?') ) . ')';
|
||||
$sql_values = array_merge( $sql_values, $monitor_ids );
|
||||
$monitor_ids = explode( ',', $user['MonitorIds'] );
|
||||
$sql .= ' AND MonitorId IN (' .implode( ',', array_fill(0,count($monitor_ids),'?') ) . ')';
|
||||
$sql_values = array_merge( $sql_values, $monitor_ids );
|
||||
}
|
||||
$event = dbFetchOne( $sql, NULL, $sql_values );
|
||||
|
||||
if ( isset( $_REQUEST['rate'] ) )
|
||||
$rate = validInt($_REQUEST['rate']);
|
||||
$rate = validInt($_REQUEST['rate']);
|
||||
else
|
||||
$rate = reScale( RATE_BASE, $event['DefaultRate'], ZM_WEB_DEFAULT_RATE );
|
||||
$rate = reScale( RATE_BASE, $event['DefaultRate'], ZM_WEB_DEFAULT_RATE );
|
||||
if ( isset( $_REQUEST['scale'] ) )
|
||||
$scale = validInt($_REQUEST['scale']);
|
||||
$scale = validInt($_REQUEST['scale']);
|
||||
else
|
||||
$scale = reScale( SCALE_BASE, $event['DefaultScale'], ZM_WEB_DEFAULT_SCALE );
|
||||
$scale = reScale( SCALE_BASE, $event['DefaultScale'], ZM_WEB_DEFAULT_SCALE );
|
||||
|
||||
$Event = new Event( $event['Id'] );
|
||||
$eventPath = $Event->Path();
|
||||
|
||||
$videoFormats = array();
|
||||
$ffmpegFormats = preg_split( '/\s+/', ZM_FFMPEG_FORMATS );
|
||||
foreach ( $ffmpegFormats as $ffmpegFormat )
|
||||
{
|
||||
if ( preg_match( '/^([^*]+)(\*\*?)$/', $ffmpegFormat, $matches ) )
|
||||
{
|
||||
$videoFormats[$matches[1]] = $matches[1];
|
||||
if ( !isset($videoFormat) && $matches[2] == "*" )
|
||||
{
|
||||
$videoFormat = $matches[1];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$videoFormats[$ffmpegFormat] = $ffmpegFormat;
|
||||
foreach ( $ffmpegFormats as $ffmpegFormat ) {
|
||||
if ( preg_match( '/^([^*]+)(\*\*?)$/', $ffmpegFormat, $matches ) ) {
|
||||
$videoFormats[$matches[1]] = $matches[1];
|
||||
if ( !isset($videoFormat) && $matches[2] == '*' ) {
|
||||
$videoFormat = $matches[1];
|
||||
}
|
||||
} else {
|
||||
$videoFormats[$ffmpegFormat] = $ffmpegFormat;
|
||||
}
|
||||
}
|
||||
|
||||
$videoFiles = array();
|
||||
if ( $dir = opendir( $eventPath ) )
|
||||
{
|
||||
while ( ($file = readdir( $dir )) !== false )
|
||||
{
|
||||
$file = $eventPath.'/'.$file;
|
||||
if ( is_file( $file ) )
|
||||
{
|
||||
if ( preg_match( '/\.(?:'.join( '|', $videoFormats ).')$/', $file ) )
|
||||
{
|
||||
$videoFiles[] = $file;
|
||||
}
|
||||
}
|
||||
if ( $dir = opendir( $eventPath ) ) {
|
||||
while ( ($file = readdir( $dir )) !== false ) {
|
||||
$file = $eventPath.'/'.$file;
|
||||
if ( is_file( $file ) ) {
|
||||
if ( preg_match( '/\.(?:'.join( '|', $videoFormats ).')$/', $file ) ) {
|
||||
$videoFiles[] = $file;
|
||||
}
|
||||
}
|
||||
closedir( $dir );
|
||||
}
|
||||
closedir( $dir );
|
||||
}
|
||||
|
||||
if ( isset($_REQUEST['deleteIndex']) )
|
||||
{
|
||||
$deleteIndex = validInt($_REQUEST['deleteIndex']);
|
||||
unlink( $videoFiles[$deleteIndex] );
|
||||
unset( $videoFiles[$deleteIndex] );
|
||||
if ( isset($_REQUEST['deleteIndex']) ) {
|
||||
$deleteIndex = validInt($_REQUEST['deleteIndex']);
|
||||
unlink( $videoFiles[$deleteIndex] );
|
||||
unset( $videoFiles[$deleteIndex] );
|
||||
}
|
||||
|
||||
if ( isset($_REQUEST['downloadIndex']) )
|
||||
{
|
||||
$downloadIndex = validInt($_REQUEST['downloadIndex']);
|
||||
header( "Pragma: public" );
|
||||
header( "Expires: 0" );
|
||||
header( "Cache-Control: must-revalidate, post-check=0, pre-check=0" );
|
||||
header( "Cache-Control: private", false ); // required by certain browsers
|
||||
header( "Content-Description: File Transfer" );
|
||||
header( 'Content-disposition: attachment; filename="'.basename($videoFiles[$downloadIndex]).'"' ); // basename is required because the video index contains the path and firefox doesn't strip the path but simply replaces the slashes with an underscore.
|
||||
header( "Content-Transfer-Encoding: binary" );
|
||||
header( "Content-Type: application/force-download" );
|
||||
header( "Content-Length: ".filesize($videoFiles[$downloadIndex]) );
|
||||
readfile( $videoFiles[$downloadIndex] );
|
||||
exit;
|
||||
if ( isset($_REQUEST['downloadIndex']) ) {
|
||||
$downloadIndex = validInt($_REQUEST['downloadIndex']);
|
||||
header( "Pragma: public" );
|
||||
header( "Expires: 0" );
|
||||
header( "Cache-Control: must-revalidate, post-check=0, pre-check=0" );
|
||||
header( "Cache-Control: private", false ); // required by certain browsers
|
||||
header( "Content-Description: File Transfer" );
|
||||
header( 'Content-disposition: attachment; filename="'.basename($videoFiles[$downloadIndex]).'"' ); // basename is required because the video index contains the path and firefox doesn't strip the path but simply replaces the slashes with an underscore.
|
||||
header( "Content-Transfer-Encoding: binary" );
|
||||
header( "Content-Type: application/force-download" );
|
||||
header( "Content-Length: ".filesize($videoFiles[$downloadIndex]) );
|
||||
readfile( $videoFiles[$downloadIndex] );
|
||||
exit;
|
||||
}
|
||||
|
||||
$focusWindow = true;
|
||||
|
@ -122,19 +110,16 @@ xhtmlHeaders(__FILE__, translate('Video') );
|
|||
</div>
|
||||
<div id="content">
|
||||
<?php
|
||||
if ( isset($_REQUEST['showIndex']) )
|
||||
{
|
||||
$showIndex = validInt($_REQUEST['showIndex']);
|
||||
preg_match( '/([^\/]+)\.([^.]+)$/', $videoFiles[$showIndex], $matches );
|
||||
$name = $matches[1];
|
||||
$videoFormat = $matches[2];
|
||||
if ( isset($_REQUEST['showIndex']) ) {
|
||||
$showIndex = validInt($_REQUEST['showIndex']);
|
||||
preg_match( '/([^\/]+)\.([^.]+)$/', $videoFiles[$showIndex], $matches );
|
||||
$name = $matches[1];
|
||||
$videoFormat = $matches[2];
|
||||
?>
|
||||
<h3 id="videoFile"><?php echo substr( $videoFiles[$showIndex], strlen(ZM_DIR_EVENTS)+1 ) ?></h3>
|
||||
<div id="imageFeed"><?php outputVideoStream( 'videoStream', $videoFiles[$showIndex], validInt($_REQUEST['width']), validInt($_REQUEST['height']), $videoFormat, $name ) ?></div>
|
||||
<?php
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
?>
|
||||
<form name="contentForm" id="contentForm" method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>">
|
||||
<input type="hidden" name="id" value="<?php echo $event['Id'] ?>"/>
|
||||
|
@ -161,29 +146,23 @@ else
|
|||
<input type="button" value="<?php echo translate('GenerateVideo') ?>" onclick="generateVideo( this.form );"<?php if ( !ZM_OPT_FFMPEG ) { ?> disabled="disabled"<?php } ?>/>
|
||||
</form>
|
||||
<?php
|
||||
if ( isset($_REQUEST['generated']) )
|
||||
{
|
||||
if ( isset($_REQUEST['generated']) ) {
|
||||
?>
|
||||
<h2 id="videoProgress" class="<?php echo $_REQUEST['generated']?'infoText':'errorText' ?>"><span id="videoProgressText"><?php echo $_REQUEST['generated']?translate('VideoGenSucceeded'):translate('VideoGenFailed') ?></span><span id="videoProgressTicker"></span></h2>
|
||||
<?php
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
?>
|
||||
<h2 id="videoProgress" class="hidden warnText"><span id="videoProgressText"><?php echo translate('GeneratingVideo') ?></span><span id="videoProgressTicker"></span></h2>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
?>
|
||||
<h2 id="videoFilesHeader"><?php echo translate('VideoGenFiles') ?></h2>
|
||||
<?php
|
||||
if ( count($videoFiles) == 0 )
|
||||
{
|
||||
if ( count($videoFiles) == 0 ) {
|
||||
?>
|
||||
<h3 id="videoNoFiles"><?php echo translate('VideoGenNoFiles') ?></h3>
|
||||
<?php
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
?>
|
||||
<table id="videoTable" class="major" cellspacing="0">
|
||||
<thead>
|
||||
|
@ -197,32 +176,24 @@ else
|
|||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$index = 0;
|
||||
foreach ( $videoFiles as $file )
|
||||
{
|
||||
if ( filesize( $file ) > 0 )
|
||||
{
|
||||
preg_match( '/^(.+)-((?:r[_\d]+)|(?:F[_\d]+))-((?:s[_\d]+)|(?:S[0-9a-z]+))\.([^.]+)$/', $file, $matches );
|
||||
if ( preg_match( '/^r(.+)$/', $matches[2], $temp_matches ) )
|
||||
{
|
||||
$rate = (int)(100 * preg_replace( '/_/', '.', $temp_matches[1] ) );
|
||||
$rateText = isset($rates[$rate])?$rates[$rate]:($rate."x");
|
||||
}
|
||||
elseif ( preg_match( '/^F(.+)$/', $matches[2], $temp_matches ) )
|
||||
{
|
||||
$rateText = $temp_matches[1]."fps";
|
||||
}
|
||||
if ( preg_match( '/^s(.+)$/', $matches[3], $temp_matches ) )
|
||||
{
|
||||
$scale = (int)(100 * preg_replace( '/_/', '.', $temp_matches[1] ) );
|
||||
$scaleText = isset($scales[$scale])?$scales[$scale]:($scale."x");
|
||||
}
|
||||
elseif ( preg_match( '/^S(.+)$/', $matches[3], $temp_matches ) )
|
||||
{
|
||||
$scaleText = $temp_matches[1];
|
||||
}
|
||||
$width = $scale?reScale( $event['Width'], $scale ):$event['Width'];
|
||||
$height = $scale?reScale( $event['Height'], $scale ):$event['Height'];
|
||||
$index = 0;
|
||||
foreach ( $videoFiles as $file ) {
|
||||
if ( filesize( $file ) > 0 ) {
|
||||
preg_match( '/^(.+)-((?:r[_\d]+)|(?:F[_\d]+))-((?:s[_\d]+)|(?:S[0-9a-z]+))\.([^.]+)$/', $file, $matches );
|
||||
if ( preg_match( '/^r(.+)$/', $matches[2], $temp_matches ) ) {
|
||||
$rate = (int)(100 * preg_replace( '/_/', '.', $temp_matches[1] ) );
|
||||
$rateText = isset($rates[$rate])?$rates[$rate]:($rate."x");
|
||||
} elseif ( preg_match( '/^F(.+)$/', $matches[2], $temp_matches ) ) {
|
||||
$rateText = $temp_matches[1]."fps";
|
||||
}
|
||||
if ( preg_match( '/^s(.+)$/', $matches[3], $temp_matches ) ) {
|
||||
$scale = (int)(100 * preg_replace( '/_/', '.', $temp_matches[1] ) );
|
||||
$scaleText = isset($scales[$scale])?$scales[$scale]:($scale."x");
|
||||
} elseif ( preg_match( '/^S(.+)$/', $matches[3], $temp_matches ) ) {
|
||||
$scaleText = $temp_matches[1];
|
||||
}
|
||||
$width = $scale?reScale( $event['Width'], $scale ):$event['Width'];
|
||||
$height = $scale?reScale( $event['Height'], $scale ):$event['Height'];
|
||||
?>
|
||||
<tr>
|
||||
<td><?php echo $matches[4] ?></td>
|
||||
|
@ -232,14 +203,14 @@ else
|
|||
<td><?php echo makePopupLink( '?view='.$view.'&eid='.$event['Id'].'&width='.$width.'&height='.$height.'&showIndex='.$index, 'zmVideo'.$event['Id'].'-'.$scale, array( 'videoview', $width, $height ), translate('View') ); ?> / <a href="<?php echo substr( $file, strlen(ZM_DIR_EVENTS)+1 ) ?>" onclick="downloadVideo( <?php echo $index ?> ); return( false );"><?php echo translate('Download') ?></a> / <a href="#" onclick="deleteVideo( <?php echo $index ?> ); return( false );"><?php echo translate('Delete') ?></a></td>
|
||||
</tr>
|
||||
<?php
|
||||
$index++;
|
||||
}
|
||||
}
|
||||
$index++;
|
||||
}
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
|
|
|
@ -49,6 +49,15 @@ ZM_DB_USER=@ZM_DB_USER@
|
|||
# ZoneMinder database password
|
||||
ZM_DB_PASS=@ZM_DB_PASS@
|
||||
|
||||
# SSL CA certificate for ZoneMinder database
|
||||
ZM_DB_SSL_CA_CERT=@ZM_DB_SSL_CA_CERT@
|
||||
|
||||
# SSL client key for ZoneMinder database
|
||||
ZM_DB_SSL_CLIENT_KEY=@ZM_DB_SSL_CLIENT_KEY@
|
||||
|
||||
# SSL client cert for ZoneMinder database
|
||||
ZM_DB_SSL_CLIENT_CERT=@ZM_DB_SSL_CLIENT_CERT@
|
||||
|
||||
# Do NOT set ZM_SERVER_HOST if you are not using Multi-Server
|
||||
# You have been warned
|
||||
#
|
||||
|
|
Loading…
Reference in New Issue